const express = require("express");
const bodyParser = require("body-parser");
const methodOverride = require("method-override");
const api = require("./api");

function server(emitter, state, logger) {
    const app = express();

    app.use(bodyParser.json());
    app.use(methodOverride());

    app.use("/api", api(emitter, state, logger));

    if (process.env.NODE_ENV === "production") {
        app.use(express.static("client/build"));
    }

    app.use((err, req, res, next) => {
        if (!err) {
            next();
        }

        logger.error(err);
        res.status(err.status || 500);
        res.send(err.message);
    });

    const port = process.env.PORT || 3001;
    return {
        app,
        server: app.listen(port, () => {
            logger.info(`Listening on port ${port}`);
        })
    };
}

module.exports = server;

const express = require("express");
const bodyParser = require("body-parser");
const methodOverride = require("method-override");
const api = require("./api");

function server(emitter, state, logger) {
    const app = express();

    app.use(bodyParser.json());
    app.use(methodOverride());

    app.use("/api", api(emitter, state, logger));

    if (process.env.NODE_ENV === "production") {
        app.use(express.static(__dirname + "/../client/build"));
    }

    app.use((err, req, res, next) => {
        if (!err) {
            next();
        }

        logger.error(err);
        res.status(err.status || 500);
        res.send(err.message);
    });

    const port = process.env.PORT || 3001;
    return {
        app,
        server: app.listen(port, () => {
            logger.info(`Listening on port ${port}`);
        })
    };
}

module.exports = server;

Use absolute path for serving static assets.


"use strict";

function removeContainer(container, onResultCallback) {
  let options = {
    "v": true,
    "force": true
  };

  container.remove(options, onResultCallback || () => { });
}

function removeContainers(containers) {
  containers.forEach(container => {
    removeContainer(container);
  });
}

exports.removeContainer =
  removeContainer;
exports.removeContainers =
  removeContainers;

"use strict";

function removeContainer(container, onResultCallback) {
  let options = {
    "v": true,
    "force": true
  };

  let dummy =
    () => { };

  container.remove(options, onResultCallback || dummy);
}

function removeContainers(containers) {
  containers.forEach(container => {
    removeContainer(container);
  });
}

exports.removeContainer =
  removeContainer;
exports.removeContainers =
  removeContainers;

Add support for Node.js 6


if(Meteor.isClient){

  processPayment = function(checkout, next){
    Meteor.call("processPayment", checkout, next);
  };

  getReceipt = function(key, next){
    return Meteor.call("getReceipt", key, next);
  };
}

if(Meteor.isServer){
  Meteor.methods({
    getReceipt : function(key){
      check(key, String);
      return SalesRepo.getReceipt(key);
    },
    processPayment : function(checkout){
      check(checkout, Match.ObjectIncluding({
        card_id: String
      }));

      //never trust the client, build the sale from the saved data
      var cart = Carts.findOne({_id : checkout.cart_id});

      //set the total
      checkout.total = cart.total;

      //set the items - have to use stringify to make sure it's saved as an array
      checkout.items = JSON.stringify(cart.items);

      //drop the response info onto the checkout object
      checkout.payment_details = StripeService.runCharge(checkout);

      //create a unique id
      checkout.reference_key = Meteor.uuid();

      //empty the cart
      Meteor.call("emptyCart", cart.userKey);

      //drop it in the DB
      return SalesRepo.saveCheckout(checkout);

    }
  })
}

if(Meteor.isClient){

  processPayment = function(checkout, next){
    Meteor.call("processPayment", checkout, next);
  };

  getReceipt = function(key, next){
    return Meteor.call("getReceipt", key, next);
  };
}

if(Meteor.isServer){
  Meteor.methods({
    getReceipt : function(key){
      check(key, String);
      return SalesRepo.getReceipt(key);
    },
    processPayment : function(checkout){
      check(checkout, Match.ObjectIncluding({
        cart_id: String
      }));

      //never trust the client, build the sale from the saved data
      var cart = Carts.findOne({_id : checkout.cart_id});

      //set the total
      checkout.total = cart.total;

      //set the items - have to use stringify to make sure it's saved as an array
      checkout.items = JSON.stringify(cart.items);

      //drop the response info onto the checkout object
      checkout.payment_details = StripeService.runCharge(checkout);

      //create a unique id
      checkout.reference_key = Meteor.uuid();

      //empty the cart
      Meteor.call("emptyCart", cart.userKey);

      //drop it in the DB
      return SalesRepo.saveCheckout(checkout);

    }
  })
}

Fix for wrong parameter check in processPayment method.


function parseResponse(response) {
  let text;

  if (response && response.content && (typeof response.content === 'string')) {
    text = response.content;
  }

  if (response && response.text && (typeof response.text === 'string')) {
    text = response.text;
  }

  try {
    const parsedResponse = JSON.parse(text);

    if (
      parsedResponse
      && parsedResponse.data
      && (parsedResponse.data instanceof Object === true)
    ) {
      return parsedResponse.data;
    }
  } catch (err) {
    console.warn('Error while parsing JSON', err);
  }

  return {};
}

export default parseResponse;

function parseResponse(response) {
  let text;

  if (response && response.content && (typeof response.content === 'string')) {
    text = response.content;
  }

  if (response && response.text && (typeof response.text === 'string')) {
    text = response.text;
  }

  try {
    const parsedResponse = JSON.parse(text);

    if (
      parsedResponse
      && parsedResponse.data
      && (parsedResponse.data instanceof Object === true)
    ) {
      return parsedResponse.data;
    }
  } catch (err) {
    throw new Error('Error while parsing JSON', err);
  }

  throw new Error('Error while parsing JSON');
}

export default parseResponse;

Throw error instead of logging.


import _ from 'lodash';

export const unexistedUID = '00000000-0000-0000-C000-000000000046';

export function initObject(classDef, attrs, id, params) {
  return new classDef({ ...attrs, ...{ id }, ...params });
}

export function prepareModelPayload(payload, namesMapping, valuesMapping) {
  return _.transform(payload, (result, val, key) => {
    let mappedVal = val;

    if (valuesMapping[key]) {
      mappedVal = valuesMapping[key](val);
    }

    const mappedKey = namesMapping[key];

    if (mappedKey) {
      result[mappedKey] = mappedVal;
    }
  });
}

import _ from 'lodash';

export const unexistedUID = '00000000-0000-0000-C000-000000000046';

export function initObject(classDef, attrs, id, params) {
  return new classDef({ ...attrs, id, ...params });
}

export function prepareModelPayload(payload, namesMapping, valuesMapping) {
  const result = {};
  const keys = _.intersection(Object.keys(payload), Object.keys(namesMapping));

  for (const key of keys) {
    const mappedKey = namesMapping[key];
    const mappedVal = valuesMapping[key] ? valuesMapping[key](payload[key]) : payload[key];
    result[mappedKey] = mappedVal;
  }

  return result;
}

Rewrite and simplify prepareModelPayload function


cordova.define("com.moust.cordova.videoplayer.VideoPlayer", function(require, exports, module) {
var exec = require("cordova/exec");

module.exports = {

    DEFAULT_OPTIONS: {
        volume: 1.0,
        scalingMode: 1
    },

    SCALING_MODE: {
        SCALE_TO_FIT: 1,
        SCALE_TO_FIT_WITH_CROPPING: 2
    },

    play: function (path, options, successCallback, errorCallback) {
        options = this.merge(this.DEFAULT_OPTIONS, options);
        exec(successCallback, errorCallback, "VideoPlayer", "play", [path, options]);
    },

    getCurrentPosition: function (successCallback, errorCallback) {
        return exec(successCallback, errorCallback, "VideoPlayer", "getCurrentPosition", []);
    },

    getDuration: function (successCallback, errorCallback) {
        return exec(successCallback, errorCallback, "VideoPlayer", "getDuration", []);
    },

    close: function (successCallback, errorCallback) {
        exec(successCallback, errorCallback, "VideoPlayer", "close", []);
    },

    merge: function () {
        var obj = {};
        Array.prototype.slice.call(arguments).forEach(function(source) {
            for (var prop in source) {
                obj[prop] = source[prop];
            }
        });
        return obj;
    }

};

});

Fix doubling of js module definition


"use strict"

var Jar = require("../src/Jar.js")

describe("Jar", function () {

})

"use strict"

var Jar = require("../src/Jar.js")
  , spawn = require("child_process").spawn
  , path = require("path")
  , fs = require("fs")
  , assert = require("assert")

var helloBuildDir = path.resolve(__dirname + "/../support/hello")
  , helloJarPath = path.resolve(__dirname + "/../support/hello/hello.jar")

var makeHelloJarFile = function (cb) {
    spawn("make", ["-C", helloBuildDir])
        .on("error", function (err) { throw err })
        .on("exit", function (code) {
            assert.equal(code, 0)
            assert(fs.existsSync(helloJarPath))
            cb()
        })
}

describe("Jar", function () {
    before(function (done) {
        makeHelloJarFile(done)
    })

    it("should …", function () {

    })
})

Build the test jar file from the tests.


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

/* eslint-env node */
module.exports = {
  test_page: 'tests/index.html?hidepassed',
  disable_watching: true,
  launch_in_ci: [
    'Chrome'
  ],
  launch_in_dev: [
    'Chrome'
  ],
  browser_args: {
    Chrome: {
      mode: 'ci',
      args: [
        '--disable-gpu',
        '--headless',
        '--remote-debugging-port=9222',
        '--window-size=1440,900',
        // Workaround for https://github.com/travis-ci/travis-ci/issues/8836
        '--no-sandbox',
      ]
    },
  }
};

Add workaround for running chrome headless on travis


/* globals module */

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

module.exports = {
    description: 'Register test helpers',

    afterInstall: function( options ) {
            // Import statement
        var firstFile          = 'tests/helpers/start-app.js',
            firstText          = "import slregisterTestHelpers from './sl/register-test-helpers';",
            firstLocationText  = "import Ember from 'ember';" + EOL,

            // Execution of registration function
            secondFile         = 'tests/helpers/start-app.js',
            secondText         = "    slregisterTestHelpers();",
            secondLocationText = "application.setupForTesting();" + EOL,

            // .jshintrc file
            thirdFile          = 'tests/.jshintrc',
            thirdText          = '    "contains",',
            thirdLocationText  = '"predef": [' + EOL;

            // Import statement
        return this.insertIntoFile( firstFile, firstText, { after: firstLocationText } )

            // Execution of registration function
            .then( function() {
                return this.insertIntoFile( secondFile, secondText, { after: secondLocationText } );
            }.bind(this))

            // .jshintrc file
            .then( function() {
                return this.insertIntoFile( thirdFile, thirdText, { after: thirdLocationText } );
            }.bind(this));
    },

    normalizeEntityName: function() {}
};

/* globals module */

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

module.exports = {
    description: 'Register test helpers',

    afterInstall: function( options ) {
            // Import statement
        var firstFile          = 'tests/helpers/start-app.js',
            firstText          = "import slregisterTestHelpers from './sl/register-test-helpers';",
            firstLocationText  = "import Ember from 'ember';" + EOL,

            // Execution of registration function
            secondFile         = 'tests/helpers/start-app.js',
            secondText         = "    slregisterTestHelpers();",
            secondLocationText = "application.setupForTesting();" + EOL,

            // .jshintrc file
            thirdFile          = 'tests/.jshintrc',
            thirdText          = '    "contains",' + EOL + '    "requires",',
            thirdLocationText  = '"predef": [' + EOL;

            // Import statement
        return this.insertIntoFile( firstFile, firstText, { after: firstLocationText } )

            // Execution of registration function
            .then( function() {
                return this.insertIntoFile( secondFile, secondText, { after: secondLocationText } );
            }.bind(this))

            // .jshintrc file
            .then( function() {
                return this.insertIntoFile( thirdFile, thirdText, { after: thirdLocationText } );
            }.bind(this));
    },

    normalizeEntityName: function() {}
};

