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

import { TopicObserver } from "../topic-observer";

describe('topic observer', () => {

    const observer = new TopicObserver();
    const fn1 = () => {};

    it('should observe all', function() {

        observer.observe(fn1);
        expect(observer.observers['__all__'].length).to.equal(1)

    });

    it('should remove all', function() {

        observer.remove(fn1);
        expect(observer.observers['__all__'].length).to.equal(0);

    });

    it('should receive all', function () {

        let subscriberHasBeenCalled = false;
        const fn2 = (data) => subscriberHasBeenCalled = data;

        observer.observe(fn2);
        observer.broadcast(true);
        expect(subscriberHasBeenCalled).to.equal(true);
        observer.remove(fn2);

    });

    const fn3 = () => {};

    it('should observe topic', function() {

        observer.observe(fn3, 'topic');
        expect(observer.observers['topic'].length).to.equal(1)

    });

    it('should remove topic', function() {

        observer.remove(fn3, 'topic');
        expect(observer.observers['topic'].length).to.equal(0);

    });

    it('should receive topic', function () {

        let subscriberHasBeenCalled = false;
        const fn4 = (data) => subscriberHasBeenCalled = data;

        observer.observe(fn4, 'topic');
        observer.broadcast(true);
        expect(subscriberHasBeenCalled).to.equal(true);
        observer.observe(fn4, 'topic');

    });

});

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

import { TopicObserver } from "../topic-observer";

describe('topic observer', () => {

    const observer = new TopicObserver();
    const fn1 = () => {};

    it('should observe all', function() {

        observer.observe(fn1);
        expect(observer.observers['__all__'].length).to.equal(1)

    });

    it('should remove all', function() {

        observer.remove(fn1);
        expect(observer.observers['__all__'].length).to.equal(0);

    });

    it('should receive all', function () {

        let subscriberHasBeenCalled = false;
        const fn2 = (data) => subscriberHasBeenCalled = data;

        observer.observe(fn2);
        observer.broadcast(true);
        expect(subscriberHasBeenCalled).to.equal(true);
        observer.remove(fn2);

    });

    const fn3 = () => {};

    it('should observe topic', function() {

        observer.observe(fn3, 'topic');
        expect(observer.observers['topic'].length).to.equal(1)

    });

    it('should remove topic', function() {

        observer.remove(fn3, 'topic');
        expect(observer.observers['topic'].length).to.equal(0);

    });

    it('should receive topic', function () {

        let subscriberHasBeenCalled = false;
        const fn4 = (data) => subscriberHasBeenCalled = data;

        observer.observe(fn4, 'topic');
        observer.broadcast(true, 'topic');
        expect(subscriberHasBeenCalled).to.equal(true);
        observer.observe(fn4, 'topic');

    });

});

Put back changes in observer after unsuccessful commit.


'use strict';

require('../helper');

const Backbone = require('backbone');
const _ = require('underscore');

const Factotum = require('../../src/backbone.factotum');

// =====
// Backbone types
// =====

const Job = Backbone.Model.extend({});

const JobCollection = Backbone.Collection.extend({ model: Job });

const User = Backbone.Model.extend({
  parse(attrs) {
    this.jobs = new JobCollection(attrs.jobs);

    return _.omit(attrs, 'jobs');
  }
});

// =====

describe('Acceptance', function() {
  describe('working with Backbone', function() {
    beforeEach(function() {
      Factotum.define('job', Job, { name: Factotum.sequence((i) => `Job ${i}`) });

      Factotum.define('user', User, {
        id: Factotum.sequence((i) => i),
        name: Factotum.sequence((i) => `User ${i}`),
        jobs: Factotum.create('job', 5).map((job) => job.toJSON())
      });
    });

    afterEach(function() {
      Factotum.reset();
    });

    it('creates a user model with a collection of jobs', function() {
      const user = Factotum.create('user');

      user.get('jobs').should.exist;
    });

    it('accepts extra options for creating the required objects', function() {
      const user = Factotum.create('user', { parse: true });

      user.jobs.should.be.an.instanceof(JobCollection);
    });
  });
});

'use strict';

require('../helper');

const Backbone = require('backbone');
const _ = require('underscore');

const Factotum = require('../../src/backbone.factotum');

// =====
// Backbone types
// =====

const Job = Backbone.Model.extend({});

const JobCollection = Backbone.Collection.extend({ model: Job });

const User = Backbone.Model.extend({
  parse(attrs) {
    this.jobs = new JobCollection(attrs.jobs);

    return _.omit(attrs, 'jobs');
  }
});

// =====

describe('Acceptance', function() {
  describe('working with Backbone', function() {
    beforeEach(function() {
      Factotum.define('job', Job, { name: Factotum.sequence((i) => `Job ${i}`) });

      Factotum.define('user', User, {
        id: Factotum.sequence((i) => i),
        name: Factotum.sequence((i) => `User ${i}`),
        jobs: Factotum.create('job', 5)
      });
    });

    it('creates a user model with a collection of jobs', function() {
      const user = Factotum.create('user');

      user.get('jobs').should.exist;
    });

    it('accepts extra options for creating the required objects', function() {
      const user = Factotum.create('user', { parse: true });

      user.jobs.should.be.an.instanceof(JobCollection);
    });
  });
});

Remove useless lines from acceptance tests


/* jshint node: true */

var EOL = require('os').EOL;

module.exports = {
  description: 'Generates ember-cli-flash test helper',

  afterInstall: function() {
    var TEST_HELPER_PATH = 'tests/test-helper.js';
    var IMPORT_STATEMENT = EOL + "import flashMessageHelper from './helpers/flash-message';";
    var INSERT_AFTER = "import resolver from './helpers/resolver';";

    return this.insertIntoFile(TEST_HELPER_PATH, IMPORT_STATEMENT, {
      after: INSERT_AFTER
    });
  },

  normalizeEntityName: function() {}
};

/* jshint node: true */

var EOL = require('os').EOL;

module.exports = {
  description: 'Generates ember-cli-flash test helper',

  afterInstall: function() {
    var TEST_HELPER_PATH = 'tests/test-helper.js';
    var IMPORT_STATEMENT = EOL + "import './helpers/flash-message';";
    var INSERT_AFTER = "import resolver from './helpers/resolver';";

    return this.insertIntoFile(TEST_HELPER_PATH, IMPORT_STATEMENT, {
      after: INSERT_AFTER
    });
  },

  normalizeEntityName: function() {}
};

Make jshint pass on test-helper.js


var Promise = require('bluebird'),
  	mongoose = Promise.promisifyAll(require('mongoose')),
  	User = mongoose.model('User');
    authUtils = require('utils/auth');

module.exports = {
	login : Login
};

function Login(request,reply) {

	var user = reply.data,
		token = authUtils.createToken({'id' : user._id, 'scope' : user.type});

	reply.data = token;
	reply.continue();

}

var Promise = require('bluebird'),
  	mongoose = Promise.promisifyAll(require('mongoose')),
  	User = mongoose.model('User');
    authUtils = require('utils/auth');

module.exports = {
	login : Login
};

function Login(request,reply) {

	var user = reply.data,
		token = authUtils.createToken({'id' : user.id, 'scope' : user.type});

	reply.data = token;
	reply.continue();

}

Use correct value for storing user id


module.exports = function*() {
	this.time('Linter')
	this.separator()

	let methods = {}

	for(let tuple of this.pages) {
		let page = tuple[1]

		for(let method of page.httpVerbs) {
			let check = () => this.checkRoute(method, page.url)

			if(!methods[method])
				methods[method] = [check]
			else
				methods[method].push(check)
		}
	}

	for(let method in methods) {
		for(let check of methods[method]) {
			yield check()
		}
	}

	this.timeEnd('Linter')
}

module.exports = function*() {
	this.time('Linter')
	this.separator()

	// Sort pages by URL
	let pages = Array.from(this.pages).sort((a, b) => a[1].url.localeCompare(b[1].url))

	let methods = {}

	for(let tuple of pages) {
		let page = tuple[1]

		for(let method of page.httpVerbs) {
			let check = () => this.checkRoute(method, page.url)

			if(!methods[method])
				methods[method] = [check]
			else
				methods[method].push(check)
		}
	}

	for(let method in methods) {
		for(let check of methods[method]) {
			yield check()
		}
	}

	this.timeEnd('Linter')
}

Sort linter results by page URL


function annotateEngine(src, rules, ruleTypes, ruleMap) {
    var tokens = [];

    while(src) {
        // Pick rule
        var rule = ruleTypes.filter(function(ruleName, idx) {
            var regex = rules[ruleName];
            return regex.exec(src);
        })[0];

        // No matching rules
        if(!rule) {
            throw new Error('No rule found for: ' + src);
        }

        // Use rule to extract block
        var ruleRegex = rules[rule];
        var block = ruleRegex.exec(src);

        // Get rule type
        var type = ruleMap[rule] || rule;

        // Get raw text
        var raw = block[0];

        // Break out here to avoid infinite loops
        if(raw.length === 0) {
            break;
        }

        tokens.push({
            type: ruleMap[rule] || rule,
            raw: raw,
        });

        // Update source
        src = src.substring(raw.length);
    }

    return tokens;
}

module.exports = annotateEngine;

function annotateEngine(src, rules, ruleTypes, ruleMap) {
    var tokens = [];

    while(src) {
        // Pick rule
        var rule = ruleTypes.filter(function(ruleName, idx) {
            var regex = rules[ruleName];
            return regex.exec(src);
        })[0];

        // No matching rules
        if(!rule) {
            throw new Error('No rule found for: ' + src);
        }

        // Use rule to extract block
        var ruleRegex = rules[rule];
        var block = ruleRegex.exec(src);

        // Get rule type
        var type = ruleMap[rule] || rule;

        // Get raw text
        var raw = block[0];

        // Break out here to avoid infinite loops
        if(raw.length === 0) {
            break;
        }

        tokens.push({
            type: ruleMap[rule] || rule,
            raw: raw,
            matches: block.slice(1),
        });

        // Update source
        src = src.substring(raw.length);
    }

    return tokens;
}

module.exports = annotateEngine;

Add matches to annotated object


'use strict';

var _ = require('lodash');

function arrayToQueryString(args) {
  var queryString = '?q=' + args.join('+');
  return queryString.substring(0, queryString.length - 1);
}

module.exports = function(config, request, args) {
  var url = config.API_URI + config.SEARCH_URI + arrayToQueryString(args);
  request
    .get({
      url: url,
      headers: {
        'User-Agent': 'request',
      },
    }, function(error, response, body) {
      var result = JSON.parse(body);
      _.forEach(result.items, function(repo) {
        /* jshint camelcase: false */
        console.log(repo.full_name);
        /* jshint camelcase: true */
      });
    });
};

'use strict';

var _ = require('lodash');

function arrayToQueryString(args) {
  return '?q=' + args.join('+');
}

module.exports = function(config, request, args) {
  var url = config.API_URI + config.SEARCH_URI + arrayToQueryString(args);
  request
    .get({
      url: url,
      headers: {
        'User-Agent': 'request',
      },
    }, function(error, response, body) {
      var result = JSON.parse(body);
      _.forEach(result.items, function(repo) {
        /* jshint camelcase: false */
        console.log(repo.full_name);
        /* jshint camelcase: true */
      });
    });
};

Fix even more stupid code.


import { toArray, Class } from './lib/objects';
import { normalizeOperations } from './lib/operations';
import { uuid } from './lib/uuid';

/**
 Transforms represent a set of operations that are applied against a
 source. After a transform has been applied, it should be assigned the
 `result`, a `TransformResult` that represents the result of the transform.

 Transforms are automatically assigned a UUID `id`.

 @class Transform
 @namespace Orbit
 @param {Array}     [operations] Operations to apply
 @param {Object}    [options]
 @param {String}    [options.id] Unique id for this transform (will be assigned a uuid by default)
 @param {Transform} [options.parent] The parent transform that spawned this one.
 @param {Array}     [options.log]
 @constructor
 */
