var Q      = require('q'),
    spawn  = require('child_process').spawn,
    fs     = require('fs'),
    net    = require('net'),
    logger = require('winston'),
    mpdConfig = require(__dirname+'/mpd-config.js');

exports.create = function(config) {
  var mpdConf = mpdConfig.create(),
      instance = {};

  instance.port = mpdConf.port;

  function waitToBeOnline() {
    var deferred = Q.defer(),
        sock = new net.Socket();

    sock.setTimeout(2500);

    sock.on('connect', function() {
      logger.info('connected to '+instance.port);
      deferred.resolve();
    });

    sock.on('error', function(error) {
      logger.error('error for '+instance.port, error);

      setTimeout(function() {
        sock.connect(instance.port);
      }, 10);
    });

    sock.connect(instance.port);

    return deferred.promise;
  }

  function spawnMPD(fileName) {
    var deferred = Q.defer(),
        mpdProcess = spawn('/usr/local/bin/mpd', [fileName, '--no-daemon']);

    mpdProcess.on('close', function(code, signal) {
      logger.error('MPD Closed: ' + code);
    });

    mpdProcess.stdout.on('data', function(data) {
      logger.info('MPD: ' + data);
    });

    mpdProcess.stderr.on('data', function(data) {
      logger.warn('MPD: ' + data);
    });

    deferred.resolve();

    return deferred.promise;
  }

  instance.start = function() {
    return mpdConf.write(config).then(spawnMPD)
      .then(waitToBeOnline);
  };

  return instance;
};

var Q      = require('q'),
    spawn  = require('child_process').spawn,
    fs     = require('fs'),
    net    = require('net'),
    logger = require('winston'),
    waitForSocket = require('./wait-for-socket.js'),
    mpdConfig = require(__dirname+'/mpd-config.js');

exports.create = function(config) {
  var mpdConf = mpdConfig.create(),
      instance = {};

  instance.port = mpdConf.port;

  function waitToBeOnline() {
    return waitForSocket
              .create(instance.port)
              .connect();
  }

  function spawnMPD(fileName) {
    var deferred = Q.defer(),
        mpdProcess = spawn('/usr/local/bin/mpd', [fileName, '--no-daemon']);

    mpdProcess.on('close', function(code, signal) {
      logger.error('MPD Closed: ' + code);
    });

    mpdProcess.stdout.on('data', function(data) {
      logger.info('MPD: ' + data);
    });

    mpdProcess.stderr.on('data', function(data) {
      logger.warn('MPD: ' + data);
    });

    deferred.resolve();

    return deferred.promise;
  }

  instance.start = function() {
    return mpdConf.write(config).then(spawnMPD)
      .then(waitToBeOnline);
  };

  return instance;
};

Replace with tested wait-for-socket implementation


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

var forceSsl = function (req, res, next) {
  if (req.headers['x-forwarded-proto'] !== 'https') {
    res.header("Strict-Transport-Security", "max-age=31536000");
    return res.redirect(['https://', req.get('Host'), req.url].join(''));
  }
  return next();
};

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

app.use(forceSsl);
app.get('/config.js', function(request, response) {

  var config = " \
    'use strict'; \
    angular.module('config', []) \
      .constant('config', { \
        'endpoint'    : '" + process.env.ALERTA_ENDPOINT + "', \
        'provider'    : '" + process.env.PROVIDER + "', \
        'client_id'   : '" + process.env.CLIENT_ID + "', \
        'gitlab_url'  : '" + process.env.GITLAB_URL + "' \
      });";

  response.send(config);
});
app.use(express.static(__dirname + '/app'));

app.listen(app.get('port'), function() {
  console.log("Node app is running at localhost:" + app.get('port'));
});

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

var forceSsl = function (req, res, next) {
  if (req.headers['x-forwarded-proto'] !== 'https') {
    res.header("Strict-Transport-Security", "max-age=31536000");
    return res.redirect(['https://', req.get('Host'), req.url].join(''));
  }
  return next();
};

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

app.use(forceSsl);
app.get('/config.js', function(request, response) {

  var config = " \
    'use strict'; \
    angular.module('config', []) \
      .constant('config', { \
        'endpoint'    : '" + process.env.ALERTA_ENDPOINT + "', \
        'provider'    : '" + process.env.PROVIDER + "', \
        'client_id'   : '" + process.env.CLIENT_ID + "', \
        'gitlab_url'  : '" + process.env.GITLAB_URL + "', \
        'tracking_id' : '" + process.env.TRACKING_ID + "' \
      });";

  response.send(config);
});
app.use(express.static(__dirname + '/app'));

app.listen(app.get('port'), function() {
  console.log("Node app is running at localhost:" + app.get('port'));
});

Make tracking ID a config option for Heroku deploys


import React from 'react';
import styles from './Banner.scss';

const Banner = () => (

      This game has a play testing session available.

        {' '}You are eligible!

      <button className={styles.Start}>Start Session</button>
      <button className={styles.Hide}>Hide</button>

);

export default Banner;

import React, { Component } from 'react';
import styles from './Banner.scss';

class Banner extends Component {
  state = {
    hide: false
  }

  onHide = () => this.setState({ hide: true });

  render() {
    const { hide } = this.state;
    return !hide
      ? (

            This game has a play testing session available.

              {' '}You are eligible!

            <button className={styles.Start}>Start Session</button>
            <button className={styles.Hide} onClick={this.onHide}>Hide</button>

      )
      : null;
  }
}

export default Banner;

Add basic hide functionality to banner


'use strict';

let co = require('co'),
    redisModule = require('../../../server/lib/redis'),
    redis = redisModule.createClient();

module.exports = {
    local: {
        dontReportSauceLabs: true
    },

    before: function(done) {
        co(function*() {
            // Clear redis database
            yield redis.flushdb();
            yield redis.quit();

            yield redisModule.initDB();
            done();
        })();
    }
};

'use strict';

let co = require('co'),
    redisModule = require('../../../server/lib/redis'),
    redis = redisModule.createClient();

module.exports = {
    local: {
        dontReportSauceLabs: true
    },

    before: function(done) {
        co(function*() {
            // Clear redis database
            yield redis.flushdb();
            yield redis.quit();

            yield redisModule.initDB();
            done();
        })();
    },

    after: function(done) {
        setTimeout(function() {
            // Some of the MAS server libs don't exit cleanly because of Redis connections
            process.exit(0);
        }, 1000);

        done();
    }
};

Make sure test run exits no matter what


/*jshint node:true*/
/* global require, module */
var EmberAddon = require('ember-cli/lib/broccoli/ember-addon');

module.exports = function(defaults) {
  var app = new EmberAddon(defaults, {
    sassOptions: {
      includePaths: [
        'bower_components/Materialize/sass'
      ]
    }
  });

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

  return app.toTree();
};

/*jshint node:true*/
/* global require, module */
var EmberAddon = require('ember-cli/lib/broccoli/ember-addon');

module.exports = function(defaults) {
  var app = new EmberAddon(defaults, {
    sassOptions: {
      includePaths: [
        'bower_components/materialize/sass'
      ]
    }
  });

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

  return app.toTree();
};

Fix capitalization in Materialize bower dependency


function Id(a) {
    this.value = a;
}

// Semigroup (value must also be a Semigroup)
Id.prototype.concat = function(b) {
    return new Id(this.value.concat(b.value));
};

// Monoid (value must also be a Monoid)
Id.prototype.empty = function() {
    return new Id(this.value.empty ? this.value.empty() : this.value.constructor.empty());
};

// Functor
Id.prototype.map = function(f) {
    return new Id(f(this.value));
};

// Applicative
Id.prototype.ap = function(b) {
    return new Id(this.value(b.value));
};

// Chain
Id.prototype.chain = function(f) {
    return f(this.value);
};

// Monad
Id.of = function(a) {
    return new Id(a);
};

if(typeof module == 'object') module.exports = Id;

function Id(a) {
    this.value = a;
}

// Semigroup (value must also be a Semigroup)
Id.prototype.concat = function(b) {
    return new Id(this.value.concat(b.value));
};

// Monoid (value must also be a Monoid)
Id.prototype.empty = function() {
    return new Id(this.value.empty ? this.value.empty() : this.value.constructor.empty());
};

// Functor
Id.prototype.map = function(f) {
    return new Id(f(this.value));
};

// Applicative
Id.prototype.ap = function(b) {
    return new Id(this.value(b.value));
};

// Chain
Id.prototype.chain = function(f) {
    return f(this.value);
};

// Extend
Id.prototype.extend = function(f){
  return new Id(f(this.value));
}

// Monad
Id.of = function(a) {
    return new Id(a);
};

// Comonad
Id.from = function(a){
  return a.value;
}

if(typeof module == 'object') module.exports = Id;

Add extend and from to Id spec


#!/usr/bin/env node
import osia from '.';
import minimist from 'minimist';
import list from 'cli-list';
import routine from 'promise-routine';
import { join } from 'path';

const args = process.argv.slice(2);
const sets = list(args);
const opts = minimist(args);
const tasks = [];

// Setup CLI for routine
if (sets.length > 1 || opts.i) {
  sets.forEach(set => {
    const mic = minimist(set.slice(1));
    tasks.push([set[0], mic, mic._]);
  });
} else {
  sets[0].forEach(task => tasks.push([task, {}, {}]));
}

// Run the config, which defines the tasks.
require(join(process.cwd(), 'osia.js'));

// Start
routine((...o) => osia.run(...o), ...tasks);

#!/usr/bin/env node
import osia from '.';
import minimist from 'minimist';
import list from 'cli-list';
import routine from 'promise-routine';
import { join } from 'path';

const args = process.argv.slice(2);
const opts = minimist(args, { boolean: true });
const sets = list(opts._);
const tasks = [];

// Setup CLI for routine
if (sets.length > 1 || opts.i) {
  sets.forEach(set => {
    const mic = minimist(set);
    tasks.push([set[0], mic, mic._.slice(1)]);
  });
} else {
  sets[0].forEach(task => tasks.push([task, {}, {}]));
}

if (opts.b || opts.babel) {
  require(require.resolve('babel-register'));
}

// Run the config, which defines the tasks.
require(join(process.cwd(), 'osia.js'));

// Start
routine((...o) => osia.run(...o), ...tasks);

Add babel-register under -b flag for ES6 tasks


import React from 'react';
import { connect } from 'react-redux';
import { bindActionCreators } from 'redux';
import { actions } from 'redux-ghost';

import JsonTree from 'react-json-tree';

import './app.css';

const App = ({ actions, blog }) => {
  return (

Redux Ghost Blog

Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo.

Posts

      <button onClick={actions.getPosts}>Load Posts</button>
      <button onClick={() => actions.getPost(1)}>Load Post</button>
      <button onClick={actions.getPost}>Load Post2</button>

Tags

      <button onClick={actions.getTags}>Load Tags</button>
      <button onClick={actions.getTag}>Load Tag</button>

Users

      <button onClick={actions.getUsers}>Load Users</button>
      <button onClick={actions.getUser}>Load User</button>

Reset

      <button onClick={actions.reset}>Reset</button>

      <JsonTree data={blog} theme="monokai" />

  );
}

const mapStateToProps = ({ blog }) => ({
  blog,
});

const mapDispatchToProps = (dispatch) => ({
  actions: bindActionCreators(actions, dispatch)
});

export default connect(
  mapStateToProps,
  mapDispatchToProps,
)(App);

import React from 'react';
import { connect } from 'react-redux';
import { bindActionCreators } from 'redux';
import { actions } from 'redux-ghost';

import JsonTree from 'react-json-tree';

import './app.css';

const App = ({ actions, blog }) => {
  return (

Redux Ghost Blog

Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo.

Posts

      <button onClick={actions.getPosts}>Load Posts</button>
      <button onClick={() => actions.getPost(1)}>Load Post</button>

Tags

      <button onClick={actions.getTags}>Load Tags</button>
      <button onClick={() => actions.getTag(1)}>Load Tag</button>

Users

      <button onClick={actions.getUsers}>Load Users</button>
      <button onClick={() => actions.getUser(1)}>Load User</button>

Reset

      <button onClick={actions.reset}>Reset</button>

      <JsonTree data={blog} theme="monokai" />

  );
}