Add "requires" entry to .jshintrc entry in blueprint (overlooked in previous commit)


import Web3 from 'web3';

// set providor
if(typeof web3 !== 'undefined')
  web3 = new Web3(web3.currentProvider);
else
  web3 = new Web3(new Web3.providers.HttpProvider("http://localhost:8545"));

// set providor
if(typeof web3 !== 'undefined') {
  web3 = new Web3(web3.currentProvider);
}
else {
  let Web3 = require('web3');
  web3 = new Web3(new Web3.providers.HttpProvider("http://localhost:8545"));
}

Fix import statement triggering in Mist


'use strict';
/*
 *
 */
angular.module('arethusa').factory('TreebankPersister', [
  'documentStore',
  'configurator',
  function (documentStore, configurator) {
    return function() {
    };
  }
]);

'use strict';
/*
 *
 */
angular.module('arethusa').factory('TreebankPersister', [
  'documentStore',
  'configurator',
  'navigator',
  function (documentStore, configurator, navigator) {
    return function(conf) {
      var resource = configurator.provideResource(conf.resource);

      function updateXml() {
        var doc = documentStore.store.tbd; // placeholder
        doc.xml = arethusaUtil.json2xml(doc.json);
      }

      function updateDocument() {
        var doc = documentStore.store.tbd; // placeholder
        var stored = arethusaUtil.toAry(doc.json.treebank.sentence);
        // navigator has to provide means to retrieve sentences by id
        // and not only through a flat array!
        var sentences = navigator.sentences;

        angular.forEach(stored, function(sentence, i) {
          var updated = sentences[sentence._id - 1];
          angular.forEach(sentence.word, function(word, i) {
            var stateWord = updated.tokens[arethusaUtil.formatNumber(word._id, 4)];

            word._head = arethusaUtil.formatNumber(stateWord.head.id, 0);
            word._relation = stateWord.relation.label;
            word._lemma = stateWord.morphology.lemma;
            word._postag = stateWord.morphology.postag;
            word._form = stateWord.string;
          });
        });
      }

      this.saveData = function(callback) {
        updateDocument();
        updateXml();

        callback();
      };
    };
  }
]);

Add update fns to TreebankPersister


import React from 'react'
import ReactTestUtils from 'react-addons-test-utils'
import assert from 'assert'
import {getKaomoji, getYmdStr } from '../app/Resources/js/utils/util.js'
import Header from '../app/Resources/js/components/header.jsx'

describe('testHeader', () => {
    const getComponent = () => {
        return ReactTestUtils.renderIntoDocument(<Header getDailyJson={[]} timelineDateList={[]} appUsername={'hoge'}/>)
    }

    it('username', () => {
        let component = getComponent()
        assert(component.props.appUsername == 'hoge')
    })
})

import React from 'react'
import ReactTestUtils from 'react-addons-test-utils'
import assert from 'assert'
import Header from '../app/Resources/js/components/header.jsx'

describe('testHeader', () => {
    const getComponent = () => {
        return ReactTestUtils.renderIntoDocument(<Header getDailyJson={[]} timelineDateList={[]} appUsername={'hoge'}/>)
    }

    it('username', () => {
        let component = getComponent()
        assert(component.props.appUsername == 'hoge')
    })
})

Revert "travis fail karma fix"


"use strict";

var renderNull = function () { return 'NULL' }
  , parser

parser = require('editorsnotes-markup-parser')({
  projectBaseURL: '/',
  resolveItemText: renderNull,
  makeBibliographyEntry: renderNull,
  makeInlineCitation: function(citations) {
    return { citations: citations.map(renderNull) }
  }
});

function getENReferences(nodeArray, items) {
  items = items || {};

  return nodeArray.reduce(function (acc, node) {
    if (node.meta && node.meta.enItemType) {
      let itemType = node.meta.enItemType
        , itemID = node.meta.enItemID

      if (!acc.hasOwnProperty(itemType)) acc[itemType] = [];
      if (acc[itemType].indexOf(itemID) === -1) acc[itemType].push(itemID);
    }

    return node.children ? getENReferences(node.children, acc) : acc;
  }, items)
}

module.exports = function (text) {
  return getENReferences(parser.parse(text))
}

"use strict";

var renderNull = function () { return 'NULL' }
  , parser

parser = require('editorsnotes-markup-parser')({
  projectBaseURL: '/',
  resolveItemText: renderNull,
  makeBibliographyEntry: renderNull,
  makeInlineCitation: function(citations) {
    return { citations: citations.map(renderNull) }
  }
});

function getENReferences(nodeArray, items) {
  items = items || {};

  return nodeArray.reduce(function (acc, node) {
    if (node.meta && node.meta.enItemType) {
      let itemType = node.meta.enItemType
        , itemID = node.meta.enItemID

      if (!acc.hasOwnProperty(itemType)) acc[itemType] = [];
      if (acc[itemType].indexOf(itemID) === -1) acc[itemType].push(itemID);
    }

    return node.children ? getENReferences(node.children, acc) : acc;
  }, items)
}

module.exports = function (text) {
  return getENReferences(parser.parse(text, {}))
}

Add second argument to parse()


import Ember from 'ember';

const o = Ember.observer;
const on = Ember.on;

export default Ember.ObjectProxy.extend({
  /*
   * Add method from content
   * @param method[function]
   * @return function
   */
  _addMethod(method){
    this[method] = function() {
      var content = this.get('content');

      if (Ember.isEmpty(content[method])) {
        return;
      }

      return content[method].apply(this, arguments);
    };
  },

  /*
   * Setup method proxying
   */
  _setupMethods: on('init', o('content', function(){
    var content = this.get('content');

    if (Ember.isEmpty(content)) { return; }

    for (var method in content) {
      if(Ember.typeOf(content[method]) === "function" && Ember.isEmpty(this[method])) {
         this._addMethod(method);
      }
    }
  })),

  /*
   * Setup ember data relationship keys
   */
  _setupEmberData: on('init', o('content', function() {
    var content = this.get('content');

    if (Ember.isEmpty(content)) { return; }

    this._internalModel = content._internalModel;
  }))
});

import Ember from 'ember';

const o = Ember.observer;
const on = Ember.on;

export default Ember.ObjectProxy.extend({
  /*
   * Add method from content
   * @param method[function]
   * @return function
   */
  _addMethod(method){
    this[method] = function() {
      var content = this.get('content');

      if (Ember.isEmpty(content[method])) {
        return;
      }

      return content[method].apply(this, arguments);
    };
  },

  /*
   * Setup method proxying
   */
  _setupMethods: on('init', o('content', function(){
    var content = this.get('content');

    if (Ember.isEmpty(content)) { return; }

    for (let method in content) {
      if(Ember.typeOf(content[method]) === "function" && Ember.isEmpty(this[method])) {
         this._addMethod(method);
      }
    }
  })),

  /*
   * Setup ember data relationship keys
   */
  _setupEmberData: on('init', o('content', function() {
    var content = this.get('content');

    if (Ember.isEmpty(content)) { return; }

    this._internalModel = content._internalModel;
  }))
});

Change var to let by ES6


// The Vue build version to load with the `import` command
// (runtime-only or standalone) has been set in webpack.base.conf with an alias.
import Vue from 'vue'
import { VueRouter, router } from './router'
import App from './App'

import './styles.js'

/* eslint-disable no-new */
new Vue({
    router,
    el: '#app',
    template: '<App/>',
    components: { App }
})

// The Vue build version to load with the `import` command
// (runtime-only or standalone) has been set in webpack.base.conf with an alias.
import Vue from 'vue'
import { VueRouter, router } from './router'
import App from './App'
import './styles.js'

Vue.use(VueRouter)

/* eslint-disable no-new */
new Vue({
    router,
    el: '#app',
    template: '<App/>',
    components: { App }
})

Order imports and load VueRoiuter config


// ORDER MATTERS!
import physicalVisualization from './vis-physical';
import provider from './data-provider';
import { hasClass, removeClass, addClass, uuidRegExp } from './utils/helpers';
let { MS } = window;

require('normalize.css');
require('animate.css/animate.css');
require('./main.less');

function parseQuery(qstr) {
  var query = {};
  var a = qstr.substr(1).split('&');
  for (var i = 0; i < a.length; i++) {
      var b = a[i].split('=');
      query[decodeURIComponent(b[0])] = decodeURIComponent(b[1] || '');
  }
  return query;
}

let query = parseQuery(window.location.search);
let tabPhysical = document.getElementById('tab-physical');
let physical = document.getElementById('vis-physical');

tabPhysical.addEventListener('click',() => {
  removeClass(physical, 'hidden');
  removeClass(tabPhysical, 'hidden');

  document.body.className = 'tab2';
});

/* Enable polling */
function reload(){
  provider.reload();
  setTimeout(reload, MS);
}

console.log("Polling refresh: " + MS);

provider.on('infrastructure-data', physicalVisualization.render);
provider.start();
reload();

//TODO: Emit Event that requeries data, removes old data recreates visualizations

// ORDER MATTERS!
import physicalVisualization from './vis-physical';
import provider from './data-provider';
import { hasClass, removeClass, addClass, uuidRegExp } from './utils/helpers';
let { MS } = window;

require('normalize.css');
require('animate.css/animate.css');
require('./main.less');

function parseQuery(qstr) {
  var query = {};
  var a = qstr.substr(1).split('&');
  for (var i = 0; i < a.length; i++) {
      var b = a[i].split('=');
      query[decodeURIComponent(b[0])] = decodeURIComponent(b[1] || '');
  }
  return query;
}

let query = parseQuery(window.location.search);
let tabPhysical = document.getElementById('tab-physical');
let physical = document.getElementById('vis-physical');

tabPhysical.addEventListener('click',() => {
  removeClass(physical, 'hidden');
  removeClass(tabPhysical, 'hidden');

  document.body.className = 'tab2';
});

tabPhysical.addEventListener('click', () => {
  let filterDiv = document.querySelector('#filter-wrapper');
  if (filterDiv.classList.value.indexOf('hide') >= 0) {
    filterDiv.classList.remove('hide');
    filterDiv.classList.add('show');
  }
  else {
    filterDiv.classList.add('hide');
    filterDiv.classList.remove('show');
  }
});

/* Enable polling */
function reload(){
  provider.reload();
  setTimeout(reload, MS);
}

console.log("Polling refresh: " + MS);

provider.on('infrastructure-data', physicalVisualization.render);
provider.start();
reload();

//TODO: Emit Event that requeries data, removes old data recreates visualizations

Add click even on whale to hide input filter


const $ = require('jquery');

$(document).ready(function(){
  $('#splitter').mousedown(function(e){
    e.preventDefault();
    $(document).mousemove(function(e){
      $('.content').css("width",$(document).width()-e.pageX+2);
      $('.side-bar').css("width",e.pageX+2);
    })
  });
  $(document).mouseup(function(e){
    $(document).unbind('mousemove');
  });
});

const $ = require('jquery');

var splitterLocation;

$(document).ready(function(){
  $('#splitter').mousedown(function(e){
    e.preventDefault();
    $(document).mousemove(function(e){
      splitterLocation = e.pageX;
      $('.content').css("width", $(document).width() - splitterLocation);
      $('.side-bar').css("width", splitterLocation);
    })
  });
  $(document).mouseup(function(e){
    $(document).unbind('mousemove');
  });
});