var Transform = Class.extend({
  operations: null,

  init: function(ops, options) {
    this.operations = normalizeOperations( toArray(ops) );

    options = options || {};

    this.id = options.id || uuid();
  },

  isEmpty: function() {
    return this.operations.length === 0;
  }
});

export default Transform;

import { toArray, Class } from './lib/objects';
import { normalizeOperations } from './lib/operations';
import { uuid } from './lib/uuid';

/**
 Transforms represent a set of operations that are applied against a
 source. After a transform has been applied, it should be assigned the
 `result`, a `TransformResult` that represents the result of the transform.

 Transforms are automatically assigned a UUID `id`.

 @class Transform
 @namespace Orbit
 @param {Array}     [operations] Operations to apply
 @param {Object}    [options]
 @param {String}    [options.id] Unique id for this transform (will be assigned a uuid by default)
 @constructor
 */
var Transform = Class.extend({
  operations: null,

  init: function(ops, options) {
    this.operations = normalizeOperations( toArray(ops) );

    options = options || {};

    this.id = options.id || uuid();
  },

  isEmpty: function() {
    return this.operations.length === 0;
  }
});

export default Transform;

Remove unused params from comments.


function NavbarCtrl($scope, $rootScope, $route, $location, Data) {
    $scope.data = Data;

    $scope.isActive = function(route) {

        var path = $location.path();

        if (path === '/')
            path = '/queries';

        return (('/' + route.toLowerCase()) === path);
    }

}

function NavbarCtrl($scope, $rootScope, $route, $location, Data) {
    $scope.data = Data;

    $scope.data.host = 'http://' + window.location.host;

    $scope.isActive = function(route) {

        var path = $location.path();

        if (path === '/')
            path = '/queries';

        return (('/' + route.toLowerCase()) === path);
    }

}

Change hardcoded localhost:9200 to window.location.host


$(function() {

    $('#side-menu').metisMenu();

});

//Loads the correct sidebar on window load,
//collapses the sidebar on window resize.
// Sets the min-height of #page-wrapper to window size
$(function() {
    $(window).on("load resize", function() {
        var topOffset = 50;
        var width = (this.window.innerWidth > 0) ? this.window.innerWidth : this.screen.width;
        if (width < 768) {
            $('div.navbar-collapse').addClass('collapse');
            topOffset = 100; // 2-row-menu
        } else {
            $('div.navbar-collapse').removeClass('collapse');
        }

        var height = ((this.window.innerHeight > 0) ? this.window.innerHeight : this.screen.height) - 1;
        height = height - topOffset;
        if (height < 1) height = 1;
        if (height > topOffset) {
            $("#page-wrapper").css("min-height", (height) + "px");
        }
    });

    var url = window.location;
    var element = $('ul.nav a').filter(function() {
        return this.href == url || url.href.indexOf(this.href) == 0;
    }).addClass('active').parent().parent().addClass('in').parent();
    if (element.is('li')) {
        element.addClass('active');
    }
});

$(function() {

    $('#side-menu').metisMenu();

});

//Loads the correct sidebar on window load,
//collapses the sidebar on window resize.
// Sets the min-height of #page-wrapper to window size
$(function() {
    $(window).on("load resize", function() {
        var topOffset = 50;
        var width = (this.window.innerWidth > 0) ? this.window.innerWidth : this.screen.width;
        if (width < 768) {
            $('div.navbar-collapse').addClass('collapse');
            topOffset = 100; // 2-row-menu
        } else {
            $('div.navbar-collapse').removeClass('collapse');
        }

        var height = ((this.window.innerHeight > 0) ? this.window.innerHeight : this.screen.height) - 1;
        height = height - topOffset;
        if (height < 1) height = 1;
        if (height > topOffset) {
            $("#page-wrapper").css("min-height", (height) + "px");
        }
    });

    var url = window.location;
    var element = $('ul.nav a').filter(function() {
        return this.href == url || url.href.split('#')[0].indexOf(this.href) == 0;
    }).addClass('active').parent().parent().addClass('in').parent();
    if (element.is('li')) {
        element.addClass('active');
    }
});

Fix for active nav links when URL has hash


function extractJSON(rawJson) {
  return rawJson
    .replace(/\s*while\((1|true)\)\s*;?/, '')
    .replace(/\s*for\(;;\)\s*;?/, '')
    .replace(/^[^{\[].+\({/, '{')
    .replace(/}\);?\s*$/, '}');
}

module.exports = extractJSON;

function extractJSON(rawJson) {
  return rawJson
    .replace(/\s*while\((1|true)\)\s*;?/, '')
    .replace(/\s*for\(;;\)\s*;?/, '')
    .replace(/^[^{\[].+\({/, '{')
    .replace(/}\s?\);?\s*$/, '}');
}

module.exports = extractJSON;

Fix the regular expression at the end of raw jsonp.


import React from 'react';

export default class SparklinesBars extends React.Component {

    static propTypes = {
        points: React.PropTypes.arrayOf(React.PropTypes.number).isRequired,
        height: React.PropTypes.number.isRequired,
        style: React.PropTypes.object
    };

    static defaultProps = {
        style: { fill: 'slategray' }
    };

    render() {

        const { points, height, style } = this.props;
        const barWidth = points.length >= 2 ? Math.max(0, points[1].x - points[0].x) : 0;

        return (
            <g>
                {points.map((p, i) =>
                    <rect
                        key={i}
                        x={p.x}
                        y={p.y}
                        width={barWidth}
                        height={Math.max(0, height - p.y)}
                        style={style} />
                )}
            </g>
        )
    }
}

import React from 'react';

export default class SparklinesBars extends React.Component {

    static propTypes = {
        points: React.PropTypes.arrayOf(React.PropTypes.number).isRequired,
        height: React.PropTypes.number.isRequired,
        style: React.PropTypes.object
    };

    static defaultProps = {
        style: { fill: 'slategray' }
    };

    render() {

        const { points, height, style } = this.props;
        const barWidth = points.length >= 2 ? Math.max(0, points[1].x - points[0].x) : 0;

        return (
            <g>
                {points.map((p, i) =>
                    <rect
                        key={i}
                        x={Math.ceil(p.x)}
                        y={Math.ceil(p.y)}
                        width={Math.ceil(barWidth)}
                        height={Math.ceil(Math.max(0, height - p.y))}
                        style={style} />
                )}
            </g>
        )
    }
}

Use Math.ceil to get rounded to px values to remove SVG rendering issues.


var process = function (url, filename, successCallback, errorCallback) {
  if(!filename) {
    filename = '';
  }

  return cordova.exec(successCallback, errorCallback, 'Hazelnut', 'Open', [{
    url: url,
    filename: filename
  }]);
};

window.hazelnutOpen = process;

if(module && module.exports) {
  module.exports = process;
}

var process = function (filename, url, successCallback, errorCallback) {
  if(!filename) {
    filename = url.split('.').pop();
  }

  return cordova.exec(successCallback, errorCallback, 'Hazelnut', 'Open', [{
    url: url,
    filename: filename
  }]);
};

window.hazelnut = {
  open: process
};

if(module && module.exports) {
  module.exports = process;
}

Improve the signature of the function open


({
    setStatus: function(cmp, status) {
        cmp.set("v.status", status);
        this.log(cmp, "\nStatus update: " + status);
    },

    log: function(cmp, log) {
        var l = cmp.get("v.log");
        l += log + "\n";
        cmp.set("v.log", l);
    },

    clearActionAndDefStorage: function(cmp) {
        // def store is not created until a dynamic def is received. if it doesn't exist
        // in aura storage service then create it, clear it (to clear the underlying persistent
        // store), then remove it.
        var defs = $A.storageService.getStorage("ComponentDefStorage");
        var defsCreated = false;
        if (!defs) {
            defsCreated = true;
            defs = $A.storageService.initStorage({
                name: "ComponentDefStorage",
                persistent: true,
                secure: false,
                maxSize: 442368,
                expiration: 10886400,
                debugLogging: true,
                clearOnInit: false
            });
        }

        return Promise.all([$A.storageService.getStorage("actions").clear(), defs.clear()])
            .then(
                function() {
                    if (defsCreated) {
                        $A.storageService.deleteStorage("ComponentDefStorage");
                    }
                }
            )
            .then(
                function() {
                    cmp.helper.log(cmp, "Action and def storage cleared");
                },
                function(e) {
                    cmp.helper.log(cmp, "Error clearing action and def storage: " + e);
                }
            );
    }
})

({
    setStatus: function(cmp, status) {
        cmp.set("v.status", status);
        this.log(cmp, "\nStatus update: " + status);
    },

    log: function(cmp, log) {
        var l = cmp.get("v.log");
        l += log + "\n";
        cmp.set("v.log", l);
    },

    clearActionAndDefStorage: function(cmp) {
        // def store is not created until a dynamic def is received. if it doesn't exist
        // in aura storage service then create it, clear it (to clear the underlying persistent
        // store), then remove it.
        var defs = $A.storageService.getStorage("ComponentDefStorage");
        var defsCreated = false;
        if (!defs) {
            defsCreated = true;
            defs = $A.storageService.initStorage({
                name: "ComponentDefStorage",
                persistent: true,
                secure: false,
                maxSize: 442368,
                expiration: 10886400,
                debugLogging: true,
                clearOnInit: false
            });
        }

        return Promise.all([$A.storageService.getStorage("actions").clear(), defs.clear()])
            .then(
                function() {
                    if (defsCreated) {
                        return $A.storageService.deleteStorage("ComponentDefStorage");
                    }
                }
            )
            .then(
                function() {
                    cmp.helper.log(cmp, "Action and def storage cleared");
                },
                function(e) {
                    cmp.helper.log(cmp, "Error clearing action and def storage: " + e);
                }
            );
    }
})

Fix race condition in test @bug W-3269340@ @rev tbliss@


var async = require( "async" );
var _ = require( "lodash" );
var cta = require( "cta-bus-tracker" );

var busTracker = cta( process.env.CHICAGO_CTA_BUS_API_KEY );

var getRoutes = function ( done ) {

    busTracker.routes( done );

};

var getDirections = function ( routes, done ) {

    var makeDirectionsTask = function ( route ) {

        return function ( parallelDone ) {

            var routeWithDirection = function ( direction ) {

                return {
                    route: route.rt,
                    direction: direction
                };

            };

            busTracker.routeDirections( route.rt, function ( err, data ) {

                if ( err ) {
                    return parallelDone( err );
                }

                data = Array.isArray( data ) ? data : [ data ];
                return parallelDone( null, data.map( routeWithDirection ) );

            } );

        };

    };

    async.parallelLimit( routes.map( makeDirectionsTask ), 5, function ( err, result ) {

        if ( err ) {
            return done( err );
        }

        return done( null, [].concat.apply( [], result ) );

    } );

};

var getStops = function ( routes, done ) {

    var makeStopsTask = function ( route ) {

        return function ( parallelDone ) {

            var makeStopObject = function ( stop ) {

                return {
                    type: "bus",
                    route: route.route,
                    direction: route.direction,
                    name: stop.stpnm,
                    latitude: stop.lat,
                    longitude: stop.lon,
                    systemId: stop.stpid,
                    systemCode: "chicago-cta"
                };

            };

            busTracker.stops( route.route, route.direction, function ( err, data ) {

                if ( err ) {
                    return parallelDone( err );
                }

                data = Array.isArray( data ) ? data : [ data ];

                return parallelDone( null, data.map( makeStopObject ) );

            } );

        };

    };

    async.parallelLimit( routes.map( makeStopsTask ), 5, function ( err, result ) {

        if ( err ) {
            return done( err );
        }

        return done( null, [].concat.apply( [], result ) );

    } );

};

var main = function() {

    var tasks = [ getRoutes, getDirections, getStops ];

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

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

    } );

};

if ( require.main === module ) {

    main();

}

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

var main = function() {

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

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

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

    } );

};