const mapStateToProps = ({ blog }) => ({
  blog,
});

const mapDispatchToProps = (dispatch) => ({
  actions: bindActionCreators(actions, dispatch)
});

export default connect(
  mapStateToProps,
  mapDispatchToProps,
)(App);

Add parameter to get single post/user/tag


'use strict';

/*
 Example of using raml2md as a script.
 */

var raml2md = require('..');

// raml2md.render() needs a config object with at least a `template` property (a url to a Nunjucks template).
// Instead of creating this config object ourselves, we can just ask for raml2md.getDefaultConfig():
var config = raml2md.getDefaultConfig();

raml2md.render(__dirname + '/example.raml', config).then(function(result) {
  console.log(result);
}, function(error) {
  console.log('error! ', error);
});

'use strict';

/*
 Example of using raml2md as a script.
 */

var raml2md = require('..');
var path = require('path');

// raml2md.render() needs a config object with at least a `template` property (a url to a Nunjucks template).
// Instead of creating this config object ourselves, we can just ask for raml2md.getDefaultConfig():
var config = raml2md.getDefaultConfig();

raml2md.render(path.join(__dirname, 'example.raml'), config).then(function(result) {
  console.log(result);
}, function(error) {
  console.log('error! ', error);
});

Fix example so it lints


define([], function() {

  'use strict';

  function ByteSource() {
  }

  function ByteSourceFromBlob(blob) {
    this.blob = blob;
  }
  (function(proto) {
    proto.slice = function() {
      return new ByteSourceFromBlob(this.blob.slice.apply(this.blob, arguments));
    };
    proto.read = function(reader) {
      var fr = new FileReader();
      fr.addEventListener('load', function() {
        if (typeof reader.onbytes === 'function') {
          reader.onbytes(new Uint8Array(fr.result));
        }
        if (typeof reader.oncomplete === 'function') {
          reader.oncomplete();
        }
      });
      fr.addEventListener('error', function(e) {
        console.error(e.error);
      });
      fr.readAsArrayBuffer(this.blob);
    };
  })(ByteSourceFromBlob.prototype = new ByteSource);

  ByteSource.FromBlob = ByteSourceFromBlob;

  ByteSource.from = function(src) {
    if (src instanceof ByteSource) return src;
    if (src instanceof Blob) return new ByteSourceFromBlob(src);
    if (src instanceof ArrayBuffer || ArrayBuffer.isView(src)) return new ByteSourceFromBlob(new Blob([src]));
    throw new TypeError('value not supported for creating ByteSource: ' + (typeof src === 'object' ? src : typeof src));
  };

  return ByteSource;

});

define([], function() {

  'use strict';

  function ByteSource() {
  }

  function ByteSourceFromBlob(blob) {
    this.blob = blob;
  }
  (function(proto) {
    proto.slice = function() {
      return new ByteSourceFromBlob(this.blob.slice.apply(this.blob, arguments));
    };
    proto.read = function(reader) {
      var fr = new FileReader();
      fr.addEventListener('load', function() {
        if (typeof reader.onbytes === 'function') {
          reader.onbytes(new Uint8Array(fr.result));
        }
        if (typeof reader.oncomplete === 'function') {
          reader.oncomplete();
        }
      });
      fr.addEventListener('error', function(e) {
        console.error(e.error);
      });
      fr.readAsArrayBuffer(this.blob);
    };
    proto.getURL = function() {
      return Promise.resolve(URL.createObjectURL(this.blob));
    };
  })(ByteSourceFromBlob.prototype = new ByteSource);

  ByteSource.FromBlob = ByteSourceFromBlob;

  ByteSource.from = function(src) {
    if (src instanceof ByteSource) return src;
    if (src instanceof Blob) return new ByteSourceFromBlob(src);
    if (src instanceof ArrayBuffer || ArrayBuffer.isView(src)) return new ByteSourceFromBlob(new Blob([src]));
    throw new TypeError('value not supported for creating ByteSource: ' + (typeof src === 'object' ? src : typeof src));
  };

  return ByteSource;

});

Add getURL() method that returns a Promise


import { mapGetters } from 'vuex'

export const formatListMixin = {

  created () {
  },

  mounted () {
  },

  beforeDestroy () {
  },

  computed: {
    ...mapGetters([
      'organisation'
    ])
  },

  methods: {
    formatDuration (duration) {
      if (duration) {
        return (duration / 60 / this.organisation.hours_by_day).toLocaleString(
          'fullwide', { maximumFractionDigits: 1 }
        )
      } else {
        return 0
      }
    },

    formatBoolean (booleanValue) {
      return booleanValue ? this.$t('main.yes') : this.$t('main.no')
    }
  }
}

import { mapGetters } from 'vuex'

export const formatListMixin = {

  created () {
  },

  mounted () {
  },

  beforeDestroy () {
  },

  computed: {
    ...mapGetters([
      'organisation'
    ])
  },

  methods: {
    formatDuration (duration) {
      if (duration) {
        return (duration / 60 / this.organisation.hours_by_day).toLocaleString(
          'fullwide', { maximumFractionDigits: 1 }
        )
      } else {
        return 0
      }
    },

    formatBoolean (booleanValue) {
      return booleanValue ? this.$t('main.yes') : this.$t('main.no')
    },

    formatEstimation (estimation) {
      if (estimation) {
        if (estimation < 10) {
          return estimation
        } else {
          return this.formatDuration(estimation)
        }
      } else {
        return 0
      }
    }
  }
}

Move format estimation function to format mixin