$(window).resize(function() {
  if(typeof splitterLocation === "undefined")
    return;

  $('.content').css("width", $(document).width() - splitterLocation);
});

Fix a bug that .content does not fit with the window (instead, blank area appears on the right side) when the window is resized.


'use strict';

var assert = require('assert');
var Response = require('../');

var res = new Response(200, {
  'Foo-Bar': 'baz-Bosh',
  'bar-foo': 'bish-Bosh'
}, 'foo bar baz');
assert(res.statusCode = 200);
assert(res.headers['foo-bar'] === 'baz-Bosh');
assert(res.headers['bar-foo'] === 'bish-Bosh');
assert(res.body === 'foo bar baz');
assert(res.getBody() === 'foo bar baz');

res = new Response(404, {
  'Foo-Bar': 'baz-Bosh'
}, 'Could not find page');
assert(res.statusCode = 404);
assert(res.headers['foo-bar'] === 'baz-Bosh');
assert(res.body === 'Could not find page');
var errored = false;
try {
  res.getBody();
} catch (ex) {
  assert(ex.statusCode === 404);
  assert(ex.headers['foo-bar'] === 'baz-Bosh');
  assert(res.body === 'Could not find page');
  errored = true;
}
if (!errored) {
  throw new Error('res.getBody() should throw an error when the status code is 404');
}
console.log('tests passed');

'use strict';

var assert = require('assert');
var Response = require('../');

var res = new Response(200, {
  'Foo-Bar': 'baz-Bosh',
  'bar-foo': 'bish-Bosh'
}, 'foo bar baz', 'http://example.com');
assert(res.statusCode = 200);
assert(res.headers['foo-bar'] === 'baz-Bosh');
assert(res.headers['bar-foo'] === 'bish-Bosh');
assert(res.body === 'foo bar baz');
assert(res.url === 'http://example.com');
assert(res.getBody() === 'foo bar baz');

res = new Response(404, {
  'Foo-Bar': 'baz-Bosh'
}, 'Could not find page', 'http://example.com');
assert(res.statusCode = 404);
assert(res.headers['foo-bar'] === 'baz-Bosh');
assert(res.body === 'Could not find page');
assert(res.url === 'http://example.com');
var errored = false;
try {
  res.getBody();
} catch (ex) {
  assert(ex.statusCode === 404);
  assert(ex.headers['foo-bar'] === 'baz-Bosh');
  assert(ex.body === 'Could not find page');
  assert(ex.url === 'http://example.com');
  errored = true;
}
if (!errored) {
  throw new Error('res.getBody() should throw an error when the status code is 404');
}
console.log('tests passed');

Update tests for `res.url` and `err.url`


import assert from 'assert';
import uakari from '../lib';

describe('uakari', function () {
  it('should determine the version of the local packge', function () {
    assert.equal(uakari.version, '0.0.0', 'expect the current version to be earliest.');
  });
});

import assert from 'assert';
import uakari from '../lib';

describe('uakari', function () {
  it('should determine the version of the local packge', function () {
    assert.equal(uakari.version, '0.0.1', 'expect the current version to be earliest.');
  });
});

Update test to reflect new version


import React, { Component } from 'react'
import { Link } from 'react-router-dom'
import Icon from '@components/Icon'
import noop from '@utils/noop'

export default class Tag extends Component {
  static defaultProps = {
    className: 'icon-tag',
    linkTo: undefined,
    onClickRemove: noop,
    removable: false,
    tag: '',
  }

  handleClickRemove = () => this.props.onClickRemove(this.props.tag)

  render = () => {
    const { className, removable, linkTo, tag } = this.props

    const classNames = [
      'tag',
      className,
      removable ? 'removable' : '',
    ].filter(Boolean)
    let text = tag.slice(0, 12)
    if (text !== tag) text += '…'

    return (

        {linkTo
          ? <Link className="link" to={linkTo}>{text}</Link>
          : {text}
        }
        {removable && <Icon name="remove" onClick={this.handleClickRemove} />}

    )
  }
}

import React, { Component } from 'react'
import { Link } from 'react-router-dom'
import Icon from '@components/Icon'
import noop from '@utils/noop'

export default class Tag extends Component {
  static defaultProps = {
    className: 'icon-tag',
    linkTo: undefined,
    onClickRemove: noop,
    removable: false,
    tag: '',
  }

  handleClickRemove = () => this.props.onClickRemove(this.props.tag)

  render = () => {
    const { className, removable, linkTo, tag } = this.props

    const classNames = [
      'tag',
      className,
      removable ? 'removable' : '',
    ].filter(Boolean)
    let text = tag.slice(0, 10)
    if (text !== tag) text += '…'

    return (

        {linkTo
          ? <Link className="link" to={linkTo}>{text}</Link>
          : {text}
        }
        {removable && <Icon name="remove" onClick={this.handleClickRemove} />}

    )
  }
}

Trim a bit more text


jsio.path.common = 'js';
jsio.path.browser = 'js';

jsio('from common.javascript import bind');
jsio('import net, logging');
jsio('import common.Item');
jsio('import common.itemFactory');
jsio('import browser.ItemView');
jsio('import browser.UbiquityClient');

gClient = new browser.UbiquityClient();

gClient.connect('csp', "http://" + (document.domain || "127.0.0.1") + ":5555", function(itemSubscriptions){
	window.top.console.debug(itemSubscriptions);

	setTimeout(function(){
		var connecting = document.getElementById('connecting');
		connecting.parentNode.removeChild(connecting);

		var placeHolder = document.getElementById('placeholder');

		for (var i=0, itemId; itemId = itemSubscriptions[i]; i++) {
			var item = common.itemFactory.getItem(itemId);
			var itemView = new browser.ItemView(item);

			placeHolder.appendChild(itemView.getPropertyView('name'))
			placeHolder.appendChild(itemView.getPropertyView('age'))
			placeHolder.appendChild(document.createElement('br'));

			placeHolder.appendChild(itemView.getPropertyView('name'))
			placeHolder.appendChild(itemView.getPropertyView('age'))
			placeHolder.appendChild(document.createElement('br'));
			placeHolder.appendChild(document.createElement('br'));

			item.subscribe('PropertySet', bind(gClient, 'onItemPropertySet', item.getId()));
			gClient.subscribeToItem(item);
		}

	})

});

jsio.path.common = 'js';
jsio.path.browser = 'js';

jsio('from common.javascript import bind');
jsio('import net, logging');
jsio('import common.Item');
jsio('import common.itemFactory');
jsio('import browser.ItemView');
jsio('import browser.UbiquityClient');

gClient = new browser.UbiquityClient();

gClient.connect('csp', "http://" + (document.domain || "127.0.0.1") + ":5555", function(itemSubscriptions){
	var connecting = document.getElementById('connecting');
	connecting.parentNode.removeChild(connecting);

	var placeHolder = document.getElementById('placeholder');

	for (var i=0, itemId; itemId = itemSubscriptions[i]; i++) {
		var item = common.itemFactory.getItem(itemId);
		var itemView = new browser.ItemView(item);

		placeHolder.appendChild(itemView.getPropertyView('name'))
		placeHolder.appendChild(itemView.getPropertyView('age'))
		placeHolder.appendChild(document.createElement('br'));

		placeHolder.appendChild(itemView.getPropertyView('name'))
		placeHolder.appendChild(itemView.getPropertyView('age'))
		placeHolder.appendChild(document.createElement('br'));
		placeHolder.appendChild(document.createElement('br'));

		item.subscribe('PropertySet', bind(gClient, 'onItemPropertySet', item.getId()));
		gClient.subscribeToItem(item);
	}
});

Remove debug statement and setTimeout. Its annoying that exceptions get caught on the rtjp layer - would probably be better to hand off control somewhere in the stack with a setTimeout


"use strict";

/**
 * TODO: make this ingest steam IDs as an event stream, and
 *       cache them and then poll the steam API in batches
 *       every now and again. Or not, depending on options :)
 */

var steam = require('steam-api'),
    events = require('events');

class SteamApiWrapper extends events {
  constructor(apiKey) {
    super();
    if (!apiKey) { throw "no api key provided to SteamApiWrapper constructor"; }

    this.steamClient = new steam.User(apiKey);
  }

  /**
   * callback - called once for each steamID provided
   */
  getPlayerInfo(steamIds, callback) {
    if (typeof callback !== "function") { throw "no callback given"; }

    this.steamClient.GetPlayerSummaries(steamIds).done(function(playersInfo,r) {
        playersInfo = Array.isArray(playersInfo) ? playersInfo : [playersInfo];

        playersInfo.forEach(function(p) {
          callback(null, p);
        });
    });
  }
}

module.exports = {
   create: function(apiKey) { return new SteamApiWrapper(apiKey); }
}

"use strict";

/**
 * TODO: make this ingest steam IDs as an event stream, and
 *       cache them and then poll the steam API in batches
 *       every now and again. Or not, depending on options :)
 */

var steam = require('steam-api'),
    events = require('events');

class SteamApiWrapper extends events {
  constructor(apiKey) {
    super();
    if (!apiKey) { throw "no api key provided to SteamApiWrapper constructor"; }

    this.steamClient = new steam.User(apiKey);
  }

  /**
   * callback - called once for each steamID provided
   */
  getPlayerInfo(steamIds, callback) {
    if (typeof callback !== "function") { throw "no callback given"; }

    var client = this.steamClient.GetPlayerSummaries(steamIds);
    client.done(function(playersInfo,r) {
        playersInfo = Array.isArray(playersInfo) ? playersInfo : [playersInfo];

        playersInfo.forEach(function(p) {
          callback(null, p);
        });
    });
    client.fail(function(e) { console.error("Failed to fetch user list: ", e); });
  }
}

module.exports = {
   create: function(apiKey) { return new SteamApiWrapper(apiKey); }
}

Add error handling for http request


import path from 'path';

export const DEFAULT_APP_NAME = 'APP';
export const ELECTRON_VERSION = '3.1.3';
export const PLACEHOLDER_APP_DIR = path.join(__dirname, './../', 'app');

import path from 'path';

export const DEFAULT_APP_NAME = 'APP';
export const ELECTRON_VERSION = '3.1.7';
export const PLACEHOLDER_APP_DIR = path.join(__dirname, './../', 'app');

Bump default electron to 3.1.7


import axios from "axios";

const baseUrl = location.origin;
const endPoint = `${baseUrl}/api/v1/plans`;
const headers = {
  "Content-type": "application/json",
  Authorization: window.gobiertoAPI.token
};

// Plans-endpoint factory to get/post/put/delete API data
export const PlansFactoryMixin = {
  locale: I18n.locale,
  methods: {
    getPlan(id, params) {
      const qs = new URLSearchParams({ locale: this.$options.locale, ...params })
      return axios.get(`${endPoint}/${id}?${qs.toString()}`, { headers })
    },
    getProjects(id, params) {
      const qs = new URLSearchParams({ locale: this.$options.locale, ...params })
      return axios.get(`${endPoint}/${id}/projects?${qs.toString()}`, { headers })
    },
    getMeta(id, params) {
      const qs = new URLSearchParams({ locale: this.$options.locale, ...params })
      return axios.get(`${endPoint}/${id}/meta?${qs.toString()}`, { headers })
    }
  }
};

import axios from "axios";

const baseUrl = location.origin;
const endPoint = `${baseUrl}/api/v1/plans`;
const token = window.gobiertoAPI.token.length == 0 && window.gobiertoAPI.basic_auth_token ? window.gobiertoAPI.basic_auth_token : window.gobiertoAPI.token

const headers = {
  "Content-type": "application/json",
  Authorization: token
};

