const chai = require('chai')
const fs = require('fs')
const path = require('path')

const should = chai.should()
const expect = chai.expect
const assert = chai.assert
const transformer = require('../../../build/api/helpers/transform-legacy-xml-to-json').default

const mockXmlLocation = path.join(__dirname, '../../../src/api/mocks/example-xml.xml')
process.env.NODE_ENV = 'test'

describe('Helper hmac validation', () => {
  describe('Returns', _ => {
    it('True when the validation matches on sha256', _ => {
    })

    it('False when the validation does not match on sha256', _ => {
    })
  })
})

const chai = require('chai')
const fs = require('fs')
const crypto = require('crypto')

const should = chai.should()
const expect = chai.expect
const assert = chai.assert
const isHmacValid = require('../../../build/api/helpers/hmac-validator').default

process.env.NODE_ENV = 'test'

describe('Helper hmac validation', () => {
  const merchantSecret = 'magic-unicorns-of-fantasy-shoot-fire-and-steam-from-their-eyes'

  describe('True when', _ => {
    let clientHmac
    let payload = 'testing+foo+bar'

    before(done => {
      clientHmac = crypto
        .createHmac('sha256', merchantSecret)
        .update(new Buffer(JSON.stringify(payload)).toString('base64'))
        .digest('hex')
        .toUpperCase()
      done()
    })

    it('Validation matches on sha256 with correct merchant shared secret', () => assert.isTrue(
      isHmacValid(clientHmac, merchantSecret, payload)
    ))
  })

  describe('False when the validation', () => {
    let clientHmac
    let payload = 'testing+foo+bar'
    let wrongPayload = 'testing-foo-bar'
    let wrongMerchantSecret = 'magic-unicorns-of-fantasy-do-not-shoot-fire-and-steam-from-their-eyes'

    before(done => {
      clientHmac = crypto
        .createHmac('sha256', merchantSecret)
        .update(new Buffer(JSON.stringify(payload)).toString('base64'))
        .digest('hex')
        .toUpperCase()
      done()
    })

    it('Does not match on sha256 with incorrect merchant shared secret', () => assert.isFalse(
      isHmacValid(clientHmac, wrongMerchantSecret, payload)
    ))

    it('Does not match on sha256 with incorrect payload', () => assert.isFalse(
      isHmacValid(clientHmac, merchantSecret, wrongPayload)
    ))
  })
})

Write tests for hmac validator


var utils = require('utilities')
  , fs = require('fs');

exports.init = function (app, callback) {
  var modelDir = utils.file.readdirR('app/models')
    , files = []
    , file
    , content
    , built;
  for (var i in modelDir) {
    file = modelDir[i].replace('app/models', '').replace('/', '');
    if (file) {
      content = fs.readFileSync(modelDir[i], 'utf8');
      files.push("(function(){\n" + content + "}());");
    }
  }
  built = files.join('\n\n');
  fs.writeFileSync('public/js/core/models.js', built);
  return callback();
};

var utils = require('utilities')
  , fs = require('fs');

exports.init = function (app, callback) {
  var modelDir = utils.file.readdirR('app/models')
    , files = []
    , file
    , content
    , built;
  for (var i in modelDir) {
    file = modelDir[i].replace('app/models', '').replace('/', '');
    if (file) {
      content = fs.readFileSync(modelDir[i], 'utf8');
      files.push("(function(){\n" + content + "}());");
    }
  }
  built = files.join('\n\n');
  geddy.file.mkdirP('public/js/core/');
  fs.writeFileSync('public/js/core/models.js', built);
  return callback();
};

Make the public js core directory if it doesn't exist


const fs = require('fs')

const LANGUAGE = 'postfix'
const ACTIVATION_EVENT = `onLanguage:${LANGUAGE}`
const task = process.argv.length > 2 && process.argv[2]

const tasksMap = new Map([
	['prerun', prerun],
	['pretest', pretest]
])

function prerun() {
	let pkg = readPackageJson()
	delete pkg.contributes.languages
	pkg.activationEvents = [
		'onLanguage:javascript',
		'onLanguage:typescript',
		'onLanguage:javascriptreact',
		'onLanguage:typescriptreact'
	]
	pkg.main = './out/extension'
	writePackageJson(pkg)
}

function pretest() {
	let pkg = readPackageJson()
	pkg.contributes.languages = [{id: LANGUAGE}]
	pkg.activationEvents = [ACTIVATION_EVENT]
	// Don't use bundler for tests as it breaks template usage tests
	pkg.main = './out/src/extension'
	writePackageJson(pkg)
}

const writePackageJson = (content) => fs.writeFileSync('./package.json', JSON.stringify(content, undefined, '\t'))
const readPackageJson = () => require('./package.json')

const taskToExecute = tasksMap.get(task)
taskToExecute && taskToExecute()

const fs = require('fs')

const LANGUAGE = 'postfix'
const task = process.argv.length > 2 && process.argv[2]

const tasksMap = new Map([
	['prerun', prerun],
	['pretest', pretest]
])

function prerun() {
	let pkg = readPackageJson()
	delete pkg.contributes.languages
	pkg.activationEvents = [
		'onLanguage:javascript',
		'onLanguage:typescript',
		'onLanguage:javascriptreact',
		'onLanguage:typescriptreact'
	]
	pkg.main = './out/extension'
	writePackageJson(pkg)
}

function pretest() {
	let pkg = readPackageJson()
	pkg.contributes.languages = [{id: LANGUAGE}]
	// Activate the extension right after start to avoid delay and failure in first test
	pkg.activationEvents = ['onStartupFinished']
	// Don't use bundler for tests as it breaks template usage tests
	pkg.main = './out/src/extension'
	writePackageJson(pkg)
}

const writePackageJson = (content) => fs.writeFileSync('./package.json', JSON.stringify(content, undefined, '\t'))
const readPackageJson = () => require('./package.json')

const taskToExecute = tasksMap.get(task)
taskToExecute && taskToExecute()

Fix extension activation in tests


//= require "lib/moment.min"
//= require "lib/jquery-2.0.3"
//= require "lib/handlebars-v1.1.2"
//= require "lib/ember"
//= require "lib/ember-data"
//= require_self
//= require "models"
//= require "views"
//= require "helpers"
//= require "./routes/authenticated_route"
//= require_tree "./controllers"
//= require_tree "./routes"

window.App = Em.Application.create({LOG_TRANSITIONS: true})

App.ApplicationSerializer = DS.ActiveModelSerializer.extend({})
App.ApplicationAdapter    = DS.RESTAdapter.reopen({namespace: "api"})
App.ApplicationView       = Em.View.extend({classNames: ["container"]})

App.Router.map(function() {
  // login
  this.route("login");

  // rooms
  this.resource("rooms");

  // users
  // users/new
  // users/:user_id
  this.resource("users", function() {
    this.route("new");

    this.resource("user", {path: "/:user_id"}, function() {
      this.route("edit");
    });

  });

});

//= require "lib/moment.min"
//= require "lib/jquery-2.0.3"
//= require "lib/handlebars-v1.1.2"
//= require "lib/ember"
//= require "lib/ember-data"
//= require_self
//= require "models"
//= require "views"
//= require "helpers"
//= require "./routes/authenticated_route"
//= require_tree "./controllers"
//= require_tree "./routes"

window.App = Em.Application.create({LOG_TRANSITIONS: true})

App.ApplicationSerializer = DS.ActiveModelSerializer.extend({})
App.ApplicationAdapter    = DS.RESTAdapter.reopen({namespace: "api"})
App.ApplicationView       = Em.View.extend({classNames: ["container"]})

App.Router.map(function() {
  // login
  this.route("login");

  // rooms
  this.resource("rooms", function() {
    this.route("new");
    this.resource("room", {path: "/:room_id"}, function() {
      this.route("edit");
    });
  });

  // users
  // users/new
  // users/:user_id
  this.resource("users", function() {
    this.route("new");
    this.resource("user", {path: "/:user_id"}, function() {
      this.route("edit");
    });

  });

});

Add detailed routes for Rooms


var GameOfLife = GameOfLife || {};

GameOfLife.Boot = function () {};

GameOfLife.Boot.prototype = {
  create: function() {
    this.scale.scaleMode = Phaser.ScaleManager.SHOW_ALL;
    this.game.state.start("load");
  }
};

var GameOfLife = GameOfLife || {};

GameOfLife.Boot = function () {};

GameOfLife.Boot.prototype = {
  create: function() {
    this.scale.scaleMode = Phaser.ScaleManager.SHOW_ALL;
    this.scale.maxWidth = 600;
    this.game.state.start("load");
  }
};

Add max width in phaser scale


import React from 'react';
import cx from 'classnames';
import { area } from 'd3-shape';

export default function AreaClosed({
  x,
  y,
  xScale,
  yScale,
  data,
  defined,
  className,
  strokeDasharray,
  strokeWidth = 2,
  stroke = 'black',
  fill = rgba(0,0,0,0.3),
  curve,
  ...restProps,
}) {
  const path = area()
    .x(d => xScale(x(d)))
    .y0(yScale(yScale.domain()[0]))
    .y1(d => yScale(y(d)))
    .defined(defined || (d => y(d) && x(d)));
  if (curve) path.curve(curve);
  return (
    <g>
      <path
        className={cx('vx-area-closed', className)}
        d={path(data)}
        stroke={stroke}
        strokeWidth={strokeWidth}
        strokeDasharray={strokeDasharray}
        fill={fill}
        {...restProps}
      />
    </g>
  );
}

import React from 'react';
import cx from 'classnames';
import { area } from 'd3-shape';

export default function AreaClosed({
  x,
  y,
  xScale,
  yScale,
  data,
  defined,
  className,
  strokeDasharray,
  strokeWidth = 2,
  stroke = 'black',
  fill = rgba(0,0,0,0.3),
  curve,
  ...restProps,
}) {
  const path = area()
    .x(d => xScale(x(d)))
    .y0(yScale.range()[0])
    .y1(d => yScale(y(d)))
    .defined(defined || (d => y(d) && x(d)));
  if (curve) path.curve(curve);
  return (
    <g>
      <path
        className={cx('vx-area-closed', className)}
        d={path(data)}
        stroke={stroke}
        strokeWidth={strokeWidth}
        strokeDasharray={strokeDasharray}
        fill={fill}
        {...restProps}
      />
    </g>
  );
}

Simplify getting the min range


var context = sessionStorage.getItem('context');
var rowThumbnails = document.querySelector('.row-thumbnails');

rowThumbnails.addEventListener("click", moeRedirect, false);

function moeRedirect(event) {

  var target = event.target;

  var notImage = target.className !== 'thumbnail-image';
  if(notImage) return;

  var pageId = target.getAttribute('data-page-id');

  window.location.href = context + '/page/' + pageId;

}

/* Cached Variables *******************************************************************************/
var context = sessionStorage.getItem('context');
var rowThumbnails = document.querySelector('.row-thumbnails');

/* Initialization *********************************************************************************/
rowThumbnails.addEventListener("click", moeRedirect, false);

function moeRedirect(event) {

  var target = event.target;

  var notImage = target.className !== 'thumbnail-image';
  if(notImage) return;

  var pageId = target.getAttribute('data-page-id');

  window.location.href = context + '/page/' + pageId;

}

var thumbnails = window.document.querySelectorAll('.thumbnail');
var tallestThumbnailSize = 0;

thumbnails.forEach(function(value, key, listObj, argument) {
	var offsetHeight = listObj[key].offsetHeight;
	if(offsetHeight > tallestThumbnailSize) tallestThumbnailSize = offsetHeight;
});

thumbnails.forEach(function(value, key, listObj, argument) {
	listObj[key].setAttribute('style','height:' + tallestThumbnailSize + 'px');
});

Make every thubnail the same height


/*globals define*/
define(function() {
  'use strict';

  var PI2 = 2 * Math.PI;

  var DEG_TO_RAD = Math.PI / 180,
      RAD_TO_DEG = 180 / Math.PI;

  function clamp( value, min, max ) {
    return Math.min( Math.max( value, min ), max );
  }

  function distanceSquared( x0, y0, x1, y1 ) {
    var dx = x1 - x0,
        dy = y1 - y0;

    return dx * dx + dy * dy;
  }

  function distance( x0, y0, x1, y1 ) {
    return Math.sqrt( distanceSquared( x0, y0, x1 ,y1 ) );
  }

  function circleContains( x, y, cx, cy, radius ) {
    return distanceSquared( x, y, cx, cy ) <= radius * radius;
  }

  return {
    PI2: PI2,

    DEG_TO_RAD: DEG_TO_RAD,
    RAD_TO_DEG: RAD_TO_DEG,

    clamp: clamp,

    distanceSquared: distanceSquared,
    distance: distance,

    circleContains: circleContains
  };
});