if ( require.main === module ) {

    main();

}

Move this out of here...


module.exports = {
  url: process.env.QA_HOST,
  elements: {
    editContactDetailsButton: 'a[href*="/edit"]',
    auditHistoryTab: 'a[href*="/audit"]',
    telephone: '#field-telephone_number',
    telephoneCountryCode: '#field-telephone_countrycode',
    archiveReason: 'label[for=field-archived_reason-1]',
    unarchiveAnContactButton: 'a[href*="/unarchive"]',
    userName: 'a[href*="/profile"]',
  },

  commands: [
    {
      editContactDetails (telephone, countryCode, number) {
        this.click('@editContactDetailsButton')
        if (number > 1) {
          this.clearValue('@telephoneCountryCode')
          this.setValue('@telephoneCountryCode', countryCode)
        }
        this.clearValue('@telephone')
        this.setValue('@telephone', telephone)
        return this
      },
    },
  ],
}

module.exports = {
  url: process.env.QA_HOST,
  elements: {
    editContactDetailsButton: 'a[href*="/edit"]',
    auditHistoryTab: 'a[href*="/audit"]',
    telephone: '#field-telephone_number',
    telephoneCountryCode: '#field-telephone_countrycode',
    archiveReason: 'label[for=field-archived_reason-1]',
    unarchiveAnContactButton: 'a[href*="/unarchive"]',
    userName: 'a[href*="/profile"]',
  },

  commands: [
    {
      editContactDetails (telephone, countryCode, number) {
        this
          .click('@editContactDetailsButton')
          .waitForElementVisible('@telephoneCountryCode')
          .waitForElementVisible('@telephone')

        if (number > 1) {
          this
            .clearValue('@telephoneCountryCode')
            .setValue('@telephoneCountryCode', countryCode)
        }

        this
          .clearValue('@telephone')
          .setValue('@telephone', telephone)

        return this
      },
    },
  ],
}

Add wait for contact element to be visible before editing


// @flow

import * as React from 'react'
import moment from 'moment-timezone'
import {Alert} from 'react-native'
import {Column, Row} from '../components/layout'
import {ListRow, Detail, Title} from '../components/list'
import type {UpdateType} from './types'

type Props = {
	onPress: string => any,
	update: UpdateType,
}

export class UpdatesRow extends React.PureComponent<Props> {
	_onPress = () => {
		if (!this.props.update.link) {
			Alert.alert('There is nowhere to go for this update')
			return
		}
		this.props.onPress(this.props.update.link)
	}

	render() {
		const {update} = this.props
		const posted = moment(update.datePublished).format('MMM Do YYYY')

		return (
			<ListRow arrowPosition="center" onPress={this._onPress}>
				<Row alignItems="center">
					<Column flex={1}>
						<Title lines={2}>{posted}</Title>
						<Detail lines={3}>{update.title}</Detail>
					</Column>
				</Row>
			</ListRow>
		)
	}
}

// @flow

import * as React from 'react'
import moment from 'moment-timezone'
import {Alert} from 'react-native'
import {Column, Row} from '../components/layout'
import {ListRow, Detail, Title} from '../components/list'
import type {UpdateType} from './types'

type Props = {
	onPress: string => any,
	update: UpdateType,
}

export class UpdatesRow extends React.PureComponent<Props> {
	_onPress = () => {
		if (!this.props.update.link) {
			Alert.alert('There is nowhere to go for this update')
			return
		}
		this.props.onPress(this.props.update.link)
	}

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

		const posted = moment
			.utc(update.datePublished)
			.utcOffset(-6)
			.format('MMM Do YYYY')

		return (
			<ListRow arrowPosition="center" onPress={this._onPress}>
				<Row alignItems="center">
					<Column flex={1}>
						<Title lines={2}>{posted}</Title>
						<Detail lines={3}>{update.title}</Detail>
					</Column>
				</Row>
			</ListRow>
		)
	}
}

Handle UTC formatting of covid newsfeed


module.exports = function(deployer) {
  deployer.deploy(PullPaymentBid);
  deployer.deploy(BadArrayUse);
  deployer.deploy(ProofOfExistence);
  deployer.deploy(SimpleTokenBounty);
  deployer.deploy(CrowdsaleTokenBounty);
  deployer.deploy(Ownable);
  deployer.deploy(LimitFunds);
  deployer.deploy(SecureTargetMock);
  deployer.deploy(InsecureTargetMock);
};

module.exports = function(deployer) {
  deployer.deploy(PullPaymentBid);
  deployer.deploy(BadArrayUse);
  deployer.deploy(ProofOfExistence);
  deployer.deploy(SimpleTokenBounty);
  deployer.deploy(CrowdsaleTokenBounty);
  deployer.deploy(Ownable);
  deployer.deploy(LimitFunds);
  if(deployer.network == 'test'){
    deployer.deploy(SecureTargetMock);
    deployer.deploy(InsecureTargetMock);
  };
};

Deploy mock contracts only for test


import { connect } from 'react-redux';
import { fetchTrends } from 'mastodon/actions/trends';
import Trends from '../components/trends';

const mapStateToProps = state => ({
  trends: state.getIn(['trends', 'items']),
});

const mapDispatchToProps = dispatch => ({
  fetchTrends: () => dispatch(fetchTrends()),
});

export default connect(mapStateToProps, mapDispatchToProps)(Trends);

import { connect } from 'react-redux';
import { fetchTrends } from 'flavours/glitch/actions/trends';
import Trends from '../components/trends';

const mapStateToProps = state => ({
  trends: state.getIn(['trends', 'items']),
});

const mapDispatchToProps = dispatch => ({
  fetchTrends: () => dispatch(fetchTrends()),
});

export default connect(mapStateToProps, mapDispatchToProps)(Trends);

Fix one import being made on mastodon's codebase instead of glitch-soc's


var app = require('app');
var argv = require('optimist').argv;
var dialog = require('dialog');
var path = require('path');

// Quit when all windows are closed and no other one is listening to this.
app.on('window-all-closed', function() {
  if (app.listeners('window-all-closed').length == 1)
    app.quit();
});

// Start the specified app if there is one specified in command line, otherwise
// start the default app.
if (argv._.length > 0) {
  try {
    require(path.resolve(argv._[0]));
  } catch(e) {
    if (e.code == 'MODULE_NOT_FOUND') {
      console.error(e.stack);
      console.error('Specified app is invalid');
      process.exit(1);
    } else {
      throw e;
    }
  }
} else {
  require('./default_app.js');
}

var app = require('app');
var dialog = require('dialog');
var path = require('path');

// Quit when all windows are closed and no other one is listening to this.
app.on('window-all-closed', function() {
  if (app.listeners('window-all-closed').length == 1)
    app.quit();
});

process.argv.splice(1, 0, 'dummyScript');
var argv = require('optimist').argv;

// Start the specified app if there is one specified in command line, otherwise
// start the default app.
if (argv._.length > 0) {
  try {
    require(path.resolve(argv._[0]));
  } catch(e) {
    if (e.code == 'MODULE_NOT_FOUND') {
      console.error(e.stack);
      console.error('Specified app is invalid');
      process.exit(1);
    } else {
      throw e;
    }
  }
} else if (argv.version) {
  console.log(process.versions['atom-shell']);
  process.exit(0);
} else {
  require('./default_app.js');
}

Add --version flag to print atom-shell's version.


var ProseEditorPackage = require('substance/packages/prose-editor/ProseEditorPackage');
var ImagePackage = require('substance/packages/image/ImagePackage');

var SaveCommand = require('substance/ui/SaveCommand');
var SaveTool = require('substance/ui/SaveTool');

module.exports = function(config) {
  // Base package with default rich text nodes
  config.import(ProseEditorPackage);
  // Custom packages
  config.import(ImagePackage, {icon: 'fa-image'});

  // Activate save tool
  config.addCommand(SaveCommand);
  config.addTool(SaveTool, {icon: 'fa-save'});
};

var ProseEditorPackage = require('substance/packages/prose-editor/ProseEditorPackage');
var ImagePackage = require('substance/packages/image/ImagePackage');

var SaveCommand = require('substance/ui/SaveCommand');
var SaveTool = require('substance/ui/SaveTool');

module.exports = {
  name: 'my-prose-editor',
  configure: function(config) {
    // Base package with default rich text nodes
    config.import(ProseEditorPackage);
    // Custom packages
    config.import(ImagePackage, {icon: 'fa-image'});

    // Activate save tool
    config.addCommand(SaveCommand);
    config.addTool(SaveTool, {icon: 'fa-save'});
  }
};

Change layout of package definition.


/**
 * @license
 * Copyright 2015 Google Inc. All Rights Reserved.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 */

CLASS({
  package: 'foam.apps.builder',
  name: 'AppBuilderContext',

  properties: [
    {
      type: 'foam.apps.builder.Identity',
      name: 'identity',
      defaultValue: null,
    },
    {
      model_: 'ArrayProperty',
      subType: 'foam.apps.builder.Identity',
      name: 'identities',
      lazyFactory: function() { return []; },
    },
    {
      model_: 'BooleanProperty',
      name: 'hasSeenDesignerView',
      defaultValue: false,
      hidden: true,
    },
    {
      model_: 'BooleanProperty',
      name: 'appBuilderAnalyticsEnabled',
      label: 'Send anonymous usage data from my apps to the App Builder team ' +
          'to help make App Builder better
Learn more',
      defaultValue: true,
    },
  ],
});

/**
 * @license
 * Copyright 2015 Google Inc. All Rights Reserved.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 */

CLASS({
  package: 'foam.apps.builder',
  name: 'AppBuilderContext',

  requires: [
    'foam.apps.builder.Identity',
  ],

  properties: [
    {
      type: 'foam.apps.builder.Identity',
      name: 'identity',
      lazyFactory: function() {
        return DEBUG ? (
          this.Identity.create({
            id: 'fakeID1234',
            displayName: 'User Q User',
            oauth: '',
            iconUrl: '',
            authType: 'WEB',
          })
        ) : null;
      },

    },
    {
      model_: 'ArrayProperty',
      subType: 'foam.apps.builder.Identity',
      name: 'identities',
      lazyFactory: function() { return []; },
    },
    {
      model_: 'BooleanProperty',
      name: 'hasSeenDesignerView',
      defaultValue: false,
      hidden: true,
    },
    {
      model_: 'BooleanProperty',
      name: 'appBuilderAnalyticsEnabled',
      label: 'Send anonymous usage data from my apps to the App Builder team ' +
          'to help make App Builder better
Learn more',
      defaultValue: true,
    },
  ],
});

DEBUG patch for appBuilder identity, so it can fake it when running in indexDebug.html


/* global _postal, SubscriptionDefinition, _config, _ */

var ChannelDefinition = function( channelName, bus ) {
	this.bus = bus;
	this.channel = channelName || _config.DEFAULT_CHANNEL;
};

ChannelDefinition.prototype.subscribe = function() {
	return this.bus.subscribe( {
		channel: this.channel,
		topic: ( arguments.length === 1 ? arguments[ 0 ].topic : arguments[ 0 ] ),
		callback: ( arguments.length === 1 ? arguments[ 0 ].callback : arguments[ 1 ] )
	} );
};

/*
    publish( envelope [, callback ] );
    publish( topic, data [, callback ] );
*/
ChannelDefinition.prototype.publish = function() {
	var envelope = {};
	var callback;
	if ( typeof arguments[ 0 ] === "string" ) {
		envelope.topic = arguments[ 0 ];
		envelope.data = arguments[ 1 ];
		callback = arguments[ 2 ];
	} else {
		envelope = arguments[ 0 ];
		callback = arguments[ 1 ];
	}
	if ( typeof envelope !== "object" ) {
		throw new Error( "The first argument to ChannelDefinition.publish should be either an envelope object or a string topic." );
	}
	envelope.channel = this.channel;
	this.bus.publish( envelope, callback );
};