// Plans-endpoint factory to get/post/put/delete API data
export const PlansFactoryMixin = {
  locale: I18n.locale,
  methods: {
    getPlan(id, params) {
      const qs = new URLSearchParams({ locale: this.$options.locale, ...params })
      return axios.get(`${endPoint}/${id}?${qs.toString()}`, { headers })
    },
    getProjects(id, params) {
      const qs = new URLSearchParams({ locale: this.$options.locale, ...params })
      return axios.get(`${endPoint}/${id}/projects?${qs.toString()}`, { headers })
    },
    getMeta(id, params) {
      const qs = new URLSearchParams({ locale: this.$options.locale, ...params })
      return axios.get(`${endPoint}/${id}/meta?${qs.toString()}`, { headers })
    }
  }
};

Include basic_auth_token in gobierto plans factories header


import React from 'react';
import { View, Button } from 'react-native';

/*
Developer screen.
Provides access to components during development.
*/
export default class DeveloperScreen extends React.Component {
  static navigationOptions = {
    tabBar: {
      label: 'Developer',
    },
    title: 'Developer',
  };

  render() {
    const { navigate } = this.props.navigation;
    return (
      <View>
        <Button onPress={() => navigate('SampleScreen')} title="Go to the sample screen" />
      </View>
    );
  }
}

import React from 'react';
import { View, Button } from 'react-native';

/*
Developer screen.
Provides access to components during development.
*/
export default class DeveloperScreen extends React.Component {
  static navigationOptions = {
    tabBar: {
      label: 'Developer',
    },
    title: 'Developer',
  };

  render() {
    const { navigate } = this.props.navigation;
    return (
      <View>
        <Button onPress={() => navigate('SampleScreen')} title="Go to the sample screen" />
        <Button onPress={() => navigate('CreateJobScreen')} title="Go to create job screen" />
      </View>
    );
  }
}

Add navigation to create job screen from developer screen


/**
 * Copyright 2015 The Incremental DOM Authors. 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.
 */

/**
 * A cached reference to the hasOwnProperty function.
 */
const hasOwnProperty = Object.prototype.hasOwnProperty;

/**
 * A cached reference to the create function.
 */
const create = Object.create;

/**
 * Used to prevent property collisions between our "map" and its prototype.
 * @param {!Object<string, *>} map The map to check.
 * @param {string} property The property to check.
 * @return {boolean} Whether map has property.
 */
const has = function(map, property) {
  return hasOwnProperty.call(map, property);
};

/**
 * Creates an map object without a prototype.
 * @return {!Object}
 */
const createMap = function() {
  return create(null);
};

/** */
export {
  createMap,
  has
};

/**
 * Copyright 2015 The Incremental DOM Authors. 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.
 */

/**
 * A cached reference to the hasOwnProperty function.
 */
const hasOwnProperty = Object.prototype.hasOwnProperty;

/**
 * A constructor function that will create blank objects.
 * @constructor
 */
function Blank() {}

Blank.prototype = Object.create(null);

/**
 * Used to prevent property collisions between our "map" and its prototype.
 * @param {!Object<string, *>} map The map to check.
 * @param {string} property The property to check.
 * @return {boolean} Whether map has property.
 */
const has = function(map, property) {
  return hasOwnProperty.call(map, property);
};

/**
 * Creates an map object without a prototype.
 * @return {!Object}
 */
const createMap = function() {
  return new Blank();
};

/** */
export {
  createMap,
  has
};

Use a faster blank object creator


function ScrollableView() {
	var self = Ti.UI.createWindow({});

	var views = [],
	viewHeight = 200,
	viewWidth = 200,
	pageCount = 5;

	for (var i = 0; i < pageCount; i++) {
		var label = Ti.UI.createLabel({
			text : String(i+1)
		});
		var view = Ti.UI.createView({});
		view.add(label);
		views.push(view);
	}

	var scrollableView = Ti.UI.createScrollableView({
		backgroundColor : 'Gray',
		showPagingControl : true,
		views : views,
		width : viewWidth,
		height : viewHeight
	});

	self.add(scrollableView);

	return self;
};

module.exports = ScrollableView;

Complete lesson 17 - scrollable view


window.addEventListener('load', function(){

  module('touchstart');

  test('should use touchstart when touchstart is supported', function() {
    ok(false, 'not implemented');
    // assert({ listener:'touchstart', receives:'touchstart' });
  });

  test('should use mousedown when touchstart is unsupported', function() {
    assert({ listener:'touchstart', receives:'mousedown' });
  });

  module('touchend');

  test('should use touchend when touchend is supported', function() {
    ok(false, 'not implemented');
    // assert({ listener:'touchend', receives:'touchend' });
  });

  test('should use mouseup when touchend is unsupported', function() {
    assert({ listener:'touchend', receives:'mouseup' });
  });

  module('touchmove');

  test('should use touchmove when touchmove is supported', function() {
    ok(false, 'not implemented');
    // assert({ listener:'touchmove', receives:'touchmove' });
  });

  test('should use mousemove when touchmove is unsupported', function() {
    assert({ listener:'touchmove', receives:'mousemove' });
  });

  module('tap');

  test('should use tap when touch events are supported', function() {
    ok(false, 'not implemented');
    // assert({ listener:'tap', receives:'tap' });
  });

  test('should use click when tap is unsupported', function() {
    assert({ listener:'tap', receives:'click' });
  });

});

window.addEventListener('load', function(){

  module('touchstart');

  test('should use touchstart when touchstart is supported', function() {
    assert({ listener:'touchstart', receives:'touchstart' });
  });

  test('should use mousedown when touchstart is unsupported', function() {
    assert({ listener:'touchstart', receives:'mousedown' });
  });

  module('touchend');

  test('should use touchend when touchend is supported', function() {
    assert({ listener:'touchend', receives:'touchend' });
  });

  test('should use mouseup when touchend is unsupported', function() {
    assert({ listener:'touchend', receives:'mouseup' });
  });

  module('touchmove');

  test('should use touchmove when touchmove is supported', function() {
    assert({ listener:'touchmove', receives:'touchmove' });
  });

  test('should use mousemove when touchmove is unsupported', function() {
    assert({ listener:'touchmove', receives:'mousemove' });
  });

  module('tap');

  test('should use tap when touch events are supported', function() {
    ok(false, 'not implemented');
    // assert({ listener:'tap', receives:'tap' });
  });

  test('should use click when tap is unsupported', function() {
    assert({ listener:'tap', receives:'click' });
  });

});

Add integrity test for touch events


const profileService = require("../../../Db/Service/profileService");

const errors = require("../Mojang/Errors");

module.exports.joinserver = (req, res, next) => {
    let accessToken = req.body.accessToken,
        selectedProfile = req.body.selectedProfile,
        serverId = req.body.serverId,
        server = "Unknown" || req.params["server"];
    profileService.getProfileByProfileId(selectedProfile, profile => {
        if (!profile || profile.accessToken !== accessToken) return errors.makeError(res, errors.ForbiddenOperationExceptionUserAccount);
        req.db.redis.set(serverId, selectedProfile, (err, msg) => {
            profileService.loginServer(profile, "${player} joined " + server + " Server", req.headers['x-forwarded-for'] || req.ip, () => {
                res.status(204).send()
            })
        })
    })
};

module.exports.hasjoinserver = (req, res, next) => {
    req.db.redis.get(req.query.serverId, (err, reply) => {
        if (err || !reply) { return res.status(204).send() }
        profileService.getProfileByProfileId(reply, profile => {
            if (profile.UserName !== req.query.username ) { return res.status(204).send() }
            if (!profile) { return res.status(204).send() }
            res.send({
                id: profile.accessToken,
                name: profile.UserName
            })
        })
    });

};

const profileService = require("../../../Db/Service/profileService");

const errors = require("../Mojang/Errors");

module.exports.joinserver = (req, res, next) => {
    let accessToken = req.body.accessToken,
        selectedProfile = req.body.selectedProfile,
        serverId = req.body.serverId,
        server = "Unknown" || req.params["server"];
    profileService.getProfileByProfileId(selectedProfile, profile => {
        if (!profile || profile.accessToken !== accessToken) return errors.makeError(res, errors.ForbiddenOperationExceptionUserAccount);
        req.db.redis.set(serverId, selectedProfile, (err, msg) => {
            profileService.loginServer(profile, "${player} joined " + server + " Server", req.headers['x-forwarded-for'] || req.ip, () => {
                res.status(204).send()
            })
        })
    })
};

module.exports.hasjoinserver = (req, res, next) => {
    req.db.redis.get(req.query.serverId, (err, reply) => {
        if (err || !reply) { return res.status(204).send() }
        profileService.getProfileByProfileId(reply, profile => {
            if (profile.UserName !== req.query.username ) { return res.status(204).send() }
            if (!profile) { return res.status(204).send() }
            res.send({
                id: profile.ProfileID,
                name: profile.UserName
            })
        })
    });

};

Fix return user ProfileID not accessToken


$('.m-scooch').scooch();

if ( $('.m-item:first-child' ).hasClass('m-active') ) {
    $('.m-scooch-controls [data-m-slide="prev"]').addClass('hide');
};

$('.m-scooch-controls a').click(function() {
    $('.m-scooch-controls a').removeClass('hide');

    if ( $('.m-item:first-child' ).hasClass('m-active') ) {
        $('.m-scooch-controls [data-m-slide="prev"]').addClass('hide');
    };

    if ( $('.m-item:last-child' ).hasClass('m-active') ) {
        $('.m-scooch-controls [data-m-slide="next"]').addClass('hide');
    };

});

// Pseusocode
//
// Create array of images in carousel
// var images = $('.m-item').map(function() {
//      return this
// }).get();
//
// when the afterSlide event fires
//
//     make sure to reevaluate the situation, adding buttons back that were previously removed
//
//     for image in images
//         if !image[i-1]
//             add hide class to previous button
//         if !image[i+1]
//             add hide class to next button
//

$('.m-scooch').scooch();

if ( $('.m-item:first-child' ).hasClass('m-active') ) {
    $('.m-scooch-controls [data-m-slide="prev"]').addClass('hide');
};

$('.m-scooch').on('afterSlide', function(e, previousSlide, nextSlide) {

    $('.m-scooch-controls a').removeClass('hide');

    if ( $('.m-item:first-child' ).hasClass('m-active') ) {
        $('.m-scooch-controls [data-m-slide="prev"]').addClass('hide');
    };

    if ( $('.m-item:last-child' ).hasClass('m-active') ) {
        $('.m-scooch-controls [data-m-slide="next"]').addClass('hide');
    };
});

Fix for scooch carousel next/prev buttons


var fs = require('fs');
var endpoint = require('endpoint');

describe('Writeable Stream Input API', function () {

  var feedPath = __dirname + '/feeds/rss2sample.xml';

  describe('.pipe()', function () {
    it('works', function (done) {
      var events = [];

      fs.createReadStream(feedPath).pipe(FeedParser())
        .on('error', function (err) {
          assert.ifError(err);
          events.push('error');
        })
        .on('meta', function (meta) {
          assert.notEqual(meta, null);
          events.push('meta');
        })
        .on('article', function (article) {
          assert.notEqual(article, null);
          events.push('article');
        })
        .on('complete', function (meta, articles) {
          assert.notEqual(meta, null);
          assert.ok(articles.length);
          events.push('complete');
        })
        .on('end', function () {
          assert.equal(events.indexOf('error'), -1);
          assert.ok(~events.indexOf('meta'));
          assert.ok(~events.indexOf('article'));
          assert.ok(~events.indexOf('complete'));
          done();
        })
        .resume();
    });
  });

  describe('.pipe()', function () {
    it('works', function (done) {
      fs.createReadStream(feedPath)
        .pipe(FeedParser())
        .pipe(endpoint({objectMode: true}, function (err, articles) {
          assert.equal(err, null);
          assert.ok(articles.length);
          done();
        }));
    });
  });

});

