(function(){

  angular
    .module('linguine.analysis')
    .controller('AnalysisIndexController', AnalysisIndexController);

  function AnalysisIndexController ($scope, $http, $interval) {

    $scope.reapply = false;

    $scope.fetchAnalyses = function() {
			$http.get('api/analysis')
				.success(function (data) {
					$scope.analyses = data;
			});
    }

    $scope.fetchAnalyses();
    $scope.reapply = true;

    $http.get('api/corpora')
      .success(function (data) {
        $scope.corpora = data;
    });

    var timeIntervalInSec = 10;
    $interval($scope.fetchAnalyses, 1000 * timeIntervalInSec);

    $scope.findCorpus = function (id) {
      return _.find($scope.corpora, {'_id': id});
    };

    $scope.getEtaTime = function(analysis) {
      var d = new Date(analysis.time_created);
      d.setSeconds(d.getSeconds() + analysis.eta);
      return d.toLocaleTimeString()
    };

    $scope.showTimeCreated = function(analysis) {
      var d = new Date(analysis.time_created);
      return d.toLocaleDateString() + " " + d.toLocaleTimeString()
    };
  }
})();

(function(){

  angular
    .module('linguine.analysis')
    .controller('AnalysisIndexController', AnalysisIndexController);

  function AnalysisIndexController ($scope, $http, $interval) {

    $scope.reapply = false;

    $scope.fetchAnalyses = function() {
			$http.get('api/analysis')
        .success(function (data) {
          $scope.analyses = data;
          $scope.analyses.forEach(function(analysis) {

            var now = new Date();
            var timeCreatedDate = new Date(analysis.time_created);
						var timeCreatedDatePlusTwelveHrs = timeCreatedDate.setHours(timeCreatedDate.getHours() + 12);

            if(!analysis.complete && timeCreatedDatePlusTwelveHrs < now) {
              $http.delete('api/analysis/' + analysis._id)
								.error(function (data) {
								  flash.danger.setMessage("An error occured trying to delete an erraneous analysis");
							});
            }
          });
			});
    }

    $scope.fetchAnalyses();
    $scope.reapply = true;

    $http.get('api/corpora')
      .success(function (data) {
        $scope.corpora = data;
    });

    var timeIntervalInSec = 10;
    $interval($scope.fetchAnalyses, 1000 * timeIntervalInSec);

    $scope.findCorpus = function (id) {
      return _.find($scope.corpora, {'_id': id});
    };

    $scope.getEtaTime = function(analysis) {
      var d = new Date(analysis.time_created);
      d.setSeconds(d.getSeconds() + analysis.eta);
      return d.toLocaleTimeString()
    };

    $scope.showTimeCreated = function(analysis) {
      var d = new Date(analysis.time_created);
      return d.toLocaleDateString() + " " + d.toLocaleTimeString()
    };
  }
})();

Remove analysis if it does not complete in 12hrs