/* global _postal, SubscriptionDefinition, _config, _ */

var ChannelDefinition = function( channelName, bus ) {
	this.bus = bus;
	this.channel = channelName || _config.DEFAULT_CHANNEL;
};

ChannelDefinition.prototype.subscribe = function() {
	return this.bus.subscribe( {
		channel: this.channel,
		topic: ( arguments.length === 1 ? arguments[ 0 ].topic : arguments[ 0 ] ),
		callback: ( arguments.length === 1 ? arguments[ 0 ].callback : arguments[ 1 ] )
	} );
};

/*
    publish( envelope [, callback ] );
    publish( topic, data [, callback ] );
*/
ChannelDefinition.prototype.publish = function() {
	var envelope = {};
	var callback;
	if ( typeof arguments[ 0 ] === "string" ) {
		envelope.topic = arguments[ 0 ];
		envelope.data = arguments[ 1 ];
		callback = arguments[ 2 ];
	} else {
		envelope = arguments[ 0 ];
		callback = arguments[ 1 ];
	}
	if ( typeof envelope !== "object" ) {
		throw new Error( "The first argument to ChannelDefinition.publish should be either an envelope object or a string topic." );
	}
	envelope.headers = _.extend( envelope.headers || {}, _.pick( _config, [ "resolverNoCache" ] ) );
	envelope.channel = this.channel;
	this.bus.publish( envelope, callback );
};

Allow resolverNoCache config option to be passed to envelope headers


// ANSI color code outputs for strings

var ANSI_CODES = {
  "off": 0,
  "bold": 1,
  "underline": 4,
  "blink": 5,
  "hidden": 8,
  "black": 30,
  "red": 31,
  "green": 32,
  "yellow": 33,
  "blue": 34,
  "magenta": 35,
  "cyan": 36,
  "white": 37,
  "black_bg": 40,
  "red_bg": 41,
  "green_bg": 42,
  "yellow_bg": 43,
  "blue_bg": 44,
  "magenta_bg": 45,
  "cyan_bg": 46,
  "white_bg": 47
};

exports.set = function(str, color) {
  if(!color) return str;

  var color_attrs = color.split("+");
  var ansi_str = "";
  for(var i=0, attr; attr = color_attrs[i]; i++) {
    ansi_str += "\033[" + ANSI_CODES[attr] + "m";
  }
  ansi_str += str + "\033[0m";
  return ansi_str;
};

// ANSI color code outputs for strings

var ANSI_CODES = {
  "off": 0,
  "bold": 1,
  "italic": 3,
  "underline": 4,
  "blink": 5,
  "inverse": 7,
  "hidden": 8,
  "black": 30,
  "red": 31,
  "green": 32,
  "yellow": 33,
  "blue": 34,
  "magenta": 35,
  "cyan": 36,
  "white": 37,
  "black_bg": 40,
  "red_bg": 41,
  "green_bg": 42,
  "yellow_bg": 43,
  "blue_bg": 44,
  "magenta_bg": 45,
  "cyan_bg": 46,
  "white_bg": 47
};

exports.set = function(str, color) {
  if(!color) return str;

  var color_attrs = color.split("+");
  var ansi_str = "";
  for(var i=0, attr; attr = color_attrs[i]; i++) {
    ansi_str += "\033[" + ANSI_CODES[attr] + "m";
  }
  ansi_str += str + "\033[" + ANSI_CODES["off"] + "m";
  return ansi_str;
};

Add italic and inverse ansi codes to definition


var ldap = require('ldapjs');
var assert = require('assert');
var options = require('./options-handler').options;
var log = require('./logger').log;

exports.login = function(uid,password,successCallback,errorCallback) {
  var client = ldap.createClient({
    url: options.ldap.server
  });
  client.bind(options.ldap.userField+'='+uid+','+options.ldap.bindDN, password, function(err) {
    if (err) {
      log.error({error:err});
      errorCallback(err.message);
    }
    client.unbind(function(err2) {
      if (err) {
        // Already reported this error.
      } else if (err2) {
        log.error({error:err2});
        errorCallback(err2.message);
      } else {
        successCallback();
      }
    });
  });
};

var ldap = require('ldapjs');
var assert = require('assert');
var options = require('./options-handler').options;
var log = require('./logger').log;

exports.login = function(uid,password,successCallback,errorCallback) {
  var client = ldap.createClient({
    url: options.ldap.server
  });
  client.bind(options.ldap.userField+'='+uid+','+options.ldap.userDN, password, function(err) {
    if (err) {
      log.error({error:err});
      errorCallback(err.message);
    }
    client.unbind(function(err2) {
      if (err) {
        // Already reported this error.
      } else if (err2) {
        log.error({error:err2});
        errorCallback(err2.message);
      } else {
        successCallback();
      }
    });
  });
};

Fix bug in ldap login.


import events from './events';
import tweets from './tweets';
import wrap from '../middleware/wrap';

/**
 * @param {Function} method
 */
function makeHandler(method) {
    /**
     * @param {http.Request} request
     * @param {http.Response} response
     */
    return function(request, response) {
        let promise = method(request, response);
        if (!(promise instanceof Promise)) {
            promise = Promise.resolve(promise);
        }

        promise
        .then(result => {
            // if the result is an object or an array, send it down as JSON
            if (result instanceof Object || result instanceof Array) {
                return response.json(result);
            }

            response.send(result);
        })
        .catch(error => {
            // unless our resource set the statusCode, let's set it too 500
            if (response.statusCode === 200) {
                response.status(500);
            }

            response.send({ error: error.message });
        })
    }
}

export default { makeHandler };
export { events, tweets };

/**
 * @author jjacobson93
 * @date 4/23/2016
 *
 * Main API file
 */

import events from './events';
import tweets from './tweets';
import wrap from '../middleware/wrap';

/**
 * This wraps an `async` function that returns a promise
 * and sends a response to the requester
 *
 * @param {Function} method
 */
function makeHandler(method) {
    /**
     * @param {http.Request} request
     * @param {http.Response} response
     */
    return function(request, response) {
        let promise = method(request, response);
        if (!(promise instanceof Promise)) {
            promise = Promise.resolve(promise);
        }

        promise
        .then(result => {
            // if the result is an object or an array, send it down as JSON
            if (result instanceof Object || result instanceof Array) {
                return response.json(result);
            }

            response.send(result);
        })
        .catch(error => {
            // unless our resource set the statusCode, let's set it too 500
            if (response.statusCode === 200) {
                response.status(500);
            }

            response.send({ error: error.message });
        })
    }
}

export default { makeHandler };
export { events, tweets };

Add more docs to API file


require('dashboard/core');

Dashboard.Repository = DS.Model.extend({
  name: DS.attr('string'),
  full_name: DS.attr('string'),
  description: DS.attr('string'),
  html_url: DS.attr('string'),
  homepage: DS.attr('string'),
  watchers: DS.attr('number'),
  forks: DS.attr('number'),
  language: DS.attr('string'),
  owner: function() {
    return this.get('data.owner')
  }.property('data')
});

Dashboard.Event = DS.Model.extend({
  type: DS.attr('string'),
  created_at: DS.attr('string'),
  actor: function() { return this.get('data.actor'); }.property('data.actor'),
  repo: function() { return this.get('data.repo'); }.property('data.repo'),
  org: function() { return this.get('data.org'); }.property('data.org'),
  payload: function() { return this.get('data.payload'); }.property('data.payload')
});

Dashboard.User = DS.Model.extend({
  primaryKey: 'login',
  login: DS.attr('string'),
  avatar_url: DS.attr('string'),
  name: DS.attr('string'),
  blog: DS.attr('string'),
  email: DS.attr('string'),
  bio: DS.attr('string'),
  public_repos: DS.attr('number'),
  followers: DS.attr('number'),
  following: DS.attr('number'),
  html_url: DS.attr('string'),
  created_at: DS.attr('string')
});

require('dashboard/core');

Dashboard.Repository = DS.Model.extend({
  name: DS.attr('string'),
  full_name: DS.attr('string'),
  description: DS.attr('string'),
  html_url: DS.attr('string'),
  homepage: DS.attr('string'),
  watchers: DS.attr('number'),
  forks: DS.attr('number'),
  language: DS.attr('string'),
  updated_at: DS.attr('date'),
  owner: function() {
    return this.get('data.owner')
  }.property('data')
});

Dashboard.Event = DS.Model.extend({
  type: DS.attr('string'),
  created_at: DS.attr('string'),
  actor: function() { return this.get('data.actor'); }.property('data.actor'),
  repo: function() { return this.get('data.repo'); }.property('data.repo'),
  org: function() { return this.get('data.org'); }.property('data.org'),
  payload: function() { return this.get('data.payload'); }.property('data.payload')
});

Dashboard.User = DS.Model.extend({
  primaryKey: 'login',
  login: DS.attr('string'),
  avatar_url: DS.attr('string'),
  name: DS.attr('string'),
  blog: DS.attr('string'),
  email: DS.attr('string'),
  bio: DS.attr('string'),
  public_repos: DS.attr('number'),
  followers: DS.attr('number'),
  following: DS.attr('number'),
  html_url: DS.attr('string'),
  created_at: DS.attr('string')
});

Add updated_at attribute for Dashboard.Repository


import 'babel-polyfill';

import React from 'react';
import ReactDOM from 'react-dom';

import MainScreen from './react/MainScreen';

import config from './config';
import SculptureApp from './app';

window.onload = () => {
  const manifest = chrome.runtime.getManifest();
  console.log(`Version: ${manifest.version}`);

  const app = new SculptureApp(config);
  ReactDOM.render(<MainScreen app={app} restart={() => chrome.runtime.reload()}/>, document.getElementById('anyware-root'));
};

import 'babel-polyfill';

import React from 'react';
import ReactDOM from 'react-dom';

import MainScreen from './react/MainScreen';

import config from './config';
import localConfig from '../config';
import SculptureApp from './app';

window.onload = () => {
  const manifest = chrome.runtime.getManifest();
  console.log(`Version: ${manifest.version}`);

  config.applyLocalConfig(localConfig);
  const app = new SculptureApp(config);
  ReactDOM.render(<MainScreen app={app} restart={() => chrome.runtime.reload()}/>, document.getElementById('anyware-root'));
};

Allow for config overrides without recompiling bundles


Refactor to follow style guidelines


import React from 'react';
import PropTypes from 'prop-types';

export default class QuillDynamicToolbarIcon extends React.Component {
  constructor(props) {
    super(props);

    this.onClick = this.onClick.bind(this);
  }

  onClick() {
    const { onClick } = this.props;
    if (onClick) onClick();
  }

  render() {
    const { icon } = this.props;
    if (React.isValidElement(icon)) {
      return (
        <button onClick={this.onClick}>
          {icon}
        </button>
      );
    }

    return (
      <button className={`ql_${icon}`}>
        <span>
          {icon.toUpperCase()}
        </span>
      </button>
    );
  }
}

QuillDynamicToolbarIcon.propTypes = {
  icon: PropTypes.oneOfType([
    PropTypes.string,
    PropTypes.element
  ]),
  onClick: PropTypes.func
};

QuillDynamicToolbarIcon.defaultProps = {
  icon: <span />,
  onClick: null
};

import React from 'react';
import PropTypes from 'prop-types';

export default class QuillDynamicToolbarIcon extends React.Component {
  constructor(props) {
    super(props);

    this.onClick = this.onClick.bind(this);
  }

  onClick() {
    const { onClick } = this.props;
    if (onClick) onClick();
  }