var fs = require('fs');

describe('Writeable Stream Input API', function () {

  var feedPath = __dirname + '/feeds/rss2sample.xml';

  describe('.pipe()', function () {
    it('works', function (done) {
      var events = [];

      fs.createReadStream(feedPath).pipe(FeedParser())
        .on('error', function (err) {
          assert.ifError(err);
          events.push('error');
        })
        .on('meta', function (meta) {
          assert.notEqual(meta, null);
          events.push('meta');
        })
        .on('article', function (article) {
          assert.notEqual(article, null);
          events.push('article');
        })
        .on('complete', function (meta, articles) {
          assert.notEqual(meta, null);
          assert.ok(articles.length);
          events.push('complete');
        })
        .on('end', function () {
          assert.equal(events.indexOf('error'), -1);
          assert.ok(~events.indexOf('meta'));
          assert.ok(~events.indexOf('article'));
          assert.ok(~events.indexOf('complete'));
          done();
        })
        .resume();
    });
  });

  describe('.pipe()', function () {
    it('works', function (done) {
      fs.createReadStream(feedPath)
        .pipe(FeedParser())
        .on('readable', function () {
          var stream = this, items = [], item;
          while (item = stream.read()) {
            items.push(item);
          }
          assert.ok(items.length);
        })
        .on('end', done)
        .on('error', done);
    });
  });

});

Change stream test to not require additional dependency


//Dependencies
var restify = require('restify');
var cors = require('cors');
var appInfo = require('./package.json');

//Configuration
//var address = process.env.OPENSHIFT_NODEJS_IP || '127.0.0.1';
var port = process.env.PORT || 1337;
var server = restify.createServer();

//Plug-ins
server.use(restify.queryParser());
server.use(restify.bodyParser());
server.use(restify.CORS());

