var websocket = require('websocket-stream');
var URL = require('url');

function buildBuilder(client, opts) {
  var host = opts.hostname || 'localhost'
    , port = opts.port || 80
    , url = opts.protocol + '://' + host + ':' + port
    , ws =  websocket(url, {
        protocol: 'mqttv3.1'
      });

  return ws;
}

function buildBuilderBrowser(mqttClient, opts) {
  var parsed = URL.parse(document.URL);

  if (!opts.protocol) {
    if (parsed.protocol === 'https:') {
      opts.protocol = 'wss';
    } else {
      opts.protocol = 'ws';
    }
  }

  if (!opts.host) {
    opts.host = parsed.hostname;
  }

  if (!opts.port) {
    opts.port = parsed.port;
  }

  var host = opts.hostname || opts.host
    , port = opts.port
    , url = opts.protocol + '://' + host + ':' + opts.port

  return websocket(url);
}

if (process.title !== 'browser') {
  module.exports = buildBuilder;
} else {
  module.exports = buildBuilderBrowser;
}

var websocket = require('websocket-stream');
var URL = require('url');

function buildBuilder(client, opts) {
  var host = opts.hostname || 'localhost'
    , port = opts.port || 80
    , url = opts.protocol + '://' + host + ':' + port
    , ws =  websocket(url, {
        protocol: 'mqttv3.1'
      });

  return ws;
}

function buildBuilderBrowser(mqttClient, opts) {
  var parsed = URL.parse(document.URL);

  if (!opts.protocol) {
    if (parsed.protocol === 'https:') {
      opts.protocol = 'wss';
    } else {
      opts.protocol = 'ws';
    }
  }

  if (!opts.host) {
    opts.host = parsed.hostname;
  }

  if (!opts.port) {
    if(opts.protocol === 'wss'){
      opts.port = 443;
    } else if(opts.protocol === 'ws') {
      opts.port = 80;
    } else {
      opts.port = parsed.port;
    }
  }

  var host = opts.hostname || opts.host
    , port = opts.port
    , url = opts.protocol + '://' + host + ':' + opts.port

  return websocket(url);
}

if (process.title !== 'browser') {
  module.exports = buildBuilder;
} else {
  module.exports = buildBuilderBrowser;
}

Use port for protocol when none is provided


const crypto = require('crypto');
const {debugLog} = require('./utilities');

/**
 * memHandler - In memory upload handler
 * @param {Object} options
 * @param {String} fieldname
 * @param {String} filename
 * @returns {Object}
 */
module.exports = (options, fieldname, filename) => {
  let buffers = [];
  let fileSize = 0; // eslint-disable-line
  let hash = crypto.createHash('md5');

  const getBuffer = () => Buffer.concat(buffers);
  const emptyFunc = () => '';

  return {
    dataHandler: (data) => {
      buffers.push(data);
      hash.update(data);
      fileSize += data.length;
      debugLog(options, `Uploading ${fieldname} -> ${filename}, bytes: ${fileSize}`);
    },
    getBuffer: getBuffer,
    getFilePath: emptyFunc,
    getFileSize: () => fileSize,
    getHash: () => hash.digest('hex'),
    complete: getBuffer,
    cleanup: emptyFunc
  };
};

const crypto = require('crypto');
const {debugLog} = require('./utilities');

/**
 * memHandler - In memory upload handler
 * @param {Object} options
 * @param {String} fieldname
 * @param {String} filename
 * @returns {Object}
 */
module.exports = (options, fieldname, filename) => {
  let buffers = [];
  let fileSize = 0; // eslint-disable-line
  let hash = crypto.createHash('md5');

  const getBuffer = () => Buffer.concat(buffers, fileSize);
  const emptyFunc = () => '';

  return {
    dataHandler: (data) => {
      buffers.push(data);
      hash.update(data);
      fileSize += data.length;
      debugLog(options, `Uploading ${fieldname} -> ${filename}, bytes: ${fileSize}`);
    },
    getBuffer: getBuffer,
    getFilePath: emptyFunc,
    getFileSize: () => fileSize,
    getHash: () => hash.digest('hex'),
    complete: getBuffer,
    cleanup: emptyFunc
  };
};

Add length to Buffer.concat invoke


import { createDispatcher } from 'fluorine-lib'

const dispatcher = createDispatcher({
  logging: true
})

export default dispatcher

import { createDispatcher } from 'fluorine-lib'
import thunk from 'fluorine-lib/lib/middleware/thunk'

const dispatcher = createDispatcher({
  logging: true
}, thunk)

export default dispatcher

Add thunk middleware to counter


// Copyright 2010 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
//
// 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.

function TryStart() {
  try {
    var raw_json_data = document.getElementById('json').textContent;
    var json = JSON.parse(raw_json_data);
    var port = chrome.extension.connect();
    port.postMessage({message: 'start', benchmark: json});
  }
  catch(err) {
    console.log("TryStart retring after exception: " + err);
    setTimeout(TryStart, 1000);
  }

  var status_element = document.getElementById('status');
  status_element.textContent = "ACK";
}

// We wait 1s before starting the test just to let chrome warm up better.
setTimeout(TryStart, 250);

// Copyright 2010 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
//
// 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.

function TryStart() {
  try {
    var raw_json_data = document.getElementById('json').textContent;
    var json = JSON.parse(raw_json_data);
    var port = chrome.extension.connect();
    port.postMessage({message: 'start', benchmark: json});
  }
  catch(err) {
    console.log("TryStart retrying after exception: " + err);
    setTimeout(TryStart, 1000);
    // TODO(mbelshe): remove me!  This is for debugging.
    console.log("Body is: " + document.body.innerHTML);
    return;
  }

  var status_element = document.getElementById('status');
  status_element.textContent = "ACK";
}

// We wait 1s before starting the test just to let chrome warm up better.
setTimeout(TryStart, 250);

Make TryStart recover more gracefully


function largestOfFour(arr) {
  // You can do this!
  largest=[];

  for(var i=0; i<arr.length; i++){
    num=arr[i][0];
    for(var j=0; j<arr[i].length; j++){
      if(arr[i][j]>num){
        num=arr[i][j];
      }
    }
    largest.push(num);
  }
  return largest;
}

largestOfFour([[4, 5, 1, 3], [13, 27, 18, 26], [32, 35, 37, 39], [1000, 1001, 857, 1]]);

function largestOfFour(arr) {
    // You can do this!
    largest = [];

    for (var i = 0; i < arr.length; i++) {
        num = arr[i][0];
        for (var j = 0; j < arr[i].length; j++) {
            if (arr[i][j] > num) {
                num = arr[i][j];
            }
        }
        largest.push(num);
    }
    return largest;
}

largestOfFour([[4, 5, 1, 3], [13, 27, 18, 26], [32, 35, 37, 39], [1000, 1001, 857, 1]]);

Return Largest Numbers in Arrays


table = function () {
  $(".js-vertical-tab-content").hide();
  $(".js-vertical-tab-content:first").show();

  $(".js-vertical-tab").click(function(event) {
    event.preventDefault();

    $(".js-vertical-tab-content").hide();
    var activeTab = $(this).attr("rel");
    $("#"+activeTab).show();

    $(".js-vertical-tab").removeClass("is-active");
    $(this).addClass("is-active");

    $(".js-vertical-tab-accordion-heading").removeClass("is-active");
    $(".js-vertical-tab-accordion-heading[rel^='"+activeTab+"']").addClass("is-active");
  });
}

function table () {
  $(".js-vertical-tab-content").hide();
  $(".js-vertical-tab-content:first").show();

  $(".js-vertical-tab").click(function(event) {
    event.preventDefault();

    $(".js-vertical-tab-content").hide();
    var activeTab = $(this).attr("rel");
    $("#"+activeTab).show();

    $(".js-vertical-tab").removeClass("is-active");
    $(this).addClass("is-active");

    $(".js-vertical-tab-accordion-heading").removeClass("is-active");
    $(".js-vertical-tab-accordion-heading[rel^='"+activeTab+"']").addClass("is-active");
  });
}

Declare function instead of express


/*global define*/
define(function() {
    "use strict";

    var a;

    /**
     * Given a URL, determine whether that URL is considered cross-origin to the current page.
     *
     * @private
     */
    var isCrossOriginUrl = function(url) {
        if (typeof a === 'undefined') {
            a = document.createElement('a');
        }

        var location = window.location;
        a.href = url;
        a.href = a.href; // IE only absolutizes href on get, not set

        // host includes both hostname and port if the port is not standard
        return a.protocol !== location.protocol || a.host !== location.host;
    };

    return isCrossOriginUrl;
});

/*global define*/
define(function() {
    "use strict";

    var a;

    /**
     * Given a URL, determine whether that URL is considered cross-origin to the current page.
     *
     * @private
     */
    var isCrossOriginUrl = function(url) {
        if (typeof a === 'undefined') {
            a = document.createElement('a');
        }

        // copy window location into the anchor to get consistent results
        // when the port is default for the protocol (e.g. 80 for HTTP)
        a.href = window.location.href;

        // host includes both hostname and port if the port is not standard
        var host = a.host;
        var protocol = a.protocol;

        a.href = url;
        a.href = a.href; // IE only absolutizes href on get, not set

        return protocol !== a.protocol || host !== a.host;
    };

    return isCrossOriginUrl;
});

Put both the window URL and the test URL through an anchor.


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

const Guests = (props) => {
  if (props.guests.size > 0) {
    const guests = props.guests.map((guest, index) => {
      return
{guest.get('name')}, {guest.get('description')}

    })
    return (

{props.guests.size === 1 ? 'Invité' : 'Invités'} :

          {guests}

    )
  } else return null
}

Guests.propTypes = {
  guests: PropTypes.object
}

export default Guests

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

const Guests = (props) => {
  if (props.guests.size > 0) {
    const guests = props.guests.map((guest, index) => {
      return
{guest.get('name')}, {guest.get('description')}

    })
    return (

{props.guests.size === 1 ? 'Invité·e' : 'Invité·e·s'} :

          {guests}

    )
  } else return null
}

Guests.propTypes = {
  guests: PropTypes.object
}

export default Guests

Add point médian to guest title


'use strict';

var <%= slugname %> = require('../lib/<%= slugname %>.js');

/*
  ======== A Handy Little Nodeunit Reference ========
  https://github.com/caolan/nodeunit

  Test methods:
    test.expect(numAssertions)
    test.done()
  Test assertions:
    test.ok(value, [message])
    test.equal(actual, expected, [message])
    test.notEqual(actual, expected, [message])
    test.deepEqual(actual, expected, [message])
    test.notDeepEqual(actual, expected, [message])
    test.strictEqual(actual, expected, [message])
    test.notStrictEqual(actual, expected, [message])
    test.throws(block, [error], [message])
    test.doesNotThrow(block, [error], [message])
    test.ifError(value)
*/

exports['<%= slugname %>'] = {
  setUp: function(done) {
    // setup here
    done();
  },
  'no args': function(test) {
    test.expect(1);
    // tests here
    test.equal(<%= slugname %>.awesome(), 'awesome', 'should be awesome.');
    test.done();
  },
};

'use strict';

var <%= slugname %> = require('../lib/<%= slugname %>.js');

/*
  ======== A Handy Little Nodeunit Reference ========
  https://github.com/caolan/nodeunit

  Test methods:
    test.expect(numAssertions)
    test.done()
  Test assertions:
    test.ok(value, [message])
    test.equal(actual, expected, [message])
    test.notEqual(actual, expected, [message])
    test.deepEqual(actual, expected, [message])
    test.notDeepEqual(actual, expected, [message])
    test.strictEqual(actual, expected, [message])
    test.notStrictEqual(actual, expected, [message])
    test.throws(block, [error], [message])
    test.doesNotThrow(block, [error], [message])
    test.ifError(value)
*/

exports['<%= slugname %>'] = {
  setUp: function(done) {
    // setup here
    done();
  },
  'no args': function(test) {
    test.expect(1);
    // tests here
    test.equal(<%= slugname %>.awesome(), 'awesome', 'should be awesome.');
    test.done();
  }
};