  render() {
    const { icon } = this.props;
    if (React.isValidElement(icon)) {
      return (
        <button onClick={this.onClick}>
          {icon}
        </button>
      );
    }

    return (
      <button className={`ql_${icon}`} onClick={this.onClick}>
        <span>
          {icon.toUpperCase()}
        </span>
      </button>
    );
  }
}

QuillDynamicToolbarIcon.propTypes = {
  icon: PropTypes.oneOfType([
    PropTypes.string,
    PropTypes.element
  ]),
  onClick: PropTypes.func
};

QuillDynamicToolbarIcon.defaultProps = {
  icon: <span />,
  onClick: null
};

Fix ToolbarIcon does not trigger onClick


$(function() {
    $("#class-table tr td:first-child").each(function() {
        var t = this.textContent;
        if (t.length > 30) {
            var elipsized = t.substring(0, 16) + "..." + t.substring(t.length - 13, t.length);
            $(this).html($("<span/>").attr({"title": t}).html(elipsized));
        }
    });
    changeQueryType();
});

function changeQueryType() {
    var transition = {
            "classes": ["Class name:"],
            "manifests": ["Header:", "Value:"]
    }[$("#qtype")[0].value];
    $("#q2").css("display", transition[1]? "inline": "none");
    $("label[for='q']").text(transition[0]);
    $("label[for='q2']").text(transition[1] || "");
}

$(function() {
    $("#class-table tr td:first-child").each(function() {
        var t = this.textContent;
        if (t.length > 30) {
            var elipsized = t.substring(0, 16) + "..." + t.substring(t.length - 13, t.length);
            $(this).html($("<span/>").attr({"title": t}).html(elipsized));
        }
    });
    changeQueryType();
});

function changeQueryType() {
    var transition = {
            "classes": ["Class name:"],
            "manifests": ["Header:", "Value:"]
    }[$("#qtype")[0].value];
    $("#q2").css("display", transition[1]? "inline": "none").prop("disabled", !transition[1]);
    $("label[for='q']").text(transition[0]);
    $("label[for='q2']").text(transition[1] || "");
}

Set q2 field as disabled when invisible


'use strict'

const config = require(__dirname + '/../config')
const privateKey = require('fs').readFileSync('staticman_key', 'utf8')
const NodeRSA = require('node-rsa')
const key = new NodeRSA()

key.importKey(privateKey)

module.exports = ((req, res) => {
  try {
    const encryptedText = key.encrypt(req.params.text, 'base64')

    res.send(encryptedText)
  } catch (err) {
    res.status(500).send('Could not encrypt text')
  }
})

'use strict'

const config = require(__dirname + '/../config')
const NodeRSA = require('node-rsa')
const key = new NodeRSA()

key.importKey(config.get('rsaPrivateKey'))

module.exports = ((req, res) => {
  try {
    const encryptedText = key.encrypt(req.params.text, 'base64')

    res.send(encryptedText)
  } catch (err) {
    res.status(500).send('Could not encrypt text')
  }
})

Read private key from config


var NeufundTestToken = artifacts.require("./NeufundTestToken.sol");
var Crowdsale = artifacts.require("./Crowdsale.sol");

module.exports = function (deployer) {
    /**
     * address ifSuccessfulSendTo,
     * uint fundingGoalInEthers,
     * uint durationInMinutes,
     * uint etherCostOfEachToken,
     * Token addressOfTokenUsedAsReward
     */
    deployer.then(()=> {
        return NeufundTestToken.deployed();
    }).then((instance)=> {
        console.log(instance.address);
        var ifSuccessfulSendTo = "0xb67fb67eb9e4700c90f2ab65c8ecbb3b687d49c7";
        var fundingGoalInEthers = 1;
        var durationInMinutes = 1;
        var weiCostOfEachToken = 1;
        var addressOfTokenUsedAsReward = instance.address;
        return deployer.deploy(Crowdsale,
            ifSuccessfulSendTo,
            fundingGoalInEthers,
            durationInMinutes,
            weiCostOfEachToken,
            addressOfTokenUsedAsReward);
    });
};

var NeufundTestToken = artifacts.require("./NeufundTestToken.sol");
var Crowdsale = artifacts.require("./Crowdsale.sol");

module.exports = function (deployer) {
    /**
     * address ifSuccessfulSendTo,
     * uint fundingGoalInEthers,
     * uint durationInMinutes,
     * uint weiCostOfEachToken,
     * Token addressOfTokenUsedAsReward
     */
    deployer.then(()=> {
        return NeufundTestToken.deployed();
    }).then((instance)=> {
        console.log(instance.address);
        var ifSuccessfulSendTo = "0xb67fb67eb9e4700c90f2ab65c8ecbb3b687d49c7";
        var fundingGoalInEthers = 1;
        var durationInMinutes = 1;
        var weiCostOfEachToken = 1;
        var addressOfTokenUsedAsReward = instance.address;
        return deployer.deploy(Crowdsale,
            ifSuccessfulSendTo,
            fundingGoalInEthers,
            durationInMinutes,
            weiCostOfEachToken,
            addressOfTokenUsedAsReward);
    });
};

Change contructor parameter in migrations


/* global define */

define({
    HEADER_TITLE: "Outline",

    TOOLBAR_ICON_TOOLTIP: "Onglet Outline",

    BUTTON_SETTINGS: "Configurer Outline",
    BUTTON_MOVE: "Changer la position de Outline",
    BUTTON_CLOSE: "Fermer Outline",

    COMMAND_SORT: "Liste courte",
    COMMAND_UNNAMED: "Monter les fonctions anonymess",
    COMMAND_ARGS: "Montrer les paramètres"
});

/* Last translated for 41a691e45e34f3ebde20bc5e3fdca8014871490d */

Add commithash to french translation


import fs from 'fs';
import path from 'path';
import handlebars from 'handlebars';
import console from 'node-custom-console';

const cnsl = console('builder');

exports.build = (eventName, payload) => new Promise((resolve, reject) => {
  let source;
  try {
    source = fs.readFileSync(
        path.resolve(path.join(
          __dirname,
          `../templates/${eventName}.hbs`,
        )),
        'utf-8',
      );
  } catch (e) {
    cnsl.log('ReadFile Error.');
    reject(e);
  }
  const template = handlebars.compile(source);
  const message = template(payload);
  cnsl.info(message);
  resolve(message);
});

import fs from 'fs';
import path from 'path';
import handlebars from 'handlebars';
import console from 'node-custom-console';

const cnsl = console('builder');

exports.build = (eventName, payload) => new Promise((resolve, reject) => {
  try {
    fs.readFile(
      path.resolve(path.join(
        __dirname,
        `../templates/${eventName}.hbs`,
      )),
      'utf-8',
      (err, source) => {
        if (err) {
          reject(err);
        } else {
          const template = handlebars.compile(source);
          const message = template(payload);
          cnsl.info(message);
          resolve(message);
        }
      },
    );
  } catch (e) {
    reject(e);
  }
});

Use readFile instead of readFileSync


import http from '../http';

export default class collections {

    static getAll() {
        return http.get(`/zebedee/collections`)
            .then(response => {
                return response;
            })
    }

}

import http from '../http';

export default class collections {

    static getAll() {
        return http.get(`/zebedee/collections`)
            .then(response => {
                return response;
            })
    }

    static addAPIDataset(collectionID, instanceID) {
        // return http.put(`/zebedee/collections/${collectionID}/dataset/${instanceID}`, body , true)
        //     .then(response => {
        //         return response;
        //     })

        // mocked ok response from Zebedee
        return new Promise(resolve => {
            setTimeout(() => { resolve(); }, 2000);
        })

    }

    static removeAPIDataset(collectionID, instanceID) {
        // return http.delete(`/zebedee/collections/${collectionID}/dataset/${instanceID}`, true)
        //     .then(response => {
        //         return response;
        //     })

        // mocked ok response from Zebedee
        return new Promise(resolve => {
            setTimeout(() => { resolve(); }, 1000);
        })

    }

}

Add add and remove methods - currently returns promise after set time until api is ready


import React, {PropTypes} from 'react'
import {render} from 'react-dom'
import {getContextualizer} from 'react-context-props'
import {equal} from 'assert'
import themeable from './themeable'

const root = document.createElement('div')
document.body.appendChild(root)

describe('themeable', () => {
  it('gets the prop from context', () => {
    const Theme = getContextualizer({
      customizations: PropTypes.shape({
        informal: PropTypes.bool
      })
    })

    function Salutation ({ formality }) {
      return

        {formality === 'formal' ? 'Greetings' : 'Hello'}

    }

    const ThemeableSalutation = themeable((customizations, props) => ({
      formality: customizations.informal ? 'informal' : 'formal'
    }))(Salutation)

    render(
      <Theme customizations={{informal: true}}>

          <ThemeableSalutation />

      </Theme>,
      root
    )

    equal(
      root.querySelector('#salutation').textContent.trim(),
      'Hello'
    )
  })
})

import React, {PropTypes} from 'react'
import {render} from 'react-dom'
import {getContextualizer} from 'react-context-props'
import {equal} from 'assert'
import themeable from './themeable'

describe('themeable', () => {
  it('gets the prop from context', () => {
    const root = document.createElement('div')
    document.body.innerHTML = ''
    document.body.appendChild(root)

    const Theme = getContextualizer({
      customizations: PropTypes.shape({
        informal: PropTypes.bool
      })
    })

    function Salutation ({ formality }) {
      return

        {formality === 'formal' ? 'Greetings' : 'Hello'}

    }

    const ThemeableSalutation = themeable((customizations, props) => ({
      formality: customizations.informal ? 'informal' : 'formal'
    }))(Salutation)

    render(
      <Theme customizations={{informal: true}}>

          <ThemeableSalutation />

      </Theme>,
      root
    )

    equal(
      root.querySelector('#salutation').textContent.trim(),
      'Hello'
    )
  })
})

Improve test structure for themeable


import { Component } from 'substance'

export default class ManuscriptComponent extends Component {

  render($$) {
    const article = this.props.node
    let el = $$('div')
      .addClass('sc-article')
      .attr('data-id', article.id)

    // front is mandatory
    const front = article.findChild('front')
    const FrontComponent = this.getComponent('front')
    el.append($$(FrontComponent, {
      node: front
    }).ref('front'))

    // body is optional
    // TODO: do we really want this? Otherwise we need to change TextureJATS
    // and create an empty body on import
    const body = article.findChild('body')
    if (body) {
      el.append($$(this.getComponent('separator'), {
        label: 'manuscript-start'
      }))
      const BodyComponent = this.getComponent('body')
      el.append($$(BodyComponent, {
        node: body
      }).ref('body'))
      el.append($$(this.getComponent('separator'), {
        label: 'manuscript-end'
      }))
    }

    const back = article.findChild('back')
    if (back) {
      const BackComponent = this.getComponent('back')
      el.append($$(BackComponent, {
        node: back
      }).ref('back'))
    }

    return el
  }

}

import { Component } from 'substance'

export default class ManuscriptComponent extends Component {

  render($$) {
    const article = this.props.node
    let el = $$('div')
      .addClass('sc-article')
      .attr('data-id', article.id)

    // front is mandatory
    const front = article.findChild('front')
    const FrontComponent = this.getComponent('front')
    el.append($$(FrontComponent, {
      node: front
    }).ref('front'))

    // body is optional
    // TODO: do we really want this? Otherwise we need to change TextureJATS
    // and create an empty body on import
    const body = article.findChild('body')
    if (body) {
      // el.append($$(this.getComponent('separator'), {
      //   label: 'manuscript-start'
      // }))
      const BodyComponent = this.getComponent('body')
      el.append($$(BodyComponent, {
        node: body
      }).ref('body'))
      // el.append($$(this.getComponent('separator'), {
      //   label: 'manuscript-end'
      // }))
    }

    const back = article.findChild('back')
    if (back) {
      const BackComponent = this.getComponent('back')
      el.append($$(BackComponent, {
        node: back
      }).ref('back'))
    }

    return el
  }

}

