// Return Promise
const mergeImages = (sources = [], options = { format: 'image/png' }) => new Promise(resolve => {
	// Setup browser/node specific variables
	const canvas = options.Canvas ? new options.Canvas() : window.document.createElement('canvas');
	const Image = options.Canvas ? options.Canvas.Image : window.Image;

	// Load sources
	const images = sources.map(source => new Promise(resolve => {
		// Convert strings to objects
		if (typeof source === 'string') {
			source = { src: source };
		}

		// Resolve source and img when loaded
		const img = new Image();
		img.onload = () => resolve(Object.assign({}, source, { img }));
		img.src = source.src;
	}));

	// Get canvas context
	const ctx = canvas.getContext('2d');

	// When sources have loaded
	Promise.all(images)
		.then(images => {
			// Set canvas dimensions
			const getSize = dim => options[dim] || Math.max(...images.map(image => image.img[dim]));
			canvas.width = getSize('width');
			canvas.height = getSize('height');

			// Draw images to canvas
			images.forEach(image => ctx.drawImage(image.img, image.x || 0, image.y || 0));

			// Resolve data uri
			resolve(canvas.toDataURL(options.format));
		});
});

module.exports = mergeImages;

// Return Promise
const mergeImages = (sources = [], options = { format: 'image/png' }) => new Promise(resolve => {
	// Setup browser/node specific variables
	const canvas = options.Canvas ? new options.Canvas() : window.document.createElement('canvas');
	const Image = options.Canvas ? options.Canvas.Image : window.Image;

	// Load sources
	const images = sources.map(source => new Promise(resolve => {
		// Convert strings to objects
		if (source.constructor.name !== 'Object') {
			source = { src: source };
		}

		// Resolve source and img when loaded
		const img = new Image();
		img.onload = () => resolve(Object.assign({}, source, { img }));
		img.src = source.src;
	}));

	// Get canvas context
	const ctx = canvas.getContext('2d');

	// When sources have loaded
	Promise.all(images)
		.then(images => {
			// Set canvas dimensions
			const getSize = dim => options[dim] || Math.max(...images.map(image => image.img[dim]));
			canvas.width = getSize('width');
			canvas.height = getSize('height');

			// Draw images to canvas
			images.forEach(image => ctx.drawImage(image.img, image.x || 0, image.y || 0));

			// Resolve data uri
			resolve(canvas.toDataURL(options.format));
		});
});

module.exports = mergeImages;

Check if source isn't object instead of is string


const prefix = '_async_computed$'

export default {
  install (Vue, options) {
    options = options || {}

    Vue.config
      .optionMergeStrategies
      .asyncComputed = Vue.config.optionMergeStrategies.computed

    Vue.mixin({
      created () {
        Object.keys(this.$options.asyncComputed || {}).forEach(key => {
          const fn = this.$options.asyncComputed[key]
          if (!this.$options.computed) this.$options.computed = {}
          Vue.set(this.$options.computed, prefix + key, fn)
          Vue.set(this.$data, key, null)
        })

        this._initComputed()

        Object.keys(this.$options.asyncComputed || {}).forEach(key => {
          let promiseId = 0
          this.$watch(prefix + key, newPromise => {
            const thisPromise = ++promiseId
            newPromise.then(value => {
              if (thisPromise !== promiseId) return
              this[key] = value
            }).catch(err => {
              if (thisPromise !== promiseId) return

              if (options.errorHandler === false) return

              const handler = (options.errorHandler === undefined)
                ?  console.error.bind(console, 'Error evaluating async computed property:')
                : options.errorHandler

              handler(err.stack)
            })
          }, { immediate: true })
        })
      }
    })
  }
}

const prefix = '_async_computed$'

export default {
  install (Vue, options) {
    options = options || {}

    Vue.config
      .optionMergeStrategies
      .asyncComputed = Vue.config.optionMergeStrategies.computed

    Vue.mixin({
      created () {
        Object.keys(this.$options.asyncComputed || {}).forEach(key => {
          const fn = this.$options.asyncComputed[key]
          if (!this.$options.computed) this.$options.computed = {}
          Vue.set(this.$options.computed, prefix + key, fn)
          Vue.set(this, key, null)
        })

        this._initComputed()

        Object.keys(this.$options.asyncComputed || {}).forEach(key => {
          let promiseId = 0
          this.$watch(prefix + key, newPromise => {
            const thisPromise = ++promiseId
            newPromise.then(value => {
              if (thisPromise !== promiseId) return
              this[key] = value
            }).catch(err => {
              if (thisPromise !== promiseId) return

              if (options.errorHandler === false) return

              const handler = (options.errorHandler === undefined)
                ?  console.error.bind(console, 'Error evaluating async computed property:')
                : options.errorHandler

              handler(err.stack)
            })
          }, { immediate: true })
        })
      }
    })
  }
}

Set the computed value on the View-Model itself, the way Vue does it


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

import Root from './components/Root';

window.document.addEventListener('DOMContentLoaded', function() {
  var $container = window.document.querySelector('#js-app-container');
  ReactDOM.render(React.createElement(Root), $container);
});

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

import Root from './components/Root';

window.document.addEventListener('DOMContentLoaded', () => {
  const container = window.document.querySelector('#js-app-container');
  ReactDOM.render(React.createElement(Root), container);
});

Rewrite to modern ES syntax


import Vue from './lib/vue';
import VueWrapper from './VueWrapper';
import './lib/matchesPolyfill';

function createElem() {
  const elem = document.createElement('div');

  document.body.appendChild(elem);

  return elem;
}

export default function mount(component, options = {}) {
  let elem = null;
  const attachToDocument = options.attachToDocument;

  if (attachToDocument) {
    elem = createElem();
    delete options.attachToDocument; // eslint-disable-line no-param-reassign
  }

  const Constructor = Vue.extend(component);
  const vm = new Constructor(options);

  if (options.slots) {
    Object.keys(options.slots).forEach((key) => {
      vm.$slots[key] = vm.$createElement(options.slots[key]);
    });
  }

  vm.$mount(elem);

  return new VueWrapper(vm, attachToDocument);
}

import Vue from './lib/vue';
import VueWrapper from './VueWrapper';
import './lib/matchesPolyfill';

Vue.config.productionTip = false;

function createElem() {
  const elem = document.createElement('div');

  document.body.appendChild(elem);

  return elem;
}

export default function mount(component, options = {}) {
  let elem = null;
  const attachToDocument = options.attachToDocument;

  if (attachToDocument) {
    elem = createElem();
    delete options.attachToDocument; // eslint-disable-line no-param-reassign
  }

  const Constructor = Vue.extend(component);
  const vm = new Constructor(options);

  if (options.slots) {
    Object.keys(options.slots).forEach((key) => {
      vm.$slots[key] = vm.$createElement(options.slots[key]);
    });
  }

  vm.$mount(elem);

  return new VueWrapper(vm, attachToDocument);
}

Remove development mode warning log


angular.module('com.likalo.ui')
  .factory('uiDialog', ['ngDialog',
    function (ngDialog) {
      return {
        alert: function (title, message) {
          var infoClass;
          title = title || 'Alert';
          message = message || '';
          return ngDialog.open({
            plain: true,
            closeByNavigation: true,
            className: 'UI-modal UI-modal--alert',
            template: '<svg class="UI-info-block--decorator"><rect class="UI-decorator--base" height="100%"></rect><use xmlns:xlink="http://www.w3.org/1999/xlink" class="UI-decorator--icon" ui-icon-xlink-href="alert" xlink:href="#icon--alert"></use></svg>
' + title + '
' + message + '
'
          });
        },
        modal: function (template, controller, more) {
          return ngDialog.openConfirm(angular.extend({
            showClose: false,
            closeByEscape: false,
            closeByDocument: false,
            className: 'UI-modal UI-modal--dialog',
            template: template,
            controller: controller
          }, more));
        },
        view: function (template, controller, more) {
          return ngDialog.open(angular.extend({
            showClose: false,
            closeByEscape: true,
            closeByDocument: true,
            closeByNavigation: true,
            className: 'UI-modal UI-modal--view',
            template: template,
            controller: controller
          }, more));
        }
      };
    }
  ]);

angular.module('com.likalo.ui')
  .factory('uiDialog', ['ngDialog',
    function (ngDialog) {
      return {
        alert: function (title, message) {
          var infoClass;
          title = title || 'Alert';
          message = message || '';
          return ngDialog.open({
            plain: true,
            closeByNavigation: true,
            className: 'UI-modal UI-modal--alert',
            template: '<svg class="UI-info-block--decorator"><rect class="UI-decorator--base" height="100%"></rect><use xmlns:xlink="http://www.w3.org/1999/xlink" class="UI-decorator--icon" xlink:href="#icon--block-alert"></use></svg>
' + title + '
' + message + '
'
          });
        },
        modal: function (template, controller, more) {
          return ngDialog.openConfirm(angular.extend({
            closeByDocument: false,
            closeByNavigation: true,
            className: 'UI-modal UI-modal--dialog',
            template: template,
            controller: controller
          }, more));
        },
        view: function (template, controller, more) {
          return ngDialog.open(angular.extend({
            showClose: false,
            closeByEscape: false,
            closeByDocument: false,
            closeByNavigation: true,
            className: 'UI-modal UI-modal--view',
            template: template,
            controller: controller
          }, more));
        }
      };
    }
  ]);

Update the alert template a bit. Allow closing by ESC


function findAncestor(element, className) {
    while ((element = element.parentElement) && element && !hasClass(element, className));
    return element;
}

function hasClass(element, cls) {
    return (' ' + element.className + ' ').indexOf(' ' + cls + ' ') > -1;
}

module.exports.findAncestor = findAncestor;
module.exports.hasClass = hasClass;

function findAncestor(element, className) {
    if (element === null) {
        return null;
    }
    while ((element = element.parentElement) && element && !hasClass(element, className));
    return element;
}

function hasClass(element, cls) {
    return (' ' + element.className + ' ').indexOf(' ' + cls + ' ') > -1;
}

module.exports.findAncestor = findAncestor;
module.exports.hasClass = hasClass;

Fix onmouseout error when leaving window.


/**
 * Icon Story: Basic Usage
 */

import React from 'react';
import { select, color } from '@storybook/addon-knobs';
import Icon from '../Icon';
import icons from '../icons';
import css from './style.css';

export default () => {
  const size = select('Size', { small: 'Small', medium: 'Medium', large: 'Large' }, 'large');
  const status = select('Status', { warn: 'Warning', error: 'Error', success: 'Success', none: 'None' }, 'none');
  const list = Object.keys(icons).map((icon, i) => (

        <Icon
          size={size}
          icon={icon}
          status={status !== 'none' ? status : undefined}
        />

{icon}

  ));

  return (

        { list }

  );
};

/**
 * Icon Story: Basic Usage
 */

import React from 'react';
import { select } from '@storybook/addon-knobs';
import Icon from '../Icon';
import icons from '../icons';
import css from './style.css';

export default () => {
  const size = select('Size', { small: 'Small', medium: 'Medium', large: 'Large' }, 'large');
  const status = select('Status', { warn: 'Warning', error: 'Error', success: 'Success', none: 'None' }, 'none');
  const list = Object.keys(icons).map((icon, i) => (

        <Icon
          size={size}
          icon={icon}
          status={status !== 'none' ? status : undefined}
        />

{icon}

  ));

  return (

        { list }

  );
};