Fix JSHint issues with template node test file


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

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

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

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

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

        helpers.mockPrompt(this.app, {
            'someOption': true
        });
        this.app.options['skip-install'] = true;
        this.app.run({}, function () {
            helpers.assertFiles(expected);
            done();
        });
    });
});

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

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

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

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

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

        helpers.mockPrompt(this.app, {
            'suite': 'algorithm'
        });
        this.app.options['skip-install'] = true;
        this.app.run({}, function () {
            helpers.assertFiles(expected);
            done();
        });
    });
});

Add test specs for basic operation


/**
 * Created by Liam Vovk on 2017-05-04.
 */

require('dotenv').config()
let mongoose = require('mongoose')
let connectionString = require('../config/connection_string')
mongoose.Promise = require('bluebird')

module.exports = mongoose.connect(connectionString)

/**
 * Created by Liam Vovk on 2017-05-04.
 */

let mongoose = require('mongoose')
let connectionString = require('../config/connection_string')
mongoose.Promise = require('bluebird')

module.exports = mongoose.connect(connectionString)

Remove un necessary .env load


var Binary = exports.Binary = function(bytes) {
    this.bytes = bytes;
};

Binary.prototype.getLength = function() {
    return this.bytes.length;
};

Binary.prototype.toString = function(encoding) {
    var jstr = encoding ?
               new java.lang.String(this.bytes, encoding) :
               new java.lang.String(this.bytes);
    return String(jstr);
};

String.prototype.toBinary = function(encoding) {
    var bytes = encoding ?
                new java.lang.String(this).getBytes(encoding) :
                new java.lang.String(this).getBytes();
    return new Binary(bytes);
};

Binary.prototype.forEach = function(block) {
    block(this);
};

Binary.prototype.toBinary = function() {
    return this;
};

var asJavaObject = require('helma/system').asJavaObject;

var Binary = exports.Binary = function(bytes) {
    this.bytes = bytes;
};

Binary.prototype.getLength = function() {
    return this.bytes.length;
};

Binary.prototype.toString = function(encoding) {
    var jstr = encoding ?
               new java.lang.String(this.bytes, encoding) :
               new java.lang.String(this.bytes);
    return String(jstr);
};

String.prototype.toBinary = function(encoding) {
    var bytes = encoding ?
                asJavaObject(this.toString()).getBytes(encoding) :
                asJavaObject(this.toString()).getBytes();
    return new Binary(bytes);
};

Binary.prototype.forEach = function(block) {
    block(this);
};

Binary.prototype.toBinary = function() {
    return this;
};

Use helma/system.asJavaObject() to prevent allocating new java.lang.Strings in String.toBinary().


// TODO:
// - end-to-end test
// - extract update code, inject it as a function?
function createAblyHandler(options) {
  var ably = options.ably
  var operations = options.operations
  var fetchOperation = options.fetchOperation
  return function (operation, variables, cacheConfig, observer) {
    var channelName, channel
    // POST the subscription like a normal query
    fetchOperation(operation, variables, cacheConfig).then(function(response) {
      channelName = response.headers.get("X-Subscription-ID")
      channel = ably.channels.get(channelName)
      // Register presence, so that we can detect empty channels and clean them up server-side
      channel.presence.enterClient("graphql-subscriber", "subscribed")
      // When you get an update from ably, give it to Relay
      channel.subscribe("update", function(message) {
        // TODO Extract this code
        // When we get a response, send the update to `observer`
        var payload = message.data
        var result = payload.result
        if (result && result.errors) {
          // What kind of error stuff belongs here?
          observer.onError(result.errors)
        } else if (result) {
          observer.onNext({data: result.data})
        }
        if (!payload.more) {
          // Subscription is finished
          observer.onCompleted()
        }
      })
    })
    return {
      dispose: function() {
        channel.presence.leaveClient()
        channel.unsubscribe()
      }
    }
  }
}

module.exports = createAblyHandler

// TODO:
// - end-to-end test
// - extract update code, inject it as a function?
function createAblyHandler(options) {
  var ably = options.ably
  var operations = options.operations
  var fetchOperation = options.fetchOperation
  return function (operation, variables, cacheConfig, observer) {
    var channelName, channel
    // POST the subscription like a normal query
    fetchOperation(operation, variables, cacheConfig).then(function(response) {
      channelName = response.headers.get("X-Subscription-ID")
      channel = ably.channels.get(channelName)
      // Register presence, so that we can detect empty channels and clean them up server-side
      if (ably.auth.clientId) {
        channel.presence.enter("subscribed")
      } else {
        channel.presence.enterClient("graphql-subscriber", "subscribed")
      }
      // When you get an update from ably, give it to Relay
      channel.subscribe("update", function(message) {
        // TODO Extract this code
        // When we get a response, send the update to `observer`
        var payload = message.data
        var result = payload.result
        if (result && result.errors) {
          // What kind of error stuff belongs here?
          observer.onError(result.errors)
        } else if (result) {
          observer.onNext({data: result.data})
        }
        if (!payload.more) {
          // Subscription is finished
          observer.onCompleted()
        }
      })
    })
    return {
      dispose: function() {
        channel.presence.leaveClient()
        channel.unsubscribe()
      }
    }
  }
}

module.exports = createAblyHandler

Work better with identified Ably client


'use strict';

let Defiant = undefined;

class Engine {
  constructor() {
    Defiant = require('./defiant');

    // Create registries for plugins.
    this.registry = {};

    // Initialize default Plugins.
    this.pluginRegistry = new Defiant.Plugin.PluginRegistry();
    [
      Defiant.Plugin.Settings,
      Defiant.Plugin.Orm,
      Defiant.Plugin.Http,
      Defiant.Plugin.Router,
      Defiant.Plugin.Menu,
      Defiant.Plugin.ThemeBase,
      Defiant.Plugin.ThemeBootstrap,
      Defiant.Plugin.Theme,
      Defiant.Plugin.QueryApi,
      Defiant.Plugin.Layout,
      Defiant.Plugin.Message,
      Defiant.Plugin.FormApi,
      Defiant.Plugin.Session,
      Defiant.Plugin.Account,
      Defiant.Plugin.FileApi,
      Defiant.Library,
      Defiant.Library.JQuery,
      Defiant.Library.JQueryUI,
      Defiant.Library.Materialize,
    ].map(plugin => this.pluginRegistry.set(plugin, this));
  }

  defineBootstrapDirectory(directory) {
    this.bootstrapDirectory = directory;
    return this;
  }

  addPlugin(plugin) {
    this.pluginRegistry.set(plugin, this);
    return this;
  }

  async init() {
    for (let plugin of this.pluginRegistry.getOrderedElements()) {
      await plugin.init();
    }
  }
}

module.exports = Engine;

'use strict';

let Defiant = undefined;

class Engine {
  constructor() {
    Defiant = require('./defiant');

    // Create registries for plugins.
    this.registry = {};

    // Initialize default Plugins.
    this.pluginRegistry = new Defiant.Plugin.PluginRegistry();
    [
      Defiant.Plugin.Settings,
      Defiant.Plugin.QueryApi,
      Defiant.Plugin.Orm,
      Defiant.Plugin.Http,
      Defiant.Plugin.Router,
      Defiant.Plugin.Menu,
      Defiant.Plugin.ThemeBase,
      Defiant.Plugin.ThemeBootstrap,
      Defiant.Plugin.Theme,
      Defiant.Plugin.Layout,
      Defiant.Plugin.Message,
      Defiant.Plugin.FormApi,
      Defiant.Plugin.Session,
      Defiant.Plugin.Account,
      Defiant.Plugin.FileApi,
      Defiant.Library,
      Defiant.Library.JQuery,
      Defiant.Library.JQueryUI,
      Defiant.Library.Materialize,
    ].map(plugin => this.pluginRegistry.set(plugin, this));
  }

  defineBootstrapDirectory(directory) {
    this.bootstrapDirectory = directory;
    return this;
  }

  addPlugin(plugin) {
    this.pluginRegistry.set(plugin, this);
    return this;
  }

  async init() {
    for (let plugin of this.pluginRegistry.getOrderedElements()) {
      await plugin.init();
    }
  }
}

module.exports = Engine;

Move QueryApi to load before Orm


describe('index (public API)', function () {
    'use strict';

    var assert = require('assert'),
        bro = require('jsdom-test-browser'),
        api = require('../index'),
        browserApi = require('../browser');

    assert(bro);

    describe('just as the full API', function () {
        it('provides the React class', function () {
            assert.strictEqual(browserApi.Klass, api.Klass);
        });

        it('provides the model class', function () {
            assert.strictEqual(browserApi.Model, api.Model);
        });

        it('provides the environment class', function () {
            assert.strictEqual(browserApi.Environment, api.Environment);
        });
    });

    it('exports no intlMessages', function () {
        assert.strictEqual(typeof browserApi.intlMessages, 'undefined');
    });
});

describe('public browser API', function () {
    'use strict';

    var assert = require('assert'),
        bro = require('jsdom-test-browser'),
        api = require('../index'),
        browserApi = require('../browser');

    assert(bro);

    describe('just as the full API', function () {
        it('provides the React class', function () {
            assert.strictEqual(browserApi.Klass, api.Klass);
        });

        it('provides the model class', function () {
            assert.strictEqual(browserApi.Model, api.Model);
        });

        it('provides the environment class', function () {
            assert.strictEqual(browserApi.Environment, api.Environment);
        });
    });

    it('exports no intlMessages', function () {
        assert.strictEqual(typeof browserApi.intlMessages, 'undefined');
    });
});

Change the copy-pasted test suite title


'use strict';

var async = require('async');

var retrieve = require('./helpers/retrieve.js');
var retrieveCards = retrieve.retrieveCards;
var retrieveDeletedCards = retrieve.retrieveDeletedCards;

var defaultCursor = retrieve.defaultCursor;

module.exports = function updateAccount(serviceData, cursor, queues, cb) {
  // Retrieve all contacts since last call
  async.parallel({
    newCards: function RetrieveNewCards(cb) {
      retrieveCards(serviceData.accessToken, cursor || defaultCursor, cb);
    },
    deletedCards: function RetrieveDeletedCards(cb) {
      retrieveDeletedCards(serviceData.accessToken, cursor || defaultCursor, cb);
    },
  }, function handleCards(err, cardsAndDates) {
    if(err) {
      return cb(err);
    }

    var newCursor = new Date(Math.max(cardsAndDates.newCards[1], cardsAndDates.deletedCards[1]) + 1000);
    cardsAndDates.newCards[0].forEach(function(card) {
      queues.addition.push(card);
    });
    cardsAndDates.deletedCards[0].forEach(function(card) {
      queues.deletion.push(card);
    });

    cb(null, newCursor);
  });
};

'use strict';

var async = require('async');
var TokenError = require('anyfetch-provider').TokenError;

var retrieve = require('./helpers/retrieve.js');
var retrieveCards = retrieve.retrieveCards;
var retrieveDeletedCards = retrieve.retrieveDeletedCards;

var defaultCursor = retrieve.defaultCursor;

module.exports = function updateAccount(serviceData, cursor, queues, cb) {
  // Retrieve all contacts since last call
  async.parallel({
    newCards: function RetrieveNewCards(cb) {
      retrieveCards(serviceData.accessToken, cursor || defaultCursor, cb);
    },
    deletedCards: function RetrieveDeletedCards(cb) {
      retrieveDeletedCards(serviceData.accessToken, cursor || defaultCursor, cb);
    },
  }, function handleCards(err, cardsAndDates) {
    if(err) {
      if(err.toString().match(/token/i)) {
        return cb(new TokenError());
      }
      return cb(err);
    }

    var newCursor = new Date(Math.max(cardsAndDates.newCards[1], cardsAndDates.deletedCards[1]) + 1000);
    cardsAndDates.newCards[0].forEach(function(card) {
      queues.addition.push(card);
    });
    cardsAndDates.deletedCards[0].forEach(function(card) {
      queues.deletion.push(card);
    });

    cb(null, newCursor);
  });
};