Disable separators for a cleaner UI.


var codeMelon = codeMelon || {};
codeMelon.games = codeMelon.games || {};

codeMelon.games.AppView = Backbone.View.extend({
    initialize: function() {
        var _this = this;

        _.bindAll(_this,
            'render'
        );

        _this.render();
    },

    render: function() {
        var _this = this,
            content = '
JavaScript generated content
';

        _this.$el.find('.col-md-12').append(content);
    }
});

var codeMelon = codeMelon || {};
codeMelon.games = codeMelon.games || {};

codeMelon.games.AppView = Backbone.View.extend({
    initialize: function(options) {
        var _this = this;

        _.bindAll(_this,
            'render'
        );

        _this.render();
    },

    render: function() {
        var _this = this,
            content = '
JavaScript generated content
';

        _this.$el.find('.col-md-12').append(content);
    }
});

Add options input to initialize method.


// Overwrite this file in your application /app/assets/javascripts/comfortable_mexican_sofa/admin/application.js
//= require requirejs/require
//= require require_config

window.CMS.wysiwyg = function() {
  require([
    'mas-editor'
  ], function (
    masEditor
  ) {
    var markdownEditorContentNode = document.querySelector('.js-markdown-editor-content'),
        classActive = 'is-active',
        htmlEditorNode;
    'use strict';

    if(!markdownEditorContentNode) return;

    markdownEditorContentNode.value = markdownEditorContentNode.value.split("\n").map(function(e) { return e.trim() }).join("\n");

    masEditor.init({
      editorContainer: document.querySelector('.l-editor'),
      cmsFormNode: document.querySelector('#edit_page') || document.querySelector('#new_page'),
      toolbarNode: document.querySelector('.js-toolbar'),
      htmlEditorNode: document.querySelector('.js-html-editor'),
      htmlEditorContentNode: document.querySelector('.js-html-editor-content'),
      markdownEditorNode: document.querySelector('.js-markdown-editor'),
      markdownEditorContentNode: markdownEditorContentNode,
      switchModeButtonNodes: document.querySelectorAll('.js-switch-mode'),
      classActive : 'is-active'
    });
  });
};

// Overwrite this file in your application /app/assets/javascripts/comfortable_mexican_sofa/admin/application.js
//= require requirejs/require
//= require require_config

window.CMS.wysiwyg = function() {
  require([
    'mas-editor'
  ], function (
    masEditor
  ) {
    'use strict';

    var markdownEditorContentNode = document.querySelector('.js-markdown-editor-content');

    if(!markdownEditorContentNode) return;

    markdownEditorContentNode.value = markdownEditorContentNode.value.split("\n").map(function(e) { return e.trim() }).join("\n");

    masEditor.init({
      editorContainer: document.querySelector('.l-editor'),
      cmsFormNode: document.querySelector('#edit_page') || document.querySelector('#new_page'),
      toolbarNode: document.querySelector('.js-toolbar'),
      htmlEditorNode: document.querySelector('.js-html-editor'),
      htmlEditorContentNode: document.querySelector('.js-html-editor-content'),
      markdownEditorNode: document.querySelector('.js-markdown-editor'),
      markdownEditorContentNode: markdownEditorContentNode,
      switchModeButtonNodes: document.querySelectorAll('.js-switch-mode')
    });
  });
};

Tidy setup code and remove unnecessary variables


import React from 'react';
import {default as KataGroupsData} from '../katagroups.js';

export default class KataGroupsComponent extends React.Component {

  static propTypes = {
    kataGroups: React.PropTypes.instanceOf(KataGroupsData).isRequired
  };

  render() {
    const {kataGroups} = this.props;
    return (

        Menu

Kata groups

              {kataGroups.groups.map(kataGroup =>

                {kataGroup.name} ({kataGroup.katasCount})

)}

    );
  }
}

import React from 'react';

export default class KataGroupsComponent extends React.Component {

  render() {
    const {kataGroups} = this.props;
    return (

        Menu

Kata groups

              {kataGroups.groups.map(kataGroup =>

                {kataGroup.name} ({kataGroup.katasCount})

)}

    );
  }
}

Remove propTypes in favor of real tests that verify the same thing.


import Vue from 'vue'
import Message from './Message.vue'
import Modal from './Modal.vue'

const MessageConstructor = Vue.extend(Message)

const Msg = function (option) {
  let instance
  option = option || {}

  if (typeof option === 'string') {
    option = {
      message: option
    }
  }

  instance = new MessageConstructor({
    data: option
  })
  instance.vm = instance.$mount()
  document.body.appendChild(instance.vm.$el)
  instance.vm.visible = true
}

const success = msg => Msg(msg)
const info = msg => {
  Msg({
    type: 'info',
    message: msg
  })
}
const warn = msg => {
  Msg({
    type: 'warn',
    message: msg
  })
}
const error = (msg, close) => {
  if (close === undefined) close = true

  Msg({
    type: 'error',
    message: msg.msg || msg,
    showClose: close
  })
}
const modal = function (option = {}) {
  let instance
  if (typeof option === 'string') {
    option = {
      content: option
    }
  }

  const Constructor = Vue.extend(Modal)
  instance = new Constructor({
    data: option
  })
  instance.vm = instance.$mount()
  document.body.appendChild(instance.vm.$el)
  instance.vm.visible = true
}

export { Msg, success, info, warn, error, modal }

import Vue from 'vue'
import Message from './Message.vue'
import Modal from './Modal.vue'

const MessageConstructor = Vue.extend(Message)

const Msg = function (option) {
  let instance
  option = option || {}

  if (typeof option === 'string') {
    option = {
      message: option
    }
  }

  instance = new MessageConstructor({
    data: option
  })
  instance.vm = instance.$mount()
  document.body.appendChild(instance.vm.$el)
  instance.vm.visible = true
}

const success = msg => Msg(msg)
const info = msg => {
  Msg({
    type: 'info',
    message: msg
  })
}
const warn = msg => {
  Msg({
    type: 'warn',
    message: msg
  })
}
const error = (msg = '', close) => {
  if (close === undefined) close = true

  Msg({
    type: 'error',
    message: msg.msg || msg,
    showClose: close
  })
}
const modal = function (option = {}) {
  let instance
  if (typeof option === 'string') {
    option = {
      content: option
    }
  }

  const Constructor = Vue.extend(Modal)
  instance = new Constructor({
    data: option
  })
  instance.vm = instance.$mount()
  document.body.appendChild(instance.vm.$el)
  instance.vm.visible = true
}

export { Msg, success, info, warn, error, modal }

Fix error when msg is undefined.


export const ADD_ITEM = 'ADD_ITEM';
export const COMPLETE_ITEM = 'COMPLETE_ITEM';

export function addItem(text) {
  return {
    type: ADD_ITEM,
    value: text
  };
}

export function completeItem(index) {
  return {
    type: COMPLETE_ITEM,
    id: index
  };
}

//========== async ==============================
export const SEND_NEW_ITEM = 'SEND_NEW_ITEM';
export const REQUEST_ITEMS = 'REQUEST_ITEMS';
export const RECEIVE_ITEMS = 'RECEIVE_ITEMS';

export function sendNewItem(text) {
  return {
    type: SEND_NEW_ITEM,
    value: text
  };
}

export function requestItems() {
  return {
    type: REQUEST_ITEMS
  };
}

export function receiveItems(items) {
  return {
    type: RECEIVE_ITEMS,
    itemList: items,
    receivedAt: Date.now()
  };
}

// ===== thunk action creators ==================
export function sendNewItemAPI(text) {
  return dispatch => {
    dispatch(sendNewItem(text));
    // send item to server
  }
}

export function requestItemsAPI() {
  return dispatch => {
    dispatch(requestItems());
    // return fetch(`http://localhost:5000/api`)
    //   .then(response => response.json())
    //   .then(json => dispatch(receiveItems(json)));
  };
}

Add basic template for async action creators


// Place all the behaviors and hooks related to the matching controller here.
// All this logic will automatically be available in application.js.

var ready = function() {
    // localize times
    $('.localize').each(function () {
        var element = $(this);
        element.html(moment(element.html()).format('LLL'));
    });

    // Fix for Material UI's <AutoComplete /> applying .browser-default
    // to a wrapping div instead of the input element.
    $('div.browser-default').children('input[type=text]').each(function() {
        $(this).addClass('browser-default');
    });

    $('.tooltipped').tooltip();
};

$(document).ready(ready);
$(document).on('turbolinks:load', function() {
    setTimeout(ready, 250)
});

// Place all the behaviors and hooks related to the matching controller here.
// All this logic will automatically be available in application.js.

var ready = function() {
    // localize times
    $('.localize').each(function () {
        var element = $(this);
        element.html(moment(element.html()).format('LLL'));
    });

    // Fix for Material UI's <AutoComplete /> applying .browser-default
    // to a wrapping div instead of the input element.
    $('div.browser-default').children('input[type=text]').each(function() {
        $(this).addClass('browser-default');
    });

    $('.tooltipped').tooltip();
};

$(document).ready(ready);
$(document).on('turbolinks:load', function() {
    setTimeout(ready, 250);
});

Add missing semicolon to javascript statement.


const Score = require('../../../lib/score');
const _ = require('lodash');
const scorePerCommit = 0.25;

module.exports = new Score ('push_event', {
  fn: (event, actor) => {
    const actorEmail = _.get(actor, 'sources.github.email');
    if (_.get(event, 'type') === 'PushEvent') {
      let score = 0;
      _.each(event.payload.commits, commit => {
        if (commit.author.email === actorEmail) score += scorePerCommit;
      });
      return score;
    }
  },
  color: '#610977',
  toHTML: (event, actor) => {
    const sha = _.get(event._source, 'payload.head');
    const repoName = _.get(event._source, 'repo.name');
    return `Pushed ${event._source.relay_total_score / scorePerCommit} commits to <a href="https://github.com/${repoName}/commit/${sha}">${repoName}</a>`;
  }
});

const Score = require('../../../lib/score');
const _ = require('lodash');
const scorePerCommit = 0.25;

module.exports = new Score ('push_event', {
  fn: (event, actor) => {
    const actorEmail = _.get(actor, 'sources.github.email');
    if (_.get(event, 'type') === 'PushEvent') {
      let score = 0;
      _.each(event.payload.commits, commit => {
        if (commit.author.email === actorEmail) score += scorePerCommit;
      });
      return score;
    }
  },
  color: '#98C261',
  toHTML: (event, actor) => {
    const sha = _.get(event._source, 'payload.head');
    const repoName = _.get(event._source, 'repo.name');
    return `Pushed ${event._source.relay_total_score / scorePerCommit} commits to <a href="https://github.com/${repoName}/commit/${sha}">${repoName}</a>`;
  }
});

Make push events more obvious


export default ngModule => {
  ngModule.config(addWrappers);

  function addWrappers(formlyConfigProvider) {
    formlyConfigProvider.setWrapper([
      {
        name: 'bootstrapLabel',
        template: require('./label.html'),
        apiCheck: check => ({
          templateOptions: {
            label: check.string,
            required: check.bool.optional,
            labelSrOnly: check.bool.optional,
          }
        })
      },
      {name: 'bootstrapHasError', template: require('./has-error.html')}
    ]);
  }
};

export default ngModule => {
  ngModule.config(addWrappers);

  function addWrappers(formlyConfigProvider) {
    formlyConfigProvider.setWrapper([
      {
        name: 'bootstrapLabel',
        template: require('./label.html'),
        apiCheck: check => ({
          templateOptions: {
            label: check.string.optional,
            required: check.bool.optional,
            labelSrOnly: check.bool.optional,
          }
        })
      },
      {name: 'bootstrapHasError', template: require('./has-error.html')}
    ]);
  }
};