Remove unused var in story


var replaceAll = function( oldToken ) {
  var configs = this;
  return {
    from: function( string ) {
      return {
        to: function( newToken ) {
          var template;
          var index = -1;
          if ( configs.ignoringCase ) {
            template = oldToken.toLowerCase();
            while((
              index = string
                .toLowerCase()
                .indexOf(
                  template,
                  index === -1 ? 0 : index + newToken.length
                )
              ) !== -1 ) {
              string = string
                .substring( 0, index ) +
                  newToken +
                string.substring( index + newToken.length );
            }
            return string;
          }
          return string.split( oldToken ).join( newToken );
        }
      };
    },
    ignoringCase: function() {
      return replaceAll.call({
        ignoringCase: true
      }, oldToken );
    }
  };
};

module.exports = {
  all: replaceAll
};

var replaceAll = function( occurrences ) {
  var configs = this;
  return {
    from: function( target ) {
      return {
        to: function( replacement ) {
          var template;
          var index = -1;
          if ( configs.ignoringCase ) {
            template = occurrences.toLowerCase();
            while((
              index = target
                .toLowerCase()
                .indexOf(
                  template,
                  index === -1 ? 0 : index + replacement.length
                )
              ) !== -1 ) {
              target = target
                .substring( 0, index ) +
                  replacement +
                target.substring( index + replacement.length );
            }
            return target;
          }
          return target.split( occurrences ).join( replacement );
        }
      };
    },
    ignoringCase: function() {
      return replaceAll.call({
        ignoringCase: true
      }, occurrences );
    }
  };
};

module.exports = {
  all: replaceAll
};

Change the types according to the names in the README


export default class Controller {
    constructor(router, baseUrl = '') {
        this.router = router
        this.baseUrl = '/' + baseUrl.trimBoth('/')
    }

    get(relativeUrl, handler, name = null) {
        this.route('GET', this.url(relativeUrl), handler)
    }

    post(relativeUrl, handler, name = null) {
        this.route('POST', this.url(relativeUrl), handler)
    }

    delete(relativeUrl, handler, name = null) {
        this.route('DELETE', this.url(relativeUrl), handler)
    }

    patch(relativeUrl, handler, name = null) {
        this.route('PATCH', this.url(relativeUrl), handler)
    }

    put(relativeUrl, handler, name = null) {
        this.route('PUT', name, this.url(relativeUrl), handler)
    }

    route(method, path, handler, name = null) {
        var route = {
            method,
            path,
            config: {
                handler
            }
        }

        if (name)
            route.config.id = name

        this.router.route(route)
    }

    url(relativeUrl) {
        return (this.baseUrl + '/' + relativeUrl).normalize('/');
    }
}

export default class Controller {
    constructor(router, baseUrl = '') {
        this.router = router
        this.baseUrl = '/' + baseUrl.trimBoth('/')
    }

    get(relativeUrl, handler, name = null) {
        this.route('GET', this.url(relativeUrl), handler)
    }

    post(relativeUrl, handler, name = null) {
        this.route('POST', this.url(relativeUrl), handler)
    }

    delete(relativeUrl, handler, name = null) {
        this.route('DELETE', this.url(relativeUrl), handler)
    }

    patch(relativeUrl, handler, name = null) {
        this.route('PATCH', this.url(relativeUrl), handler)
    }

    put(relativeUrl, handler, name = null) {
        this.route('PUT', this.url(relativeUrl), handler)
    }

    route(method, path, handler, name = null) {
        var route = {
            method,
            path,
            config: {
                handler
            }
        }

        if (name)
            route.config.id = name

        this.router.route(route)
    }

    url(relativeUrl) {
        return (this.baseUrl + '/' + relativeUrl).normalize('/');
    }
}

Fix invalid route-syntax in base controller class. Affected route is related to PUT-routes. Previous behavior was still using the name-parameter when calling a route-helper function.


import Backbone from 'backbone';
import { appendStyles } from 'utils/mixins';

const $ = Backbone.$;

export default Backbone.View.extend({
  initialize() {
    const { model } = this;
    model.view = this;
    this.conf = model.config;
    this.pn = model.get('Panels');
    this.cv = model.get('Canvas');
    model.once('change:ready', () => {
      this.pn.active();
      this.pn.disableButtons();
      setTimeout(() => {
        model.trigger('load', model.get('Editor'));
      });
    });
  },

  render() {
    const { model, $el, conf } = this;
    const pfx = conf.stylePrefix;
    const contEl = $(conf.el || `body ${conf.container}`);
    appendStyles(conf.cssIcons, { unique: 1, prepand: 1 });
    $el.empty();

    if (conf.width) contEl.css('width', conf.width);
    if (conf.height) contEl.css('height', conf.height);

    $el.append(this.cv.render());
    $el.append(this.pn.render());
    $el.attr('class', `${pfx}editor ${pfx}one-bg ${pfx}two-color`);
    contEl
      .addClass(`${pfx}editor-cont`)
      .empty()
      .append($el);

    return this;
  }
});

import Backbone from 'backbone';
import { appendStyles } from 'utils/mixins';

const $ = Backbone.$;

export default Backbone.View.extend({
  initialize() {
    const { model } = this;
    model.view = this;
    this.conf = model.config;
    this.pn = model.get('Panels');
    this.cv = model.get('Canvas');
    model.once('change:ready', () => {
      this.pn.active();
      this.pn.disableButtons();
      model.set('changesCount', 0);
      setTimeout(() => {
        model.trigger('load', model.get('Editor'));
      });
    });
  },

  render() {
    const { model, $el, conf } = this;
    const pfx = conf.stylePrefix;
    const contEl = $(conf.el || `body ${conf.container}`);
    appendStyles(conf.cssIcons, { unique: 1, prepand: 1 });
    $el.empty();

    if (conf.width) contEl.css('width', conf.width);
    if (conf.height) contEl.css('height', conf.height);

    $el.append(this.cv.render());
    $el.append(this.pn.render());
    $el.attr('class', `${pfx}editor ${pfx}one-bg ${pfx}two-color`);
    contEl
      .addClass(`${pfx}editor-cont`)
      .empty()
      .append($el);

    return this;
  }
});

Reset changes count on editor ready


var _ = require('lodash');

var regexCache = {
	post: {}
};

hexo.extend.helper.register('is_post', function() {
	var permalink = this.config.permalink;
	var r = regexCache.post[permalink];
	if (!r) {
		var rUrl = permalink
			.replace(':lang', '[a-zA-Z]{1,8}(-[a-zA-Z0-9]{1,8})*')
			.replace(':id', '\\d+')
			.replace(':category', '(\\w+\\/?)+')
			.replace(':year', '\\d{4}')
			.replace(/:(month|day)/g, '\\d{2}')
			.replace(/:i_(month|day)/g, '\\d{1,2}')
			.replace(/:title/, '[^\\/]+');
		r = regexCache.post[permalink] = new RegExp(rUrl);
	}
	return r.test(this.path);
});

hexo.extend.helper.register('get_categories_for_lang', function(lang) {
	var result = [];
	_.each(this.site.data.categories, function(category, title) {
		_.each(category, function(data, categoryLang) {
			if (lang == categoryLang) {
				result = result.concat({
					title: data.name
				});
			}
		});
	});
	return result;
});

var _ = require('lodash');

var regexCache = {
	post: {}
};

hexo.extend.helper.register('is_post', function() {
	var permalink = this.config.permalink;
	var r = regexCache.post[permalink];
	if (!r) {
		var rUrl = permalink
			.replace(':lang', '[a-zA-Z]{1,8}(-[a-zA-Z0-9]{1,8})*')
			.replace(':id', '\\d+')
			.replace(':category', '(\\w+\\/?)+')
			.replace(':year', '\\d{4}')
			.replace(/:(month|day)/g, '\\d{2}')
			.replace(/:i_(month|day)/g, '\\d{1,2}')
			.replace(/:title/, '[^\\/]+');
		r = regexCache.post[permalink] = new RegExp(rUrl);
	}
	return r.test(this.path);
});

hexo.extend.helper.register('get_categories_for_lang', function(lang) {
	var result = [];
	_.each(this.site.data.categories, function(category, title) {
		_.each(category, function(data, categoryLang) {
			if (lang == categoryLang) {
				result = result.concat({
					title: data.name,
					path: lang + '/' + data.slug
				});
			}
		});
	});
	return result;
});

Add category path in category helper


const Promise = require('bluebird');
const db = require('./lib/db');

Promise.resolve()
  .then( () => db.startup() )
  .then( () => db.sqlite().migrate() )
  .catch( err => console.log(err) );

const Promise = require('bluebird');
const db = require('../lib/db');

Promise.resolve()
  .then( () => db.startup() )
  .then( () => db.sqlite().migrate() )
  .catch( err => console.log(err) );

Fix path to lib dir since this script moved


const Lang = imports.lang;

const Local = imports.misc.extensionUtils.getCurrentExtension();
const BaseProvider = Local.imports.BaseProvider;

const Api = new Lang.Class({
  Name: 'Bitso.Api',
  Extends: BaseProvider.Api,

  apiName: "Bitso",

  currencies: ['MXN'],

  coins: ['BTC','mBTC','ETH'],

  /* quote https://bitso.com/api_info#rate-limits
   *
   * > Rate limits are are based on one minute windows. If you do more than 30
   * > requests in a minute, you get locked out for one minute.
   */
  interval: 30,

  attributes: {
    last: function (options) {
      const renderCurrency = BaseProvider.CurrencyRenderer(options);
      const renderChange = BaseProvider.ChangeRenderer();

      return {
        text: (data) => renderCurrency(data.last),
        change: (data) => renderChange(data.last)
      };
    }
  },

  getLabel: function(options) {
    return "Bitso " + options.currency + "/" + options.coin;
  },

  getUrl: function(options) {
    const coin = BaseProvider.baseCoin(options.coin);
    return "https://api.bitso.com/v2/ticker?book=" + coin + "_" + options.currency;
  }
});

const Lang = imports.lang;

const Local = imports.misc.extensionUtils.getCurrentExtension();
const BaseProvider = Local.imports.BaseProvider;

const Api = new Lang.Class({
  Name: 'Bitso.Api',
  Extends: BaseProvider.Api,

  apiName: "Bitso",

  currencies: ['MXN'],

  coins: ['BTC','mBTC','ETH', 'XRP'],

  /* quote https://bitso.com/api_info#rate-limits
   *
   * > Rate limits are are based on one minute windows. If you do more than 30
   * > requests in a minute, you get locked out for one minute.
   */
  interval: 30,

  attributes: {
    last: function (options) {
      const renderCurrency = BaseProvider.CurrencyRenderer(options);
      const renderChange = BaseProvider.ChangeRenderer();

      return {
        text: (data) => renderCurrency(data.payload.last),
        change: (data) => renderChange(data.payload.last)
      };
    }
  },

  getLabel: function(options) {
    return "Bitso " + options.currency + "/" + options.coin;
  },

  getUrl: function(options) {
    const coin = BaseProvider.baseCoin(options.coin);
    return "https://api.bitso.com/v3/ticker?book=" + coin.toLowerCase() + "_" + options.currency.toLowerCase();
  }
});

Update bitso to newest api and add xrp coin