function drawBlock(parent, width, depth) {
    var newWidth = Math.sqrt(2 * Math.pow(width / 2, 2));
    var color = 'rgba(' + [0, Math.floor(depth * 25), 100, (1 - depth / 40)].join(',') + ');';
    console.log(color);
    var style = 'width: ' + newWidth + 'px;height: ' + newWidth + 'px;background-color: ' + color;
    var left = $('

');
    var right = $('

');

    left.hide();
    right.hide();

    parent.append(left);
    parent.append(right);

    left.fadeIn(1000);
    right.fadeIn(1000);

    if (depth > 0) {
        setTimeout(function() {
            drawBlock(left, newWidth, depth - 1);
            drawBlock(right, newWidth, depth - 1);
        }, 500);
    }

}

// TODO - Append to DOM once it's completely built out
drawBlock($('#root'), 100, 7);

console.time('tree');

// TODO - Return the entire HTML for that depth.
var map = {};
function getData(depth, width) {

    var color,
        left,
        nextWidth,
        right,
        style;

    if (!map[depth]) {

        color = 'rgba(' + [0, Math.floor(depth * 25), 100, (1 - depth / 40)].join(',') + ');';
        nextWidth = Math.sqrt(2 * Math.pow(width / 2, 2));
        style = 'width: ' + nextWidth + 'px;height: ' + nextWidth + 'px;background-color: ' + color;
        left = $('

');
        right = $('

');

        map[depth] = {
            left: left,
            nextWidth: nextWidth,
            right: right
        };
    }

    return map[depth];
}

function drawBlock(parent, width, depth) {
    var data  = getData(depth, width),
        left  = data.left.clone(),
        right = data.right.clone();

    parent.append(left);
    parent.append(right);

    if (depth > 0) {
        drawBlock(left, data.nextWidth, depth - 1);
        drawBlock(right, data.nextWidth, depth - 1);
    }

}

// TODO - Append to DOM once it's completely built out
drawBlock($('#root'), 100, 10);
console.timeEnd('tree');

Add timer, only calculate values once for each depth level, and remove delayed 'growth' of tree


/**
 * Resolves queries from a map to arrays of maps to field values.
 *
 * @param queries A map to arrays of maps to field values.
 * @param name Query name.
 * @param rank Query result rank.
 * @param field Query result field name.
 * @return Resolved value, or '' if there was an error.
 */
function queriesObjectReader (queries, name, rank, field) {
  const items = queries[name]
  if (rank - 1 >= items.length) {
    return ''
  }
  const item = queries[name][rank - 1]
  if (!Object.prototype.hasOwnProperty.call(item, field)) {
    return ''
  }
  return item[field]
}

/**
 * Resolves a query in an environment.
 *
 * @param env The environment to evaluate the query in.
 * @return A function that resolves a query given its name, rank, and field.
 */
function resolveQuery (env) {
  return (name, rank, field) => {
    if (!env || !env.queries || !Object.prototype.hasOwnProperty.call(env.queries, name)) {
      return ''
    }
    const reader = typeof env.queries === 'function'
      ? env.queries
      : queriesObjectReader.bind(null, env.queries)

    const value = reader(name, rank, field)
    if (['number', 'string'].indexOf(typeof value) === -1) {
      return ''
    }
    return value
  }
}

module.exports = {
  query: resolveQuery
}

/**
 * Resolves queries from a map to arrays of maps to field values.
 *
 * @param queries A map to arrays of maps to field values.
 * @param name Query name.
 * @param rank Query result rank.
 * @param field Query result field name.
 * @return Resolved value, or '' if there was an error.
 */
function queriesObjectReader (queries, name, rank, field) {
  if (!Object.prototype.hasOwnProperty.call(queries, name)) {
    return ''
  }
  const items = queries[name]
  if (rank - 1 >= items.length) {
    return ''
  }
  const item = queries[name][rank - 1]
  if (!Object.prototype.hasOwnProperty.call(item, field)) {
    return ''
  }
  return item[field]
}

/**
 * Resolves a query in an environment.
 *
 * @param env The environment to evaluate the query in.
 * @return A function that resolves a query given its name, rank, and field.
 */
function resolveQuery (env) {
  return (name, rank, field) => {
    if (!env || !env.queries) {
      return ''
    }
    const reader = typeof env.queries === 'function'
      ? env.queries
      : queriesObjectReader.bind(null, env.queries)

    const value = reader(name, rank, field)
    if (['number', 'string'].indexOf(typeof value) === -1) {
      return ''
    }
    return value
  }
}

module.exports = {
  query: resolveQuery
}

Fix bug when env.queries is a function


import {
  moduleForComponent,
  test
} from 'ember-qunit';

moduleForComponent('nav-bar', {});

test('it renders', function(assert) {
  assert.expect(2);

  // creates the component instance
  var component = this.subject();
  assert.equal(component._state, 'preRender');

  // renders the component to the page
  this.render();
  assert.equal(component._state, 'inDOM');

});

import {
  moduleForComponent,
  test
} from 'ember-qunit';

moduleForComponent('nav-bar', {});

test('it renders', function(assert) {
  assert.expect(3);

  var component = this.subject();
  assert.equal(component._state, 'preRender');

  this.render();
  assert.equal(component._state, 'inDOM');

  assert.equal(this.$().find('a').length, 2);

});

Test for links in nav bar component


chrome.storage.local.get(['pattern', 'case_insensitive', 'color', 'opacity'], function(items) {
  var pattern, case_insensitive, color, opacity;
  var re, flags;

  pattern = items.pattern;
  if (!pattern) {
    pattern = 'WIP';
  }

  case_insensitive = !!items.case_insensitive;
  if (case_insensitive) {
    flags = 'i';
  } else {
    flags = '';
  }

  re = new RegExp(pattern, flags);

  color   = items.color   || 'lightgray';
  opacity = items.opacity || 0.7;

  var unhighlighter = function(element, re, color, opacity) {
    var items = element.querySelectorAll('.Box-row-link');
    Array.prototype.filter.call(items, function(item) {
      if (item.innerText.match(re)) {
        element.style.backgroundColor = color;
        element.style.opacity = opacity;
      }
    })
  };

  var walk = function(re, color, opacity) {
    var elements = document.querySelectorAll('.js-issue-row');
    Array.prototype.forEach.call(elements, function(element) {
      unhighlighter(element, re, color, opacity);
    });
  }

  var target = document.querySelector('body');
  var config = {
    childList: true,
    subtree: true
  };

  var observer = new MutationObserver(function(mutations, self) {
    mutations.forEach(function(mutation) {
      if (mutation.type === 'childList') {
        walk(re, color, opacity);
      }
    });
  });

  observer.observe(target, config);
  walk(re, color, opacity);
});

chrome.storage.local.get(['pattern', 'case_insensitive', 'color', 'opacity'], function(items) {
  var pattern, case_insensitive, color, opacity;
  var re, flags;

  pattern = items.pattern;
  if (!pattern) {
    pattern = 'WIP';
  }

  case_insensitive = !!items.case_insensitive;
  if (case_insensitive) {
    flags = 'i';
  } else {
    flags = '';
  }

  re = new RegExp(pattern, flags);

  color   = items.color   || 'lightgray';
  opacity = items.opacity || 0.7;

  var unhighlighter = function(element, re, color, opacity) {
    var items = element.querySelectorAll('.Box-row-link');
    Array.from(items).filter(function(item) {
      if (item.innerText.match(re)) {
        element.style.backgroundColor = color;
        element.style.opacity = opacity;
      }
    });
  };

  var walk = function(re, color, opacity) {
    var elements = document.querySelectorAll('.js-issue-row');
    Array.from(elements).forEach(function(element) {
      unhighlighter(element, re, color, opacity);
    });
  }

  var target = document.querySelector('body');
  var config = {
    childList: true,
    subtree: true
  };

  var observer = new MutationObserver(function(mutations, self) {
    mutations.forEach(function(mutation) {
      if (mutation.type === 'childList') {
        walk(re, color, opacity);
      }
    });
  });

  observer.observe(target, config);
  walk(re, color, opacity);
});

Use Array.from instead of Array.prototype


import PIXI from 'pixi.js';
import Hexagon from './hexagon.js';
import GroundLayer from './layer/groundLayer.js';
import PositionLayer from './layer/positionLayer.js';

let hexagon = new Hexagon(80);

/**
 * Represents a tile in the game.
 */
export default class Tile {
  constructor(x, y, entities) {
    this.x = x;
    this.y = y;
    this.entities = entities;
    this.selected = false;
    this.hover = false;
    this.sprite = new PIXI.Container();
    this.layers = [];
    this.addLayer(new GroundLayer(this));
    this.addLayer(new PositionLayer(this));
  }
  addLayer(layer) {
    this.layers.push(layer);
    this.sprite.addChild(layer.getSprite());
  }
  shouldComponentUpdate() {
    return !this.layers.every((e) => !e.shouldComponentUpdate());
  }
  update(renderer) {
    this.layers.forEach((e) => {
      if (e.shouldComponentUpdate()) e.update(renderer);
    });
  }
  static get hexagon() {
    return hexagon;
  }
}

import PIXI from 'pixi.js';
import Hexagon from './hexagon.js';
import GroundLayer from './layer/groundLayer.js';

let hexagon = new Hexagon(80);

/**
 * Represents a tile in the game.
 */
export default class Tile {
  constructor(x, y, entities) {
    this.x = x;
    this.y = y;
    this.entities = entities;
    this.selected = false;
    this.hover = false;
    this.sprite = new PIXI.Container();
    this.layers = [];
    this.addLayer(new GroundLayer(this));
  }
  addLayer(layer) {
    this.layers.push(layer);
    this.sprite.addChild(layer.getSprite());
  }
  shouldComponentUpdate() {
    return !this.layers.every((e) => !e.shouldComponentUpdate());
  }
  update(renderer) {
    this.layers.forEach((e) => {
      if (e.shouldComponentUpdate()) e.update(renderer);
    });
  }
  static get hexagon() {
    return hexagon;
  }
}

Remove PositionLayer as it's an expensive operation


import lightBaseTheme from 'material-ui/styles/baseThemes/lightBaseTheme'
import {merge} from 'lodash'

export default merge(lightBaseTheme, {
  palette: {
    primary1Color: '#f09442',
    primary2Color: '#e95d3b',
    accent1Color: '#99dc3c',
    pickerHeaderColor: '#f09442',
    footerColor: '#336699'
  }
})

import lightBaseTheme from 'material-ui/styles/baseThemes/lightBaseTheme'
import {merge} from 'lodash'

export default merge(lightBaseTheme, {
  palette: {
    primary1Color: '#f09442',
    primary2Color: '#e95d3b',
    accent1Color: '#99dc3c',
    pickerHeaderColor: '#f09442',
    footerColor: 'rgb(0, 122, 204)'
  }
})

Update footer color: use vscode-footer-like color


// @flow

import * as React from 'react';
import { Redirect } from 'react-router';

import { AuthToken } from 'lib/auth';

export default class Logout extends React.Component<{}> {
  constructor() {
    super();
    AuthToken.logout();
    window.location.href = '/'; // Hard navigation to login screen. Dirty fix for GIFT-216
  }

  render(): React.Node {
    // return <Redirect to="/auth/login"/>;
    return null;
  }
}

// @flow

import * as React from 'react';
import { Redirect } from 'react-router';

import { AuthToken } from 'lib/auth';

export default class Logout extends React.Component<{}> {
  constructor() {
    super();
    AuthToken.logout();
    window.location.href = '/'; // Hard navigation to login screen. Dirty fix for GIFT-216
  }

  render(): React.Node {
    // return <Redirect to="/auth/login"/>;
    return (

Logging out

        Click here if you are not redirected.

      );
  }
}

Add text and manual redirect link to log out page


"use strict";

$(function(){
  var Workspace = Backbone.Router.extend({
    routes: {
      "": "index",
      "event/:id": "event"
    },

    index: function() {
    },

    event: function(id) {
      window.Evento = Backbone.Model.extend({
        urlRoot: '/event',
      });

      window.EventView = Backbone.View.extend({
        initialize: function() {
          this.model.bind('change', this.render, this);
        },
        render: function(callback) {
          var foo = this.model.get('foo');
          dust.render('event_nginx_access', this.model.toJSON(), function(err, out) {
            $(foo).html(out);
          });
        }
      });

      window.EventPageView = Backbone.View.extend({
        el: $('#backbone-view'),
        initialize: function() {
          var m = new Evento({id: id});
          m.set('foo', this.el);
          m.fetch();
          var view = new EventView({model: m});
          view.render();
        }
      });

      window.EventPageView= new EventPageView();
    },
  });

  new Workspace();
  Backbone.history.start();
});

"use strict";

$(function(){
  var Workspace = Backbone.Router.extend({
    routes: {
      '': 'index',
      'timeline': 'timeline',
      'event/:id': 'event'
    },

    index: function() {
    },

    event: function(id) {
      window.Evento = Backbone.Model.extend({
        urlRoot: '/event',
      });

      window.EventView = Backbone.View.extend({
        initialize: function() {
          this.model.bind('change', this.render, this);
        },
        render: function(callback) {
          var foo = this.model.get('foo');
          dust.render(
            'event_' + this.model.attributes.parser,
            this.model.toJSON(),
            function(err, out) {
              $(foo).html(out);
            }
          );
        }
      });

      window.EventPageView = Backbone.View.extend({
        el: $('#backbone-view'),
        initialize: function() {
          var m = new Evento({id: id});
          m.set('foo', this.el);
          m.fetch();
          var view = new EventView({model: m});
          view.render();
        }
      });

      window.EventPageView= new EventPageView();
    },
  });

  new Workspace();
  Backbone.history.start();
});

Add dynamic event template selection


'use strict';

const fs = require('fs');
const camelCase = require('camelcase');

function getPackageDeps() {
  const pkgFile = fs.readFileSync('./package.json');
  return Object.keys(JSON.parse(pkgFile).devDependencies);
}

function restArgs(args) {
  let params = [];
  for (var i = 0; i < args.length; i++) {
    params.push(args[i]);
  }

  return params;
}

function renamePlugin(name) {
  return camelCase(name.replace(/^gulp(-|\.)/, ''));
}

GLOBAL.needDeps = [];
GLOBAL.use = function() {
  const pkgDeps = getPackageDeps();
  const taskDeps = restArgs(arguments);
  const needDeps = taskDeps.filter((dependName) => {
    return (pkgDeps.indexOf(dependName) === -1);
  });

  if (needDeps.length) {
    GLOBAL.needDeps = GLOBAL.needDeps.concat(needDeps);
    return;
  }

  const deps = { gulp: require('gulp') };
  taskDeps.forEach((dependName) => {
    deps[renamePlugin(dependName)] = require(dependName);
  });

  return deps;
};

'use strict';

const fs = require('fs');
const camelCase = require('camelcase');

function getPackageDeps() {
  const pkgFile = fs.readFileSync('./package.json');
  return Object.keys(JSON.parse(pkgFile).devDependencies);
}

function renamePlugin(name) {
  return camelCase(name.replace(/^gulp(-|\.)/, ''));
}

GLOBAL.needDeps = [];
GLOBAL.use = function() {
  const pkgDeps = getPackageDeps();
  const taskDeps = Array.from(arguments);
  const needDeps = taskDeps.filter((dependName) => {
    return (pkgDeps.indexOf(dependName) === -1);
  });

  if (needDeps.length) {
    GLOBAL.needDeps = GLOBAL.needDeps.concat(needDeps);
    return;
  }

  const deps = { gulp: require('gulp') };
  taskDeps.forEach((dependName) => {
    deps[renamePlugin(dependName)] = require(dependName);
  });

  return deps;
};

Use Array.from instead of custom function


'use strict';

var gulp = require('gulp');
var to5 = require('gulp-6to5');
var watch = require('gulp-watch');
var deploy = require('gulp-gh-pages');
var jshint = require('gulp-jshint');

var options = {
    cacheDir: './tmp'
};

gulp.task('deploy', ['js:lint', 'js:compile'], function () {
    return gulp.src(['./**/*', '!./node_modules/**'])
        .pipe(deploy(options));
});

gulp.task('js:compile', function() {
    return gulp.src('src/**/*.js')
        .pipe(to5())
        .pipe(gulp.dest('dist'));
});

gulp.task('js:lint', function() {
    return gulp.src('./src/**/*.js')
        .pipe(jshint({ esnext: true }))
        .pipe(jshint.reporter('default'));
});

gulp.task('default', function () {
    watch('./src/**/*.js', function () {
        gulp.start('js:lint');
        gulp.start('js:compile');
    });
});

'use strict';

var gulp = require('gulp');
var to5 = require('gulp-6to5');
var watch = require('gulp-watch');
var deploy = require('gulp-gh-pages');
var jshint = require('gulp-jshint');

var options = {
    cacheDir: './tmp'
};

gulp.task('deploy', ['js:lint', 'js:compile'], function () {
    return gulp.src(['./**/*', '!./node_modules/**', '!./tmp/**'])
        .pipe(deploy(options));
});

gulp.task('js:compile', function() {
    return gulp.src('src/**/*.js')
        .pipe(to5())
        .pipe(gulp.dest('dist'));
});

gulp.task('js:lint', function() {
    return gulp.src('./src/**/*.js')
        .pipe(jshint({ esnext: true }))
        .pipe(jshint.reporter('default'));
});

gulp.task('default', function () {
    watch('./src/**/*.js', function () {
        gulp.start('js:lint');
        gulp.start('js:compile');
    });
});

Add tmp directory to deploy exclude list


/**
 * jKif - 2015
 * truth_tree_base.js
 * @file Base TruthTree Constructor which all TruthTrees call up to on initialization
 * @author Clark Feusier - cfeusier@gmail.com
 * @copyright Copyright (C) Clark Feusier - All Rights Reserved
 */

/**
 *
 * @params {Array} kwBase
 * @constructor
 */
function TruthTreeBase(kwBase) {
  this.kwBase = kwBase || [];
}

TruthTreeBase.prototype = {
  constructor: TruthTreeBase,
  isConsistent: function() {}
};

/**
 *
 * @type {TruthTreeBase}
 */
module.exports = TruthTreeBase;

/**
 * jKif - 2015
 * truth_tree_base.js
 * @file Base TruthTree Constructor which all TruthTrees call up to on initialization
 * @author Clark Feusier - cfeusier@gmail.com
 * @copyright Copyright (C) Clark Feusier - All Rights Reserved
 */

/**
 *
 * @params {Array} kwBase
 * @constructor
 */
function TruthTreeBase(kwBase) {
  this.openStacks = {};
  this.closedStacks = {};
}

TruthTreeBase.prototype = {
  constructor: TruthTreeBase,
  isConsistent: function() {}
};

/**
 *
 * @type {TruthTreeBase}
 */
module.exports = TruthTreeBase;

Update truth tree base to have open and close stack properties


var gulp = require('gulp'),
    gutil = require('gulp-util'),
    coffee = require('gulp-coffee');

gulp.task('logger-change', function(){
    gulp.src('src/*.coffee')
        .pipe(coffee({bare: true}).on('error', gutil.log))
        .pipe(gulp.dest('lib'));
});

gulp.task('watch', function() {
    gulp.watch('src/*.coffee', ['logger-change']);
});

var gulp = require('gulp'),
    gutil = require('gulp-util'),
    coffee = require('gulp-coffee');

gulp.task('default', function(){
    gulp.src('src/*.coffee')
        .pipe(coffee({bare: true}).on('error', gutil.log))
        .pipe(gulp.dest('lib'));
});

gulp.task('watch', function() {
    gulp.watch('src/*.coffee', ['default']);
});

Make compilation the gulp default task


// gulp
var gulp = require('gulp');

// plugins
var jshint = require('gulp-jshint');
var jscs = require('gulp-jscs');
var argv   = require('yargs').argv;
var gulpIgnore = require('gulp-ignore');

var filesJSToScan =  (argv.js ? argv.js : 'src/**/*.js');
var filesJSToIgnore = [
    '**/*jquery*',
    '**/*min*',
    '**/wowslider*',
    '**/wowSlider/engine1/script.js',
    '**/js/bootstrap.js',
    '**/js/wowSliderForm.js'
];

gulp.task('jscs', function () {
    return gulp.src(filesJSToScan)
        .pipe(gulpIgnore.exclude(filesJSToIgnore))
        .pipe(jscs());
});

gulp.task('jshint', function () {
    gulp.src(filesJSToScan)
        .pipe(gulpIgnore.exclude(filesJSToIgnore))
        .pipe(jshint())
        .pipe(jshint.reporter('default'));
});

gulp.task('default',
    ['jshint', 'jscs']
);

// gulp
var gulp = require('gulp');

// plugins
var jshint = require('gulp-jshint');
var jscs = require('gulp-jscs');
var argv   = require('yargs').argv;
var gulpIgnore = require('gulp-ignore');

var filesJSToScan =  (argv.js ? argv.js : 'src/**/*.js');
var filesJSToIgnore = [
];

gulp.task('jscs', function () {
    return gulp.src(filesJSToScan)
        .pipe(gulpIgnore.exclude(filesJSToIgnore))
        .pipe(jscs());
});

gulp.task('jshint', function () {
    gulp.src(filesJSToScan)
        .pipe(gulpIgnore.exclude(filesJSToIgnore))
        .pipe(jshint())
        .pipe(jshint.reporter('default'));
});

gulp.task('default',
    ['jshint', 'jscs']
);

Delete non present ignore files from gulp parsing


/* exported AllSets */

// Copyright 2016 Endless Mobile, Inc.

const Actions = imports.app.actions;
const Dispatcher = imports.app.dispatcher;
const Module = imports.app.interfaces.module;
const QueryObject = imports.search.queryObject;
const Xapian = imports.app.modules.selection.xapian;

const AllSets = new Module.Class({
    Name: 'Selection.AllSets',
    Extends: Xapian.Xapian,

    _init: function (props={}) {
        this.parent(props);
        this._set_needs_refresh(true);
    },

    construct_query_object: function (limit, query_index) {
        if (query_index > 0)
            return null;
        return new QueryObject.QueryObject({
            limit: limit,
            tags_match_all: ['EknSetObject'],
        });
    },

    show_more: function () {
        Dispatcher.get_default().dispatch({
            action_type: Actions.ALL_SETS_CLICKED,
        });
    },
});

/* exported AllSets */

// Copyright 2016 Endless Mobile, Inc.

const Actions = imports.app.actions;
const Dispatcher = imports.app.dispatcher;
const Module = imports.app.interfaces.module;
const QueryObject = imports.search.queryObject;
const Xapian = imports.app.modules.selection.xapian;

const AllSets = new Module.Class({
    Name: 'Selection.AllSets',
    Extends: Xapian.Xapian,

    _init: function (props={}) {
        this.parent(props);
        this._set_needs_refresh(true);
    },

    construct_query_object: function (limit, query_index) {
        if (query_index > 0)
            return null;
        return new QueryObject.QueryObject({
            limit: limit,
            tags_match_all: ['EknSetObject'],
            sort: QueryObject.QueryObjectSort.SEQUENCE_NUMBER,
        });
    },

    show_more: function () {
        Dispatcher.get_default().dispatch({
            action_type: Actions.ALL_SETS_CLICKED,
        });
    },
});

Sort sets by sequence number.


import update from 'immutability-helper'

// TODO: port user-specific code from the otp reducer.
function createUserReducer () {
  const initialState = {}

  return (state = initialState, action) => {
    switch (action.type) {
      case 'SET_CURRENT_USER': {
        return update(state, {
          accessToken: { $set: action.payload.accessToken },
          loggedInUser: { $set: action.payload.user }
        })
      }

      case 'SET_CURRENT_USER_MONITORED_TRIPS': {
        return update(state, {
          loggedInUserMonitoredTrips: { $set: action.payload }
        })
      }

      case 'SET_PATH_BEFORE_SIGNIN': {
        return update(state, {
          pathBeforeSignIn: { $set: action.payload }
        })
      }

      default:
        return state
    }
  }
}

export default createUserReducer

import update from 'immutability-helper'

// TODO: port user-specific code from the otp reducer.
function createUserReducer () {
  const initialState = {
    accessToken: null,
    loggedInUser: null,
    loggedInUserMonitoredTrips: null,
    pathBeforeSignIn: null
  }

  return (state = initialState, action) => {
    switch (action.type) {
      case 'SET_CURRENT_USER': {
        return update(state, {
          accessToken: { $set: action.payload.accessToken },
          loggedInUser: { $set: action.payload.user }
        })
      }

      case 'SET_CURRENT_USER_MONITORED_TRIPS': {
        return update(state, {
          loggedInUserMonitoredTrips: { $set: action.payload }
        })
      }

      case 'SET_PATH_BEFORE_SIGNIN': {
        return update(state, {
          pathBeforeSignIn: { $set: action.payload }
        })
      }

      default:
        return state
    }
  }
}

export default createUserReducer

Add initial user redux state.


/*global $, Whisper, Backbone, textsecure, extension*/
/*
 * vim: ts=4:sw=4:expandtab
 */
(function () {
    'use strict';

    function logError(error) {
        extension.windows.getBackground(function(bg) {
            bg.console.log('index.html: ', error);
        });
    }

    window.onerror = function(message, script, line, col, error) {
        logError(error);
    };

    function render() {
        extension.windows.getBackground(function(bg) {
            bg.owsDesktopApp.getAppView(window).then(function(appView) {
                var bodyEl = document.getElementById('signal-container');
                bodyEl.innerHTML = "";
                bodyEl.append(appView.el);
            });
        });
    }

    window.addEventListener('onreload', render);
    render();
}());

/*global $, Whisper, Backbone, textsecure, extension*/
/*
 * vim: ts=4:sw=4:expandtab
 */
(function () {
    'use strict';

    function logError(error) {
        extension.windows.getBackground(function(bg) {
            bg.console.log('index.html: ', error);
        });
    }

    window.onerror = function(message, script, line, col, error) {
        logError(error);
    };

    function render() {
        extension.windows.getBackground(function(bg) {
            bg.owsDesktopApp.getAppView(window).then(function(appView) {
                var bodyEl = document.getElementById('signal-container');
                bodyEl.innerHTML = "";
                bodyEl.appendChild(appView.el);
            });
        });
    }

    window.addEventListener('onreload', render);
    render();
}());

Use appendChild instead of append


import React from "react";
import moment from 'moment';
import ReactCSSTransitionGroup from 'react-addons-css-transition-group'

import NewsItem from "./NewsItem";
import NewsCow from "./NewsCow";

// const NewsItem = ({info}) => (
//

//     {info.title} {info.sitelink}
//

//       <img src={`/dist/icons/${info.sitehost}.jpg`} />
//

//

// )

export default class NewsList extends React.Component {
  render () {

    if (this.props.loading) {
      return
Loading

    }

    if (this.props.newsItems.length === 0) {
      return (

      );
    } else {
      var makeList = x =>
<NewsItem info={x}/>
;

      return (
          <ReactCSSTransitionGroup component="ul" className="newsWrapper"
            transitionName="example"
            transitionAppear={true}
            transitionAppearTimeout={500}
            transitionEnterTimeout={500}
            transitionLeaveTimeout={300}>
            { this.props.newsItems.map(makeList) }
          </ReactCSSTransitionGroup>
  		);
    }
	}
};

Use itemID as react list key.


import React from 'react';
import PropTypes from 'prop-types';
import { useTranslator } from '@u-wave/react-translate';
import CircularProgress from '@material-ui/core/CircularProgress';
import Tooltip from '@material-ui/core/Tooltip';
import IconButton from '@material-ui/core/IconButton';
import ShuffleIcon from '@material-ui/icons/Shuffle';

const { useCallback, useState } = React;
const HARDCODED_LOADING_SIZE = 24; // FIXME derive this from some mui property?

function ShuffleButton({ onShuffle }) {
  const [isLoading, setLoading] = useState(false);
  const { t } = useTranslator();

  const onClick = useCallback(() => {
    setLoading(true);
    onShuffle().finally(() => {
      setLoading(false);
    });
  }, [onShuffle]);

  return (
    <Tooltip title={t('playlists.shuffle')} placement="top">
      <IconButton
        className="PlaylistMeta-iconButton"
        onClick={onClick}
      >
        {isLoading ? (
          <CircularProgress size={HARDCODED_LOADING_SIZE} />
        ) : (
          <ShuffleIcon />
        )}
      </IconButton>
    </Tooltip>
  );
}

ShuffleButton.propTypes = {
  onShuffle: PropTypes.func.isRequired,
};

export default ShuffleButton;

import React from 'react';
import PropTypes from 'prop-types';
import { useTranslator } from '@u-wave/react-translate';
import CircularProgress from '@material-ui/core/CircularProgress';
import Tooltip from '@material-ui/core/Tooltip';
import IconButton from '@material-ui/core/IconButton';
import ShuffleIcon from '@material-ui/icons/Shuffle';

const { useCallback, useState } = React;
const HARDCODED_LOADING_SIZE = 24; // FIXME derive this from some mui property?

function ShuffleButton({ onShuffle }) {
  const [isLoading, setLoading] = useState(false);
  const { t } = useTranslator();

  const onClick = useCallback(() => {
    setLoading(true);
    onShuffle().finally(() => {
      setLoading(false);
    });
  }, [onShuffle]);

  return (
    <Tooltip title={t('playlists.shuffle')} placement="top">
      <IconButton
        className="PlaylistMeta-iconButton"
        onClick={onClick}
        disabled={isLoading}
      >
        {isLoading ? (
          <CircularProgress size={HARDCODED_LOADING_SIZE} />
        ) : (
          <ShuffleIcon />
        )}
      </IconButton>
    </Tooltip>
  );
}

ShuffleButton.propTypes = {
  onShuffle: PropTypes.func.isRequired,
};

export default ShuffleButton;

Disable shuffle button while shuffling in progress.


var mongoose = require('mongoose');

module.exports.finderForProperty = function(field, props) {
    if (props === undefined) {
        props = {};
    }

    var isFindOne = props['findOne'] === true;
    var caseInsensitive = props['caseInsensitive'] === true;

    return function(value, cb) {
        var lookupHash = {};
        lookupHash[field] = caseInsensitive ? new RegExp('^' + value + '$', 'i') : value;

        if(isFindOne) {
            this.findOne(lookupHash, cb);
        } else {
            this.find(lookupHash, cb);
        }
    };
};

module.exports.pushOntoProperty = function(field, props) {
  if (props === undefined) {
    props = {};
  }

  var isFindOne = props['findOne'] === true;
  var caseInsensitive = props['caseInsensitive'] === true;

  return function(value, cb) {
    var lookupHash = {};
    lookupHash[field] = caseInsensitive ? new RegExp('^' + value + '$', 'i') : value;

    if (isFindOne) {
      //
    } else {
      //
    }
  }
}

var mongoose = require('mongoose');

module.exports.finderForProperty = function(field, props) {
    if (props === undefined) {
        props = {};
    }

    var isFindOne = props['findOne'] === true;
    var caseInsensitive = props['caseInsensitive'] === true;

    return function(value, cb) {
        var lookupHash = {};
        lookupHash[field] = caseInsensitive ? new RegExp('^' + value + '$', 'i') : value;

        if(isFindOne) {
            this.findOne(lookupHash, cb);
        } else {
            this.find(lookupHash, cb);
        }
    };
};

module.exports.pushOntoProperty = function(field, props) {
  if (props === undefined) {
    props = {};
  }

  var isFindOne = props['findOne'] === true;
  var caseInsensitive = props['caseInsensitive'] === true;

  return function(value, cb) {
    var lookupHash = {};
    lookupHash[field] = caseInsensitive ? new RegExp('^' + value + '$', 'i') : value;

    if (isFindOne) {
      this.
    } else {
      this.
    }
  }
}

Revert "SYNTAX ERROR SORRY EVERYONE"


/**
	An mochi-styled TextArea control. In addition to the features of
	enyo.TextArea, mochi.TextArea has a
	*defaultFocus* property that can be set to true to focus the TextArea when
	it's rendered. Only one TextArea should be set as the *defaultFocus*.

	Typically, an mochi.TextArea is placed inside an
	mochi.InputDecorator, which provides
	styling, e.g.:

		{kind: "mochi.InputDecorator", components: [
			{kind: "mochi.TextArea", onchange: "inputChange"}
		]}
*/
enyo.kind({
	name: "mochi.TextArea",
	kind: "enyo.TextArea",
	classes: "mochi-textarea"
});

/**
 * A mochi-styled TextArea control. In addition to the features of {@link enyo.TextArea}, `mochi.TextArea` has a
 * `defaultFocus` property that can be set to true to focus the TextArea when it is rendered. Only one `mochi.TextArea`
 * should be set as the `defaultFocus`.
 *
 * Typically, a `mochi.TextArea` is placed inside a {@link mochi.InputDecorator}, which provides styling, e.g.:
 *
 * ```
 * {kind: "mochi.InputDecorator", components: [
 *	{kind: "mochi.TextArea", onchange: "inputChange"}
 * ]}
 * ```
 *
 * @class mochi.TextArea
 * @extends enyo.TextArea
 * @ui
 * @public
 */
(function (enyo, scope) {
	enyo.kind(
		/** @lends mochi.TextArea.prototype */ {

		/**
		 * @private
		 */
		name: "mochi.TextArea",

		/**
		 * @private
		 */
		kind: "enyo.TextArea",

		/**
		 * @private
		 */
		classes: "mochi-textarea"
	});

})(enyo, this);

Complete JSDoc3 conversion on Mochi


// annotations that are simple annotations
export const RICH_TEXT_ANNOS = ['bold', 'italic', 'small-caps', 'superscript', 'subscript']

export const EXTENDED_FORMATTING = ['monospace', 'strike-through', 'underline', 'overline']

export const LINKS_AND_XREFS = ['xref', 'external-link']

export const INLINE_NODES = ['inline-formula', 'inline-graphic']

export const BLOCK_LEVEL = ['block-formula', 'block-quote', 'figure', 'heading', 'list', 'paragraph', 'preformat', 'table-figure']

// annotations that are simple annotations
export const RICH_TEXT_ANNOS = ['bold', 'italic', 'superscript', 'subscript']

export const EXTENDED_FORMATTING = ['monospace', 'small-caps', 'strike-through', 'underline', 'overline']

export const LINKS_AND_XREFS = ['xref', 'external-link']

export const INLINE_NODES = ['inline-formula', 'inline-graphic']

export const BLOCK_LEVEL = ['block-formula', 'block-quote', 'figure', 'heading', 'list', 'paragraph', 'preformat', 'table-figure']

Move small caps to extended formatting.


const Timer = React.createClass({
  getInitialState: function () {
    return { secondsToWait: this.props.secondsToWait };
  },
  runTimer: function () {
    let timer = setInterval(() => {
      if (this.state.secondsToWait === 0) clearInterval(timer);

      this.setState({ secondsToWait: this.state.secondsToWait - 1 });
    }, 1000);
  },
  componentDidMount: function () {
    this.runTimer();
  },
  render: function () {
    if (this.state.secondsToWait > 0) {
      let unit = (this.state.secondsToWait > 1)? 'seconds': 'second';
      return
 Waiting {this.state.secondsToWait} {unit}...
;
    }

    return
Done!
;
  }
});

const Timer = React.createClass({
  getInitialState: function () {
    return { secondsToWait: this.props.secondsToWait };
  },
  runTimer: function () {
    this.setState({
      timer: setInterval(() => {
        if (this.state.secondsToWait === 0) clearInterval(timer);

        this.setState({ secondsToWait: this.state.secondsToWait - 1 });
      }, 1000)
    });
  },
  stopTimer: function () {
    clearInterval(this.state.timer);
  },
  componentDidMount: function () {
    this.runTimer();
  },
  componentWillUnmount: function () {
    this.stopTimer();
  },
  render: function () {
    if (this.state.secondsToWait > 0) {
      let unit = (this.state.secondsToWait > 1)? 'seconds': 'second';
      return
 Waiting {this.state.secondsToWait} {unit}...
;
    }

    return
Done!
;
  }
});

Correct the timer element issue


'use strict'

module.exports = {
  hooks: {
    'pre-commit': 'npm run lint',
    'pre-push': 'npm run lint && npm test',
  },
}

'use strict'

module.exports = {
  hooks: {
    'pre-commit': 'npm run lint',
    'pre-push': 'npm run lint && npm build && npm test',
  },
}

Add build step to pre-push git hook


import moment from 'moment'
const today = new moment()

export default [
	{
		start_date: {
			year: 2015,
			month: 6,
			day: 16
		},
		end_date: {
			year: 2016,
			month: 11,
			day: 9
		},
		text: {
			headline: "2016 Presidential Campaign"
		},
	},
	{
		start_date: {
			year: 2016,
			month: 11,
			day: 9
		},
		end_date: {
			year: 2017,
			month: 1,
			day: 20
		},
		text: {
			headline: "Transition Period"
		},
	},
	{
		start_date: {
			year: 2017,
			month: 1,
			day: 20
		},
		end_date: {
			year: today.year(),
			month: today.month() + 1,
			day: today.day() + 1
		},
		text: {
			headline: "Trump Administration"
		}
	}
]

import moment from 'moment'
const today = new moment()

export default [
	{
		start_date: {
			year: 2015,
			month: 6,
			day: 16
		},
		end_date: {
			year: 2016,
			month: 11,
			day: 9
		},
		text: {
			headline: "2016 Presidential Campaign"
		},
	},
	{
		start_date: {
			year: 2016,
			month: 11,
			day: 9
		},
		end_date: {
			year: 2017,
			month: 1,
			day: 20
		},
		text: {
			headline: "Transition Period"
		},
	},
	{
		start_date: {
			year: 2017,
			month: 1,
			day: 20
		},
		end_date: {
			year: today.year(),
			month: today.month() + 1,
			day: today.date() + 1
		},
		text: {
			headline: "Trump Administration"
		}
	}
]

Fix administration era end day


import Ember from 'ember';
import RecordKeeperMixin from 'ember-time-machine/mixins/time-machine';
import Record from 'ember-time-machine/-private/Record';
import { wrapValue, unwrapValue } from 'ember-time-machine/utils/value';
import { pathInGlobs } from 'ember-time-machine/utils/utils';

export default Ember.ObjectProxy.extend(RecordKeeperMixin, {
  unknownProperty(key) {
    return wrapValue(this, key, this._super(...arguments));
  },

  setUnknownProperty(key, value) {
    let content = this.get('content');
    let state = this.get('_rootMachineState');
    let path = this.get('_path');

    if (state && !pathInGlobs(path.concat(key).join('.'), state.get('frozenProperties'))) {
      this._addRecord(new Record({
        target: content,
        path,
        key,
        before: content.get(key),
        after: value
      }));

      return this._super(key, unwrapValue(value));
    }

    this.notifyPropertyChange(key);
    return;
  }
});

import Ember from 'ember';
import RecordKeeperMixin from 'ember-time-machine/mixins/time-machine';
import Record from 'ember-time-machine/-private/Record';
import { wrapValue, unwrapValue } from 'ember-time-machine/utils/value';
import { pathInGlobs } from 'ember-time-machine/utils/utils';

const { get } = Ember;

export default Ember.ObjectProxy.extend(RecordKeeperMixin, {
  unknownProperty(key) {
    return wrapValue(this, key, this._super(...arguments));
  },

  setUnknownProperty(key, value) {
    let content = get(this, 'content');
    let state = get(this, '_rootMachineState');
    let path = get(this, '_path');

    if (state && !pathInGlobs(path.concat(key).join('.'), get(state, 'frozenProperties'))) {
      this._addRecord(new Record({
        target: content,
        path,
        key,
        before: get(content, key),
        after: value
      }));

      return this._super(key, unwrapValue(value));
    }

    this.notifyPropertyChange(key);
    return;
  }
});

Use Ember.get in Object Proxy


var Scale = new Class({
    initialize: function(degrees, tuning) {
        this.degrees = degrees;
        this.tuning = tuning || new EqualTemperamentTuning(12);
    },

    getFrequency: function(degree, rootFrequency, octave) {
        var frequency = rootFrequency;
        frequency *= Math.pow(this.tuning.octaveRatio, octave);
        frequency *= this.tuning.ratios[this.degrees[degree]];
        return frequency;
    }
});

var Scale = new Class({
    initialize: function(degrees, tuning) {
        this.degrees = degrees;
        this.tuning = tuning || new EqualTemperamentTuning(12);
    },

    getFrequency: function(degree, rootFrequency, octave) {
        var frequency = rootFrequency;
        octave += Math.floor(degree / this.degrees.length);
        degree %= this.degrees.length;
        frequency *= Math.pow(this.tuning.octaveRatio, octave);
        frequency *= this.tuning.ratios[this.degrees[degree]];
        return frequency;
    }
});

Make scale automatically wrap high degrees to higher octaves.


var express = require('express');
var gm = require('gm');
var hashblot = require('hashblot');

function encode(str) {
  return encodeURIComponent(str).replace(/%20/g,'+')}
function decode(str) {
  return decodeURIComponent(str.replace(/\+/g,' '))}

module.exports = function appctor(opts) {
  var gmopts = opts.gm || {};

  var app = express();

  app.get('/sha1q/:size/:input.png', function sha1qpPng(req, res, next) {
    var str = req.params.input;

    gm(req.params.size, req.params.size, '#fff')
      .options(gmopts)
      .fill('#000').draw('path ' + hashblot.sha1qpd(str))
      .toBuffer('PNG',function (err, buffer) {
        if (err) return next(err);
        res.type('png');
        res.send(buffer);
      });
  });

  return app;
};

var express = require('express');
var gm = require('gm');
var hashblot = require('hashblot');

function encode(str) {
  return encodeURIComponent(str).replace(/%20/g,'+')}
function decode(str) {
  return decodeURIComponent(str.replace(/\+/g,' '))}

module.exports = function appctor(opts) {
  var gmopts = opts.gm || {};

  var app = express();

  app.get('/sha1q/:size/:input.png', function sha1qpPng(req, res, next) {
    var str = req.params.input;
    var size = req.params.size;

    gm(size, size, '#fff').options(gmopts)
      .draw([
        'viewbox 0 0 255 255',
        'fill #000',
        'fill-rule nonzero',
        'path', hashblot.sha1qpd(str)].join(' '))
      .toBuffer('PNG',function (err, buffer) {
        if (err) return next(err);
        res.type('png');
        res.send(buffer);
      });
  });

  return app;
};

Add viewbox, fill, and fill-rule in draw command


var path = require('path');

var Rules = require('./lib/rules');
var Resource = require('./resource');

var rules = new Rules({
  filename: path.join(process.cwd(), './rules.yml')
});

module.exports = function(server) {
  var devices = {};

  var deviceCommand = function(d) {
    var device = devices[d.device];
    if (!device) {
      server.warn('Device ' + d.device + ' not found');
      return;
    }
    var command = d.action.split(' ');
    device.call.apply(this, command);
  };

  var handleStateUpdate = function(device, state) {
    rules.getByTrigger(device.name, state).forEach(function(triggered) {
      server.info('Rule ' + triggered.id + ' triggered by ' + device.name + ':' + state);
      triggered.getActions().forEach(deviceCommand);
    });
  };

  var deviceQuery = server.query('type like "%"');
  server.observe([deviceQuery], function(device) {
    devices[device.name] = device;
    device.streams.state.on('data', function(event) {
      handleStateUpdate(device, event.data);
    });
  });

  var webhook = function(name, rule) {
    if (name !== 'trigger') return;
    server.info('Rule ' + rule.id + ' triggered by Webhook');
    rule.getActions().forEach(deviceCommand);
  };

  var argo = server.httpServer.cloud.argo;
  argo.add(Resource, rules, webhook);
};

var path = require('path');

var Rules = require('./lib/rules');
var Resource = require('./resource');

var rules = new Rules({
  filename: path.join(process.cwd(), './rules.yml')
});

module.exports = function(server) {
  var devices = {};

  var deviceCommand = function(d) {
    var device = devices[d.device];
    if (!device) {
      server.warn('Device ' + d.device + ' not found (Rule: ' + this.id + ')');
      return;
    }
    var command = d.action.split(' ');
    device.call.apply(this, command);
  };

  var handleStateUpdate = function(device, state) {
    rules.getByTrigger(device.name, state).forEach(function(triggered) {
      server.info('Rule ' + triggered.id + ' triggered by ' + device.name + ':' + state);
      triggered.getActions().forEach(deviceCommand, triggered);
    });
  };

  var deviceQuery = server.query('type like "%"');
  server.observe([deviceQuery], function(device) {
    devices[device.name] = device;
    device.streams.state.on('data', function(event) {
      handleStateUpdate(device, event.data);
    });
  });

  var webhook = function(name, rule) {
    if (name !== 'trigger') return;
    server.info('Rule ' + rule.id + ' triggered by Webhook');
    rule.getActions().forEach(deviceCommand, rule);
  };

  var argo = server.httpServer.cloud.argo;
  argo.add(Resource, rules, webhook);
};

Add rule context for device command (enhances logging)


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

var routes = require('./api/routes');

app.set('port', 14000);

app.use('/api/', routes);

app.listen(app.get('port'), function() {
});

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

var routes = require('./api/routes');

app.set('port', 14000);

app.use(function(req, res, next) {
  res.header('Access-Control-Allow-Origin', '*');
  next();
});

app.use('/api/', routes);

app.listen(app.get('port'), function() {
});

Allow CORS requests from all domains (*)


function NotesApplication(author) {
	this.author = author;
	this.notesList = [];
}

NotesApplication.prototype.create(note_content){
	this.notesList.push(note_content)
}

NotesApplication.prototype.listNotes(){
	if(this.notesList.length>0){
		for(i = 0; i<this.notesList.length; i++){
			console.log("Note ID: " +i)
			console.log(this.notesList[i])
			console.log("By Author " + this.author );
		}
	}
}

NotesApplication.prototype.get(note_id){
	return this.notesList[note_id];
}

NotesApplication.prototype.search(search_text){
	var result = "Showing results for search"
	for(i=0; i<this.notesList.length; i++){
		if(this.notesList.indexOf(search_text) !== -1){

		}
	}
}

function NotesApplication(author) {
	this.author = author;
	this.notesList = [];
}

NotesApplication.prototype.create(note_content){
	this.notesList.push(note_content)
}

NotesApplication.prototype.listNotes(){
	if(this.notesList.length>0){
		for(i = 0; i<this.notesList.length; i++){
			console.log("Note ID: " +i)
			console.log(this.notesList[i])
			console.log("By Author " + this.author );
		}
	}
}

NotesApplication.prototype.get(note_id){
	console.log(this.notesList[note_id]);
}

NotesApplication.prototype.search(search_text){
	var result = "Showing results for search"
	for(i=0; i<this.notesList.length; i++){
		if(this.notesList.indexOf(search_text) !== -1){

		}
	}
}

NotesApplication.prototype.delete(note_id){
	if(this.notesList.length> note_id)
	this.notesList.splice(note_id, 1)
}

NotesApplication.prototype.edit(note_id, new_content){
	if(this.notesList.length > note_id){
		this.notesList[note_id] = new_content;
	}
}

Add the delete and the edit functionality


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

app.get('/', function(req, res) {
  res.send('Hello World!');
});

var server = app.listen(3000, function() {
  console.log('Listening on http://localhost:3000');
});

var app = require('express')();
var db = require('mongoose');
db.connect('mongodb://localhost/caedence_net_development');

var Note = db.model('Note', {
  title: String,
  body_html: String,
  body_text: String,
  user_id: Number,
  created_at: { type: Date, default: Date.now },
  updated_at: { type: Date, default: Date.now }
});

// create some records with mongo cli like this:
// $ mongod
// > use caedence_net_development
// > db.notes.insert({ title: "Hello", body_html: "
World
", body_text: "World" })
// WriteResult({ "nInserted" : 1 })
// > db.notes.find()
// { "_id" : ObjectId("562473c0802fa33ca991217b"), "title" : "Hello", "body_html" : "
World
", "body_text" : "World" }
app.get('/notes/', function(req, res) {
  Note.find(function(err, notes) {
    res.json(notes);
  });
});
app.get('/', function(req, res) {
  res.send('Hello World!');
});

var server = app.listen(3000, function() {
  console.log('Listening on http://localhost:3000');
});

Create Mongoose model for Note, and list notes.


import React from 'react'

import GameCard from './GameCard'

export default (props) => {
  const userId = props.userId
  const teams = props.teams
  const games = []
  const gamesObj = props.games
  console.log('teams', teams)
  for (let key in gamesObj) {
    const game = gamesObj[key]
    game.gameKey = key
    game.ownedByCurrentUser = game.owner_id === userId
    games.push(game)
  }
  const fetchingGames = props.fetchingGames
  return (

      {!fetchingGames
        ? (games.length
          ? games.map((game, key) => <GameCard key={key} game={game} homeTeam={teams[game.home_team.key] && teams[game.home_team.key].name || '???'} awayTeam={teams[game.away_team.key] && teams[game.away_team.key].name || '???'} />)
          :
No Games Found
)
        :
Loading Games...

      }

  )
}

import React from 'react'

import GameCard from './GameCard'

export default (props) => {
  const userId = props.userId
  const teams = props.teams
  const games = []
  const gamesObj = props.games
  console.log('teams', teams)
  for (let key in gamesObj) {
    const game = gamesObj[key]
    game.gameKey = key
    game.ownedByCurrentUser = game.owner_id === userId
    games.push(game)
  }
  const fetchingGames = props.fetchingGames
  return (

      {!fetchingGames
        ? (games.length
          ? games.map((game, key) => <GameCard key={key} game={game}
              homeTeam={game.status_initialized ? teams[game.home_team.key] && teams[game.home_team.key].name || '???' : teams[game.home_team].name}
              awayTeam={game.status_initialized ? teams[game.away_team.key] && teams[game.away_team.key].name || '???' : teams[game.away_team].name} />)
          :
No Games Found
)
        :
Loading Games...

      }

  )
}

Fix game name on Dashboard when not initalised


/**
 * Main entrypoint for banknote.
 *
 * Copyright 2015 Ethan Smith
 */

var Backbone = require('backbone'),
    Marionette = require('backbone.marionette'),
    $ = require('jquery'),
    _ = require('underscore'),
    MainLayout = require('./src/view/MainLayout'),
    RegionModal = require('./src/common/modal/RegionModal.js'),
    CategorizedController = require('./src/controller/CategorizedController');
require('./src/common/library/CurrencyInputStyler');

var Banknote = new Marionette.Application();

Banknote.addRegions({
   central: '#app',
   modal: RegionModal
});

Banknote.addInitializer(function(options) {
   $.getJSON('demo.json', function(data) {

      var incomeCollection = new AmountEntryCollection(_.map(data.income, function(note) {
         return new AmountEntry(note);
      }));

      var controller = new CategorizedController({
         title: 'Income Totals'
      });
      Banknote.central.show(controller.render(incomeCollection));
   });
});

Banknote.start();

// Make Banknote available globally
global.Banknote = Banknote;

/**
 * Main entrypoint for banknote.
 *
 * Copyright 2015 Ethan Smith
 */

var Backbone = require('backbone'),
    Marionette = require('backbone.marionette'),
    $ = require('jquery'),
    _ = require('underscore'),
    MainLayout = require('./src/view/MainLayout'),
    RegionModal = require('./src/common/modal/RegionModal.js'),
    CategorizedController = require('./src/controller/CategorizedController');
require('./src/common/library/CurrencyInputStyler');

var Banknote = new Marionette.Application();

Banknote.addRegions({
   central: '#app',
   modal: RegionModal
});

Banknote.addInitializer(function(options) {
   $.getJSON('demo.json', function(data) {

      var incomeCollection = new AmountEntryCollection(data.income);

      var controller = new CategorizedController({
         title: 'Income Totals'
      });
      Banknote.central.show(controller.render(incomeCollection));
   });
});

Banknote.start();

// Make Banknote available globally
global.Banknote = Banknote;

Remove unneeded map for collection model


import { init, captureException, configureScope } from '@sentry/browser';

import { LOG_ERRORS, RAVEN_ENDPT, RELEASE } from '../constants';

export const setup = () => {
  if (LOG_ERRORS) {
    /* eslint-disable no-underscore-dangle */
    if (!window.__SENTRY_READY__) {
      init({ dsn: RAVEN_ENDPT });
      window.__SENTRY_READY__ = true;
    }
    /* eslint-enable no-underscore-dangle */
  }
};

export default (ex, context) => {
  if (LOG_ERRORS) {
    setup(); // memoized, it is fine to call this on every log

    configureScope(scope => scope.setExtra('release', RELEASE));

    if (context) {
      configureScope(scope => scope.setExtra('context', context));
    }

    captureException(ex);
  }

  return window.console && console.error && console.error(ex);
};

import { init, captureException, configureScope } from '@sentry/browser';

import { LOG_ERRORS, RAVEN_ENDPT, RELEASE } from '../constants';

export const setup = () => {
  if (LOG_ERRORS) {
    /* eslint-disable no-underscore-dangle */
    if (!window.__SENTRY_READY__) {
      init({ dsn: RAVEN_ENDPT, release: RELEASE });
      window.__SENTRY_READY__ = true;
    }
    /* eslint-enable no-underscore-dangle */
  }
};

export default (ex, context) => {
  if (LOG_ERRORS) {
    setup(); // memoized, it is fine to call this on every log

    if (context) {
      configureScope(scope => scope.setExtra('context', context));
    }

    captureException(ex);
  }

  return window.console && console.error && console.error(ex);
};

Set release attr in correct place


// Sohm Al (normal)
// Nobody remembers what to do here, so here's triggers.
[{
  zoneRegex: /^Sohm Al$/
  triggers: [
    {
      id: 'Sohm Al Myath Stack',
      regex: /1B:........:(\y{Name}):....:....:0017:0000:0000:0000:/,
      alertText: function(data) {
        if (data.matches[1] == data.me)
          return 'Stack on YOU';
        return 'Stack on ' + matches[1];
      },
    },
    {
      id: 'Sohm Al Myath Spread',
      regex: /1B:........:(\y{Name}):....:....:00AE:0000:0000:0000:/,
      alertText: function(data) {
        if (data.matches[1] == data.me)
          return 'Move away from others';
        return 'Move away from ' + matches[1];
      },
    },
    {
      id: 'Sohm Al Myath Chyme',
      regex: /:Added new combatant Chyme Of The Mountain/
      alertText: function(data) { return 'Kill Chyme Add'; },
    },
    {
      id: 'Sohm Al Tioman Meteor',
      regex: /1B:........:(\y{Name}):....:....:0007:0000:0000:0000:/,
      alertText: function(data) {
        if (data.matches[1] == data.me)
          return 'place meteor on edge';
      },
    },
  ]
}]

// Sohm Al (normal)
// Nobody remembers what to do here, so here's triggers.
[{
  zoneRegex: /^Sohm Al$/,
  triggers: [
    {
      id: 'Sohm Al Myath Stack',
      regex: /1B:........:(\y{Name}):....:....:0017:0000:0000:0000:/,
      alertText: function(data) {
        if (data.matches[1] == data.me)
          return 'Stack on YOU';
        return 'Stack on ' + matches[1];
      },
    },
    {
      id: 'Sohm Al Myath Spread',
      regex: /1B:........:(\y{Name}):....:....:00AE:0000:0000:0000:/,
      alertText: function(data) {
        if (data.matches[1] == data.me)
          return 'Move away from others';
        return 'Move away from ' + matches[1];
      },
    },
    {
      id: 'Sohm Al Myath Chyme',
      regex: /:Added new combatant Chyme Of The Mountain/
      alertText: function(data) { return 'Kill Chyme Add'; },
    },
    {
      id: 'Sohm Al Tioman Meteor',
      regex: /1B:........:(\y{Name}):....:....:0007:0000:0000:0000:/,
      alertText: function(data) {
        if (data.matches[1] == data.me)
          return 'place meteor on edge';
      },
    },
  ]
}]

Fix missing comma in sohm al triggers


#!/usr/bin/env node
'use strict';

var input = process.argv.slice(2);
var pkg = require('./package.json');
var stdin = require('get-stdin');
var viewport = require('./');

/**
 * Help screen
 */

function help() {
    console.log(pkg.description);
    console.log('');
    console.log('Usage');
    console.log('  $ viewport [device]');
    console.log('  $ cat <file> | viewport [device]');
    console.log('');
    console.log('Example');
    console.log('  $ viewport');
    console.log('  $ viewport iphone4 iphone5');
    console.log('  $ cat devices.txt | viewport');
}

/**
 * Show help
 */

if (input.indexOf('-h') !== -1 || input.indexOf('--help') !== -1) {
    help();
    return;
}

/**
 * Show package version
 */

if (input.indexOf('-v') !== -1 || input.indexOf('--version') !== -1) {
    console.log(pkg.version);
    return;
}

/**
 * Run
 */

function run(input) {
    viewport(input, function (err, devices) {
        if (err) {
            throw err;
        }

        console.log(devices);
    });
}

/**
 * Apply arguments
 */

if (process.stdin.isTTY) {
    run(input);
} else {
    stdin(function (data) {
        [].push.apply(input, data.trim().split('\n'));
        run(input);
    });
}

#!/usr/bin/env node
'use strict';

var csv = require('to-csv');
var input = process.argv.slice(2);
var pkg = require('./package.json');
var stdin = require('get-stdin');
var viewport = require('./');

/**
 * Help screen
 */

function help() {
    console.log(pkg.description);
    console.log('');
    console.log('Usage');
    console.log('  $ viewport [device]');
    console.log('  $ cat <file> | viewport [device]');
    console.log('');
    console.log('Example');
    console.log('  $ viewport');
    console.log('  $ viewport iphone4 iphone5');
    console.log('  $ cat devices.txt | viewport');
}

/**
 * Show help
 */

if (input.indexOf('-h') !== -1 || input.indexOf('--help') !== -1) {
    help();
    return;
}

/**
 * Show package version
 */

if (input.indexOf('-v') !== -1 || input.indexOf('--version') !== -1) {
    console.log(pkg.version);
    return;
}

/**
 * Run
 */

function run(input) {
    viewport(input, function (err, devices) {
        if (err) {
            throw err;
        }

        console.log(csv(devices));
    });
}

/**
 * Apply arguments
 */

if (process.stdin.isTTY) {
    run(input);
} else {
    stdin(function (data) {
        [].push.apply(input, data.trim().split('\n'));
        run(input);
    });
}

Convert CLI output to `.csv`


#!/usr/bin/env node

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

const flat = require('./index')

if (process.stdin.isTTY) {
  const filepath = process.argv.slice(2)[0]
  if (!filepath) return usage()
  // Read from file
  const file = path.resolve(process.cwd(), filepath)
  if (!file) return usage()
  if (!fs.existsSync(file)) return usage()
  out(require(file))
} else {
  // Read from newline-delimited STDIN
  const lines = []
  readline.createInterface({
    input: process.stdin,
    output: process.stdout,
    terminal: false
  })
    .on('line', line => lines.push(line))
    .on('close', () => out(JSON.parse(lines.join('\n'))))
}

function out (data) {
  process.stdout.write(JSON.stringify(flat(data), null, 2))
}

function usage () {
  console.log(`
Usage:

flat foo.json
cat foo.json | flat
`)

  process.exit(1)
}

#!/usr/bin/env node

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

const flat = require('./index')

if (process.stdin.isTTY) {
  const filepath = process.argv.slice(2)[0]
  if (!filepath) return usage()
  // Read from file
  const file = path.resolve(process.cwd(), filepath)
  if (!file) return usage(1)
  if (!fs.existsSync(file)) return usage(1)
  out(require(file))
} else {
  // Read from newline-delimited STDIN
  const lines = []
  readline.createInterface({
    input: process.stdin,
    output: process.stdout,
    terminal: false
  })
    .on('line', line => lines.push(line))
    .on('close', () => out(JSON.parse(lines.join('\n'))))
}

function out (data) {
  process.stdout.write(JSON.stringify(flat(data), null, 2))
}

function usage (code) {
  console.log(`
Usage:

flat foo.json
cat foo.json | flat
`)

  process.exit(code || 0)
}

Exit 1 on usage if specified a file.


/**
 *
 */
module.exports = {
  extends: 'airbnb-base',

  env: {
    jest: true,
  },

  rules: {
    'no-underscore-dangle': ['off'],
    'global-require': ['off'],
    'no-use-before-define': ['warn'],
    'brace-style': ['error', 'stroustrup', { allowSingleLine: true }],
    'import/prefer-default-export': ['off'],
    'import/imports-first': ['off'],
    'import/no-extraneous-dependencies': ['error', {
      devDependencies: ['**/*.test.js', '**/__mocks__/**', '**/__fixtures__/**'],
    }],
  },
};

/**
 *
 */
module.exports = {
  extends: 'airbnb-base',

  env: {
    jest: true,
  },

  rules: {
    'no-underscore-dangle': ['off'],
    'global-require': ['off'],
    'no-use-before-define': ['warn'],
    'brace-style': ['error', 'stroustrup', { allowSingleLine: true }],
    'import/prefer-default-export': ['off'],
    'import/imports-first': ['off'],
    'import/no-extraneous-dependencies': ['error', {
      devDependencies: ['**/*.test.js', '**/__mocks__/**', '**/__fixtures__/**'],
    }],
    'max-len': ['error', 150, 2, {
      ignoreUrls: true,
      ignoreComments: true,
      ignoreStrings: true,
      ignoreTemplateLiterals: true,
    }],
  },
};

Increase the maximum length of a line


import gql from 'graphql-tag';

export default gql`
  mutation createChannelMutation($title: String!) {
    create_channel(input: { title: $title }) {
      channel {
        id: slug
        href
        title
      }
    }
  }
`;

import gql from 'graphql-tag';

export default gql`
  mutation createChannelMutation($title: String!, $visibility: ChannelVisibility = PRIVATE) {
    create_channel(input: { title: $title, visibility: $visibility }) {
      channel {
        id: slug
        href
        title
      }
    }
  }
`;

Make channel private by default


craftEd.controller('RecsController', ['$scope', '$http', '$location', function($scope, $http, $location){

  var config = {
    headers: {
      'content-type': 'application/json'
    }
  };

  var tokens = {
    headers: {
      "access-token": window.sessionStorage.token,
      "token-type": "Bearer",
      "client": window.sessionStorage.client,
      "expiry": window.sessionStorage.expiry,
      "uid": window.sessionStorage.uid
    }
  };

  var tokensConfig = {
    headers: {
      'content-type': 'application/json',
      "access-token": window.sessionStorage.token,
      "token-type": "Bearer",
      "client": window.sessionStorage.client,
      "expiry": window.sessionStorage.expiry,
      "uid": window.sessionStorage.uid
    }
  };

  $http.get(rootUrl + '/users/:id/beer_types/rec_new', tokens)
    .then(function(response){
      $scope.allNewRecs= response.data
      $scope.rootUrl= rootUrl
    });

  $scope.selectBeer = function(newRecId){
    console.log(rootUrl +'/users/:user_id/beer_types/' + newRecId + '/tried_beer_ratings/new')
    $http.get(rootUrl +'/users/:user_id/beer_types/' + newRecId + '/tried_beer_ratings/new', tokens)
    $location.path('/rating');
  }
}]);

craftEd.controller('RecsController', ['$scope', '$http', '$location', function($scope, $http, $location){

  var config = {
    headers: {
      'content-type': 'application/json'
    }
  };

  var tokens = {
    headers: {
      "access-token": window.sessionStorage.token,
      "token-type": "Bearer",
      "client": window.sessionStorage.client,
      "expiry": window.sessionStorage.expiry,
      "uid": window.sessionStorage.uid
    }
  };

  var tokensConfig = {
    headers: {
      'content-type': 'application/json',
      "access-token": window.sessionStorage.token,
      "token-type": "Bearer",
      "client": window.sessionStorage.client,
      "expiry": window.sessionStorage.expiry,
      "uid": window.sessionStorage.uid
    }
  };

  $http.get(rootUrl + '/users/:id/beer_types/rec_new', tokens)
    .then(function(response){
      $scope.allNewRecs= response.data
      $scope.rootUrl= rootUrl
    });

  $scope.selectBeer = function(newRecId){
    $http.get(rootUrl +'/users/:user_id/beer_types/' + newRecId + '/tried_beer_ratings/new', tokens)
    $location.path('/rating');
  }
}]);

Update new recs view and controller 2


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

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

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

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

fs.writeFileSync('./docs/typography/white-space/index.html', html)

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

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

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

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

fs.writeFileSync('./docs/typography/white-space/index.html', html)

Add site footer to each documentation generator


export default {
    chan: {
        headers: {
            "Accept": "text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8",
            "Accept-Encoding": "gzip, deflate, sdch",
            "Accept-Language": "en-US,en;q=0.8",
            "Host": "a.4cdn.org",
            "User-Agent": "Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/53.0.2785.116 Safari/537.36",
            "Origin": "a.4cdn.org",
            "Referer": "http://boards.4chan.org/",
            "If-Modified-Since": "0",
            "DNT": 1,
            "Upgrade-Insecure-Requests": 1
        }
    }
}

export default {
    chan: {
        headers: {
            "Accept": "text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8",
            "Accept-Encoding": "gzip, deflate, sdch",
            "Accept-Language": "en-US,en;q=0.8",
            "Host": "a.4cdn.org",
            "User-Agent": "Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/53.0.2785.116 Safari/537.36",
            "Origin": "a.4cdn.org",
            "Referer": "http://boards.4chan.org/",
            "If-Modified-Since": "0",
            "DNT": 1,
            "Upgrade-Insecure-Requests": 1
        }
    },
    reddit: {
        "Accept": "text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8",
        "Accept-Encoding": "gzip, deflate, sdch, br",
        "Accept-Language": "en-US,en;q=0.8",
        "Cache-Control": "max-age=0",
        "Connection": "keep-alive",
        "Host": "www.reddit.com",
        "User-Agent": "Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/53.0.2785.116 Safari/537.36",
        "Upgrade-Insecure-Requests": 1,
        "DNT": 1
    }
}

Add headers for Reddit requests


// see http://vuejs-templates.github.io/webpack for documentation.
const path = require('path')

module.exports = {
    build: {
        env: require('./env.prod'),
        index: path.resolve(__dirname, '../build/index.html'),
        assetsRoot: path.resolve(__dirname, '../build'),
        assetsSubDirectory: 'static',
        assetsPublicPath: '/',
        productionSourceMap: true,
        // Gzip off by default as many popular static hosts such as
        // Surge or Netlify already gzip all static assets for you.
        // Before setting to `true`, make sure to:
        // npm install --save-dev compression-webpack-plugin
        productionGzip: false,
        productionGzipExtensions: ['js', 'css']
    },
    dev: {
        env: require('./env.dev'),
        port: 8080,
        assetsSubDirectory: 'static',
        assetsPublicPath: '/',
        proxyTable: {},
        // CSS Sourcemaps off by default because relative paths are "buggy"
        // with this option, according to the CSS-Loader README
        // (https://github.com/webpack/css-loader#sourcemaps)
        // In our experience, they generally work as expected,
        // just be aware of this issue when enabling this option.
        cssSourceMap: false
    }
}

// see http://vuejs-templates.github.io/webpack for documentation.
const path = require('path')

module.exports = {
    build: {
        env: require('./env.prod'),
        index: path.resolve(__dirname, '../build/200.html'),
        assetsRoot: path.resolve(__dirname, '../build'),
        assetsSubDirectory: 'static',
        assetsPublicPath: '/',
        productionSourceMap: true,
        // Gzip off by default as many popular static hosts such as
        // Surge or Netlify already gzip all static assets for you.
        // Before setting to `true`, make sure to:
        // npm install --save-dev compression-webpack-plugin
        productionGzip: false,
        productionGzipExtensions: ['js', 'css']
    },
    dev: {
        env: require('./env.dev'),
        port: 8080,
        assetsSubDirectory: 'static',
        assetsPublicPath: '/',
        proxyTable: {},
        // CSS Sourcemaps off by default because relative paths are "buggy"
        // with this option, according to the CSS-Loader README
        // (https://github.com/webpack/css-loader#sourcemaps)
        // In our experience, they generally work as expected,
        // just be aware of this issue when enabling this option.
        cssSourceMap: false
    }
}

Use Surge's 200 file for clean routing.


/**
 * Rename a function to a new name, for call stack and debugging
 *
 * @param {Function} func the function to rename
 * @param {String} newName the new name
 */
function renameFunction(func, newName) {
    // eslint-disable-next-line no-unused-vars
    const { value, ...others } = Object.getOwnPropertyDescriptor(func, 'name');
    Object.defineProperty(func, 'name', { value: newName, ...others });
}

export default renameFunction;

/**
 * Rename a function to a new name, for call stack and debugging
 *
 * @param {Function} func the function to rename
 * @param {String} newName the new name
 */
function renameFunction(func, newName) {
    // eslint-disable-next-line no-unused-vars
    const prop = Object.getOwnPropertyDescriptor(func, 'name');
    if (prop) {
        const { value, ...others } = prop;
        Object.defineProperty(func, 'name', { value: newName, ...others });
    }
}

export default renameFunction;

Rename function - Make it safer for IE


export const SELECT_KEY = 'SELECT_KEY'
export const CLEAR_SELECTED_KEYS = 'CLEAR_SELECTED_KEYS'
export const TOGGLE_COMPOSER = 'TOGGLE_COMPOSER'
export const TOGGLE_GENERATING_KEY = 'TOGGLE_GENERATING_KEY'
export const SHOW_COMPOSER_WITH_TYPE = 'SHOW_COMPOSER_WITH_TYPE'
export const SET_ACTIVE_ALIAS = 'SET_ACTIVE_ALIAS'
export const SET_OUTPUT = 'SET_OUTPUT'

export const SELECT_KEY = 'SELECT_KEY'
export const CLEAR_SELECTED_KEYS = 'CLEAR_SELECTED_KEYS'
export const TOGGLE_COMPOSER = 'TOGGLE_COMPOSER'
export const TOGGLE_GENERATING_KEY = 'TOGGLE_GENERATING_KEY'
export const TOGGLE_IS_COPIED = 'TOGGLE_IS_COPIED'
export const SHOW_COMPOSER_WITH_TYPE = 'SHOW_COMPOSER_WITH_TYPE'
export const SET_ACTIVE_ALIAS = 'SET_ACTIVE_ALIAS'
export const SET_OUTPUT = 'SET_OUTPUT'

Add toggle is copied constant


'use strict';

var path = process.cwd();
var Graphriend = require(path + '/app/controllers/Graphriend.server.js');
var sess;

module.exports = function (app) {

	function isLoggedIn (req, res, next) {
		sess = req.session;
		if (sess.user) {
			return next();
		} else {
			res.redirect('/');
		}
	}

	var graPhriend = new Graphriend();

	var user = {
		isLogged: false,
		name: 'Jinme',
		username: 'mirabalj'
	}

	app.route('/')
		.get(function (req, res) {
			res.render('main', { user: user, page: 'index' });
		});

/* Example Authenticated verify
	app.route('/profile')
		.get(isLoggedIn, function (req, res) {
			res.render('main', { user: sess.user, page: 'profile' });
		});
*/

/* Example REST api
	app.route('/friends')
		.get(isLoggedIn, graPhriend.getFriend)
		.post(isLoggedIn, graPhriend.addFriend)
		.delete(isLoggedIn, graPhriend.delFriend);
*/

};

'use strict';

var path = process.cwd();
var Graphriend = require(path + '/app/controllers/Graphriend.server.js');
var sess;

module.exports = function (app) {

	function isLoggedIn (req, res, next) {
		sess = req.session;
		if (sess.user) {
			return next();
		} else {
			res.redirect('/');
		}
	}

	var graPhriend = new Graphriend();

	var user = {
		isLogged: false,
		name: 'Jinme',
		username: 'mirabalj'
	}

	app.route('/')
		.get(function (req, res) {
			res.render('main', { user: user, page: 'index' });
		});

	app.post('/api/signup', graPhriend.signUp);

/* Example Authenticated verify
	app.route('/profile')
		.get(isLoggedIn, function (req, res) {
			res.render('main', { user: sess.user, page: 'profile' });
		});
*/

/* Example REST api
	app.route('/friends')
		.get(isLoggedIn, graPhriend.getFriend)
		.post(isLoggedIn, graPhriend.addFriend)
		.delete(isLoggedIn, graPhriend.delFriend);
*/

};

Add route to register users


import fs from 'fs'
import test from 'tape'
import sinon from 'sinon'
import index, {formats} from '../src/commit-msg'

test('index', t => {
  t.equal(typeof index, 'object', 'default export is an object')
  t.end()
})

test('index.check()', t => {
  const readFileSync = sinon.stub(fs, 'readFileSync')
  const base = sinon.stub(formats, 'base')

  index.check('base', './test/fixtures/COMMIT_EDITMSG')

  t.throws(() => { index.check('nein')}, 'calls an existing format')
  t.assert(readFileSync.calledOnce, 'calls fs.readFileSync')
  t.assert(base.calledOnce, 'calls formats.base')

  formats.base.restore()

  t.end()
})

test('formats', t => {
  t.equal(typeof formats, 'object', 'is an object')
  t.end()
})

test('formats.base()', t => {
  const under = '..........' // 10
  const equal = '..................................................' // 50
  const over = '...................................................' // 51

  t.equal(formats.base(under), true)
  t.equal(formats.base(equal), true)
  t.equal(formats.base(over), false)
  t.assert(typeof formats.base, 'is a function')
  t.end()
})

import fs from 'fs'
import test from 'tape'
import sinon from 'sinon'
import index, {formats} from '../src/commit-msg'

test('index', t => {
  t.equal(typeof index, 'object', 'default export is an object')
  t.end()
})

test('index.check()', t => {
  const readFileSync = sinon.stub(fs, 'readFileSync')
  const base = sinon.stub(formats, 'base')

  index.check('base', './test/fixtures/COMMIT_EDITMSG')

  t.throws(() => { index.check('nein')}, 'calls an existing format')
  t.assert(readFileSync.calledOnce, 'calls fs.readFileSync')
  t.assert(base.calledOnce, 'calls formats.base')

  formats.base.restore()

  t.end()
})

test('formats', t => {
  t.equal(typeof formats, 'object', 'is an object')
  t.end()
})

test('formats.base()', t => {
  const under = buildString(10)
  const equal = buildString(50)
  const over = buildString(51)

  t.equal(formats.base(under), true)
  t.equal(formats.base(equal), true)
  t.equal(formats.base(over), false)
  t.assert(typeof formats.base, 'is a function')
  t.end()
})

function buildString (length) {
  let string = ''

  for (let i = 0; i < length; i++) {
    string += '_'
  }

  return string
}

Use helper to create test strings


var esprima = require('esprima');

/**
 * Position in the source file.
 *
 * @see [SpiderMonkey Parser API]
 *   {@link https://developer.mozilla.org/en-US/docs/Mozilla/Projects/SpiderMonkey/Parser_API}
 *
 * @typedef {Object} Position
 * @property {number} line - Line number, 1-indexed.
 * @property {?number} column - Column number, 0-indexed.
 */

/**
 * Location - position range.
 *
 * @typedef {Object} Location
 * @property {Position} start
 * @property {Position} end
 */

/**
 * Extract comments from code.
 * Each comment is represented by a string within its usual comment delimiters.
 * Returned location always contains both line and column info.
 *
 * @arg {string} code - JavaScript code.
 * @return {{text: string, loc: Location}[]}
 */
var extract = function (code) {
  return esprima.parse(code, {
    comment: true,
    loc: true
  }).comments.map(function (comment) {
    return {
      text: (comment.type == 'Line')
        ? '//' + comment.value
        : '/*' + comment.value + '*/',
      loc: comment.loc
    };
  });
};

module.exports = extract;

var esprima = require('esprima');

/**
 * Position in the source file.
 *
 * @see [SpiderMonkey Parser API]
 *   {@link https://developer.mozilla.org/en-US/docs/Mozilla/Projects/SpiderMonkey/Parser_API}
 *
 * @typedef {Object} Position
 * @property {number} line - Line number, 1-indexed.
 * @property {?number} column - Column number, 0-indexed.
 */

/**
 * Location - position range.
 *
 * @typedef {Object} Location
 * @property {Position} start - Column number must be set.
 * @property {Position} end - Column number must be set.
 */

/**
 * Extract comments from code.
 * Each comment is represented by a string within its usual comment delimiters.
 *
 * @arg {string} code - JavaScript code.
 * @return {{text: string, loc: Location}[]}
 */
var extract = function (code) {
  return esprima.parse(code, {
    comment: true,
    loc: true
  }).comments.map(function (comment) {
    return {
      text: (comment.type == 'Line')
        ? '//' + comment.value
        : '/*' + comment.value + '*/',
      loc: comment.loc
    };
  });
};

module.exports = extract;

Make column numbers in Location mandatory


import moment from 'moment'
import React from 'react'
import PropTypes from 'prop-types'

function format (value) {
  if (value.isAfter(moment().startOf('year'))) {
    return value.format('MM/DD HH:mm')
  }

  return value.format('YYYY/MM/DD HH:mm')
}

function ReadableTime ({ value }) {
  const parsedValue = moment(value)

  if (!parsedValue.isValid()) {
    return null
  }

  return <time dateTime={value}>{format(parsedValue)}</time>
}

ReadableTime.propTypes = {
  value: PropTypes.string.isRequired
}

export default ReadableTime

import moment from 'moment'
import React from 'react'
import PropTypes from 'prop-types'

function fullFormat (value) {
  return value.format('YYYY/MM/DD HH:mm')
}

function format (value) {
  if (value.isAfter(moment().subtract(48, 'hours'))) {
    return value.fromNow()
  }

  if (value.isAfter(moment().startOf('year'))) {
    return value.format('MM/DD HH:mm')
  }

  return fullFormat(value)
}

function ReadableTime ({ value }) {
  const parsedValue = moment(value)

  if (!parsedValue.isValid()) {
    return null
  }

  return (
    <time dateTime={value} title={fullFormat(parsedValue)}>
      {format(parsedValue)}
    </time>
  )
}

ReadableTime.propTypes = {
  value: PropTypes.string.isRequired
}

export default ReadableTime

Add full date and time to the cell value titles


import React from 'react';
import moment from 'moment';
import { Link } from 'react-router-dom';

import { getEventDescriptions } from '../utilities/event';

const UpcomingEventHero = ({ event }) => {
  const { mainTitle, subTitle, byline, description } = getEventDescriptions(
    event
  );
  const date = moment(event.time);
  return (

        <Link to={`/events/${event.id}`}>
          {mainTitle}
          {subTitle &&

              :
              {' '}

                {subTitle}

            }
        </Link>

{byline}

{date.format('MMMM Do YYYY')}

  );
};

export default UpcomingEventHero;

Disable background image for now