Send TokenError when we need a refresh


function initHomeMap (coords){
	$("h1.logozone").after("

");
	var imgmarker='../img/marker.png';
	var objectMarker = coords.map(function(coord) {
		return { latitude: coord.lat, longitude: coord.lon, icon: imgmarker};
	});
	$("#homeMap").goMap({
        navigationControl: false,
        mapTypeControl: false,
        scrollwheel: false,
        disableDoubleClickZoom: true,
        zoom: 12,
        maptype: 'ROADMAP',
        markers: objectMarker
    });
	$.goMap.setMap({latitude:'46.836944988044465', longitude:'-71.28376007080078'});
}

$(document).on('homemap:ready', function(e, coords) {
	// Idempotence
	if($("#zoneHomeMap").length !== 0) return;
	$(window).on('resize.homemap', function() {
		if($("#zoneHomeMap").length == 0 && $(window).width() > 960) {
			initHomeMap(coords);
		}
	})
	$(window).trigger('resize.homemap');
	$(document).on('page:change', function() { $(window.off('.homemap')); });
});

function initHomeMap (coords){
	$("h1.logozone").after("

");
	var imgmarker='../img/marker.png';
	var objectMarker = coords.map(function(coord) {
		return { latitude: coord.lat, longitude: coord.lon, icon: imgmarker};
	});
	$("#homeMap").goMap({
        navigationControl: false,
        mapTypeControl: false,
        scrollwheel: false,
        disableDoubleClickZoom: true,
        zoom: 12,
        maptype: 'ROADMAP',
        markers: objectMarker
    });
	$.goMap.setMap({latitude:'46.836944988044465', longitude:'-71.28376007080078'});
}

$(document).on('homemap:ready', function(e, coords) {
	// Idempotence
	if($("#zoneHomeMap").length !== 0) return;
	$(window).on('resize.homemap', function() {
		if($("#zoneHomeMap").length == 0 && $(window).width() > 960) {
			initHomeMap(coords);
		}
	})
	$(window).trigger('resize.homemap');
	$(document).on('page:change', function() { $(window).off('.homemap'); });
});

Fix merge problem with map


'use strict';

define([
    'jquery',
    'underscore',
    'backbone',
    'router-front-frontrouter',
    'router-forum-forumrouter',
    'router-tag-tagrouter',
], function($, _, Backbone, FrontPageRoter, ForumRouter, TagRouter) {
    var initialize = function() {
        // TODO: Write all application routers, and start them here
        FrontPageRoter.initialize();
        ForumRouter.initialize();
        TagRouter.initialize();

        // Kicking off routing
        Backbone.history.start({
            pushState: true,
            hashChange: false // Continue using URLs for older browsers
        });
    };

    return {
        initialize: initialize
    };
});

'use strict';

define([
    'jquery',
    'underscore',
    'backbone',
    'router-front-frontrouter',
    'router-forum-forumrouter',
    'router-tag-tagrouter',
], function($, _, Backbone, FrontPageRoter, ForumRouter, TagRouter) {
    var initialize = function() {
        FrontPageRoter.initialize();
        ForumRouter.initialize();
        TagRouter.initialize();

        // Kicking off routing
        Backbone.history.start({
            silent: true, // Don't trigger on the initial page
            pushState: true,
            hashChange: false // Continue using URLs for older browsers
        });
    };

    return {
        initialize: initialize
    };
});

Add silent start to `Backbone.history.start` call


'use strict';

/*
 * Outputs objs into key="value" string
 */
module.exports = function attributes(obj) {
  if (!obj) return '';

  let str = '';

  Object.keys(obj).forEach((key) => {
    if (obj[key] && !(obj[key] instanceof Function)) {
      str += ` ${key}="${obj[key]}"`;
    }
  });

  return str;
};

'use strict';

// copied from preact/src/dom/index.js 20161103
function hashToClassName(c) {
  let str = '';

  // eslint-disable-next-line
  for (let prop in c) {
    if (c[prop]) {
      if (str) str += ' ';

      str += prop;
    }
  }

  return str;
}

/*
 * Outputs objs into key="value" string
 */
module.exports = function attributes(obj) {
  if (!obj) return '';

  let str = '';

  Object.keys(obj).forEach((key) => {
    let value = obj[key];

    if (value && !(value instanceof Function)) {
      if (key === 'className') key = 'class';

      if (key === 'class') {
        if (Array.isArray(value)) {
          value = value.filter((value) => value).join(' ');
        } else if (typeof value === 'object') {
          value = hashToClassName(value);
        }
      }

      str += ` ${key}="${value}"`;
    }
  });

  return str;
};

Allow class attribute to be an object


const mongoose = require('mongoose');
const MONGO_URL = process.env.MONGO_URL || 'mongodb://localhost:3101/meteor';

mongoose.Promise = global.Promise;
mongoose.connect(MONGO_URL)
    .catch(error => {
        console.error(`Failed to connect to mongodb: ${error}`);
    })

module.exports = mongoose;

const mongoose = require('mongoose');
const MONGO_URL = process.env.MONGO_URL || 'mongodb://localhost:3101/meteor';

const reconnectIntervalInMs = 1500,
    socketOptions = {
        keepAlive: 120
    },
    options = {
        server: {
            socketOptions,
        },
        replset: {
            socketOptions
        }
    };

const db = mongoose.connection;
mongoose.Promise = global.Promise;

function handleError(error) {
    console.error(`MongoDB connection error: ${error}`);
    setTimeout(connectWithRetry, reconnectIntervalInMs);
}

function connectWithRetry() {
    mongoose.connect(MONGO_URL, options)
        .catch(handleError);
}
connectWithRetry();

db.on('error', handleError);

module.exports = mongoose;

Make database connections more robust against missing mongodb server


"use strict";

module.exports = {
  "env": {
    "node": false
  },
  "rules": {
    // enforces error handling in callbacks (node environment)
    "handle-callback-err": 0,
    // disallow mixing regular variable and require declarations
    "no-mixed-requires": [0, false],
    // disallow use of new operator with the require function
    "no-new-require": 0,
    // disallow string concatenation with __dirname and __filename
    "no-path-concat": 0,
    // disallow process.exit()
    "no-process-exit": 2,
    // restrict usage of specified node modules
    "no-restricted-modules": 0,
    // disallow use of synchronous methods (off by default)
    "no-sync": 0
  }
};

"use strict";

module.exports = {
  "env": {
    "node": true
  },
  "rules": {
    // enforces error handling in callbacks (node environment)
    "handle-callback-err": 0,
    // disallow mixing regular variable and require declarations
    "no-mixed-requires": [0, false],
    // disallow use of new operator with the require function
    "no-new-require": 0,
    // disallow string concatenation with __dirname and __filename
    "no-path-concat": 0,
    // disallow process.exit()
    "no-process-exit": 2,
    // restrict usage of specified node modules
    "no-restricted-modules": 0,
    // disallow use of synchronous methods (off by default)
    "no-sync": 0
  }
};

Set node value to true


angular
  .module('confRegistrationWebApp')
  .filter('localizedCurrency', function($locale) {
    return function(number, currencyCode) {
      let localeId = $locale.id ? $locale.id : 'en-us';
      return number.toLocaleString(localeId, {
        style: 'currency',
        currency: currencyCode,
      });
    };
  });

angular
  .module('confRegistrationWebApp')
  .filter('localizedSymbol', ($locale, $window) => {
    return currencyCode => {
      let localeId = $locale && $locale.id ? $locale.id : 'en-us';
      let symbol = symbolFromFormatToParts(localeId, currencyCode, $window);
      if (symbol) {
        return symbol;
      }
      let number = 0;
      return number
        .toLocaleString(localeId, {
          style: 'currency',
          currency: currencyCode,
        })
        .replace(/[0., ]/g, '');
    };
  });

function symbolFromFormatToParts(localeId, currencyCode, window) {
  if (!('Intl' in window)) {
    return;
  }
  let numberFormat = new Intl.NumberFormat(localeId, {
    style: 'currency',
    currency: currencyCode,
  });
  if (!('formatToParts' in numberFormat)) {
    return;
  }
  return numberFormat.formatToParts().filter(e => e.type === 'currency')[0]
    .value;
}

angular
  .module('confRegistrationWebApp')
  .filter('localizedCurrency', function($locale) {
    return function(number, currencyCode) {
      let localeId = $locale.id ? $locale.id : 'en-us';
      return number.toLocaleString(localeId, {
        style: 'currency',
        currency: currencyCode,
      });
    };
  });

angular
  .module('confRegistrationWebApp')
  .filter('localizedSymbol', ($locale, $window) => {
    return currencyCode => {
      let localeId = $locale && $locale.id ? $locale.id : 'en-us';
      let symbol = symbolFromFormatToParts(localeId, currencyCode, $window);
      if (symbol) {
        return symbol;
      }
      let number = 0;
      return number
        .toLocaleString(localeId, {
          style: 'currency',
          currency: currencyCode,
        })
        .replace(/[0., ]/g, '');
    };
  });

function symbolFromFormatToParts(localeId, currencyCode, window) {
  if (!('Intl' in window)) {
    return;
  }
  let numberFormat = new Intl.NumberFormat(localeId, {
    style: 'currency',
    currency: currencyCode,
  });
  if (!('formatToParts' in numberFormat)) {
    return;
  }
  // Some browsers incorrectly return nan or Nan for the formatToParts function
  // This will filter out all incorrect results.
  const filteredSymbol = numberFormat
    .formatToParts()
    .filter(e => e.type !== 'nan' || e.value !== 'NaN');

  if (filteredSymbol.length > 0) {
    return filteredSymbol.filter(e => e.type === 'currency')[0].value;
  }
}

Add filter for browsers returning incorrect type and values for formatToParts function


/**
 * @constructor
 * @param {number} width
 * @param {number} height
 */
webfont.Size = function (width, height) {
  this.width = width;
  this.height = height;
};

/**
 * Returns true if this size equals other.
 *
 * @param {webfont.Size} other
 * @return {boolean}
 */
webfont.Size.prototype.equals = function (other) {
  return !!other && this.width == other.width && this.height == other.height;
};

/**
 * @constructor
 * @param {number} width
 * @param {number} height
 */
webfont.Size = function (width, height) {
  this.width = width;
  this.height = height;
};

/**
 * Returns true if this size equals other.
 *
 * @param {webfont.Size} other
 * @return {boolean}
 */
webfont.Size.prototype.equals = function (other) {
  return this.equalsWidth(other) && this.equalsHeight(other);
};

/**
 * Returns true if this.width equals other.width
 * @param {webfont.Size} other
 * @return {boolean}
 */
webfont.Size.prototype.equalsWidth = function (other) {
  return !!other && this.width == other.width;
};

/**
 * Returns true if this.height equals other.height
 * @param {webfont.Size} other
 * @return {boolean}
 */
webfont.Size.prototype.equalsHeight = function (other) {
  return !!other && this.height == other.height;
};

Add methods to individually compare width and height to Size.


require('babel-register')

import Koa from 'koa'
import cors from 'koa2-cors'
import Router from 'koa-router'
import bodyParser from 'koa-bodyparser'
import serve from 'koa-static'
import mongoose from 'mongoose'

import error from './middlewares/error'
import logger from './middlewares/logger'
import route from './routes'
import { DATABASE, NODE_ENV, ALLOW_ORIGIN } from '../.env'

mongoose.Promise = global.Promise
mongoose.connect(DATABASE, {
  promiseLibrary: global.Promise,
  config: {
    autoIndex: NODE_ENV !== 'production'
  }
})

const app = new Koa()
const router = new Router()

app.use(error())
app.use(cors({ origin: ALLOW_ORIGIN }))
app.use(logger())
app.use(serve('assets'))
app.use(bodyParser())

route(router)
app.use(router.routes())
app.use(router.allowedMethods())

app.listen(3001)

require('babel-register')

import Koa from 'koa'
import cors from 'koa2-cors'
import Router from 'koa-router'
import bodyParser from 'koa-bodyparser'
import serve from 'koa-static'
import mongoose from 'mongoose'

import error from './middlewares/error'
import logger from './middlewares/logger'
import route from './routes'
import { DATABASE, NODE_ENV, ALLOW_ORIGIN } from '../.env'

mongoose.Promise = global.Promise
mongoose.connect(DATABASE, {
	promiseLibrary: global.Promise,
	config: {
		autoIndex: NODE_ENV !== 'production'
	}
})

const app = new Koa()
const router = new Router()

app.use(error())
app.use(
	cors({
		origin: ALLOW_ORIGIN,
		methods: 'GET,HEAD,PUT,PATCH,POST,DELETE'
	})
)
app.use(logger())
app.use(serve('assets'))
app.use(bodyParser())

route(router)
app.use(router.routes())
app.use(router.allowedMethods())

app.listen(3001)

Allow method PATCH in cors


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

const middlewares = [
];

if (process.env.NODE_ENV !== 'production') {
  middlewares.push(require('redux-logger')({
    actionTransformer(action) {
      return Object.assign({}, action, { type: String(action.type) });
    },
  }));
}

const createStoreWithMiddleware = applyMiddleware(...middlewares)(createStore);
const store = createStoreWithMiddleware(RootReducer);

// TODO: hot module replacement for reducers

export default store;

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

const middlewares = [
];

if (process.env.NODE_ENV !== 'production') {
  middlewares.push(require('redux-logger')({
    actionTransformer(action) {
      return Object.assign({}, action, { type: String(action.type) });
    },
    stateTransformer(state) {
      return state.toJS();
    },
  }));
}

const createStoreWithMiddleware = applyMiddleware(...middlewares)(createStore);
const store = createStoreWithMiddleware(RootReducer);

// TODO: hot module replacement for reducers

export default store;

Transform state to JS object for redux-logger


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

var path = './node_modules/prebuild-install/bin.js';
var platform = process.platform;
var arch = process.arch;

if (platform === 'linux' && arch === 'arm') {
  arch += 'v' + process.config.variables.arm_version;
  path += ' --arch=' + arch;
}

exec(path, function(err, stdout, stderr) {
  console.log(stdout);
  console.log(stderr);
  if (err) process.exit(1);
});

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

var pbi = 'prebuild-install';
var platform = process.platform;
var arch = process.arch;

if (platform === 'linux' && arch === 'arm') {
  arch += 'v' + process.config.variables.arm_version;
  pbi += ' --arch=' + arch;
}

exec(pbi, function(err, stdout, stderr) {
  console.log(stdout);
  console.log(stderr);
  if (err) process.exit(1);
});

Fix path error in npm installation


/**
 * Main application file of Fittable
 *  - Requires React.js to be loaded before loading this
 */

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

import Counterpart from 'counterpart'

import 'moment/locale/cs'
import LocaleCS from './locales/cs.json'
import LocaleEN from './locales/en.json'

import AppContainer from './containers/AppContainer'

const rootElement = document.getElementById('fittable')

// Register translations
Counterpart.registerTranslations('en', LocaleEN)
Counterpart.registerTranslations('cs', Object.assign(LocaleCS, {
  counterpart: {
    pluralize: (entry, count) => {
      entry[ (count === 0 && 'zero' in entry) ? 'zero' : (count === 1) ? 'one' : 'other' ]
    },
  },
}))
// Counterpart.setLocale(options.locale)
// Moment.locale(options.locale)

ReactDOM.render(React.createElement(AppContainer), rootElement)

/**
 * Main application file of Fittable
 *  - Requires React.js to be loaded before loading this
 */

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

import Counterpart from 'counterpart'

import 'moment/locale/cs'
import LocaleCS from './locales/cs.json'
import LocaleEN from './locales/en.json'

import AppContainer from './containers/AppContainer'

const rootElement = document.getElementById('fittable')

// Register translations
Counterpart.registerTranslations('en', LocaleEN)
Counterpart.registerTranslations('cs', Object.assign(LocaleCS, {
  counterpart: {
    pluralize: (entry, count) => {
      entry[ (count === 0 && 'zero' in entry) ? 'zero' : (count === 1) ? 'one' : 'other' ]
    },
  },
}))
// Counterpart.setLocale(options.locale)
// Moment.locale(options.locale)

ReactDOM.render(<AppContainer />, rootElement)

Use JSX to initialize AppContainer


import {a,b} from 'module2.js';
import {jquery} from 'https://code.jquery.com/jquery-2.1.3.min.js';
import {d as foo2} from 'sub1/sub1.js';

var e = 10,
    f = 20;

function c () {
    return 30;
}

export { e, f as z };

export default c;

import {a,b} from 'module2.js';
import {jquery} from 'https://code.jquery.com/jquery-2.1.3.min.js';
import {d as foo2} from 'sub1/sub1.js';

var e = 10,
    f = 20;

function c () {
    return 30;
}

let empty = () => {};

(() => "foobar")() // returns "foobar"

var simple = a => a > 15 ? 15 : a;
simple(16); // 15
simple(10); // 10

var complex = (a, b) => {
    if (a > b) {
        return a;
    } else {
        return b;
    }
}

export { e, f as z };

export default c;

Add more ES6 code in example


import { Kinvey as CoreKinvey } from 'kinvey-javascript-sdk-core';
import { NetworkRack } from 'kinvey-javascript-sdk-core/es5/rack/rack';
import { KinveyHttpMiddleware } from 'kinvey-javascript-sdk-core/es5/rack/middleware/http';
import { HttpMiddleware } from './http';
import { Push } from 'kinvey-phonegap-sdk/es5/push';
import Device from './device';

// Add the Http Middleware to the network rack
const networkRack = NetworkRack.sharedInstance();
networkRack.swap(KinveyHttpMiddleware, new HttpMiddleware());

// Extend the Core Kinvey class
class Kinvey extends CoreKinvey {
  static init(options) {
    // Initialize Kinvey
    const client = super.init(options);

    // Add Push module to Kinvey
    if (Device.isiOS() || Device.isAndroid()) {
      this.Push = new Push();
    }

    // Return the client
    return client;
  }
}

// ngKinveyProvider class
export default class KinveyProvider {
  init(options) {
    return Kinvey.init(options);
  }

  $get() {
    return Kinvey;
  }
}

import { Kinvey } from 'kinvey-phonegap-sdk';
import { NetworkRack } from 'kinvey-javascript-sdk-core/es5/rack/rack';
import { HttpMiddleware as KinveyHttpMiddleware } from 'kinvey-javascript-sdk-core/es5/rack/middleware/http';
import { HttpMiddleware } from './http';

// Add the Http Middleware to the network rack
const networkRack = NetworkRack.sharedInstance();
networkRack.swap(KinveyHttpMiddleware, new HttpMiddleware());

// ngKinveyProvider class
export default class KinveyProvider {
  init(options) {
    return Kinvey.init(options);
  }

  $get() {
    return Kinvey;
  }
}

Use the kinvey-phonegap-sdk Kinvey class with ngKinveyProvider


var $module=(function($B){

    return {
        modules :
            {'__get__':function(){return $B.builtins.dict($B.JSObject($B.imported))},
             '__set__':0 // data descriptor, to force use of __get__
            },
        stderr : {
            __set__:function(self, obj, value){$B.stderr = value},
            write:function(data){$B.stderr.write(data)}
            },
        stdout : {
            __set__:function(self, obj, value){$B.stdout = value},
            write:function(data){$B.stdout.write(data)}
            },
        stdin : $B.stdin
    }
})(__BRYTHON__)

var $module=(function($B){

    return {
        modules :
            {'__get__':function(){return $B.builtins.dict($B.JSObject($B.imported))},
             '__set__':0 // data descriptor, to force use of __get__
            },
        stderr : {
            __get__:function(){return $B.stderr},
            __set__:function(self, obj, value){console.log('set stderr');$B.stderr = value},
            write:function(data){$B.builtins.getattr($B.stderr,"write")(data)}
            },
        stdout : {
            __get__:function(){return $B.stdout},
            __set__:function(self, obj, value){console.log('set stdout');$B.stdout = value},
            write:function(data){$B.builtins.getattr($B.stdout,"write")(data)}
            },
        stdin : $B.stdin
    }
})(__BRYTHON__)

Add __get__ to sys.stderr and stdout


var angular = require('angular')

require('angular-aria')
require('angular-animate')
require('angular-material')
require('angular-material/angular-material.css')
require('angular-route')
require('3Dmol/release/3Dmol.js')

var app = angular.module('chemPhyWebApp', ['ngRoute', 'ngMaterial'])

var angular = require('angular')

require('angular-aria')
require('angular-animate')
require('angular-material')
require('angular-material/angular-material.css')
require('angular-route')

var app = angular.module('chemPhyWebApp', ['ngRoute', 'ngMaterial'])

Remove require(...) for 3Dmol from main app


/**
* Contact. Send messages
*/
const express = require('express')
const router = express.Router()
const _ = require('lodash')

const content = require('../content/english/contact.json')

// Routes
router.get('/', contact)
router.get('/talk', contactTalk)
router.get('/feedback', feedback)

module.exports = router

// FUNCTIONS

function contact (req, res, next) {
  return res.render('contact', {
    title: 'Contact me',
    path: 'contact',
    content: content,
    redirect: req.query.redirect
  })
}

function contactTalk (req, res, next) {
  let talkContact = _.cloneDeep(content)
  talkContact.config.claim = "Are you organizing an event and want me to talk? I'll be happy to be part of it! Send me an email with a proposal and let's talk! (jorge at ferreiro dot me)."
  talkContact.sendButton = "Submit proposal"
  talkContact.form.message = "What's your event about and when is it?"

  res.render('contact', {
    title: 'Jorge at your event',
    content: talkContact,
    path: 'talks',
    redirect: req.query.redirect
  })
}

function feedback(req, res, next) {
  const feedbackContent = require('../content/english/feedback.json')
  res.render('feedback', {
    title: 'Feeback',
    path: 'feedback',
    content: feedbackContent,
    redirect: req.query.redirect
  })
}

/**
* Contact. Send messages
*/
const express = require('express')
const router = express.Router()
const _ = require('lodash')

const content = require('../content/english/contact.json')

// Routes
router.get('/', contact)
router.get('/talk', contactTalk)
router.get('/feedback', feedback)

module.exports = router

// FUNCTIONS

function contact (req, res, next) {
  return res.render('contact', {
    title: 'Contact me',
    path: 'contact',
    content: content,
    redirect: req.query.redirect
  })
}

function contactTalk (req, res, next) {
  let talkContact = _.cloneDeep(content)
  talkContact.config.claim = "Are you organizing an event and want me to talk? I'll be happy to be part of it! Send me an email with a proposal and let's talk! (jorge at ferreiro dot me)."
  talkContact.sendButton = "Submit proposal"
  talkContact.form.message = "What's your event about and when is it?"

  res.render('contact', {
    title: 'Bring me to your event',
    content: talkContact,
    path: 'talks',
    redirect: req.query.redirect
  })
}

function feedback(req, res, next) {
  const feedbackContent = require('../content/english/feedback.json')
  res.render('feedback', {
    title: 'Feeback',
    path: 'feedback',
    content: feedbackContent,
    redirect: req.query.redirect
  })
}

Bring me to your event title


(function() {
  'use strict';

  angular.module('PDRClient')
      .controller('AnalysisReportCtrl', AnalysisReportCtrl);

  AnalysisReportCtrl.$inject = [
    'analysis_priorities',
    'analysis_comparison_data',
    'analysis_review_header_data'
  ];

  function AnalysisReportCtrl(analysis_priorities, analysis_comparison_data, analysis_review_header_data) {
    var vm = this;

    vm.priorities = analysis_priorities;
    vm.comparisonData = analysis_comparison_data;
    vm.reviewHeaderData = analysis_review_header_data;
  }
})();

(function() {
  'use strict';

  angular.module('PDRClient')
      .controller('AnalysisReportCtrl', AnalysisReportCtrl);

  AnalysisReportCtrl.$inject = [
    'analysis_comparison_data',
    'analysis_review_header_data'
  ];

  function AnalysisReportCtrl(analysis_comparison_data, analysis_review_header_data) {
    var vm = this;

    vm.comparisonData = analysis_comparison_data;
    vm.reviewHeaderData = analysis_review_header_data;
  }
})();

Remove unnecessarily injected resource from AnalysisReport


const merge = require('webpack-merge');
const UglifyJsPlugin = require('uglifyjs-webpack-plugin');

const devConfig = require('./webpack.website-dev.js');

module.exports = merge(devConfig, {
  mode: 'production',
  devtool: false,
  plugins: [
    new UglifyJsPlugin()
  ]
});

const merge = require('webpack-merge');
const UglifyJsPlugin = require('uglifyjs-webpack-plugin');

const devConfig = require('./webpack.website-dev.js');

module.exports = merge(devConfig, {
  mode: 'production',
  devtool: false,
  plugins: [
    new UglifyJsPlugin(),
    new webpack.DefinePlugin({
      PRODUCTION: true
    })
  ]
});

Define the PRODUCTION variable in production.


import {build} from './builder';

const config = {
    user: {
        ttl: 300,
        api: {
            getUsers: (x) => x,
            deleteUser: (x) => x,
        },
        invalidates: ['alles']
    },
    userPreview: {
        ttl: 200,
        api: {
            getPreviews: (x) => x,
            updatePreview: (x) => x,
        },
        invalidates: ['fda'],
        viewOf: 'user'
    }
};
config.user.api.getUsers.operation = 'READ';
config.user.api.deleteUser.operation = 'DELETE';
config.userPreview.api.getPreviews.operation = 'READ';
config.userPreview.api.updatePreview.operation = 'UPDATE';

describe('builder', () => {
    it('builds stuff', () => {
        const api = build(config);
        expect(api).to.not.equal(null);
    });
});

import {build} from './builder';
import sinon from 'sinon';

const getUsers = () => Promise.resolve([{id: 1}, {id: 2}]);
getUsers.operation = 'READ';

const deleteUser = () => Promise.resolve();
deleteUser.operation = 'DELETE';

const config = () => ({
    user: {
        ttl: 300,
        api: {
            getUsers,
            deleteUser
        },
        invalidates: ['alles']
    }
});

describe('builder', () => {
    it('Builds the API', () => {
        const api = build(config());
        expect(api).to.be.ok;
    });
    it('Two read api calls will only require one api request to be made', (done) => {
        const myConfig = config();
        myConfig.user.api.getUsers = sinon.spy(myConfig.user.api.getUsers);
        const api = build(myConfig);

        const expectOnlyOneApiCall = () => {
            expect(myConfig.user.api.getUsers.callCount).to.equal(1);
            done();
        };

        Promise.resolve()
               .then(() => api.user.getUsers())
               .then(() => api.user.getUsers())
               .then(expectOnlyOneApiCall);
    });
});

Update builder test to include a test of the produced api


var RouteHandler  = require( 'react-router' ).RouteHandler,
    Header        = require( './header' ),
    Modal         = require( './modal' ),
    classNames = require('classnames'),
    { connect } = require('react-redux'),
    { routeActions } = require('react-router-redux'),
    { compose } = require( 'redux' ),
    HTML5Backend = require('react-dnd-html5-backend'),
    DragDropContext = require('react-dnd').DragDropContext
;
import React, { Component, PropTypes } from 'react';

// depends Header, Nav
class App extends Component {

  componentDidMount() {
    const {
      dispatch,
    } = this.props;

    if ( this.props.routing.location.pathname === "/" ) {
      dispatch(routeActions.push('/home'));
    }
  }

  render() {
    var pathname = this.props.routing.location.pathname;
    var appContentClasses = classNames({
      'app-wide-view': true,
      'home-page-view': pathname  === '/home'
    });
    return(

            {this.props.children}

        <Modal />

    );
   }
};

function selectState(newState) {
  return {
    routing: newState.routing
  };
}

export default connect(selectState)(App);

var RouteHandler  = require( 'react-router' ).RouteHandler,
    classNames = require('classnames'),
    { connect } = require('react-redux'),
    { routeActions } = require('react-router-redux'),
    { compose } = require( 'redux' ),
    HTML5Backend = require('react-dnd-html5-backend'),
    DragDropContext = require('react-dnd').DragDropContext
;
import React, { Component, PropTypes } from 'react';
import Header from './header';
import Modal from './modal';

// depends Header, Nav
class App extends Component {

  componentDidMount() {
    const {
      dispatch,
    } = this.props;

    if ( this.props.routing.location.pathname === "/" ) {
      dispatch(routeActions.push('/home'));
    }
  }

  render() {
    var pathname = this.props.routing.location.pathname;
    var appContentClasses = classNames({
      'app-wide-view': true,
      'home-page-view': pathname  === '/home'
    });
    return(

            {this.props.children}

        <Modal />

    );
   }
};

function selectState(newState) {
  return {
    routing: newState.routing
  };
}

export default connect(selectState)(App);

Change import method so components show up


import Vue from 'vue';
import Translate from '~/vue_shared/translate';
import axios from '~/lib/utils/axios_utils';
import { initializeTestTimeout } from './helpers/timeout';

// wait for pending setTimeout()s
afterEach(() => {
  jest.runAllTimers();
});

initializeTestTimeout(300);

// fail tests for unmocked requests
beforeEach(done => {
  axios.defaults.adapter = config => {
    const error = new Error(`Unexpected unmocked request: ${JSON.stringify(config, null, 2)}`);
    error.config = config;
    done.fail(error);
    return Promise.reject(error);
  };

  done();
});

Vue.use(Translate);

// workaround for JSDOM not supporting innerText
// see https://github.com/jsdom/jsdom/issues/1245
Object.defineProperty(global.Element.prototype, 'innerText', {
  get() {
    return this.textContent;
  },
  configurable: true, // make it so that it doesn't blow chunks on re-running tests with things like --watch
});

Add workaround for innerText in Jest


$("#submit-all").attr("disabled", true);
$("#submit-all").hide();

Dropzone.options.uploadDropzone = {
  autoProcessQueue: false,
  init: function() {
    var submitButton = document.querySelector("#submit-all")
        myDropzone = this;
    submitButton.addEventListener("click", function() {
      myDropzone.processQueue();
    });

    this.on("error", function(file, errorMessage) {
      $("#upload-file-validation").text("Please upload only jpg, png or jpeg file");
      $("#upload-file-validation").addClass("alert");
      this.removeAllFiles(file);
      $("#submit-all").hide();
    });

    this.on("addedfile", function() {
      $("#submit-all").show();
      $("#upload-file-validation").text("Upload the scanned template");
      $("#upload-file-validation").removeClass("alert");
    });

    this.on("success", function(file, response) {
      window.location="/finish?key=" + response.key + "&fontname=" + response.font_name;
    });

    this.on("maxfilesexceeded", function(file) {
      $("#upload-file-validation").text("Upload the scanned template");
      $("#upload-file-validation").removeClass("alert");
      this.removeAllFiles();
      this.addFile(file);
      $("#submit-all").show();
    });

  },
  paramName: "file", // The name that will be used to transfer the file
  maxFilesize: 20, // MB
  maxFiles: 1,
  acceptedFiles: ".png, .jpg, .jpeg",
  dictDefaultMessage: "Drop image here<br>or click to upload"
};

$("#submit-all").attr("disabled", true);
$("#submit-all").hide();

Dropzone.options.uploadDropzone = {
  autoProcessQueue: false,
  init: function() {
    var submitButton = document.querySelector("#submit-all")
        myDropzone = this;
    submitButton.addEventListener("click", function() {
      myDropzone.processQueue();
    });

    this.on("error", function(file, errorMessage) {
      $("#upload-file-validation").text("Please upload jpg, png or jpeg file less than 20 MB");
      $("#upload-file-validation").addClass("alert");
      this.removeAllFiles(file);
      $("#submit-all").hide();
    });

    this.on("addedfile", function() {
      $("#submit-all").show();
      $("#upload-file-validation").text("Upload the scanned template");
      $("#upload-file-validation").removeClass("alert");
    });

    this.on("success", function(file, response) {
      window.location="/finish?key=" + response.key + "&fontname=" + response.font_name;
    });

    this.on("maxfilesexceeded", function(file) {
      $("#upload-file-validation").text("Upload the scanned template");
      $("#upload-file-validation").removeClass("alert");
      this.removeAllFiles();
      this.addFile(file);
      $("#submit-all").show();
    });

  },
  paramName: "file", // The name that will be used to transfer the file
  maxFilesize: 20, // MB
  maxFiles: 1,
  acceptedFiles: ".png, .jpg, .jpeg",
  dictDefaultMessage: "Drop image here<br>or click to upload"
};

Update front end error message


"use strict";

var $    = require("gulp-load-plugins")();
var gulp = require("gulp");
var notify = require("./gulp/utils/notify");

/**
 * Load all tasks from the gulp directory.
 */
require("require-dir")("gulp");

/**
 * Build
 * Runs style-related, script-related and image-related tasks to create a
 * distribution directory at `/dist`.
 */
gulp.task("build", ["style:compile", "markup:minify", "script:minify", "image:minify", "source"], function(callback) {
  notify.showNotification({
    subtitle: "Task build",
    message: "Build complete!"
  });

  callback();
});

/**
 * Default
 * Lists all available tasks.
 */
gulp.task("default", function () {
  $.util.log("Available tasks:");
  Object.keys(gulp.tasks).forEach(function (taskName) {
    $.util.log("\t", $.util.colors.yellow(taskName));
  });
  $.util.log("Use", $.util.colors.green("gulp serve"), "to get started.");
});

"use strict";

var $    = require("gulp-load-plugins")();
var gulp = require("gulp");
var notify = require("./gulp/utils/notify");

/**
 * Load all tasks from the gulp directory.
 */
require("require-dir")("gulp");

/**
 * Build
 * Creates a build artifact, ready for deployment.
 */
gulp.task("build", ["style:compile", "markup:minify", "script:minify", "image:minify", "source"], function(callback) {
  notify.showNotification({
    subtitle: "Task build",
    message: "Build complete!"
  });

  callback();
});

/**
 * Default
 * Lists all available tasks.
 */
gulp.task("default", function () {
  $.util.log("Available tasks:");
  Object.keys(gulp.tasks).forEach(function (taskName) {
    $.util.log("\t", $.util.colors.yellow(taskName));
  });
  $.util.log("Use", $.util.colors.green("gulp serve"), "to get started.");
});

Use a terse doc comment.


/* global window */
'use strict'

var win = typeof window === 'undefined' ? null : window

exports.routeFromLocation = function routeFromLocation (event, router) {
  router.route(win.location.href)
}

exports.routeFromLinkClick = function routeFromLinkClick (event, router) {
  setTimeout(function () {
    // ignore if it could open a new window, if a right click
    if (
      event.metaKey || event.shiftKey || event.ctrlKey || event.altKey ||
      event.which === 3 || event.button === 2 || event.defaultPrevented
    ) return

    // ignore if not a link click
    var html = win.document.documentElement
    var target = event.target
    while (target && !target.href && target !== html) {
      target = target.parentNode
    }
    if (!target || !target.href) return

    // ignore if not the same origin as the page
    var location = win.location
    var origin = location.origin || (location.protocol + '//' + location.host)
    if (target.href.slice(0, origin.length) !== origin) return

    event.preventDefault()
    var path = target.href.slice(origin.length)
    var hash = router.hash
    if (hash && path.indexOf(hash) >= 0) {
      path = path.slice(path.indexOf(hash) + hash.length)
    }
    router.navigate(path)
  }, 0)
}

/* global window */
'use strict'

var win = typeof window === 'undefined' ? null : window

exports.routeFromLocation = function routeFromLocation (event, router) {
  router.route(win.location.href)
}

exports.routeFromLinkClick = function routeFromLinkClick (event, router) {
  // ignore if it could open a new window, if a right click
  if (
    event.metaKey || event.shiftKey || event.ctrlKey || event.altKey ||
    event.which === 3 || event.button === 2
  ) return

  // ignore if not a link click
  var html = win.document.documentElement
  var target = event.target
  while (target && !target.href && target !== html) {
    target = target.parentNode
  }
  if (!target || !target.href) return

  // ignore if not the same origin as the page
  var location = win.location
  var origin = location.origin || (location.protocol + '//' + location.host)
  if (target.href.slice(0, origin.length) !== origin) return

  event.preventDefault()
  var path = target.href.slice(origin.length)
  var hash = router.hash
  if (hash && path.indexOf(hash) >= 0) {
    path = path.slice(path.indexOf(hash) + hash.length)
  }
  router.navigate(path)
}

Revert "check defaultPrevented when intercepting link clicks"


var gulp = require('gulp');

var browserify = require('gulp-browserify');
var jshint = require('gulp-jshint');
var mocha = require('gulp-mocha');
var uglify = require('gulp-uglify');
var size = require('gulp-size');
var rename = require('gulp-rename');

gulp.task('scripts', function() {
  gulp.src('index.js')
      .pipe(browserify())
      .pipe(jshint())
      .pipe(rename('grex.js'))
      .pipe(gulp.dest('./build'))
      .pipe(size({ showFiles: true }))
      // Minified version
      .pipe(uglify())
      .pipe(rename('grex.min.js'))
      .pipe(gulp.dest('./build'))
      .pipe(size({ showFiles: true }));
});

gulp.task('test', function() {
  require('should');

  gulp.src('test/**/*')
      .pipe(mocha({
        reporter: 'spec',
      }))
      .on('error', function(error) {
        console.error('\nError:', error.plugin);
        console.error(error.message);
      });
});

gulp.task('watch', function() {
  function onChange(event) {
    console.log('File', event.type +':', event.path);
  }

  gulp.watch('src/**/*', ['test']).on('change', onChange);
  gulp.watch('test/**/*', ['test']).on('change', onChange);
});

gulp.task('default', ['dev']);

gulp.task('dev', ['build', 'test', 'watch']);

gulp.task('build', ['test', 'scripts']);

var gulp = require('gulp');

var browserify = require('gulp-browserify');
var jshint = require('gulp-jshint');
var mocha = require('gulp-mocha');
var uglify = require('gulp-uglify');
var size = require('gulp-size');
var rename = require('gulp-rename');

gulp.task('scripts', function() {
  gulp.src('index.js')
      .pipe(browserify())
      .pipe(jshint())
      .pipe(rename('grex.js'))
      .pipe(gulp.dest('./build'))
      .pipe(size({ showFiles: true }))
      // Minified version
      .pipe(uglify())
      .pipe(rename('grex.min.js'))
      .pipe(gulp.dest('./build'))
      .pipe(size({ showFiles: true }));
});

gulp.task('test', function() {
  require('should');

  gulp.src('test/**/*')
      .pipe(mocha({
        reporter: 'spec',
      }))
      .on('error', function(error) {
        console.error('\nError:', error.plugin);
        console.error(error.message);
      });
});

gulp.task('watch', function() {
  function onChange(event) {
    console.log('File', event.type +':', event.path);
  }

  gulp.watch('src/**/*', ['test']).on('change', onChange);
  gulp.watch('test/**/*', ['test']).on('change', onChange);
});

gulp.task('default', ['dev']);

gulp.task('dev', ['test', 'watch']);

gulp.task('build', ['test', 'scripts']);

Update gulp dev task to no longer build scripts


'use strict';
angular.module('omniFilters', [])
    .filter('bigjs', ['$sce', function ($sce) {
        return function (input, format, noZerosTrimming) {
            var dropFraction = false;

            if (input == null || format == null)
                return input;
            if (format === '')
                return '';
            if (format.indexOf('.') < 0) {
                format = format.replace(/(\D*$)/, ".0$1");
                dropFraction = true;
            }

            var nums = Big(input).valueOf();
            if (!noZerosTrimming && nums !== '0') {
                nums = nums.replace(/0+(?=\D+$)|0+$/g, '').replace(/(^\D*|:)?0+/g, function(str, p1) {return p1 ? p1 + '0' : str;});
            }
            nums = nums.split('.');

            if (nums[1] !== undefined && format.indexOf('.') >= 0 && nums[1].replace(/\D/g, '') === '') {
                nums[1] = '0' + nums[1];
            }

            if (nums.length > 1 && !dropFraction) {
                nums[0] = '' + nums[0] + '';
                nums[1] = '.' + nums[1] + '';
            }
            else {
                nums[0] = '' + nums[0] + '';
                nums = [nums[0]];
            }

            return $sce.trustAsHtml(nums.join(''));
        };
    }]);

'use strict';
angular.module('omniFilters', [])
    .filter('bigjs', ['$sce', function ($sce) {
        return function (input, format, noZerosTrimming) {
            var dropFraction = false;

            if (input == null || format == null)
                return input;
            if (format === '')
                return '';
            if (format.indexOf('.') < 0) {
                format = format.replace(/(\D*$)/, ".0$1");
                dropFraction = true;
            }

            var nums = Big(input).toFixed();
            if (!noZerosTrimming && nums !== '0') {
                nums = nums.replace(/0+(?=\D+$)|0+$/g, '').replace(/(^\D*|:)?0+/g, function(str, p1) {return p1 ? p1 + '0' : str;});
            }
            nums = nums.split('.');

            if (nums[1] !== undefined && format.indexOf('.') >= 0 && nums[1].replace(/\D/g, '') === '') {
                nums[1] = '0' + nums[1];
            }

            if (nums.length > 1 && !dropFraction) {
                nums[0] = '' + nums[0] + '';
                nums[1] = '.' + nums[1] + '';
            }
            else {
                nums[0] = '' + nums[0] + '';
                nums = [nums[0]];
            }

            return $sce.trustAsHtml(nums.join(''));
        };
    }]);

Use toFixed for bigjs filter


function Scratch3ControlBlocks(runtime) {
    /**
     * The runtime instantiating this block package.
     * @type {Runtime}
     */
    this.runtime = runtime;
}

/**
 * Retrieve the block primitives implemented by this package.
 * @return {Object.<string, Function>} Mapping of opcode to Function.
 */
Scratch3ControlBlocks.prototype.getPrimitives = function() {
    return {
        'control_repeat': this.repeat,
        'control_forever': this.forever,
        'control_wait': this.wait,
        'control_stop': this.stop
    };
};

Scratch3ControlBlocks.prototype.repeat = function(argValues, util) {
    // Initialize loop
    if (util.stackFrame.loopCounter === undefined) {
        util.stackFrame.loopCounter = parseInt(argValues[0]); // @todo arg
    }
    // Decrease counter
    util.stackFrame.loopCounter--;
    // If we still have some left, start the substack
    if (util.stackFrame.loopCounter >= 0) {
        util.startSubstack();
    }
};

Scratch3ControlBlocks.prototype.forever = function(argValues, util) {
    util.startSubstack();
};

Scratch3ControlBlocks.prototype.wait = function(argValues, util) {
    util.yield();
    util.timeout(function() {
        util.done();
    }, 1000 * parseFloat(argValues[0]));
};

Scratch3ControlBlocks.prototype.stop = function() {
    // @todo - don't use this.runtime
    this.runtime.stopAll();
};

module.exports = Scratch3ControlBlocks;

function Scratch3ControlBlocks(runtime) {
    /**
     * The runtime instantiating this block package.
     * @type {Runtime}
     */
    this.runtime = runtime;
}

/**
 * Retrieve the block primitives implemented by this package.
 * @return {Object.<string, Function>} Mapping of opcode to Function.
 */
Scratch3ControlBlocks.prototype.getPrimitives = function() {
    return {
        'control_repeat': this.repeat,
        'control_forever': this.forever,
        'control_wait': this.wait,
        'control_stop': this.stop
    };
};

Scratch3ControlBlocks.prototype.repeat = function(argValues, util) {
    // Initialize loop
    if (util.stackFrame.loopCounter === undefined) {
        util.stackFrame.loopCounter = parseInt(argValues[0]); // @todo arg
    }
    // Decrease counter
    util.stackFrame.loopCounter--;
    // If we still have some left, start the substack
    if (util.stackFrame.loopCounter >= 0) {
        util.startSubstack();
    }
};

Scratch3ControlBlocks.prototype.forever = function(argValues, util) {
    util.startSubstack();
};

Scratch3ControlBlocks.prototype.wait = function(argValues, util) {
    util.yield();
    util.timeout(function() {
        util.done();
    }, 1000 * argValues.DURATION);
};

Scratch3ControlBlocks.prototype.stop = function() {
    // @todo - don't use this.runtime
    this.runtime.stopAll();
};

module.exports = Scratch3ControlBlocks;

Fix implementation of "wait" block


import React, { Component } from 'react';
import ChatMessages from './ChatMessages';
import ChatFooter from './ChatFooter';

class Chatbody extends Component {
    constructor(props) {
        super(props);
        this.state = {
            partner: {
                gender: 'female',
                university: 'University of Athens, Greece'
            },
            me: {
                gender: 'undefined',
                university: 'National Technical University of Athens, Greece'
            },
            messages: [
                {from: 'partner', timestamp: new Date(), content: 'hi, how are you?',},
                {from: 'me', timestamp: new Date(), content: 'Fine, you?',},
            ]
        };
    }

    handleNewMessage = (message) => {
        let newMessages = this.state.messages;
        let newMessage = {
            content: message,
            timestamp: new Date(),
            from: 'me'
        };
        newMessages.push(newMessage);
        this.setState({messages: newMessages});
        //TODO: Message request to backend
    }

    render() {
        return (
            <div className="Chatbox">
                <ChatMessages messages={this.state.messages} partner={this.state.partner} me={this.state.me} />
                <ChatFooter handleNewMessage={this.handleNewMessage} />
            </div>
        );
    }
}

export default Chatbody;

import React, { Component } from 'react';
import ChatMessages from './ChatMessages';
import ChatFooter from './ChatFooter';
import connector from '../connection/connector';
import * as config from '../connection/config';

class Chatbody extends Component {
    constructor(props) {
        super(props);
        this.state = {
            partner: {
                gender: 'female',
                university: 'University of Athens, Greece'
            },
            me: {
                gender: 'undefined',
                university: 'National Technical University of Athens, Greece'
            },
            messages: [
                {from: 'partner', timestamp: new Date(), content: 'hi, how are you?',},
                {from: 'me', timestamp: new Date(), content: 'Fine, you?',},
            ]
        };
    }

    handleNewMessage = (message, from) => {
        if (from === undefined) {
            from = 'me';
        }
        let newMessages = this.state.messages;
        let newMessage = {
            content: message,
            timestamp: new Date(),
            from: from
        };
        newMessages.push(newMessage);
        this.setState({messages: newMessages});
        //TODO: Message request to backend
    }

    render() {
        return (
            <div className="Chatbox">
                <ChatMessages messages={this.state.messages} partner={this.state.partner} me={this.state.me} />
                <ChatFooter handleNewMessage={this.handleNewMessage} />
            </div>
        );
    }
}

export default Chatbody;

Add from parameter in handleNewMessage


import React, {PropTypes} from 'react'
import {List, ListItem} from 'material-ui/List'
import Divider from 'material-ui/Divider'

const TeamListItem = props => {
  let memberCount = props.members

  return (

      <Divider/>
      <ListItem
        onClick={() => props.onClick()}
        primaryText={props.name}
        secondaryTextLines={2}
        rightAvatar= {
          <a href={`tel:${props.phone}`}>
            {props.phone}

        }
      />

  )
}

TeamListItem.propTypes = {
  members: React.PropTypes.array,
  name: React.PropTypes.string,
  scans: React.PropTypes.string,
  onClick: React.PropTypes.func
}

export default TeamListItem

import React, {PropTypes} from 'react'
import {List, ListItem} from 'material-ui/List'
import Divider from 'material-ui/Divider'
import Avatar from 'material-ui/Avatar'
import SocialPersonOutline from 'material-ui/svg-icons/social/person-outline';
const TeamListItem = props => {
  let memberCount = props.members
  let avatar
  if (props.photoUrl) {
    avatar = <Avatar src={props.pictureUrl} />
  } else {
    avatar = <SocialPersonOutline />
  }

  return (

      <Divider/>
      <ListItem
        onClick={() => props.onClick()}
        primaryText={props.name}
        secondaryText={
          <a href={`tel:${props.phone}`}>
            {props.phone}

        }
        leftAvatar={avatar}
      />

  )
}

TeamListItem.propTypes = {
  members: React.PropTypes.array,
  name: React.PropTypes.string,
  scans: React.PropTypes.string,
  onClick: React.PropTypes.func
}

export default TeamListItem

Add member avatars and move phone link


import m from 'mithril';
import attributes from './attributes';

export let Grid = {
	view(ctrl, args, ...children) {
		args = args || {};
		let attr = attributes(args);

		attr.class.push('mdl-grid');
		return
{children}
;
	}
};

export let Cell = {
	view(ctrl, args, ...children) {
		args = args || {};
		let attr = attributes(args);
		let {align, width, phone, tablet, desktop, nophone, notablet, nodesktop} = args;

		attr.class.push('mdl-cell');
		if(align) attr.class.push(`mdl-cell--${align}`);
		if(width) attr.class.push(`mdl-cell--${width}-col`);
		if(phone) attr.class.push(`mdl-cell--${phone}-col-phone`);
		if(tablet) attr.class.push(`mdl-cell--${tablet}-col-tablet`);
		if(desktop) attr.class.push(`mdl-cell--${desktop}-col-desktop`);
		if(nophone) attr.class.push('mdl-cell--hide-phone');
		if(notablet) attr.class.push('mdl-cell--hide-tablet');
		if(nodesktop) attr.class.push('mdl-cell--hide-desktop');

		return
{children}
;
	}
};

import m from 'mithril';
import attributes from './attributes';

export let Grid = {
	view(ctrl, args, ...children) {
		args = args || {};
		let attr = attributes(args);
		let {nospacing} = args;

		attr.class.push('mdl-grid');
		if (nospacing) attr.class.push('mdl-grid--no-spacing');
		return
{children}
;
	}
};

export let Cell = {
	view(ctrl, args, ...children) {
		args = args || {};
		let attr = attributes(args);
		let {align, width, phone, tablet, desktop, nophone, notablet, nodesktop} = args;

		attr.class.push('mdl-cell');
		if(align) attr.class.push(`mdl-cell--${align}`);
		if(width) attr.class.push(`mdl-cell--${width}-col`);
		if(phone) attr.class.push(`mdl-cell--${phone}-col-phone`);
		if(tablet) attr.class.push(`mdl-cell--${tablet}-col-tablet`);
		if(desktop) attr.class.push(`mdl-cell--${desktop}-col-desktop`);
		if(nophone) attr.class.push('mdl-cell--hide-phone');
		if(notablet) attr.class.push('mdl-cell--hide-tablet');
		if(nodesktop) attr.class.push('mdl-cell--hide-desktop');

		return
{children}
;
	}
};

Add nospacing attribute to Grid


import h from 'snabbdom/h'
import extend from 'extend'

const sanitizeProps = (props) => {

  props = props === null ? {} : props

  Object.keys(props).map((prop) => {

    const keysRiver = prop.split('-').reverse()

    if (keysRiver.length > 1) {

      let newObject = keysRiver.reduce(
        (object, key) => ({ [key]: object }),
        props[prop]
      )
      extend(true, props, newObject)
      delete props[prop]

    } else if (!(['class', 'props', 'attrs', 'style', 'on', 'hook', 'key'].indexOf(prop) > -1)) {

      extend(true, props, { props: { [prop]: props[prop] } })
      delete props[prop]

    }

    return { toto: true }

  })

  return props

}

const sanitizeChilds = (children) => {

  if(children.length === 1 && typeof children[0] === 'string') {
      return children[0]
  }
  if(children.reduce((acc, curr) => acc || Array.isArray(curr), false))
  {
      return children
          .reduce((acc, curr) => Array.isArray(curr) ? [...acc, ...curr] : [...acc, curr], []);
  }
  return children;

}

export const createElement = (type, props, ...children) => {

  return (typeof type === 'function') ?
    type(props, children) :
    h(type, sanitizeProps(props), sanitizeChilds(children))

}

export default {
  createElement
}

import h from 'snabbdom/h'
import extend from 'extend'

const sanitizeProps = (props) => {

  props = props === null ? {} : props

  Object.keys(props).map((prop) => {

    const keysRiver = prop.split('-').reverse()

    if (keysRiver.length > 1) {

      let newObject = keysRiver.reduce(
        (object, key) => ({ [key]: object }),
        props[prop]
      )
      extend(true, props, newObject)
      delete props[prop]

    } else if (!(['class', 'props', 'attrs', 'style', 'on', 'hook', 'key'].indexOf(prop) > -1)) {

      extend(true, props, { props: { [prop]: props[prop] } })
      delete props[prop]

    }

    return { toto: true }

  })

  return props

}

const sanitizeChilds = (children) => {

  if (children.length === 1 && typeof children[0] === 'string') {

    return children[0]

  }
  if (children.reduce((acc, curr) => acc || Array.isArray(curr), false)) {

    return children
      .reduce((acc, curr) => Array.isArray(curr) ? [...acc, ...curr] : [...acc, curr], [])

  }

  return children

}

export const createElement = (type, props, ...children) => {

  return (typeof type === 'function') ?
    type(props, children) :
    h(type, sanitizeProps(props), sanitizeChilds(children))

}

export default {
  createElement
}

Update jvanbruegge additions according to XO rules


import { forEach, isObject, assign, camelCase } from 'lodash';

const nested = (input, ...keys) => {
  const obj = {};
  forEach(input, (value, key) => {
    if (isObject(value)) {
      assign(obj, nested(value, ...keys, key));
    } else {
      obj[camelCase(`${[...keys, key].join('-')}`)] = value;
    }
  });
  return obj;
};

export default nested;

import { forEach, isObject, assign, camelCase } from 'lodash';

const nested = (input, ...keys) => {
  const obj = {};
  forEach(input, (value, key) => {
    if (isObject(value)) {
      assign(obj, nested(value, ...keys, key));
    } else {
      obj[camelCase([...keys, key].join('-'))] = value;
    }
  });
  return obj;
};

export default nested;

Remove unnecessary template string literal.


import Promise from 'bluebird';
import process from 'process';
import util from 'util';
import { withRetries } from 'killrvideo-nodejs-common';
import { Scheduler } from './scheduler';
import * as availableTasks from './tasks';
import { initCassandraAsync } from './utils/cassandra';
import { initializeSampleDataAsync } from './sample-data/initialize';

// Allow promise cancellation
Promise.config({ cancellation: true });

/**
 * Async start the application.
 */
async function startAsync() {
  let scheduler = null;

  try {
    // Make sure C* is ready to go
    await withRetries(initCassandraAsync, 10, 10, 'Could not initialize Cassandra keyspace', false);

    // Initialize sample data
    await initializeSampleDataAsync();

    // Start scheduled tasks
    scheduler = new Scheduler(availableTasks);
    scheduler.start();
    return scheduler;
  } catch (err) {
    if (scheduler !== null) scheduler.stop();

    console.error('Unable to start Sample Data Generator');
    console.error(err);
    process.exit(1);
  }
}

// Start the app
let startPromise = startAsync();

// Handler for attempting to gracefully shutdown
function onStop() {
  console.log('Attempting to shutdown');

  if (startPromise.isFulfilled()) {
    let s = startPromise.value();
    s.stop();
  } else {
    startPromise.cancel();
  }

  process.exit(0);
}

// Attempt to shutdown on SIGINT
process.on('SIGINT', onStop);

import Promise from 'bluebird';
import process from 'process';
import util from 'util';
import { withRetries } from 'killrvideo-nodejs-common';
import { Scheduler } from './scheduler';
import * as availableTasks from './tasks';
import { initCassandraAsync } from './utils/cassandra';
import { initializeSampleDataAsync } from './sample-data/initialize';

// Allow promise cancellation
Promise.config({ cancellation: true });

/**
 * Async start the application.
 */
async function startAsync() {
  let scheduler = null;

  try {
    // Make sure C* is ready to go
    await withRetries(initCassandraAsync, 10, 10, 'Could not initialize Cassandra keyspace', false);

    // Initialize sample data
    await withRetries(initializeSampleDataAsync, 10, 10, 'Could not initialize sample data', false);

    // Start scheduled tasks
    scheduler = new Scheduler(availableTasks);
    scheduler.start();
    return scheduler;
  } catch (err) {
    if (scheduler !== null) scheduler.stop();

    console.error('Unable to start Sample Data Generator');
    console.error(err);
    process.exit(1);
  }
}

// Start the app
let startPromise = startAsync();

// Handler for attempting to gracefully shutdown
function onStop() {
  console.log('Attempting to shutdown');

  if (startPromise.isFulfilled()) {
    let s = startPromise.value();
    s.stop();
  } else {
    startPromise.cancel();
  }

  process.exit(0);
}

// Attempt to shutdown on SIGINT
process.on('SIGINT', onStop);

Add retries to sample data init


#!/usr/bin/env node
var fs = require('fs');

var source = process.argv.splice(2)[0];
var target = source.substring(0, source.lastIndexOf('.')) + '.css';

fs.readFile(source, 'utf-8', function(err, data) {
    if (err) throw err;

    fs.writeFile(target, data, function(err) {
        if (err) throw err;

        console.log('Wrote ' + target + '!');
    });
});

Make it possible to read and write a file


import chrono from 'chrono-node'

const BEFORE_REGEX = /before "(.*?)"/
const AFTER_REGEX = /after "(.*?)"/

// Takes in query as a string and extracts startDate and endDate
export default function extractTimeFiltersFromQuery(query) {
    const matchedBefore = query.match(BEFORE_REGEX)
    const matchedAfter = query.match(AFTER_REGEX)

    let startDate
    let endDate
    if (matchedBefore && matchedBefore[1]) endDate = chrono.parseDate(matchedBefore[1]).getTime()
    if (matchedAfter && matchedAfter[1]) startDate = chrono.parseDate(matchedAfter[1]).getTime()

    // Checks which comes first before/after and based on that decide where query should be sliced
    const firstIndexOfTimeFilter = Math.min(
        matchedBefore ? matchedBefore.index : query.length,
        matchedAfter ? matchedAfter.index : query.length,
    )
    const extractedQuery = query.substring(0, firstIndexOfTimeFilter)

    return {
        startDate,
        endDate,
        extractedQuery,
    }
}

import chrono from 'chrono-node'

const BEFORE_REGEX = /before "(.*?)"/
const AFTER_REGEX = /after "(.*?)"/

// Takes in query as a string and extracts startDate and endDate
export default function extractTimeFiltersFromQuery(query) {
    const matchedBefore = query.match(BEFORE_REGEX)
    const matchedAfter = query.match(AFTER_REGEX)

    let startDate
    let endDate
    if (matchedBefore && matchedBefore[1]) {
        const parsedDate = chrono.parseDate(matchedBefore[1])
        endDate = parsedDate && parsedDate.getTime()
    }
    if (matchedAfter && matchedAfter[1]) {
        const parsedDate = chrono.parseDate(matchedAfter[1])
        startDate = parsedDate && parsedDate.getTime()
    }

    // Checks which comes first before/after and based on that decide where query should be sliced
    const firstIndexOfTimeFilter = Math.min(
        matchedBefore ? matchedBefore.index : query.length,
        matchedAfter ? matchedAfter.index : query.length,
    )
    const extractedQuery = query.substring(0, firstIndexOfTimeFilter)

    return {
        startDate,
        endDate,
        extractedQuery,
    }
}

Fix a bug: Called getTime on date that couldn't be parsed.


describe( 'AppCtrl', function() {
  describe( 'dummyTest', function() {
    var AppCtrl, $location, $scope;

    beforeEach( module( 'angularTemplate' ) );

    beforeEach( inject( function( $controller, _$location_, $rootScope ) {
      $location = _$location_;
      $scope = $rootScope.$new();
    }));

    it( 'should pass a dummy test', inject( function() {
      expect( true ).toBeTruthy();
    }));
  });
});

describe( 'AppCtrl', function() {
  describe( 'dummyTest', function() {
    var AppCtrl, $location, $scope;

    beforeEach( module( 'story-book' ) );

    beforeEach( inject( function( $controller, _$location_, $rootScope ) {
      $location = _$location_;
      $scope = $rootScope.$new();
    }));

    it( 'should pass a dummy test', inject( function() {
      expect( true ).toBeTruthy();
    }));
  });
});

Rename unit tests to story book


#!/usr/bin/env node

var is_fine = true;

// is everything fine?
console.log('[INFO] Test started');
if (is_fine) {
    console.log('[INFO] Test succeeded');
    process.exit(0); // everything is fine
} else {
    console.log('[CRITICAL] Test failed!');
    process.exit(1); // something is not fine
}

Switch to camelCase and use completly safe obfuscation


module.exports = function ( mongoose ) {
  var Packages = new mongoose.Schema({
    name : { type: String, required: true, unique: true, index: true },
    repo : { type: String, required: true },

    description : { type: String, required: true },
    keywords : { type: Array },
    ui : { type: Boolean }
  });

  Packages.statics.search = function ( query, callback ) {
    this.find({}).or([
      { name : query },
      { keywords : query },
      { description : query }
    ]).exec( callback );
  };

  return Packages;
};

module.exports = function ( mongoose ) {
  var Packages = new mongoose.Schema({
    name : { type: String, required: true, unique: true, index: true, lowercase: true },
    repo : { type: String, required: true },
    script : { type: String, required: true },
    description : { type: String, required: true },
    keywords : { type: Array },
    ui : { type: Boolean }
  });

  Packages.path('name').validate(function ( v ) {
    return /^[a-zA-Z][a-zA-Z0-9\-]{0,29}$/.test( v );
  }, 'Package names must be between 1 and 30 characters, and contain only letters, numbers and dashes, and must begin with a letter.' );

  Packages.statics.search = function ( query, callback ) {
    this.find({}).or([
      { name : query },
      { keywords : query },
      { description : query }
    ]).exec( callback );
  };

  return Packages;
};

Add name validation, require script in Package model


var command = process.argv[2]
var target = process.argv[3]

var concerns = require('./cfg/concerns.json')
var types = require('./cfg/types.json')

var tasks = {
  new: {
    runner: require('tasks/new'),
    help: "new"
  },
  install: {
    runner: require('tasks/install'),
    help: "install"
  },
  search: {
    runner: require('tasks/search'),
    help: "search"
  },
  help: {
    runner: require('tasks/help'),
    help: "help"
  }
}

if (tasks[command])
  tasks[command].runner(target)
else
	console.log("No task was found for command [" + command + "] and target [" + target + "]!")

var command = process.argv[2]
var target = process.argv[3]

var concerns = require('./cfg/concerns.json')
var types = require('./cfg/types.json')

var tasks = {
  new: {
    runner: require('./tasks/new')
  },
  install: {
    runner: require('./tasks/install')
  },
  search: {
    runner: require('./tasks/search')
  },
  help: {
    runner: require('./tasks/help')
  }
}

if (tasks[command])
  tasks[command].runner(target)
else
	console.log("No task was found for command [" + command + "] and target [" + target + "]!")

Remove help value from tasks


/**
 * Has Vowels
 *
 * hasVowel tests if the String calling the function has a vowels
 *
 * @param {void}
 * @return {Boolean} returns true or false indicating if the string
 * has a vowel or not
 */
String.prototype.hasVowels = function() {
    var inputString = this;
    return /[aeiou]/gi.test(inputString);
};

/**
 * To Upper
 *
 * toUpper converts its calling string to all uppercase characters
 *
 * @param {void}
 * @return {String} returns an upperCase version of the calling string
 */
String.prototype.toUpper = function() {
    // the 'this' keyword represents the string calling the function
    return this.replace(/[a-z]/g, function(item, position, string) {
        return String.fromCharCode(string.charCodeAt(position)-32);
    });
};

String.prototype.toLower = function() {
    // the 'this' keyword represents the string calling the function
    return this.replace(/[A-Z]/g, function(item, position, string) {
        return String.fromCharCode(string.charCodeAt(position)+32);
    });
};

/**
 * Uc First
 *
 * ucFirst capitalises the first character of its calling string
 *
 * @param {void}
 * @return {String} returns the calling string with the first character
 * capitalised
 */
String.prototype.ucFirst = function() {
    return this.replace(/^[a-z]/g, function(item, position, string) {
		return item.toUpper();
	});
};

/**
 * Has Vowels
 *
 * hasVowel tests if the String calling the function has a vowels
 *
 * @param {void}
 * @return {Boolean} returns true or false indicating if the string
 * has a vowel or not
 */
String.prototype.hasVowels = function() {
    var inputString = this;
    return /[aeiou]/gi.test(inputString);
};

/**
 * To Upper
 *
 * toUpper converts its calling string to all uppercase characters
 *
 * @param {void}
 * @return {String} returns an upperCase version of the calling string
 */
String.prototype.toUpper = function() {
    // the 'this' keyword represents the string calling the function
    return this.replace(/[a-z]/g, function(item, position, string) {
        return String.fromCharCode(string.charCodeAt(position)-32);
    });
};

String.prototype.toLower = function() {
    // the 'this' keyword represents the string calling the function
    return this.replace(/[A-Z]/g, function(item, position, string) {
        return String.fromCharCode(string.charCodeAt(position)+32);
    });
};

/**
 * Uc First
 *
 * ucFirst capitalises the first character of its calling string
 *
 * @param {void}
 * @return {String} returns the calling string with the first character
 * capitalised
 */
String.prototype.ucFirst = function() {
	// get the first character
  	return this.replace(/^[a-z]/g, function(item, position, string) {
	    return item.toUpper();
	});
};

Fix a tab/space issue coming up on github


/**
Is a number finite?

@module number/isfinite
@param {Number} num The number to check.
@returns {bool} Is `num` positive or negative `Infinity`?
@example
var isFinite = require('lildash/object/isfinite');
isFinite(123);     // => true
isFinite(1 / 0);   // => false
isFinite(-1 / 0);  // => false
*/
module.exports = function (num) {
  return num !== Infinity && num !== -Infinity;
};

/**
Is a number finite?

@module number/isfinite
@param {number} num The number to check.
@returns {bool} Is `num` positive or negative `Infinity`?
@example
var isFinite = require('lildash/number/isfinite');
isFinite(123);     // => true
isFinite(1 / 0);   // => false
isFinite(-1 / 0);  // => false
*/
module.exports = function (num) {
  return num !== Infinity && num !== -Infinity;
};

Fix some errors in number/isFinite docs


module.exports = {
  presets: [
    ['@babel/preset-env', { shippedProposals: true, useBuiltIns: 'usage' }],
    '@babel/preset-react',
    '@babel/preset-flow',
  ],
  plugins: [
    'babel-plugin-emotion',
    'babel-plugin-macros',
    '@babel/plugin-proposal-class-properties',
    '@babel/plugin-proposal-object-rest-spread',
    '@babel/plugin-proposal-export-default-from',
  ],
  env: {
    test: {
      plugins: ['babel-plugin-require-context-hook', 'babel-plugin-dynamic-import-node'],
    },
  },
  overrides: [
    {
      test: './examples/vue-kitchen-sink',
      presets: ['babel-preset-vue'],
    },
    {
      test: [
        './lib/core/src/server',
        './lib/node-logger',
        './lib/codemod',
        './addons/storyshots',
        './addons/storysource/src/loader',
        './app/**/src/server/**',
      ],
      presets: [
        [
          '@babel/preset-env',
          {
            targets: {
              node: '8.11',
            },
          },
        ],
      ],
    },
  ],
};

module.exports = {
  presets: [
    ['@babel/preset-env', { shippedProposals: true, useBuiltIns: 'usage', modules: false }],
    '@babel/preset-react',
    '@babel/preset-flow',
  ],
  plugins: [
    'babel-plugin-emotion',
    'babel-plugin-macros',
    '@babel/plugin-proposal-class-properties',
    '@babel/plugin-proposal-object-rest-spread',
    '@babel/plugin-proposal-export-default-from',
  ],
  env: {
    test: {
      plugins: ['babel-plugin-require-context-hook', 'babel-plugin-dynamic-import-node'],
    },
  },
  overrides: [
    {
      test: './examples/vue-kitchen-sink',
      presets: ['babel-preset-vue'],
    },
    {
      test: [
        './lib/core/src/server',
        './lib/node-logger',
        './lib/codemod',
        './addons/storyshots',
        './addons/storysource/src/loader',
        './app/**/src/server/**',
      ],
      presets: [
        [
          '@babel/preset-env',
          {
            targets: {
              node: '8.11',
            },
          },
        ],
      ],
    },
  ],
};

Use `modules: false` for client-side code


module.exports = {
    presets: [
        [
            '@babel/env',
            {
                modules: process.env.MODULE ? false : 'commonjs',
                targets: ['>0.25%', 'not ie 11', 'not op_mini all'],
            },
        ],
        '@babel/preset-react',
        '@babel/preset-typescript',
    ],
    plugins: ['@babel/plugin-proposal-class-properties'],
};

module.exports = {
    presets: [
        [
            '@babel/env',
            {
                modules: process.env.MODULE ? false : 'commonjs',
                targets: ['last 2 versions', 'not dead'],
            },
        ],
        '@babel/preset-react',
        '@babel/preset-typescript',
    ],
    plugins: ['@babel/plugin-proposal-class-properties'],
};

Allow IE11 compilation and more


const REQUEST_STATUSES = [
  {
    value: 'Unassigned',
    label: 'Unassigned'
  },
  {
    value: 'Assigned',
    label: 'Assigned'
  },
  {
    value: 'Contacted',
    label: 'Contacted'
  },
  {
    value: 'Awaiting Response',
    label: 'Awaiting Response'
  },
  {
    value: 'Scheduled',
    label: 'Scheduled'
  },
  {
    value: 'Completed',
    label: 'Completed'
  }
];

export const MENTOR_ANSWERS = [
  {
    value: true,
    label: 'Yes'
  },
  {
    value: false,
    label: 'No'
  }
];

export default REQUEST_STATUSES;

const REQUEST_STATUSES = [
  {
    value: 'Unassigned',
    label: 'Unassigned'
  },
  {
    value: 'Assigned',
    label: 'Assigned'
  },
  {
    value: 'Contacted',
    label: 'Contacted'
  },
  {
    value: 'Awaiting Response',
    label: 'Awaiting Response'
  },
  {
    value: 'Scheduled',
    label: 'Scheduled'
  },
  {
    value: 'Completed',
    label: 'Completed'
  }
];

export const MENTOR_ANSWERS = [
  {
    value: true,
    label: 'Yes'
  },
  {
    value: false,
    label: 'No'
  }
];

export const IDENTIFIERS = [
  {
    value: 'civ',
    label: 'Volunteer'
  },
  {
    value: 'mil',
    label: 'Mil / Vet / Spouse'
  },
  {
    value: 'sponsor',
    label: 'Sponsor'
  }
];

export default REQUEST_STATUSES;

Add identifier constants for form select component