// Object with a diversity of properties to
// be asserted
var types = {
  string: String('hey'),
  number: Number(10),
  fn: function coolName () {},
  error: new Error(),
  date: new Date(),
  boolean: true,
  obj: {},
  array: [],
  buffer: new Buffer(1234),
  nulz: null,
  undf: undefined,
  regexp: /regexp/
}

module.exports = types

// Object with a diversity of properties to
// be asserted
var types = {
  string: 'hey',
  number: 10,
  fn: function coolName () {},
  error: new Error(),
  date: new Date(),
  boolean: true,
  obj: {},
  array: [],
  buffer: new Buffer(1234),
  nulz: null,
  undf: undefined,
  regexp: /regexp/
}

module.exports = types

Revert "change string and number"


/**
 * Copyright (c) 2013-2015 Memba Sarl. All rights reserved.
 * Sources at https://github.com/Memba
 */

/* jshint node: true, expr: true */
/* globals describe: false, before: false, it: false */

'use strict';

var request = require('supertest'),

    //We cannot define app like this because the server is already running
    //app = request('../../../webapp/server');

    config = require('../../../webapp/config'),
    app = config.get('uris:webapp:root');

describe('routes/sitemapRoute', function() {

    it('it should return an english sitemap', function(done) {
        request(app)
            .get('/en/sitemap.xml')
            .expect(200)
            .expect('Content-Type', /xml/)
            .end(done);
    });

    it('it should return a french sitemap', function(done) {
        request(app)
            .get('/fr/sitemap.xml')
            .expect(200)
            .expect('Content-Type', /xml/)
            .end(done);
    });

});

/**
 * Copyright (c) 2013-2015 Memba Sarl. All rights reserved.
 * Sources at https://github.com/Memba
 */

/* jshint node: true, expr: true */
/* globals describe: false, before: false, it: false */

'use strict';

var request = require('supertest'),
    util= require('util'),

    //We cannot define app like this because the server is already running
    //app = request('../../../webapp/server');

    config = require('../../../webapp/config'),
    app = config.get('uris:webapp:root');

describe('routes/sitemapRoute', function() {

    it('it should return an english sitemap', function(done) {
        request(app)
            .get(util.format(config.get('uris:webapp:sitemap'), 'en'))
            .expect(200)
            .expect('Content-Type', /xml/)
            .end(done);
    });

    it('it should return a french sitemap', function(done) {
        request(app)
            .get(util.format(config.get('uris:webapp:sitemap'), 'fr'))
            .expect(200)
            .expect('Content-Type', /xml/)
            .end(done);
    });

});

Use config to build paths


/* eslint no-console:off */
const fs = require('fs');
const { Decoder } = require('./lib/ebml.js');

const decoder = new Decoder();

decoder.on('data', chunk => console.log(chunk));

fs.readFile('media/test.webm', (err, data) => {
    if (err) {
        throw err;
    }
    decoder.write(data);
});

/* eslint no-console:off */
const fs = require("fs");
const { Decoder } = require("./lib/ebml.js");

const decoder = new Decoder();

decoder.on("data", chunk => console.log(chunk));

fs.readFile("media/test.webm", (err, data) => {
  if (err) {
    throw err;
  }
  decoder.write(data);
});

Apply fixes to commit 9d2b647


/*
 * @license
 * Copyright 2016 Google Inc. All Rights Reserved.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

foam.CLASS({
  package: 'foam.core',
  name: 'Int',
  extends: 'Property',

  properties: [
    'units',
    [ 'value', 0 ],
    [ 'adapt', function adaptInt(_, v) {
        return typeof v === 'number' ?
          Math.trunc(v) : v ? parseInt(v) : 0 ;
      }
    ]
  ]
});

/*
 * @license
 * Copyright 2016 Google Inc. All Rights Reserved.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

foam.CLASS({
  package: 'foam.core',
  name: 'Int',
  extends: 'Property',

  properties: [
    'units',
    [ 'value', 0 ],
    [ 'adapt', function adaptInt(_, v) {
        return typeof v === 'number' ? ( v > 0 ? Math.floor(v) : Math.ceil(v) ) :
          v ? parseInt(v) :
          0 ;
      }
    ]
  ]
});

Remove use of Math.truc() which isn't available in IE 11.


$(document).ready(function() {
  $("#input").focus();
  $("#input").on("keydown", function(event) {
    if (event.keyCode === 13) {
      event.preventDefault();
      controllers.submitQuery();
    }
  });
});

var controllers = {
  submitQuery: function() {
    $.post({
      url: '/submit',
      data: $("#input").val()
    }).done(function(data) {
      view.addQueryAndResponse();
    }).fail(function(err) {
      console.log(err);
    });
  }
};

var view = {
  addQueryAndResponse: function(query, response) {
    $("#bash").apppend("
$  "+ query + "\

chatbot$  " + response + "");
  }
};

$(document).ready(function() {
  $("#input").focus();
  $("#input").on("keydown", function(event) {
    if (event.keyCode === 13) {
      event.preventDefault();
      controllers.submitQuery();
    }
  });
});

var controllers = {
  submitQuery: function() {
    var query = $("#input").val();
    $.post({
      url: '/submit',
      data: query
    }).done(function(data) {
      if (data.success) {
        view.addQueryAndResponse(query, data.response);
      } else {
        console.log(err);
      }
    }).fail(function(err) {
      console.log(err);
    });
  }
};

var view = {
  addQueryAndResponse: function(query, response) {
    $("#bash").apppend("
$  "+ query + "\

chatbot$  " + response + "");
  }
};

Add query and response in function call


import { loginUser } from '../../gateways/users';
import { replace } from 'react-router-redux';
import { authSuccess, authFailure } from '../../common/auth/actions';
import { storeUser } from '../../utils/localStorage';
import { setAuthorization } from '../../gateways/index';

export const submitLogin = ({ username, password }) => (dispatch, getState) => {
    const { route: { location } } = getState();
    const redirectLocation = location.state && location.state.from;
    const fallbackLocation = {
        path: '/',
        state: {
            from: location,
        },
    };

    return loginUser({ username, password }).then(({ user }) => {
        // Set user in local storage to keep user signed in after page refresh
        storeUser(user);

        // Set Authorization header for future fetching
        setAuthorization(user.token);

        dispatch(authSuccess(user));
        dispatch(replace(redirectLocation || fallbackLocation));
    }).catch(() => {
        // TODO: Give user feedback on form on error
        dispatch(authFailure());
    });
};

import { loginUser } from '../../gateways/users';
import { replace } from 'react-router-redux';
import { authSuccess, authFailure } from '../../common/auth/actions';
import { storeUser } from '../../utils/localStorage';
import { setAuthorization } from '../../gateways/index';

export const submitLogin = ({ username, password }) => (dispatch, getState) => {
    const { route: { location } } = getState();
    const redirectLocation = location.state && location.state.from;
    const fallbackLocation = {
        pathname: '/',
        state: {
            from: location,
        },
    };

    return loginUser({ username, password }).then(({ user }) => {
        // Set user in local storage to keep user signed in after page refresh
        storeUser(user);

        // Set Authorization header for future fetching
        setAuthorization(user.token);

        dispatch(authSuccess(user));
        dispatch(replace(redirectLocation || fallbackLocation));
    }).catch(() => {
        // TODO: Give user feedback on form on error
        dispatch(authFailure());
    });
};

Fix incorrect property on fallback location object


import Orchestrator from 'orchestrator'
import TaskBuilder from './task-builder'

export const orchestrator = new Orchestrator()

/**
 * Creates a task of the given name and dependencies.
 *
 * @param {string} taskName The task name
 * @param {string|string[]} deps (The list of) the dependency task names
 */
export function createTask(taskName, deps) {

  const builder = new TaskBuilder(taskName)

  orchestrator.add(taskName, deps, () => builder.getTask().execute())

  const modifier = {

    /**
     * Adds the behavior of the action.
     * @param {Function} action The behavior o the action
     */
    do(action) {

      builder.addAction(action)

      return modifier

    },

    /**
     * @return {object}
     */
    get container() {

      return modifier

    },

    /**
     * Adds the way of getting a container
     * @param {string|string[]} names The names of the containers
     * @param {object} param The parameters for getting containers
     */
    get(names, params) {

      builder.containerGet(names, params)

      return modifier

    },

    /**
     * Adds the way of creating a container
     * @param {object|object[]} names The names of the containers
     */
    create(params) {

      builder.containerCreate(params)

      return modifier

    }

  }

  return modifier

}

import Orchestrator from 'orchestrator'
import TaskBuilder from './task-builder'

export const orchestrator = new Orchestrator()

/**
 * Creates a task of the given name and dependencies.
 *
 * @param {string} taskName The task name
 * @param {string|string[]} deps (The list of) the dependency task names
 */
export function createTask(taskName, deps) {

  const builder = new TaskBuilder(taskName)

  orchestrator.add(taskName, deps, () => builder.getTask().execute())

  const modifier = {

    /**
     * Adds the behavior of the action.
     * @param {Function} action The behavior o the action
     */
    do(action) {

      builder.addAction(action)

      return modifier

    },

    /**
     * Adds the way of getting a container
     * @param {string|string[]} names The names of the containers
     * @param {object} param The parameters for getting containers
     */
    get(names, params) {

      builder.containerGet(names, params)

      return modifier

    },

    /**
     * Adds the way of creating a container
     * @param {object|object[]} names The names of the containers
     */
    create(params) {

      builder.containerCreate(params)

      return modifier

    }

  }

  return modifier

}

Remove unnecessary node of DSL


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

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

  return ws;
}

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

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

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

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

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

  return websocket(url);
}

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

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

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

  return ws;
}

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

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

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

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

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

  return websocket(url);
}

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

Revert "Use port for protocol when none is provided"


'use strict';

var sessions = require('../sessions');

var kafkaConsumer = function (kafkaConfig) {
    var kafka = require('../services/kafka')(kafkaConfig.uri);
    var players = require('../players');
    return {
        addTraces: function (playerId, versionId, gameplayId, data) {
            return sessions.find({
                versionId: sessions.toObjectID(versionId),
                end: null
            }, false).then(function (sessions) {
                if (!sessions) {
                    return false;
                }
                return players.findById(playerId)
                    .then(function(player) {
                        if(!player) {
                            return false;
                        }

                        var promises = [];
                        for(var i = 0; i < sessions.length; ++i) {
                            var session = sessions[i];
                            if(!session.allowAnonymous) {
                                if (!session.students || session.students.indexOf(player.name) === -1) {
                                    continue;
                                }
                            }

                            promises.push(kafka.send(session._id.toString(), data));
                        }

                        return promises;
                    });
            });
        }
    };
};

module.exports = kafkaConsumer;

'use strict';

var sessions = require('../sessions');