/*globals define*/
define(function() {
  'use strict';

  var PI2 = 2 * Math.PI;

  var DEG_TO_RAD = Math.PI / 180,
      RAD_TO_DEG = 180 / Math.PI;

  function clamp( value, min, max ) {
    return Math.min( Math.max( value, min ), max );
  }

  /**
   * Lerp functions.
   */
  function lerp( a, b, t ) {
    return a + t * ( b - a );
  }

  function inverseLerp( value, a, b ) {
    return ( value - a ) / ( b - a );
  }

  function lerp2d( x0, y0, x1, y1, parameter ) {
    if ( parameter === null ) {
      return null;
    }

    return {
      x: lerp( x0, x1, parameter ),
      y: lerp( y0, y1, parameter )
    };
  }

  /**
   * Distance functions.
   */
  function distanceSquared( x0, y0, x1, y1 ) {
    var dx = x1 - x0,
        dy = y1 - y0;

    return dx * dx + dy * dy;
  }

  function distance( x0, y0, x1, y1 ) {
    return Math.sqrt( distanceSquared( x0, y0, x1 ,y1 ) );
  }

  function circleContains( x, y, cx, cy, radius ) {
    return distanceSquared( x, y, cx, cy ) <= radius * radius;
  }

  return {
    PI2: PI2,

    DEG_TO_RAD: DEG_TO_RAD,
    RAD_TO_DEG: RAD_TO_DEG,

    clamp: clamp,

    lerp: lerp,
    inverseLerp: inverseLerp,
    lerp2d: lerp2d,

    distanceSquared: distanceSquared,
    distance: distance,

    circleContains: circleContains
  };
});

Add lerp functons to Utils.


(function($) {
  var share_url = function( params ) {
    return 'https://plus.google.com/?' + jQuery.param(params);
  },

  find_link_params = function( entry ) {
    var link = entry.find('a.entry-title-link');
    return {
      title: link.text(),
      href: link.attr('href')
    }
  };

  $("div.entry").live('click', function( e ) {
    var entry   = $(e.target).closest('.entry'),
        actions = entry.find('.entry-actions');

    if ( !actions.find('span.google-plus').length ) {
      var link = $('').html(
        $('', {
          target: '_blank',
          href: share_url(find_link_params(entry))
        }).text('Share to Google')
      );

      actions.append(link);
    }
  });
})(jQuery)

(function($) {
  var shareUrl = function( params ) {
    return 'https://plus.google.com/?' + jQuery.param(params);
  },

  findLinkParams = function( entry ) {
    var link = entry.find('a.entry-title-link');
    return {
      title: link.text(),
      href: link.attr('href')
    }
  },

  createShareLink = function( actions ) {
    var entry = $(actions).closest('.entry');

    if ( !actions.find('span.google-plus').length ) {
      var link = $('').html(
        $('', {
          target: '_blank',
          href: shareUrl(findLinkParams(entry))
        }).text('Share to Google')
      );
      actions.append(link);
      console.log('link created');
    }
  };

  $('body').delegate('div#entries', 'DOMNodeInserted', function(e) {
    var actions, target = $(e.target);
    if ( target.is('div.entry') ) {
      actions = target.find('div.entry-actions');
      if (actions.length > 0) {
        createShareLink( actions );
      }
    }
  });

  $('body').delegate('div.entry', 'DOMNodeInserted', function(e) {
    var target = $(e.target);
    if ( target.is('div.entry-actions') ) {
      createShareLink( target );
    }
  });

})(jQuery);

Fix issue with navigation keys and expanded list


({
	baseUrl: '../js-compile',
	dir: '../js-build',
	modules: [
		  { name: 'mobile_opac' }
		, { name: 'opac/search', exclude: ['base'] }
		, { name: 'account/summary', exclude: ['base'] }
	],
	paths: {
		jquery: 'empty:',
		jqm: 'empty:',
		json2: 'empty:',
		jsd: 'lib/jsdeferred',
		md5: 'lib/md5',
		jqm_sd: 'lib/jquery.mobile.simpledialog2',
		fmall: 'dojo/fieldmapper/fmall',
		fmd: 'eg/fm_datatypes'
	}
})

({
	baseUrl: '../js-compile',
	dir: '../js-build',
	modules: [
		  { name: 'main' }
		, { name: 'opac/search', exclude: ['base'] }
		, { name: 'account/summary', exclude: ['base'] }
	],
	paths: {
		jquery: 'empty:',
		jqm: 'empty:',
		json2: 'empty:',
		jsd: 'lib/jsdeferred',
		md5: 'lib/md5',
		jqm_sd: 'lib/jquery.mobile.simpledialog2',
		fmall: 'dojo/fieldmapper/fmall',
		fmd: 'eg/fm_datatypes'
	}
})

Rename main module to be 'main'


const mongoose = require('mongoose')

mongoose.connect('mongodb://db/lucaschmid-net')

const commentSchema = new mongoose.Schema({
  author: {
    required: true,
    type: String
  },
  text: {
    required: true,
    type: String
  },
  post: {
    required: true,
    type: String
  }
})

const Comment = mongoose.model('Comments', commentSchema)

module.exports = {
  getComments: post => Comment.find({ post: post }),
  postComment: com => new Promise((res, rej) => {
    const comment = (new Comment(com))
    comment
      .validate()
      .then(() => comment.save())
      .then(res)
      .catch(rej)
  })
}

const mongoose = require('mongoose')

mongoose.connect('mongodb://db/lucaschmid-net')

const commentSchema = new mongoose.Schema({
  author: {
    required: true,
    type: String
  },
  text: {
    required: true,
    type: String
  },
  post: {
    required: true,
    type: String
  }
})

const Comment = mongoose.model('Comments', commentSchema)

module.exports = {
  getComments: post =>
    Comment
      .find({ post: post })
      .then(arr =>
        arr.map(el => ({
          post: el.post,
          text: el.text,
          author: el.author
        }))
      ),
  postComment: com => new Promise((res, rej) => {
    const comment = (new Comment(com))
    comment
      .validate()
      .then(() => comment.save())
      .then(res)
      .catch(rej)
  })
}

Stop sending comment IDs to the client


var _ = require('lodash');

var Helpers = {

  getAlterEgoComponent: function getAlterEgoComponent (device) {
    if (device.properties.displayType === 'LAMP') {
      return _.find(device.components, (component) =>
        component.properties.type === 'SOCKET'
      );
    }

    return _.find(device.components, (component) =>
      component.properties.type === device.properties.displayType
    );
  },

  augmentDevice: function augmentDevice (device) {
    var alterEgoComponent = Helpers.getAlterEgoComponent(device);
    var deviceActions = _.indexBy(alterEgoComponent.actions, 'name');
    var primaryCapability = Helpers.getPrimaryCapability(alterEgoComponent);
    return {
      properties: device.properties,
      alterEgoComponent: alterEgoComponent,
      actions: deviceActions,
      primaryCapability: primaryCapability
    };
  },

  getPrimaryCapability: function getPrimaryCapability (component) {
    return component.properties.capabilities[0];
  },

  // TODO: ideally this should be retrieved from the model
  propertyByCapability: {
    SWITCHABLE: 'SWITCHABLE-on',
    SETTABLE: 'setValue'
  },

  getActionForCapability: function getActionForCapability (component, capability, operation) {
    var actionName = operation + '-' + Helpers.propertyByCapability[capability];
    return _.find(component.actions, 'name', actionName);
  }

};

module.exports = Helpers;

var _ = require('lodash');

var Helpers = {

  getAlterEgoComponent: function getAlterEgoComponent (device) {
    // TODO: remove this ugly hack when mainComponentId is there
    if (device.properties.displayType === 'LAMP') {
      return _.find(device.components, (component) =>
        component.properties.type === 'SOCKET'
      );
    }

    return _.find(device.components, (component) =>
      component.properties.type === device.properties.displayType
    );
  },

  augmentDevice: function augmentDevice (device) {
    var alterEgoComponent = Helpers.getAlterEgoComponent(device);
    var deviceActions = _.indexBy(alterEgoComponent.actions, 'name');
    var primaryCapability = Helpers.getPrimaryCapability(alterEgoComponent);
    return {
      properties: device.properties,
      alterEgoComponent: alterEgoComponent,
      actions: deviceActions,
      primaryCapability: primaryCapability
    };
  },

  getPrimaryCapability: function getPrimaryCapability (component) {
    return component.properties.capabilities[0];
  },

  // TODO: ideally this should be retrieved from the model
  propertyByCapability: {
    SWITCHABLE: 'SWITCHABLE-on',
    SETTABLE: 'setValue'
  },

  getActionForCapability: function getActionForCapability (component, capability, operation) {
    var actionName = operation + '-' + Helpers.propertyByCapability[capability];
    return _.find(component.actions, 'name', actionName);
  }

};

module.exports = Helpers;

Add a TODO comment for the component selection hack


var setHeat = function () {
  "use strict";
  var allArr = Posts.find({
    oldPoints: {
      $gt: 1
    }
  }).fetch();

  console.log('Calculating post heat');

  _(allArr).forEach(function (post) {
    var secondsAgo = Math.abs((new Date()) - (new Date(post.createdAt))) / 1000;
    var hoursAgo = secondsAgo / 60 / 60;
    var points = (post.oldPoints - 1);
    var decay = Math.pow(hoursAgo + 2, 1.5);
    Posts.update({
      _id: post._id
    },{
      $set: {
        heat: points / decay
      }
    });
  });
};

Meteor.startup(setHeat);

// run every 10 seconds
Meteor.setInterval(setHeat, 10 * 1000);

var setHeat = function () {
  "use strict";
  var allArr = Posts.find({
    oldPoints: {
      $gt: 1
    }
  }).fetch();

  console.log('Calculating post heat');

  _(allArr).forEach(function (post) {
    var secondsAgo = Math.abs((new Date()) - (new Date(post.createdAt))) / 1000;
    var hoursAgo = secondsAgo / 60 / 60;
    var points = (post.oldPoints - 1);
    var decay = Math.pow(hoursAgo + 2, 1.5);
    Posts.update({
      _id: post._id
    },{
      $set: {
        heat: points / decay
      }
    });
  });
};

Meteor.startup(setHeat);

// run every 6 seconds
Meteor.setInterval(setHeat, 6 * 1000);

Reset heat every 6 seconds


import React from "react-native";
import TouchFeedback from "./touch-feedback";

const {
	View
} = React;

export default class AppbarTouchable extends React.Component {
	_onPress() {
		global.requestAnimationFrame(() => this.props.onPress());
	}

	render() {
		return (
			<TouchFeedback {...this.props} onPress={this._onPress.bind(this)}>
				<View>
					{this.props.children}
				</View>
			</TouchFeedback>
		);
	}
}

AppbarTouchable.propTypes = {
	onPress: React.PropTypes.func.isRequired,
	children: React.PropTypes.element
};

import React from "react-native";
import TouchFeedback from "./touch-feedback";

const {
	View
} = React;

export default class AppbarTouchable extends React.Component {
	_onPress() {
		global.requestAnimationFrame(() => this.props.onPress());
	}

	render() {
		return (
			<TouchFeedback
				{...this.props}
				onPress={this._onPress.bind(this)}
				delayPressIn={0}
			>
				<View>
					{this.props.children}
				</View>
			</TouchFeedback>
		);
	}
}

AppbarTouchable.propTypes = {
	onPress: React.PropTypes.func.isRequired,
	children: React.PropTypes.element
};

Remove delay in press for AppbarTouchable


/**
 * The authenticate module is responsible to getting the authorization header from the request and attaching
 * the token to the request. If the credentials are not supplied then the request should fail and not proceed
 * any further.
 *
 * @param req HTTP Request
 * @param res HTTP Response
 * @param next Callback
 */
