const electron = require('electron')
const app = electron.app
const BrowserWindow = electron.BrowserWindow
const path = require('path')
const url = require('url')

// Keep main window in scope so it isn't garbage collected
let mainWindow

function createWindow () {
  mainWindow = new BrowserWindow({ width: 1000, height: 750 })
  mainWindow.loadURL(url.format({
    pathname: path.join(__dirname, 'index.html'),
    protocol: 'file:',
    slashes: true
  }))

  mainWindow.webContents.openDevTools()

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

app.on('ready', createWindow)

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

app.on('activate', function () {
  if (mainWindow === null) {
    createWindow()
  }
})

const electron = require('electron')
const app = electron.app
const BrowserWindow = electron.BrowserWindow
const path = require('path')
const url = require('url')

// Keep main window in scope so it isn't garbage collected
let mainWindow

function createWindow () {
  mainWindow = new BrowserWindow({ width: 1000, height: 750 })
  mainWindow.loadURL(url.format({
    pathname: path.join(__dirname, 'index.html'),
    protocol: 'file:',
    slashes: true
  }))

  // mainWindow.webContents.openDevTools()

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

app.on('ready', createWindow)

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

app.on('activate', function () {
  if (mainWindow === null) {
    createWindow()
  }
})

Disable dev tools for now.


import events from 'events';

class Feeder extends events.EventEmitter {
    queue = [];
    pending = false;

    feed(data) {
        this.queue = this.queue.concat(data);
    }
    clear() {
        this.queue = [];
        this.pending = false;
    }
    next() {
        if (this.queue.length === 0) {
            this.pending = false;
            return false;
        }

        const data = this.queue.shift();
        this.pending = true;
        this.emit('data', data);

        return data;
    }
    isPending() {
        return this.pending;
    }
}

export default Feeder;

import events from 'events';

class Feeder extends events.EventEmitter {
    queue = [];
    pending = false;

    feed(data) {
        this.queue = this.queue.concat(data);
    }
    clear() {
        this.queue = [];
        this.pending = false;
    }
    size() {
        return this.queue.length;
    }
    next() {
        if (this.queue.length === 0) {
            this.pending = false;
            return false;
        }

        const data = this.queue.shift();
        this.pending = true;
        this.emit('data', data);

        return data;
    }
    isPending() {
        return this.pending;
    }
}

export default Feeder;

Return the number of items in the queue


const gulp = require('gulp');
const tasks = require('./utils/getTasks');

const isDev = process.env.NODE_ENV !== 'production';

const buildTasks = [
  tasks.static,
  tasks.images,
  tasks.icons,
  tasks.styles,
];

if (!isDev) {
  buildTasks.push(tasks.scripts);
}

const seriesTasks = [
  tasks.clean,
  gulp.parallel(...buildTasks),
];

if (isDev) {
  seriesTasks.push(tasks.serve);
}

gulp.task('default', gulp.series(...seriesTasks));

const gulp = require('gulp');
const tasks = require('./utils/getTasks');
const config = require('./config');

const isDev = process.env.NODE_ENV !== 'production';

const buildTasks = [
  tasks.static,
  tasks.images,
  tasks.icons,
  tasks.styles,
];

if (!isDev || config.scripts.publicPath === '') {
  buildTasks.push(tasks.scripts);
}

const seriesTasks = [
  tasks.clean,
  gulp.parallel(...buildTasks),
];

if (isDev) {
  seriesTasks.push(tasks.serve);
}

gulp.task('default', gulp.series(...seriesTasks));

Enable scripts task if publicPath is empty


import React from 'react';

import refractToComponentsMap from 'refractToComponentMap';
import theme from 'theme'

class Attribute extends React.Component {
  static propTypes = {
    element: React.PropTypes.object,
    expandableCollapsible: React.PropTypes.bool,
    parentElement: React.PropTypes.object,
  }

  static childContextTypes = {
    theme: React.PropTypes.object,
  }

  constructor(props) {
    super(props);
  }

  getChildContext() {
    return {
      theme
    };
  }

  render() {
    if (!this.props.element) {
      return false;
    }

    const reactComponent = refractToComponentsMap[this.props.element.element];

    if (typeof reactComponent === 'undefined') {
      return new Error(`Unable to find a rendering component for ${this.props.element.element}`);
    }

    return React.createElement(reactComponent, {
      element: this.props.element,
      expandableCollapsible: this.props.expandableCollapsible,
      parentElement: this.props.parentElement,
    });
  }
}

export default Attribute;

import React from 'react';

import refractToComponentsMap from 'refractToComponentMap';
import theme from 'theme';

class Attribute extends React.Component {
  static propTypes = {
    element: React.PropTypes.object,
    expandableCollapsible: React.PropTypes.bool,
    parentElement: React.PropTypes.object,
  }

  static childContextTypes = {
    theme: React.PropTypes.object,
  }

  constructor(props) {
    super(props);
  }

  getChildContext() {
    return {
      theme,
    };
  }

  render() {
    if (!this.props.element) {
      return false;
    }

    const reactComponent = refractToComponentsMap[this.props.element.element];

    if (typeof reactComponent === 'undefined') {
      return new Error(`Unable to find a rendering component for ${this.props.element.element}`);
    }

    return React.createElement(reactComponent, {
      element: this.props.element,
      expandableCollapsible: this.props.expandableCollapsible,
      parentElement: this.props.parentElement,
    });
  }
}

export default Attribute;

Fix missing semicolon and missing trailing comma.


var Game = function(boardString){
  this.board = '';
  if (arguments.length === 1) {
    this.board = boardString;
  } else {
    function random() {
      return Math.floor(Math.random() * 10 + 6);
    };
    var firstTwo = random();
    var secondTwo = random();
    for ( var i = 0; i < 16; i++ ) {
      if (i === firstTwo || i === secondTwo) {
        this.board += '2';
      } else {
        this.board += '0';
      };
    };
  }
};

Game.prototype.toString = function() {
  this.board.
};

Create board in Game constructor


function(newDoc, oldDoc, userCtx) {

  if (oldDoc){
    if (userCtx.roles.indexOf("_admin") == -1) {
      throw({forbidden: "Only database admins are allowed to update pastes/comments"});
    }
  }
}

function(newDoc, oldDoc, userCtx) {
  if (oldDoc){
    if (userCtx.roles.indexOf("_admin") == -1) {
      throw({forbidden: "Only database admins are allowed to update pastes/comments"});
    }
  } else {
    // verify well-formedness

    var malformed = false;
    if (typeof(newDoc.data) != "string") {
      malformed = true;
    } else if (["paste", "comment"].indexOf(newDoc.type) == -1) {
      malformed = true;
    }

    if (malformed == true) {
      throw({forbidden: "The document is malformed. Not accepting this."});
    }
  }
}

Add basic well-formedness verification before storing a doc


import React from 'react';
import { render } from 'react-dom';
import { Router, Route, IndexRoute, hashHistory } from 'react-router'

window.$ = require('jquery');

import Case from './components/Case.js'
import CaseReader from './components/CaseReader.js'
import {CaseOverview} from './components/CaseOverview.js'
import EdgenoteGallery from './components/EdgenoteGallery.js'
import Modal from './components/Modal.js'

window.i18n = {}
window.i18n.locale = 'ja'

render((
  <Router history={hashHistory}>
    <Route path="/" component={Case}>
      <IndexRoute component={CaseOverview} />
      <Route onEnter={() => window.scrollTo(0, 0)} path="edgenotes" component={EdgenoteGallery}>
        <Route path=":edgenoteID" component={Modal} />
      </Route>
      <Route onEnter={() => window.scrollTo(0, 0)} path=":chapter" component={CaseReader}>
        <Route path="edgenotes/:edgenoteID" component={Modal} />
      </Route>
    </Route>
  </Router>
), document.getElementById('container'))

import React from 'react';
import { render } from 'react-dom';
import { Router, Route, IndexRoute, useRouterHistory } from 'react-router'
import { createHashHistory } from 'history'

window.$ = require('jquery');

import Case from './components/Case.js'
import CaseReader from './components/CaseReader.js'
import {CaseOverview} from './components/CaseOverview.js'
import EdgenoteGallery from './components/EdgenoteGallery.js'
import Modal from './components/Modal.js'

window.i18n = {}
window.i18n.locale = 'ja'
const appHistory = useRouterHistory(createHashHistory)({ queryKey: false  })

render((
  <Router history={appHistory}>
    <Route path="/" component={Case}>
      <IndexRoute component={CaseOverview} />
      <Route onEnter={() => window.scrollTo(0, 0)} path="edgenotes" component={EdgenoteGallery}>
        <Route path=":edgenoteID" component={Modal} />
      </Route>
      <Route onEnter={() => window.scrollTo(0, 0)} path=":chapter" component={CaseReader}>
        <Route path="edgenotes/:edgenoteID" component={Modal} />
      </Route>
    </Route>
  </Router>
), document.getElementById('container'))

Remove the query key from hashHistory


async function makeDirectRequest(reqInfo) {
    const {
        owner,
        repo,
        path,
        ref
    } = reqInfo;

    const resp = await fetch(`https://raw.githubusercontent.com/${owner}/${repo}/${ref}/${path}`);
    return await resp.text();
}

export default async function getGithubFile(reqInfo) {
    const {
        owner,
        repo,
        path,
        ref
    } = reqInfo;

    let resp;

    try {
        resp = await fetch(`https://api.github.com/repos/${owner}/${repo}/contents/${path}?ref=${ref}`);
    } catch (e) {
        // fallback in case if API limmit is exceeded
        return makeDirectRequest(reqInfo);
    }

    if (resp.status !== 200) {
        return makeDirectRequest(reqInfo);
    }

    const data = await resp.json();

    if (!data.content) {
        throw Error('Guthub API returned wrong data');
    }

    return atob(data.content.replace(/\s/g, ''));
}

/* function getFile(data, path, callback) {
    var xhr = new XMLHttpRequest();
    xhr.open('GET', 'https://api.github.com/repos/'+data.owner+'/'+data.repo+'/contents/'+path+'?ref=' + data.ref, true);

    xhr.onload = function() {
        var resp = JSON.parse(xhr.responseText);
        if(resp.content) {
            callback(atob(resp.content.replace(/\s/g, '')));
        } else {
            xhr.onerror();
        }
    }

    // fallback in case if API limmit is exceeded
    xhr.onerror = function() {
        var xhr2 = new XMLHttpRequest();
        xhr2.open('GET', 'https://raw.githubusercontent.com/'+data.owner+'/'+data.repo+'/'+data.ref+'/'+path, true);
        xhr2.onload = function() {
            if(xhr2.status !== 200) {
                callback({ error: xhr2.statusText })
            } else {
                callback(xhr2.responseText);
            }
        }

        xhr2.onerror = function() {
            callback({ error: xhr2.statusText });
        }

        xhr2.send();
    }

    xhr.send();
}*/

Remove commented code from getGithubFile


const assert = require('assert')
const Fetcher = require('../../src/models/Fetcher')
const fetchMock = require('fetch-mock')

describe('Fetcher', () => {
  describe('get', () => {
    before(() => {
      fetchMock.get('http://example.com', { foo: 'bar' })
    })

    it('returns a promise that resolves with the JSON response', done => {
      const fetcher = new Fetcher()
      fetcher.get('http://example.com').then(json => {
        assert.deepEqual({ foo: 'bar' }, json)
        fetchMock.restore()
        done()
      })
    })
  })

  describe('getStatus', () => {
    it('includes status and statusText properties', () => {
      const fakeResponse = {
        status: '404',
        statusText: 'Not Found',
      }
      const expected = '404 Not Found'
      const fetcher = new Fetcher()
      assert.equal(expected, fetcher.getStatus(fakeResponse))
    })
  })
})

const assert = require('assert')
const Fetcher = require('../../src/models/Fetcher')
const fetchMock = require('fetch-mock')

describe('Fetcher', () => {
  describe('get', () => {
    before(() => {
      fetchMock.get('http://example.com', { foo: 'bar' })
    })

    it('returns a promise that resolves with the JSON response', done => {
      const fetcher = new Fetcher()
      fetcher.get('http://example.com').then(({ json }) => {
        assert.deepEqual({ foo: 'bar' }, json)
        fetchMock.restore()
        done()
      })
    })
  })

  describe('getStatus', () => {
    it('includes status and statusText properties', () => {
      const fakeResponse = {
        status: '404',
        statusText: 'Not Found',
      }
      const expected = '404 Not Found'
      const fetcher = new Fetcher()
      assert.equal(expected, fetcher.getStatus(fakeResponse))
    })
  })
})

Update failing Fetcher get() test


import View from './View'

export default class RoomView extends View {
	constructor() {
		super();

		this.title = 'Room';
		this.templateUrl = 'templates/room';
	}

  afterInit() {
    const roomId = 'test';

    [1, 2].forEach(playerId => {
      const joinArea = this.find(`.join-area:nth-child(${playerId})`);
      const qrCodeHolder = joinArea.querySelector('.code-holder');
      const clearCode = joinArea.querySelector('.clear-code');

      new QRCode(qrCodeHolder, `${roomId}-${playerId}`);
      clearCode.value = `${roomId}-${playerId}`;
    });
  }

  joinLeft() {
    CastleCrush.ViewManager.load('game');
  }

  joinRight() {
    CastleCrush.ViewManager.load('game');
  }
}

import View from './View'

export default class RoomView extends View {
	constructor() {
		super();

		this.title = 'Room';
		this.templateUrl = 'templates/room';
	}

  afterInit() {
    const roomId = 'test';
    const joinUrl = location.href.split('#')[0] + '#/join/';

    [1, 2].forEach(playerId => {
      const joinArea = this.find(`.join-area:nth-child(${playerId})`);
      const qrCodeHolder = joinArea.querySelector('.code-holder');
      const clearCode = joinArea.querySelector('.clear-code');

      new QRCode(qrCodeHolder, `${joinUrl}${roomId}-${playerId}`);
      clearCode.value = `${roomId}-${playerId}`;
    });
  }

  joinLeft() {
    CastleCrush.ViewManager.load('game');
  }

  joinRight() {
    CastleCrush.ViewManager.load('game');
  }
}

Add a correct url to the qr-code to allow scanning with other apps


describe("Gilded Rose", function() {
  var foo;

  // beforeEach(function() {
  //   foo = 0;
  //   foo += 1;
  // });

  // afterEach(function() {
  //   foo = 0;
  // });

  it("has an Item constructor", function() {
    expect(typeof Item).toBe('function');
  });

  it("should set a global array", function() {
    expect(items instanceof Array).toBe(true);
  });

  // it("can have a negative case", function() {
  //   expect(false).not.toBe(true);
  // });

  // describe("Another spec", function() {
  //   it("is just a function, so it can contain any code", function() {
  //     var foo = 0;
  //     foo += 1;

  //     expect(foo).toEqual(1);
  //   });

  //   it("The 'toBeLessThan' matcher is for mathematical comparisons", function() {
  //     var pi = 3, e = 2;

  //     expect(e).toBeLessThan(pi);
  //     expect(pi).not.toBeLessThan(e);
  //   });

  //   it("The 'toBeGreaterThan' is for mathematical comparisons", function() {
  //     var pi = 3, e = 2;

  //     expect(pi).toBeGreaterThan(e);
  //     expect(e).not.toBeGreaterThan(pi);
  //   });
  // });

});

describe("Gilded Rose", function() {

  it("has an Item constructor", function() {
    expect(typeof Item).toBe('function');
  });

  it("should set a global array", function() {
    expect(items instanceof Array).toBe(true);
  });

  it("should contain data", function() {
    expect(items.length).toBeGreaterThan(0);
  });

  it("has an update function", function() {
    expect(typeof update_quality).toBe('function');
  });

});

Add some integrity tests to coverage report


//take variables from from
function Character() {
  this.name = "idkMyBFFGil";
  this.health = 100;
  this.attack = 10;
  this.defense = 10;
}
var hero = new Character();

//initialize character
function initialize_character() {
  hero.name = document.getElementById('fname');
  hero.health = 100; // Starting health
  hero.attack = document.getElementById('stats'); // document.getElementById("attack").value
  hero.defense = 10 - document.getElementById('stats'); // document.getElementById("defense").value
}

function switch_to_gameplay() {
  document.getElementById('initialization').style.display = "none";
  document.getElementById('gameplay').style.display = "block";
}

function update_status() {
  // document.getElementById("health").value = hero.health
}

function gameplay() {
  wolvesAttack();
  banditsAttack();
  //pathFunction3();
  //pathFunction4();
  //pathFunction5();

  //finalBoss();
}

function run () {
  initialize_character();
  switch_to_gameplay();
  gameplay();
}

//take variables from from
function Character() {
  this.name = "idkMyBFFGil";
  this.health = 100;
  this.attack = 10;
  this.defense = 10;
}
var hero = new Character();

//initialize character
function initialize_character() {
  hero.name = document.getElementById('fname');
  hero.health = 100; // Starting health
  hero.attack = document.getElementById('stats'); // document.getElementById("attack").value
  hero.defense = 10 - document.getElementById('stats'); // document.getElementById("defense").value
}

function switch_to_gameplay() {
  document.getElementById('initialForm').style.display = "none";
  document.getElementById('gameplay').style.display = "block";
}

function update_status() {
  // document.getElementById("health").value = hero.health
}

function gameplay() {
  wolvesAttack();
  banditsAttack();
  //pathFunction3();
  //pathFunction4();
  //pathFunction5();

  //finalBoss();
}

function run () {
  initialize_character();
  switch_to_gameplay();
  gameplay();
}

Change id to match what index.html


---
layout: null
sitemap:
  exclude: 'yes'
---

$(document).ready(function () {
  $('a.about-button').click(function (e) {
    if ($('.panel-cover').hasClass('panel-cover--collapsed')) return
    currentWidth = $('.panel-cover').width()
    if (currentWidth < 960) {
      $('.panel-cover').addClass('panel-cover--collapsed')
      $('.content-wrapper').addClass('animated slideInRight')
    } else {
      $('.panel-cover').css('max-width', currentWidth)
      $('.panel-cover').animate({'max-width': '530px', 'width': '40%'}, 400, swing = 'swing', function () {})
    }
  })

  if (window.location.hash && window.location.hash == '#about') {
    $('.panel-cover').addClass('panel-cover--collapsed')
  }
/*
  if (window.location.pathname !== '{{ site.baseurl }}' && window.location.pathname !== '{{ site.baseurl }}index.html') {
    $('.panel-cover').addClass('panel-cover--collapsed')
  }

  $('.btn-mobile-menu').click(function () {
    $('.navigation-wrapper').toggleClass('visible animated bounceInDown')
    $('.btn-mobile-menu__icon').toggleClass('icon-list icon-x-circle animated fadeIn')
  })

  $('.navigation-wrapper .about-button').click(function () {
    $('.navigation-wrapper').toggleClass('visible')
    $('.btn-mobile-menu__icon').toggleClass('icon-list icon-x-circle animated fadeIn')
  })
*/
})

---
layout: null
sitemap:
  exclude: 'yes'
---

$(document).ready(function () {
  $('a.about-button').click(function (e) {
    if ($('.panel-cover').hasClass('panel-cover--collapsed')) return
    currentWidth = $('.panel-cover').width()
    if (currentWidth < 960) {
      $('.panel-cover').addClass('panel-cover--collapsed')
      $('.content-wrapper').addClass('animated slideInRight')
    } else {
      $('.panel-cover').css('max-width', currentWidth)
      $('.panel-cover').animate({'max-width': '530px', 'width': '40%'}, 400, swing = 'swing', function () {})
    }
  })

  if (window.location.hash && window.location.hash == '#about') {
    $('.panel-cover').addClass('panel-cover--collapsed')
  }
/*
  if (window.location.pathname !== '{{ site.baseurl }}' && window.location.pathname !== '{{ site.baseurl }}index.html') {
    $('.panel-cover').addClass('panel-cover--collapsed')
  }
*/
  $('.btn-mobile-menu').click(function () {
    $('.navigation-wrapper').toggleClass('visible animated bounceInDown')
    $('.btn-mobile-menu__icon').toggleClass('icon-list icon-x-circle animated fadeIn')
  })

  $('.navigation-wrapper .about-button').click(function () {
    $('.navigation-wrapper').toggleClass('visible')
    $('.btn-mobile-menu__icon').toggleClass('icon-list icon-x-circle animated fadeIn')
  })
})

Add animation on button press


module.exports = function (grunt) {
    grunt.initConfig({
        pkg: grunt.file.readJSON('package.json'),

        typescript: {
            options: {
                module: 'commonjs',
                target: 'es5'
            },
            dist: {
                src: 'src/lib/**/*.ts'
            }
        },

        mochaTest: {
            test: {
                options: {
                    reporter: 'spec'
                },
                src: ['test/**/*.js']
            }
        },

        jshint: {
            options: {
                jshintrc: '.jshintrc'
            },
            src: {
                files: {
                    src: [
                      'Gruntfile.js',
                      'src/**/*.js',
                    ]
                }
            }
        },

        watch: {
            all: {
                files: [
                    'src/**/*.ts',
                    'test/**/*.js'
                ],
                tasks: ['default']
            }
        }
    });

    grunt.loadNpmTasks('grunt-contrib-jshint');
    grunt.loadNpmTasks('grunt-mocha-test');
    grunt.loadNpmTasks('grunt-typescript-compile');
    grunt.loadNpmTasks('grunt-npm-install');
    grunt.loadNpmTasks('grunt-contrib-watch');

    grunt.registerTask('test', ['mochaTest']);
    grunt.registerTask('build', ['typescript']);
    grunt.registerTask('default', ['npm-install', 'build', 'test', 'jshint']);
};

module.exports = function (grunt) {
    grunt.initConfig({
        pkg: grunt.file.readJSON('package.json'),

        typescript: {
            options: {
                module: 'commonjs',
                target: 'es5'
            },
            dist: {
                src: 'src/**/*.ts'
            }
        },

        mochaTest: {
            test: {
                options: {
                    reporter: 'spec'
                },
                src: ['test/**/*.js']
            }
        },

        jshint: {
            options: {
                jshintrc: '.jshintrc'
            },
            src: {
                files: {
                    src: [
                      'Gruntfile.js',
                      'src/**/*.js',
                    ]
                }
            }
        },

        watch: {
            all: {
                files: [
                    'src/**/*.ts',
                    'test/**/*.js'
                ],
                tasks: ['default']
            }
        }
    });

    grunt.loadNpmTasks('grunt-contrib-jshint');
    grunt.loadNpmTasks('grunt-mocha-test');
    grunt.loadNpmTasks('grunt-typescript-compile');
    grunt.loadNpmTasks('grunt-npm-install');
    grunt.loadNpmTasks('grunt-contrib-watch');

    grunt.registerTask('test', ['mochaTest']);
    grunt.registerTask('build', ['typescript']);
    grunt.registerTask('default', ['npm-install', 'build', 'test', 'jshint']);
};

Make grunt build all .ts files in src/


module.exports = function(grunt) {

  // Project configuration.
  grunt.initConfig({
    pkg: grunt.file.readJSON('package.json'),
    jshint: {
      all: 'timbles.js'
    },
    jscs: {
      src: 'timbles.js',
      options: {
        config: '.jscsrc',
        verbose: true // Create output with rule names
      }
    }
  });

  // Load the JSHint and JSCS plugins.
  grunt.loadNpmTasks('grunt-contrib-jshint');
  grunt.loadNpmTasks('grunt-jscs');

  // Default task(s).
  grunt.registerTask('default', ['jshint', 'jscs']);
};

module.exports = function(grunt) {

  // Project configuration.
  grunt.initConfig({
    pkg: grunt.file.readJSON('package.json'),
    jshint: {
      all: ['Gruntfile.js', 'timbles.js', 'tests/test-scripts/*.js']
    },
    jscs: {
      src: ['Gruntfile.js', 'timbles.js', 'tests/test-scripts/*.js'],
      options: {
        config: '.jscsrc',
        verbose: true // Create output with rule names
      }
    }
  });

  // Load the JSHint and JSCS plugins.
  grunt.loadNpmTasks('grunt-contrib-jshint');
  grunt.loadNpmTasks('grunt-jscs');

  // Default task(s).
  grunt.registerTask('default', ['jshint', 'jscs']);
};

Include test scripts gruntfile itself to validation targets.


'use strict';

var from          = require('es5-ext/array/from')
  , iterable      = require('es5-ext/iterable/validate-object')
  , stringifiable = require('es5-ext/object/validate-stringifiable')
  , repeat        = require('es5-ext/string/#/repeat')
  , strip         = require('./strip');

module.exports = function (rows/*, options*/) {
	var options = Object(arguments[1]), cols = [];
	return from(iterable(rows), function (row, index) {
		return from(iterable(row), function (str, index) {
			var col = cols[index], strLength;
			if (!col) col = cols[index] = { width: 0 };
			str = stringifiable(str);
			strLength = strip(str).length;
			if (strLength > col.width) col.width = strLength;
			return { str: str, length: strLength };
		});
	}).map(function (row) {
		return row.map(function (item, index) {
			var pad = repeat.call(' ', cols[index].width - item.length);
			return item.str + pad;
		}).join((options.sep == null) ? ' | ' : options.sep);
	}).join('\n') + '\n';
};

'use strict';

var from          = require('es5-ext/array/from')
  , iterable      = require('es5-ext/iterable/validate-object')
  , stringifiable = require('es5-ext/object/validate-stringifiable')
  , repeat        = require('es5-ext/string/#/repeat')
  , strip         = require('./strip');

module.exports = function (rows/*, options*/) {
	var options = Object(arguments[1]), cols = []
	  , colsOptions = options.cols || [];
	return from(iterable(rows), function (row, index) {
		return from(iterable(row), function (str, index) {
			var col = cols[index], strLength;
			if (!col) col = cols[index] = { width: 0 };
			str = stringifiable(str);
			strLength = strip(str).length;
			if (strLength > col.width) col.width = strLength;
			return { str: str, length: strLength };
		});
	}).map(function (row) {
		return row.map(function (item, index) {
			var pad, align = 'left', colOptions = colsOptions && colsOptions[index];
			align = (colOptions && (colOptions.align === 'right')) ? 'right' : 'left';
			pad = repeat.call(' ', cols[index].width - item.length);
			if (align === 'left') return item.str + pad;
			else return pad + item.str;
		}).join((options.sep == null) ? ' | ' : options.sep);
	}).join('\n') + '\n';
};

Allow to customize allign per column


const { post } = require('koa-route');
const schemas = require('schemas');
const { users } = require('src/api');
const { create } = require('src/middleware/api/methods');

module.exports = [
  post('/users/', create(schemas.definitions.user.new, users.create))
];

const _ = require('koa-route');
const schemas = require('schemas');
const { users } = require('src/api');
const { create } = require('src/middleware/api/methods');

module.exports = [
  _.post('/users/', create(schemas.definitions.user.new, users.create))
];

Use koa-route as object rather than using its functions directly


import Sendgrid from 'sendgrid';

const helper = Sendgrid.mail;
const sender = new helper.Email('adiemerci@bisnaer.ch');
const reciever = new helper.Email('tuelsch@gmail.com');
const content = new helper.Content('text/plain', 'He, tsäueli');
const subject = 'Nöis mail';
const mail = new helper.Mail(sender, subject, reciever, content);
const sendgrid = Sendgrid(process.env.SENDGRID_API_KEY);
const request = sendgrid.emptyRequest({
  method: 'POST',
  path: '/v3/mail/send',
  body: mail.toJSON(),
});

export default function () {
  return sendgrid.API(request)
    .then((response) => {
      console.log(JSON.stringify(response.body));
      console.log(response.statusCode);
      console.log(response.headers);
    })
    .catch((error) => {
      console.error(JSON.stringify(error));
    });
}

import Sendgrid from 'sendgrid';

const helper = Sendgrid.mail;
const sender = new helper.Email('tuelsch@gmail.com', 'Philipp Gfeller');
const reciever = new helper.Email('tuelsch@gmail.com');
const content = new helper.Content('text/plain', 'He, tsäueli');
const subject = 'Nöis mail';
const mail = new helper.Mail(sender, subject, reciever, content);
const sendgrid = Sendgrid(process.env.SENDGRID_API_KEY);
const request = sendgrid.emptyRequest({
  method: 'POST',
  path: '/v3/mail/send',
  body: mail.toJSON(),
});

export default function () {
  return sendgrid.API(request)
    .then((response) => {
      console.log(JSON.stringify(response.body));
      console.log(response.statusCode);
      console.log(response.headers);
    })
    .catch((error) => {
      console.error(JSON.stringify(error));
    });
}

Use me as sender, for replies


'use strict';

var cmd = require('commander');
var pkg = require('../lib/package');
var replace = require('../lib/replace');
var semver = require('semver');
var sh = require('shelljs');

cmd
  .option('-s, --semver [version]', 'The semantic version to release in lieu of --type. This takes precedence over --type.')
  .option('-t, --type [major, minor or patch]', 'The type of release being performed in lieu of --semver.')
  .parse(process.argv);

module.exports = function () {
  var currentVersion = pkg.version;
  var nextVersion = cmd.semver || semver.inc(
    currentVersion,
    cmd.type || 'patch'
  );

  sh.exec('npm run lint');
  sh.exec('npm run test');
  replace('bower.json', currentVersion, nextVersion);
  replace('package.json', currentVersion, nextVersion);
  sh.exec('npm run dist');
  sh.exec('npm run lib');
  sh.exec('git commit -am "' + currentVersion + ' -> ' + nextVersion + '"');
  sh.exec('git tag -a ' + nextVersion + ' -m ' + nextVersion);
  sh.exec('git push');
  sh.exec('git push --tags');
  sh.exec('npm publish');
};

'use strict';

var cmd = require('commander');
var pkg = require('../lib/package');
var replace = require('../lib/replace');
var semver = require('semver');
var sh = require('shelljs');

cmd
  .option('-s, --semver [version]', 'The semantic version to release in lieu of --type. This takes precedence over --type.')
  .option('-t, --type [major, minor or patch]', 'The type of release being performed in lieu of --semver.')
  .parse(process.argv);

module.exports = function () {
  var currentVersion = pkg.version;
  var nextVersion = cmd.semver || semver.inc(
    currentVersion,
    cmd.type || 'patch'
  );

  sh.exec('gulp lint');
  sh.exec('gulp test');
  replace('bower.json', currentVersion, nextVersion);
  replace('package.json', currentVersion, nextVersion);
  sh.exec('gulp dist');
  sh.exec('gulp lib');
  sh.exec('git commit -am "' + currentVersion + ' -> ' + nextVersion + '"');
  sh.exec('git tag -a ' + nextVersion + ' -m ' + nextVersion);
  sh.exec('git push');
  sh.exec('git push --tags');
  sh.exec('npm publish');
};

Use gulp command instead of npm run because that requires them to have the scripts entries in the package.json.


'use strict';

const config = require('json!./../../../build/config.json');
const db = require('src/storage/webtask');
const sandbox = require('sandboxjs');
const helper = require('src/helpers')({
  config: config,
  profile: sandbox.fromToken(config.sandbox.token, config.sandbox)
});

module.exports = (ctx, cb) => {
  db.init(ctx.storage);

  helper.monitor.getMonitoringResult()
    .then(services => db.getStoredData()
    .then(stored => {
      helper.notifier.notify(helper.monitor.getDifference(services, stored))
        .then(() => db.storeData(services)
        .then(() => cb()));
    })
  ).catch(err => cb(err));
};

'use strict';

const config = require('build/config.json');
const db = require('src/storage/webtask');
const sandbox = require('sandboxjs');
const helper = require('src/helpers')({
  config: config,
  profile: sandbox.fromToken(config.sandbox.token, config.sandbox)
});

module.exports = (ctx, cb) => {
  db.init(ctx.storage);

  helper.monitor.getMonitoringResult()
    .then(services => db.getStoredData()
    .then(stored => {
      helper.notifier.notify(helper.monitor.getDifference(services, stored))
        .then(() => db.storeData(services)
        .then(() => cb()));
    })
  ).catch(err => cb(err));
};

Fix json file loading using valid json-loader path.


import Ember from 'ember';

const { get, set, guidFor, merge, getOwner } = Ember;

function updateTitle(tokens) {
  set(this, 'title', tokens.toString());
}

export default Ember.Helper.extend({
  pageTitleList: Ember.inject.service(),
  headData: Ember.inject.service(),

  init() {
    this._super();
    let tokens = get(this, 'pageTitleList');
    tokens.push({ id: guidFor(this) });
  },

  compute(params, _hash) {
    let tokens = get(this, 'pageTitleList');
    let hash = merge({}, _hash);
    hash.id = guidFor(this);
    hash.title = params.join('');
    tokens.push(hash);
    Ember.run.scheduleOnce('afterRender', get(this, 'headData'), updateTitle, tokens);
    return '';
  },

  destroy() {
    let tokens = get(this, 'pageTitleList');
    let id = guidFor(this);
    tokens.remove(id);

    let router = getOwner(this).lookup('router:main');
    let routes = router._routerMicrolib || router.router;
    let { activeTransition } = routes;
    let headData = get(this, 'headData');
    if (activeTransition) {
      activeTransition.promise.finally(function () {
        if (headData.isDestroyed) { return; }
        Ember.run.scheduleOnce('afterRender', headData, updateTitle, tokens);
      });
    } else {
      Ember.run.scheduleOnce('afterRender', headData, updateTitle, tokens);
    }
  }
});

import Ember from 'ember';

const { get, set, guidFor, merge, getOwner } = Ember;

function updateTitle(tokens) {
  set(this, 'title', tokens.toString());
}

export default Ember.Helper.extend({
  pageTitleList: Ember.inject.service(),
  headData: Ember.inject.service(),

  init() {
    this._super();
    let tokens = get(this, 'pageTitleList');
    tokens.push({ id: guidFor(this) });
  },

  compute(params, _hash) {
    let tokens = get(this, 'pageTitleList');
    let hash = merge({}, _hash);
    hash.id = guidFor(this);
    hash.title = params.join('');
    tokens.push(hash);
    Ember.run.scheduleOnce('afterRender', get(this, 'headData'), updateTitle, tokens);
    return '';
  },

  destroy() {
    let tokens = get(this, 'pageTitleList');
    let id = guidFor(this);
    tokens.remove(id);

    let router = getOwner(this).lookup('router:main');
    let routes = router._routerMicrolib || router.router;
    let { activeTransition } = routes || {};
    let headData = get(this, 'headData');
    if (activeTransition) {
      activeTransition.promise.finally(function () {
        if (headData.isDestroyed) { return; }
        Ember.run.scheduleOnce('afterRender', headData, updateTitle, tokens);
      });
    } else {
      Ember.run.scheduleOnce('afterRender', headData, updateTitle, tokens);
    }
  }
});

Add blank object fallback for routes in integration tests


var express = require('express')
var router = express.Router()
var multer = require('multer')
var dateFormat = require('dateformat')

var db = require('../db')

var storage = multer.diskStorage({
  destination: function (request, file, callback) {
    callback(null, './public/images/blog')
  },
  filename: function (request, file, callback) {
    callback(null, file.originalname)
  }
})
var upload = multer({storage: storage})

router.get('/', function (req, res) {
  db.getUsers(req.app.get('connection'))
    .then(function (users) {
      res.render('index', { users: users })
    })
    .catch(function (err) {
      res.status(500).send('DATABASE ERROR: ' + err.message)
    })
})

module.exports = router

var express = require('express')
var router = express.Router()
var multer = require('multer')
var dateFormat = require('dateformat')

var db = require('../db')

var storage = multer.diskStorage({
  destination: function (request, file, callback) {
    callback(null, './public/images/blog')
  },
  filename: function (request, file, callback) {
    callback(null, file.originalname)
  }
})
var upload = multer({storage: storage})

// SITE STRUCTURE

// Frontpage - URL - /
router.get('/', function (req, res) {
  db.getRecentBlogs(req.app.get('connection'))
  .then((results) => {
    const viewData = {
      blog: []
    }
    for (var i = 0; i < results.length; i++) {
      const blogEntry = {
        title: results[i].title,
        published_date: dateFormat(results[i].published_date, 'mmmm dd yyyy'),
        image: results[i].image,
        summary: results[i].summary,
        first_name: results[i].first_name,
        last_name: results[i].last_name,
        profile_id: results[i].profile_id,
        tags: results[i].tags,
        slugs: results[i].slugs}
      viewData.blog.push(blogEntry)
    }
    res.render('index', viewData)
  })
  .catch(function (err) {
    res.status(500).send('DATABASE ERROR: ' + err.message)
  })
})

module.exports = router

Add get route for frontpage


var logPanelFlag = window["$manifest$logPanel"]

if (logPanelFlag) {
	log = function(dummy) {
		var args = Array.prototype.slice.call(arguments)
		var logger = document.getElementById("logger") || undefined
		if (logger)
			logger.innerHTML += args.join(" ") + "<br>"
	}
} else {
	log = console.log.bind(console)
}

var logPanelFlag = window["$manifest$logPanel"]
var logMessagesBuffer = []

if (logPanelFlag) {
	log = function(dummy) {
		var args = Array.prototype.slice.call(arguments)
		var logger = document.getElementById("logger") || undefined
		if (logger) {
			if (logMessagesBuffer.length > 0) {
				for (var i = 0; i < logMessagesBuffer.length; ++i)
					logger.innerHTML += logMessagesBuffer[i] + "
"
				logMessagesBuffer = []
			}
			logger.innerHTML += args.join(" ") + "
"
		} else {
			logMessagesBuffer.push(args.join(" "))
		}
	}
} else {
	log = console.log.bind(console)
}

Add log messages buffer while loggerpanel is not initialized


module.exports = function(config) {
    config.set({
        basePath: '../../',

        files: [
            'web/js/vendor/angular.js',
            'web/js/vendor/angular-*.js',
            'test/lib/angular/angular-mocks.js',
            'web/js/vendor/jquery*.js',
            'web/js/**/*.js',
            'test/unit/**/*.js'
        ],

        autoWatch: false,

        singleRun: true,
        browsers: ['PhantomJS', 'Firefox'],
        frameworks: ["jasmine"],

        reporters: ['dots', 'junit', 'coverage'],

        preprocessors: {
            // source files, that you wanna generate coverage for
            // do not include tests or libraries
            // (these files will be instrumented by Istanbul)
            'web/js/*.js': ['coverage']
        },

        junitReporter: {
            outputFile: 'app/build/logs/js_unit.xml',
            suite: 'unit'
        },

        coverageReporter: {
            type : 'html',
            dir : 'app/build/logs/jscoverage/'
        },

        reportSlowerThan: 500,
    });
};

module.exports = function(config) {
    config.set({
        basePath: '../../',

        files: [
            'web/js/vendor/angular.js',
            'web/js/vendor/angular-*.js',
            'test/lib/angular/angular-mocks.js',
            'web/js/vendor/jquery*.js',
            'web/js/**/*.js',
            'test/unit/**/*.js'
        ],

        autoWatch: false,

        singleRun: true,
        browsers: ['PhantomJS', 'Firefox'],
        frameworks: ["jasmine"],

        reporters: ['dots', 'junit', 'coverage'],

        preprocessors: {
            // source files, that you wanna generate coverage for
            // do not include tests or libraries
            // (these files will be instrumented by Istanbul)
            'web/js/*.js': ['coverage']
        },

        junitReporter: {
            outputFile: 'app/build/logs/js_unit.xml',
            suite: 'unit'
        },

        coverageReporter: {
            type : 'html',
            dir : 'app/build/jscoverage/'
        },

        reportSlowerThan: 500,
    });
};

Fix wrong path for jscoverage report


class Node_Tree {
  constructor(val) {
    this.val = val;
    this.parent = null;
    this.children = [];
  }
}

class Node_Trie {
  constructor(val) {
    /*
    ** The node stores a single character as its value.
    ** Child nodes are stored in an object instead of an array for constant-time lookup
    ** because every child node will have a unique value/letter
    */
    this.val = val;
    this.children = {};
  }
}

class Node_BST {
  constructor(key, val) {
    /*
    ** Key should be an integer, for proper placement of the node in a Binary Search Tree
    */
    this.key    = key;
    this.val    = val;
    this.parent = null;
    this.left   = null;
    this.right  = null;
  }
}

export { Node_Tree, Node_Trie, Node_BST };

class Node_Tree {
  constructor(val) {
    this.val = val;
    this.parent = null;
    this.children = [];
  }
}

class Node_Trie {
  constructor(val) {
    /*
    ** The node stores a single character as its value.
    ** Child nodes are stored in an object instead of an array for constant-time lookup
    ** because every child node will have a unique value/letter
    */
    this.val = val;
    this.children = {};
  }
}

class Node_BST {
  constructor(key, val) {
    /*
    ** Key should be an integer, for proper placement of the node in a Binary Search Tree
    */
    this.key    = key;
    this.val    = val;
    this.parent = null;
    this.left   = null;
    this.right  = null;
  }
}

class Node_LinkedList {
  constructor(val) {
    /*
    ** A block of information stored in a doubly linked list.
    ** Stores references to the next and previous nodes.
    */
    this.val  = val;
    this.next = null;
    this.prev = null;
  }
}

export { Node_Tree, Node_Trie, Node_BST };

Add linked list node to utils


'use babel';

import { SelectListView } from 'atom-space-pen-views';

export default class ChangeThemeView extends SelectListView {

  constructor(themeManager) {
    super();
    this.modal = atom.workspace.addModalPanel({
      item: this,
      visible: false
    });
    this.addClass('overlay from-top');
    this.list.addClass('mark-active');

    this.themeManager = themeManager;
  }

  changeTheme() {
    this.setItems(this.themeManager.getThemeNames());
    this.modal.show();
    this.focus();
  }

  viewForItem(theme) {
    let el = document.createElement('li');
    el.textContent = theme;
    return el;
  }

  confirmed(theme) {
    this.themeManager.loadTheme(theme);
    this.modal.hide();
  }

  cancel() {
    super.cancel();
    this.modal.hide();
  }

  destroy() {
    this.modal.destroy();
  }
}

'use babel';

import { SelectListView } from 'atom-space-pen-views';

export default class ChangeThemeView extends SelectListView {

  constructor(themeManager) {
    super();
    this.modal = atom.workspace.addModalPanel({
      item: this,
      visible: false
    });
    this.addClass('overlay from-top');
    this.list.addClass('mark-active');

    this.themeManager = themeManager;
  }

  changeTheme() {
    this.setItems(this.themeManager.getThemeNames());
    this.modal.show();
    this.focusFilterEditor();
  }

  viewForItem(theme) {
    let el = document.createElement('li');
    el.textContent = theme;
    return el;
  }

  confirmed(theme) {
    this.themeManager.loadTheme(theme);
    this.modal.hide();
  }

  cancel() {
    super.cancel();
    this.modal.hide();
  }

  destroy() {
    this.modal.destroy();
  }
}

Maintain focus in change theme list view


import User from 'src/models/UserModel';
import jwt from 'jsonwebtoken';

export const list = (req, res, next) => User.find({})
  .then(data => res.json(data))
  .catch(err => next(err));

export const getUser = (req, res) => {
  res.json(req.user);
}

export const registerUser = (req, res, next) => {
  const { email, password, displayName } = req.body;
  const user = new User({ email, password, displayName });
  user.save()
    .then(newUser => res.json(newUser))
    .catch(err => next(err));
}

export const authenticateUser = (req, res, next) => {
  User.findOne({
    email: req.body.email,
  })
    .select('password')
    .then((user) => {
      if (!user) {
        res.status = 401;
        throw new Error('You shall not pass');
      }

      return user.comparePassword(req.body.password);
    })
    .then((user) => {
      const token = jwt.sign(user.toJSON(), process.env.JWT_SECRET, {
        expiresIn: '7 days',
      });

      return res.json({ token });
    })
    .catch((err) => {
      res.status = 401;
      next(err);
    });
}

export const remove = (req, res, next) => User.remove({
  _id: req.params.id,
})
  .then(data => res.json(data))
  .catch(err => next(err));

import User from 'src/models/UserModel';
import jwt from 'jsonwebtoken';

export const list = (req, res, next) => User.find({})
  .then(data => res.json(data))
  .catch(err => next(err));

export const getUser = (req, res) => {
  res.json(req.user);
}

export const registerUser = (req, res, next) => {
  const { email, password, displayName } = req.body;
  const user = new User({ email, password, displayName });
  user.save()
    .then(newUser => res.json(newUser))
    .catch(err => next(err));
}

export const authenticateUser = (req, res, next) => {
  User.findOne({
    email: req.body.email,
  })
    .select('password')
    .then((user) => {
      if (!user) {
        res.status = 401;
        return next(new Error('You shall not pass'));
      }

      return user.comparePassword(req.body.password);
    })
    .then((user) => {
      const token = jwt.sign(user.toJSON(), process.env.JWT_SECRET, {
        expiresIn: '7 days',
      });

      return res.json({ token });
    })
    .catch((err) => {
      res.status = 401;
      next(err);
    });
}

export const remove = (req, res, next) => User.remove({
  _id: req.params.id,
})
  .then(data => res.json(data))
  .catch(err => next(err));

Use next to propagate err


/**
 * Copyright 2013-2020 the original author or authors from the JHipster project.
 *
 * This file is part of the JHipster project, see https://www.jhipster.tech/
 * for more information.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

const { runJHipster } = require('./program');
const { done } = require('./utils');

module.exports = runJHipster().catch(done);

process.on('unhandledRejection', up => {
    throw up;
});

/**
 * Copyright 2013-2020 the original author or authors from the JHipster project.
 *
 * This file is part of the JHipster project, see https://www.jhipster.tech/
 * for more information.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

const { runJHipster } = require('./program');
const { done, logger } = require('./utils');

module.exports = runJHipster().catch(done);

process.on('unhandledRejection', up => {
    logger.error('Unhandled promise rejection at:');
    logger.fatal(up);
});

Add logger at unhandled rejection.


Handlebars.registerViewHelper('loading', function(view) {
  var _render = view.render;
  view.render = function() {
    if (view.parent.$el.hasClass(view.parent._loadingClassName)) {
      return _render.call(this, view.fn);
    } else {
      return _render.call(this, view.inverse);
    }
  };
  var callback = _.bind(view.render, view);
  view.parent._loadingCallbacks = view.parent._loadingCallbacks || [];
  view.parent._loadingCallbacks.push(callback);
  view.on('freeze', function() {
    view.parent._loadingCallbacks = _.without(view.parent._loadingCallbacks, callback);
  });
  view.render();
});

Handlebars.registerViewHelper('loading', function(view) {
  var _render = view.render;
  view.render = function() {
    if (view.parent.$el.hasClass(view.parent._loadingClassName)) {
      return _render.call(this, view.fn);
    } else {
      return _render.call(this, view.inverse);
    }
  };
  var callback = _.bind(view.render, view);
  view.parent._loadingCallbacks = view.parent._loadingCallbacks || [];
  view.parent._loadingCallbacks.push(callback);
  view.on('destroyed', function() {
    view.parent._loadingCallbacks = _.without(view.parent._loadingCallbacks, callback);
  });
  view.render();
});

Use destroyed event for cleanup instead of freeze


var EC = protractor.ExpectedConditions;

describe('con10t pages', function() {

    it('should contain markers on the grako_map page', function() {
    	browser.get('/project/grako_map');
        var marker = element(by.css('.leaflet-marker-icon'));
        browser.wait(EC.presenceOf(marker), 10000);
        expect(marker.isPresent()).toBe(true);
    });

});

describe('con10t-image', function() {

    fit('should display an image from a given url', function() {
        browser.get('/project/steindorff');
        var marker = element(by.css('.con10t-image-fromsrc'));
        browser.wait(EC.presenceOf(marker), 10000);
        expect(marker.isPresent()).toBe(true);
    });

});

Add test for con10t-image widget.


// @flow
import React from 'react';
import PropTypes from 'prop-types';
import type { ReactChildren } from 'react-flow-types';

import FlexCell from './FlexCell';
import Tag from './Tag';
import TextEllipsis from './TextEllipsis';

import wrapIfNotElement from './utils/wrapIfNotElement';

export type Props = {
    basic?: ReactChildren,
    tag?: ReactChildren,
    statusIcon?: ReactChildren,
    children?: ReactChildren, // eslint-disable-line react/require-default-props
};

function BasicRow({ basic, tag, statusIcon, children, ...otherProps }: Props) {
    if (!basic) {
        return null;
    }

    return (
        <div {...otherProps}>
            <FlexCell shrink>
                <TextEllipsis>{basic}</TextEllipsis>
            </FlexCell>

            {tag && wrapIfNotElement(tag, { with: Tag })}
            {statusIcon && wrapIfNotElement(statusIcon, { with: 'span' })}
            {children}
        </div>
    );
}

BasicRow.propTypes = {
    basic: PropTypes.node,
    tag: PropTypes.node,
    statusIcon: PropTypes.node,
};

BasicRow.defaultProps = {
    basic: undefined,
    tag: undefined,
    statusIcon: undefined,
};

export default BasicRow;

// @flow
import React from 'react';
import PropTypes from 'prop-types';
import type { ReactChildren } from 'react-flow-types';

import FlexCell from './FlexCell';
import Tag from './Tag';

import wrapIfNotElement from './utils/wrapIfNotElement';

export type Props = {
    basic?: ReactChildren,
    tag?: ReactChildren,
    statusIcon?: ReactChildren,
    children?: ReactChildren, // eslint-disable-line react/require-default-props
};

function BasicRow({ basic, tag, statusIcon, children, ...otherProps }: Props) {
    if (!basic) {
        return null;
    }

    return (
        <div {...otherProps}>
            <FlexCell shrink>{basic}</FlexCell>

            {tag && wrapIfNotElement(tag, { with: Tag })}
            {statusIcon && wrapIfNotElement(statusIcon, { with: 'span' })}
            {children}
        </div>
    );
}

BasicRow.propTypes = {
    basic: PropTypes.node,
    tag: PropTypes.node,
    statusIcon: PropTypes.node,
};

BasicRow.defaultProps = {
    basic: undefined,
    tag: undefined,
    statusIcon: undefined,
};

export default BasicRow;

Make text labels break into multi-lines by default.


/*jshint node:true*/
/* global require, module */

var EmberApp = require( 'ember-cli/lib/broccoli/ember-app' );

module.exports = function( defaults ) {

	var app = new EmberApp( defaults, {

		outputPaths: {

			app: {
				js: '/assets/app.js',
				css: {
					'app': '/assets/styles.css'
				}
			}

		},

		sourcemaps: {
			enabled: false
		}

	});

	// Use `app.import` to add additional libraries to the generated output files.
	return app.toTree();

}

/*jshint node:true*/
/* global require, module */

var EmberApp = require( 'ember-cli/lib/broccoli/ember-app' );

module.exports = function( defaults ) {

	var app = new EmberApp( defaults, {

		outputPaths: {

			app: {
				js: '/assets/app.js',
				css: {
					'app': '/assets/styles.css'
				}
			}

		},

		sourcemaps: {
			enabled: false
		},

		sassOptions: {
			outputStyle: 'expanded'
		}

	});

	// Use `app.import` to add additional libraries to the generated output files.
	return app.toTree();

}

Expand Sass in dev environment


'use strict';

const EmberAddon = require('ember-cli/lib/broccoli/ember-addon');

module.exports = function (defaults) {
  let app = new EmberAddon(defaults, {
    // 'ember-cli-babel': {
    //   includePolyfill: true
    // }
  });

  /*
   This build file specifies the options for the dummy test app of this
   addon, located in `/tests/dummy`
   This build file does *not* influence how the addon or the app using it
   behave. You most likely want to be modifying `./index.js` or app's build file
   */

  return app.toTree();
};

'use strict';

const EmberAddon = require('ember-cli/lib/broccoli/ember-addon');

module.exports = function (defaults) {
  let app = new EmberAddon(defaults, {});

  /*
   This build file specifies the options for the dummy test app of this
   addon, located in `/tests/dummy`
   This build file does *not* influence how the addon or the app using it
   behave. You most likely want to be modifying `./index.js` or app's build file
   */

  return app.toTree();
};

Remove commented out babel polyfill


/** @jsx React.DOM */

var CountriesComponent = require('./countries_component');
var PathsComponent     = require('./paths_component');
var PolygonsComponent  = require('./polygons_component');
var React              = require('react');

module.exports = React.createClass({
  selectCountry: function(country) {
    this.refs.countries.setState({selectedCountry: country});
  },

  render: function() {
    var world = this.props.world;

    return <svg width={world.width} height={world.height}>
      <PolygonsComponent className="hexgrid" polygons={world.hexagons} />
      <CountriesComponent ref="countries" className="PiYG" countries={world.countries} stream={this.props.stream} />
      <PathsComponent className="voronoi" paths={world.cells} />
    </svg>;
  }
});

/** @jsx React.DOM */

var CountriesComponent = require('./countries_component');
var PathsComponent     = require('./paths_component');
var PolygonsComponent  = require('./polygons_component');
var React              = require('react');

module.exports = React.createClass({
  // Selects a given country.
  selectCountry: function(country) {
    this.refs.countries.setState({selectedCountry: country});
  },

  // Deselects the currently selected country.
  deselectCountry: function() {
    this.selectCountry(null);
  },

  render: function() {
    var world = this.props.world;

    return <svg width={world.width} height={world.height}>
      <PolygonsComponent className="hexgrid" polygons={world.hexagons} />
      <CountriesComponent ref="countries" className="PiYG" countries={world.countries} stream={this.props.stream} />
      <PathsComponent className="voronoi" paths={world.cells} />
    </svg>;
  }
});

Add deselectCountry method to WorldComponent.


import reactDOMRunner from './reactDOMRunner';

export const viewerEndpoint = 'https://happo.now.sh';
export const snapEndpoint = 'https://happo-snap.now.sh';
export const include = '**/@(*-snaps|snaps).@(js|jsx)';
export const stylesheets = [];
export const targets = {};
export const configFile = './.happo.js';
export const hooks = {
  run: reactDOMRunner,
  finish: (result) => {
    console.log(result);
  }
}
export function customizeWebpackConfig(config) {
  // provide a default no-op for this config option so that we can assume it's
  // always there.
  return config;
}

import reactDOMRunner from './reactDOMRunner';

export const endpoint = 'https://happo.now.sh';
export const include = '**/@(*-snaps|snaps).@(js|jsx)';
export const stylesheets = [];
export const targets = {};
export const configFile = './.happo.js';
export const hooks = {
  run: reactDOMRunner,
  finish: (result) => {
    console.log(result);
  }
}
export function customizeWebpackConfig(config) {
  // provide a default no-op for this config option so that we can assume it's
  // always there.
  return config;
}

Fix default for endpoint config


(function() {
  var socket = io.connect('http://localhost:3000'); // NOTE: Use real server IP if using mobile devices
  var $body = $('body');
  var $status = $('.status');
  var status = '';

  $body.on('click touchstart', function() {
    status = status === 'on' ? 'off' : 'on';
    socket.emit('ledToggle', status);
  });

  socket.on('message', function(data) {
    $status.text(data);
    $body.removeClass('on off').addClass(data);
  });

  socket.emit('ledStatus');
}());

(function() {
  var socket = io.connect('/'); // NOTE: Use real server hostname:port if using devices not directly connected to server
  var $body = $('body');
  var $status = $('.status');
  var status = '';

  $body.on('click touchstart', function() {
    status = status === 'on' ? 'off' : 'on';
    socket.emit('ledToggle', status);
  });

  socket.on('message', function(data) {
    $status.text(data);
    $body.removeClass('on off').addClass(data);
  });

  socket.emit('ledStatus');
}());

Update client socket connect url


module.exports = {
  'hotkey': 'alt+space',
  'theme': 'tinytacoteam/dark-theme',
  'plugins': [],
}

module.exports = {
  'hotkey': 'alt+space',
  'theme': 'tinytacoteam/light-theme',
  'plugins': [
    'tinytacoteam/calculator',
  ],
}

Update the default zazu file to have more sensible defaults.


const User = require('./../../models/user')

const usersRoutes = (api) => {
  api.post('/users', (req, res) => {
    User.create(req.body.user)
      .then((user) => {
        delete user.password

        res.status(201).send(user)
      })
      .catch((user) => {
        res.status(400).send({ 'errors': user.errors })
      })
  })
}

module.exports = usersRoutes

const _    = require('lodash')
const User = require('./../../models/user')

const usersRoutes = (api) => {
  api.post('/users', (req, res) => {
    User.create(req.body.user)
      .then((user) => {
        res.status(201).send(_.omit(user, ['password']))
      })
      .catch((user) => {
        res.status(400).send({ 'errors': user.errors })
      })
  })
}

module.exports = usersRoutes

Return model instance instead of simple object


// var foursquare = (require('foursquarevenues'))('CLIENTIDKEY', 'CLIENTSECRETKEY');
var foursquare = (require('foursquarevenues'))('Y5D1QX0FS4R5ZTJ5TLU3RDL1U0PUE4ZU3NPTQDS2AGMW1KGP', 'XM2PZ15QKM0I5TQYTCTYXOVBGWKRHCCFZFNNKZYQA1JV1SNZ');

var params = {
    "ll": "40.7,-74"
};

foursquare.getVenues(params, function(error, venues) {
    if (!error) {
	console.log(venues.toString());
    }
});

foursquare.exploreVenues(params, function(error, venues) {
    if (!error) {
  	console.log(venues.toString());
    }
});

const dotenv = require('dotenv');

dotenv.config();

var foursquare = (require('foursquarevenues'))(process.env.FOURSQUARE_CLIENT_ID, process.env.FOURSQUARE_CLIENT_SECRET);

var params = {
    "ll": "40.7,-74"
};

foursquare.getVenues(params, function(error, venues) {
    if (!error) {
	console.log(venues);
    }
});

foursquare.exploreVenues(params, function(error, venues) {
    if (!error) {
  	console.log(venues);
    }
});

Use dotenv for Foursquare credentials.


module.exports = [
  {
    "name": "English",
    "code": "en_US",
    "country": "United States"
  },
  {
    "name": "Italian",
    "code": "it_IT",
    "country": "Italia"
  },
  {
    "name": "Portuguese",
    "code": "pt_PT",
    "country": "Portuguesa"
  },
  {
    "name": "German",
    "code": "de_DE",
    "country": "Deutsche"
  }
];

module.exports = [
  {
    "name": "English",
    "code": "en_US",
    "country": "United States"
  },
  {
    "name": "Italian",
    "code": "it_IT",
    "country": "Italia"
  },
  {
    "name": "Portuguese",
    "code": "pt_PT",
    "country": "Portuguesa"
  },
  {
    "name": "German",
    "code": "de_DE",
    "country": "Deutsche"
  },
  {
    "name": "Dutch",
    "code": "nl_NL",
    "country": "Nederlands"
  }
];

Add Dutch as a language option


if (typeof chrome === 'undefined' ||
    !chrome ||
    (!chrome.extension && !chrome.app)) {
  (function(i,s,o,g,r,a,m){i['GoogleAnalyticsObject']=r;i[r]=i[r]||function(){
  (i[r].q=i[r].q||[]).push(arguments)},i[r].l=1*new Date();a=s.createElement(o),
  m=s.getElementsByTagName(o)[0];a.async=1;a.src=g;m.parentNode.insertBefore(a,m)
  })(window,document,'script','//www.google-analytics.com/analytics.js','ga');

  ga('create', 'UA-40459098-2', 'passwordgen.org');
  ga('send', 'pageview');
}

if (typeof chrome === 'undefined' ||
    !chrome ||
    (!chrome.extension &&
     (!chrome.app || !chrome.app.window))) {
  console.log('Adding analytics code.');
  (function(i,s,o,g,r,a,m){i['GoogleAnalyticsObject']=r;i[r]=i[r]||function(){
  (i[r].q=i[r].q||[]).push(arguments)},i[r].l=1*new Date();a=s.createElement(o),
  m=s.getElementsByTagName(o)[0];a.async=1;a.src=g;m.parentNode.insertBefore(a,m)
  })(window,document,'script','//www.google-analytics.com/analytics.js','ga');

  ga('create', 'UA-40459098-2', 'passwordgen.org');
  ga('send', 'pageview');
}

Fix check for extension/app in Analytics.


// -*- coding: utf-8 -*-
//
// (c) 2014 Bjoern Ricks <bjoern.ricks@gmail.com>
//
// See LICENSE comming with the source of 'trex' for details.
//
'use strict';

var trexServices = angular.module('trex.services', ['ngResource']);

trexServices.factory('Conf', function($location) {
    function getRootUrl() {
        var rootUrl = $location.protocol() + '://' + $location.host();
        if ($location.port())
            rootUrl += ':' + $location.port();
        return rootUrl;
    };

    return {
        'apiBase': '/api/1',
        'rootUrl': getRootUrl()
    };
});

trexServices.factory('Project', ['$resource', 'Conf',
    function($resource, Conf) {
        return $resource(Conf.apiBase + '/projects/:projectId',
            {projectId: '@id'},
            {entries: {method: 'GET', isArray: true,
                       url: Conf.apiBase + '/projects/:projectId/entries',
                       params: {projectId: '@id'}
                      }
           }
        );
    }
]);

trexServices.factory('Entry', ['$resource', 'Conf',
    function($resource, Conf) {
        return $resource(Conf.apiBase + '/entries/:entryId', {entryId: '@id'}, {
        });
    }
]);

// -*- coding: utf-8 -*-
//
// (c) 2014 Bjoern Ricks <bjoern.ricks@gmail.com>
//
// See LICENSE comming with the source of 'trex' for details.
//
'use strict';

var trexServices = angular.module('trex.services', ['ngResource']);

trexServices.factory('Conf', function($location) {
    function getRootUrl() {
        var rootUrl = $location.protocol() + '://' + $location.host();
        if ($location.port())
            rootUrl += ':' + $location.port();
        return rootUrl;
    };

    return {
        'apiBase': '/api/1',
        'rootUrl': getRootUrl()
    };
});

trexServices.factory('Project', ['$resource', 'Conf',
    function($resource, Conf) {
        return $resource(Conf.apiBase + '/projects/:projectId',
            {projectId: '@id'},
            {entries: {method: 'GET', isArray: true,
                       url: Conf.apiBase + '/projects/:projectId/entries',
                       params: {projectId: '@id'}
                      },
             tags: {method: 'GET', isArray: true,
                    url: Conf.apiBase + '/projects/:projectId/tags',
                    params: {projectId: '@id'}
                   }
           }
        );
    }
]);

trexServices.factory('Entry', ['$resource', 'Conf',
    function($resource, Conf) {
        return $resource(Conf.apiBase + '/entries/:entryId', {entryId: '@id'}, {
        });
    }
]);

Allow to search for project tags


import calculateParamOffset from 'helpers/calculateParamOffset';
import sanitizeParamLimit from 'helpers/sanitizeParamLimit';

export default ({ limit, page }) => {
  const saneLimit = sanitizeParamLimit(limit);

  return {
    resolve: 'city,owner',
    sort: '-created',
    offset: calculateParamOffset(page, saneLimit),
    limit: saneLimit
  };
};

import calculateParamOffset from 'helpers/calculateParamOffset';
import sanitizeParamLimit from 'helpers/sanitizeParamLimit';

export default ({ limit, page }) => {
  const saneLimit = sanitizeParamLimit(limit);

  return {
    resolve: 'city,owner',
    sort: '-created',
    offset: calculateParamOffset(page, saneLimit),
    limit: saneLimit,
    state: [
      'supportable.active',
      'supportable.winner',
      'loser',
      'processing.*',
      'closed'
    ]
  };
};

Add state querying to petitions listing


var _ = require('lodash')
var fs = require('fs')
var gzip = require('gzip-size')
var filesize = require('filesize')
var cssstats = require('cssstats')

var module = require('tachyons-font-weight/package.json')
var moduleCss = fs.readFileSync('node_modules/tachyons-font-weight/tachyons-font-weight.min.css', 'utf8')
var moduleObj = cssstats(moduleCss)
var moduleSize = filesize(moduleObj.gzipSize)

var srcCSS = fs.readFileSync('./src/_font-weight.css', 'utf8')
var navDocs = fs.readFileSync('./templates/nav_docs.html', 'utf8')

var template = fs.readFileSync('./templates/docs/font-weight/index.html', 'utf8')
var tpl = _.template(template)
var html = tpl({
  moduleVersion: module.version,
  moduleSize: moduleSize,
  moduleObj: moduleObj,
  srcCSS: srcCSS,
  navDocs: navDocs
})

fs.writeFileSync('./docs/typography/font-weight/index.html', html)

var _ = require('lodash')
var fs = require('fs')
var gzip = require('gzip-size')
var filesize = require('filesize')
var cssstats = require('cssstats')

var module = require('tachyons-font-weight/package.json')
var moduleCss = fs.readFileSync('node_modules/tachyons-font-weight/tachyons-font-weight.min.css', 'utf8')
var moduleObj = cssstats(moduleCss)
var moduleSize = filesize(moduleObj.gzipSize)

var srcCSS = fs.readFileSync('./src/_font-weight.css', 'utf8')
var navDocs = fs.readFileSync('./templates/nav_docs.html', 'utf8')
var siteFooter = fs.readFileSync('./templates/footer.html', 'utf8')

var template = fs.readFileSync('./templates/docs/font-weight/index.html', 'utf8')
var tpl = _.template(template)
var html = tpl({
  moduleVersion: module.version,
  moduleSize: moduleSize,
  moduleObj: moduleObj,
  srcCSS: srcCSS,
  navDocs: navDocs,
  siteFooter: siteFooter
})

fs.writeFileSync('./docs/typography/font-weight/index.html', html)

Add site footer to each documentation generator


const OFF = 0;

module.exports = {
    "env": {
        "node": true
    },
    "parserOptions": {
        "ecmaVersion": 2017
    },
    "extends": "eslint:recommended",
    "rules": {
        "indent": [
            "error",
            "tab"
        ],
        "linebreak-style": [
            "error",
            "unix"
        ],
        "quotes": [
            "error",
            "single"
        ],
        "semi": [
            "error",
            "always"
        ],
        "no-console": OFF
    },
    "globals": {
        // Testing globals
        "describe": true,
        "it": true,
        "expect": true
    }
};

const OFF = 0;

module.exports = {
    "env": {
        "node": true,
        "es6": true
    },
    "parserOptions": {
        "ecmaVersion": 2017
    },
    "extends": "eslint:recommended",
    "rules": {
        "indent": [
            "error",
            "tab"
        ],
        "linebreak-style": [
            "error",
            "unix"
        ],
        "quotes": [
            "error",
            "single"
        ],
        "semi": [
            "error",
            "always"
        ],
        "no-console": OFF
    },
    "globals": {
        // Testing globals
        "describe": true,
        "it": true,
        "expect": true
    }
};

Update eslint to support ES6 globals (like `Promise`)


'use strict'

// let logger = require('tracer').colorConsole()
let registry = require('../../extensions/registry')
let basic = require('./basic-response-helper')

module.exports = {

  setStageConfig: function setStageConfig(req, res) {
    basic.insertRespond(req, res, 'pipeline_stage_configs')
  },

  deleteStageConfig: function setStageConfig(req, res) {
    basic.deleteRespond(req, res, 'pipeline_stage_configs')
  },

  getAvailableTypes: function getAvailableTypes(req, res) {
    res.status(201).send({data: registry.getStageTypes()})
  },

  /**
   * Get stages for a specific pipeline
   *
   * @param req
   * @param res
   */
  getListForPipeline: function getListForPipeline(req, res) {

    basic.getListCustom(req, res, 'pipeline_stage_configs', query => {
      return query.where('pipeline_config_id', req.params.id)
    })

  }

}

'use strict'

// let logger = require('tracer').colorConsole()
let registry = require('../../extensions/registry')
let basic = require('./basic-response-helper')

module.exports = {

  setStageConfig: function setStageConfig(req, res) {
    basic.insertRespond(req, res, 'pipeline_stage_configs')
  },

  updateStageConfig: function updateStageConfig(req, res) {
    basic.patchRespond(req, res, 'pipeline_stage_configs')
  },

  deleteStageConfig: function deleteStageConfig(req, res) {
    basic.deleteRespond(req, res, 'pipeline_stage_configs')
  },

  getAvailableTypes: function getAvailableTypes(req, res) {
    res.status(201).send({data: registry.getStageTypes()})
  },

  /**
   * Get stages for a specific pipeline
   *
   * @param req
   * @param res
   */
  getListForPipeline: function getListForPipeline(req, res) {

    basic.getListCustom(req, res, 'pipeline_stage_configs', query => {
      return query.where('pipeline_config_id', req.params.id)
    })

  }

}

Add controller to handle patching stage configs


import devtools from '../devtools';
import { REALLY_BIG_NUMBER } from '../utils';

import merge from 'deepmerge';

import PineCone from '../sprites/object/PineCone';

const itemMax = 50;

let items = {
  'wood-axe': {
    value: true,
    sellable: false,
  },
  bucket: {
    value: false,
    sellable: false,
  },
  water: {
    value: 0,
    max: itemMax,
    sellable: false,
  },
  log: {
    value: 0,
    max: itemMax,
    sellable: true,
  },
  'pine-cone': {
    value: 0,
    max: itemMax,
    sellable: true,
    place: PineCone,
  },
};

let money = {
  value: 10000,
  max: REALLY_BIG_NUMBER,
};

items = merge(items, devtools.enabled ? devtools.items : {});
money = merge(money, devtools.enabled ? devtools.money : {});

export { items, money };

import devtools from '../devtools';
import { REALLY_BIG_NUMBER } from '../utils';

import merge from 'deepmerge';

import PineCone from '../sprites/object/PineCone';

const itemMax = 500;

let items = {
  'wood-axe': {
    value: true,
    sellable: false,
  },
  bucket: {
    value: false,
    sellable: false,
  },
  water: {
    value: 0,
    max: itemMax,
    sellable: false,
  },
  log: {
    value: 0,
    max: itemMax,
    sellable: true,
  },
  'pine-cone': {
    value: 0,
    max: itemMax,
    sellable: true,
    place: PineCone,
  },
};

let money = {
  value: 10000,
  max: REALLY_BIG_NUMBER,
};

items = merge(items, devtools.enabled ? devtools.items : {});
money = merge(money, devtools.enabled ? devtools.money : {});

export { items, money };

Increase item max to 500


var animation = require("alloy/animation");

function IndexOpen(e) {

    $.logo.init({ image: '/images/alloy.png', width: 216, height: 200, opacity: 0.1 });
    $.buttongrid.relayout(e);
}

var red = "#900A05";
var brightred = "#B00C07";
var black = "#000000";
var gray = '#888888';

$.buttongrid.init({
    buttons: [
        { id: 'Cloudy', title: "Cloudy", backgroundColor: black, backgroundSelectedColor: gray },
        { id: 'Drizzle', title: "Drizzle" },
        { id: 'Haze', title: 'Haze' },
        { id: 'MostlyCloudy', title: "Mostly Cloudy" },
        { id: 'SlightDrizzle' },
        { id: 'Snow', title: 'Snow' },
        { id: 'Sunny', title: 'Sunny' },
        { id: 'Thunderstorms', title: 'Thunderstorms', click: function (e) { alert("Thunder!"); } }
    ],
    buttonWidth: Alloy.isTablet ? 200: 100,
    buttonHeight: Alloy.isTablet ? 200 : 100,
    backgroundColor: red,
    backgroundSelectedColor: brightred,
    duration: 1000
});

$.index.open();

var animation = require('alloy/animation');

function IndexOpen(e) {

    $.logo.init({ image: '/images/alloy.png', width: 216, height: 200, opacity: 0.1 });
    $.buttongrid.relayout(e);
}

var red = '#900A05';
var brightred = '#B00C07';
var black = '#000000';
var gray = '#888888';

$.buttongrid.init({
    buttons: [
        { id: 'Cloudy', title: 'Cloudy', backgroundColor: black, backgroundSelectedColor: gray },
        { id: 'Drizzle', title: 'Drizzle' },
        { id: 'Haze', title: 'Haze' },
        { id: 'MostlyCloudy', title: 'Mostly Cloudy' },
        { id: 'SlightDrizzle', title: 'Slight Drizzle' },
        { id: 'Snow', title: 'Snow' },
        { id: 'Sunny', title: 'Sunny' },
        { id: 'Thunderstorms', title: 'Thunderstorms', click: function (e) { alert('Thunder!'); } }
    ],
    buttonWidth: Alloy.isTablet ? 200: 100,
    buttonHeight: Alloy.isTablet ? 200 : 100,
    backgroundColor: red,
    backgroundSelectedColor: brightred,
    duration: 1000
});

$.index.open();

Fix missing label on button


#!/usr/bin/env node

'use strict'

var open = require('opn')
var meow = require('meow')
var extend = require('xtend')
var degree = require('./')

var cli = meow({
  help: [
    'Usage',
    '  degree [entries...]'
  ]
})

var entries = cli.input
if (!entries.length) throw new Error('entries are required')

degree(entries, extend(cli.flags, {stream: process.stdout}))
  .on('connect', function (server) {
    var uri = server.uri
    console.log('Server running at', uri)
    open(uri)
  })

Remove server log statement (handled by budo)


import React, {Component, View, ListView} from 'react-native';
import PinListItem from './PinListItem.js';

export default class PinList extends Component {

  constructor(props) {
    super(props);
    this.state = {
      // create the data source
      dataSource: new ListView.DataSource({
        rowHasChanged: (r1, r2) => r1 !== r2
      })
    };
  }

  componentWillReceiveProps(nextProps) {
    this.setState({
      dataSource: this.state.dataSource.cloneWithRows(nextProps.pins)
    });
  }

  renderItem(pin) {
    const {deletePin} = this.props;
    return (
        // pass down pin info to PinListItem
        <PinListItem
          pin={pin}
          deletePin={deletePin}
        />
      );
  }

  render() {
    return (
      <ListView
        dataSource={this.state.dataSource}
        renderRow={this.renderItem.bind(this)}
      />
    );
  }
}

import React, {Component, View, ListView} from 'react-native';
import PinListItem from './PinListItem.js';

export default class PinList extends Component {

  constructor(props) {
    super(props);
    this.state = {
      // create the data source
      dataSource: new ListView.DataSource({
        rowHasChanged: (r1, r2) => r1 !== r2
      })
    };
  }

  componentWillReceiveProps(nextProps) {
    this.setState({
      dataSource: this.state.dataSource.cloneWithRows(nextProps.pins)
    });
  }

  componentWillMount() {
    this.setState({
      dataSource: this.state.dataSource.cloneWithRows(this.props.pins)
    });
  }

  renderItem(pin) {
    const {deletePin} = this.props;
    return (
        // pass down pin info to PinListItem
        <PinListItem
          pin={pin}
          deletePin={deletePin}
        />
      );
  }

  render() {
    return (
      <ListView
        dataSource={this.state.dataSource}
        renderRow={this.renderItem.bind(this)}
      />
    );
  }
}

Clone datasource in ComponentWillMount to rerendender ListView


import Ember from 'ember';

export default Ember.Controller.extend({
  trey: {
    musicMoney: 537,
    hostingMoney: 38,
    hours: 240,
  },
});

import Ember from 'ember';

export default Ember.Controller.extend({
  trey: {
    musicMoney: 547,
    hostingMoney: 38,
    hours: 200 + 79, // Estimated 200 hours spent before I started keeping track
  },
});

Update hours and money spent


import { currentStoreView } from '@vue-storefront/core/lib/multistore'
import dayjs from 'dayjs'
import dayjsLocalizedFormat from 'dayjs/plugin/localizedFormat'
import { once } from '../helpers';

once('__VUE_EXTEND_DAYJS_LOCALIZED_FORMAT__', () => {
  dayjs.extend(dayjsLocalizedFormat)
})

/**
 * Converts date to format provided as an argument or defined in config file (if argument not provided)
 * @param {String} date
 * @param {String} format
 */
export function date (date, format) {
  const displayFormat = format || currentStoreView().i18n.dateFormat
  let storeLocale = currentStoreView().i18n.defaultLocale.toLocaleLowerCase()
  const separatorIndex = storeLocale.indexOf('-')
  const languageCode = separatorIndex ? storeLocale.substr(0, separatorIndex) : storeLocale

  const isStoreLocale = dayjs().locale(storeLocale).locale()
  const isLanguageLocale = dayjs().locale(languageCode).locale()
  const locale = isStoreLocale || isLanguageLocale

  if (locale) return dayjs(date).locale(languageCode).format(displayFormat)
  return dayjs(date).format(displayFormat)
}

import { currentStoreView } from '@vue-storefront/core/lib/multistore'
import dayjs from 'dayjs'
import dayjsLocalizedFormat from 'dayjs/plugin/localizedFormat'
import { once } from '../helpers';

once('__VUE_EXTEND_DAYJS_LOCALIZED_FORMAT__', () => {
  dayjs.extend(dayjsLocalizedFormat)
})

/**
 * Converts date to format provided as an argument or defined in config file (if argument not provided)
 * @param {String} date
 * @param {String} format
 */
export function date (date, format) {
  const displayFormat = format || currentStoreView().i18n.dateFormat
  let storeLocale = currentStoreView().i18n.defaultLocale.toLocaleLowerCase()
  const separatorIndex = storeLocale.indexOf('-')
  const languageCode = (separatorIndex > -1) ? storeLocale.substr(0, separatorIndex) : storeLocale

  const isStoreLocale = dayjs().locale(storeLocale).locale()
  const isLanguageLocale = dayjs().locale(languageCode).locale()
  const locale = isStoreLocale || isLanguageLocale

  if (locale) return dayjs(date).locale(languageCode).format(displayFormat)
  return dayjs(date).format(displayFormat)
}

Check separatorIndex is > -1 instead of truthiness


#!/usr/bin/env node
'use strict';
var chalk = require('chalk');
var envcheck = require('./index');

if (process.argv.indexOf('-h') !== -1 || process.argv.indexOf('--help') !== -1) {
	return console.log('Usage\n  ' + chalk.blue('envcheck') + '\n\nRuns checks against the environment');
}

if (process.argv.indexOf('-v') !== -1 || process.argv.indexOf('--version') !== -1) {
	return console.log(require('./package').version);
}

envcheck(function (err, results) {
	if (err) {
		throw err;
	}

	var fail = false;

	console.log(chalk.underline('\nEnvironment check\n') + results.map(function (el) {
		if (el.fail) {
			fail = true;
			return chalk.red('✘ ' + el.title) + (el.message ? ' - ' + el.message : '');
		}

		return chalk.green('✔ ' + el.title) + (el.message ? ' - ' + el.message : '');
	}).join('\n'));

	process.exit(fail ? 1 : 0);
});

#!/usr/bin/env node
'use strict';
var chalk = require('chalk');
var envcheck = require('./index');

if (process.argv.indexOf('-h') !== -1 || process.argv.indexOf('--help') !== -1) {
	return console.log('Usage\n  ' + chalk.blue('envcheck') + '\n\nRuns checks against the environment');
}

if (process.argv.indexOf('-v') !== -1 || process.argv.indexOf('--version') !== -1) {
	return console.log(require('./package').version);
}

envcheck(function (err, results) {
	if (err) {
		throw err;
	}

	var fail = false;

	console.log(chalk.underline('\nEnvironment check\n') + results.map(function (el) {
		if (el.fail) {
			fail = true;
			return chalk.red('✘ ') + el.title + (el.message ? ' - ' + el.message : '');
		}

		return chalk.green('✔ ') + el.title + (el.message ? ' - ' + el.message : '');
	}).join('\n'));

	process.exit(fail ? 1 : 0);
});

Make only the status symbol colored


var argv = require('minimist')(process.argv.slice(2))
var execSync = require('child_process').execSync

if (argv.h || argv.help || argv._.length === 0) {
  console.log('Usage: generate-static name [html]')
  process.exit(0)
}

var imagename = argv._.shift()
var html = argv._.shift() || '
Hello Docker
'
console.log('Image:', imagename)
console.log('HTML:', html)

var command = 'docker pull nginx:latest'
console.log(command)
var pull = execSync(command).toString()
console.log(pull)

command = 'docker run -d nginx:latest'
console.log(command)
var id = execSync(command).toString().replace('\n', '')
console.log('id', id)

var index = 'echo "' + html + '" > /usr/share/nginx/html/index.html'
command = 'docker exec ' + id + ' /bin/sh -c "' + index.replace(/"/g, '\\"') + '"'
console.log(command)
var done = execSync(command).toString()
console.log(done)

command = 'docker commit ' + id + ' ' + imagename
console.log(command)
var container = execSync(command).toString()
console.log(container)

console.log('Docker image is now generated:')
console.log(imagename)

var argv = require('minimist')(process.argv.slice(2))
var execSync = require('child_process').execSync

if (argv.h || argv.help || argv._.length === 0) {
  console.log('Usage: generate-static name [html]')
  process.exit(0)
}

var imagename = argv._.shift()
var html = argv._.shift() || '
Hello Docker
'
console.log('Image:', imagename)
console.log('HTML:', html)

var command = 'docker pull nginx:latest'
console.log(command)
var pull = execSync(command).toString()
console.log(pull)

command = 'docker run -d nginx:latest'
console.log(command)
var id = execSync(command).toString().replace('\n', '')
console.log('id', id)

var index = 'echo "' + html + '" > /usr/share/nginx/html/index.html'
command = 'docker exec ' + id + ' /bin/sh -c "' + index.replace(/"/g, '\\"') + '"'
console.log(command)
var done = execSync(command).toString()
console.log(done)

command = 'docker commit ' + id + ' ' + imagename
console.log(command)
var container = execSync(command).toString()
console.log(container)

command = 'docker rm -f ' + id
console.log(command)
var rm = execSync(command).toString()
console.log(rm)

console.log('\nDocker image is now generated:')
console.log(imagename)

Remove the container after the image has been created


var org = require('org');

var parser = new org.Parser();
console.log('plugin loading');

exports.translate = function(load) {
    console.log('translate');
    return parser.parse(load.source);
};

exports.instantiate = function(load) {
    console.log('instantiate');
    return load.source;
};

var org = require('org');

var parser = new org.Parser();

exports.translate = function(load) {
    load.metadata.content = parser.parse(load.source);
};

exports.instantiate = function(load) {
    return load.metadata.content;
};

Return parsed object and removed log statements


/**
 * bit.ly access library code.
 */
var Shortener = new JS.Singleton('Shortener', {
	shorten: function() {
	  var uri, callback;

	  if (typeof arguments[0] == 'string') {
	    uri      = arguments[0];
	    callback = arguments[1];
	  } else {
	    uri      = window.location.href;
	    callback = arguments[0];
	  }

		var name = this._registerCallback(callback);

		this.CLIENT.call('shorten', {
			'longUrl': uri
		}, 'BitlyCB.' + name);
	},

	_dispatcher: function(callback, data) {
		var result;

    // Results are keyed by longUrl, so we need to grab the first one.
    for (var r in data.results) {
			result = data.results[r];
			break;
    }

		callback(result, data);
	},

	_registerCallback: function(callback) {
		var name = 'abbr' + this.callbackCount;
		this.callbackCount += 1;

		this.NAMESPACE[name] = function(data) {
			this._dispatcher(callback, data);
		}.bind(this);

		return name;
	},

	callbackCount: 0,

	CLIENT: BitlyClient,
	NAMESPACE: BitlyCB
});

/**
 * bit.ly access library code.
 */
var Shortener = new JS.Singleton('Shortener', {
  initialize: function() {
    this.extend({
      CLIENT:    BitlyClient || null,
      NAMESPACE: BitlyCB     || null
    });
  },

	shorten: function() {
	  var uri, callback;

	  if (typeof arguments[0] == 'string') {
	    uri      = arguments[0];
	    callback = arguments[1];
	  } else {
	    uri      = window.location.href;
	    callback = arguments[0];
	  }

		var name = this._registerCallback(callback);

		if (!(name && this.CLIENT)) return;

		this.CLIENT.call('shorten', {
			'longUrl': uri
		}, 'BitlyCB.' + name);
	},

	_dispatcher: function(callback, data) {
		var result;

    // Results are keyed by longUrl, so we need to grab the first one.
    for (var r in data.results) {
			result = data.results[r];
			break;
    }

		callback(result, data);
	},

	_registerCallback: function(callback) {
	  if (!this.NAMESPACE) return false;

		var name = 'abbr' + this.callbackCount;
		this.callbackCount += 1;

		this.NAMESPACE[name] = function(data) {
			this._dispatcher(callback, data);
		}.bind(this);

		return name;
	},

	callbackCount: 0
});

Add some error handling in case there's no connection to the bit.ly servers.


/* eslint-disable import/no-import-module-exports */

import React, { StrictMode } from 'react';
import ReactDOM from 'react-dom';
import { QueryClientProvider, QueryClient } from '@tanstack/react-query';

import Analytics from './components/Analytics';
import Routes from './components/Routes';
import { setup as sentrySetup } from './services/errors';

sentrySetup();
const queryClient = new QueryClient();

const rootEl = document.getElementById('main');
const render = () => {
  ReactDOM.render(
    <StrictMode>
      <QueryClientProvider client={queryClient}>
        <Analytics />
        <Routes />
      </QueryClientProvider>
    </StrictMode>,
    rootEl,
  );
};

if (module.hot) {
  module.hot.accept('./components/Routes', () => {
    render();
  });
}

render();

/* eslint-disable import/no-import-module-exports */

import React, { StrictMode } from 'react';
import { createRoot } from 'react-dom/client';
import { QueryClientProvider, QueryClient } from '@tanstack/react-query';

import Analytics from './components/Analytics';
import Routes from './components/Routes';
import { setup as sentrySetup } from './services/errors';

sentrySetup();
const queryClient = new QueryClient();

const rootEl = document.getElementById('main');
const root = createRoot(rootEl);
const render = () => {
  root.render(
    <StrictMode>
      <QueryClientProvider client={queryClient}>
        <Analytics />
        <Routes />
      </QueryClientProvider>
    </StrictMode>,
  );
};

if (module.hot) {
  module.hot.accept('./components/Routes', () => {
    render();
  });
}

render();

Update to createRoot. This breaks @reach/router routing w. StrictMode


import readInput from './readInput.js';
import {PieceBlock} from './PieceBlock.js';
import {StateQueue} from './StateQueue.js';

// Read Inputs (Synchronous Read)
// [0] - Cube Dimension , [1] Number of Pieces , [2+] Cube Positions as Seperate 2D arrays
// -----------------------------------------------------------------------------
let piecesData = readInput('./src/Input.txt');
let Pieces = [];
//console.log(piecesData);

// Create PieceBlock objects with names A, B, ... , G
// -----------------------------------------------------------------------------
for (let i = 0; i < piecesData[1]; i++) {
  Pieces.push(new PieceBlock(piecesData[i + 2], String.fromCharCode(65 + i)));
}

// Create State Depth First Searcher Object
// -----------------------------------------------------------------------------
console.time('solver');
let cubeDim = piecesData[0];
let totalPieces = piecesData[1];
let states = new StateQueue(cubeDim, totalPieces, Pieces);
let solvedState = states.stateDepthFirstSearch(null);
console.log(solvedState.cubeMat);
console.timeEnd('solver');

import readInput from './readInput.js';
import {PieceBlock} from './PieceBlock.js';
import {StateQueue} from './StateQueue.js';

// Read Inputs (Synchronous Read)
// [0] - Cube Dimension , [1] Number of Pieces , [2+] Cube Positions as Seperate 2D arrays
// -----------------------------------------------------------------------------
let piecesData = readInput('./src/Input.txt');
let Pieces = [];
//console.log(piecesData);

// Create PieceBlock objects with names A, B, ... , G
// -----------------------------------------------------------------------------
for (let i = 0; i < piecesData[1]; i++) {
  Pieces.push(new PieceBlock(piecesData[i + 2], String.fromCharCode(65 + i)));
}

// Sort by number of unique isometries
Pieces.sort((a,b) => (b.uniqueIsos.length - a.uniqueIsos.length));

// Create State Depth First Searcher Object
// -----------------------------------------------------------------------------
console.time('solver');
let cubeDim = piecesData[0];
let totalPieces = piecesData[1];
let states = new StateQueue(cubeDim, totalPieces, Pieces);
let solvedState = states.stateDepthFirstSearch(null);
console.log(solvedState.cubeMat);
console.timeEnd('solver');

Reorder pieces by largest to smallest unique number of isometries.


#!/usr/bin/env node --harmony

import spawn from "cross-spawn";
import os from "os";
import { exec } from "child_process";
import exit from 'exit';

function normalize( args, isWindows ) {
    return args.map( arg => {
        Object.keys( process.env ).forEach( key => {
            const regex = new RegExp( `\\$${ key }|%${ key }%`, "i" );
            arg = arg.replace( regex, process.env[ key ] );
        } );
        return arg;
    } )
}

let args = process.argv.slice( 2 );
if ( args.length === 1 ) {
    const [ command ] = normalize( args );
    const proc = exec( command, ( error, stdout, stderr ) => {
        if ( error ) {
            console.error( `exec error: ${ error }` );
            return;
        }
        process.stdout.write( stdout );
        process.stderr.write( stderr );
        exit(proc.code);
    });
} else {
    args = normalize( args );
    const command = args.shift();
    const proc = spawn.sync( command, args, { stdio: "inherit" } );
    exit(proc.status);
}

#!/usr/bin/env node --harmony

import spawn from "cross-spawn";
import os from "os";
import { exec } from "child_process";
import exit from 'exit';

function normalize( args, isWindows ) {
    return args.map( arg => {
        Object.keys( process.env )
            .sort( ( x, y ) => x.length < y.length ) // sort by descending length to prevent partial replacement
            .forEach( key => {
                const regex = new RegExp( `\\$${ key }|%${ key }%`, "i" );
                arg = arg.replace( regex, process.env[ key ] );
            } );
        return arg;
    } )
}

let args = process.argv.slice( 2 );
if ( args.length === 1 ) {
    const [ command ] = normalize( args );
    const proc = exec( command, ( error, stdout, stderr ) => {
        if ( error ) {
            console.error( `exec error: ${ error }` );
            return;
        }
        process.stdout.write( stdout );
        process.stderr.write( stderr );
        exit(proc.code);
    });
} else {
    args = normalize( args );
    const command = args.shift();
    const proc = spawn.sync( command, args, { stdio: "inherit" } );
    exit(proc.status);
}

Sort env vars by descending length to prevent partial replacement


var config = {
  // Default server port
  port: 8080,

  // Base static path
  base: '/static',

  // Node handlers route
  routes: {},

  // Now handlers list
  everyone: [ require('./handlers/task.js').everyone ],

  // MongoDB configuration
  mongo: {
    server: '127.0.0.1',
    port: 27017,
    serverOption: {},
    databaseOption: {},
    database: 'Scrum'
  },

  // Log facility
  log: {
    appenders: [ { type: 'file',
                   filename: 'scrum.log',
                   maxLogSize: 102400,
                   backups: 3,
                   pollInterval: 15 },
                 { type: 'console' } ],
    levels: {
      console: 'info'
    }
  }
}

exports.config = config;

var config = {
  // Default server port
  port: 8080,

  // Base static path
  base: '/static',

  // Node handlers route
  routes: {},

  // Now handlers list
  everyone: [ require('./handlers/task.js').everyone ],

  // MongoDB configuration
  mongo: {
    server: '127.0.0.1',
    port: 27017,
    serverOption: {},
    databaseOption: {},
    database: 'Scrum'
  },

  // Log facility
  log: {
    appenders: [ { type: 'file',
                   filename: 'scrum.log',
                   maxLogSize: 10485760,
                   backups: 3,
                   pollInterval: 15 },
                 { type: 'console' } ],
    levels: {
      console: 'info'
    }
  }
}

exports.config = config;

Increase log size to 10 MB


command: 'echo ""',
refreshFrequency: 3600000,

style: [
	'@import "/common/base.css"',
	'@import "/common/fira/fira.css"',
].join('\n'),

render: function(output) {
	return ''
},

command: 'echo ""',
refreshFrequency: 3600000,

render: function(output) {
	return [
		'<link rel="stylesheet" type="text/css" href="/common/base.css">',
		'<link rel="stylesheet" type="text/css" href="/common/fira/fira.css">',
	].join('\n')
},

Use links to load the css files


'use strict';

/**
 * Module dependencies.
 */
var mongoose = require('mongoose'),
	Schema = mongoose.Schema;

/**
 * Activity Schema
 */
var ActivitySchema = new Schema({
	name: {
		type: String,
		default: '',
		required: 'Please fill Activity name',
		trim: true /*Removes Whitespace from Beginning and End from String */
	},
	description: {
		type: String,
		default: '',
		required: 'Please fill Activity description',
		trim: true /*Removes Whitespace from Beginning and End from String */
	},
	created: {
		type: Date,
		default: Date.now
	},
	user: {
		type: Schema.ObjectId,
		ref: 'User'
	},
	entries: [
		{
			entryText: {
				type: String,
				trim: true
			},
			entryDate: {
				type: Date,
				default: Date.now()
			},
			entryDatePicker: {
				type: Date,
				default: Date.now()
			},
			entryDuration: {
				type: Number,
				trim: true,
				default: 0
			},
			entryDescription: {
				type: String,
				default: 'empty',
				trim: true
			}
		}
	]
});

mongoose.model('Activity', ActivitySchema);

'use strict';

/**
 * Module dependencies.
 */
var mongoose = require('mongoose'),
	Schema = mongoose.Schema;

/**
 * Activity Schema
 */
var ActivitySchema = new Schema({
	name: {
		type: String,
		default: '',
		required: 'Please fill Activity name',
		trim: true /*Removes Whitespace from Beginning and End from String */
	},
	color: {
		type: String,
		trim: true,
		default: '#FFFFFF'
	},
	description: {
		type: String,
		default: '',
		required: 'Please fill Activity description',
		trim: true /*Removes Whitespace from Beginning and End from String */
	},
	created: {
		type: Date,
		default: Date.now
	},
	user: {
		type: Schema.ObjectId,
		ref: 'User'
	},
	entries: [
		{
			entryText: {
				type: String,
				trim: true
			},
			entryDate: {
				type: Date,
				default: Date.now()
			},
			entryDatePicker: {
				type: Date,
				default: Date.now()
			},
			entryDuration: {
				type: Number,
				trim: true,
				default: 0
			},
			entryDescription: {
				type: String,
				default: 'empty',
				trim: true
			}
		}
	]
});

mongoose.model('Activity', ActivitySchema);

Change Activity Model to support background color