var kafkaConsumer = function (kafkaConfig) {
    var kafka = require('../services/kafka')(kafkaConfig.uri);
    var players = require('../players');
    return {
        addTraces: function (playerId, versionId, gameplayId, data) {
            return sessions.find({
                versionId: sessions.toObjectID(versionId),
                start : {
                    $ne : null
                },
                end: null
            }, false).then(function (sessions) {
                if (!sessions) {
                    return false;
                }
                return players.findById(playerId)
                    .then(function (player) {
                        if (!player) {
                            return false;
                        }

                        var allowedSessions = [];
                        for (var i = 0; i < sessions.length; ++i) {
                            var session = sessions[i];
                            if (!session.allowAnonymous) {
                                if (!session.students || session.students.indexOf(player.name) === -1) {
                                    continue;
                                }
                            }
                            allowedSessions.push(session._id.toString());
                        }

                        if (allowedSessions.length > 0) {

                            var traces = [];
                            for(i = 0; i < data.length; ++i) {
                                traces.push(data[i].object.definition.extensions);
                            }

                            var promises = [];
                            for (i = 0; i < allowedSessions.length; ++i) {
                                promises.push(kafka.send(allowedSessions[i], traces));
                            }
                            return promises;
                        }

                        return false;
                    });
            });
        }
    };
};

module.exports = kafkaConsumer;

Send data using traces format to realtime analysis.


import React from 'react';
import ReactDOM from 'react-dom';
// import App from './components/app.jsx';

class App extends React.Component {
  render () {
    return (
      <h1>{"JIM"}</h1>
    );
  }
}

ReactDOM.render(
  <App />,
  document.getElementById('app')
);

import React from 'react';
import ReactDOM from 'react-dom';
// import App from './components/app.jsx';

class App extends React.Component {
  render () {
    return (
      <h1>{"JIM"}</h1>
    );
  }
}

ReactDOM.render(
  <App />,
  document.getElementById('mountpoint')
);

Attach to correct DOM node


/**
 * Userscript format:
 * http://wiki.greasespot.net/Metadata_Block
 */

module.exports = function extractMetablock (userscriptText) {
  try {
    var metablockReg = /\B\/\/ ==UserScript==\n([\S\s]*?)\n\/\/ ==\/UserScript==([\S\s]*)/
    var metablock = userscriptText.match(metablockReg)
    if (!metablock) {
      return null
    }

    var meta = {}
    var metaArray = metablock[1].split('\n')
    metaArray.forEach(function (m) {
      var parts = m.match(/@(\w+)\s+(.+)/)
      if (parts) {
        meta[parts[1]] = meta[parts[1]] || []
        meta[parts[1]].push(parts[2])
      }
    })

    var content = metablock[2]

    return {
      meta: meta,
      content: content
    }
  } catch(e) {
    if (console) console.error(e)
    return null
  }
}

/**
 * Userscript format:
 * http://wiki.greasespot.net/Metadata_Block
 */

module.exports = function extractMetablock (userscriptText) {
  try {
    var metablockReg = /\B\/\/ ==UserScript==\n([\S\s]*?)\n\/\/ ==\/UserScript==([\S\s]*)/
    var metablock = userscriptText.match(metablockReg)
    if (!metablock) {
      return null
    }

    var meta = {}
    var metaArray = metablock[1].split('\n')
    metaArray.forEach(function (m) {
      var parts = m.match(/@(\w+)\s+(.+)/)
      if (parts) {
        meta[parts[1]] = meta[parts[1]] || []
        meta[parts[1]].push(parts[2])
      }
    })

    var content = metablock[2]

    return {
      meta: meta,
      metablock: metablock,
      content: content
    }
  } catch(e) {
    if (console) console.error(e)
    return null
  }
}

Add raw `metablock` to parse result


'use strict';
var fs       = require('fs');
var tempfile = require('tempfile2');

module.exports = function createWriteStream(params) {
  var path = tempfile(params);
  var ws = fs.createWriteStream(path);
  var close = function() {
    ws.end();
    fs.close(ws.fd);
  };

  ws.path = path;

  ws.cleanup = function(cb) {
    close();
    fs.unlink(path, function(err) {
      if (err) ws.emit('error', err);
      if (cb) cb(err);
    });
  };

  ws.cleanupSync = function() {
    close();
    try {
      fs.unlinkSync(path);
    } catch (err) {
      ws.emit('error', err);
    }
  };

  return ws;
};

'use strict';
var fs       = require('fs');
var tempfile = require('tempfile2');

module.exports = function createWriteStream(params) {
  var path = tempfile(params);
  var writeStream = fs.createWriteStream(path);

  writeStream.path = path;

  writeStream.cleanup = function(cb) {
    writeStream.end();
    fs.unlink(path, function(err) {
      if (err) writeStream.emit('error', err);
      if (cb) cb(err);
    });
  };

  writeStream.cleanupSync = function() {
    writeStream.end();
    try {
      fs.unlinkSync(path);
    } catch (err) {
      writeStream.emit('error', err);
    }
  };

  return writeStream;
};

Revert "ensure to close the file"


'use strict';

var React = require('react'),
    withSideEffect = require('react-side-effect');

function reducePropsToState(propsList) {
  var innermostProps = propsList[propsList.length - 1];
  if (innermostProps) {
    return innermostProps.title;
  }
}

function handleStateChangeOnClient(title) {
  document.title = title || '';
}

var DocumentTitle = React.createClass({
  displayName: 'DocumentTitle',

  propTypes: {
    title: React.PropTypes.string.isRequired
  },

  render: function render() {
    if (this.props.children) {
      return React.Children.only(this.props.children);
    } else {
      return null;
    }
  }
});

module.exports = withSideEffect(
  reducePropsToState,
  handleStateChangeOnClient
)(DocumentTitle);

'use strict';

var React = require('react'),
    withSideEffect = require('react-side-effect');

function reducePropsToState(propsList) {
  var innermostProps = propsList[propsList.length - 1];
  if (innermostProps) {
    return innermostProps.title;
  }
}

function handleStateChangeOnClient(title) {
  if (title !== document.title) {
    document.title = title || '';
  }
}

var DocumentTitle = React.createClass({
  displayName: 'DocumentTitle',

  propTypes: {
    title: React.PropTypes.string.isRequired
  },

  render: function render() {
    if (this.props.children) {
      return React.Children.only(this.props.children);
    } else {
      return null;
    }
  }
});

module.exports = withSideEffect(
  reducePropsToState,
  handleStateChangeOnClient
)(DocumentTitle);

Check if title should be updated


module.exports = function buildPreset(context, options) {
  return {
    presets: [
      [require('babel-preset-latest'), { es2015: options }],
    ],
    plugins: [
      require('babel-plugin-transform-class-properties'),
      require('babel-plugin-transform-decorators'),
      require('babel-plugin-transform-export-extensions'),
      require('babel-plugin-transform-object-rest-spread'),
      require('babel-plugin-add-module-exports'),
    ],
  };
};

const defaultOptions = {
  modules: false,
};

module.exports = function buildPreset(context, options) {
  return {
    presets: [
      [require('babel-preset-latest'), {
        es2015: Object.assign({}, defaultOptions, options),
      }],
    ],
    plugins: [
      require('babel-plugin-transform-class-properties'),
      require('babel-plugin-transform-decorators'),
      require('babel-plugin-transform-export-extensions'),
      require('babel-plugin-transform-object-rest-spread'),
      require('babel-plugin-add-module-exports'),
    ],
  };
};

Disable module transformer by default.


// also add animation like http://codepen.io/lbebber/pen/KwGEQv

function renderStatus(statusText) {
	document.getElementById('status').textContent = statusText;
}

document.addEventListener('DOMContentLoaded', function() {

});

Add link to additional animation


import { User } from './ActionTypes';
import { post } from '../http';
import { replaceWith } from 'redux-react-router';

function putInLocalStorage(key) {
  return (payload) => {
    window.localStorage.setItem(key, JSON.stringify(payload));
    return payload;
  };
}

function performLogin(username, password) {
  return post('/login', { username, password })
    .then(data => ({ username, token: data.authToken }))
    .then(putInLocalStorage('user'));
}

export function login(username, password) {
  return {
    type: User.LOGIN,
    promise: performLogin(username, password)
  };
}

export function logout() {
  return (dispatch) => {
    window.localStorage.removeItem('user');
    dispatch({ type: User.LOGOUT });
    dispatch(replaceWith('/'));
  };
}

export function loginWithExistingToken(username, token) {
  return {
    type: User.LOGIN_SUCCESS,
    payload: { username, token }
  };
}

/**
 * Dispatch a login success if a token exists in local storage.
 */
export function loginAutomaticallyIfPossible() {
  return (dispatch) => {
    const { username, token } = JSON.parse(window.localStorage.getItem('user')) || {};
    if (username && token) {
      dispatch(loginWithExistingToken(username, token));
    }
  };
}

import { User } from './ActionTypes';
import { post } from '../http';
import { replaceWith } from 'redux-react-router';

function putInLocalStorage(key) {
  return (payload) => {
    window.localStorage.setItem(key, JSON.stringify(payload));
    return payload;
  };
}

function clearLocalStorage(key) {
  window.localStorage.removeItem(key);
}

function performLogin(username, password) {
  return post('/token-auth/', { username, password })
    .then(putInLocalStorage('user'));
}

export function refreshToken(token) {
  return post('/token-auth/refresh/', { token })
    .then(putInLocalStorage('user'))
    .catch(err => {
      clearLocalStorage('user');
      throw err;
    });
}

export function login(username, password) {
  return {
    type: User.LOGIN,
    promise: performLogin(username, password)
  };
}

export function logout() {
  return (dispatch) => {
    window.localStorage.removeItem('user');
    dispatch({ type: User.LOGOUT });
    dispatch(replaceWith('/'));
  };
}

export function loginWithExistingToken(token) {
  return {
    type: User.LOGIN,
    promise: refreshToken(token)
  };
}

/**
 * Dispatch a login success if a token exists in local storage.
 */
export function loginAutomaticallyIfPossible() {
  return (dispatch) => {
    const { token } = JSON.parse(window.localStorage.getItem('user')) || {};
    if (token) {
      dispatch(loginWithExistingToken(token));
    }
  };
}

Refresh the JWT token on page load


import Vue from 'vue';
import Translate from '~/vue_shared/translate';
import axios from '~/lib/utils/axios_utils';

const testTimeoutInMs = 300;
jest.setTimeout(testTimeoutInMs);

let testStartTime;

// https://github.com/facebook/jest/issues/6947
beforeEach(() => {
  testStartTime = Date.now();
});

afterEach(() => {
  const elapsedTimeInMs = Date.now() - testStartTime;
  if (elapsedTimeInMs > testTimeoutInMs) {
    throw new Error(`Test took too long (${elapsedTimeInMs}ms > ${testTimeoutInMs}ms)!`);
  }
});

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

  done();
});

Vue.use(Translate);

Load i18n Vue plugin for Jest tests


'use strict';

var each = require('lodash.foreach');
var get = require('jaunt').get;

var identity = function(o) {
  return o;
};

var taxonomist = function(arr, prop, fn) {

  fn = fn || identity;

  var result = {};
  each(arr, function(val) {
    var tags = [].concat(get(val, prop)).filter(Boolean);
    each(tags, function(tag) {
      tag = fn(tag);
      // create empty [] if `tag` is not a key in `result`
      if (tag in result === false) {
        result[tag] = [];
      }
      result[tag].push(val);
    });
  });

  return result;

};

module.exports = taxonomist;

'use strict';

var each = require('lodash.foreach');
var get = require('jaunt').get;

var identity = function(o) {
  return o;
};

var taxonomist = function(arr, prop, fn) {

  fn = fn || identity;

  var result = {};
  each(arr, function(val) {
    var tags = [].concat(get(val, prop)).filter(Boolean);
    each(tags, function(tag) {
      tag = fn(tag);
      // create empty [] if `tag` is not a key in `result`
      if (!(tag in result)) {
        result[tag] = [];
      }
      result[tag].push(val);
    });
  });

  return result;

};