Change label to be optional


const express = require('express')
const feedsAPI = express.Router()

// Remember for stricter rate limit for unauthenticated getFeed

feedsAPI.get('/:url', require('../../../controllers/api/feeds/getFeed.js')())

module.exports = feedsAPI

const express = require('express')
const feedsAPI = express.Router()
const rateLimit = require('express-rate-limit')
const validate = require('../../../middleware/validator.js')
const createError = require('../../../util/createError.js')
const {
  param
} = require('express-validator')

if (process.env.NODE_ENV !== 'test') {
  feedsAPI.use(rateLimit({
    windowMs: 1 * 60 * 1000,
    max: 20, // 20 requests per 1 minute
    message: createError(429, 'Too many requests')
  }))
}

feedsAPI.get('/:url', validate([
  param('url', 'Not a valid URL').isURL({
    protocols: ['http', 'https'],
    require_protocol: true
  })
]), require('../../../controllers/api/feeds/getFeed.js')())

module.exports = feedsAPI

Add rate limit and url check for getting feeds


function filter(v, min, max) {
    v = Math.min(v, max);
    v = Math.max(v, min);
    return v;
}

class MagicPoint {
    constructor(current, max) {
        this.max = max;
        this.min = 0;
        this.current = filter(curremt, 0, this.max);
    };

    change(next) {
        this.current = filter(next, 0, this.max);
        return this;
    }
}

module.exports = MagicPoint;

function filter(v, min, max) {
    v = Math.min(v, max);
    v = Math.max(v, min);
    return v;
}

class MagicPoint {
    constructor(current, max) {
        this.max = max;
        this.min = 0;
        this.current = filter(current, 0, this.max);
    };

    change(next) {
        this.current = filter(next, 0, this.max);
        return this;
    }
}

module.exports = MagicPoint;

Fix typo of magic point model


'use strict';
(function () {

    var net = require('net');

    /**
     * Constructor
     */
    var FreegeoipGeocoder = function(httpAdapter) {

        if (!httpAdapter || httpAdapter == 'undefinded') {

            throw new Error('FreegeoipGeocoder need an httpAdapter');
        }

        this.httpAdapter = httpAdapter;
    };

    FreegeoipGeocoder.prototype._endpoint = 'http://freegeoip.net/json/';

    FreegeoipGeocoder.prototype.geocode = function(value, callback) {

        if (!net.isIP(value)) {
            throw new Error('FreegeoipGeocoder suport only ip geocoding');
        }
        var _this = this;

        this.httpAdapter.get(this._endpoint + value , { }, function(err, result) {
            if (err) {
                throw err;
            } else {

                var results = [];

                results.push({
                    'latitude' : result.latitude,
                    'longitude' : result.longitude,
                    'country' : result.country_name,
                    'city' : result.city,
                    'zipcode' : null,
                    'streetName': null,
                    'streetNumber' : null,
                    'countryCode' : result.country_code

                });

                callback(false, results);
            }

        });

    };

    FreegeoipGeocoder.prototype.reverse = function(lat, lng, callback) {
        throw new Error('FreegeoipGeocoder no support reverse geocoding');
    };

    module.exports = FreegeoipGeocoder;

})();

'use strict';
(function () {

    var net = require('net');

    /**
     * Constructor
     */
    var FreegeoipGeocoder = function(httpAdapter) {

        if (!httpAdapter || httpAdapter == 'undefinded') {

            throw new Error('FreegeoipGeocoder need an httpAdapter');
        }

        this.httpAdapter = httpAdapter;
    };

    FreegeoipGeocoder.prototype._endpoint = 'http://freegeoip.net/json/';

    FreegeoipGeocoder.prototype.geocode = function(value, callback) {

        if (!net.isIP(value)) {
            throw new Error('FreegeoipGeocoder suport only ip geocoding');
        }
        var _this = this;

        this.httpAdapter.get(this._endpoint + value , { }, function(err, result) {
            if (err) {
                throw err;
            } else {

                var results = [];

                results.push({
                    'latitude' : result.latitude,
                    'longitude' : result.longitude,
                    'country' : result.country_name,
                    'city' : result.city,
                    'zipcode' : result.zipcode,
                    'streetName': null,
                    'streetNumber' : null,
                    'countryCode' : result.country_code

                });

                callback(false, results);
            }

        });

    };

    FreegeoipGeocoder.prototype.reverse = function(lat, lng, callback) {
        throw new Error('FreegeoipGeocoder no support reverse geocoding');
    };

    module.exports = FreegeoipGeocoder;

})();

Add zipcode to Freegeoip results


import { SWITCH_FACTION, TOGGLE_FILTER, TOGGLE_GROUP } from '../constants/MapConstants';

export function switchFaction() {
  return {
    type: SWITCH_FACTION
  };
}

export function toggleFilter(group, filter) {
  return {
    type: TOGGLE_FILTER,
    payload: {
      group,
      filter
    }
  };
}

export function toggleGroup(group, on) {
  return {
    type: TOGGLE_GROUP,
    payload: {
      group,
      on
    }
  };
}

import { SWITCH_FACTION, TOGGLE_FILTER, TOGGLE_GROUP } from '../constants/MapConstants';

/*
  Associated with the FactionSwitch component
  Changes whether the map should display from the perspective of radiant or dire
*/
export function switchFaction() {
  return {
    type: SWITCH_FACTION
  };
}

/*
  Associated wtih the FilterToggle component
  Changes whether a category of entities (rune wards, push wards, etc) should be shown
*/
export function toggleFilter(group, filter) {
  return {
    type: TOGGLE_FILTER,
    payload: {
      group,
      filter
    }
  };
}

/*
  Associated wtih the ControlLabel component
  Changes whether an entire group of filters (wards, camps, etc) should be shown
*/
export function toggleGroup(group, on) {
  return {
    type: TOGGLE_GROUP,
    payload: {
      group,
      on
    }
  };
}

Add comments to actions for detail


var net    = require('net');
    utils  = require('radiodan-client').utils,
    logger = utils.logger(__filename);

function create(port, socket) {
  var deferred = utils.promise.defer(),
      socket = socket || new net.Socket(),
      timeout = 10;

  socket.setTimeout(2500);

  function addHandlers() {
    socket.on('connect', handleConnect);
    socket.on('error', handleError);
  }

  function handleConnect() {
    deferred.resolve(port);
  }

  function handleError() {
    logger.warn('Cannot connect');
    setTimeout(function () {
      socket.connect(port);
    }, timeout);
  }

  function connect() {
    logger.debug('Connecting to port', port);
    addHandlers();
    socket.connect(port);
    return deferred.promise;
  }

  return {
    _addHandlers: addHandlers,
    connect: connect
  };
}

module.exports = {
  create: create
};

var net    = require('net');
    utils  = require('radiodan-client').utils,
    logger = utils.logger(__filename);

function create(port, socket) {
  var deferred = utils.promise.defer(),
      socket = socket || new net.Socket(),
      timeout = 1000;

  socket.setTimeout(2500);

  function addHandlers() {
    socket.on('connect', handleConnect);
    socket.on('error', handleError);
  }

  function handleConnect() {
    deferred.resolve(port);
  }

  function handleError() {
    logger.warn('Cannot connect');
    setTimeout(function () {
      socket.connect(port);
    }, timeout);
  }

  function connect() {
    logger.debug('Connecting to port', port);
    addHandlers();
    socket.connect(port);
    return deferred.promise;
  }

  return {
    _addHandlers: addHandlers,
    connect: connect
  };
}

module.exports = {
  create: create
};

Increase wait time to 1 second


/**
 * Created by david on 27/11/2015.
 */
/*
 * Copyright (C) 2015 The Gravitee team (http://gravitee.io)
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *         http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
class ApiDescriptorController {
  constructor (ApiService, resolvedApi, $state, NotificationService, $scope) {
    'ngInject';
    this.ApiService = ApiService;
    this.NotificationService = NotificationService;
    this.$scope = $scope;
    this.$state = $state;
    this.api = _.cloneDeep(resolvedApi.data);

    delete this.api.created_at;
    delete this.api.updated_at;
    delete this.api.visibility;
    delete this.api.state;
    delete this.api.permission;
    delete this.api.owner;
  }
}

export default ApiDescriptorController;

/**
 * Created by david on 27/11/2015.
 */
/*
 * Copyright (C) 2015 The Gravitee team (http://gravitee.io)
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *         http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
class ApiDescriptorController {
  constructor (ApiService, resolvedApi, $state, NotificationService, $scope) {
    'ngInject';
    this.ApiService = ApiService;
    this.NotificationService = NotificationService;
    this.$scope = $scope;
    this.$state = $state;
    this.api = _.cloneDeep(resolvedApi.data);

    delete this.api.description;
    delete this.api.created_at;
    delete this.api.updated_at;
    delete this.api.visibility;
    delete this.api.state;
    delete this.api.permission;
    delete this.api.owner;
  }
}

export default ApiDescriptorController;

Remove the description from the API descriptor when printing it.


let formId, formValidationFields;

window.addEventListener('DOMContentLoaded', function() {
    let $_form = $('form#' + formId + '-form'),
        $_formInputs = $_form.find(':input'),
        _formHasCheckboxes = false;

    $_formInputs.each(function () {
        $(this).removeAttr('required minlength maxlength');

        if ($(this).is(':checkbox')) {
            $(this).addClass('hidden');
            _formHasCheckboxes = true;
        }
    });

    if (_formHasCheckboxes) {
        $('.ui.checkbox').checkbox();
    }

    let _recaptchaElement = form + '-recaptcha-render',
        _recaptcha_enabled = document.getElementById(_recaptchaElement);

    if (_recaptcha_enabled !== null) {
        grecaptcha.render(formId + '-recaptcha-render', {
            size: 'invisible',
            callback: function (token) {
                document.getElementById(formId + '-form').submit();
            }
        });
    }

    let _form_validation = {
        inline: true,
        on: 'blur',
        onSuccess: function (e, validation_fields) {
            if (_recaptcha_enabled !== null) {
                e.preventDefault();
                grecaptcha.execute();
            }
        }
    };

    _form_validation.fields = formValidationFields;

    $_form.form(_form_validation);
});

let formId, formValidationFields;

window.addEventListener('load', function() {
    let $_form = $('form#' + formId + '-form'),
        $_formInputs = $_form.find(':input'),
        _formHasCheckboxes = false;

    $_formInputs.each(function () {
        $(this).removeAttr('required minlength maxlength');

        if ($(this).is(':checkbox')) {
            $(this).addClass('hidden');
            _formHasCheckboxes = true;
        }
    });

    if (_formHasCheckboxes) {
        $('.ui.checkbox').checkbox();
    }

    let _recaptchaElement = $_form + '-recaptcha-render',
        _recaptcha_enabled = document.getElementById(_recaptchaElement);

    if (_recaptcha_enabled !== null) {
        grecaptcha.render(formId + '-recaptcha-render', {
            size: 'invisible',
            callback: function (token) {
                document.getElementById(formId + '-form').submit();
            }
        });
    }

    let _form_validation = {
        inline: true,
        on: 'blur',
        onSuccess: function (e, validation_fields) {
            if (_recaptcha_enabled !== null) {
                e.preventDefault();
                grecaptcha.execute();
            }
        }
    };

    _form_validation.fields = formValidationFields;

    $_form.form(_form_validation);
});

Fix variable + change event


/* jshint node: true */