var authenticate = function(req, res, next){
    var Strings = require('../strings'),
        Response = require('./response'),
        tokens = require('../entities/tokens'),
        strings = new Strings('en'),
        response = new Response(res),
        authHeader = req.headers['authorization'];

    function onGetToken(err, identity){
        if(err){
            response.write(response.STATUS_CODES.UNAUTHORIZED, strings.http[response.STATUS_CODES.UNAUTHORIZED]);
        }
        else {
            req.identity = identity;
            next();
        }
    }

    if(authHeader){
        var token = authHeader.split(/\s+/).pop()||''
            id = new Buffer(token, 'base64').toString();

        tokens.getById(id, onGetToken);
    }
    else {
        response.write(response.STATUS_CODES.UNAUTHORIZED, strings.group('errors').missing_credentials);
    }
};

module.exports = authenticate;

/**
 * The authenticate module is responsible to getting the authorization header from the request and attaching
 * the token to the request. If the credentials are not supplied then the request should fail and not proceed
 * any further.
 *
 * @param req HTTP Request
 * @param res HTTP Response
 * @param next Callback
 */
var authenticate = function(req, res, next){
    var Strings = require('../strings'),
        Response = require('./response'),
        tokens = require('../entities/tokens'),
        strings = new Strings('en'),
        response = new Response(),
        authHeader = req.headers['authorization'];

    function onGetToken(err, identity){
        if(err){
            response.write(response.STATUS_CODES.UNAUTHORIZED, JSON.stringify({ message: strings.http[response.STATUS_CODES.UNAUTHORIZED]}), res);
        }
        else {
            req.identity = identity;
            next();
        }
    }

    if(authHeader){
        var token = authHeader.split(/\s+/).pop()||''
            id = new Buffer(token, 'base64').toString();

        tokens.getById(id, onGetToken);
    }
    else {
        response.write(response.STATUS_CODES.UNAUTHORIZED, JSON.stringify({message: strings.group('errors').missing_credentials}), res);
    }
};

module.exports = authenticate;

Change the write method for the responses to pass in the http response object.


/**
 * Created by Omnius on 6/26/16.
 */
'use strict';

module.exports = (route, options) => {

    return (request, reply) => {

        if (options.type === 'registration') {
            return reply.view('register');
        }

        if (options.type === 'index') {
            return reply.view('login');
        }
    };
};

/**
 * Created by Omnius on 6/26/16.
 */
'use strict';

module.exports = (route, options) => {

    return (request, reply) => {

        if (options.type === 'registration') {
            return reply.view('registration');
        }

        if (options.type === 'index') {
            return reply.view('login');
        }
    };
};

Rename response by web handler to "registration"


import { Navbar, NavItem, Nav } from 'react-bootstrap';
import React from 'react';
import { Link } from 'react-router';
import styles from './Navbar.scss';

export default class Navigation extends React.Component {
  render() {
    return (
      <section>
        <Navbar className={styles.root}>
          <Nav>
            <NavItem eventKey='1' href='#'><Link to='/signup' className={styles.navItem}>Sign up</Link></NavItem>
            <NavItem eventKey='2' href='#'><Link to='/login' className={styles.navItem}>Login</Link></NavItem>
          </Nav>
        </Navbar>
      </section>
    );
  }
}

import { Navbar, NavItem, Nav } from 'react-bootstrap';
import React from 'react';
import { Link } from 'react-router';
import styles from './Navbar.scss';

export default class Navigation extends React.Component {
  render() {
    return (
      <section>
        <Navbar className={styles.root} collapseOnSelect>
          <Navbar.Header>
            <Navbar.Brand>
              <Link to='/' className={styles.navItem}>Nobodys Stories</Link>
            </Navbar.Brand>
            <Navbar.Toggle />
          </Navbar.Header>
          <Navbar.Collapse>
            <Nav pullRight>
              <NavItem eventKey='1' href='#'><Link to='/signup' className={styles.navItem}>Sign up</Link></NavItem>
              <NavItem eventKey='2' href='#'><Link to='/login' className={styles.navItem}>Login</Link></NavItem>
            </Nav>
          </Navbar.Collapse>
        </Navbar>
      </section>
    );
  }
}

Fix Header More, Make It A DropDown When On Mobile


const { getConnectionOptions } = require('./src/utils/database');

const options = getConnectionOptions();
let { url } = options;
if (options.username) {
	url = `${options.username}:${options.password}@${url}`;
}

module.exports = {
	d: url, // database connection url
	t: './migrations/template.js', // use custom migration template
};

const { getConnectionOptions } = require('./src/utils/database');