module.exports = taxonomist;

Use `!` instead of `=== false`


define(function(require){
    var TimeLine = require("timeline");

    var renderer = new THREE.WebGLRenderer( {antialias: true, alpha:true} );
    renderer.shadowMapEnabled = true;

    var scene;
    var timeSource;

    return {
        init:function(){
            renderer.autoClear = false;
            $(window).resize(resizeViewPort);
            document.body.appendChild( renderer.domElement );
            $(renderer.domElement).hide();
            resizeViewPort();
        },
        setTimeSource: function(argTimeSource){
            timeSource = argTimeSource;
        },
        start: function() {
            $(renderer.domElement).fadeIn(1000);
            function render() {
                scene = TimeLine.getScene();
                if(!scene){
                    requestAnimationFrame(render);
                    return;
                }
                scene.render(timeSource.getTime());
                requestAnimationFrame(render);
                renderer.render(scene.scene, scene.camera);
            }
            render();
        },
        renderer: renderer
    };

    function resizeViewPort() {
        var width = window.innerWidth;
        var height = width * (9 / 16);
        var position = (window.innerHeight - height) / 2;
        renderer.setSize(width, height);
        $("canvas").css("margin-top", position + "px");
    }
});

define(function(require){
    var TimeLine = require("timeline");

    var renderer = new THREE.WebGLRenderer( {antialias: true, alpha:true} );
    renderer.shadowMapEnabled = true;

    var scene;
    var timeSource;

    return {
        init:function(){
            renderer.autoClear = false;
            $(window).resize(resizeViewPort);
            document.body.appendChild( renderer.domElement );
            $("canvas").hide();
            resizeViewPort();
        },
        setTimeSource: function(argTimeSource){
            timeSource = argTimeSource;
        },
        start: function() {
            $("canvas").fadeIn(3000);
            function render() {
                scene = TimeLine.getScene();
                if(!scene){
                    requestAnimationFrame(render);
                    return;
                }
                scene.render(timeSource.getTime());
                requestAnimationFrame(render);
                renderer.render(scene.scene, scene.camera);
            }
            render();
        },
        renderer: renderer
    };

    function resizeViewPort() {
        var width = window.innerWidth;
        var height = width * (9 / 16);
        var position = (window.innerHeight - height) / 2;
        renderer.setSize(width, height);
        $("canvas").css("margin-top", position + "px");
    }
});

Make initial scene fadein slower.


'use strict';

let geolocation = require('nativescript-geolocation');
let requester = require('../web/requester').defaultInstance;
let cacheService = require('../common/cache/cache-service');
let constants = require('../common/constants');
let settings = require('../common/settings');

class GeolocationHelper {
  isEnabled() {
    return geolocation.isEnabled();
  }

  enableLocationRequest() {
    return geolocation.enableLocationRequest();
  }

  getCurrentLocation(configuration) {
    if (settings.isCacheEnabled() && cacheService.hasItem(constants.CACHE_LOCATION_KEY)) {
      return new Promise(function (resolve, reject) {
        resolve(cacheService.getItem(constants.CACHE_LOCATION_KEY));
      });
    }

    let promise = new Promise(function (resolve, reject) {
      geolocation.getCurrentLocation(configuration)
        .then(function(location) {
          if (settings.isCacheEnabled()) {
            cacheService.addItem(location);
          }

          resolve(location);
        }, function (err) {
          return requester.getJsonAsync('http://ipinfo.io/json', true);
        })
        .then(function (response) {
          let latitude = +response.loc.split(',')[0];
          let longitude = +response.loc.split(',')[1];

          let location = {
            latitude,
            longitude
          };

          resolve(location);
        }, reject);
    });

    return promise;
  }
}

module.exports = {
  GeolocationHelper: GeolocationHelper,
  defaultInstance: new GeolocationHelper()
};

'use strict';

let geolocation = require('nativescript-geolocation');
let requester = require('../web/requester').defaultInstance;
let cacheService = require('../common/cache/cache-service');
let constants = require('../common/constants');
let settings = require('../common/settings');

class GeolocationHelper {
  isEnabled() {
    return geolocation.isEnabled();
  }

  enableLocationRequest() {
    return geolocation.enableLocationRequest();
  }

  getCurrentLocation(configuration) {
    if (settings.isCacheEnabled() && cacheService.hasItem(constants.CACHE_LOCATION_KEY)) {
      return new Promise(function (resolve, reject) {
        resolve(cacheService.getItem(constants.CACHE_LOCATION_KEY));
      });
    }

    let promise = new Promise(function (resolve, reject) {
      geolocation.getCurrentLocation(configuration)
        .then(function(location) {
          if (settings.isCacheEnabled()) {
            let cacheDuration = settings.cacheDuration();
            cacheService.addItem(constants.CACHE_LOCATION_KEY, location, cacheDuration);
          }

          resolve(location);
        }, function (err) {
          return requester.getJsonAsync('http://ipinfo.io/json', true);
        })
        .then(function (response) {
          let latitude = +response.loc.split(',')[0];
          let longitude = +response.loc.split(',')[1];

          let location = {
            latitude,
            longitude
          };

          resolve(location);
        }, reject);
    });

    return promise;
  }
}

module.exports = {
  GeolocationHelper: GeolocationHelper,
  defaultInstance: new GeolocationHelper()
};

Fix adding the geolocation to the cache


/**
 * Send stanza.
 *
 * Response stanzas prepared by Junction are extended with this function.
 *
 * @api public
 */
exports.send = function() {
  this.connection.send(this);
}

Document send() extension to Stanza.


define([
  'json3',
],

function (JSON) {
  'use strict';

  return {};
});

define([
  'json3',
  'ember',
],

function (JSON, Em) {
  'use strict';

  // create a hook for jQuery logic that will run after
  // a view and all child views have been rendered,
  // since didInsertElement runs only when the view's el
  // has rendered, and not necessarily all child views.
  // Note: if you use this hook, and also override didInsertElement,
  //       be sure to call this._super() in your overridden didInsertElement.
  //
  // http://mavilein.github.io/javascript/2013/08/01/Ember-JS-After-Render-Event/
  Em.View.reopen({
    didInsertElement : function(){
      this._super();
      Em.run.scheduleOnce('afterRender', this, this.afterRenderEvent);
    },
    afterRenderEvent : function(){
      // implement this hook in your own subclasses and run your jQuery logic there
    }
  });

  return {};
});

Add hook for a view for when all its child views have been rendered


import React, { Component } from 'react';
import { connect } from 'react-redux';
import map from 'lodash/map';

class Profile extends Component {
  render() {
    const { profile, assets } = this.props;
    if ( ! assets.data.Hairstyles) return
;
    return (

        {map(assets.data, asset => (
          <img src={`svg/${asset.id}/${assets.data[asset.id].files[profile[asset.id]]}`} />
        ))}

    );
  }
}

export default connect(({ profile, assets }) => ({ profile, assets }))(Profile);

import React, { Component } from 'react';
import { connect } from 'react-redux';
import map from 'lodash/map';

class Profile extends Component {
  render() {
    const { profile, assets } = this.props;
    if ( ! assets.data.Hairstyles) return
;
    const data = map(assets.data);
    data.sort((a, b) => a.sortOrder - b.sortOrder);
    return (

        {map(data, asset => (
          <img key={asset.id} src={`svg/${asset.id}/${assets.data[asset.id].files[profile[asset.id]]}`} />
        ))}

    );
  }
}

export default connect(({ profile, assets }) => ({ profile, assets }))(Profile);

Sort assets on client side


/* @flow */
import type { Template } from 'create-npm/lib/template'

export default function({
  encryptedNpmToken,
  encryptedGitHubToken
}: {
  encryptedNpmToken: string,
  encryptedGitHubToken: string
}): Template {
  return {
    path: '.travis.yml',
    content: `
      dist: xenial
      language: node_js
      node_js: node
      cache: yarn
      env:
        global:
        - secure: ${encryptedGitHubToken}
        - secure: ${encryptedNpmToken}
      branches:
        only:
          - master
      deploy:
        provider: script
        skip_cleanup: true
        script:
        - yarn release
    `
  }
}

/* @flow */
import type { Template } from 'create-npm/lib/template'

export default function({
  encryptedNpmToken,
  encryptedGitHubToken
}: {
  encryptedNpmToken: string,
  encryptedGitHubToken: string
}): Template {
  return {
    path: '.travis.yml',
    content: `
      dist: xenial
      language: node_js
      node_js: node
      cache: yarn
      env:
        global:
        - secure: ${encryptedGitHubToken}
        - secure: ${encryptedNpmToken}
      if: tag IS blank
      deploy:
        provider: script
        skip_cleanup: true
        script:
        - yarn release
    `
  }
}

Use a cleaner way of excluding release tags


/*
  Normilize Footnotes contents.
  Removes everything except textual paragraphs from Footnotes.
*/
export default class NormalizeFn {

  import(dom) {
    let fns = dom.findAll('fn')
    fns.forEach(fn => {
      let paragraphs = []
      let ptags = fn.findAll('p')
      ptags.forEach(p => {
        if(p.children.length === 0) {
          paragraphs.push(p.getTextContent())
        }
      })

      fn.children.forEach(child => {
        fn.removeChild(child)
      })
      paragraphs.forEach(text => {
        fn.append(
          dom.createElement('p').append(text)
        )
      })
    })
  }

  export() {
    // nothing
  }
}

/*
  Normalize footnotes contents.
  Removes everything except textual paragraphs from footnotes.
*/
export default class NormalizeFn {

  import(dom) {
    let fns = dom.findAll('fn')
    fns.forEach(fn => {
      // Find all ptags that are nested in another p tag
      let ptags = fn.findAll('p p')
      // If any nested paragraphs are found we need to take action
      if (ptags.length > 0) {
        fn.empty()
        fn.append(ptags)
      }
    })
  }

  export() {
    // nothing
  }
}

Make transformer more robust, was loosing annotations.


"use strict"
Browser.platform.cordova = (window.Phonegap || window.Cordova || window.cordova) && Browser.isMobile && !Browser.safari;

"use strict"
// Browser.platform.cordova = (window.Phonegap || window.Cordova || window.cordova) && Browser.isMobile && !Browser.safari;

Fix an issue where Browser.platform is now readonly, thus can't add cordova


var express = require('express');
var app = express();

app.set('port', (process.env.PORT || 5000));

app.use(express.static(__dirname + '/screens'));
app.use(express.static(__dirname + '/public'));

// views is directory for all template files
app.set('views', __dirname + '/views');
app.set('view engine', 'ejs');

app.get('/', function(request, response) {
  response.render('pages/index');
});

app.listen(app.get('port'), function() {
  console.log('Node app is running on port', app.get('port'));
});

var express = require('express');
var app = express();

app.set('port', (process.env.PORT || 5000));

app.use('/screens', express.static(__dirname + '/screens'));
app.use('/public', express.static(__dirname + '/public'));

// views is directory for all template files
app.set('views', __dirname + '/views');
app.set('view engine', 'ejs');

app.get('/', function(request, response) {
  response.render('pages/index');
});

app.listen(app.get('port'), function() {
  console.log('Node app is running on port', app.get('port'));
});