module.exports = function(environment) {
  var ENV = {
    modulePrefix: 'dummy',
    environment: environment,
    baseURL: '/',
    locationType: 'auto',
    EmberENV: {
      FEATURES: { }
    },

    APP: {
      imgix: {
        source: 'assets.imgix.net',
        debug: true
      }
    },

    contentSecurityPolicy: {
      'img-src': "'self' assets.imgix.net"
    }
  };

  if (environment === 'test') {
    // Testem prefers this...
    ENV.baseURL = '/';
    ENV.locationType = 'none';

    // keep test console output quieter
    ENV.APP.LOG_ACTIVE_GENERATION = false;
    ENV.APP.LOG_VIEW_LOOKUPS = false;

    ENV.APP.rootElement = '#ember-testing';
  }

  if (environment === 'production') {

  }

  return ENV;
};

/* jshint node: true */

module.exports = function(environment) {
  var ENV = {
    modulePrefix: 'dummy',
    environment: environment,
    baseURL: '/',
    locationType: 'auto',
    EmberENV: {
      FEATURES: { }
    },

    APP: {
      imgix: {
        source: 'assets.imgix.net',
        debug: true
      }
    },

    contentSecurityPolicy: {
      'img-src': "'self' assets.imgix.net"
    }
  };

  if (environment === 'test') {
    // Testem prefers this...
    ENV.baseURL = '/';
    ENV.locationType = 'none';

    // keep test console output quieter
    ENV.APP.LOG_ACTIVE_GENERATION = false;
    ENV.APP.LOG_VIEW_LOOKUPS = false;

    ENV.APP.rootElement = '#ember-testing';
  }

  if (environment === 'production') {
    ENV.baseURL = '/ember-cli-imgix';
  }

  return ENV;
};

Set the proper path for our dummy app


//@flow

import styled, { css } from "styled-components";

export const Wrapper = styled.div`

  ${({ theme }: { theme: Theme }) => css`

    box-sizing: border-box;
    position: relative;
    width: 100%;
    background-color: ${theme.color.background};

  `}
`;

//@flow

import styled, { css } from "styled-components";

export const Wrapper = styled.div`

  ${({ theme }: { theme: Theme }) => css`

    box-sizing: border-box;
    position: relative;
    width: 100%;
    background-color: ${theme.color.background};
    padding: 1em ${theme.scale.s4()};
    display: flex;
    align-items: baseline;
    justify-content: space-between;

  `}
`;

Arrange desktop header items with flexbox


for (let i = 1; i <= 100; ++i) {
    let str = "";
    if (i % 3 === 0 ) {
        str += "Fizz";
    }
    if (i % 5 === 0) {
        str += "Buzz"
    }
    if (str === "") {
        str = i
    }
    console.log(str)
}

// Nicer solution:
// console.log(str || i) instead of last if-clause

Add chapter 2, exercise 2


var util = require('util');
var amazon = require("amazon");
var sqs = require("sqs");

var env = process.env;
var accessKeyId = process.env.ACCESS_KEY_ID;
var secretAccessKey = process.env.SECRET_ACCESS_KEY;
var awsAccountId = process.env.AWS_ACCOUNT_ID;

var sqs = new sqs.Sqs(accessKeyId, secretAccessKey, awsAccountId, amazon.US_EAST_1);

console.log( 'Region :', sqs.region() );
console.log( 'EndPoint :',  sqs.host() );
console.log( 'AccessKeyId :', sqs.accessKeyId() );
// console.log( 'SecretAccessKey :', sqs.secretAccessKey() );
console.log( 'AwsAccountId :', sqs.awsAccountId() );

sqs.listQueues({}, function(err, data) {
    console.log('Error :', util.inspect(err, true, null));
    console.log('Data :', util.inspect(data, true, null));
});

var util = require('util');
var amazon = require("amazon");
var sqs = require("sqs");

var env = process.env;
var accessKeyId = process.env.ACCESS_KEY_ID;
var secretAccessKey = process.env.SECRET_ACCESS_KEY;
var awsAccountId = process.env.AWS_ACCOUNT_ID;

var sqs = new sqs.Sqs(accessKeyId, secretAccessKey, awsAccountId, amazon.US_EAST_1);

console.log( 'Region :', sqs.region() );
console.log( 'EndPoint :',  sqs.host() );
console.log( 'AccessKeyId :', sqs.accessKeyId() );
// console.log( 'SecretAccessKey :', sqs.secretAccessKey() );
console.log( 'AwsAccountId :', sqs.awsAccountId() );

sqs.listQueues({}, function(err, data) {
    console.log("\nlisting queues - expecting success");
    console.log('Error :', util.inspect(err, true, null));
    console.log('Data :', util.inspect(data, true, null));
});

Add a message when listing queues :)


'use strict';

const cfg = require('./config');
const { cloneGitProject, applyOneTemplate, tagAndPush, commitAndPush } = require('./utils');

async function run () {

  const templateFilepath = './templates/dockerhub/Dockerfile';
  const gitPath = './git-dockerhub';
  const { git, appInfos } = cfg;
  const gitUrl = `ssh://git@github.com:CleverCloud/clever-tools-dockerhub.git`;
  const version = cfg.getVersion();

  await cloneGitProject({ gitUrl, gitPath, git });
  await applyOneTemplate(gitPath, templateFilepath, {
    version,
    ...appInfos,
  });
  await commitAndPush({ gitPath, version });
  await tagAndPush({ gitPath, tagName: version });
}

run().catch((e) => {
  console.error(e);
  process.exit(1);
});

'use strict';

const cfg = require('./config');
const { cloneGitProject, applyOneTemplate, tagAndPush, commitAndPush } = require('./utils');

async function run () {

  const templateFilepath = './templates/dockerhub/Dockerfile';
  const gitPath = './git-dockerhub';
  const { git, appInfos } = cfg;
  const gitUrl = `git@github.com:CleverCloud/clever-tools-dockerhub.git`;
  const version = cfg.getVersion();

  await cloneGitProject({ gitUrl, gitPath, git });
  await applyOneTemplate(`${gitPath}/Dockerfile`, templateFilepath, {
    version,
    ...appInfos,
  });
  await commitAndPush({ gitPath, version });
  await tagAndPush({ gitPath, tagName: version });
}

run().catch((e) => {
  console.error(e);
  process.exit(1);
});

Fix the git url and template path


const swPrecache = {
	staticFileGlobs: [
		'dist/**.html',
		'dist/**/*.css',
		'dist/**/*.js',
		'dist/**.json',
		'dist/**.txt',
		'dist/images/*'
	],
	root: 'dist',
	stripPrefix: 'dist/',
	directoryIndex: 'index.html',
	navigateFallback: 'index.html',
	runtimeCaching: []
}

module.exports = swPrecache

const swPrecache = {
	staticFileGlobs: [
		'https://fonts.googleapis.com/css?family=Roboto:300,300italic,700,700italic',
		'https://cdnjs.cloudflare.com/ajax/libs/normalize/5.0.0/normalize.css',
		'https://cdnjs.cloudflare.com/ajax/libs/milligram/1.3.0/milligram.min.css',
		'https://browser.sentry-cdn.com/5.5.0/bundle.min.js',
		'dist/**.html',
		'dist/**/*.css',
		'dist/**/*.js',
		'dist/**.json',
		'dist/**.txt',
		'dist/images/*'
	],
	root: 'dist',
	stripPrefix: 'dist/',
	directoryIndex: 'index.html',
	navigateFallback: 'index.html',
	runtimeCaching: []
}

module.exports = swPrecache

Fix to register a ServiceWorker


(function($) {

  var prevText = null;
  var convert = function() {
    var text = srcTextarea.val().replace(/^[\s　]+/, '').replace(/[\s　]+$/, '');
    if (text === prevText) {
      return;
    }
    destTextarea.val(TreeDiagram.indentTextToTreeText(text));
    prevText = text;
  };

  var srcTextarea = $('#src_text');
  var destTextarea = $('#dest_text');

  srcTextarea.keyup(convert);

  srcTextarea.val('好きな果物\n' +
                  '　りんご\n' +
                  '　みかん');
  convert();

})(jQuery);

'use strict';

(function($) {

  var prevText = null;
  var convert = function() {
    var text = srcTextarea.val().replace(/^[\s　]+/, '').replace(/[\s　]+$/, '');
    if (text === prevText) {
      return;
    }
    destTextarea.val(TreeDiagram.indentTextToTreeText(text));
    prevText = text;
  };

  var srcTextarea = $('#src_text');
  var destTextarea = $('#dest_text');

  srcTextarea.keyup(convert);
  destTextarea.focus(function() {
    $(this).select();
    // for Chrome, see: http://stackoverflow.com/questions/5797539/jquery-select-all-text-from-a-textarea
    $(this).one('mouseup', function(e) {
      e.preventDefault();
    });
  });

  srcTextarea.val('好きな果物\n' +
                  '　りんご\n' +
                  '　みかん');
  convert();

})(jQuery);

Select all text when the destination textarea is focused


require.config({
    baseUrl: 'js',
    paths: {
        // modules
        'app': 'app',
        'topBar': 'topBar',
        'services': 'services',
        'echoContent': 'echoContent',
        'opmlContent': 'opmlContent',
        // utils
        'utils': 'utils/utils',
        // Plugins
        'text': 'plugins/text',
        // Vendor
        'jquery': '../node_modules/jquery/dist/jquery.min',
        'materialize': '../node_modules/materialize-css/bin/materialize'
    }
});

require(['app',
        'topBar',
        'services',
        'echoContent',
        'opmlContent',
        'utils',
        'text',
        'jquery'
    ],
    function(app) {
        'use strict';
        app.init();
    });

require.config({
    baseUrl: 'js',
    paths: {
        // modules
        'app': 'app',
        'topBar': 'topBar',
        'services': 'services',
        'echoContent': 'echoContent',
        'opmlContent': 'opmlContent',
        // utils
        'utils': 'utils/utils',
        // Plugins
        'text': 'plugins/text',
        // Vendor
        'jquery': '../node_modules/jquery/dist/jquery.min',
        'materialize': '../node_modules/materialize-css/bin/materialize'
    },
    callback: function() {
        'use strict';

        require(['app',
                'topBar',
                'services',
                'echoContent',
                'opmlContent',
                'utils',
                'text',
                'jquery'
            ],
            function(app) {
                app.init();
            });
    }
});

Improve init function inserting it in callback require propertie


import { connect } from 'react-redux';
import { createReport } from 'redux-modules/reports';
import { NavigationActions } from 'react-navigation';
import Map from 'components/map';

function mapStateToProps() {
  return {};
}

function mapDispatchToProps(dispatch, { navigation }) {
  return {
    createReport: (name, position) => {
      dispatch(createReport(name, position));
    },
    navigateReset: (routeName, params) => {
      const action = NavigationActions.reset({
        index: 2,
        actions: [
          { type: 'Navigate', routeName: 'Dashboard' },
          { type: 'Navigate', routeName: 'Alerts' },
          { type: 'Navigate', routeName, params }
        ]
      });
      navigation.dispatch(action);
    }
  };
}

export default connect(
  mapStateToProps,
  mapDispatchToProps
)(Map);

import { connect } from 'react-redux';
import { createReport } from 'redux-modules/reports';
import { NavigationActions } from 'react-navigation';
import tracker from 'helpers/googleAnalytics';
import Map from 'components/map';

function mapStateToProps() {
  return {};
}

function mapDispatchToProps(dispatch, { navigation }) {
  return {
    createReport: (name, position) => {
      dispatch(createReport(name, position));
      tracker.trackEvent('Report', 'Create Report', { label: 'Click Done', value: 0 });
    },
    navigateReset: (routeName, params) => {
      const action = NavigationActions.reset({
        index: 2,
        actions: [
          { type: 'Navigate', routeName: 'Dashboard' },
          { type: 'Navigate', routeName: 'Alerts' },
          { type: 'Navigate', routeName, params }
        ]
      });
      navigation.dispatch(action);
    }
  };
}

export default connect(
  mapStateToProps,
  mapDispatchToProps
)(Map);

Add trackEvent to report create