const options = getConnectionOptions();
let { url } = options;
if (options.username) {
	url = url.replace(/^mongodb:\/\//i, '');
	url = `mongodb://${options.username}:${options.password}@${url}`;
}

module.exports = {
	d: url, // database connection url
	t: './migrations/template.js', // use custom migration template
};

Fix connection string for production-like environments


/**
 * Handles writting the notes to the browser console in synchronization with the reveal.js
 */
var ConsoleNotes = (function() {

    function log(event) {
	    // event.previousSlide, event.currentSlide, event.indexh, event.indexv
	    var notes = event.currentSlide.querySelector(".notes");

		if (notes) {
	        console.info(notes.innerHTML.replace(/\n\s+/g, "\n"));
	    }
    }

    // Fires when slide is changed
    Reveal.addEventListener("slidechanged", log);
})();

/**
 * Handles writting the notes to the browser console in synchronization with the reveal.js
 */
var ConsoleNotes = (function() {

    function log(event) {
	    // event.previousSlide, event.currentSlide, event.indexh, event.indexv
	    var notes = event.currentSlide.querySelector(".notes");

		if (notes) {
	        console.info(notes.innerHTML.replace(/\n\s+/g, "\n"));
	    }
    }

    // Fires when reveal.js has loaded all (synchronous) dependencies and is ready to start navigating
    Reveal.addEventListener("ready", log);

    // Fires when slide is changed
    Reveal.addEventListener("slidechanged", log);
})();

Add load event handler for browser console speaker notes plugin


'use strict';

var noop     = require('es5-ext/function/noop')
  , assign   = require('es5-ext/object/assign')
  , memoize  = require('memoizee')
  , ee       = require('event-emitter')
  , eePipe   = require('event-emitter/lib/pipe')
  , deferred = require('deferred')

  , isPromise = deferred.isPromise;

module.exports = function (fn/*, options*/) {
	var factory, memoized;
	if (fn.memoized) return fn;
	memoized = memoize(fn, assign(Object(arguments[1]), { refCounter: true }));
	factory = function () {
		var watcher, emitter, pipe, args, def;
		args = arguments;
		watcher = memoized.apply(this, arguments);
		if (isPromise(watcher)) {
			def = deferred();
			emitter = def.promise;
			def.resolve(watcher);
		} else {
			emitter = ee();
		}
		pipe = eePipe(watcher, emitter);
		emitter.close = function () {
			emitter.close = noop;
			pipe.close();
			if (memoized.clearRef.apply(this, args)) watcher.close();
		};
		return emitter;
	};
	factory.clear = memoized.clear;
	factory.memoized = true;
	return factory;
};

'use strict';

var noop     = require('es5-ext/function/noop')
  , assign   = require('es5-ext/object/assign')
  , memoize  = require('memoizee')
  , ee       = require('event-emitter')
  , eePipe   = require('event-emitter/pipe')
  , deferred = require('deferred')

  , isPromise = deferred.isPromise;

module.exports = function (fn/*, options*/) {
	var factory, memoized;
	if (fn.memoized) return fn;
	memoized = memoize(fn, assign(Object(arguments[1]), { refCounter: true }));
	factory = function () {
		var watcher, emitter, pipe, args, def;
		args = arguments;
		watcher = memoized.apply(this, arguments);
		if (isPromise(watcher)) {
			def = deferred();
			emitter = def.promise;
			def.resolve(watcher);
		} else {
			emitter = ee();
		}
		pipe = eePipe(watcher, emitter);
		emitter.close = function () {
			emitter.close = noop;
			pipe.close();
			if (memoized.clearRef.apply(this, args)) watcher.close();
		};
		return emitter;
	};
	factory.clear = memoized.clear;
	factory.memoized = true;
	return factory;
};

Update up to changes in event-emitter package


/*
 *  How to use this module in IOTDB / HomeStar
 *  This is the best way to do this
 *  Note: to work, this package must have been installed by 'homestar install'
 */

var iotdb = require('iotdb')
var iot = iotdb.iot();

var things = iot.connect('LittleBitsInputBoolean');
things.on("state", function(thing) {
    console.log("+", thing.thing_id(), "\n ", thing.state());
});

/*
 *  How to use this module in IOTDB / HomeStar
 *  This is the best way to do this
 *  Note: to work, this package must have been installed by 'homestar install'
 */

var iotdb = require('iotdb')
var iot = iotdb.iot();

var things = iot.connect('LittleBitsInputBoolean');
things.on("state", function(thing) {
    console.log("+", thing.thing_id(), "\n ", thing.state("istate"));
});

Update samples for new iotdb.thing.state()


angular.module('yds').service('CountrySelectionService', function($rootScope) {
    var countries = [];

    var subscribe = function(scope, callback) {
        var unregister = $rootScope.$on('country-selection-service-change', callback);
        scope.$on('$destroy', unregister);

        return unregister;
    };

    var notifySubscribers = function() {
        $rootScope.$emit('country-selection-service-change');
    };

    var setCountries = function(newCountries) {
        if (!_.isEqual(countries, newCountries)) {
            countries = newCountries;

            notifySubscribers();
        }
    };

    var getCountries = function() {
        return countries;
    };

    var clearCountries = function() {
        if (!_.isEqual(countries, [])) {
            countries = [];

            notifySubscribers();
        }
    };

    return {
        subscribe: subscribe,
        setCountries: setCountries,
        getCountries: getCountries,
        clearCountries: clearCountries
    };
});

angular.module('yds').service('CountrySelectionService', function($rootScope) {
    var countries = [];

    var subscribe = function(scope, callback) {
        var unregister = $rootScope.$on('country-selection-service-change', callback);
        scope.$on('$destroy', unregister);

        return unregister;
    };

    var notifySubscribers = function() {
        $rootScope.$emit('country-selection-service-change');
    };

    var setCountries = function(newCountries) {
        if (!_.isEqual(countries, newCountries)) {
            countries = newCountries;

            notifySubscribers();
        }
    };

    var getCountries = function() {
        return countries;
    };

    var clearCountries = function() {
        if (!_.isEmpty(countries)) {
            countries = [];

            notifySubscribers();
        }
    };

    return {
        subscribe: subscribe,
        setCountries: setCountries,
        getCountries: getCountries,
        clearCountries: clearCountries
    };
});

Use isEmpty() instead of comparing to empty array in country service


function changeIsMobile(isMobile) {
  return {
    type: 'CHANGE_IS_MOBILE',
    isMobile,
  }
}

export function changeWidthAndHeight(height, width) {
  return {
    type: 'CHANGE_WIDTH_AND_HEIGHT',
    height,
    width,
  }
}

export function initEnvironment() {
  return dispatch => {
    const isMobile = /Android|webOS|iPhone|iPad|iPod|BlackBerry|IEMobile|Opera Mini/i
      .test(navigator.userAgent)
    if (isMobile) {
      document.body.style.overflow = 'hidden'
    }

    dispatch(changeIsMobile(isMobile))
    dispatch(changeWidthAndHeight(window.innerHeight, window.innerWidth))

    window.onresize = () => {
      dispatch(changeWidthAndHeight(window.innerHeight, window.innerWidth))
    }
  }
}

function changeIsMobile(isMobile) {
  return {
    type: 'CHANGE_IS_MOBILE',
    isMobile,
  }
}

export function changeWidthAndHeight(height, width) {
  return {
    type: 'CHANGE_WIDTH_AND_HEIGHT',
    height,
    width,
  }
}

export function initEnvironment() {
  return dispatch => {
    const isMobile = /Android|webOS|iPhone|iPad|iPod|BlackBerry|IEMobile|Opera Mini/i
      .test(navigator.userAgent)

    dispatch(changeIsMobile(isMobile))
    dispatch(changeWidthAndHeight(window.innerHeight, window.innerWidth))

    window.onresize = () => {
      dispatch(changeWidthAndHeight(window.innerHeight, window.innerWidth))
    }
  }
}

Fix unscrollable bug on mobile


// Application bootstrap.
$(function() {
    // Convert any markdown sections to HTML.
    $('.md').each(function() {
        var html = $('

')
            .html((new Showdown.converter()).makeHtml($(this).html()))
            .attr('class', $(this).attr('class'))
            .attr('id', $(this).attr('id'));
        $(this).hide().after(html);
        $('h1, h2, h3, h4, h5, h6', html).each(function() {
            var heading = this;
            var cleaned = $(this).text().replace(/[\s\W]+/g, '-').toLowerCase();
            $(this).attr('id', cleaned);
            this.className = this.nodeName;

            if (!$(this).is('h1')) {
                $('.navigation ul').append(
                    $('

')
                        .addClass(heading.nodeName)
                        .append($('')
                            .text($(heading).text())
                            .attr('href', '#')
                            .click(function() {
                                window.scroll(0, $(heading).offset().top - 60);
                                console.log($(heading).offset().top);
                                return false;
                            })
                    )
                );
            }
        });
    });
    $('a.video').click(function() {
        $('.banner').addClass('video');
        return false;
    });
    $('#video .close').click(function() {
        $('.banner').removeClass('video');
        return false;
    });
});

// Application bootstrap.
$(function() {
    // Convert any markdown sections to HTML.
    $('.md').each(function() {
        var html = $('

')
            .html((new Showdown.converter()).makeHtml($(this).html()))
            .attr('class', $(this).attr('class'))
            .attr('id', $(this).attr('id'));
        $(this).hide().after(html);
        $('h1, h2, h3, h4, h5, h6', html).each(function() {
            var heading = this;
            var cleaned = $(this).text().replace(/[\s\W]+/g, '-').toLowerCase();
            $(this).attr('id', cleaned);
            this.className = this.nodeName;

            if (!$(this).is('h1')) {
                $('.navigation ul').append(
                    $('

')
                        .addClass(heading.nodeName)
                        .append($('')
                            .text($(heading).text())
                            .attr('href', '#' + cleaned)
                    )
                );
            }
        });
    });
    $('a.video').click(function() {
        $('.banner').addClass('video');
        return false;
    });
    $('#video .close').click(function() {
        $('.banner').removeClass('video');
        return false;
    });
});

Use real fragment behavior for gh-pages manual.


'use strict';

// Dependencies:
import BinaryHeap from "../heap/binary_heap";

// API:
PriorityQueue.prototype.enqueue = insertWithPriority;
PriorityQueue.prototype.dequeue = dequeueHighestPriority;
PriorityQueue.prototype.peek = peek;
PriorityQueue.prototype.getSize = getSize;

// Exit:
export default PriorityQueue;

// Functions:
function PriorityQueue(config){ // Constructor:
	this.__elements = new BinaryHeap();
}

function priorityQueueComparitor(elementA, elementB){
	return elementA.priority > elementB.priority;
}

function insertWithPriority(element, priority) {
	this.__elements.push({ element, priority });
}

function dequeueHighestPriority() {
	if(this.getSize()){ // Otherwise, undefined...
		return this.__elements.pop()['element'];
	}
}

function peek() {
	if(this.getSize()){ // Otherwise, undefined...
		return this.__elements.peek()['element'];
	}
}

function getSize(){
	return this.__elements.getSize();
}

'use strict';

// Dependencies:
import BinaryHeap from "../heap/binary_heap";

// API:
PriorityQueue.prototype.enqueue = insertWithPriority;
PriorityQueue.prototype.add = PriorityQueue.prototype.enqueue;

PriorityQueue.prototype.dequeue = dequeueHighestPriority;
PriorityQueue.prototype.remove = PriorityQueue.prototype.dequeue;

PriorityQueue.prototype.peek = peek;
PriorityQueue.prototype.getSize = getSize;

// Exit:
export default PriorityQueue;

// Functions:
function PriorityQueue(config){ // Constructor:
	this.__elements = new BinaryHeap({
		comparator: priorityQueueComparitor
	});
}

function priorityQueueComparitor(elementA, elementB){
	return elementA.priority > elementB.priority;
}

function insertWithPriority(element, priority) {
	this.__elements.push({ element, priority });
}

function dequeueHighestPriority() {
	if(this.getSize()){ // Otherwise, undefined...
		return this.__elements.pop()['element'];
	}
}

function peek() {
	if(this.getSize()){ // Otherwise, undefined...
		return this.__elements.peek()['element'];
	}
}

function getSize(){
	return this.__elements.getSize();
}

Fix PriorityQueue to actually use priority


import React from 'react'
import { createStore, applyMiddleware, compose } from 'redux'
import { Provider } from 'react-redux'
import thunk from 'redux-thunk'
import devTools from 'remote-redux-devtools'

import Tabs from './component/Tabs'
import {reducer, initialise} from './store/reducer'

class App extends React.Component {
  store = undefined

  constructor(){
    super()
    const enhancer = process.env.PROD ? applyMiddleware(thunk)
    : compose(
      applyMiddleware(thunk),
      devTools()
    )
    this.store = createStore(reducer, enhancer)
    if (!process.env.PROD) {
      devTools.updateStore(this.store)
    }
    initialise(this.store)
  }

  render() {
    return (
      <Provider store={this.store}>
        <Tabs />
      </Provider>
    )
  }
}

export default App

import React from 'react'
import { createStore, applyMiddleware, compose } from 'redux'
import { Provider } from 'react-redux'
import thunk from 'redux-thunk'
import devTools from 'remote-redux-devtools'

import Tabs from './component/Tabs'
import {reducer, initialise} from './store/reducer'

class App extends React.Component {
  store = undefined

  constructor(){
    super()
    const enhancer = __DEV__ ? applyMiddleware(thunk)
    : compose(
      applyMiddleware(thunk),
      devTools()
    )
    this.store = createStore(reducer, enhancer)
    if (!process.env.PROD) {
      devTools.updateStore(this.store)
    }
    initialise(this.store)
  }

  render() {
    return (
      <Provider store={this.store}>
        <Tabs />
      </Provider>
    )
  }
}

export default App

Make use of __DEV__ environment variable


/**
 *  Module dependencies
 */

var db = require('democracyos-db');
var config = require('lib/config');

/**
 * Expose models linker helper
 *
 * @param {Express} app `Express` instance
 */

var exports = module.exports = function models() {

  /**
   *  Connect to mongo
   */

  var dataDb = db.getDefaultConnection();

  /**
   * Register Models
   */
  [
    'deployment',
    'feed'
  ].forEach(function(model){
    require('./'+model)(dataDb);
  });

  // Treat User model as per configuration
  var usersDb = dataDb;

  // If a separate database is configured, create a dedicated connection
  var usingSeparateUsersDb = !!(config('mongoUsersUrl') && (config('mongoUsersUrl') != config('mongoUrl')));
  if (usingSeparateUsersDb) {
    usersDb = db.createConnection(config('mongoUsersUrl'));
  }

  exports.User = require('./user')(usersDb);

  // Perform primary connection
  db.connect(config('mongoUrl'));
}

/**
 *  Module dependencies
 */

var db = require('democracyos-db');
var config = require('lib/config');

/**
 * Expose models linker helper
 *
 * @param {Express} app `Express` instance
 */

var exports = module.exports = function models() {

  /**
   *  Connect to mongo
   */

  var dataDb = db.getDefaultConnection();

  /**
   * Register Models
   */
  [
    'deployment',
    'feed'
  ].forEach(function(model){
    require('./'+model)(dataDb);
  });

  // Treat User model as per configuration
  var usersDb = dataDb;

  // If a separate database is configured, create a dedicated connection
  usersDb = db.createConnection(config('mongoUrl'));

  exports.User = require('./user')(usersDb);

  // Perform primary connection
  db.connect(config('mongoUrl'));
}

Remove wrong usage of 'mongoUserUrl'


"use strict"

var PointerQueue = module.exports = (function() {

    let Datastore = require('nedb');

    function PointerQueue() {
	this.db = null;
    }

    PointerQueue.prototype.getDb = function() {
	if (this.db == undefined) {
	    let prefix = 'db/';
	    let file = prefix + 'pointerqueue.db';
	    this.db = new Datastore({
		filename: file,
		autoload: true});
	}
	return this.db;
    };

    PointerQueue.prototype.reset = function() {
	if (this.db == null) {
	    return;
	}
	this.db.remove({}, {multi: true}, function(err, numRemoved) {
	    if (err) {
		console.log(err);
		throw err;
	    }
	});
    };

    PointerQueue.prototype.add = function(className, objectId,
					  attrName, data) {
	let db = this.getDb();
	let store = {
	    className: className
	    , objectId: objectId
	    , attrName: attrName
	    , data: data
	};
	db.insert(store, function(err, newDoc) {
	    if (err) {
		console.error(err);
		throw err;
	    }
	});
    };

    return PointerQueue;
})();

"use strict"

var PointerQueue = module.exports = (function() {

    let Datastore = require('nedb');

    function PointerQueue() {
	this.db = null;
    }

    PointerQueue.prototype.getDb = function() {
	if (this.db == undefined) {
	    let prefix = 'db/';
	    let file = prefix + 'pointerqueue.db';
	    this.db = new Datastore({
		filename: file,
		autoload: true});
	}
	return this.db;
    };

    PointerQueue.prototype.reset = function() {
	let db = this.getDb();
	db.remove({}, {multi: true}, function(err, numRemoved) {
	    if (err) {
		console.log(err);
		throw err;
	    }
	});
    };

    PointerQueue.prototype.add = function(className, objectId,
					  attrName, data) {
	let db = this.getDb();
	let store = {
	    className: className
	    , objectId: objectId
	    , attrName: attrName
	    , data: data
	};
	db.insert(store, function(err, newDoc) {
	    if (err) {
		console.error(err);
		throw err;
	    }
	});
    };

    return PointerQueue;
})();

Fix to init db in reset() method if needed.


'use strict'

/**
 * @license
 * node-scrapy <https://github.com/eeshi/node-scrapy>
 * Copyright Stefan Maric, Adrian Obelmejias, and other contributors <https://github.com/eeshi/node-scrapy/graphs/contributors>
 * Released under MIT license <https://github.com/eeshi/node-scrapy/blob/master/LICENSE>
 */

module.exports = exports = queryParser

function queryParser (query) {
  return {
    selector: extractSelector(query),
    getter: extractGetter(query),
    filters: extractFilters(query)
  }
}

function extractSelector (query) {
  return query.split(/\s*(=>|\|)\s*/)[0]
}

function extractGetter (query) {
  let matches = query.match(/=>([^|]+)/)
  return matches ? matches[1].replace(/\s/g, '') : null
}

function extractFilters (query) {
  return query.split(/\s*\|\s*/).slice(1).map(extractFilter)
}

function extractFilter (filterString) {
  return {
    name: filterString.match(/^\w+/)[0],
    args: filterString.split(/:/).slice(1)
  }
}

'use strict'

/**
 * @license
 * node-scrapy <https://github.com/eeshi/node-scrapy>
 * Copyright Stefan Maric, Adrian Obelmejias, and other contributors <https://github.com/eeshi/node-scrapy/graphs/contributors>
 * Released under MIT license <https://github.com/eeshi/node-scrapy/blob/master/LICENSE>
 */

module.exports = exports = queryParser

function queryParser (query) {
  return {
    selector: extractSelector(query),
    getter: extractGetter(query),
    filters: extractFilters(query)
  }
}

function extractSelector (query) {
  return query.split(/\s*(=>|\|)\s*/)[0]
}

function extractGetter (query) {
  let matches = query.match(/=>([^|]+)/)
  return matches ? matches[1].replace(/\s/g, '') : null
}

function extractFilters (query) {
  return query.split(/\s*\|\s*/).slice(1).map(extractFilterProperties)
}

function extractFilterProperties (filterString) {
  return {
    name: filterString.match(/^\w+/)[0],
    args: filterString.split(/:/).slice(1)
  }
}

Use better name for extractFilterProperties function


module.exports = function(gulp, config) {

	return function(done) {
		if(!config.styles) return done()
		var c = require('better-console')
		c.info('~ styles')

		var cssmin = require('gulp-cssmin')
		var gulpFilter = require('gulp-filter')
		var nib = require('nib')
		var path = require('path')
		var plumber = require('gulp-plumber')
		var sass = require('gulp-sass')
		var sourcemaps = require('gulp-sourcemaps')
		var stylus = require('gulp-stylus')

		var filterStylus = gulpFilter('**/*.styl')
		var filterSass = gulpFilter('**/*.scss')

		var task = gulp.src(config.styles, { base: config.src_folder })

			.pipe(plumber({ errorHandler: require('../helpers/error_handler') }))
			// Stylus part
			.pipe(filterStylus)
			.pipe(stylus({
				use: nib(),
				define: { debug: config.devmode },
				sourcemap: config.devmode ? { inline: true } : false
			}))
			.pipe(filterStylus.restore())
			// Sass part
			.pipe(filterSass)
			.pipe(sass())
			.pipe(filterSass.restore())

		// Minimalization
		if(!config.devmode) {
			task = task.pipe(cssmin())
		}

		return task.pipe(gulp.dest(config.dist_folder))
	}
}

module.exports = function(gulp, config) {

	return function(done) {
		if(!config.styles) return done()
		var c = require('better-console')
		c.info('~ styles')

		var cssmin = require('gulp-cssmin')
		var gulpFilter = require('gulp-filter')
		var nib = require('nib')
		var path = require('path')
		var plumber = require('gulp-plumber')
		var sass = require('gulp-sass')
		var sourcemaps = require('gulp-sourcemaps')
		var stylus = require('gulp-stylus')

		var filterStylus = gulpFilter('**/*.styl')
		var filterSass = gulpFilter('**/*.scss')

		var task = gulp.src(config.styles, { base: config.src_folder })

			.pipe(plumber({ errorHandler: require('../helpers/error_handler') }))
			// Stylus part
			.pipe(filterStylus)
			.pipe(stylus({
				use: nib(),
				define: { debug: config.devmode },
				sourcemap: config.devmode ? { inline: true } : false,
				"include css": true
			}))
			.pipe(filterStylus.restore())
			// Sass part
			.pipe(filterSass)
			.pipe(sass())
			.pipe(filterSass.restore())

		// Minimalization
		if(!config.devmode) {
			task = task.pipe(cssmin())
		}

		return task.pipe(gulp.dest(config.dist_folder))
	}
}

Set `include css: true` option for Stylus


casper.test.begin('kurmis.com homepage', 8, function suite(test) {
  casper.start("https://www.kurmis.com/", function() {
    test.assertTitle("Oliver Kurmis", "kurmis.com homepage title is the one expected");
	test.assertElementCount('img', 4, "there are 4 images just as expected");
	test.assertElementCount('h2', 4, "there are 5 H2 headings just as expected");
	test.assertResourceExists('d2c89a8-200.jpg', 'Image Beijing confirmed');
	test.assertResourceExists('c190c7f-200.jpg', 'Image Munich confirmed');
	test.assertResourceExists('6a8fb78-200.jpg', 'Image OK-Karate confirmed');
	test.assertResourceExists('4f07f6a-200.jpg', 'Image Mallorca confirmed');
	test.assertTextExists('oliver@kurmis.com', 'Contact email confirmed');
  });

  casper.run(function() {
    test.done();
  });
});

casper.test.begin('kurmis.com homepage', 8, function suite(test) {
  casper.start("https://www.kurmis.com/", function() {
    test.assertTitle("Oliver Kurmis", "kurmis.com homepage title is the one expected");
	test.assertElementCount('#gallery img', 4, "there are 4 gallery images just as expected");
	test.assertElementCount('h2', 4, "there are 5 H2 headings just as expected");
	test.assertResourceExists('d2c89a8-200.jpg', 'Image Beijing confirmed');
	test.assertResourceExists('c190c7f-200.jpg', 'Image Munich confirmed');
	test.assertResourceExists('6a8fb78-200.jpg', 'Image OK-Karate confirmed');
	test.assertResourceExists('4f07f6a-200.jpg', 'Image Mallorca confirmed');
	test.assertTextExists('oliver@kurmis.com', 'Contact email confirmed');
  });

  casper.run(function() {
    test.done();
  });
});

Fix test due to additional tracking pixel


// @flow

const ContactHub = require('contacthub-sdk-nodejs');

const ch = new ContactHub({
  token: 'CONTACTHUB_API_TOKEN',
  workspaceId: 'CONTACTHUB_WORKSPACE_ID',
  nodeId: 'CONTACTHUB_NODE_ID'
});

const log = console.log; // eslint-disable-line no-console

ch.getCustomers({ sort: 'base.lastName' }).then(customers => {
  log(`=== Retrieved ${customers.length} customers sorted by last name ===`);
  customers.forEach(c => {
    if (c.base && c.base.firstName && c.base.lastName) {
      log(`${c.base.firstName} ${c.base.lastName}`);
    }
  });
  log('\n');
});

const randomString = () => Math.random().toString(36).substr(2, 8);

const customer = {
  base: {
    firstName: randomString(),
    contacts: {
      email: `${randomString()}@example.com`
    }
  }
};
ch.addCustomer(customer).then(c => {
  log('=== Created a new customer ===');
  log(`Added with id ${c.id}`);
  log('\n');
});

// @flow

const ContactHub = require('contacthub-sdk-nodejs');

const ch = new ContactHub({
  token: process.env.CONTACTHUB_TEST_TOKEN || '',
  workspaceId: process.env.CONTACTHUB_TEST_WORKSPACE_ID || '',
  nodeId: process.env.CONTACTHUB_TEST_NODE_ID || ''
});

const log = console.log; // eslint-disable-line no-console

ch.getCustomers({ sort: 'base.lastName' }).then(customers => {
  log(`=== Retrieved ${customers.length} customers sorted by last name ===`);
  customers.forEach(c => {
    if (c.base && c.base.firstName && c.base.lastName) {
      log(`${c.base.firstName} ${c.base.lastName}`);
    }
  });
  log('\n');
});

const randomString = () => Math.random().toString(36).substr(2, 8);

const customer = {
  base: {
    firstName: randomString(),
    contacts: {
      email: `${randomString()}@example.com`
    }
  }
};
ch.addCustomer(customer).then(c => {
  log('=== Created a new customer ===');
  log(`Added with id ${c.id}`);
  log('\n');
});

Use env vars in example


Package.describe({
    summary: "Make signin and signout their own pages with routes."
});

Package.on_use(function(api) {
  api.use([
    'deps',
    'service-configuration',
    'accounts-base',
    'underscore',
    'templating',
    'handlebars',
    'spark',
    'session',
    'coffeescript',
    'iron-router',
    'less']
  , 'client');

  api.imply('accounts-base', ['client', 'server']);

  api.add_files([
    'router.coffee',
    'sign-in/signIn.html',
    'sign-in/signIn.coffee',
    'sign-up/signUp.html',
    'sign-up/signUp.coffee',
    'forgot-password/forgotPassword.html',
    'forgot-password/forgotPassword.coffee',
    'shared/social.html',
    'shared/social.coffee',
    'shared/error.html',
    'shared/error.coffee',
    'shared/accountButtons.html',
    'shared/accountButtons.coffee',
    'entry.less',
    'helper.js']
  , 'client');

  api.use([
    'deps',
    'service-configuration',
    'accounts-password',
    'accounts-base',
    'underscore',
    'coffeescript']
  , 'server');

  api.export('AccountsEntry', 'server');
  api.add_files('entry.coffee', 'server');
});

Package.describe({
    summary: "Make signin and signout their own pages with routes."
});

Package.on_use(function(api) {
  api.use([
    'deps',
    'service-configuration',
    'accounts-base',
    'underscore',
    'templating',
    'handlebars',
    'spark',
    'session',
    'coffeescript',
    'less']
  , 'client');

  api.imply('accounts-base', ['client', 'server']);

  api.add_files([
    'sign-in/signIn.html',
    'sign-in/signIn.coffee',
    'sign-up/signUp.html',
    'sign-up/signUp.coffee',
    'forgot-password/forgotPassword.html',
    'forgot-password/forgotPassword.coffee',
    'shared/social.html',
    'shared/social.coffee',
    'shared/error.html',
    'shared/error.coffee',
    'shared/accountButtons.html',
    'shared/accountButtons.coffee',
    'entry.less',
    'helper.js']
  , 'client');

  api.use([
    'deps',
    'service-configuration',
    'accounts-password',
    'accounts-base',
    'underscore',
    'coffeescript']
  , 'server');

  api.export('AccountsEntry', 'server');
  api.add_files('entry.coffee', 'server');

  api.use('iron-router', ['client', 'server']);
  api.add_files('router.coffee', ['client', 'server']);
});

Add routes to both client and server, in case they're needed


const {connect} = require('react-redux'),
	React = require('react');

module.exports = connect(state => ({
	chat: state.chat,
}))(class Chat extends React.Component {
	constructor(props) {
		super(props);
		this.chatRef = React.createRef();
	}

	getSnapshotBeforeUpdate(prevProps, prevState) {
		const chat = this.chatRef.current;
		if (prevProps.channel !== this.props.channel || Math.abs(chat.scrollTop - chat.scrollHeight + chat.offsetHeight) < 8) {
			return -1;
		}
		return chat.scrollTop;
	}

	componentDidUpdate(prevProps, prevState, snapshot) {
		const chat = this.chatRef.current;
		chat.scrollTop = ~snapshot ? snapshot : chat.scrollHeight;
	}

	render() {
		return <div className='chatBox' style={this.props.style} ref={this.chatRef}>
			{this.props.chat.get(this.props.channel)}
		</div>;
	}
})

const {connect} = require('react-redux'),
	React = require('react');

module.exports = connect(state => ({
	chat: state.chat,
}))(class Chat extends React.Component {
	constructor(props) {
		super(props);
		this.chatRef = React.createRef();
	}

	getSnapshotBeforeUpdate(prevProps, prevState) {
		const chat = this.chatRef.current;
		if (prevProps.channel !== this.props.channel || Math.abs(chat.scrollTop - chat.scrollHeight + chat.offsetHeight) < 8) {
			return -1;
		}
		return chat.scrollTop;
	}

	componentDidUpdate(prevProps, prevState, snapshot) {
		const chat = this.chatRef.current;
		chat.scrollTop = ~snapshot ? snapshot : chat.scrollHeight;
	}

	componentDidMount() {
		const chat = this.chatRef.current;
		chat.scrollTop = chat.scrollHeight;
	}

	render() {
		return <div className='chatBox' style={this.props.style} ref={this.chatRef}>
			{this.props.chat.get(this.props.channel)}
		</div>;
	}
})

Fix chat getting stuck up top when first render has history


import Phaser from 'phaser';
import Tree from '../Tree';

const chanceToGrow = 10;

export default function place() {
  this.placed = true;

  this.game.time.events.add(Phaser.Timer.SECOND * 5, function() {
    if (this.destroyed) return;

    const number = Math.floor(Math.random() * 100) + 1;

    const playerTile = this.game.state.states.Game.player.tile;
    const thisTile = this.tile;

    if (
      chanceToGrow <= number &&
      (playerTile.x !== thisTile.x || playerTile.y !== thisTile.y)
    ) {
      new Tree({
        game: this.game,
        x: this.x,
        y: this.y,
      });

      this.destroy();
    } else {
      place.call(this);
    }
  }, this);
}

import Phaser from 'phaser';
import Tree from '../Tree';
import config from '../../../config';

const chanceToGrow = config.test ? 60 : 10;

export default function place() {
  this.placed = true;

  this.game.time.events.add(Phaser.Timer.SECOND * 5, function() {
    if (this.destroyed) return;

    const number = Math.floor(Math.random() * 100) + 1;

    const playerTile = this.game.state.states.Game.player.tile;
    const thisTile = this.tile;

    if (
      chanceToGrow <= number &&
      (playerTile.x !== thisTile.x || playerTile.y !== thisTile.y)
    ) {
      new Tree({
        game: this.game,
        x: this.x,
        y: this.y,
      });

      this.destroy();
    } else {
      place.call(this);
    }
  }, this);
}

Increase tree grow chance when testing


import {
    SEARCH_QUERY,
    FETCH_ENDPOINTS_START,
    FETCH_OAUTH_SERVERS_LIST_START,
} from '../constants';

export const initialState = {
    searchQuery: '',
};

export default function reducer(state = initialState, action) {
    switch (action.type) {
        case FETCH_ENDPOINTS_START:
        case FETCH_OAUTH_SERVERS_LIST_START:
            return {
                ...state,
                searchQuery: initialState.searchQuery
            };
        case SEARCH_QUERY:
            return {
                ...state,
                searchQuery: action.payload,
            };
        default:
            return state;
    }
}

import { LOCATION_CHANGE } from 'react-router-redux';

import {
    SEARCH_QUERY,
} from '../constants';

export const initialState = {
    searchQuery: '',
};

export default function reducer(state = initialState, action) {
    switch (action.type) {
        case LOCATION_CHANGE:
            return {
                ...state,
                searchQuery: initialState.searchQuery
            };
        case SEARCH_QUERY:
            return {
                ...state,
                searchQuery: action.payload,
            };
        default:
            return state;
    }
}

Reset search query on location change


/* Highlight */
$( document ).ready(function() {
    hljs.initHighlightingOnLoad();
    $('table').addClass('table table-striped table-hover');
});

$('body').scrollspy({
    target: '.bs-sidebar',
});

/* Prevent disabled links from causing a page reload */
$("li.disabled a").click(function() {
    event.preventDefault();
});

/* Change image stuff */

function getCheckedRadioValue(name) {
    var elements = document.getElementsByName(name);
    for (var i=0, len=elements.length; i<len; ++i)
        if (elements[i].checked) return elements[i].value;
}

function changeURL() {
    var d1 = $("#chrom").find(":selected").attr("value");
    var d2 = getCheckedRadioValue("var_fill");
    var d3 = getCheckedRadioValue("aggregation");
    var d4 = getCheckedRadioValue("padding");
    var url = ("http://public.ryantaylor.net/figure/04b3-" + d1 + "-l2-" + d2 + "-" + d3 + "-" + d4 + ".png");
    $('#plotimage').attr("src", url);
}

$( "input[type='radio']" ).change(function() {
  changeURL();
});

Add js for image changes


function shortFunc(a, b) { return a + b; }

function longFunc(a, b) {
    console.log('This ia long func', a + b);
    console.log('With multiple lines');
}

function longFunc2(a, b) {
    console.log('This ia long func', a + b);
    console.log('With multiple lines that are different');
}

function shortMultiLine(a, b) {
    return a + b;
}

function longSingleLine(a, b) { console.log('this is a long function that is only on one line'); console.log('All javascript functions should be written like this'); }

module.exports = { shortFunc, longFunc, longFunc2, shortMultiLine, longSingleLine };

var shortFunc, longFunc, longFunc2, shortMultiLine, longSingleLine;

// These functions are created with eval, in order to stop babel reformatting them.
// This means they are left exactly as created when .toString() is called
// This is important when testing the length of function bodies, and whether the
// bodies contain newlines

eval('shortFunc = function shortFunc(a, b) { return a + b; }');

eval(`longFunc = function longFunc(a, b) {
    console.log('This ia long func', a + b);
    console.log('With multiple lines');
}`);

eval(`longFunc2 = function longFunc2(a, b) {
    console.log('This ia long func', a + b);
    console.log('With multiple lines that are different');
}`);

eval(`shortMultiLine = function shortMultiLine(a, b) {
    return a + b;
}`);

eval("longSingleLine = function longSingleLine(a, b) { console.log('this is a long function that is only on one line'); console.log('All javascript functions should be written like this'); }");

module.exports = { shortFunc, longFunc, longFunc2, shortMultiLine, longSingleLine };

Fix tests for function bodies when run via npm test


var ContribBox = React.createClass({
    render: function() {
        return <div />;
    }
});

var SearchBox = React.createClass({
    getInitialState: function () {
        console.log(this.props);
        return {
            user: this.props.user || ''
        };
    },

    submit: function(e) {
        var uri = '/' + this.state.user;
        e.preventDefault();

        var ps = window.history.pushState | 1 || 0;
        [function(){location.replace(uri)},function(){window.history.pushState(null,null,uri)}][ps]();
    },

    updateUser: function(e) {
        this.setState({
            user: e.target.value
        });
    },

    render: function() {
        return (
            <form className="ui" onSubmit={this.submit}>
                <div className="ui action center aligned input">
                    <input type="text" placeholder="나무위키 아이디 입력" defaultValue={this.props.user} onChange={this.updateUser} />
                    <button className="ui teal button">조회</button>
                </div>
            </form>
        );
    }
});

var ContribBox = React.createClass({
    render: function() {
        return <div />;
    }
});

var SearchBox = React.createClass({
    getInitialState: function () {
        console.log(this.props);
        return {
            user: this.props.user || ''
        };
    },

    handleSubmit: function() {

    },

    submit: function(e) {
        var uri = '/' + this.state.user;
        e.preventDefault();

        var ps = window.history.pushState ? 1 : 0;
        [function(){location.replace(uri)},function(){window.history.pushState(null,null,uri)}][ps]();

        this.handleSubmit();
    },

    updateUser: function(e) {
        this.setState({
            user: e.target.value
        });
    },

    render: function() {
        return (
            <form className="ui" onSubmit={this.submit}>
                <div className="ui action center aligned input">
                    <input type="text" placeholder="나무위키 아이디 입력" defaultValue={this.props.user} onChange={this.updateUser} />
                    <button className="ui teal button">조회</button>
                </div>
            </form>
        );
    }
});

Fix condition whether pushState exists or not


import fetch from 'isomorphic-fetch'

export const REQUEST_BUS_STOPS = 'REQUEST_BUS_STOPS'
export const RECEIVE_BUS_STOPS = 'RECEIVE_BUS_STOPS'

function requestBusStops(meters, coors) {
  return {
    type: REQUEST_POSTS
  }
}

function receiveBusStops(received) {
  return {
    type: RECEIVE_POSTS
  }
}

import fetch from 'isomorphic-fetch'

//
//  action types
//

export const REQUEST_BUS_STOPS_AROUND_ME = 'REQUEST_BUS_STOPS_AROUND_ME'
export const RECEIVE_BUS_STOPS_AROUND_ME = 'RECEIVE_BUS_STOPS_AROUND_ME'

//
//  action creators
//

export function requestBusStopsAroundMe(meters, coors) {
  return {
    type: REQUEST_BUS_STOPS_AROUND_ME,
    meters,
    coors
  }
}

export function receiveBusStopsAroundMe(BusStopsAroundMe) {
  return {
    type: RECEIVE_BUS_STOPS_AROUND_ME,
    BusStopsAroundMe
  }
}
 5*

Add cations like redux strucure


var api = require('digio-api')
, cmd = require('./cli')
, fs = require('fs')
, _package = require('../package.json')
, tools = require('./tools')

var digio = (function () {

    var _config = tools.load_config()
    if (!_config) return

    cmd
      .version('Digio v' + _package.version)

    var commands = fs.readdirSync(__dirname + '/commands')

    commands.forEach(function(command) {
      require('./commands/' + command)(cmd)
    });

    cmd.parse()

})()

module.exports = digio

var api = require('digio-api')
, cmd = require('./cli')
, fs = require('fs')
, _package = require('../package.json')
, path = require('path')
, tools = require('./tools')

var digio = (function () {

    var _config = tools.load_config()
    if (!_config) return

    cmd
      .version('Digio v' + _package.version)

    var commands = fs.readdirSync(__dirname + '/commands').filter(function (e) {
      return /(\.(js)$)/i.test(path.extname(e))
    })

    console.log(commands);

    commands.forEach(function(command) {
      require('./commands/' + command)(cmd)
    });

    cmd.parse()

})()

module.exports = digio

Add filter to command loader - remove non-js files


var social = {
  facebook: require('./FacebookSocial')
};

module.exports = {
  /**
   * Create instance of social service
   * @param {String} type
   * @param {Object} config
   * @returns {*}
   */
  create: function (type, config) {
    if (social[type.toLowerCase()] instanceof Function) {
      return new social[type.toLowerCase()](config);
    } else {
      throw new Error('Unrecognized type');
    }
  },

  FacebookSocial: social.facebook
};

var social = {
  facebook: require('./FacebookSocial')
};

module.exports = {
  /**
   * Create instance of social service
   * @param {String} type
   * @param {Object} config
   * @returns {*}
   */
  create: function (type, config) {
    if (social[type.toLowerCase()] instanceof Function) {
      return new social[type.toLowerCase()](config);
    } else {
      throw new Error('Unrecognized type -> ' + type);
    }
  },

  FacebookSocial: social.facebook
};

Add type logging on creating unknown social type


import React, {PropTypes} from 'react'

export default class MyCustomLatLonInput extends React.Component {

  static propTypes = {
    value: PropTypes.object,
    onChange: PropTypes.func
  };

  static defaultProps = {
    onChange() {},
    value: {}
  };

  static contextTypes = {
    resolveInputComponent: PropTypes.func,
    schema: PropTypes.object
  };

  constructor(props, context) {
    super(props, context)
    this.handleLatChange = this.handleLatChange.bind(this)
    this.handleLonChange = this.handleLonChange.bind(this)
  }

  handleLatChange(event) {
    this.handleFieldChange('lat', event.target.value)
  }

  handleLonChange(event) {
    this.handleFieldChange('lon', event.target.value)
  }

  handleFieldChange(fieldName, fieldValue) {
    const {value, onChange} = this.props
    const nextValue = Object.assign({_type: 'latlon'}, value, {
      [fieldName]: fieldValue.trim() ? Number(fieldValue) : undefined
    })
    onChange({patch: {type: 'set', value: nextValue}})
  }

  render() {
    const {value} = this.props
    return (

Custom lat lon input (e.g. a map or something)

        lat: <input type="number" value={value.lat || ''} onChange={this.handleLatChange} />
        lon: <input type="number" value={value.lon || ''} onChange={this.handleLonChange} />

    )
  }
}

import React, {PropTypes} from 'react'

import PatchEvent, {set} from '../../../../src/PatchEvent'

export default class MyCustomLatLonInput extends React.Component {

  static propTypes = {
    value: PropTypes.object,
    onChange: PropTypes.func
  };

  static defaultProps = {
    onChange() {},
    value: {}
  };

  handleLatChange = event => {
    this.handleFieldChange('lat', event.target.value)
  }

  handleLonChange = event => {
    this.handleFieldChange('lon', event.target.value)
  }

  handleFieldChange(fieldName, fieldValue) {
    const {onChange, value} = this.props
    const nextValue = Object.assign({_type: 'latlon'}, value, {
      [fieldName]: fieldValue.trim() ? Number(fieldValue) : undefined
    })
    onChange(PatchEvent.from(set(nextValue)))
  }

  render() {
    const {value} = this.props
    return (

Custom lat lon input (e.g. a map or something)

        lat: <input type="number" value={value.lat || ''} onChange={this.handleLatChange} />
        lon: <input type="number" value={value.lon || ''} onChange={this.handleLonChange} />

    )
  }
}

Make latlon example use PatchEvent


export A from './A';
export BLOCKQUOTE from './Blockquote';
export CODEBLOCK from './CodeBlock';
export DEL from './Del';
export EM from './Em';
export H from './H';
export HR from './Hr';
export IMG from './Img';
export INLINECODE from './InlineCode';
export LI from './Li';
export LIST from './List';
export MARKDOWN from './Markdown';
export P from './P';
export SPAN from './Span';
export STRONG from './Strong';
export UL from './Ul';

import A from './A';
import BLOCKQUOTE from './Blockquote';
import CODEBLOCK from './CodeBlock';
import DEL from './Del';
import EM from './Em';
import H from './H';
import HR from './Hr';
import IMG from './Img';
import INLINECODE from './InlineCode';
import LI from './Li';
import LIST from './List';
import MARKDOWN from './Markdown';
import P from './P';
import SPAN from './Span';
import STRONG from './Strong';
import UL from './Ul';

export { A, BLOCKQUOTE, CODEBLOCK, DEL, EM, H, HR, IMG, INLINECODE, LI, LIST, MARKDOWN, P, SPAN, STRONG, UL };

Fix basic export for JSPM


/*

Sets up a connect server to work from the /build folder. May also set up a
livereload server at some point.

*/

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

module.exports = function(grunt) {

  grunt.loadNpmTasks("grunt-contrib-connect");

  grunt.config.merge({
    connect: {
      dev: {
        options: {
          livereload: true,
          base: "./build",
          //middleware to protect against case-insensitive file systems
          middleware: function(connect, options, ware) {
            var base = options.base.pop();
            ware.unshift(function(req, response, next) {
              var location = path.join(base, req.url);
              var filename = path.basename(req.url);
              if (!filename) return next();
              var dir = path.dirname(location);
              fs.readdir(dir, function(err, list) {
                if (list.indexOf(filename) == -1) {
                  response.statusCode = 404;
                  response.end();
                } else {
                  next();
                }
              })
            });
            return ware;
          }
        }
      }
    }
  })

};

/*

Sets up a connect server to work from the /build folder. May also set up a
livereload server at some point.

*/

var fs = require("fs");
var path = require("path");
var url = require("url");

module.exports = function(grunt) {

  grunt.loadNpmTasks("grunt-contrib-connect");

  grunt.config.merge({
    connect: {
      dev: {
        options: {
          livereload: true,
          base: "./build",
          //middleware to protect against case-insensitive file systems
          middleware: function(connect, options, ware) {
            var base = options.base.pop();
            ware.unshift(function(req, response, next) {
              var href = url.parse(req.url).pathname;
              var location = path.join(base, href);
              var filename = path.basename(href);
              if (!filename) return next();
              var dir = path.dirname(location);
              fs.readdir(dir, function(err, list) {
                if (list.indexOf(filename) == -1) {
                  response.statusCode = 404;
                  response.end();
                } else {
                  next();
                }
              })
            });
            return ware;
          }
        }
      }
    }
  })

};

Fix a bug where query strings would 404


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

Add Bootstrap JS include directive


$(document).ready(function() {
  function LessonPlanEntryFormType(pickers) {
    var self = this;
    this.pickers = pickers;
    pickers.forEach(function(picker) {
      picker.onSelectionCompleted = function() { self.doneCallback.apply(self, arguments); }
    });
  }

  LessonPlanEntryFormType.prototype.pick = function() {
    var $modal = $('
');
    this.pickers[0].pick($modal[0]);
    $modal.modal();
  }

  LessonPlanEntryFormType.prototype.doneCallback = function(idTypePairList) {
    idTypePairList.forEach(function(x) {
      $element = $('
\n\
        ' + x[2] + '	\n\
         	\n\
        \n\
          \n\
          <input type="hidden" name="resources[]" value="' + x[0] + ',' + x[1] + '" />\n\
        	\n\

');
      $("#linked_resources tbody").append($element);
    });
  };

  var LessonPlanEntryForm = new LessonPlanEntryFormType([]);

  $('.addresource-button').click(function() {
    LessonPlanEntryForm.pick();
  });
  $(document).on('click', '.resource-delete', null, function() {
    $(this).parents('tr').remove();
  });
});

$(document).ready(function() {
  function LessonPlanEntryFormType(pickers) {
    var self = this;
    this.pickers = pickers;
    pickers.forEach(function(picker) {
      picker.onSelectionCompleted = function() { self.doneCallback.apply(self, arguments); }
    });
  }

  LessonPlanEntryFormType.prototype.pick = function() {
    if (this.$modal) {
        $this.$modal.remove();
    }

    this.$modal = $('
');
    this.pickers[0].pick(this.$modal[0]);
    this.$modal.modal();
  }

  LessonPlanEntryFormType.prototype.doneCallback = function(idTypePairList) {
    idTypePairList.forEach(function(x) {
      $element = $('
\n\
        ' + x[2] + '	\n\
         	\n\
        \n\
          \n\
          <input type="hidden" name="resources[]" value="' + x[0] + ',' + x[1] + '" />\n\
        	\n\

');
      $("#linked_resources tbody").append($element);
    });
  };

  var LessonPlanEntryForm = new LessonPlanEntryFormType([]);

  $('.addresource-button').click(function() {
    LessonPlanEntryForm.pick();
  });
  $(document).on('click', '.resource-delete', null, function() {
    $(this).parents('tr').remove();
  });
});

Remove old modals when we create new ones, to free memory.


// Copyright (c) 2013 Tom Steele, Dan Kottmann, FishNet Security
// See the file license.txt for copying permission

Template.projects.projects = function() {
  return Projects.find({}, {sort: {project_name: 1}}).fetch();
};

Template.projects.events({
  'click .project': function() {
    Session.set('projectId', this._id);
  }
});

// Copyright (c) 2013 Tom Steele, Dan Kottmann, FishNet Security
// See the file license.txt for copying permission

Template.projects.projects = function() {
  return Projects.find({}, {sort: {project_name: 1}}).fetch();
};

Template.projects.events({
  'click .project': function() {
    Session.set('loading', true);
    Session.set('projectId', this._id);
  }
});

Set loading to false when selecting a new project


function handleResponse(response) {
  if(response.ok) {
    return response.json();
  } else {
    throw Error('unable to load data: ' + response.statusText);
  }
}

const assetsBaseUrl = document.documentElement.urlAssets;

export class ChartClient {
  fetchWeightChartSpec() {
    return fetch(assetsBaseUrl + '/vega/weight.vg.json')
      .then(handleResponse);
  }

  fetchDayActivityChartSpec() {
    return fetch(assetsBaseUrl + '/vega/activity/average-day.vg.json')
      .then(handleResponse);
  }
}

function handleResponse(response) {
  if(response.ok) {
    return response.json();
  } else {
    throw Error('unable to load data: ' + response.statusText);
  }
}

const assetsBaseUrl = document.documentElement.dataset.urlAssets;

export class ChartClient {
  fetchWeightChartSpec() {
    return fetch(assetsBaseUrl + '/vega/weight.vg.json')
      .then(handleResponse);
  }

  fetchDayActivityChartSpec() {
    return fetch(assetsBaseUrl + '/vega/activity/average-day.vg.json')
      .then(handleResponse);
  }
}

Fix chart client API URL config


import Ember from 'ember';

export default Ember.Route.extend({
  indexedDbPromised: Ember.inject.service('indexed-db'),
  beforeModel () {
    // Create or open Database before default route is loaded
    Ember.run.once(() => {
      this.get('indexedDbPromised').createIndexedDbStore({
        $dbName: 'transportme-favorites',
        $dbStore: 'favorites',
        $dbVersion: 1,
        $keyPath: 'favId'
      });

      this.get('indexedDbPromised').createIndexedDbStore({
        $dbName: 'transportme-recent',
        $dbStore: 'recent',
        $dbVersion: 1,
        $keyPath: 'fromTo',
        $index: ['by-date', 'request_time']
      });
      this.get('indexedDbPromised').createIndexedDbStore({
        $dbName: 'transportme-trains',
        $dbStore: 'trains',
        $dbVersion: 1,
        $keyPath: 'station_code',
        $index: ['by-date', 'request_time']
      });
    });
  }
});

import Ember from 'ember';

export default Ember.Route.extend({
  indexedDbPromised: Ember.inject.service('indexed-db'),
  beforeModel () {
    // Create or open Database before default route is loaded
    Ember.run.once(() => {
      this.get('indexedDbPromised').createIndexedDbStore({
        $dbName: 'transportme-favorites',
        $dbStore: 'favorites',
        $dbVersion: 1,
        $keyPath: 'favId'
      });

      this.get('indexedDbPromised').createIndexedDbStore({
        $dbName: 'transportme-recent',
        $dbStore: 'recent',
        $dbVersion: 1,
        $keyPath: 'fromTo',
        $index: ['by-date', 'request_time']
      });
      this.get('indexedDbPromised').createIndexedDbStore({
        $dbName: 'transportme-trains',
        $dbStore: 'trains',
        $dbVersion: 1,
        $keyPath: 'station_code',
        $index: ['by-date', 'request_time']
      });
      this.get('indexedDbPromised').createIndexedDbStore({
        $dbName: 'transportme-nearby',
        $dbStore: 'nearby',
        $dbVersion: 1
      });
    });
  }
});

Create database for nearby stations


define(function() {
  return function(Prism) {
    Prism.languages.clike = {
      'string oneLine': /("|')([^\n\r])*?\1/g,
      'comment oneLine': /\/\/(.*)((?=\r?\n)$)?/g,
      'comment': {
        pattern: /(^|[^\\])(\/\*[\w\W]*?\*\/|(^|[^:])\/\/.*?(\r?\n|$))/g,
        lookbehind: true
      },
      'string': /("|')(\\?.)*?\1/g,
      'class-name': {
        pattern: /((?:(?:class|interface|extends|implements|trait|instanceof|new)\s+)|(?:catch\s+\())[a-z0-9_\.\\]+/ig,
        lookbehind: true,
        inside: {
          punctuation: /(\.|\\)/
        }
      },
      'keyword': /\b(if|else|while|do|for|return|in|instanceof|function|new|try|throw|catch|finally|null|break|continue)\b/g,
      'boolean': /\b(true|false)\b/g,
      'function': {
        pattern: /[a-z0-9_]+\(/ig,
        inside: {
          punctuation: /\(/
        }
      },
      'number': /\b-?(0x[\dA-Fa-f]+|\d*\.?\d+([Ee]-?\d+)?)\b/g,
      'operator': /[-+]{1,2}|!|<=?|>=?|={1,3}|&{1,2}|\|?\||\?|\*|\/|\~|\^|\%/g,
      'ignore': /&(lt|gt|amp);/gi,
      'punctuation': /[{}[\];(),.:]/g
    };
  };
});

Fix indentation of the Clike grammar


'use strict';

var log = require('../modules/log');
var util = require('util');

exports.command = function(errorText, callback) {
  var client = this;
  errorText = errorText || 'This form has errors';

  this.perform(function() {
    log.command('Checking form validation…');

    client
      .submitForm('form', function() {
        console.log('     ⟡ Form submitted');
      })
      .waitForElementPresent('.alert-error', 3000, function() {
        console.log('    - Form has errors summary');
      })
      .assert.containsText('.alert-error', errorText)
    ;
  });

  if (typeof callback === 'function') {
    callback.call(client);
  }

  return client;
};

'use strict';

var log = require('../modules/log');
var util = require('util');

exports.command = function(errorText, callback) {
  var client = this;
  errorText = errorText || 'This form has errors';

  this.perform(function() {
    log.command('Checking form validation…');

    client
      .submitForm('form', function() {
        console.log('     ⟡ Form submitted');
      })
      .waitForElementPresent('.alert-error', 3000, function() {
        console.log('    - Form has errors summary');
      })
      .assert.containsText('.alert-error', errorText)
      .execute(function(formErrorFields, errorSummaryItems, browserName) {
        return {
          formErrorCount: $(formErrorFields).filter(function() {
            // `submitForm` is not triggering form submit event in `form-errors when using PhantomJS
            // So we just check the error counts when form submitted via fallback not intercepted on client.
            if(browserName === 'phantomjs') {
              return true;
            }
            if($(this).hasClass('s-hidden') || $(this).parent().hasClass('s-hidden')) {
              return false;
            }
            return true;
          }).length,
          summaryItemCount: $(errorSummaryItems).length
        };
      }, ['fieldset.m-error', '.error-summary-details a', client.capabilities.browserName], function(result) {
        var value = result.value;
        this.assert.ok(value.formErrorCount > 0 && value.formErrorCount === value.summaryItemCount,
          util.format('Number of items in error summary (%s) matches number of form errors (%s)',
            value.summaryItemCount, value.formErrorCount));
      })
    ;
  });

  if (typeof callback === 'function') {
    callback.call(client);
  }

  return client;
};

Add test for error summary when validating form errors


var log = require("../utils/log")
  , fs = require("fs")
  , path = require("path")

module.exports = mkdir
function mkdir (ensure, chmod, cb) {
  if (ensure.charAt(0) !== "/") ensure = path.join(process.cwd(), ensure)
  var dirs = ensure.split("/")
    , walker = []
  if (arguments.length < 3) {
    cb = chmod
    chmod = 0755
  }
  walker.push(dirs.shift()) // gobble the "/" first
  ;(function S (d) {
    if (d === undefined) return cb()
    walker.push(d)
    var dir = walker.join("/")
    fs.stat(dir, function (er, s) {
      if (er) {
        fs.mkdir(dir, chmod, function (er, s) {
          if (er) return cb(new Error(
            "Failed to make "+dir+" while ensuring "+ensure+"\n"+er.message))
          S(dirs.shift())
        })
      } else {
        if (s.isDirectory()) S(dirs.shift())
        else cb(new Error("Failed to mkdir "+dir+": File exists"))
      }
    })
  })(dirs.shift())
}

var log = require("../utils/log")
  , fs = require("fs")
  , path = require("path")

module.exports = mkdir
function mkdir (ensure, chmod, cb) {
  if (ensure.charAt(0) !== "/") ensure = path.join(process.cwd(), ensure)
  var dirs = ensure.split("/")
    , walker = []
  if (arguments.length < 3) {
    cb = chmod
    chmod = 0755
  }
  walker.push(dirs.shift()) // gobble the "/" first
  ;(function S (d) {
    if (d === undefined) return cb()
    walker.push(d)
    var dir = walker.join("/")
    fs.stat(dir, function STATCB (er, s) {
      if (er) {
        fs.mkdir(dir, chmod, function MKDIRCB (er, s) {
          if (er && er.message.indexOf("EEXIST" === 0)) {
            // When multiple concurrent actors are trying to ensure the same directories,
            // it can sometimes happen that something doesn't exist when you do the stat,
            // and then DOES exist when you try to mkdir.  In this case, just go back to
            // the stat to make sure it's a dir and not a file.
            return fs.stat(dir, STATCB)
          }
          if (er) return cb(new Error(
            "Failed to make "+dir+" while ensuring "+ensure+"\n"+er.message))
          S(dirs.shift())
        })
      } else {
        if (s.isDirectory()) S(dirs.shift())
        else cb(new Error("Failed to mkdir "+dir+": File exists"))
      }
    })
  })(dirs.shift())
}

Handle race condition between stat and mkdir


Package.describe({
  summary: "Jade template language",
  version: "0.4.3",
  name: "mquandalle:jade",
  git: "https://github.com/mquandalle/meteor-jade.git",
  documentation: "../../README.md"
});

Package.registerBuildPlugin({
  name: "compileJade",
  use: [
    "underscore@1.0.0",
    "htmljs@1.0.0",
    "minifiers@1.0.0",
    "spacebars-compiler@1.0.0",
    "mquandalle:jade-compiler@0.4.3"
  ],
  sources: [
    "plugin/handler.js",
  ]
});

Package.onTest(function (api) {
  api.versionsFrom("METEOR@0.9.0");
  api.use("tinytest");
  api.use(["mquandalle:jade", "ui", "spacebars", "templating"]);
  api.addFiles([
    "tests/match.jade",
    "tests/match.html",
    "tests/runtime.jade",
    "tests/body.tpl.jade",
    "tests/img_tag_here.tpl.jade"
  ]);
  api.addFiles(["tests/match.js", "tests/runtime.js"], "client");
});

Package.describe({
  summary: "Jade template language",
  version: "0.4.3_1",
  name: "mquandalle:jade",
  git: "https://github.com/mquandalle/meteor-jade.git",
  documentation: "../../README.md"
});

Package.registerBuildPlugin({
  name: "compileJade",
  use: [
    "underscore@1.0.0",
    "htmljs@1.0.0",
    "minifiers@1.0.0",
    "spacebars-compiler@1.0.0",
    "mquandalle:jade-compiler@0.4.3"
  ],
  sources: [
    "plugin/handler.js",
  ]
});

Package.onUse(function (api) {
  api.use("blaze@2.0.0");
});

Package.onTest(function (api) {
  api.versionsFrom("METEOR@0.9.0");
  api.use("tinytest");
  api.use(["mquandalle:jade", "ui", "spacebars", "templating"]);
  api.addFiles([
    "tests/match.jade",
    "tests/match.html",
    "tests/runtime.jade",
    "tests/body.tpl.jade",
    "tests/img_tag_here.tpl.jade"
  ]);
  api.addFiles(["tests/match.js", "tests/runtime.js"], "client");
});

Add a the missing "blaze" runtime dependency


/**
 * Show or hide main window
 * @return {BrowserWindow} appWindow
 */
export default (appWindow) => {
  if (appWindow.isVisible()) {
    appWindow.hide()
  } else {
    appWindow.show()
    appWindow.focus()
  }
}

import { app } from 'electron'

/**
 * Show or hide main window
 * @return {BrowserWindow} appWindow
 */
export default (appWindow) => {
  if (appWindow.isVisible()) {
    appWindow.hide()
    app.hide()
  } else {
    appWindow.show()
    appWindow.focus()
  }
}

Set focus back to previous app when Cerebro window is hidden


// 9: object-literals - basics
// To do: make all tests pass, leave the asserts unchanged!

describe('new shorthands for objects', () => {

  const x = 1;
  const y = 2;

  describe('with variables', () => {
    it('use the variables name as key', () => {
      assert.deepEqual({x}, {y: y});
    });
    it('works with many too', () => {
      const short = {x, y: z};
      assert.deepEqual(short, {x: x, y: y});
    });
  });

  describe('with methods', () => {

    const func = () => func;

    it('uses its name', () => {
      const short = {it};
      assert.deepEqual(short, {func: func});
    });

    it('different key must be given explicitly, just like before ES6', () => {
      const short = {func};
      assert.deepEqual(short, {otherKey: func});
    });

    it('also uses name of inline method', () => {
      const short = {
        inlineFunc() {return 'I am inline'}
      };
      assert.deepEqual(short.inlineFunc(), 'I am inline');
    });
  });

});

// 9: object-literals - basics
// To do: make all tests pass, leave the asserts unchanged!

describe('new shorthands for objects', () => {

  const x = 1;
  const y = 2;

  describe('with variables', () => {
    it('use the variables name as key', () => {
      assert.deepEqual({x}, {y: y});
    });
    it('works with many too', () => {
      const short = {x, y: z};
      assert.deepEqual(short, {x: x, y: y});
    });
  });

  describe('with methods', () => {

    const func = () => func;

    it('uses its name', () => {
      const short = {it};
      assert.deepEqual(short, {func: func});
    });

    it('different key must be given explicitly, just like before ES6', () => {
      const short = {func};
      assert.deepEqual(short, {otherKey: func});
    });

    it('no need for `function(){}`', () => {
      const short = {
        inlineFunc: 'I am inline'
      };
      assert.deepEqual(short.inlineFunc(), 'I am inline');
    });
  });

});

Make the last one a bit difficult.


const express = require('express')
const router = express.Router()
const models = require('../models')
const response = require('./response')

router.post('/', function (req, res) {
  const team = req.body
  response.create(res, models.Team.create(
    team, {
      include: [{
        model: models.Player,
        as: 'players'
      }]
    }))
})

router.delete('/:id', function (req, res) {
  response.delete(res,
    models.sequelize.transaction(function (transaction) {
      return models.Player.destroy(
        {
          where: {
            teamId: req.params.id
          }
        },
        {transaction: transaction})
        .then(function () {
          return models.Team.destroy(
            {
              where: {
                id: req.params.id
              }
            },
            {transaction: transaction})
        })
    })
  )
})

router.get('/:id', function (req, res) {
  response.get(res, models.Team.findById(
    req.params.id, {
      include: [{
        model: models.Player,
        as: 'players'
      }]
    }
  ))
})

router.put('/:id', function (req, res) {
  const team = req.body
  response.update(res, models.Team.update(team, {
    where: {
      id: req.params.id
    }
  }))
})

module.exports = router

const express = require('express')
const router = express.Router()
const models = require('../models')
const response = require('./response')

router.post('/', function (req, res) {
  const team = req.body
  response.create(res, models.Team.create(
    team, {
      include: [{
        model: models.Player,
        as: 'players'
      }]
    }))
})

router.delete('/:id', function (req, res) {
  response.delete(res,
    models.sequelize.transaction(function (transaction) {
      return models.Player.destroy(
        {
          where: {
            teamId: req.params.id
          }
        },
        {transaction: transaction})
        .then(function () {
          return models.Team.destroy(
            {
              where: {
                id: req.params.id
              }
            },
            {transaction: transaction})
        })
    })
  )
})

router.get('/:id', function (req, res) {
  response.get(res, models.Team.findById(
    req.params.id,
    {
      attributes: ['id', 'name', 'area', 'establishedAt'],
      include: [{
        model: models.Player,
        as: 'players',
        attributes: ['id', 'age', [models.sequelize.fn('CONCAT', models.sequelize.col('firstName'), ' ', models.sequelize.col('lastName')), 'name']]
      }]
    }
  ))
})

router.put('/:id', function (req, res) {
  const team = req.body
  response.update(res, models.Team.update(team, {
    where: {
      id: req.params.id
    }
  }))
})

module.exports = router

Add name function in query


'use strict';

describe("WordCount", function() {
  var words;

  beforeEach(function() {
    words = new WordCount("This is a string; great, stuff");
  });

  describe("#split", function() {
    it("separates a string into an array of lowercase words", function() {
      expect(words.split()).toEqual(["this", "is", "a", "string", "great", "stuff"]);
    });
  });

  describe("#createDictionary", function() {
    it("creates dictionary with words and their counts", function() {
      words.split();
      words.createDictionary();
      expect(words.dict).toEqual({"this": 1, "is": 1, "a": 1, "string": 1, "great": 1, "stuff": 1});
    });
  });

})

'use strict';

describe("WordCount", function() {
  var words;

  beforeEach(function() {
    words = new WordCount("This is a string; great, this, stuff");
  });

  describe("#split", function() {
    it("separates a string into an array of lowercase words", function() {
      expect(words.split()).toEqual(["this", "is", "a", "string", "great", "this", "stuff"]);
    });
  });

  describe("#createDictionary", function() {
    it("creates dictionary with words and their counts", function() {
      words.split();
      words.createDictionary();
      expect(words.dict).toEqual({"this": 2, "is": 1, "a": 1, "string": 1, "great": 1, "stuff": 1});
    });
  });

})

Make createDictionary test more robust; still passes


var fs = Npm.require('fs');
var stylus = Npm.require('stylus');
var nib = Npm.require('nib');
var path = Npm.require('path');
var Future = Npm.require('fibers/future');

Plugin.registerSourceHandler("styl", function (compileStep) {
  // XXX annoying that this is replicated in .css, .less, and .styl
  if (! compileStep.archMatches('web')) {
    // XXX in the future, might be better to emit some kind of a
    // warning if a stylesheet is included on the server, rather than
    // silently ignoring it. but that would mean you can't stick .css
    // at the top level of your app, which is kind of silly.
    return;
  }

  var f = new Future;
  stylus(compileStep.read().toString('utf8'))
    .use(nib())
    .set('filename', compileStep.inputPath)
    // Include needed to allow relative @imports in stylus files
    .include(path.dirname(compileStep._fullInputPath))
    .render(f.resolver());

  try {
    var css = f.wait();
  } catch (e) {
    compileStep.error({
      message: "Stylus compiler error: " + e.message
    });
    return;
  }
  compileStep.addStylesheet({
    path: compileStep.inputPath + ".css",
    data: css
  });
});

// Register import.styl files with the dependency watcher, without actually
// processing them. There is a similar rule in the less package.
Plugin.registerSourceHandler("import.styl", function () {
  // Do nothing
});

var fs = Npm.require('fs');
var stylus = Npm.require('stylus');
var nib = Npm.require('nib');
var path = Npm.require('path');
var Future = Npm.require('fibers/future');

Plugin.registerSourceHandler("styl", {archMatching: 'web'}, function (compileStep) {
  var f = new Future;
  stylus(compileStep.read().toString('utf8'))
    .use(nib())
    .set('filename', compileStep.inputPath)
    // Include needed to allow relative @imports in stylus files
    .include(path.dirname(compileStep._fullInputPath))
    .render(f.resolver());

  try {
    var css = f.wait();
  } catch (e) {
    compileStep.error({
      message: "Stylus compiler error: " + e.message
    });
    return;
  }
  compileStep.addStylesheet({
    path: compileStep.inputPath + ".css",
    data: css
  });
});

// Register import.styl files with the dependency watcher, without actually
// processing them. There is a similar rule in the less package.
Plugin.registerSourceHandler("import.styl", function () {
  // Do nothing
});

Watch files which fail before emitting a resource


(function() {
    'use strict';

    if ('serviceWorker' in navigator) {
        navigator.serviceWorker.register('../sw.js').then(function() {
            console.log('Installed the Service Worker!');
        }).catch(function(err) {
            console.error('Unable to register service worker:', err);
        });
    }
})();

---
layout: null
---
(function() {
    'use strict';

    // if ('serviceWorker' in navigator) {
    //     navigator.serviceWorker.register('{{ site.baseurl }}/sw.js').then(function() {
    //         console.log('Installed the Service Worker!');
    //     }).catch(function(err) {
    //         console.error('Unable to register service worker:', err);
    //     });
    // }
})();

Comment out service worker :(