Add scoping of static assets


(function(app){

  "use strict"

  /**
   * Creates a new HTML element ov-index to create an openVeo player
   * index, with a list of presentation slides.
   * It requires ovPlayerDirectory global variable to be defined and have
   * a value corresponding to the path of the openVeo Player
   * root directory.
   *
   * e.g.
   * <ov-index></ov-index>
   */
  app.directive("ovChapters", ovChapters);
  ovChapters.$inject = ["ovChaptersLink"];

  function ovChapters(ovChaptersLink){
    return {
      require : "^ovPlayer",
      restrict : "E",
      templateUrl : ovPlayerDirectory + "/templates/chapters.html",
      scope : true,
      link : ovChaptersLink
    }
  }

  app.factory("ovChaptersLink", function(){
    return function(scope, element, attrs, playerCtrl){
      scope.chapters = scope.data.chapter;

      scope.open = function(chapter){
        if(!chapter.isOpen)
          angular.forEach(scope.chapters, function (value, key) {
            value.isOpen = false;
          });
        chapter.isOpen = !chapter.isOpen;
      }
      /**
       * Seeks media to the given timecode.
       * @param Number timecode The timecode to seek to
       */
      scope.goToTimecode = function(time){
        if(time <= 1)
          playerCtrl.player.setTime(time * scope.duration);
      };

    };
  });

})(angular.module("ov.player"));

(function(app){

  "use strict"

  /**
   * Creates a new HTML element ov-index to create an openVeo player
   * index, with a list of presentation slides.
   * It requires ovPlayerDirectory global variable to be defined and have
   * a value corresponding to the path of the openVeo Player
   * root directory.
   *
   * e.g.
   * <ov-index></ov-index>
   */
  app.directive("ovChapters", ovChapters);
  ovChapters.$inject = ["ovChaptersLink"];

  function ovChapters(ovChaptersLink){
    return {
      require : "^ovPlayer",
      restrict : "E",
      templateUrl : ovPlayerDirectory + "templates/chapters.html",
      scope : true,
      link : ovChaptersLink
    }
  }

  app.factory("ovChaptersLink", function(){
    return function(scope, element, attrs, playerCtrl){
      scope.chapters = scope.data.chapter;

      scope.open = function(chapter){
        if(!chapter.isOpen)
          angular.forEach(scope.chapters, function (value, key) {
            value.isOpen = false;
          });
        chapter.isOpen = !chapter.isOpen;
      }
      /**
       * Seeks media to the given timecode.
       * @param Number timecode The timecode to seek to
       */
      scope.goToTimecode = function(time){
        if(time <= 1)
          playerCtrl.player.setTime(time * scope.duration);
      };

    };
  });

})(angular.module("ov.player"));

Remove slash from chapters partial path


// CoffeeScript compilation. This must be enabled by modification
// of Gruntfile.js.
//
// The `bare` option is used since this file will be transpiled
// anyway. In CoffeeScript files, you need to escape out for
// some ES6 features like import and export. For example:
//
// `import 'appkit/models/user' as User`
//
// Posts = Em.Object.extend
//   init: (userId) ->
//     @set 'user', User.findById(userId)
//
// `export = Posts`
//

module.exports = {
  "test": {
    options: {
      bare: true
    },
    files: [{
      expand: true,
      cwd: 'tests/',
      src: ['**/*.coffee', '!vendor/**/*.coffee'],
      dest: 'tmp/javascript/tests',
      ext: '.js'
    }]
  },
  "app": {
    options: {
      bare: true
    },
    files: [{
      expand: true,
      cwd: 'app/',
      src: '**/*.coffee',
      dest: 'tmp/javascript/app',
      ext: '.js'
    }]
  }
};

// CoffeeScript compilation. This must be enabled by modification
// of Gruntfile.js.
//
// The `bare` option is used since this file will be transpiled
// anyway. In CoffeeScript files, you need to escape out for
// some ES6 features like import and export. For example:
//
// `import User from 'appkit/models/user'`
//
// Post = Em.Object.extend
//   init: (userId) ->
//     @set 'user', User.findById(userId)
//
// `export default Post`
//

module.exports = {
  "test": {
    options: {
      bare: true
    },
    files: [{
      expand: true,
      cwd: 'tests/',
      src: ['**/*.coffee', '!vendor/**/*.coffee'],
      dest: 'tmp/javascript/tests',
      ext: '.js'
    }]
  },
  "app": {
    options: {
      bare: true
    },
    files: [{
      expand: true,
      cwd: 'app/',
      src: '**/*.coffee',
      dest: 'tmp/javascript/app',
      ext: '.js'
    }]
  }
};

Update CoffeeScript docs to use the current ES6 syntax


module.exports = function (babel) {
  var exportIds = [];
  var t = babel.types;
  return new babel.Transformer("babel-jsm-plugin", {
    ExportNamedDeclaration: {
      enter: function(node, parent) {
        // For variable declarations since exports will have multiple id names in one
        if (node.declaration.declarations) {
          node.declaration.declarations.forEach(function(declaration) {
            exportIds.push(declaration.id.name);
          }.bind(this));
          return node.declaration;
        }
        exportIds.push(node.declaration.id.name);
        // Replace with declarations, which removes the export
        return node.declaration;
      },
    },
    Program: {
      exit: function(node, parent) {
        var arrayOfSymbols = t.arrayExpression([]);
        exportIds.forEach(function(exportedId) {
          // Create an array of strings with the export identifier names
          arrayOfSymbols.elements.push(t.literal(exportedId));

          // Add in this.identifier = identifier for each export and add it to the end
          var assignmentStatement = t.expressionStatement(
            t.assignmentExpression('=', t.identifier('this.' + exportedId), t.identifier(exportedId)));
          this.pushContainer('body', assignmentStatement);
        }.bind(this));

        // Create an assignment for this.EXPORTED_SYMBOLS = ['export1', 'export2', ...]
        var exportsVar = t.expressionStatement(
          t.assignmentExpression('=', t.identifier('this.EXPORTED_SYMBOLS'), arrayOfSymbols));
        this.unshiftContainer('body', exportsVar);
      },
    },
  });
};

var exportIds = [];
function exportEnterHandler(node, parent) {
  // For variable declarations since exports will have multiple id names in one
  if (node.declaration.declarations) {
    node.declaration.declarations.forEach(function(declaration) {
      exportIds.push(declaration.id.name);
    }.bind(this));
    return node.declaration;
  }
  exportIds.push(node.declaration.id.name);
  // Replace with declarations, which removes the export
  return node.declaration;
}

module.exports = function (babel) {
  var t = babel.types;
  return new babel.Transformer("babel-jsm-plugin", {
    ExportNamedDeclaration: {
      enter: exportEnterHandler.bind(this),
    },
    ExportDefaultDeclaration: {
      enter: exportEnterHandler.bind(this),
    },
    Program: {
      exit: function(node, parent) {
        var arrayOfSymbols = t.arrayExpression([]);
        exportIds.forEach(function(exportedId) {
          // Create an array of strings with the export identifier names
          arrayOfSymbols.elements.push(t.literal(exportedId));

          // Add in this.identifier = identifier for each export and add it to the end
          var assignmentStatement = t.expressionStatement(
            t.assignmentExpression('=', t.identifier('this.' + exportedId), t.identifier(exportedId)));
          this.pushContainer('body', assignmentStatement);
        }.bind(this));

        // Create an assignment for this.EXPORTED_SYMBOLS = ['export1', 'export2', ...]
        var exportsVar = t.expressionStatement(
          t.assignmentExpression('=', t.identifier('this.EXPORTED_SYMBOLS'), arrayOfSymbols));
        this.unshiftContainer('body', exportsVar);
      },
    },
  });
};

Support default exports with babel-jsm-plugin


module.exports = {
  "extends": [
    "eslint-config-airbnb",
    "eslint-config-standard"
  ],
  "parser": "babel-eslint",
  "plugins": [
    "flow-vars"
  ],
  "rules": {
    "strict": 0,
    "brace-style": [2, "stroustrup"],
    "comma-dangle": [2, "always-multiline"],
    "flow-vars/define-flow-type": 1,
    "flow-vars/use-flow-type": 1,
    "padded-blocks": [2, "always"]
  }
}

module.exports = {
  "extends": [
    "eslint-config-airbnb",
    "eslint-config-standard"
  ],
  "parser": "babel-eslint",
  "plugins": [
    "flow-vars"
  ],
  "rules": {
    "strict": 0,
    "arrow-body-style": [2, "always"],
    "brace-style": [2, "stroustrup"],
    "comma-dangle": [2, "always-multiline"],
    "flow-vars/define-flow-type": 1,
    "flow-vars/use-flow-type": 1,
    "padded-blocks": [2, "always"]
  }
}

Add arrow-body-style override for consistency


var Generator = require('generate-js'),
    Joi = require('joi'),
    async = require('async');

var EndPoint = Generator.generate(function EndPoint(options) {
    var _ = this;

    _.defineProperties(options);
});

EndPoint.Joi = Joi;

EndPoint.definePrototype({
    incomingSchema: Joi.object(),
    outgoingSchema: Joi.object(),
    run: function run(data, done) {
        var _ = this;

        function validateIncoming(next) {
            console.log("validateIncoming", data);
            Joi.validate(data, _.incomingSchema, _.validateOptions, next);
        }

        function runIncoming(data, next) {
            console.log("runIncoming", data);
            _.incoming(data, next);
        }

        function validateOutgoing(data, next) {
            console.log("validateOutgoing", data);
            Joi.validate(data, _.outgoingSchema, _.validateOptions, next);
        }

        function runOutgoing(data, next) {
            console.log("runOutgoing", data);
            _.outgoing(data, next);
        }

        async.waterfall([
            validateIncoming,
            runIncoming,
            validateOutgoing,
            runOutgoing
        ], done);
    },

    incoming: function incoming(data, done) {
        var _ = this;

        done(null, data);
    },

    outgoing: function outgoing(data, done) {
        var _ = this;

        done(null, data);
    }
});

module.exports = EndPoint;

var Generator = require('generate-js'),
    Joi = require('joi'),
    async = require('async');

var EndPoint = Generator.generate(function EndPoint(options) {
    var _ = this;

    _.defineProperties(options);
    _.debug = false;
});

EndPoint.Joi = Joi;

EndPoint.definePrototype({
    incomingSchema: Joi.object(),
    outgoingSchema: Joi.object(),
    run: function run(data, done) {
        var _ = this;

        function validateIncoming(next) {
            _.debug && console.log("validateIncoming", data);
            Joi.validate(data, _.incomingSchema, _.validateOptions, next);
        }

        function runIncoming(data, next) {
            _.debug && console.log("runIncoming", data);
            _.incoming(data, next);
        }

        function validateOutgoing(data, next) {
            console.log("validateOutgoing", data);
            Joi.validate(data, _.outgoingSchema, _.validateOptions, next);
        }

        function runOutgoing(data, next) {
            _.debug && console.log("runOutgoing", data);
            _.outgoing(data, next);
        }

        async.waterfall([
            validateIncoming,
            runIncoming,
            validateOutgoing,
            runOutgoing
        ], done);
    },

    incoming: function incoming(data, done) {
        var _ = this;

        done(null, data);
    },

    outgoing: function outgoing(data, done) {
        var _ = this;

        done(null, data);
    }
});