//Routes
server.get(/\//, echo);
server.get(/\/(echo)\b/, echo);
server.post(/\/(parse)\b/, parse);

/******************************************************************************
End-points
******************************************************************************/
function echo(req, res) {
    var info = {
        name: appInfo.name,
        version: appInfo.version,
        description: appInfo.description,
        homepage: appInfo.homepage,
        author:{
            name: appInfo.author.name,
            email: appInfo.author.email,
        }
    };
    res.send(info);
}

function parse(req, res) {
    //Parse the data
    var agentString = req.body.userAgent || req.body.UserAgent || req.params['useragent'];

    var useragent = require('useragent');
    useragent(true);

    var agent = useragent.lookup(agentString);
    res.send(agent.toJSON());
}
/*****************************************************************************/

//Start server
server.listen(port, function () {
    console.log('listening on port: ' + port);
});

//Dependencies
var restify = require('restify');
var cors = require('cors');
var appInfo = require('./package.json');

//Configuration
var port = process.env.PORT || 1337;
var server = restify.createServer();

//Plug-ins
server.use(restify.queryParser());
server.use(restify.bodyParser());
server.use(restify.CORS());

//Routes
server.get(/\//, echo);
server.get(/\/(echo)\b/, echo);
server.post(/\/(parse)\b/, parse);

/******************************************************************************
End-points
******************************************************************************/
function echo(req, res) {
    var info = {
        name: appInfo.name,
        version: appInfo.version,
        description: appInfo.description,
        homepage: appInfo.homepage,
        author:{
            name: appInfo.author.name,
            email: appInfo.author.email,
        }
    };
    res.send(info);
}

function parse(req, res) {
    //Parse the data
    var agentString = req.body.userAgent || req.body.UserAgent || req.params['useragent'];

    var useragent = require('useragent');
    useragent(true);

    var agent = useragent.lookup(agentString);
    res.send(agent.toJSON());
}
/*****************************************************************************/

//Start server
server.listen(port, function () {
    console.log('listening on port: ' + port);
});

Remove reference to openshfit specifics.


const { join } = require('path');
const { transform } = require('babel-core');

module.exports = function testPlugin(code) {
  const loaderPath = join(__dirname, 'exampleTranslationLoader.js');
  const result = transform(code, {
    plugins: [['./index.js', {
      translationLoader: loaderPath,
    }]],
  });

  return result.code;
};

const { transform } = require('babel-core');

module.exports = function testPlugin(code) {
  const result = transform(code, {
    plugins: [['./index.js', {
      translationLoader: './test/exampleTranslationLoader',
    }]],
  });

  return result.code;
};

Use relative path for translationLoader


var asyncTesting = require("async_testing");
var fs = require("fs");
var path = require("path");

// extend cobbled together from suggestions in
// http://groups.google.com/group/nodejs/browse_thread/thread/e82b45b3f8faa5af
function extend (a, b) {
  var property;
  for (property in b) {
    a[property] = b[property];
  }
  return a;
}

fs.readdirSync(path.dirname(module.filename)).forEach(function (filename) {
  if (filename.match(/.js$/) && filename != "all.js") {
    extend(exports, require("./" + path.basename(filename, ".js")));
  }
});

asyncTesting.runSuites(exports);

// Contingent upon async_testing acceptance
//asyncTesting.runSuitesForPaths([path.dirname(module.filename)]);

var asyncTesting = require("async_testing");
var fs = require("fs");
var path = require("path");

// extend cobbled together from suggestions in
// http://groups.google.com/group/nodejs/browse_thread/thread/e82b45b3f8faa5af
function extend (a, b) {
  var property;
  for (property in b) {
    a[property] = b[property];
  }
  return a;
}

fs.readdirSync(path.dirname(module.filename)).forEach(function (filename) {
  if (filename.match(/.js$/) && filename != "all.js") {
    extend(exports, require("./" + path.basename(filename, ".js")));
  }
});

asyncTesting.runSuites(exports);

// Contingent upon async_testing acceptance
//asyncTesting.runSuitesInPaths([path.dirname(module.filename)]);

Change on theoretical method in async-testing


define({
    suites: false,
    proxyPort: 9090,
    proxyUrl: "http://localhost:9090/",
    capabilities: {
        "selenium-version": "2.45.0"
    },
    tunnel: "SauceLabsTunnel",
    tunnelOptions: {
        port: 4444
    },
    environments: [
        { browserName: "Firefox" },
        { browserName: "Internet Explorer", platform: "Windows 8.1", version: "11" },
        { browserName: "Chrome" }
    ],
    maxConcurrency: 3,
    functionalSuites: [
        "tests/functional/mainpage",
        "tests/functional/admin/auth/signin",
        "tests/functional/admin/dashboard/dashboard",
        "tests/functional/admin/dashboard/comics/create",
    ],
    excludeInstrumentation: /tests|node_modules/
});

define({
    suites: false,
    proxyPort: 9090,
    proxyUrl: "http://localhost:9090/",
    capabilities: {
        "selenium-version": "2.45.0"
    },
    tunnel: "SauceLabsTunnel",
    tunnelOptions: {
        port: 4444
    },
    environments: [
        { browserName: "Firefox" },
        { browserName: "Internet Explorer", platform: "Windows 8.1", version: "11" },
        { browserName: "Chrome" }
    ],
    maxConcurrency: 1,
    functionalSuites: [
        "tests/functional/mainpage",
        "tests/functional/admin/auth/signin",
        "tests/functional/admin/dashboard/dashboard",
        "tests/functional/admin/dashboard/comics/create",
    ],
    excludeInstrumentation: /tests|node_modules/
});

Decrease concurrency of Selenium tests


/**
 * main.js
 *
 * Author: Marian Friedmann
 *
 */

$(function() {

});

/**
 * main.js
 *
 * Author: Marian Friedmann
 *
 */

$(function() {
  $('.modal-open').click(function() {
    $('.modal').addClass('modal--active');
  });

  $('.modal-loading').click(function() {
    $('.modal').removeClass('modal--active');
    $('.modal').addClass('modal--loading');
  });

  $('.modal-close').click(function() {
    $('.modal').removeClass('modal--active');
    $('.modal').removeClass('modal--loading');
  });
});

Add modal dummy open/close function


var app = require('app');
var BrowserWindow = require('browser-window');
var menu = require('./menu');

process.env.PATH += ':/usr/local/bin';

var mainWindow;

app.on('ready', function () {
	mainWindow = createWindow();
});

app.on('window-all-closed', function() {
	if(process.platform !== 'darwin') {
		app.quit();
	}
});

app.on('activate-with-no-open-windows', function () {
	mainWindow = createWindow();
});

function createWindow() {
	var window = new BrowserWindow({
		'web-preferences': {
			'overlay-scrollbars': true
		},
		resizable: true,
		show: false
	});

	window.loadUrl('file://' + __dirname + '/../../index.html');
	menu.setMenu(app, window);

	window.on('closed', function() {
		window = null;
	});

	window.webContents.on('did-finish-load', function () {
		mainWindow.show();
		mainWindow.focus();
	});

	return window;
}

var app = require('app');
var BrowserWindow = require('browser-window');
var menu = require('./menu');

process.env.PATH += ':/usr/local/bin';

var mainWindow;

app.on('ready', function () {
    mainWindow = createWindow();
});

app.on('window-all-closed', function () {
    if (process.platform !== 'darwin') {
        app.quit();
    }
});

app.on('activate-with-no-open-windows', function () {
    mainWindow = createWindow();
});

function createWindow() {
    var workAreaSize = require('screen').getPrimaryDisplay().workAreaSize;
    var window = new BrowserWindow({
        'web-preferences': {
            'overlay-scrollbars': true
        },
        resizable: true,
        width: workAreaSize.width,
        height: workAreaSize.height,
        show: false
    });

    window.loadUrl('file://' + __dirname + '/../../index.html');
    menu.setMenu(app, window);

    window.on('closed', function () {
        window = null;
    });

    window.webContents.on('did-finish-load', function () {
        mainWindow.show();
        mainWindow.focus();
    });

    return window;
}

Make the window full screen.


import req from '../req.js';
import Event from '../Event';

export default function Ticker() {
  this.event = new Event();

  this.ready = false;
  this.data = {};

  this.load();

  setInterval(() => {this.load()}, 60*1000) // Refresh every 60 seconds
}

const MAX_ATTEMPTS = 5;

Ticker.prototype.load = function(attempt) {
  if (attempt >= MAX_ATTEMPTS) {
    return;
  }
  req.getJson('https://api.stellarterm.com/v1/ticker.json')
  .then(tickerData => {
    this.ready = true;
    this.data = tickerData;
    console.log('Loaded ticker. Data generated ' + Math.round((new Date() - this.data._meta.start * 1000)/1000) + ' seconds ago.')
    this.event.trigger();
  })
  .catch(e => {
    console.log('Unable to load ticker', e);
    if (!attempt) {
      attempt = 0;
    }
    setTimeout(() => {
      this.load(attempt + 1);
    }, 100)
  })
}

import req from '../req.js';
import Event from '../Event';

export default function Ticker() {
  this.event = new Event();

  this.ready = false;
  this.data = {};

  this.load();

  setInterval(() => {this.load()}, 60*1000) // Refresh every 60 seconds
}

const MAX_ATTEMPTS = 120;

Ticker.prototype.load = function(attempt) {
  if (attempt >= MAX_ATTEMPTS) {
    return;
  }
  req.getJson('https://api.stellarterm.com/v1/ticker.json')
  .then(tickerData => {
    this.ready = true;
    this.data = tickerData;
    console.log('Loaded ticker. Data generated ' + Math.round((new Date() - this.data._meta.start * 1000)/1000) + ' seconds ago.')
    this.event.trigger();
  })
  .catch(e => {
    console.log('Unable to load ticker', e);
    if (!attempt) {
      attempt = 0;
    }
    setTimeout(() => {
      this.load(attempt + 1);
    }, 1000)
  })
}

Increase attempts for loading ticker


/* eslint-env node */

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

/* eslint-env node */

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

Use headless chrome only on CI


'use strict';

const { DEFAULT_INPUT_CHARSET } = require('./constants');
const { validateCharset } = require('./validate');
const { decodeCharset } = require('./transform');

// Normalize charset, including adding defaults and validating
const getCharset = function (charset, { format } = {}) {
  const charsetA = addDefaultCharset({ charset, format });

  const charsetB = charsetA.toLowerCase();

  validateCharset({ charset: charsetB, format });

  const charsetC = createInstance({ charset: charsetB, title: charsetA });

  return charsetC;
};

// Add default charsets, including the format's default charset
const addDefaultCharset = function ({ charset, format }) {
  const formatCharset = findFormatCharset({ format });

  return charset || formatCharset || DEFAULT_INPUT_CHARSET;
};

const findFormatCharset = function ({ format }) {
  if (format === undefined) { return; }

  return format.getCharset();
};

// Returns a charset adapter object
const createInstance = function ({ charset, title }) {
  const decode = decodeCharset.bind(null, charset);

  return { name: charset, title, decode };
};

module.exports = {
  getCharset,
};

'use strict';

// eslint-disable-next-line import/no-internal-modules
const encodings = require('iconv-lite/encodings');

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

const { DEFAULT_INPUT_CHARSET } = require('./constants');
const { validateCharset } = require('./validate');
const { decodeCharset } = require('./transform');

// Normalize charset, including adding defaults and validating
const getCharset = function (charset, { format } = {}) {
  const charsetA = addDefaultCharset({ charset, format });

  const charsetB = charsetA.toLowerCase();

  validateCharset({ charset: charsetB, format });

  const charsetC = createInstance({ charset: charsetB, title: charsetA });

  return charsetC;
};

// Add default charsets, including the format's default charset
const addDefaultCharset = function ({ charset, format }) {
  const formatCharset = findFormatCharset({ format });

  return charset || formatCharset || DEFAULT_INPUT_CHARSET;
};

const findFormatCharset = function ({ format }) {
  if (format === undefined) { return; }

  return format.getCharset();
};

// Returns a charset adapter object
const createInstance = function ({ charset, title }) {
  const decode = decodeCharset.bind(null, charset);

  return { name: charset, title, decode };
};

// Get list of supported charset
const getCharsets = function () {
  // Remove charsets that are just aliases, to keep return value small
  const charsets = omitBy(encodings, value => typeof value === 'string');

  const charsetsA = Object.keys(charsets);
  return charsetsA;
};

module.exports = {
  getCharset,
  getCharsets,
};

Allow retrieving list of supported charsets


import nodemailer from 'nodemailer'
import Mailgen from 'mailgen'
import path from 'path'
import config from '../config'

/**
 * Class for managing sending emails
 */
export default class Mail {
  /**
   * Create a mail sender class instance
   */
  constructor () {
    this.transporter = nodemailer.createTransport({
      host: config.smtp.host,
      port: config.smtp.port
    })

    this.mailgen = new Mailgen({
      theme: {
        path: path.resolve('static/mailgen/index.html'),
        plaintextPath: path.resolve('static/mailgen/index.txt')
      },
      product: {
        name: 'The Fuel Rats',
        link: 'https://fuelrats.com/',
        logo: 'https://wordpress.fuelrats.com/wp-content/uploads/2018/09/email.jpg'
      }
    })
  }

  /**
   * Send an email
   * @param {object} arg function arguments object
   * @param {string} arg.to the email recipient
   * @param {string } arg.subject the email subject
   * @param {object} arg.body mailgen body configuration
   * @returns {Promise<void>} fulfills a promise when successful
   */
  async send ({ to: recipient, subject, body }) {
    const email = {
      body
    }

    await this.transporter.sendMail({
      from: 'Fuel Rats (Do Not Reply) <blackhole@fuelrats.com>',
      to: recipient,
      subject,
      text: this.mailgen.generatePlaintext(email),
      html: this.mailgen.generate(email)
    })
  }
}

import nodemailer from 'nodemailer'
import Mailgen from 'mailgen'
import path from 'path'
import config from '../config'

/**
 * Class for managing sending emails
 */
export default class Mail {
  /**
   * Create a mail sender class instance
   */
  constructor () {
    this.transporter = nodemailer.createTransport({
      host: config.smtp.hostname,
      port: config.smtp.port
    })

    this.mailgen = new Mailgen({
      theme: {
        path: path.resolve('static/mailgen/index.html'),
        plaintextPath: path.resolve('static/mailgen/index.txt')
      },
      product: {
        name: 'The Fuel Rats',
        link: 'https://fuelrats.com/',
        logo: 'https://wordpress.fuelrats.com/wp-content/uploads/2018/09/email.jpg'
      }
    })
  }

  /**
   * Send an email
   * @param {object} arg function arguments object
   * @param {string} arg.to the email recipient
   * @param {string } arg.subject the email subject
   * @param {object} arg.body mailgen body configuration
   * @returns {Promise<void>} fulfills a promise when successful
   */
  async send ({ to: recipient, subject, body }) {
    const email = {
      body
    }

    await this.transporter.sendMail({
      from: 'Fuel Rats (Do Not Reply) <blackhole@fuelrats.com>',
      to: recipient,
      subject,
      text: this.mailgen.generatePlaintext(email),
      html: this.mailgen.generate(email)
    })
  }
}

Fix issue causing api to connect to incorrect mail host


import Graphics from 'flockn/graphics';

var isVisible = function isVisible() {
  var hasVisibleFlag = Object.hasOwnProperty.call(this, 'visible');

  if (hasVisibleFlag) {
    return this.visible;
  } else {
    return true;
  }
};

var renderable = function renderable() {
  this.on('render', () => {
    // Only render if element is visible
    if (!isVisible.call(this)) {
      return;
    }

    // Emit `render` event on the `Graphics` object
    Graphics.trigger('render', this);

    // Render all children elements
    this.children.forEach(child => child.trigger('render'));
  });
};

export default renderable;

import checkForFlag from 'flockn/utils/checkforflag';
import Graphics from 'flockn/graphics';

var isVisible = checkForFlag('visible');

var renderable = function renderable() {
  this.on('render', () => {
    // Only render if element is visible
    if (!isVisible.call(this)) {
      return;
    }

    // Emit `render` event on the `Graphics` object
    Graphics.trigger('render', this);

    // Render all children elements
    this.children.forEach(child => child.trigger('render'));
  });
};

export default renderable;

Use checkForFlag in render mixin


export default {
  COUNTRIES: 'filter:countries',
  COLLECTIONS: 'filter:collection_id',
  SCHEMA: 'filter:schema'
};

export default {
  COUNTRIES: 'filter:countries',
  COLLECTIONS: 'filter:collection_id',
  SCHEMA: 'post_filter:schema'
};

Use post filter for schema


(function() {
  'use strict';

  function Game() {
    this.player = null;
    this.cursors = null;
  }

  Game.prototype = {

    create: function () {
      this.game.world.setBounds(0,0, 1280, 940);
      this.background = this.add.sprite(0,0, 'background');

      var x = this.game.width / 2
        , y = this.game.height / 2;

      this.player = this.add.sprite(x, y, 'player');
      this.player.anchor.setTo(0.5, 0.5);

      this.cursors = this.game.input.keyboard.createCursorKeys();
      this.game.camera.follow(this.player);
    },

    update: function () {
      if (this.cursors.up.isDown) {
        this.player.y--;
      }
      if (this.cursors.left.isDown) {
        this.player.x--;
      }
      if (this.cursors.right.isDown) {
        this.player.x++;
      }
      if (this.cursors.down.isDown) {
        this.player.y++;
      }
    }

  };

  window.darwinator.Game = Game;

}());

(function() {
  'use strict';

  function Game() {
    this.player = null;
    this.cursors = null;
  }

  Game.prototype = {

    create: function () {
      this.game.world.setBounds(0,0, 1280, 940);
      this.background = this.add.sprite(0,0, 'background');

      var x = this.game.width / 2
        , y = this.game.height / 2;

      this.player = this.add.sprite(x, y, 'player');
      this.player.anchor.setTo(0.5, 0.5);

      this.cursors = this.game.input.keyboard.createCursorKeys();
      this.game.camera.follow(this.player);
    },

    update: function () {
      this.player.body.velocity.setTo(0,0);
      if (this.cursors.up.isDown) {
        this.player.body.velocity.y = -100;
      }
      if (this.cursors.left.isDown) {
        this.player.body.velocity.x = -100;
      }
      if (this.cursors.right.isDown) {
        this.player.body.velocity.x = 100;
      }
      if (this.cursors.down.isDown) {
        this.player.body.velocity.y = 100;
      }
    }

  };

  window.darwinator.Game = Game;

}());

Use velocity rather than just changing positions


import React from 'react';
import { addStoryInGroup, LOW_LEVEL_BLOCKS } from '../../../.storybook/utils';
import { Store, State } from '@sambego/storybook-state';
import { IconAddMediumOutline } from '@teamleader/ui-icons';
import Button from '../button';
import ButtonGroup from './ButtonGroup';

const store = new Store({
  value: 'option2',
});

const handleChangeValue = (value, event) => {
  store.set({ value });
};

export default {
  component: ButtonGroup,
  title: addStoryInGroup(LOW_LEVEL_BLOCKS, 'Button group'),
};

export const Normal = (args) => (
  <ButtonGroup {...args}>
    <Button label="Button 1" />
    <Button label="Button 2" />
    <Button icon={<IconAddMediumOutline />} />
  </ButtonGroup>
);

export const withActive = () => (
  <State store={store}>
    <ButtonGroup segmented value="option2" onChange={handleChangeValue} level="secondary">
      <Button label="Option 1" value="option1" />
      <Button label="Option 2" value="option2" />
      <Button label="Option 3" value="option3" />
    </ButtonGroup>
  </State>
);

withActive.story = {
  name: 'With active',
};

import React, { useState } from 'react';
import { addStoryInGroup, LOW_LEVEL_BLOCKS } from '../../../.storybook/utils';
import { IconAddMediumOutline } from '@teamleader/ui-icons';
import Button from '../button';
import ButtonGroup from './ButtonGroup';

export default {
  component: ButtonGroup,
  title: addStoryInGroup(LOW_LEVEL_BLOCKS, 'Button group'),
};

export const Normal = (args) => (
  <ButtonGroup {...args}>
    <Button label="Button 1" />
    <Button label="Button 2" />
    <Button icon={<IconAddMediumOutline />} />
  </ButtonGroup>
);

export const withActive = () => {
  const [value, setValue] = useState('option2');

  const handleChange = (value) => {
    setValue(value);
  };

  return (
    <ButtonGroup segmented value={value} onChange={handleChange} level="secondary">
      <Button label="Option 1" value="option1" />
      <Button label="Option 2" value="option2" />
      <Button label="Option 3" value="option3" />
    </ButtonGroup>
  );
};

withActive.story = {
  name: 'With active',
};

Replace storybook-state usage from buttonGroup story with useState


#!/usr/bin/env node

/**
 * Module dependencies.
 */

const program = require('commander');
const fs = require('extfs');
const caller = require('./docsmith/utils/caller');
const templates = require('./docsmith/init/templates');
const init = require('./docsmith/init');

let template;

program
  .arguments('[template]')
  .option('-f, --force', 'Initialise whether the current directory is empty or not.')
  .option('--defaults', 'Accepts defaults prompts and skips confirmation.')
  .option('-l, --link', 'For development purposes. Link local packages.')
  .option('--verbose', 'Display npm log.')
  .action(function(templ) {
    template = templ;
  })
  .parse(process.argv);

// check if this is an empty folder.

fs.isEmpty('.', function(empty) {
  if (empty || program.force) {
    if (caller.original()) {
      // initialises from a built-in template
      templates.init(template);
    } else {
      // called from a content as code instance, initialise from the instance configuration
      init.run({
        template,
        config: caller.path(true),
        link: program.link,
        defaults: program.defaults,
        verbose: program.verbose
      });
    }
  } else {
    console.warn('This directory is not empty. Aborting init. Use --force to ignore current content.');
  }
});

#!/usr/bin/env node

/**
 * Module dependencies.
 */

const program = require('commander');
const fs = require('extfs');
const caller = require('./docsmith/utils/caller');
const templates = require('./docsmith/init/templates');
const init = require('./docsmith/init');

let template;

program
  .arguments('[template]')
  .option('-f, --force', 'Initialise whether the current directory is empty or not.')
  .option('--defaults', 'Accepts defaults prompts and skips confirmation.')
  .option('-l, --link', 'For development purposes. Link local packages.')
  .option('--debug', 'Display npm log.')
  .action(function(templ) {
    template = templ;
  })
  .parse(process.argv);

// check if this is an empty folder.

fs.isEmpty('.', function(empty) {
  if (empty || program.force) {
    if (caller.original()) {
      // initialises from a built-in template
      templates.init(template);
    } else {
      // called from a content as code instance, initialise from the instance configuration
      init.run({
        template,
        config: caller.path(true),
        link: program.link,
        defaults: program.defaults,
        verbose: program.debug
      });
    }
  } else {
    console.warn('This directory is not empty. Aborting init. Use --force to ignore current content.');
  }
});

Use debug option instead of verbose already used by main executable


var fs = require('fs');

module.exports.controller = function(app) {

    app.get('/', function(req, res) {
        fs.readdir(process.cwd() + '/public/photos/', function(err, data) {
            var photos = data.filter(function(file) {
                return null !== file.match(/\.jpg$/i);
            });
            res.render('index', { title: 'RPI Photo Booth', action: 'TAKE A PHOTO', last: photos.slice(0, 12) });
        });
    });

};

var fs = require('fs');

module.exports.controller = function(app) {

    app.get('/', function(req, res) {
        fs.readdir(process.cwd() + '/public/photos/', function(err, data) {
            var photos = data.filter(function(file) {
                return null !== file.match(/_thumb\.jpg$/i);
            });
            res.render('index', { title: 'RPI Photo Booth', action: 'TAKE A PHOTO', last: photos.slice(0, 12) });
        });
    });

};

Load thumbnails only at startup


// ==UserScript==
// @name        HideExtraCourses
// @namespace   https://www.github.com/zeta12ti/DuolingoCourseSwitcher
// @description Hides extra courses from the update introduced in the update on 2017/06/19.
// @include     https://*.duolingo.com/*
// @version     1.1
// @grant       none
// ==/UserScript==

// Base Languages. Use flag codes.
var extraCourses = [
    '_1PruQ',   // hide courses from Italian
    'OgUIe',    // hide courses from Hindi
    '_1ARRD'    // hide courses from Arabic
]

function hideExtraCourses() {
    var len = extraCourses.length
    for (var i=0; i<len; i++) {
        var smallFlagClass = '.' + extraCourses[i] + '._2c_Ro'
        var matchingFlags = document.querySelectorAll(smallFlagClass)
        var len2 = matchingFlags.length
        for (var j=0; j<len2; j++) {
            matchingFlags[j].parentNode.setAttribute('style', 'display: none')
        }
    }
}

async function waitForMenu() {
    try {
        var menu = document.querySelector('._3I51r._3HsQj._2OF7V')
    }
    catch(e) {
        setInterval(waitForMenu, 300)
        return
    }
    menu.addEventListener('mouseenter', hideExtraCourses)
    return
}

waitForMenu()

// ==UserScript==
// @name        HideExtraCourses
// @namespace   https://www.github.com/zeta12ti/DuolingoCourseSwitcher
// @description Hides extra courses from the update introduced in the update on 2017/06/19.
// @include     https://*.duolingo.com/*
// @version     1.1
// @grant       none
// ==/UserScript==

// Base Languages. Use flag codes.
var extraCourses = [
    '_1PruQ',   // hide courses from Italian
    'OgUIe',    // hide courses from Hindi
    '_1ARRD'    // hide courses from Arabic
]

function hideExtraCourses() {
    var len = extraCourses.length
    for (var i=0; i<len; i++) {
        var smallFlagClass = '.' + extraCourses[i] + '._2c_Ro'
        var matchingFlags = document.querySelectorAll(smallFlagClass)
        var len2 = matchingFlags.length
        for (var j=0; j<len2; j++) {
            matchingFlags[j].parentNode.setAttribute('style', 'display: none')
        }
    }
}

async function waitForMenu() {
    try {
        var menu = document.querySelector('._3I51r._3HsQj._2OF7V')
    }
    catch(e) {
        setInterval(waitForMenu, 300)
        return
    }
    hideExtraCourses()
    menu.addEventListener('mouseenter', hideExtraCourses)
    return
}

waitForMenu()

Hide extra courses when the page is sufficiently loaded


'use strict'

var test = require('tape')
var proxyquire = require('proxyquire')

test('router (server)', function (t) {
  var Router = proxyquire('../router', {
    'global/document': {},
    './history': {
      onPopState: noop,
      pushState: noop
    }
  })
  var router = Router()
  t.equal(router(), '')
  t.end()
})

function noop () {}

'use strict'

var test = require('tape')
var proxyquire = require('proxyquire')

test('router (server)', function (t) {
  var Router = proxyquire('../router', {
    'global/document': {},
    './history': {
      onPopState: noop,
      pushState: noop
    }
  })
  var router = Router()
  t.equal(router(), '')
  t.end()
})

test('router (browser) - set path', function (t) {
  t.plan(2)
  var Router = proxyquire('../router', {
    'global/document': {
      location: {pathname: '/the/initial/path'}
    },
    './history': {
      onPopState: noop,
      pushState: function (path) {
        t.equal(path, '/the/set/path')
      }
    }
  })
  var router = Router()
  t.equal(router(), '/the/initial/path')
  router.set('/the/set/path')
})

test('router (browser) - pop state', function (t) {
  var onPopState
  var Router = proxyquire('../router', {
    'global/document': {},
    './history': {
      onPopState: function (callback) {
        onPopState = callback
      },
      pushState: t.fail.bind(t, 'pushState not called when pop is handled')
    }
  })
  var router = Router()
  onPopState('/the/popped/path')
  t.equal(router(), '/the/popped/path')
  t.end()
})

function noop () {}

Add tests against path handling via user set or pop state


/* eslint-disable import/no-unresolved, global-require */
import nodeExternals from 'webpack-node-externals';
import webpack from 'webpack';

import common from './common';

const isProduction = process.env.NODE_ENV === 'production';

export default {
  name: 'server',
  entry: './server',
  target: 'node',
  node: {
    __dirname: false,
  },
  output: {
    filename: 'server.js',
    path: common.output.path,
    publicPath: common.output.publicPath,
    libraryTarget: 'commonjs2',
  },
  module: {
    rules: [
      ...common.module.rules,
      {
        exclude: /\.(js|json)$/,
        use: [
          {
            loader: 'url-loader',
            options: {
              emitFile: false,
              limit: 10000,
              name: `media/[name]${isProduction ? '.[hash:8]' : ''}.[ext]`,
            },
          },
        ],
      },
    ],
  },
  plugins: [
    ...common.plugins,
    new webpack.DefinePlugin({
      CLIENT_BUNDLE: JSON.stringify(isProduction ? require('../build/assets.json').client.js : '/js/client.js'),
      VENDOR_BUNDLE: JSON.stringify(isProduction ? require('../build/assets.json').vendor.js : '/js/vendor.js'),
    }),
  ],
  externals: [nodeExternals()],
  bail: isProduction,
};

/* eslint-disable import/no-unresolved, global-require */
import nodeExternals from 'webpack-node-externals';
import webpack from 'webpack';

import common from './common';

const isProduction = process.env.NODE_ENV === 'production';

export default {
  name: 'server',
  entry: './server',
  target: 'node',
  node: {
    __dirname: false,
  },
  output: {
    filename: 'server.js',
    path: common.output.path,
    publicPath: common.output.publicPath,
    libraryTarget: 'commonjs2',
  },
  module: {
    rules: [
      ...common.module.rules,
      {
        exclude: /\.(js|json)$/,
        use: [
          {
            loader: 'url-loader',
            options: {
              emitFile: false,
              limit: 10000,
              name: `media/[name]${isProduction ? '.[hash:8]' : ''}.[ext]`,
            },
          },
        ],
      },
    ],
  },
  plugins: [
    ...common.plugins,
    new webpack.DefinePlugin({
      CLIENT_BUNDLE: JSON.stringify(isProduction ? require('../build/assets.json').client.js : '/js/client.js'),
      VENDOR_BUNDLE: JSON.stringify(isProduction ? require('../build/assets.json').vendor.js : '/js/vendor.js'),
      'process.env': {
        PORT: JSON.stringify(process.env.PORT),
      },
    }),
  ],
  externals: [nodeExternals()],
  bail: isProduction,
};

Add process.env.PORT in webpack server config


var userController = require('../users/userController.js');
var helpers = require('./helpers.js'); // our custom middleware

module.exports = function (app, express) {
  app.get('/api/users/profile', userController.getUser);
  // app.post('/api/users/signin', userController.signin);
  app.post('/api/users/signup', userController.signup);
  // app.get('/api/users/signedin', userController.checkAuth);
  //
  // // authentication middleware used to decode token and made available on the request
  // // app.use('/api/links', helpers.decode);

  // If a request is sent somewhere other than the routes above,
  // send it through our custom error handler
  app.use(helpers.errorLogger);
  app.use(helpers.errorHandler);
};

var userController = require('../users/userController.js');
var helpers = require('./helpers.js'); // our custom middleware

module.exports = function (app, express) {
  app.get('/api/users/profile', userController.getUser);
  app.put('/api/users/profile', userController.updateUser);
  // app.post('/api/users/signin', userController.signin);
  app.post('/api/users/signup', userController.signup);
  // app.get('/api/users/signedin', userController.checkAuth);
  //
  // // authentication middleware used to decode token and made available on the request
  // // app.use('/api/links', helpers.decode);

  // If a request is sent somewhere other than the routes above,
  // send it through our custom error handler
  app.use(helpers.errorLogger);
  app.use(helpers.errorHandler);
};

Handle PUT request to user profile


// @flow

const path = require('path');

/**
 * This list is basically copy pasted from Metro Bundler but the tests
 * folders are excluded so we can execute tests in the Playground.
 *
 * TODO: do this only in the "playground" environment (not in production)
 *
 * See: https://github.com/facebook/metro/blob/01ab028d4fb3600c89438b0b7916ec8350f8f71a/packages/metro/src/blacklist.js#L17
 */
const sharedBlacklist = [
  /node_modules[/\\]react[/\\]dist[/\\].*/,
  /website\/node_modules\/.*/,
  /heapCapture\/bundle\.js/,
  // /.*\/__tests__\/.*/,
];

const config = {
  getBlacklistRE() {
    return new RegExp(
      '(' +
        sharedBlacklist
          .map(regexp => regexp.source.replace(/\//g, path.sep))
          .join('|') +
        ')$',
    );
  },
};

module.exports = config;

// @flow

const path = require('path');

/**
 * This list is basically copy pasted from Metro Bundler but the tests
 * folders are excluded so we can execute tests in the Playground.
 *
 * TODO: do this only in the "playground" environment (not in production)
 *
 * See: https://github.com/facebook/metro/blob/01ab028d4fb3600c89438b0b7916ec8350f8f71a/packages/metro/src/blacklist.js#L17
 */
const sharedBlacklist = [
  /node_modules[/\\]react[/\\]dist[/\\].*/,
  /website\/node_modules\/.*/,
  /heapCapture\/bundle\.js/,
  /\.build[/\\].*/,
  // /.*\/__tests__\/.*/,
];

const config = {
  getBlacklistRE() {
    return new RegExp(
      '(' +
        sharedBlacklist
          .map(regexp => regexp.source.replace(/\//g, path.sep))
          .join('|') +
        ')$',
    );
  },
};

module.exports = config;

Add build package.json to blacklist


import tmpl from '~/lib/tmpl.js';

/**
 * This module contains functions to help standardize the responses used to send back to ASK. The
 * constants are all functions, built by the `tmpl` tag function, documented below. They take
 * parameters and replace them with the data they are provided. Each index in the template string
 * corresponds to the parameter given. So `${1}` is the 2nd parameter when you execute the function.
 *
 * Responses that don't take any parameters are still functions, for the sake of consistency.
 */

export const CANCEL_RESPONSE = tmpl`Exiting Couch Potato`;

export const HELP_RESPONSE = tmpl`You can ask Couch Potato about the movies in your queue or add
new movies to it. Try asking "Is The Godfather on the list?". If it's not and you want to add
it, try saying "Add The Godfather"`;

export const WELCOME_DESCRIPTION = tmpl`This skill allows you to manage your Couch Potato movie
list.`;

import tmpl from '~/lib/tmpl.js';

/**
 * This module contains functions to help standardize the responses used to send back to ASK. The
 * constants are all functions, built by the `tmpl` tag function, documented below. They take
 * parameters and replace them with the data they are provided. Each index in the template string
 * corresponds to the parameter given. So `${1}` is the 2nd parameter when you execute the function.
 *
 * Responses that don't take any parameters are still functions, for the sake of consistency.
 */

export const CANCEL_RESPONSE = tmpl`Exiting Couch Potato`;

export const HELP_RESPONSE = tmpl`You can ask Libby about the movies or shows in your library, or
add new media to them. Try asking "is the movie The Godfather on the list", or try adding a new
show by saying "add the show Silicon Valley".`;

export const WELCOME_DESCRIPTION = tmpl`This skill allows you to manage your Sonarr, Radarr, Couch
Potato, or Sickbeard queue.`;

Update welcome and help responses.


define(function(require){
    var scene = new THREE.Object3D();
    var camera = new THREE.PerspectiveCamera(75, 16 / 9, 0.1, 5000);
    var loader = new THREE.JSONLoader(true);
    var origin = new THREE.Vector3();
    var StarSystem = require("component/starfield");
    var starfield = StarSystem.create(33, 100);

    scene.add(starfield);

    camera.position.set(0, 3, 10);
    camera.lookAt(origin);

    var ship;
    loader.load("models/spaceship-five.js", function(geometry, materials){
        materials[1].bumpMap.bumpScale = 0.0001;
        ship = new THREE.Object3D();
        ship.add(new THREE.Mesh(geometry, new THREE.MeshFaceMaterial(materials)));

        var tailLight = new THREE.PointLight(0xCCCCFF, 2, 3);
        tailLight.position.set(0, 0, 5.2);
        ship.add(tailLight);

        scene.add(ship);
        console.log(ship);
    });

    var light = new THREE.PointLight(0xFFFFFF, 1, 100);
    light.position.set(10, 10, 10);
    scene.add(light);

    return {
        scene: scene,
        camera: camera,
        render: render,
        init: init
    };

    function render(time){
        ship.rotation.y += 0.01;
    }
});

define(function(require){
    var scene = new THREE.Object3D();
    var camera = new THREE.PerspectiveCamera(75, 16 / 9, 0.1, 5000);
    var loader = new THREE.JSONLoader(true);
    var origin = new THREE.Vector3();
    var StarSystem = require("component/starfield");
    var starfield = StarSystem.create(33, 100);

    scene.add(starfield);

    camera.position.set(0, 3, 10);
    camera.lookAt(origin);

    var ship;
    loader.load("models/spaceship-five.js", function(geometry, materials){
        materials[1].bumpMap.bumpScale = 0.0001;
        ship = new THREE.Object3D();
        ship.add(new THREE.Mesh(geometry, new THREE.MeshFaceMaterial(materials)));

        var tailLight = new THREE.PointLight(0xCCCCFF, 2, 3);
        tailLight.position.set(0, 0, 5.2);
        ship.add(tailLight);

        scene.add(ship);
        console.log(ship);
    });

    var light = new THREE.PointLight(0xFFFFFF, 1, 100);
    light.position.set(10, 10, 10);
    scene.add(light);

    return {
        scene: scene,
        camera: camera,
        render: render
    };

    function render(time){
        ship.rotation.y += 0.01;
    }
});

Remove init from scene definition


/**
 * mSupply Mobile
 * Sustainable Solutions (NZ) Ltd. 2021
 */
import { syncStrings } from '../localization';

export const selectTemperatureSyncMessage = ({ vaccine }) => {
  const { isSyncingTemps = false, error } = vaccine;
  if (isSyncingTemps) {
    return syncStrings.downloading_temperature_logs;
  }

  if (error) {
    return `${syncStrings.error_downloading_temperature_logs} ${error}`;
  }

  return syncStrings.sync_complete;
};

export const selectIsSyncingTemps = ({ vaccine }) => {
  const { isSyncingTemps = false } = vaccine || {};
  return isSyncingTemps;
};

export const selectDownloadingLogsFrom = ({ vaccine }) => {
  const { downloadingLogsFrom } = vaccine;
  return downloadingLogsFrom;
};

export const selectLastDownloadTime = ({ vaccine }, macAddress) => {
  const { lastDownloadTime } = vaccine;
  return lastDownloadTime[macAddress] ? new Date(lastDownloadTime[macAddress]) : null;
};

export const selectLastDownloadFailed = ({ vaccine }, macAddress) => {
  const { lastDownloadStatus } = vaccine;
  const status = lastDownloadStatus[macAddress];
  const lastDownloadFailed = status === 'ERROR';
  return lastDownloadFailed;
};

export const selectIsDownloading = (state, macAddress) => {
  const downloadingLogsFrom = selectDownloadingLogsFrom(state);
  const isDownloading = downloadingLogsFrom === macAddress;

  return isDownloading;
};

/**
 * mSupply Mobile
 * Sustainable Solutions (NZ) Ltd. 2021
 */
import { syncStrings } from '../localization';

export const selectTemperatureSyncMessage = ({ vaccine }) => {
  const { isSyncingTemps = false, error } = vaccine;
  if (isSyncingTemps) {
    return syncStrings.downloading_temperature_logs;
  }

  if (error) {
    return `${syncStrings.error_downloading_temperature_logs} ${error}`;
  }

  return syncStrings.sync_complete;
};

export const selectIsSyncingTemps = ({ vaccine }) => {
  const { isSyncingTemps = false } = vaccine || {};
  return isSyncingTemps;
};

export const selectDownloadingLogsFrom = ({ vaccine }) => {
  const { downloadingLogsFrom } = vaccine;
  return downloadingLogsFrom;
};

export const selectLastDownloadTime = ({ vaccine }, macAddress) => {
  const { lastDownloadTime } = vaccine;
  return lastDownloadTime[macAddress] ? new Date(lastDownloadTime[macAddress]) : null;
};

export const selectLastDownloadFailed = ({ vaccine }, macAddress) => {
  const { lastDownloadStatus } = vaccine;
  const status = lastDownloadStatus[macAddress];
  const lastDownloadFailed = status !== 'OK';
  return lastDownloadFailed;
};

export const selectIsDownloading = (state, macAddress) => {
  const downloadingLogsFrom = selectDownloadingLogsFrom(state);
  const isDownloading = downloadingLogsFrom === macAddress;

  return isDownloading;
};

Add open up lastDownloadFailed to other error cases


Application.Controllers.controller("FilesController", ["$state", FilesController]);
function FilesController($state) {
    //console.log("FileController going to projects.project.files.all");
    //$state.go('projects.project.files.all');
    var ctrl = this;

    ctrl.showSearchResults = showSearchResults;

    //////////////////
    function showSearchResults() {
        $state.go('projects.project.files.search');
    }
}

Application.Controllers.controller("FilesController", ["$state", FilesController]);
function FilesController($state) {
    var ctrl = this;

    ctrl.showSearchResults = showSearchResults;

    init();

    //////////////////
    function showSearchResults() {
        $state.go('projects.project.files.search');
    }

    function init() {
        if ($state.current.name == "projects.project.files") {
            $state.go('projects.project.files.all');
        }
    }
}

Remove console statement. Move initialization logic for controller into init() method.


/**
 * Precompiles Underscore templates to a `.jst` file.
 *
 * ---------------------------------------------------------------
 *
 * (i.e. basically it takes HTML files and turns them into tiny little
 *  javascript functions that you pass data to and return HTML. This can
 *  speed up template rendering on the client, and reduce bandwidth usage.)
 *
 * For usage docs see:
 * 		https://github.com/gruntjs/grunt-contrib-jst
 *
 */

module.exports = function(grunt) {

	var templateFilesToInject = [
		'templates/**/*.html'
	];

	grunt.config.set('jst', {
		dev: {

			// To use other sorts of templates, specify a regexp like the example below:
			// options: {
			//   templateSettings: {
			//     interpolate: /\{\{(.+?)\}\}/g
			//   }
			// },

			// Note that the interpolate setting above is simply an example of overwriting lodash's
			// default interpolation. If you want to parse templates with the default _.template behavior
			// (i.e. using

), there's no need to overwrite `templateSettings.interpolate`.

			files: {
				// e.g.
				// 'relative/path/from/gruntfile/to/compiled/template/destination'  : ['relative/path/to/sourcefiles/**/*.html']
				'.tmp/public/jst.js': require('../pipeline').templateFilesToInject
			}
		}
	});

	grunt.loadNpmTasks('grunt-contrib-jst');
};

/**
 * Precompiles Underscore templates to a `.jst` file.
 *
 * ---------------------------------------------------------------
 *
 * (i.e. basically it takes HTML files and turns them into tiny little
 *  javascript functions that you pass data to and return HTML. This can
 *  speed up template rendering on the client, and reduce bandwidth usage.)
 *
 * For usage docs see:
 * 		https://github.com/gruntjs/grunt-contrib-jst
 *
 */

module.exports = function(grunt) {

	var templateFilesToInject = [
		'templates/**/*.html'
	];

	grunt.config.set('jst', {
		dev: {

			// To use other sorts of templates, specify a regexp like the example below:
			options: {
				templateSettings: {
					interpolate: /\{\{(.+?)\}\}/g
				}
			},

			// Note that the interpolate setting above is simply an example of overwriting lodash's
			// default interpolation. If you want to parse templates with the default _.template behavior
			// (i.e. using

), there's no need to overwrite `templateSettings.interpolate`.

			files: {
				// e.g.
				// 'relative/path/from/gruntfile/to/compiled/template/destination'  : ['relative/path/to/sourcefiles/**/*.html']
				'.tmp/public/jst.js': require('../pipeline').templateFilesToInject
			}
		}
	});

	grunt.loadNpmTasks('grunt-contrib-jst');
};

Use custom interpolation for client templates.