var loadTodos = function() {
  $.ajax({
    url: "/graphql?query={todoList{id,text,done}}",
    context: document.body
  }).done(function(data) {
    console.log(data);
    var dataParsed = JSON.parse(data);
    var todos = dataParsed.data.todoList;

    if (!$('.todo-list').length) {
      $(this).append('

');
    }

    if (!todos.length) {
      $(this).append('
There are no tasks for you today
');
    }

    $.each(todos, function(i, v) {
      var doneHtml = '<input id="' + v.id + '" type="checkbox"' + (v.done ? ' checked="checked"' : '') + '>';
      var labelHtml = '' + doneHtml + v.text + '';
      var itemHtml = '
' + labelHtml + '
';

      $('.todo-list').append(itemHtml);
    });
  });
};

$(document).ready(function() {
  loadTodos();
});

var loadTodos = function() {
  $.ajax({
    url: "/graphql?query={todoList{id,text,done}}"
  }).done(function(data) {
    console.log(data);
    var dataParsed = JSON.parse(data);
    var todos = dataParsed.data.todoList;

    if (!todos.length) {
      $('.todo-list-container').append('
There are no tasks for you today
');
    }

    $.each(todos, function(i, v) {
      var doneHtml = '<input id="' + v.id + '" type="checkbox"' + (v.done ? ' checked="checked"' : '') + '>';
      var labelHtml = '' + doneHtml + v.text + '';
      var itemHtml = '
' + labelHtml + '
';

      $('.todo-list-container').append(itemHtml);
    });
  });
};

$(document).ready(function() {
  loadTodos();
});

Adjust javascript for updated html structure


import fs from 'fs'
import path from 'path'
import {rollup} from 'rollup'
import uglify from 'rollup-plugin-uglify'
import chalk from 'chalk'

async function build () {
  console.log(chalk.cyan('Building individual translations.'))
  const files = fs.readdirSync('./src/locale/translations')
  files.forEach(async function (file) {
    const inputOptions = {
      input: path.join(__dirname, '..', 'src', 'locale', 'translations', file),
      plugins: [
        uglify()
      ]
    }
    const bundle = await rollup(inputOptions)
    const outputOptions = {
      file: path.join(__dirname, '..', 'dist', 'locale', 'translations', file),
      format: 'es'
    }
    await bundle.write(outputOptions)
  })
  await console.log(chalk.green('Individual translations built.'))
}

async function buildAll () {
  console.log(chalk.cyan('Building translation importer.'))
  const bundle = await rollup({
    input: path.join(__dirname, '..', 'src', 'locale', 'index.js'),
    plugins: [
      uglify()
    ]
  })
  await bundle.write({
    file: path.join(__dirname, '..', 'dist', 'locale', 'index.js'),
    format: 'es'
  })
  await console.log(chalk.green('All translations built.'))
}

build()
buildAll()

import fs from 'fs'
import path from 'path'
import {rollup} from 'rollup'
import uglify from 'rollup-plugin-uglify'
import chalk from 'chalk'
import buble from 'rollup-plugin-buble'

async function build () {
  console.log(chalk.cyan('Building individual translations.'))
  const files = fs.readdirSync('./src/locale/translations')
  files.forEach(async function (file) {
    const inputOptions = {
      input: path.join(__dirname, '..', 'src', 'locale', 'translations', file),
      plugins: [
        buble(),
        uglify()
      ]
    }
    const bundle = await rollup(inputOptions)
    const outputOptions = {
      file: path.join(__dirname, '..', 'dist', 'locale', 'translations', file),
      format: 'es'
    }
    await bundle.write(outputOptions)
  })
  await console.log(chalk.green('Individual translations built.'))
}

async function buildAll () {
  console.log(chalk.cyan('Building translation importer.'))
  const bundle = await rollup({
    input: path.join(__dirname, '..', 'src', 'locale', 'index.js'),
    plugins: [
      buble(),
      uglify()
    ]
  })
  await bundle.write({
    file: path.join(__dirname, '..', 'dist', 'locale', 'index.js'),
    format: 'es'
  })
  await console.log(chalk.green('All translations built.'))
}

build()
buildAll()

Apply Buble to build locale


import { describeModule, it } from 'ember-mocha';
import { setResolverRegistry } from 'tests/test-support/resolver';

window.expect = chai.expect;

function setupRegistry() {
  setResolverRegistry({
    'component:x-foo': Ember.Component.extend()
  });
}

var a = 0;
var b = 0;
var beforeSetupOk = false;
var beforeTeardownOk = false;

describeModule('component:x-foo', 'TestModule callbacks', {
  beforeSetup: function() {
    setupRegistry();

    beforeSetupOk = (a === 0);
    b += 1;
  },

  setup: function() {
    a += 1;
  },

  beforeTeardown: function() {
    beforeTeardownOk = (a === 1);
    b -= 1;
  },

  teardown: function() {
    a -= 1;
  }

}, function() {
  it("beforeSetup callback is called prior to any test setup", function() {
    expect(beforeSetupOk).to.be.truthy;
    expect(b).to.equal(1);
  });

  it("setup callback is called prior to test", function() {
    expect(a).to.equal(1);
  });

  it("teardown callback is called after test", function() {
    expect(a).to.equal(1);
  });

  it("beforeTeardown callback is called prior to any test teardown", function() {
    expect(beforeTeardownOk).to.be.truthy;
    expect(b).to.equal(1);
  });

});

import { describeModule, it } from 'ember-mocha';
import { setResolverRegistry } from 'tests/test-support/resolver';

window.expect = chai.expect;

function setupRegistry() {
  setResolverRegistry({
    'component:x-foo': Ember.Component.extend()
  });
}

var callbackOrder, setupContext, teardownContext, beforeSetupContext, afterTeardownContext;

describeModule('component:x-foo', 'TestModule callbacks', {
  beforeSetup: function() {
    beforeSetupContext = this;
    callbackOrder = [ 'beforeSetup' ];

    setupRegistry();
  },

  setup: function() {
    setupContext = this;
    callbackOrder.push('setup');

    expect(setupContext).to.not.equal(beforeSetupContext);
  },

  teardown: function() {
    teardownContext = this;
    callbackOrder.push('teardown');

    expect(callbackOrder).to.deep.equal([ 'beforeSetup', 'setup', 'teardown']);
    expect(setupContext).to.equal(teardownContext);
  },

  afterTeardown: function() {
    afterTeardownContext = this;
    callbackOrder.push('afterTeardown');

    expect(callbackOrder).to.deep.equal([ 'beforeSetup', 'setup', 'teardown', 'afterTeardown']);
    expect(afterTeardownContext).to.equal(beforeSetupContext);
    expect(afterTeardownContext).to.not.equal(teardownContext);
  }

}, function() {
  it("should call setup callbacks in the correct order", function() {
    expect(callbackOrder).to.deep.equal([ 'beforeSetup', 'setup' ]);
  });
});

Fix tests to match ember-test-helpers 0.3.0.


var $ = django.jQuery;
$(function() {
  $('input:checkbox.publish-checkbox').change(function() {
    if ($(this).is(':checked')) {
      var url = $(this).attr('data-publish');
    } else {
      var url = $(this).attr('data-unpublish');
    }
    $.get(url, function(data) {
      if (data.success) {
        $('.published-icon').find('img').toggle();
      }
    });
  });
});

(function(){
  var $ = django.jQuery;
  $(function() {
    $('input:checkbox.publish-checkbox').change(function() {
      var elem = $(this);
      if (elem.is(':checked')) {
        var url = elem.attr('data-publish');
      } else {
        var url = elem.attr('data-unpublish');
      }
      $.get(url, function(data) {
        if (data.success) {
          elem.parent().find('.published-icon img').toggle();
        }
      });
    });
  });
})();

Fix scoping of dollar symbol and only change icon of selected item on publish/unpublish


const { ALLOWED_CSV_FIELD_PATHS } = require('./constants')

const getAllowedCSVFieldPaths = fieldPaths => {
  const allowed = fieldPaths.reduce((agg, x) => (
    ALLOWED_CSV_FIELD_PATHS.indexOf(x) > -1 ? [...agg, x] : agg
  ), [])
  return allowed
}

const getFileName = (queryParams, fileType) => {
  const fileExtension = fileType === 'JSON' ? 'jsonl' : 'csv'
  const { limit, skip, frozen, view, download, ...other } = queryParams
  const filterNames = Object.keys(other)
  if (filterNames.length === 0) {
    return `all.${fileExtension}.gz`
  }
  return `${filterNames.reduce((agg, x) => `${agg}_${x}=${other[x]}`, '')}.${fileExtension}.gz`
}

const getDescendantProp = obj => path => (
  path.split('.').reduce((acc, part) => acc ? acc[part] : '', obj)
)

module.exports = { getAllowedCSVFieldPaths, getDescendantProp, getFileName }

const crypto = require('crypto')
const { ALLOWED_CSV_FIELD_PATHS } = require('./constants')

const getAllowedCSVFieldPaths = fieldPaths => {
  const allowed = fieldPaths.reduce((agg, x) => (
    ALLOWED_CSV_FIELD_PATHS.indexOf(x) > -1 ? [...agg, x] : agg
  ), [])
  return allowed
}

const getFileName = (queryParams, fileType) => {
  const fileExtension = fileType === 'JSON' ? 'jsonl' : 'csv'
  const { limit, skip, frozen, view, download, ...other } = queryParams
  const filterNames = Object.keys(other)
  if (filterNames.length === 0) {
    return `all.${fileExtension}.gz`
  }
  const fileName = `${filterNames.reduce((agg, x) => `${agg}_${x}=${other[x]}`, '')}`
  const hexFileName = crypto.createHmac('sha256', 'my-secret').update(fileName).digest('hex')
  return `${hexFileName}.${fileExtension}.gz`
}

const getDescendantProp = obj => path => (
  path.split('.').reduce((acc, part) => acc ? acc[part] : '', obj)
)

module.exports = { getAllowedCSVFieldPaths, getDescendantProp, getFileName }

Save file name as hash to avoid errors


import THREE from 'three';

export default function pointerLock( controls ) {
  const hasPointerLock = 'pointerLockElement' in document ||
    'mozPointerLockElement' in document ||
    'webkitPointerLockElement' in document;

  if ( !hasPointerLock ) {
    return;
  }

  const element = document.body;
  const dispatcher = new THREE.EventDispatcher();

  function onPointerLockChange() {
    if ( document.pointerLockElement === element ||
         document.mozPointerLockElement === element ||
         document.webkitPointerLockElement === element ) {
      controls.enabled = true;
    } else {
      controls.enabled = false;
    }

    dispatcher.dispatchEvent({
      type: 'change',
      enabled: controls.enabled
    });
  }

  function onPointerLockError() {
    dispatcher.dispatchEvent({ type: 'error' });
  }

  document.addEventListener( 'pointerlockchange', onPointerLockChange );
  document.addEventListener( 'mozpointerlockchange', onPointerLockChange );
  document.addEventListener( 'webkitpointerlockchange', onPointerLockChange );

  document.addEventListener( 'pointerlockerror', onPointerLockError );
  document.addEventListener( 'mozpointerlockerror', onPointerLockError );
  document.addEventListener( 'webkitpointerlockerror', onPointerLockError );

  element.requestPointerLock = element.requestPointerLock ||
    element.mozRequestPointerLock ||
    element.webkitRequestPointerLock;

  document.addEventListener( 'click', () => element.requestPointerLock() );

  return dispatcher;
}

import THREE from 'three';

export default function pointerLock( controls, element = document.body ) {
  const hasPointerLock = (
    'pointerLockElement' in document ||
    'mozPointerLockElement' in document ||
    'webkitPointerLockElement' in document
  );

  if ( !hasPointerLock ) {
    return;
  }

  const dispatcher = new THREE.EventDispatcher();

  function onPointerLockChange() {
    controls.enabled = (
      element === document.pointerLockElement ||
      element === document.mozPointerLockElement ||
      element === document.webkitPointerLockElement
    );

    dispatcher.dispatchEvent({
      type: 'change',
      enabled: controls.enabled
    });
  }

  function onPointerLockError() {
    dispatcher.dispatchEvent({ type: 'error' });
  }

  document.addEventListener( 'pointerlockchange', onPointerLockChange );
  document.addEventListener( 'mozpointerlockchange', onPointerLockChange );
  document.addEventListener( 'webkitpointerlockchange', onPointerLockChange );

  document.addEventListener( 'pointerlockerror', onPointerLockError );
  document.addEventListener( 'mozpointerlockerror', onPointerLockError );
  document.addEventListener( 'webkitpointerlockerror', onPointerLockError );

  element.requestPointerLock = (
    element.requestPointerLock ||
    element.mozRequestPointerLock ||
    element.webkitRequestPointerLock
  );

  document.addEventListener( 'click', () => element.requestPointerLock() );

  return dispatcher;
}

Add pointerLock() custom element support.


const LOGIN_URL = '/login'

export const USER_LOGIN_FAIL = 'USER_LOGIN_FAIL';
export const USER_LOGIN_SUCCESS = 'USER_LOGIN_SUCCESS';
export const USER_LOGOUT = 'USER_LOGOUT';

let userLogInSuccess = email => {
  console.log('In userLogInSuccess, email=', email)
  return {
    type: USER_LOGIN_SUCCESS,
    payload: email
  }
}

let userLoginFail = () => {
  return {
    type: USER_LOGIN_FAIL,
    payload: null
  }
}

export const doLogout = () => {
  return {
    type : USER_LOGOUT,
    payload : null
  }
}

export const doLogin = (email, password) => {
  return dispatch => {
    fetch(LOGIN_URL, {
      method : 'get'
    })
    .then(response => {
      return response.json()
    })
    .then(json => {
      console.log('/login json=', json)
      dispatch(userLogInSuccess(json['email']))
    })
    .catch(error => {
      dispatch(userLoginFail())
    })
  }
}

const LOGIN_URL = '/login'

export const USER_LOGIN_FAIL = 'USER_LOGIN_FAIL';
export const USER_LOGIN_SUCCESS = 'USER_LOGIN_SUCCESS';
export const USER_LOGOUT = 'USER_LOGOUT';

let userLogInSuccess = email => {
  console.log('In userLogInSuccess, email=', email)
  return {
    type: USER_LOGIN_SUCCESS,
    payload: email
  }
}

let userLoginFail = () => {
  return {
    type: USER_LOGIN_FAIL,
    payload: null
  }
}

export const doLogout = () => {
  return {
    type : USER_LOGOUT,
    payload : null
  }
}

export const doLogin = (email, password) => {
  return dispatch => {
    fetch(LOGIN_URL, {
      method : 'post'
    })
    .then(response => {
      return response.json()
    })
    .then(json => {
      console.log('/login json=', json)
      dispatch(userLogInSuccess(json['email']))
    })
    .catch(error => {
      dispatch(userLoginFail())
    })
  }
}

Change login request to post


export default class ComponentExtensionItemSelectorToMembers {
    itemSelectorToMembers() {

        let selector = this.options.itemSelector || '[data-js-item]';
        let domItemsInSubModules = Array.from(this.el.querySelectorAll(
            `${this.moduleSelector}`)
        );
        let domItems = Array.from(this.el.querySelectorAll(selector));

        this.items = {};

        let isContainedInSubmodule = false;

        domItems.forEach((domItem) => {
            domItemsInSubModules.forEach((domItemInSubModule) => {
                if (!isContainedInSubmodule && domItemInSubModule.contains(domItem)) {
                    isContainedInSubmodule = true;
                }
            });

            if (!isContainedInSubmodule && domItem.dataset.jsItem) {
                if (this.items[domItem.dataset.jsItem + 's'] &&
                    (this.items[domItem.dataset.jsItem + 's'] instanceof Array)) {
                    // add to pluralized key and array
                    this.items[domItem.dataset.jsItem + 's'].push(domItem);
                } else if (this.items[domItem.dataset.jsItem]) {
                    // make pluralized key and array
                    this.items[domItem.dataset.jsItem + 's'] = [
                        this.items[domItem.dataset.jsItem],
                        domItem
                    ];

                    delete this.items[domItem.dataset.jsItem];
                } else {
                    // just one item
                    this.items[domItem.dataset.jsItem] = domItem;
                }
            }
        });
    }
}

import dashesToCamelCase from '../../helpers/string/dashes-to-camel-case';

export default class ComponentExtensionItemSelectorToMembers {
    itemSelectorToMembers() {

        let selector = this.options.itemSelector || '[data-js-item]';
        let domItemsInSubModules = Array.from(this.el.querySelectorAll(
            `${this.moduleSelector}`)
        );
        let domItems = Array.from(this.el.querySelectorAll(selector));

        this.items = {};

        let isContainedInSubmodule = false;

        domItems.forEach((domItem) => {
            domItemsInSubModules.forEach((domItemInSubModule) => {
                if (!isContainedInSubmodule && domItemInSubModule.contains(domItem)) {
                    isContainedInSubmodule = true;
                }
            });

            if (!isContainedInSubmodule && domItem.dataset.jsItem) {

                let itemsKey = dashesToCamelCase(domItem.dataset.jsItem);

                if (this.items[itemsKey + 's'] &&
                    (this.items[itemsKey + 's'] instanceof Array)) {
                    // add to pluralized key and array
                    this.items[itemsKey + 's'].push(domItem);
                } else if (this.items[itemsKey]) {
                    // make pluralized key and array
                    this.items[itemsKey + 's'] = [
                        this.items[itemsKey],
                        domItem
                    ];

                    delete this.items[itemsKey];
                } else {
                    // just one item
                    this.items[itemsKey] = domItem;
                }
            }
        });
    }
}

Convert dashed itemKeys to camel cased


import React from 'react';

class HeaderImg extends React.Component {
  render() {
    return (

    );
  }
}

export default HeaderImg;

import React from 'react';

class HeaderImg extends React.Component {
  render() {
    const style = {
      width: '98%',
      height: '500px',
      clear: 'both',
      margin: '0 1% .5em',
      text-align: 'center',
      overflow: 'hidden'
    };

    const src = 'https://wordanddeedindia.imgix.net/images/child.jpg?fit=crop&crop=faces';

    return (

    );
  }
}

export default HeaderImg;

Add inline style for header image component


"use strict";

const ScottishPostcode = require("../models/scottish_postcode");
const Postcode = require("../models/postcode");
const Pc = require("postcode");
const {
  InvalidPostcodeError,
  PostcodeNotFoundError,
  PostcodeNotInSpdError,
} = require("../lib/errors.js");

exports.show = (request, response, next) => {
  const { postcode } = request.params;
  if (!Pc.isValid(postcode.trim()))
    return next(new InvalidPostcodeError());

  ScottishPostcode.find(postcode, (error, result) => {
    if (error) return next(error);
    if (!result) {
      Postcode.find(postcode, (gerr, gres) => {
        if (gerr) return next(gerr);
        if (!gres) return next(new PostcodeNotFoundError());
        return next(new PostcodeNotInSpdError());
      });
    }
    response.jsonApiResponse = {
      status: 200,
      result: ScottishPostcode.toJson(result),
    };
    return next();
  });
};

"use strict";

const ScottishPostcode = require("../models/scottish_postcode");
const Postcode = require("../models/postcode");
const Pc = require("postcode");
const {
  InvalidPostcodeError,
  PostcodeNotFoundError,
  PostcodeNotInSpdError,
} = require("../lib/errors.js");

exports.show = (request, response, next) => {
  const { postcode } = request.params;
  if (!Pc.isValid(postcode.trim()))
    return next(new InvalidPostcodeError());

  ScottishPostcode.find(postcode, (error, result) => {
    if (error) return next(error);
    if (!result) {
      // The return value of Postcode.find is the return value of the callback
      // see postcode.js lines 186-188
      return Postcode.find(postcode, (gerr, gres) => {
        if (gerr) return next(gerr);
        if (!gres) return next(new PostcodeNotFoundError());
        return next(new PostcodeNotInSpdError());
      });
    }
    response.jsonApiResponse = {
      status: 200,
      result: ScottishPostcode.toJson(result),
    };
    return next();
  });
};

Return Postcode.find's callback to prevent next being called twice


'use strict';

describe('Routing', function () {
  var $route;
  beforeEach(module('bolt'));

  beforeEach(inject(function ($injector) {
    $route = $injector.get('$route');
  }));

  it('Should have /signup route, template, and controller', function () {
    expect($route.routes['/signup']).to.be.defined;
    expect($route.routes['/signup'].controller).to.equal('AuthController');
    expect($route.routes['/signup'].templateUrl).to.equal('app/auth/signup.html');
  });

  it('Should have /signin route, template, and controller', function () {
    expect($route.routes['/signin']).to.be.defined;
    expect($route.routes['/signin'].controller).to.equal('AuthController');
    expect($route.routes['/signin'].templateUrl).to.equal('app/auth/signin.html');
  });

  it('Should have /bolt route, template, and controller', function () {
    expect($route.routes['/bolt']).to.be.defined;
    expect($route.routes['/bolt'].controller).to.equal('BoltController');
    expect($route.routes['/bolt'].templateUrl).to.equal('app/views/bolt.html');
  });

  // it('Should have /shorten route, template, and controller', function () {
  //   expect($route.routes['/shorten']).to.be.defined;
  //   expect($route.routes['/shorten'].controller).to.equal('ShortenController');
  //   expect($route.routes['/shorten'].templateUrl).to.equal('app/shorten/shorten.html');
  // });
});

'use strict';

describe('Routing', function () {
  var $route;
  beforeEach(module('bolt'));

  beforeEach(inject(function ($injector) {
    $route = $injector.get('$route');
  }));

  it('Should have /signup route, template, and controller', function () {
    expect($route.routes['/signup']).to.be.defined;
    expect($route.routes['/signup'].controller).to.equal('AuthController');
    expect($route.routes['/signup'].templateUrl).to.equal('app/auth/signup.html');
  });

  it('Should have /signin route, template, and controller', function () {
    expect($route.routes['/signin']).to.be.defined;
    expect($route.routes['/signin'].controller).to.equal('AuthController');
    expect($route.routes['/signin'].templateUrl).to.equal('app/auth/signin.html');
  });

  it('Should have /bolt route, template, and controller', function () {
    expect($route.routes['/']).to.be.defined;
    expect($route.routes['/'].controller).to.equal('BoltController');
    expect($route.routes['/'].templateUrl).to.equal('app/views/bolt.html');
  });

  // it('Should have /shorten route, template, and controller', function () {
  //   expect($route.routes['/shorten']).to.be.defined;
  //   expect($route.routes['/shorten'].controller).to.equal('ShortenController');
  //   expect($route.routes['/shorten'].templateUrl).to.equal('app/shorten/shorten.html');
  // });
});

Fix routing to /bolt to / in tests


var EnhancedPullRequest = {
  _callbacks: [],

  /**
   * Loads the given callback and registers it to reload on pjax requests.
   *
   * @param {Function} callback - function to execute
   */
  load: function (callback) {
    this._callbacks.unshift(callback);
    callback();
  }
};

chrome.runtime.onMessage.addListener(function (msg, sender, sendResponse) {
  // This isn't pretty, but I haven't found a better way yet.
  $('.epr').remove();
  EnhancedPullRequest._callbacks.forEach((callback) => callback());
});

const EnhancedPullRequest = {
  _callbacks: [],

  /**
   * Loads the given callback and registers it to reload on pjax requests.
   *
   * @param {Function} callback - function to execute
   */
  load: function (callback) {
    this._callbacks.unshift(callback);
    callback();
  }
};

chrome.runtime.onMessage.addListener(function (msg, sender, sendResponse) {
  // This isn't pretty, but I haven't found a better way yet.
  $('.epr').remove();
  EnhancedPullRequest._callbacks.forEach((callback) => callback());
});

Use const instead of var


import * as ACTION_TYPES from '../constants/action_types'

function extractToken(hash, oldToken) {
  const match = hash.match(/access_token=([^&]+)/)
  let token = !!match && match[1]
  if (!token) {
    token = oldToken
  }
  return token
}

export function checkAuth(dispatch, oldToken, location) {
  const token = extractToken(location.hash, oldToken)
  if (window.history && window.history.replaceState) {
    window.history.replaceState(window.history.state, document.title, window.location.pathname)
  } else {
    document.location.hash = '' // this is a fallback for IE < 10
  }
  if (token) {
    dispatch({
      type: ACTION_TYPES.AUTHENTICATION.USER_SUCCESS,
      payload: {
        response: {
          accessToken: token,
          tokenType: 'onboarding_auth',
          expiresIn: 7200,
          createdAt: new Date(),
        },
      },
    })
  } else {
    const url = ENV.AUTH_DOMAIN + '/api/oauth/authorize.html' +
      '?response_type=token' +
      '&scope=public' +
      '&client_id=' + ENV.AUTH_CLIENT_ID +
      '&redirect_uri=' + ENV.AUTH_REDIRECT_URI

    window.location.href = url
  }
}

export function resetAuth(dispatch, location) {
  checkAuth(dispatch, null, location)
}

import * as ACTION_TYPES from '../constants/action_types'

function extractToken(hash, oldToken) {
  const match = hash.match(/access_token=([^&]+)/)
  let token = !!match && match[1]
  if (!token) {
    token = oldToken
  }
  return token
}

export function checkAuth(dispatch, oldToken, location) {
  const token = extractToken(location.hash, oldToken)
  if (window.history && window.history.replaceState) {
    window.history.replaceState(window.history.state, document.title, window.location.pathname)
  } else {
    document.location.hash = '' // this is a fallback for IE < 10
  }
  if (token) {
    dispatch({
      type: ACTION_TYPES.AUTHENTICATION.USER_SUCCESS,
      payload: {
        response: {
          accessToken: token,
          tokenType: 'onboarding_auth',
          expiresIn: 7200,
          createdAt: new Date(),
        },
      },
    })
  } else {
    const url = ENV.AUTH_DOMAIN + '/api/oauth/authorize.html' +
      '?response_type=token' +
      '&scope=web_app+public' +
      '&client_id=' + ENV.AUTH_CLIENT_ID +
      '&redirect_uri=' + ENV.AUTH_REDIRECT_URI

    window.location.href = url
  }
}

export function resetAuth(dispatch, location) {
  checkAuth(dispatch, null, location)
}

Revert "Only ask for public privileges"


#!/usr/bin/env node

const chalk = require("chalk");
const cli = require("../cli/cli");

console.log(``);
console.log(`${chalk.black(chalk.bgYellow("  ~~~~~~~~~~  "))}`);
console.log(`${chalk.black(chalk.bgYellow("   🍫  kaba    "))}`);
console.log(`${chalk.black(chalk.bgYellow("  ~~~~~~~~~~  "))}`);
console.log(``);

console.log('Running webpack ...');
console.log();

if (cli.showHelp())
{
    // @todo implement
    console.log("show help");
    process.exit(0);
}
else if (cli.showVersion())
{
    // @todo implement
    console.log("show version");
    process.exit(0);
}

return require('webpack/bin/webpack');

#!/usr/bin/env node

const chalk = require("chalk");
const cli = require("../cli/cli");

console.log(``);
console.log(`${chalk.black(chalk.bgYellow("  ~~~~~~~~~~  "))}`);
console.log(`${chalk.black(chalk.bgYellow("   🍫  kaba    "))}`);
console.log(`${chalk.black(chalk.bgYellow("  ~~~~~~~~~~  "))}`);
console.log(``);

console.log('Running webpack ...');
console.log();

if (cli.showHelp())
{
    // @todo implement
    console.log("show help");
    process.exit(0);
}
else if (cli.showVersion())
{
    // @todo implement
    console.log("show version");
    process.exit(0);
}

// set environment
process.env.NODE_ENV = cli.isDebug() ? '"development"' : '"production"';

// strip all other arguments
process.argv = process.argv.slice(0,2);

require('webpack/bin/webpack');

Set node env + strip our CLI arguments


import { observable, action, runInAction } from 'mobx'
import axios from 'axios'
import OrderModel from '../models/OrderModel';

class AppState {
  @observable authenticated = false;
  @observable authenticating = false;
  @observable items = [];
  @observable item = {};

  // constructor() {
  //   this.authenticated = false;
  //   this.authenticating = false;
  //   this.items = [];
  //   this.item = {};
  // }

  @action async fetchData(next = () => {}) {
    const APIURL = 'http://localhost:3000/?limit=1000';

    try {
      let {data} = await axios.get(APIURL)
      // this.setOrders(data);
      runInAction(() => this.items.replace(data))
    } catch (e) {
      // this.setOrders([]);
      runInAction(() => this.items.replace([]))
    }

    next();
  }

  @action setOrders(data) {
    // this.items = data.map(item => new OrderModel(item));
    this.items.replace(data);
  }

  @action setData(data) {
    this.items = data
  }

  @action setSingle(data) {
    this.item = data
  }

  @action clearItems() {
    this.items = []
    this.item = {}
  }

  @action authenticate() {
    return new Promise((resolve,reject) => {
          this.authenticating = true
          setTimeout(() => {
            this.authenticated = !this.authenticated
            this.authenticating = false
            resolve(this.authenticated)
        }, 0)
    })
  }

}

export default AppState;

import { observable, action, runInAction } from 'mobx'
import axios from 'axios'

class AppState {
  @observable authenticated = false;
  @observable authenticating = false;
  @observable items = [];
  @observable item = {};

  // constructor() {
  //   this.authenticated = false;
  //   this.authenticating = false;
  //   this.items = [];
  //   this.item = {};
  // }

  @action async fetchData(next = () => {}) {
    const APIURL = 'http://localhost:3000/?limit=1000';

    try {
      let {data} = await axios.get(APIURL)
      // this.setOrders(data);
      runInAction(() => this.items.replace(data))
    } catch (e) {
      // this.setOrders([]);
      runInAction(() => this.items.replace([]))
    }

    next();
  }

  @action setOrders(data) {
    // this.items = data.map(item => new OrderModel(item));
    this.items.replace(data);
  }

  @action setData(data) {
    this.items = data
  }

  @action setSingle(data) {
    this.item = data
  }

  @action clearItems() {
    this.items = []
    this.item = {}
  }

  @action authenticate() {
    return new Promise((resolve,reject) => {
          this.authenticating = true
          setTimeout(() => {
            this.authenticated = !this.authenticated
            this.authenticating = false
            resolve(this.authenticated)
        }, 0)
    })
  }

}

export default AppState;

Remove import of non-existent module


var args = arguments[0] || {},
	moment = require('alloy/moment'),
	imagecache = require('imagecache');

function closeWindow(e) {
    $.scheduleDetailWindow.closeWindow();
}
imagecache.cachedImageView('speakerimages', args.picture, $.pictureView);
Ti.API.info(JSON.stringify(args.room));
$.titleLabel.text = args.title;
$.descriptionLabel.text = args.content;
$.nameLabel.text = args.speaker;
$.bioLabel.text = args.bio;
$.dateLabel.text = moment(args.startDate).format('DD MMM HH:mm') + " - " + moment(args.startDate).format('HH:mm');
$.roomLabel.text = args.room;

Ti.App.fireEvent('setMainTitle', {
	title: "Schedule"
});

var args = arguments[0] || {},
	moment = require('alloy/moment'),
	imagecache = require('imagecache');

function closeWindow(e) {
    $.scheduleDetailWindow.closeWindow();
}
imagecache.cachedImageView('speakerimages', args.picture, $.pictureView);

$.titleLabel.text = args.get('title');
$.descriptionLabel.text = args.get('content');
$.nameLabel.text = args.speaker;
$.bioLabel.text = args.bio;
$.dateLabel.text = moment(args.get('startDate')).format('DD MMM HH:mm') + " - " + moment(args.get('startDate')).format('HH:mm');
$.roomLabel.text = args.get('room');

Ti.App.fireEvent('setMainTitle', {
	title: "Schedule"
});

Set talk details (to be refactored)


import React from 'react'
import {Grid, Row, Col} from 'react-bootstrap'

import SecurityBanner from '../components/SecurityBanner'
import Header from '../components/Header'
import Nav from '../components/Nav'

export default class App extends React.Component {
	render() {
		return (

				<SecurityBanner location={this.props.location} />

					{this.props.children.type.useNavigation === false ? (
						<Col xs={12}>
							{this.props.children}
						</Col>
					) : (
						<Grid>
							<Row>
								<Col sm={3}>

								</Col>
								<Col sm={9}>
									{this.props.children}
								</Col>
							</Row>
						</Grid>
					)}

		)
	}
}

import React from 'react'
import {Grid, Row, Col} from 'react-bootstrap'

import SecurityBanner from '../components/SecurityBanner'
import Header from '../components/Header'
import Nav from '../components/Nav'

export default class App extends React.Component {
	render() {
		return (

				<SecurityBanner location={this.props.location} />

					{this.props.children.type.useNavigation === false ? (
						<Col xs={12}>
							{this.props.children}
						</Col>
					) : (
						<Grid>
							<Row>
								<Col sm={3}>
									{this.props.children.type.useNavigation || }
								</Col>
								<Col sm={9}>
									{this.props.children}
								</Col>
							</Row>
						</Grid>
					)}

		)
	}
}

Allow setting Route.useNavigation to an actual navigation component to replace the default


'use strict';

/* Directives */

var module = angular.module('myApp.directives', []);

module.directive('appVersion', ['version', function(version) {
    return function(scope, elm, attrs) {
      elm.text(version);
    };
  }]);

module.directive('gcBubbleRow', [function() {
    var bubbledValue = '';
    return {
        restrict: 'EA',
        scope: {
            choices: '=',
            value: '='
        },
        templateUrl: 'partials/gcBubbleRow.html',
        link: function(scope, element, attrs) {
            scope.getBubbleValue = function(bub, $index) {
                var indexes;
                if (scope.value && scope.value.indexOf($index) > -1) {
                    return bubbledValue;
                } else {
                    return scope.choices.substr($index,1);
                }
            };
            scope.toggleBubbleValue = function($index, $event) {
            };
        }
    };
  }]);

'use strict';

/* Directives */

var module = angular.module('myApp.directives', []);

module.directive('appVersion', ['version', function(version) {
    return function(scope, elm, attrs) {
      elm.text(version);
    };
  }]);

module.directive('gcBubbleRow', [function() {
    var bubbledValue = '';
    return {
        restrict: 'EA',
        scope: {
            choices: '=',
            value: '='
        },
        templateUrl: 'partials/gcBubbleRow.html',
        link: function(scope, element, attrs) {
            scope.getBubbleValue = function(bub, $index) {
                var indexes;
                if (scope.value && scope.value.indexOf($index) > -1) {
                    return bubbledValue;
                } else {
                    return scope.choices.substr($index,1);
                }
            };
            scope.toggleBubbleValue = function($index, $event) {
                var oldVal = scope.value || "";
                var newVal = oldVal;
                if (oldVal.indexOf($index) > -1) {
                    newVal = oldVal.replace($index, '', 'g');
                } else {
                    newVal += $index;
                    // enforce sort order
                    newVal = newVal.split("");
                    newVal.sort();
                    newVal = newVal.join("");
                }
                scope.value = newVal;
                $event.stopPropagation();
            };
        }
    };
  }]);

Make the data binding on value 2-way; allow clicking on bubbles to toggle


(function(app) {

app.filter('lengthUnit', function() {
  return function(value) {
    if (value < 1500) {
      return Math.round(value) + ' m';
    }
    return Math.round(value / 10) / 100 + ' km';
  }
});

// From http://stackoverflow.com/a/17364716/252384
app.directive('ngEnter', function() {
  return function(scope, element, attrs) {
    element.bind("keydown keypress", function(event) {
      if(event.which === 13) {
        scope.$apply(function(){
          scope.$eval(attrs.ngEnter);
        });

        event.preventDefault();
      }
    });
  };
});

})(window.angkot.app);

(function(app) {

app.filter('lengthUnit', function() {
  return function(value) {
    if (value < 1500) {
      return Math.round(value) + ' m';
    }
    return Math.round(value / 10) / 100 + ' km';
  }
});

// From http://stackoverflow.com/a/17364716/252384
app.directive('ngEnter', function() {
  return function(scope, element, attrs) {
    element.bind("keydown keypress", function(event) {
      if(event.which === 13) {
        scope.$apply(function(){
          scope.$eval(attrs.ngEnter);
        });

        event.preventDefault();
      }
    });
  };
});

app.directive('eatClick', function() {
  return function(scope, element, attrs) {
    $(element).click(function(event) {
      event.stopPropagation();
    });
  }
})

})(window.angkot.app);

Add directive to stop event propagation: eat-click


var loaderUtils = require('loader-utils');

module.exports = function applyLoader(source) {
  var query = loaderUtils.parseQuery(this.query);
  var args = [];

  this.cacheable && this.cacheable();

  // apply?config=key => sourceFn(require('webpack.config').key)
  if (typeof query.config === 'string') {
    if (!(query.config in this.options))
      throw new Error("apply-loader: '"+query.config+"' property not present in webpack config");
    args.push(this.options[query.config]);
  }

  // apply?{obj: {a: 1, b: 2}} => sourceFn({a: 1, b:2})
  if (query.obj) {
    args.push(query.obj);
  }

  // apply?args[]=1&args[]=2 => sourceFn(1, 2)
  if (Array.isArray(query.args)) {
    args.push.apply(args, query.args);
  }

  var json = JSON.stringify(args);
  source += "\n\nmodule.exports = module.exports.apply(module, " + json + ")";
  return source;
};

var loaderUtils = require("loader-utils");

module.exports = function() {}
module.exports.pitch = function applyLoader(remainingRequest) {
  var query = loaderUtils.parseQuery(this.query);
  var args = [];

  this.cacheable && this.cacheable();

  // apply?config=key => sourceFn(require('webpack.config').key)
  if (typeof query.config === "string") {
    if (!(query.config in this.options))
      throw new Error("apply-loader: '"+query.config+"' property not present in webpack config");
    args.push(this.options[query.config]);
  }

  // apply?{obj: {a: 1, b: 2}} => sourceFn({a: 1, b:2})
  if (query.obj) {
    args.push(query.obj);
  }

  // apply?args[]=1&args[]=2 => sourceFn(1, 2)
  if (Array.isArray(query.args)) {
    args.push.apply(args, query.args);
  }

  return [
    "var req = require(",
    JSON.stringify("!!" + remainingRequest),
    ");\n",
    "module.exports = (req['default'] || req).apply(req, ",
    JSON.stringify(args),
    ")"
  ].join("")
};

Rewrite as a pitching loader, adding support for webpack v2 exports['default']


var path = require('path'),
    static = require('serve-static');

module.exports = {
    setup: function (app, assetpath, options) {

        if (arguments.length === 2 && typeof assetpath === 'object') {
            options = assetpath;
            assetpath = '';
        }

        options = options || {};
        assetpath = assetpath || options.path || '/govuk-assets';

        app.use(assetpath, static(path.join(__dirname, './node_modules/govuk_template_mustache/assets'), options));
        app.use(function (req, res, next) {
            res.locals.govukAssetPath = assetpath + '/';
            res.locals.partials = res.locals.partials || {};
            res.locals.partials['govuk-template'] = path.resolve(__dirname, './govuk_template');
            next();
        });

    }
};

var path = require('path'),
    servestatic = require('serve-static');

module.exports = {
    setup: function (app, options) {

        options = options || {};
        options.path = options.path || '/govuk-assets';

        app.use(options.path, servestatic(path.join(__dirname, './node_modules/govuk_template_mustache/assets'), options));
        app.use(function (req, res, next) {
            res.locals.govukAssetPath = options.path + '/';
            res.locals.partials = res.locals.partials || {};
            res.locals.partials['govuk-template'] = path.resolve(__dirname, './govuk_template');
            next();
        });

    }
};

Bring code implementation in line with documentation


'use strict';

var through = require('through2');
var Slack = require('node-slack-upload');
var chalk = require('chalk');
var errors = require('./errors');

function upload(token, config) {
  var slack = new Slack(token);

  slack.uploadFile(config, function(error, data) {
    if (data && data.ok === true) {
      chalk.green('Success (gulp-slack-upload): Uploaded ' + data.file.name);
    } else if (error) {
      chalk.red('Error (gulp-slack-upload): ' + errors.hasOwnProperty(error) ? errors[error] : error);
    } else {
      chalk.orange('Warning (gulp-slack-upload): No valid response');
    }
  });

  return through.obj(function(file, enc, callback) {
    return callback();
  })
}

module.exports = upload;

'use strict';

var through = require('through2');
var Slack = require('node-slack-upload');
var chalk = require('chalk');
var gutil = require('gutil');
var errors = require('./errors');

function upload(token, config) {
  var slack = new Slack(token);

  slack.uploadFile(config, function(error, data) {
    if (data && data.ok === true) {
      gutil.log(chalk.green('Success (gulp-slack-upload): Uploaded ' + data.file.name));
    } else if (error) {
      gutil.log(chalk.red('Error (gulp-slack-upload): ' + (errors.hasOwnProperty(error) ? errors[error] : error)));
    } else {
      gutil.log(chalk.orange('Warning (gulp-slack-upload): No valid response'));
    }
  });

  return through.obj(function(file, enc, callback) {
    return callback();
  })
}

module.exports = upload;

Fix issues with logging error messages


"use strict";

function EntityComponentSystem() {
	this.systems = [];
}
EntityComponentSystem.prototype.add = function(code) {
	this.systems.push(code);
};
EntityComponentSystem.prototype.addEach = function(code, requirements) {
	this.systems.push(function(entities) {
		var args = arguments;
		for (var i = 0; i < entities.length; i++) {
			var entity = entities[i];
			if (requirements && !entityHasComponents(requirements, entity)) {
				return;
			}
			args[0] = entity;
			code.apply(undefined, args);
		}
	});
};
EntityComponentSystem.prototype.run = function() {
	var args = arguments;
	this.systems.forEach(function(system) {
		system.apply(undefined, args);
	});
};

function entityHasComponents(components, entity) {
	for (var i = 0; i < components.length; i++) {
		if (!entity.hasOwnProperty(components[i])) {
			return false;
		}
	}
	return true;
}

module.exports = EntityComponentSystem;

"use strict";

function EntityComponentSystem() {
	this.systems = [];
}
EntityComponentSystem.prototype.add = function(code) {
	this.systems.push(code);
};
EntityComponentSystem.prototype.addEach = function(code, requirements) {
	this.systems.push(function(entities) {
		var args = arguments;
		for (var i = 0; i < entities.length; i++) {
			var entity = entities[i];
			if (requirements && !entityHasComponents(requirements, entity)) {
				return;
			}
			args[0] = entity;
			code.apply(undefined, args);
		}
	});
};
EntityComponentSystem.prototype.run = function() {
	var args = arguments;
	for (var i = 0; i < this.systems.length; i++) {
		this.systems[i].apply(undefined, args);
	}
};

function entityHasComponents(components, entity) {
	for (var i = 0; i < components.length; i++) {
		if (!entity.hasOwnProperty(components[i])) {
			return false;
		}
	}
	return true;
}

module.exports = EntityComponentSystem;

Convert forEach to for loop to avoid closure.


/* jshint node: true */
'use strict';

module.exports = {
  name: 'ember-basic-dropdown',
  contentFor: function(type, config) {
    if (config.environment !== 'test' && type === 'body-footer') {
      return '

';
    }
  }
};

/* jshint node: true */
'use strict';

module.exports = {
  name: 'ember-basic-dropdown',
  contentFor: function(type, config) {
    if (config.environment !== 'test' && type === 'body-footer' && !config._emberBasicDropdownContentForInvoked) {
      config._emberBasicDropdownContentForInvoked = true;
      return '

';
    }
  }
};

Make the contentFor hook idemponent


var pkg = require('./package.json');
var command = require('commander');

var container = require('./src/bootstrap')();

process.title = pkg.name;

command
    .version(pkg.version);

command
    .command('init')
    .description('Initialize a directory with configuration json.')
    .action(container.command.init.handle.bind(container.command.init));

command
    .command('publish')
    .description('Synchronize remote binary files to the publisher adapter.')
    .action(container.command.publish.handle.bind(container.command.publish));

command.parse(process.argv);

var pkg = require('./package.json');
var command = require('commander');
var subcommand = process.argv.slice(2, 3);

var container = require('./src/bootstrap')();

process.title = pkg.name;

command
    .version(pkg.version);

command
    .command('init')
    .description('Initialize a directory with configuration json.')
    .action(container.command.init.handle.bind(container.command.init));

command
    .command('publish')
    .description('Synchronize remote binary files to the publisher adapter.')
    .action(container.command.publish.handle.bind(container.command.publish));

command.parse(process.argv);

if (!subcommand.length || ['init', 'install', 'publish'].indexOf(subcommand[0]) < 0) {
    console.error();
    console.error('  Unknown command: ' + subcommand);
    command.help();
}

Add help on mistype of command or no command.


/* jshint node: true */
'use strict';

var VersionChecker = require('ember-cli-version-checker');
var hasBeenWarned = false;

module.exports = {
  name: 'ember-factory-for-polyfill',
  included: function() {
    this._ensureThisImport();

    var checker = new VersionChecker(this);
    var emberVersion = checker.forEmber();

    if (emberVersion.lt('2.12.0-beta.1')) {
      this.import('vendor/ember-factory-for-polyfill/index.js');
    } else if (this.parent === this.project && !hasBeenWarned){
      console.warn('ember-factory-for-polyfill is not required for Ember 2.12.0 and later, please remove from your `package.json`.');
      hasBeenWarned = true;
    }
  },

  _ensureThisImport: function() {
    if (!this.import) {
      this._findHost = function findHostShim() {
        var current = this;
        var app;
        do {
          app = current.app || app;
        } while (current.parent.parent && (current = current.parent));
        return app;
      };
      this.import = function importShim(asset, options) {
        var app = this._findHost();
        app.import(asset, options);
      };
    }
  }
};

/* jshint node: true */
'use strict';

var VersionChecker = require('ember-cli-version-checker');
var hasBeenWarned = false;

module.exports = {
  name: 'ember-factory-for-polyfill',
  included: function() {
    this._ensureThisImport();

    var checker = new VersionChecker(this);
    var emberVersion = checker.forEmber();

    if (emberVersion.lt('2.12.0-beta.1')) {
      this.import('vendor/ember-factory-for-polyfill/index.js');
    } else if (this.parent === this.project && !hasBeenWarned){
      this.ui.writeWarnLine('ember-factory-for-polyfill is not required for Ember 2.12.0 and later, please remove from your `package.json`.');
      hasBeenWarned = true;
    }
  },

  _ensureThisImport: function() {
    if (!this.import) {
      this._findHost = function findHostShim() {
        var current = this;
        var app;
        do {
          app = current.app || app;
        } while (current.parent.parent && (current = current.parent));
        return app;
      };
      this.import = function importShim(asset, options) {
        var app = this._findHost();
        app.import(asset, options);
      };
    }
  }
};

Use `ui.writeWarnLine` instead of `console.warn`.


'use strict'

var Promise = require('pinkie-promise')
var sliced = require('sliced')

function cb2promise () {
  var args = sliced(arguments)
  var fn = args.shift()
  var resolve
  var reject

  function callbackHandle () {
    var err
    args = sliced(arguments)
    err = args.shift()
    return (!err) ? resolve.apply(null, args) : reject(err)
  };

  function promiseFactory (resolvePromise, rejectPromise) {
    resolve = resolvePromise
    reject = rejectPromise
    return fn.apply(null, args)
  }

  args.push(callbackHandle)

  return new Promise(promiseFactory)
};

module.exports = cb2promise

'use strict'

const Promise = require('pinkie-promise')
const sliced = require('sliced')

function cb2promise () {
  let args = sliced(arguments)
  const fn = args.shift()
  let resolve
  let reject

  function callbackHandle () {
    let err
    args = sliced(arguments)
    err = args.shift()
    return (!err) ? resolve.apply(null, args) : reject(err)
  };

  function promiseFactory (resolvePromise, rejectPromise) {
    resolve = resolvePromise
    reject = rejectPromise
    return fn.apply(null, args)
  }

  args.push(callbackHandle)

  return new Promise(promiseFactory)
};

module.exports = cb2promise

Use const when is possible


'use strict';

const fs   = require('fs');
const dss  = require('dss');
const glob = require('glob');

class Doki {
  constructor(files) {
    this.files = files;
    this.parsedArray = [];
  }

  parse(destFile, options) {
    let files;
    options || {};

    if (Array.isArray(this.files)) {
      files = this.files;
    } else {
      files = glob.sync(this.files);
    }

    // Check if has any file
    if (files.length === 0) {
      console.error(`Has no file in ${this.files}!`);
      process.exit();
    }

    files.forEach((file) => {
      let fileContent = fs.readFileSync(file);
      dss.parse(fileContent, options, (parsedObject) => {
        this.parsedArray.push(parsedObject.blocks[0]);
        fs.writeFileSync(destFile, JSON.stringify(this.parsedArray));
      });
    });
  }

  parser(name, cb) {
    dss.parser(name, ( i, line, block ) => cb(i, line, block));
  }
}

module.exports = Doki;

'use strict';

const fs   = require('fs');
const dss  = require('dss');
const glob = require('glob');

class Doki {
  constructor(files) {
    this.files = files;
    this.parsedArray = [];
  }

  parse(destFile, options) {
    let files;
    options || {};

    if (Array.isArray(this.files)) {
      files = this.files;
    } else {
      files = glob.sync(this.files);
    }

    // Check if has any file
    if (files.length === 0) {
      console.error(`Has no file in ${this.files}!`);
      process.exit();
    }

    files.forEach((file) => {
      let fileContent = fs.readFileSync(file);
      dss.parse(fileContent, options, (parsedObject) => {
        this.parsedArray.push(parsedObject.blocks[0]);
      });
    });

    return this.parsedArray;
  }

  parser(name, cb) {
    dss.parser(name, ( i, line, block ) => cb(i, line, block));
  }
}

module.exports = Doki;

Return an array instead of a json file


'use strict';

const choo = require('choo');
const html = require('choo/html');
const app = choo();

app.model({
  state: {
    todos: [
      { title: 'Buy milk' },
      { title: 'Call Samantha'},
    ],
  },
});

const view = (state, prevState, send) => {
  return html`

ChooDo

        ${state.todos.map((todo) => html`<li>${todo.title}</li>`)}

`;
}

app.router([
  ['/', view],
]);

const tree = app.start();
document.body.appendChild(tree);

'use strict';

const choo = require('choo');
const html = require('choo/html');
const app = choo();

app.model({
  state: {
    todos: [
      { title: 'Buy milk' },
      { title: 'Call Samantha'},
    ],
  },
});

const hTodoLi = (todo) => html`<li>${todo.title}</li>`;

const view = (state, prevState, send) => {
  return html`

ChooDo

        ${state.todos.map(hTodoLi)}

`;
}

app.router([
  ['/', view],
]);

const tree = app.start();
document.body.appendChild(tree);

Move list item creation into its own component


'use strict';

angular.module('Teem')
  .factory('NewForm', [
  '$location', '$window', '$rootScope',
  function($location, $window, $rootScope) {
    var scope,
        objectName,
        scopeFn = {
          isNew () {
            return $location.search().form === 'new';
          },
          cancelNew () {
            scope[objectName].delete();

            $location.search('form', undefined);

            $window.history.back();
          },
          confirmNew () {
            $location.search('form', undefined);

            scope.invite.selected.forEach(function(i){
              scope.project.addContributor(i);
            });

            $rootScope.$broadcast('teem.project.join');
          }
        };

    function initialize(s, o) {
      scope = s;
      objectName = o;

      Object.assign(scope, scopeFn);
    }

    return {
      initialize
    };
  }]);

'use strict';

angular.module('Teem')
  .factory('NewForm', [
  '$location', '$window', '$rootScope',
  function($location, $window, $rootScope) {
    var scope,
        objectName,
        scopeFn = {
          isNew () {
            return $location.search().form === 'new';
          },
          cancelNew () {
            scope[objectName].delete();

            $location.search('form', undefined);

            $window.history.back();
          },
          confirmNew () {
            $location.search('form', undefined);

            // TODO fix with community invite
            if (objectName === 'project') {
              scope.invite.selected.forEach(function(i){
                scope.project.addContributor(i);
              });
            }

            $rootScope.$broadcast('teem.' + objectName + '.join');
          }
        };

    function initialize(s, o) {
      scope = s;
      objectName = o;

      Object.assign(scope, scopeFn);
    }

    return {
      initialize
    };
  }]);

Fix new form confirm code


var SEP = ':';

var crypto = require('crypto');

function genLongTermKey( username, realm, password ) {
	var md5 = crypto.createHash( 'md5' );

	var text = username + SEP + realm + SEP + password;
	md5.update( text );

	var hash = md5.digest( 'hex' );

	return hash;

	return;
}

module.exports = {
	genLongTermKey : genLongTermKey,
}

var SEP = ':';

var crypto = require('crypto');

function genLongTermKey( username, realm, password ) {
	var md5 = crypto.createHash( 'md5' );

	var text = username + SEP + realm + SEP + password;
	md5.update( text );

	var hash = md5.digest( 'hex' );

	return hash;
}

function genSharedKey( username, shared_key ) {
	var hmac = crypto.createHmac( 'sha1', shared_key );

	hmac.update( username );

	var hash = hmac.digest( 'base64' );

	return hash;
}

module.exports = {
	genLongTermKey : genLongTermKey,
	genSharedKey : genSharedKey,
}

Implement password generation for TURN REST API


module.exports = function (rgb, options) {
  var opts = options || {};

  var depth = opts.depth || 24;
  var size = rgb.length / (depth / 8);

  var coefficients = opts.coefficients || {};
  coefficients.r = coefficients.r || 0.299;
  coefficients.g = coefficients.g || 0.587;
  coefficients.b = coefficients.b || 0.144;

  var grayscale = new Buffer(size);
  var r, g, b;
  for (var i = 0; i < size; i++) {
    r = rgb[i * 3];
    g = rgb[i * 3 + 1];
    b = rgb[i * 3 + 2];
    grayscale[i] = Math.floor(
      coefficients.r * r +
      coefficients.g * g +
      coefficients.b * b);
  }
  return grayscale;
};

module.exports = function (rgb, options) {
  var opts = options || {};

  var depth = opts.depth || 24;
  var size = rgb.length / (depth / 8);

  // Default values for those coefficients are stolen from
  // http://stackoverflow.com/q/687261, althought they are mostly experimental
  // and fine-tunable.
  // http://www.johndcook.com/blog/2009/08/24/algorithms-convert-color-grayscale/
  // is an example of different set of values.
  var coefficients = opts.coefficients || {};
  coefficients.r = coefficients.r || 0.299;
  coefficients.g = coefficients.g || 0.587;
  coefficients.b = coefficients.b || 0.144;

  var grayscale = new Buffer(size);
  var r, g, b;
  for (var i = 0; i < size; i++) {
    r = rgb[i * 3];
    g = rgb[i * 3 + 1];
    b = rgb[i * 3 + 2];
    grayscale[i] = Math.floor(
      coefficients.r * r +
      coefficients.g * g +
      coefficients.b * b);
  }
  return grayscale;
};

Add a comment about origin of coefficients


if (console._isPatched) return;

['log', 'info', 'warn', 'error', 'dir', 'assert'].forEach(function(method) {
  var baseConsoleMethod = console[method];
  var slice = Array.prototype.slice;

  console[method] = function() {
    var date = (new Date()).toISOString();
    var args = slice.call(arguments);
    var output = method === 'warn' || method === 'error' ? 'stderr' : 'stdout';

    process[output].write('[' + date + '] ' + method.toUpperCase() + ' ');

    return baseConsoleMethod.apply(console, args);
  }
});

console._isPatched = true;

if (console._isPatched) return;

function addColor(string, name) {
  var colors = {
    green: ['\x1B[32m', '\x1B[39m'],
    red: ['\x1B[1m\x1B[31m', '\x1B[39m\x1B[22m'],
    yellow: ['\x1B[33m', '\x1B[39m']
  }

  return colors[name][0] + string + colors[name][1];
}

function getColorName(methodName) {
  switch (methodName) {
    case 'error': return 'red';
    case 'warn': return 'yellow';
    default: return 'green';
  }
}

['log', 'info', 'warn', 'error', 'dir', 'assert'].forEach(function(method) {
  var baseConsoleMethod = console[method];
  var slice = Array.prototype.slice;
  var color = getColorName(method);
  var output = method === 'warn' || method === 'error' ? 'stderr' : 'stdout';

  console[method] = function() {
    var date = (new Date()).toISOString();
    var args = slice.call(arguments);

    process[output].write('[' + date + '] ' + addColor(method.toUpperCase(), color) + ' ');

    return baseConsoleMethod.apply(console, args);
  }
});

console._isPatched = true;

Add color to message type labels


/**
 * Generates a new GeoJSON Polygon feature, given an array of coordinates
 * and list of properties.
 *
 * @module turf/polygon
 * @param {number[][]} rings - an array of LinearRings
 * @param {Object} properties - an optional properties object
 * @return {GeoJSONPolygon} output
 * @example
 * var poly1 = turf.polygon([[[20.0,0.0],[101.0,0.0],[101.0,1.0],[100.0,1.0],[100.0,0.0]]])
 * var poly2 = turf.polygon([[[20.0,0.0],[101.0,0.0],[101.0,1.0],[100.0,1.0],[100.0,0.0]]],
 *   {name: 'line 1', distance: 145})
 * console.log(poly1)
 * console.log(poly2)
 */
module.exports = function(coordinates, properties){
  if(coordinates === null) return new Error('No coordinates passed');
  var polygon = {
    "type": "Feature",
    "geometry": {
      "type": "Polygon",
      "coordinates": coordinates
    },
    "properties": properties
  };

  if(!polygon.properties){
    polygon.properties = {};
  }

  return polygon;
}

/**
 * Generates a new GeoJSON Polygon feature, given an array of coordinates
 * and list of properties.
 *
 * @module turf/polygon
 * @param {number[][]} rings - an array of LinearRings
 * @param {Object} properties - an optional properties object
 * @return {GeoJSONPolygon} output
 * @example
 * var poly1 = turf.polygon([[[20.0,0.0],[101.0,0.0],[101.0,1.0],[100.0,1.0],[100.0,0.0]]])
 * var poly2 = turf.polygon([[[20.0,0.0],[101.0,0.0],[101.0,1.0],[100.0,1.0],[100.0,0.0]]],
 *   {name: 'line 1', distance: 145})
 * console.log(poly1)
 * console.log(poly2)
 */
module.exports = function(coordinates, properties){
  if(coordinates === null) return new Error('No coordinates passed');
  if(coordinates[0] !== coordinates[coordinates.length - 1]) return new Error('First and last coordinate pair are not equivalent');
  var polygon = {
    "type": "Feature",
    "geometry": {
      "type": "Polygon",
      "coordinates": coordinates
    },
    "properties": properties
  };

  if(!polygon.properties){
    polygon.properties = {};
  }

  return polygon;
}

Add error for non-wrapped LinearRing


var deleteItem = require('./lib/deleteItem');

module.exports = function (connect) {
  return function (options) {
    var cookieSessionMiddleware = connect.cookieSession.apply(connect, arguments);
    var blacklist = options.blacklist;
    return function (req, res, next) {
      res.on('header', function () {
        if (blacklist) {
          blacklist.forEach(function(key) {
            deleteItem(req.session, key);
          });
        }
      });
      return cookieSessionMiddleware(req, res, next);
    };
  };
};

var deleteItem = require('./lib/deleteItem');

module.exports = function (connect) {
  return function (options) {
    var cookieSessionMiddleware = connect.cookieSession.apply(connect, arguments);
    var blacklist = options.blacklist;
    return function (req, res, next) {
      req._sessionBeforeBlacklist = JSON.parse(JSON.stringify(req.session));
      res.on('header', function () {
        if (blacklist) {
          blacklist.forEach(function(key) {
            deleteItem(req.session, key);
          });
        }
      });
      return cookieSessionMiddleware(req, res, next);
    };
  };
};

Add _sessionBeforeBlacklist for testing session in res.on('header'


#!/usr/bin/env node --harmony
"use strict";

let path = require('path')
  , fs = require('fs')
  , argv = require('yargs').argv._
  , traceur = require('traceur')
  , rc = require('rc')
  , _ = require('lodash')
  , defaultrc = JSON.parse(fs.readFileSync(__dirname + '/.traceurrc'))
  , tracuerConfig = _.omit(rc('traceur', defaultrc), ['config', '_'])

// Don't transpile dependencies
function traceurFilter(filename) {
	return filename.indexOf('node_modules') === -1
}

// Display correct line numbers in error stack traces
require('traceur-source-maps').install(traceur);

// Set traceur compiler with options as default require()
traceur.require.makeDefault(traceurFilter, tracuerConfig)

require(path.resolve(process.cwd(), argv[0]))

#!/usr/bin/env node --harmony
"use strict";

let path = require('path')
  , fs = require('fs')
  , child_process = require('child_process')
  , argv = require('yargs').argv
  , traceur = require('traceur')
  , rc = require('rc')
  , _ = require('lodash')
  , defaultrc = JSON.parse(fs.readFileSync(__dirname + '/.traceurrc'))
  , tracuerConfig = _.omit(rc('traceur', defaultrc), ['config', '_'])
  , noopArgs = ['v', 'version', 'i', 'interactive', 'h', 'help', 'v8-options']
  , replArgs = ['e', 'eval', 'p', 'print']
  , evalArg
  , processArgv

// Don't transpile dependencies
function traceurFilter(filename) {
  return filename.indexOf('node_modules') === -1
}

// Display correct line numbers in error stack traces
require('traceur-source-maps').install(traceur);

// Set traceur compiler with options as default require()
traceur.require.makeDefault(traceurFilter, tracuerConfig)

if (_.any(noopArgs, _.has.bind(_, argv))) {
  return child_process.spawn('node', process.argv.slice(2), { stdio: 'inherit' })
}

// TODO: Pass --v8-options, --debug & --debug-brk
processArgv = _.pull(process.argv.slice(2), '--harmony', '--debug', '--debug-brk')
evalArg = []
for(let k in replArgs) {
  let v = replArgs[k]
  if (argv[v]) evalArg = ['-e', argv[v]]
}

if (!evalArg.length && processArgv.length) {
  require(path.resolve(process.cwd(), processArgv[0]))
} else {
  child_process.spawn('traceur-cli', evalArg, { stdio: 'inherit' })
}

Add passthrough to traceur-cli and other cli options


// This is a manifest file that'll be compiled into application.js, which will include all the files
// listed below.
//
// Any JavaScript/Coffee file within this directory, lib/assets/javascripts, vendor/assets/javascripts,
// or vendor/assets/javascripts of plugins, if any, can be referenced here using a relative path.
//
// It's not advisable to add code directly here, but if you do, it'll appear at the bottom of the
// the compiled file.
//
// WARNING: THE FIRST BLANK LINE MARKS THE END OF WHAT'S TO BE PROCESSED, ANY BLANK LINE SHOULD
// GO AFTER THE REQUIRES BELOW.
//
//= require jquery
//= require jquery_ujs
//= require_tree .
//= require bootstrap

// Replace link targets with JavaScript handlers to prevent iOS fullscreen web
// app from opening links in extra browser app
if (navigator.userAgent.match(/(ipod|iphone|ipad)/i)) {
	$(function() {
		$('body a[href]').click(function(e) {
	  	e.preventDefault();
	    top.location.href = this;
	  });
	});
}

//hide notification bars after a few seconds
$('.alert').delay(10000).fadeOut('slow');

Hide notification bars after a few seconds.


//= require govuk_publishing_components/lib
//= require govuk_publishing_components/components/button
//= require govuk_publishing_components/components/checkboxes
//= require govuk_publishing_components/components/details
//= require govuk_publishing_components/components/feedback
//= require govuk_publishing_components/components/govspeak
//= require govuk_publishing_components/components/print-link
//= require govuk_publishing_components/components/radio
//= require govuk_publishing_components/components/step-by-step-nav
//= require components/hide-this-page-banner
//= require modules/track-responses
//= require modules/track-results

window.addEventListener('DOMContentLoaded', function () {
  var error = document.getElementById('current-error')
  if (error) { error.focus() }
})

Remove old error focus script


import printInReverseOrder from './printInReverseOrder/printInReverseOrder';

test('printInReverseOrder', () => {
  expect(printInReverseOrder(211)).toBe('1 1 2');
  expect(printInReverseOrder(4317)).toBe('7 1 3 4');
  expect(printInReverseOrder(51239991)).toBe('1 9 9 9 3 2 1 5');
});

import printInReverseOrder from '../src/printInReverseOrder/printInReverseOrder';

test('printInReverseOrder', () => {
  expect(printInReverseOrder(211)).toBe('1 1 2');
  expect(printInReverseOrder(4317)).toBe('7 1 3 4');
  expect(printInReverseOrder(51239991)).toBe('1 9 9 9 3 2 1 5');
});

Resolve the path to printInReverseOrder module


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

$(function(){ $(document).foundation(); });

var loader = document.getElementById('loader');
startLoading();
finishedLoading();
function startLoading() {
  loader.className = '';
}
function finishedLoading() {
  loader.className = 'done';
  setTimeout(function() {
    loader.className = 'hide';
  }, 1000);
}

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

$(function(){ $(document).foundation(); });

var loader = document.getElementById('loader');
startLoading();
finishedLoading();
function startLoading() {
  loader.className = '';
}
function finishedLoading() {
  loader.className = 'done';
  setTimeout(function() {
    loader.className = 'hide';
  }, 5000);
}

Set timeout for loader to 5000


import React, { Component } from 'react'
import { Table, Button, Popconfirm, Row, Col } from 'antd'
import Tree from './Tree'
import Page from './Page'

import styles from './Documentation.scss'
import docTree from 'documentation.js'

class Documentation extends Component {

    constructor(props) {
        super(props)
        this.state = {
            tree: docTree.getTree(),
            homePath: docTree.basePath + '/home.md'
        }
    }

    render() {
        return (

Docs

                <Row gutter={16}>
                    <Col span={16}>
                        <Page path={this.state.homePath} />
                    </Col>
                    <Col span={8} className={styles.treeWrapper}>
                        <Tree tree={this.state.tree} />
                    </Col>
                </Row>

        )
    }

}

export default Documentation

Clean up some code in the docs component


$(document).ready(function ()
{
    // initialize progress bar
    var progress = 0;
    $("#pbar").css("width", progress + "%").attr('aria-valuenow', progress);

    // initialize the connection to the server
    var progressNotifier = $.connection.initializerHub;

    // client-side sendMessage function that will be called from the server-side
    progressNotifier.client.sendMessage =
        function (message)
        {
            UpdateProgress(message);
        };

    // establish the connection to the server and start server-side operation
    $.connection.hub.start().done(
        function ()
        {
            progressNotifier.server.loadData();
        });

});

function UpdateProgress(message)
{
    var output = $("#resultmessage");
    n = parseInt(message.Progress);

    output.html("Status: <b>" + n + "%</b> " + message.Message);

    //var percetage = $("#percentage");
    //percetage.html(message.Progress + "%");

    $("#pbar").css("width", n + "%").attr('aria-valuenow', n);

    if (n == 100)
    {
        $("#pbar").removeClass("progress-bar-striped");
    }
}

$(document).ready(function ()
{
    // initialize progress bar
    var progress = 0;
    $("#pbar").css("width", progress + "%").attr('aria-valuenow', progress);

    // initialize the connection to the server
    var progressNotifier = $.connection.initializerHub;

    // client-side sendMessage function that will be called from the server-side
    progressNotifier.client.sendMessage =
        function (message)
        {
            UpdateProgress(message);
        };

    // establish the connection to the server and start server-side operation
    $.connection.hub.start().done(
        function ()
        {
            progressNotifier.server.loadData();
        });

});

function UpdateProgress(message)
{
    var output = $("#resultmessage");
    n = parseInt(message.Progress);

    output.html("Status: <b>" + n + "%</b> " + message.Message);

    //var percetage = $("#percentage");
    //percetage.html(message.Progress + "%");

    $("#pbar").css("width", n + "%").attr('aria-valuenow', n);

    if (n === 100)
    {
        $("#pbar").removeClass("progress-bar-striped");
    }
}

Use the correct js comparator


import React from "react";
import DayPicker from "react-day-picker";
import { dateUtils } from "react-day-picker/utils";

import "react-day-picker/lib/style.css";

export default class SelectableDay extends React.Component {

  state = {
    selectedDay: null
  }

  handleDayClick(e, day, modifiers) {
    if (modifiers.indexOf("selected") > -1) {
      this.setState({
        selectedDay: null
      })
    }
    else {
      this.setState({
        selectedDay: day
      });
    }
  }

  render() {
    const { selectedDay } = this.state;

    // Add the `selected` modifier to the cell of the clicked day
    const modifiers = {
      selected: day => dateUtils.isSameDay(selectedDay, day)
    };

    return (

        <DayPicker
          modifiers={ selectedDay && modifiers }
          onDayClick={ this.handleDayClick.bind(this) }
        />

          Selected: { selectedDay ? selectedDay.toLocaleDateString() : "(none)" }

    );
  }
}

import React from "react";
import DayPicker from "react-day-picker";
import { dateUtils } from "react-day-picker/utils";

import "react-day-picker/lib/style.css";

export default class SelectableDay extends React.Component {

  state = {
    selectedDay: null
  }

  handleDayClick(e, day, modifiers) {
    if (modifiers.indexOf("selected") > -1) {
      this.setState({
        selectedDay: null
      })
    }
    else {
      this.setState({
        selectedDay: day
      });
    }
  }

  render() {
    const { selectedDay } = this.state;

    // Add the `selected` modifier to the cell of the clicked day
    const modifiers = {
      selected: day => dateUtils.isSameDay(selectedDay, day)
    };

    return (

        <DayPicker
          modifiers={ modifiers }
          onDayClick={ this.handleDayClick.bind(this) }
        />

          Selected: { selectedDay ? selectedDay.toLocaleDateString() : "(none)" }

    );
  }
}

Update example for update to isSameDay()


'use strict';
/*global SVG */

SVG.DumpTruckHaul = SVG.invent({
  create: 'g',
  inherit: SVG.G,
  extend: {
    build: function() {
      this.t = 10000;

      this.body = this.doc().use('Haulage_Dump_Truck_2', 'images/master.svg');
      this.add(this.body);

      return this;
    }
  }
  , construct: {
    dumpTruckHaul: function() {
      return this.put(new SVG.DumpTruckHaul)
        .build();
    }
  }
});

SVG.extend(SVG.DumpTruckHaul, {
  forward: function() {
    return this
      .animate(this.t)
      .x(300)
      .y(-14)
    ;
  }

  , backward: function() {
    return this
      .animate(this.t)
      .x(0)
      .y(0)
    ;
  }

  , go: function() {
    var self = this;
    self.forward().after(function(){
      self.backward().after(function(){
        self.go();
      });
    });
  }

});

'use strict';
/*global SVG */

SVG.DumpTruckHaul = SVG.invent({
  create: 'g',
  inherit: SVG.G,
  extend: {
    build: function() {
      this.t = 10000;

      this.rect = this.doc().rect(250, 100);
      this.rect.move(280, 500);
      this.add(this.rect);

      this.body = this.doc().use('Haulage_Dump_Truck_2', 'images/master.svg');
      this.add(this.body);

      this.clipWith(this.rect);

      return this;
    }
  }
  , construct: {
    dumpTruckHaul: function() {
      return this.put(new SVG.DumpTruckHaul)
        .build();
    }
  }
});

SVG.extend(SVG.DumpTruckHaul, {
  forward: function() {
    return this.body
      .animate(this.t)
      .x(300)
      .y(-14)
    ;
  }

  , backward: function() {
    return this.body
      .animate(this.t)
      .x(0)
      .y(0)
    ;
  }

  , flip: function() {
    return this.body.scale(-1, 1);
  }

  , go: function() {
    var self = this;
    self.forward().after(function(){
      self.body.rotate(-5);
      self.flip();
      self.backward().after(function(){
        self.flip();
        self.body.rotate(0);
        self.go();
      });
    });
  }

});

Add clip to haul truck


import DS from 'ember-data';

export default DS.JSONAPIAdapter.extend({
  host: 'http://localhost:8000'
});

import DS from 'ember-data';

export default DS.JSONAPIAdapter.extend({
  host: 'http://localhost:8080'
});

Switch to talking to the backend on a different port.


'use strict';

/**
 * @ngdoc directive
 * @name waxeApp.directive:UserAccount
 * @description
 * # UserAccount
 */
angular.module('waxeApp')
    .directive('useraccount', ['Session', 'UserProfile', function (Session, UserProfile) {
        return {
            template: '
You are working on the account of {{Session.login}}
',
            restrict: 'E',
            replace: true,
            link: function postLink(scope) {
                scope.Session = Session;
                scope.UserProfile = UserProfile;
            }
        };
    }]);

'use strict';

/**
 * @ngdoc directive
 * @name waxeApp.directive:UserAccount
 * @description
 * # UserAccount
 */
angular.module('waxeApp')
    .directive('useraccount', ['$location', 'Session', 'UserProfile', 'File', function ($location, Session, UserProfile, File) {
        return {
            template: '
You are working on the account of {{Session.login}}. Open this file in your account
',
            restrict: 'E',
            replace: true,
            link: function postLink(scope) {
                scope.Session = Session;
                scope.UserProfile = UserProfile;
                scope.openUserFile = function() {
                    var file = File.loadFromPath(Session.filename);
                    file.user = UserProfile.login;
                    $location.url(file.editUrl);
                };
            }
        };
    }]);

Add link to open opened file in user account


'use strict';

describe('Directive: addMyriaDataset', function () {

  // load the directive's module
  beforeEach(module('polestar'));

  var element,
    scope;

  beforeEach(inject(function ($rootScope) {
    scope = $rootScope.$new();
  }));

  it('should show correct form', inject(function ($compile) {
    element = angular.element('<add-myria-dataset></add-myria-dataset>');
    element = $compile(element)(scope);

    scope.$digest();
    expect(element.find('button').length).to.eql(1);
  }));
});

'use strict';

describe('Directive: addMyriaDataset', function () {

  // load the directive's module
  beforeEach(module('vlui'));

  var element,
    scope;

  beforeEach(inject(function ($rootScope) {
    scope = $rootScope.$new();
  }));

  it('should show correct form', inject(function ($compile) {
    element = angular.element('<add-myria-dataset></add-myria-dataset>');
    element = $compile(element)(scope);

    scope.$digest();
    expect(element.find('button').length).to.eql(1);
  }));
});

Fix broken test because of moved files


import Docker from 'dockerode'
import bluebird from 'bluebird'

const dockerClient = bluebird.promisifyAll(new Docker({
  Promise: bluebird,
}))

export default dockerClient

import Docker from 'dockerode'
import bluebird from 'bluebird'

const dockerClient = new Docker({
  Promise: bluebird,
})

export default dockerClient

Stop promisifying the docker client


import Ember from 'ember';

/**
 * @module components
 * @class sl-drop-option
 */
export default Ember.Component.extend({

    /**
     * Component actions hash
     *
     * @property {object} actions
     */
    actions: {

        /**
         * Send the primary action when the click action is triggered
         *
         * @method actions.click
         */
        click: function() {
            this.sendAction( 'action' );
        }
    },

    /**
     * Class name bindings for the root element
     *
     * @property {array} classNameBindings
     */
    classNameBindings: [ 'optionType' ],

    /**
     * Class names for the root element
     *
     * @property {array} classNames
     */
    classNames: [ 'sl-drop-option' ],

    /**
     * Represents the type of option; "divider" if the label is undefined, or
     * "presentation" otherwise
     *
     * @property {string} optionType
     */
    optionType: function() {
        return this.get( 'label' ) ? 'presentation' : 'divider';
    }.property( 'label' ),

    /**
     * HTML tag name for the root element
     *
     * @property {string} tagName
     * @default "li"
     */
    tagName: 'li'
});

import Ember from 'ember';

/** @module sl-components/components/sl-drop-option */
export default Ember.Component.extend({

    /**
     * HTML tag name for the root element
     *
     * @property {string}       tagName
     * @type     {Ember.String}
     * @default  "li"
     */
    tagName: 'li',

    /**
     * Class names for the root element
     *
     * @property {array}       classNames
     * @type     {Ember.Array}
     */
    classNames: [ 'sl-drop-option' ],

    /**
     * Class name bindings for the root element
     *
     * @property {array}       classNameBindings
     * @type     {Ember.Array}
     */
    classNameBindings: [ 'optionType' ],

    /**
     * Component actions hash
     *
     * @property {object}       actions
     * @type     {Ember.Object}
     */
    actions: {

        /**
         * Send the primary action when the click action is triggered
         *
         * @function actions.click
         * @return   {void}
         */
        click: function() {
            this.sendAction( 'action' );
        }
    },

    /**
     * Represents the type of option; "divider" if the label is undefined, or
     * "presentation" otherwise
     *
     * @function optionType
     * @type     {Ember.String}
     * @observes 'label'
     * @return   {string}
     */
    optionType: function() {
        return this.get( 'label' ) ? 'presentation' : 'divider';
    }.property( 'label' )
});

Refactor to follow style guides


var vows = require('vows'),
    assert = require('assert'),
    events = require('events'),
    load = require('./load'),
    suite = vows.describe('brushMode');

function d3Parcoords() {
  var promise = new(events.EventEmitter);
  load(function(d3) {
    var pc = d3.parcoords();

    d3.select('body').append('div').attr('id', 'test');
    pc('div#test');
    promise.emit('success', pc);
  });
  return promise;
}

suite.addBatch({
  'd3.parcoords': {
    'has by default': {
       topic: d3Parcoords(),
       'the brush mode "None"': function(pc) {
         assert.strictEqual(pc.brushMode(), "None");
         // Make sure that no brush related methods are exposed when the brush
         // mode is set to "None"
         assert.strictEqual(pc.brushExtents, undefined);
         assert.strictEqual(pc.brushReset, undefined);
       }
    },
  }
});

suite.export(module);

var vows = require('vows'),
    assert = require('assert'),
    events = require('events'),
    load = require('./load'),
    suite = vows.describe('brushMode');

function d3Parcoords() {
  var promise = new(events.EventEmitter);
  load(function(d3) {
    var pc = d3.parcoords();

    d3.select('body').append('div').attr('id', 'test');
    pc('div#test');
    promise.emit('success', pc);
  });
  return promise;
}

suite.addBatch({
  'd3.parcoords': {
    'has by default': {
       topic: d3Parcoords(),
       'the brush mode "None"': function(pc) {
         assert.strictEqual(pc.brushMode(), "None");
         // Make sure that no brush related methods are exposed when the brush
         // mode is set to "None"
         assert.strictEqual(pc.brushExtents, undefined);
         assert.strictEqual(pc.brushReset, undefined);
       }
    },
    'can be configured to': {
      topic: d3Parcoords(),
      'have brush mode 1D-axes': function(pc) {
        // brushExtents is a function that should be install by 1D-axes, as this
        // only makes sense when 1D brushing is active.
        assert.strictEqual(pc.brushExtents, undefined);
        pc.brushMode("1D-axes");
        assert.strictEqual(pc.brushMode(), "1D-axes");
        // Okay, let's see if brushExtents is there now, and if it is a function.
        assert.notStrictEqual(pc.brushExtents, undefined);
        assert.strictEqual(typeof(pc.brushExtents), "Function");
      }
  }
});

suite.export(module);

Add some first test for switching to the 1D-axes brushMode.


/* global vfs */
import {
  TextureConfigurator, ArticlePackage,
  ArticleEditorSession,
  ArticleAPI, createEditorContext,
  VfsStorageClient, TextureArchive, InMemoryDarBuffer
} from '../index'

export default function setupTestArticleSession (docInitializer) {
  let configurator = new TextureConfigurator()
  configurator.import(ArticlePackage)
  // TODO: this could be a little easier
  let config = configurator.getConfiguration('article').getConfiguration('manuscript')

  // load the empty archive
  let storage = new VfsStorageClient(vfs, './data/')
  let archive = new TextureArchive(storage, new InMemoryDarBuffer())
  // ATTENTION: in case of the VFS loading is synchronous
  // TODO: make sure that this is always the case
  archive.load('blank', () => {})
  let session = archive.getEditorSession('manuscript')
  let doc = session.getDocument()
  if (docInitializer) {
    docInitializer(doc)
  }
  // NOTE: this indirection is necessary because we need to pass the context to parts of the context
  let contextProvider = {}
  let editorSession = new ArticleEditorSession(doc, config, contextProvider)
  let api = new ArticleAPI(editorSession, config, archive)
  let context = Object.assign(createEditorContext(config, editorSession), { api })
  // ... after the context is ready we can store it into the provider
  contextProvider.context = context

  return { context, editorSession, doc, archive }
}

/* global vfs */
import {
  TextureConfigurator, ArticlePackage,
  ArticleEditorSession,
  ArticleAPI, createEditorContext,
  VfsStorageClient, TextureArchive, InMemoryDarBuffer
} from '../index'

export default function setupTestArticleSession (docInitializer) {
  let configurator = new TextureConfigurator()
  configurator.import(ArticlePackage)
  // TODO: this could be a little easier
  let config = configurator.getConfiguration('article').getConfiguration('manuscript')

  // load the empty archive
  let storage = new VfsStorageClient(vfs, './data/')
  let archive = new TextureArchive(storage, new InMemoryDarBuffer())
  // ATTENTION: in case of the VFS loading is synchronous
  // TODO: make sure that this is always the case
  archive.load('blank', () => {})
  let session = archive.getEditorSession('manuscript')
  let doc = session.getDocument()
  if (docInitializer) {
    // clear the body
    let body = doc.get('body')
    body.removeAt(0)
  }
  if (docInitializer) {
    docInitializer(doc)
  }
  // NOTE: this indirection is necessary because we need to pass the context to parts of the context
  let contextProvider = {}
  let editorSession = new ArticleEditorSession(doc, config, contextProvider)
  let api = new ArticleAPI(editorSession, config, archive)
  let context = Object.assign(createEditorContext(config, editorSession), { api })
  // ... after the context is ready we can store it into the provider
  contextProvider.context = context

  return { context, editorSession, doc, archive }
}

Clear the body for test fixtures.


"use strict";

/**
 * Opens the given page and resolves when phantomjs called back.
 * Will be executed inside of phantomjs.
 *
 * @private
 * @param {string} url
 * @param {Function} resolve
 * @param {Function} reject
 */
function openPage(url, resolve, reject) { /* jshint validthis: true */
    this.open(url, function onPageLoaded(status) {
        if (status !== "success") {
            return reject(new Error("Cannot load " + url + ": Phantomjs returned status " + status));
        }
        resolve();
    });
}

/**
 * Calls phantom.exit() with errorcode 0
 *
 * @private
 */
function exitPhantom() { /* global phantom */
    phantom.exit(0);
}

/**
 * Cleans all references to a specific page.
 *
 * @private
 * @param {number} pageId
 */
function disposePage(pageId) { /* global pages */
    pages[pageId].close();
    delete pages[pageId];
}

exports.openPage = openPage;
exports.exitPhantom = exitPhantom;
exports.disposePage = disposePage;

"use strict";

/**
 * Opens the given page and resolves when phantomjs called back.
 * Will be executed inside of phantomjs.
 *
 * @private
 * @param {string} url
 * @param {Function} resolve
 * @param {Function} reject
 */
function openPage(url, resolve, reject) { /* jshint validthis: true */
    this.open(url, function onPageLoaded(status) {
        if (status !== "success") {
            return reject(new Error("Cannot load " + url + ": Phantomjs returned status " + status));
        }
        resolve();
    });
}

/**
 * Calls phantom.exit() with errorcode 0
 *
 * @private
 */
function exitPhantom() { /* global phantom */
    Object.keys(pages).forEach(function (pageId) {
        // Closing all pages just to cleanup properly
        pages[pageId].close();
    });
    pages = null;

    // Using setTimeout(0) to ensure that all JS code still waiting in the queue is executed before exiting
    // Otherwise PhantomJS prints a confusing security warning
    // @see https://github.com/ariya/phantomjs/commit/1eec21ed5c887bf21a1a6833da3c98c68401d90e
    setTimeout(function () {
        phantom.exit(0);
    }, 0);
}

/**
 * Cleans all references to a specific page.
 *
 * @private
 * @param {number} pageId
 */
function disposePage(pageId) { /* global pages */
    pages[pageId].close();
    delete pages[pageId];
}

exports.openPage = openPage;
exports.exitPhantom = exitPhantom;
exports.disposePage = disposePage;

Add fix for confusing security warnings when phantomjs exits


app.controller('HomeController', ['$http', function($http) {
  console.log('HomeController running');

}]);

app.controller('HomeController', ['$http', function($http) {
  console.log('HomeController running');

  self.dataArray = [
      {
        src: 'https://www.travelexcellence.com/images/movil/La_Paz_Waterfall.jpg'
      },
      {
        src: 'http://www.parasholidays.in/blog/wp-content/uploads/2014/05/holiday-tour-packages-for-usa.jpg'
      },
      {
        src: 'http://clickker.in/wp-content/uploads/2016/03/new-zealand-fy-8-1-Copy.jpg'
      },
      {
        src: 'http://images.kuoni.co.uk/73/indonesia-34834203-1451484722-ImageGalleryLightbox.jpg'
      },
      {
        src: 'http://www.holidaysaga.com/wp-content/uploads/2014/09/Day08-SIN-day-Free-City-View.jpg'
      },
      {
        src: 'http://images.kuoni.co.uk/73/malaysia-21747826-1446726337-ImageGalleryLightbox.jpg'
      },
      {
        src: 'http://www.kimcambodiadriver.com/uploads/images/tours/kim-cambodia-driver-angkor-wat.jpg'
      },
      {
        src: 'https://www.travcoa.com/sites/default/files/styles/flexslider_full/public/tours/images/imperialvietnam-halong-bay-14214576.jpg?itok=O-q1yr5_'
      }
    ];

}]);

Add array of test pictures


'use strict';

/* global Connector */

Connector.playerSelector = 'body';

Connector.getArtistTrack = function () {
	var text = $('#portablecontrols h3').text().replace(' [ buffering... ]', '');
	var separator = this.findSeparator(text);

	var artist = null;
	var track = null;

	if (separator !== null) {
		artist = text.substr(0, separator.index);
		track = text.substr(separator.index + separator.length);
	}

	return {artist: artist, track: track};
};

Connector.isPlaying = function () {
	return $('.playing').length > 0;
};

'use strict';

/* global Connector */

var INFO_CURRENT_TIME = 1;
var INFO_DURATION = 2;

Connector.playerSelector = 'body';

Connector.getArtistTrack = function () {
	var text = $('#portablecontrols h3').text().replace(' [ buffering... ]', '');
	var separator = this.findSeparator(text);

	var artist = null;
	var track = null;

	if (separator !== null) {
		artist = text.substr(0, separator.index);
		track = text.substr(separator.index + separator.length);
	}

	return {artist: artist, track: track};
};

Connector.getCurrentTime = function() {
	return getTimeInfo(INFO_CURRENT_TIME);
};

Connector.getDuration = function() {
	return getTimeInfo(INFO_DURATION);
};

Connector.isPlaying = function () {
	return $('.playing').length > 0;
};

Connector.isStateChangeAllowed = function() {
	return Connector.getCurrentTime() > 0;
};

function getTimeInfo(field) {
	var pattern = /(.+)\s-\s(.+)/gi;
	var songInfo = pattern.exec($('.elapsed').text());
	if (songInfo) {
		return  Connector.stringToSeconds(songInfo[field]);
	}
	return 0;
}

Add current time/duration processing for Reddit Playlister


(function() {

  var onkey = function(event) {
    if (event.key === 'z' || event.keyCode == 122) {
      vrControls.zeroSensor();
    }
    if (event.key === 'f' || event.keyCode == 102) {
      return vrEffect.setFullScreen(true);
    }
  };

  window.addEventListener("keypress", onkey, true);

}).call(this);

(function() {

  var onkey = function(event) {
    if (event.key === 'z' || event.keyCode == 122) {
      vrControls.zeroSensor();
    }
    if (event.key === 'f' || event.keyCode == 102) {
      return vrEffect.setFullScreen(true);
    }
  };

  window.addEventListener("keypress", onkey, true);
  window.addEventListener("dblclick", function(){
    // TODO: add a toggleFullScreen method to VREffect
    var isFullscreen = document.mozFullScreenElement || document.webkitFullscreenElement;
    vrEffect.setFullScreen(!isFullscreen);
  });

}).call(this);

Add double click to toggle fullscreen


import React from "react";
import { ClipLoader } from "halogenium";
import { Icon } from "../../base";
import { getTaskDisplayName } from "../../utils";

const JobStep = ({ step, isDone }) => {

    let hasBar;
    let stateIcon;
    let entryStyle;

    switch (step.state) {

        case "running":
            hasBar = isDone;
            stateIcon = isDone ? "check fa-fw" : "";
            entryStyle = isDone ? "success" : "primary";
            break;

        case "complete":
            hasBar = false;
            stateIcon = "check fa-fw";
            entryStyle = "success";
            break;

        case "error":
        case "cancelled":
            hasBar = false;
            stateIcon = "times fa-fw";
            entryStyle = "danger";
            break;

        default:
            return null;

    }

    const stepEntry = (

                {stateIcon.length
                    ? <Icon name={stateIcon} bsStyle={entryStyle} />
                    :
<ClipLoader size="14px" color="#07689d" style={{padding: "3px 2px"}} />

                }

                {getTaskDisplayName(step.stage)}

            <div
                className={hasBar ? `step-entry-bar-${entryStyle}` : "step-entry-nobar"}
            />

    );

    return stepEntry;
};

export default JobStep;

import React from "react";
import { ClipLoader } from "halogenium";
import { Icon } from "../../base";
import { getTaskDisplayName } from "../../utils";

const JobStep = ({ step, isDone }) => {

    let hasBar;
    let stateIcon;
    let entryStyle;

    switch (step.state) {

        case "running":
            hasBar = isDone;
            stateIcon = isDone ? "check fa-fw" : "";
            entryStyle = isDone ? "success" : "primary";
            break;

        case "complete":
            hasBar = false;
            stateIcon = "check fa-fw";
            entryStyle = "success";
            break;

        case "error":
        case "cancelled":
            hasBar = false;
            stateIcon = "times fa-fw";
            entryStyle = "danger";
            break;

        default:
            return null;

    }

    const stepEntry = (

                {stateIcon.length
                    ? <Icon name={stateIcon} bsStyle={entryStyle} />
                    : <ClipLoader size="14px" color="#07689d" style={{padding: "0 1.5px"}} />
                }

                {getTaskDisplayName(step.stage)}

            <div
                className={hasBar ? `step-entry-bar-${entryStyle}` : "step-entry-nobar"}
            />

    );

    return stepEntry;
};

export default JobStep;

Fix padding on cliploader icon


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

foam.CLASS({
  package: 'foam.nanos.auth',
  name: 'EnabledAware',

  properties: [
    {
      class: 'Boolean',
      name: 'enabled',
      value: true
    }
  ]
});

// TODO: create an EnabledAwareDAO

// ???: Split into AuthSPI / AuthService ?
/*
foam.INTERFACE({

  interface AuthService {

  String generateChallenge(String username);

  void login(X x, String response)
    throws LoginException;

  void login(String username, String password);

  // Is this needed?
  void logout(String username);

  // Use standard Java types or FOAM-specific?
  public boolean check(X x, java.security.Principal principal, java.security.Permission permission);

  public void updatePassword(X x, Principal principal, String oldPassword, String newPassword)
    throws IllegalStateException;

  public void validatePrincipal(X x, Principal oldValue, Principal newValue)
    throws IllegalStateException;
}
*/

// TODO: create UserAndGroupAuthService
// TODO: create CachingAuthService

Move template comments to the right file