module.exports = EndPoint;

Add debug mode to hide logs.


module.exports = {
  name: 'ember-cli-toggle',

  included: function(app) {
    app.import('vendor/ember-cli-toggle/base.css');

    // Use configuration to decide which theme css files
    // to import, thus not populating the user's app
    this.importThemes(app);
  },

  importThemes: function(app) {
    var projectConfig = this.project.config(app.env);
    var config = projectConfig['ember-cli-toggle'];

    if (config && Object.keys(config).length) {
      var allThemes = ['light', 'ios', 'default', 'flat', 'skewed', 'flip'];
      var included = config.includedThemes;
      var excluded = config.excludedThemes;
      var themes = [];

      if (included && Array.isArray(included)) {
        themes = themes.concat(included);
      }
      else {
        themes = allThemes;
      }

      if (excluded && Array.isArray(excluded)) {
        themes = themes.filter(function (theme) {
          return excluded.indexOf(theme) === -1;
        });
      }

      themes = themes.filter(function (theme) {
        return theme && allThemes.indexOf(theme) !== -1;
      });

      themes.forEach(function (theme) {
        app.import('vendor/ember-cli-toggle/themes/' + theme + '.css');
      });
    }
  }
};

module.exports = {
  name: 'ember-cli-toggle',

  included: function(app) {
    app.import('vendor/ember-cli-toggle/base.css');

    // Use configuration to decide which theme css files
    // to import, thus not populating the user's app
    this.importThemes(app);
  },

  importThemes: function(app) {
    var projectConfig = this.project.config(app.env);
    var config = projectConfig['ember-cli-toggle'];

    if (config) {
      var allThemes = ['light', 'ios', 'default', 'flat', 'skewed', 'flip'];
      var included = config.includedThemes;
      var excluded = config.excludedThemes;
      var themes = [];

      if (included && Array.isArray(included)) {
        themes = themes.concat(included);
      }
      else {
        themes = allThemes;
      }

      if (excluded && Array.isArray(excluded)) {
        themes = themes.filter(function (theme) {
          return excluded.indexOf(theme) === -1;
        });
      }

      themes = themes.filter(function (theme) {
        return theme && allThemes.indexOf(theme) !== -1;
      });

      themes.forEach(function (theme) {
        app.import('vendor/ember-cli-toggle/themes/' + theme + '.css');
      });
    }
  }
};

Make sure themes come in if no config present


import React, { Component, PropTypes } from 'react'
import Relay from 'react-relay';

export default class CategoryFilter extends Component {

  static propTypes = {
    category: PropTypes.object.isRequired
  }

  render() {
    const { category } = this.props;
    return (

Categories:

            {category.name}
            {category.productsCount}

          {category.siblings && (category.siblings.map((sibling) => {
              return (

                  {sibling.name}
                  {sibling.productsCount}

              );
            })
          )}

    );
  }
}

Add bold to current category on list


var config = require('./config');
var redisClient = require('redis').createClient(config.redis_port, config.redis_host);

var loadedScriptStrings = {};

exports.client = redisClient;

exports.ensureScript = function(client, scriptString, cb) {
  if (scriptString in loadedScriptStrings) { return cb(null); }
  client.script('load', scriptString, function(err) {
    if (err) { return cb(err); }
    loadedScriptStrings[scriptString] = true;
    return cb(null);
  });
};

var config = require('./config');
var redisClient = require('redis').createClient(config.redis_port, config.redis_host);

var loadedScriptStrings = {};

exports.client = redisClient;

exports.ensureScript = function(client, scriptString, cb) {
  if (scriptString in loadedScriptStrings) { return cb(); }
  client.script('load', scriptString, function(err) {
    if (err) { return cb(err); }
    loadedScriptStrings[scriptString] = true;
    return cb();
  });
};

Remove extraneous args when function does not error


var toc = require('markdown-toc');

module.exports = {
    book: {},
    hooks: {
        "page:before": function (page) {
            page.content = toc.insert(page.content, {
                slugify: function (str) {
                    return encodeURI(str.toLowerCase()).replace(/%20/g, '-');
                }
            });
            if (this.options.pluginsConfig.atoc.addClass) {
                var className = this.options.pluginsConfig.atoc.className || 'atoc';
                page.content = page.content + '\n\n\n<script type="text/javascript">var targetUl = document.getElementsByClassName(\'page-inner\')[0].getElementsByTagName(\'ul\')[0];if(targetUl&&targetUl.getElementsByTagName(\'a\').length>0){targetUl.className=\'' + className + '\';}</script>';
            }
            return page;
        }
    }
};

var toc = require('markdown-toc');

module.exports = {
    book: {},
    hooks: {
        "page:before": function (page) {
            page.content = toc.insert(page.content, {
                slugify: function (str) {
                    return encodeURI(str.toLowerCase()).replace(/%20/g, '-').replace(/[\.,\-\(\)]/g,'');
                }
            });
            if (this.options.pluginsConfig.atoc.addClass) {
                var className = this.options.pluginsConfig.atoc.className || 'atoc';
                page.content = page.content + '\n\n\n<script type="text/javascript">var targetUl = document.getElementsByClassName(\'page-inner\')[0].getElementsByTagName(\'ul\')[0];if(targetUl&&targetUl.getElementsByTagName(\'a\').length>0){targetUl.className=\'' + className + '\';}</script>';
            }
            return page;
        }
    }
};

Fix heading content's ID generation


const logger = require('./lib/logging')
const make = require('./lib/make')
const random = require('./lib/random')
const utils = require('./lib/utils')

module.exports = {
    logger,
    random,
    make,
    utils
}

Add entry point for node package


'use strict';

module.exports = function() {
  return new RegExp('^([0-9A-Fa-f]{2}[:-]){5}([0-9A-Fa-f]{2})$');
}

'use strict';

module.exports = function(options) {
  options = options || {};
  var regexBase = '([0-9A-Fa-f]{2}[:-]){5}([0-9A-Fa-f]{2})';

  return options.exact ? new RegExp('^' + regexBase + '$') :
                         new RegeExp(regexBase + 'g')
}

Add options for exact or globa regex


/*jshint node:true */

"use strict";

var es = require('event-stream'),
  gutil = require('gulp-util'),
	path = require('path'),
	exec = require('child_process').exec;

module.exports = function(command, opt){
	if (!command) {
		throw new Error('command is blank');
	}
	// defaults
	if (!opt) {
		opt = {
			silent: false
		};
	}
	return es.map(function (file, cb){
		var filepath = path.resolve(file.path);
		var cmd = gutil.template(command, {file: file});

		exec(cmd, function (error, stdout, stderr) {
			if (stderr) {
				gutil.log(stderr);
			}
			if (stdout) {
				stdout = stdout.trim(); // Trim trailing cr-lf
			}
			if (stdout) {
				gutil.log(stdout);
			}
			cb(error, file);
		});
	});
};

/*jshint node:true */

"use strict";

var es = require('event-stream'),
	gutil = require('gulp-util'),
	path = require('path'),
	exec = require('child_process').exec;

module.exports = function(command, opt){
	if (!command) {
		throw new Error('command is blank');
	}
	// defaults
	if (!opt) {
		opt = {
			silent: false
		};
	}
	return es.map(function (file, cb){
		opt.file = file;
		var cmd = gutil.template(command, opt);

		exec(cmd, function (error, stdout, stderr) {
			if (stderr) {
				gutil.log(stderr);
			}
			if (stdout) {
				stdout = stdout.trim(); // Trim trailing cr-lf
			}
			if (stdout) {
				gutil.log(stdout);
			}
			cb(error, file);
		});
	});
};

Use passed options, overwriting file on each lap


'use strict';
var PinyinHelper = require('./lib/PinyinHelper');
var ChineseHelper = require('./lib/ChineseHelper');
var PinyinFormat = require('./lib/PinyinHelper');

var pinyin4js = {
    WITH_TONE_MARK      :"WITH_TONE_MARK",   //带声调
    WITHOUT_TONE        :"WITHOUT_TONE",     //不带声调
    WITH_TONE_NUMBER    :"WITH_TONE_NUMBER", //数字代表声调
    FIRST_LETTER        :"FIRST_LETTER",      //首字母风格

    convertToPinyinString(str, separator, format) {
        return PinyinHelper.PinyinHelper.convertToPinyinString(str, separator, format)
    },

    convertToSimplifiedChinese(str) {
        return ChineseHelper.ChineseHelper.convertToSimplifiedChinese(str)
    },

    convertToTraditionalChinese(str) {
        return ChineseHelper.ChineseHelper.convertToTraditionalChinese(str)
    },

    getShortPinyin(str) {
        return PinyinHelper.PinyinHelper.getShortPinyin(str)
    }
}

;(function(){
    global.PinyinHelper = PinyinHelper.PinyinHelper;
    global.ChineseHelper = ChineseHelper.ChineseHelper;
    global.PinyinFormat = PinyinFormat.PinyinFormat;
})();

module.exports = pinyin4js

'use strict';
var PinyinHelper = require('./lib/PinyinHelper');
var ChineseHelper = require('./lib/ChineseHelper');
var PinyinFormat = require('./lib/PinyinHelper');

var pinyin4js = {
    WITH_TONE_MARK      :"WITH_TONE_MARK",   //带声调
    WITHOUT_TONE        :"WITHOUT_TONE",     //不带声调
    WITH_TONE_NUMBER    :"WITH_TONE_NUMBER", //数字代表声调
    FIRST_LETTER        :"FIRST_LETTER",      //首字母风格

    convertToPinyinString: function(str, separator, format) {
        return PinyinHelper.PinyinHelper.convertToPinyinString(str, separator, format);
    },

    convertToSimplifiedChinese: function(str) {
        return ChineseHelper.ChineseHelper.convertToSimplifiedChinese(str);
    },

    convertToTraditionalChinese: function(str) {
        return ChineseHelper.ChineseHelper.convertToTraditionalChinese(str);
    },

    getShortPinyin: function(str) {
        return PinyinHelper.PinyinHelper.getShortPinyin(str);
    }
}

;(function(){
    global.PinyinHelper = PinyinHelper.PinyinHelper;
    global.ChineseHelper = ChineseHelper.ChineseHelper;
    global.PinyinFormat = PinyinFormat.PinyinFormat;
})();

module.exports = pinyin4js

Switch the format ES6 back to ES5 to make it compatible with IE11


import test from 'ava';

test.todo('Write test for inView.is');

import test from 'ava';
import inView from '../src/in-view';
import { inViewport } from '../src/viewport';

test('inView.is is a proxy for inViewport', t => {
    t.deepEqual(inView.is, inViewport);
});

Add initial tests for inView.is


const
    Hapi        = require('hapi')
  , Path        = require('path')
  , helper      = require('./helper.js')
  , config      = require('./config.js');

/* Configure the Hapi server */
var server = new Hapi.Server();
server.connection({ port: 3000 });

server.views({
  engines: {
    html: require('handlebars')
  },
  path: Path.join(__dirname, 'templates')
});

server.route([
  {
    path: '/public/{path*}',
    method: 'GET',
    handler: {
      directory: {
        path: "./public",
        listing: false,
        index: false
      }
    }
  },
  {
    method: 'GET',
    path: '/',
    handler: function(request, reply) {
      reply.view('index', {
        title: 'FetchrBot | Home'});
    }
  },
  {
    method: 'GET',
    path: '/twilioCall',
    handler: function(request, reply) {
      /* Send the client the data being requested */
      helper.parseRequestBody(request, function(err, parsedData) {
        if (!err) {
          helper.sendSMSToClient(request, parsedData);
        };
      });
    }
  }
]);

server.start(function() {
  console.log("The magic happens on port " + server.info.port);
});

const
    Hapi        = require('hapi')
  , Path        = require('path')
  , helper      = require('./helper.js');

/* Configure the Hapi server */
var server = new Hapi.Server();
server.connection({ port: 3000 });

server.views({
  engines: {
    html: require('handlebars')
  },
  path: Path.join(__dirname, 'templates')
});

server.route([
  {
    path: '/public/{path*}',
    method: 'GET',
    handler: {
      directory: {
        path: "./public",
        listing: false,
        index: false
      }
    }
  },
  {
    method: 'GET',
    path: '/',
    handler: function(request, reply) {
      reply.view('index', {
        title: 'FetchrBot | Home'});
    }
  },
  {
    method: 'GET',
    path: '/twilioCall',
    handler: function(request, reply) {
      /* Send the client the data being requested */
      helper.parseRequestBody(request, function(err, parsedData) {
        if (!err) {
          helper.sendSMSToClient(request, parsedData);
        };
      });
    }
  }
]);

server.start(function() {
  console.log("The magic happens on port " + server.info.port);
});

Remove unused config file reference.


/**
 * @description Gulp plugin that lets you work with folders and treat them as package names
 * @see README.md
 */
var fs = require('fs'),
	path = require('path');
	es = require('event-stream');

function getFolders(dir){
	return fs.readdirSync(dir)
		.filter(function(file){
			return fs.statSync(path.join(dir, file)).isDirectory();
		});
}

module.exports = function folders(dir, tasks){
	return function(done){
		var folders = getFolders(dir),
			streams = folders.map(tasks);

		if (streams.length === 0) {
			done();
		}

		return es.merge.apply(null, streams);
	};
};

/**
 * @description Gulp plugin that lets you work with folders and treat them as package names
 * @see README.md
 */
var fs = require('fs'),
	path = require('path');
	es = require('event-stream');

function getFolders(dir){
	return fs.readdirSync(dir)
		.filter(function(file){
			return fs.statSync(path.join(dir, file)).isDirectory();
		});
}

module.exports = function folders(dir, tasks){
	return function(done){
		var folders = getFolders(dir),
			streams = folders.map(tasks);

		if (streams.length === 0) {
			return done();
		}

		return es.merge.apply(null, streams);
	};
};

Add a return to avoid multiple callbacks


module.exports = {
	version: '2.6.0-pre.5-dev'
};

Update version string to 2.6.0-pre.14.1


var
	Spotlight = require('./src/spotlight');

Spotlight.version = '2.6.0-pre.20.1';

module.exports = Spotlight;

var
	Spotlight = require('./src/spotlight');

Spotlight.version = '2.6.0-rc.1';

module.exports = Spotlight;

Update version string to 2.6.0-rc.1


var connect = require('connect');

var app = connect();
app.use(connect.static(__dirname));
app.listen(7000);

var connect = require('connect');

var app = connect();
app.use(connect.static(__dirname));g
app.listen(process.env.PORT);

Fix port in connect middleware.


const
    fs = require('fs'),
    inquirer = require('inquirer');

const
    configFilePath = process.env.HOME + '/.gitauthors.json',
    authors = loadConfig(),
    choices = createChoices(authors);

if (authors) {
    inquirer.prompt([
        {
            name: 'q1',
            message: 'Which author details would you like to use with this repository?',
            type: 'list',
            choices
        }
    ]).then(console.log).catch(console.error);
} else {
    console.log('No authors defined.');
}

function loadConfig() {
    if (fs.existsSync(configFilePath)) {
        return JSON.parse(fs.readFileSync(configFilePath));
    } else {
        console.log('No config file found');
    }
}

function createChoices(authors) {
    let choices;

    if (authors) {
        choices = authors.map(author => {
            if (typeof author.email !== 'string') { throw new Error('Expected type string for email') }
            if (typeof author.name !== 'string') { throw new Error('Expected type string for name') }

            return {
                name: author.alias || `${author.name} <${author.email}>`,
                value: {
                    email: author.email,
                    name: author.name
                }
            };
        }).concat(
            [
                new inquirer.Separator(),
                'Add new author...',
                'Remove author...'
            ]
        );
    }

    return choices;
}

const
    fs = require('fs'),
    inquirer = require('inquirer');

const
    configFilePath = process.env.HOME + '/.gitauthors.json',
    authors = loadConfig(),
    choices = createChoices(authors);

if (authors) {
    inquirer.prompt([
        {
            name: 'q1',
            message: 'Which author details would you like to use with this repository?',
            type: 'list',
            choices
        }
    ]).then(console.log).catch(console.error);
} else {
    console.log('No authors found!')
    inquirer.prompt([
        {
            name: 'createConfig',
            message: 'Would you like to create a new authors config file?',
            type: 'confirm',
            default: true
        }
    ]).then(answers => {
        if (answers.createConfig) {
            console.log('TODO: Show prompts for creating config...');
        }
    }).catch(console.error);
}

function loadConfig() {
    if (fs.existsSync(configFilePath)) {
        return JSON.parse(fs.readFileSync(configFilePath));
    }
    return false;
}

function createChoices(authors) {
    let choices;

    if (authors) {
        choices = authors.map(author => {
            if (typeof author.email !== 'string') { throw new Error('Expected type string for email') }
            if (typeof author.name !== 'string') { throw new Error('Expected type string for name') }

            return {
                name: author.alias || `${author.name} <${author.email}>`,
                value: {
                    email: author.email,
                    name: author.name
                }
            };
        }).concat(
            [
                new inquirer.Separator(),
                'Add new author...',
                'Remove author...'
            ]
        );
    }

    return choices;
}

Create prompt if no config file found


/**
 * util-log.js - The tiny log function
 */

// The safe wrapper for `console.xxx` functions
// log("message") ==> console.log("message")
// log("message", "warn") ==> console.warn("message")
var log = seajs.log = function(msg, type) {
  var console = global.console

  if (console) {
    // Do NOT print `log(msg)` in non-debug mode
    if (type || configData.debug) {
      (console[type] || console["log"])(msg)
    }
  }

}

/**
 * util-log.js - The tiny log function
 */

// The safe wrapper for `console.xxx` functions
// log("message") ==> console.log("message")
// log("message", "warn") ==> console.warn("message")
var log = seajs.log = function(msg, type) {
  var console = global.console

  if (console) {
    // Do NOT print `log(msg)` in non-debug mode
    if (type || configData.debug) {
      (console[type] || console["log"]).call(console, msg)
    }
  }

}

Fix an illegal invocation bug for seajs.log


'use strict';

var Module = angular.module('datePicker');

Module.directive('dateRange', function () {
  return {
    templateUrl: 'templates/daterange.html',
    scope: {
      start: '=',
      end: '='
    },
    link: function (scope, element, attrs) {

      /*
       * If no date is set on scope, set current date from user system
       */
      scope.start = new Date(scope.start || new Date());
      scope.end = new Date(scope.end || new Date());

      attrs.$observe('disabled', function(isDisabled){
          scope.disableDatePickers = !!isDisabled;
        });
      scope.$watch('start.getTime()', function (value) {
        if (value && scope.end && value > scope.end.getTime()) {
          scope.end = new Date(value);
        }
      });
      scope.$watch('end.getTime()', function (value) {
        if (value && scope.start && value < scope.start.getTime()) {
          scope.start = new Date(value);
        }
      });
    }
  };
});

'use strict';

var Module = angular.module('datePicker');

Module.directive('dateRange', function () {
  return {
    templateUrl: 'templates/daterange.html',
    scope: {
      start: '=',
      end: '=',
      callback: '='
    },
    link: function (scope, element, attrs) {

      /*
       * If no date is set on scope, set current date from user system
       */
      scope.start = new Date(scope.start || new Date());
      scope.end = new Date(scope.end || new Date());

      isFunction = function(functionToCheck) {
        var getType = {};
        return functionToCheck && getType.toString.call(functionToCheck) === '[object Function]';
      }

      attrs.$observe('disabled', function(isDisabled){
          scope.disableDatePickers = !!isDisabled;
        });
      scope.$watch('start.getTime()', function (value) {
        if (value && scope.end && value > scope.end.getTime()) {
          scope.end = new Date(value);
          if(isFunction(callback)) {
            callback.call();
          }
        }
      });
      scope.$watch('end.getTime()', function (value) {
        if (value && scope.start && value < scope.start.getTime()) {
          scope.start = new Date(value);
          if(isFunction(callback)) {
            callback.call();
          }
        }
      });
    }
  };
});

Add callback to range change


var Botkit = require('botkit')
var request = require('request')
var mapsToken = process.env.GOOGLE_MAPS_TOKEN

// Expect a SLACK_TOKEN environment variable
var slackToken = process.env.SLACK_TOKEN
if (!slackToken) {
  console.error('SLACK_TOKEN is required!')
  process.exit(1)
}

var controller = Botkit.slackbot()
var bot = controller.spawn({
  token: slackToken
})

bot.startRTM(function (err, bot, payload) {
  if (err) {
    throw new Error('Could not connect to Slack')
  }
})

controller.hears(['where'], ['direct_message', 'direct_mention'], function (bot, message) {
	request('http://tracker.brandonevans.ca/points/latest', function(error, response, body) {
		var point = JSON.parse(body);
		var prefix = point.inside ? " in " : " near ";
		var location = point.name;

		var text = 'I\'m' + prefix + location
		var imageURL = 'https://maps.googleapis.com/maps/api/staticmap?center=' + encodeURIComponent(location) + '&zoom=6&size=600x300&maptype=roadmap&key=' + mapsToken
		var attachments = [{
			fallback: text,
			pretext: ':world_map: ' + text,
			image_url: imageURL
		}]
		bot.reply(message, {attachments: attachments});
	});
})

var Botkit = require('botkit')
var request = require('request')
var mapsToken = process.env.GOOGLE_MAPS_TOKEN

// Expect a SLACK_TOKEN environment variable
var slackToken = process.env.SLACK_TOKEN
if (!slackToken) {
  console.error('SLACK_TOKEN is required!')
  process.exit(1)
}

var controller = Botkit.slackbot()
var bot = controller.spawn({
  token: slackToken
})

bot.startRTM(function (err, bot, payload) {
  if (err) {
    throw new Error('Could not connect to Slack')
  }
})

controller.hears(['where'], ['direct_message', 'direct_mention'], function (bot, message) {
	request('http://tracker.brandonevans.ca/points/latest', function(error, response, body) {
		var point = JSON.parse(body);
		var prefix = point.inside ? " in " : " near ";
		var location = point.name;

		var text = 'I\'m' + prefix + location
		var imageURL = 'https://maps.googleapis.com/maps/api/staticmap?center=' + encodeURIComponent(location) + '&zoom=6&size=600x300&maptype=roadmap&key=' + mapsToken
		var attachments = [{
			fallback: text,
			pretext: ':round_pushpin: ' + text,
			image_url: imageURL
		}]
		bot.reply(message, {attachments: attachments});
	});
})

Change map emoji to pushpin