import { greekLetters, randomFromList } from "../constants";

export default (_, { name, stations, displayName = name }) => {
  // Find the station with the probe construction
  const station = stations.find(s =>
    s.cards.find(c => c.component === "ComputerCore")
  );
  const stationName = station ? station.name : "Computer Core";
  return `A user should be created in the computer core to repair the ${displayName} system. Ask the ${stationName} create a user with the following username and password:

  Username: ${displayName} Repair
  Password: ${randomFromList(greekLetters) -
    Math.round(Math.random() * 10000)}-${randomFromList(greekLetters)}

  `;
};

import { greekLetters, randomFromList } from "../constants";

export default (_, { name, stations, displayName = name }) => {
  // Find the station with the probe construction
  const station = stations.find(s =>
    s.cards.find(c => c.component === "ComputerCore")
  );
  const stationName = station ? station.name : "Computer Core";
  return `A user should be created in the computer core to repair the ${displayName} system. Ask the ${stationName} create a user with the following username and password:

  Level: #[1-10]
  Username: ${displayName} Repair
  Password: ${randomFromList(greekLetters) -
    Math.round(Math.random() * 10000)}-${randomFromList(greekLetters)}

  `;
};

Add level to computer core damage report template


function validateCacheSize(cacheSize) {
  if (cacheSize === undefined) {
    throw new Error('Missing the required property `cacheSize`.');
  }
  if (!Number.isInteger(cacheSize) || cacheSize <= 0) {
    throw new Error(
      'The `cacheSize` property must be a positive integer value.'
    );
  }
}

export default validateCacheSize;

function validateCacheSize(cacheSize) {
  if (cacheSize === undefined) {
    throw new Error('Missing the required property "cacheSize".');
  }
  if (!Number.isInteger(cacheSize) || cacheSize <= 0) {
    throw new Error(
      'The "cacheSize" property must be a positive integer value.'
    );
  }
}

export default validateCacheSize;

Use double quotes in error messages


import { GoogleAnalyticsBackwardsCompability } from './src/GoogleAnalyticsBackwardsCompability';
import { GoogleAnalyticsTracker } from './src/GoogleAnalyticsTracker';
import { GoogleAnalyticsSettings } from './src/GoogleAnalyticsSettings';
import { GoogleTagManager } from './src/GoogleTagManager';
import { GoogleAnalyticsBridge } from './src/NativeBridges';

export {
  GoogleAnalyticsTracker,
  GoogleAnalyticsSettings,
  GoogleTagManager,
};

/**
 * Backwards compatibility default export.
 * Versions bellow 3.1.0 used static GoogleAnalytics class.
 * This exported instance makes sure older implementations work.
 */
export default new GoogleAnalyticsBackwardsCompability(GoogleAnalyticsBridge.nativeTrackerId);

import { GoogleAnalyticsBackwardsCompability } from './src/GoogleAnalyticsBackwardsCompability';
import { GoogleAnalyticsTracker } from './src/GoogleAnalyticsTracker';
import { GoogleAnalyticsSettings } from './src/GoogleAnalyticsSettings';
import { GoogleTagManager } from './src/GoogleTagManager';
import { GoogleAnalyticsBridge } from './src/NativeBridges';

export {
  GoogleAnalyticsTracker,
  GoogleAnalyticsSettings,
  GoogleTagManager,
};

/**
 * Backwards compatibility default export.
 * Versions bellow 3.1.0 used static GoogleAnalytics class.
 * This exported instance makes sure older implementations work.
 */
export default new GoogleAnalyticsBackwardsCompability(GoogleAnalyticsBridge ? GoogleAnalyticsBridge.nativeTrackerId : null);

Fix error if native bridge is not available


'use strict';

var request = require('superagent');
var glimpse = require('glimpse');
var util = require('../../lib/util');

var uriTemplate = require("uri-templates");

var _metadata = null;
var _callbacks = [];

function flushListeners(metadata){
    while (_callbacks.length > 0){
        var callback = _callbacks.shift();
        callback(metadata);
    }
};

function templatize(metadata){
    Object.keys(metadata.resources).forEach(function(key){
        metadata.resources[key] = uriTemplate(metadata.resources[key]);
    });

    return metadata;
}

module.exports = {
	triggerGetMetadata: function(){

        var metadataUri = util.getQueryStringParam('metadataUri');

        if (!metadataUri) {
            // TODO: Make this better w/ a custom dialog, caching & perhaps a list of recently used servers?
            metadataUri = prompt('What\'s the address to your Glimpse server metadata?', window.location.origin + '/glimpse/metadata');
        }

        request
            .get(metadataUri)
            .accept('application/vnd.glimpse.metadata+json,application/json')
            .end(function(err, res){
                if (err){
                    console.error('Glimpse metadata could not be obtained.');
                }
                else{
                    _metadata = templatize(res.body);
                    flushListeners(_metadata);
                }
            });
	},
    registerListener: function(callback){
        if(_metadata){
            callback(_metadata);
            return;
        }

        _callbacks.push(callback);
    }
};

'use strict';

var request = require('superagent');
var glimpse = require('glimpse');
var util = require('../../lib/util');

var uriTemplate = require("uri-templates");

var _metadata = null;
var _callbacks = [];

function flushListeners(metadata){
    while (_callbacks.length > 0){
        var callback = _callbacks.shift();
        callback(metadata);
    }
};

function templatize(metadata){
    Object.keys(metadata.resources).forEach(function(key){
        metadata.resources[key] = uriTemplate(metadata.resources[key]);
    });

    return metadata;
}

module.exports = {
	triggerGetMetadata: function(){

        var metadataUri = util.getQueryStringParam('metadataUri');

        if (!metadataUri) {
            if (!FAKE_SERVER) {
                // TODO: Make this better w/ a custom dialog, caching & perhaps a list of recently used servers?
                metadataUri = prompt('What\'s the address to your Glimpse server metadata?', window.location.origin + '/glimpse/metadata');
            }
            else {
                metadataUri = window.location.origin + '/glimpse/metadata';
            }
        }

        request
            .get(metadataUri)
            .accept('application/vnd.glimpse.metadata+json,application/json')
            .end(function(err, res){
                if (err){
                    console.error('Glimpse metadata could not be obtained.');
                }
                else{
                    _metadata = templatize(res.body);
                    flushListeners(_metadata);
                }
            });
	},
    registerListener: function(callback){
        if(_metadata){
            callback(_metadata);
            return;
        }

        _callbacks.push(callback);
    }
};

Make it so that metadata repository doesn't prompt with fake server


Support babel on browser process


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

var derequire = require('broccoli-derequire');

module.exports = {
  name: 'ember-derequire',

  included: function(app) {
    this._super.included.apply(this, arguments);
    this.options = app.options.derequire || {};

    var defaultOptions = {enabled: true};

    for (var option in defaultOptions) {
      if (!this.options.hasOwnProperty(option)) {
        this.options[option] = defaultOptions[option];
      }
    }
  },

  postprocessTree: function(type, tree) {
    if (type === 'all' && this.options && this.options.enabled) {
      tree = derequire(tree, this.options);
    }

    return tree;
  },

  contentFor:function (type) {
    if(type === 'app-boot'){
      return 'var define = define; var require = require;'
    }
  }
};

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

var derequire = require('broccoli-derequire');

module.exports = {
  name: 'ember-derequire',

  included: function(app) {
    this._super.included.apply(this, arguments);
    this.options = app.options.derequire || {};

    var defaultOptions = {enabled: this.app.env !== 'test'};

    for (var option in defaultOptions) {
      if (!this.options.hasOwnProperty(option)) {
        this.options[option] = defaultOptions[option];
      }
    }
  },

  postprocessTree: function(type, tree) {
    if (type === 'all' && this.isEnabled()) {
      tree = derequire(tree, this.options);
    }
    return tree;
  },

  contentFor:function (type) {
    if(type === 'app-boot' && this.isEnabled()){
      return 'var define = define; var require = require;'
    }
  },

  isEnabled:function() {
    return this.options && this.options.enabled;
  }
};

Disable derequire in test environments by default


var util = require('util')
var assert = require('assert')
var Transform = require('stream').Transform

var ChangesResponse = module.exports = function(options) {
  var self = this

  self._type = options.type || 'normal'
  self._heartbeat = options.heartbeat || 60000
  self._include_docs = options.include_docs || false

  assert(self._type === 'continuous', 'only continuous feed type is supported')

  if (self._heartbeat) {
    self._heartbeatInterval = setInterval(self._writeHeartbeat.bind(self), self._heartbeat)
    self.once('end', function() {
      clearInterval(self._heartbeatInterval)
    })
  }

  Transform.call(self, { objectMode: true })
}
util.inherits(ChangesResponse, Transform)

ChangesResponse.prototype._transform = function(chunk, encoding, cb) {
  if (!this._include_docs && chunk.doc) delete chunk.doc

  if (this._type === 'continuous')
    this.push(JSON.stringify(chunk) + '\n')

  cb()
}

ChangesResponse.prototype._writeHeartbeat = function() {
  this.push('\n')
}

var util = require('util')
var assert = require('assert')
var Transform = require('stream').Transform

var ChangesResponse = module.exports = function(options) {
  var self = this

  self._type = options.type || 'normal'
  self._heartbeat = options.heartbeat || 60000
  self._include_docs = options.include_docs || false

  assert(self._type === 'continuous', 'only continuous feed type is supported')

  if (self._heartbeat) {
    self._heartbeatInterval = setInterval(self._writeHeartbeat.bind(self), self._heartbeat)
    self.once('finish', function() {
      clearInterval(self._heartbeatInterval)
    })
  }

  Transform.call(self, { objectMode: true })
}
util.inherits(ChangesResponse, Transform)

ChangesResponse.prototype._transform = function(chunk, encoding, cb) {
  if (!this._include_docs && chunk.doc) delete chunk.doc

  if (this._type === 'continuous')
    this.push(JSON.stringify(chunk) + '\n')

  cb()
}

ChangesResponse.prototype._writeHeartbeat = function() {
  this.push('\n')
}

Clear the heartbeat interval once data is flushed


'use strict';
const gutil = require('gulp-util');
const through = require('through2');
const meta = require('jsforce-metadata-tools');

module.exports = function(options) {
  return through.obj(function(file, enc, callback) {
    let err;
    if (file.isNull()) {
      return callback(null, file);
    }
    if (file.isStream()) {
      err = new gutil.PluginError('gulp-jsforce-deploy', 'Stream input is not supported');
      return callback(err);
    }
    options.logger = gutil;
    meta.deployFromZipStream(file.contents, options)
    .then(function(res) {
      meta.reportDeployResult(res, gutil, options.verbose);
      if (!res.success) {
        return callback(new Error('Deploy Failed.'),file);
      }
      file.contents = new Buffer.from(JSON.stringify(res));
      callback(null, file);
    })
    .catch(function(err) {
      gutil.log('gulp-jsforce-deploy',err.message)
      callback(err);
    });
  });
};

'use strict';
const gutil = require('gulp-util');
const through = require('through2');
const meta = require('jsforce-metadata-tools');

const PLUGIN_NAME = 'gulp-jsforce-deploy';

module.exports = options => {
  return through.obj((file, enc, callback) => {
    let err;
    if (file.isNull()) {
      return callback(null, file);
    }
    if (file.isStream()) {
      return callback(new gutil.PluginError(PLUGIN_NAME, 'Stream input is not supported'));
    }
    options.logger = gutil;
    meta.deployFromZipStream(file.contents, options)
    .then(res => {
      meta.reportDeployResult(res, gutil, options.verbose);
      if (!res.success) {
        return callback(new gutil.PluginError(PLUGIN_NAME, 'Deploy failed'),file);
      }
      file.contents = new Buffer.from(JSON.stringify(res));
      callback(null, file);
    })
    .catch(err => {
      gutil.log(PLUGIN_NAME,err.message)
      callback(err);
    });
  });
};

Convert this class to ES6


export default {
  name: 'ArticleNav',
  configure (config) {
    config.addLabel('mode', 'Mode')
    config.addViewMode({
      name: 'open-manuscript',
      viewName: 'manuscript',
      commandGroup: 'switch-view',
      icon: 'fa-align-left',
      label: 'Manuscript'
    })

    config.addViewMode({
      name: 'open-metadata',
      viewName: 'metadata',
      commandGroup: 'switch-view',
      icon: 'fa-th-list',
      label: 'Metadata'
    })

    config.addViewMode({
      name: 'open-reader',
      viewName: 'reader',
      commandGroup: 'switch-view',
      icon: 'fa-th-list',
      label: 'Reader'
    })
  }
}

export default {
  name: 'ArticleNav',
  configure (config) {
    config.addLabel('mode', 'Mode')
    config.addViewMode({
      name: 'open-manuscript',
      viewName: 'manuscript',
      commandGroup: 'switch-view',
      icon: 'fa-align-left',
      label: 'Manuscript'
    })

    config.addViewMode({
      name: 'open-metadata',
      viewName: 'metadata',
      commandGroup: 'switch-view',
      icon: 'fa-th-list',
      label: 'Metadata'
    })

    // TODO: make reader great again
    /*
    config.addViewMode({
      name: 'open-reader',
      viewName: 'reader',
      commandGroup: 'switch-view',
      icon: 'fa-th-list',
      label: 'Reader'
    })
    */
  }
}

Hide a reader for now.


import EventEmitter from "events";
import PubNub from "pubnub";

export default class Subscriptions extends EventEmitter {
  constructor() {
    super();
    this.subscribers = {};
  }

  getOrAddSubscriber(subscribeKey) {
    if (!this.subscribers[subscribeKey]) {
      this.subscribers[subscribeKey] = new PubNub({
        subscribeKey
      });

      this.subscribers[subscribeKey].addListener({
        message: this.onMessage.bind(this)
      });
    }

    return this.subscribers[subscribeKey];
  }

  subscribe(subscription) {
    const { pubnub } = subscription;
    const subscriber = this.getOrAddSubscriber(pubnub.subscribe_key);
    subscriber.subscribe({
      channels: [pubnub.channel]
    });
  }

  unsubscribe(subscription) {
    const { pubnub } = subscription;
    const subscriber = this.getOrAddSubscriber(pubnub.subscribe_key);
    subscriber.unsubscribe({
      channels: [pubnub.channel]
    });
  }

  onMessage(message) {
    const msg = typeof message.message !== "string"
      ? message.message
      : JSON.parse(message.message);

    this.emit("message", msg);

    if (
      msg.uuid &&
      msg.name &&
      msg.object_type &&
      msg.object_id &&
      msg.last_reading
    ) {
      return this.emit("device-update", msg);
    }

    if (msg.data && Array.isArray(msg.data)) {
      return this.emit("device-list", msg);
    }

    this.emit("unknown-message", msg);
  }
}

import EventEmitter from "events";
import PubNub from "pubnub";

export default class Subscriptions extends EventEmitter {
  constructor() {
    super();
    this.subscribers = {};
  }

  getOrAddSubscriber(subscribeKey) {
    if (!this.subscribers[subscribeKey]) {
      this.subscribers[subscribeKey] = new PubNub({
        ssl:true,
        subscribeKey
      });

      this.subscribers[subscribeKey].addListener({
        message: this.onMessage.bind(this)
      });
    }

    return this.subscribers[subscribeKey];
  }

  subscribe(subscription) {
    const { pubnub } = subscription;
    const subscriber = this.getOrAddSubscriber(pubnub.subscribe_key);
    subscriber.subscribe({
      channels: [pubnub.channel]
    });
  }

  unsubscribe(subscription) {
    const { pubnub } = subscription;
    const subscriber = this.getOrAddSubscriber(pubnub.subscribe_key);
    subscriber.unsubscribe({
      channels: [pubnub.channel]
    });
  }

  onMessage(message) {
    const msg = typeof message.message !== "string"
      ? message.message
      : JSON.parse(message.message);

    this.emit("message", msg);

    if (
      msg.uuid &&
      msg.name &&
      msg.object_type &&
      msg.object_id &&
      msg.last_reading
    ) {
      return this.emit("device-update", msg);
    }

    if (msg.data && Array.isArray(msg.data)) {
      return this.emit("device-list", msg);
    }

    this.emit("unknown-message", msg);
  }
}

Enable SSL communication for PubNub


function Supercollider() {
  this.options = {};
  this.searchOptions = {};
  this.adapters = {};
  this.tree = [];
  this.template = null;
}

Supercollider.prototype.init = require('./lib/init');
Supercollider.prototype.parse = require('./lib/parse');
Supercollider.prototype.build = require('./lib/build');
Supercollider.prototype.adapter = require('./lib/adapter');
Supercollider.prototype.config = require('./lib/config');
Supercollider.prototype.buildSearch = require('./lib/buildSearch');
Supercollider.prototype.searchConfig = require('./lib/searchConfig');

module.exports = new Supercollider();
module.exports.Supercollider = Supercollider;

Add Supercollider class as a separate module export


/**
 * @class CM_FormField_Set_Select
 * @extends CM_FormField_Set
 */
var CM_FormField_Set_Select = CM_FormField_Set.extend({
	_class: 'CM_FormField_Set_Select'
});

/**
 * @class CM_FormField_Set_Select
 * @extends CM_FormField_Set
 */
var CM_FormField_Set_Select = CM_FormField_Set.extend({
	_class: 'CM_FormField_Set_Select',

	events: {
		'change select': function() {
			this.trigger('change');
		}
	}
});

Fix select to trigger change event


describe('My module', () => {
  beforeEach(() => {
    myModule = injectLoaderCompatibility(
      __dirname,
      '../src/my-module.js',
      {
        'events': eventsMock,
        '../lib/dispatcher': dispatcherMock,
        '../lib/handle-action': handleActionMock
      }
    );
  });
});

import injector from 'inject!../src/my-module.js';

describe('My module', () => {
  beforeEach(() => {
    myModule = injector({
      'events': eventsMock,
      '../lib/dispatcher': dispatcherMock,
      '../lib/handle-action': handleActionMock
    });
  });
});

Undo applying macro to example


import test from 'ava'
import any from '../../packages/any'

test('any() of an empty array returns false', t => {
  t.is(any(i => i, []), false)
})

test('any() of a list that fully matches the predicate function returns true', t => {
  t.is(any(i => i, [true, true]), true)
})

test('any() of a list that partially matches the predicate function returns true', t => {
  t.is(any(i => i, [true, false]), true)
})

test('any() of a list that never matches the predicate function returns false', t => {
  t.is(any(i => i, [false, false]), false)
})

import test from 'ava'
import any from '../../packages/any'

test('any() of an empty array returns false', t => {
  t.is(any(i => i, []), false)
})

test('any() of a list that fully matches the predicate returns true', t => {
  t.is(any(i => i, [true, true]), true)
})

test('any() of a list that partially matches the predicate returns true', t => {
  t.is(any(i => i, [true, false]), true)
})

test('any() of a list that never matches the predicate returns false', t => {
  t.is(any(i => i, [false, false]), false)
})

test('any() is curried', t => {
  t.is(any(i => i)([true, false]), true)
})

Test that any() is curried


import React from 'react';
import { CircularProgress } from 'material-ui';

const Loading = () => (
  <div style={{textAlign: 'center'}}>
    <CircularProgress mode='indeterminate' />
  </div>
);

export default Loading;

import React, { Component } from 'react';
import { Link } from 'react-router';
import { CircularProgress } from 'material-ui';

class Loading extends Component {
  state = { timedOut: false };

  componentDidMount() {
    setTimeout(() => this.handleLoadingTimeout(), 5000);
  }

  componentWillUnmount() {
    clearTimeout();
  }

  handleLoadingTimeout() {
    this.setState({ timedOut: true });
  }

  render() {
    return (
      this.state.timedOut ?
      (

Please check your Pivotal Project

It appears you do not have any current stories

        <Link to='/settings'>back to settings</Link>

) :
      (

        <CircularProgress mode='indeterminate' />

)

    );
  }
};

export default Loading;

Add timed out message to Loader


'use strict';

angular.module('<%= scriptAppName %>')
  .directive('<%= cameledName %>', function () {
    return {
      template: '

',
      restrict: 'EA',
      link: function (scope, element, attrs) {
        element.text('this is the <%= cameledName %> directive');
      }
    };
  });

'use strict';
(function(){

  var <%= cameledName %> = function(){
    return {
      template: '

',
      restrict: 'EA',
      link: link
    };

    //////////////

    function link(scope, element, attrs) {
      element.text('this is the <%= cameledName %> directive');
    }
  };
  angular
    .module('<%= scriptAppName %>')
    .directive('<%= cameledName %>', <%= cameledName %>);

}).call(this);

Refactor Javascript Directive Simple to match best practices


var React = require('react/addons');

var Notification = React.createClass({

  /**
   * @function hide
   * @description Focus on the element.
   */
  open: function () {
    this.setState({
      active: true
    });
  },

  /**
   * @function hide
   * @description Focus on the element.
   */
  hide: function () {
    this.setState({
      active: false
    });
  },

  propTypes: {
    message: React.PropTypes.string.isRequired,
    action: React.PropTypes.string,
    onClick: React.PropTypes.func
  },

  getInitialState: function () {
    return {
      active: false
    };
  },

  getDefaultProps: function () {
    return {
      action: 'Dismiss',
      onClick: function () {
        this.hide();
      }
    };
  },

  render: function () {
    return (

          {this.props.message}
          {this.props.action}

    );
  }

});

module.exports = Notification;

var React = require('react/addons');

var Notification = React.createClass({

  /**
   * @function hide
   * @description Focus on the element.
   */
  show: function () {
    this.setState({
      active: true
    });
  },

  /**
   * @function hide
   * @description Focus on the element.
   */
  hide: function () {
    this.setState({
      active: false
    });
  },

  propTypes: {
    message: React.PropTypes.string.isRequired,
    action: React.PropTypes.string,
    onClick: React.PropTypes.func
  },

  getInitialState: function () {
    return {
      active: false
    };
  },

  getDefaultProps: function () {
    return {
      action: 'Dismiss',
      onClick: function () {
        this.hide();
      }
    };
  },

  render: function () {
    return (

          {this.props.message}
          {this.props.action}

    );
  }

});

module.exports = Notification;

Rename "open" method to "show"


import chai from 'chai';
import Game from '../lib/game';
import $ from "jquery";
const expect = chai.expect;

describe('game', function () {
  after(function() {
    $('.game-table').remove();
  });

  context('start', function () {
    it('starts the game', function () {
      const game = new Game(2, 2);

      game.start();

      expect($('.game-table').length).to.equal(1);
      expect(game.clicks).to.equal(0);
      expect(game.interval > 0).to.equal(true);
    });

    it('starts click event handlers', function () {
      const game = new Game(2, 2);

      game.start();
      $('.game-table td').first().trigger('click');

      expect(game.clicks).to.equal(1);
    });
  });

  context('end', function () {
    it("resets the game's state", function () {
      const game = new Game(2, 2);
      game.start();
      $('.game-table td').first().trigger('click', 'td');

      game.end();

      expect($('.game-table').length).to.equal(0);
      expect(game.clicks).to.equal(0);
    });
  });
});

import chai from 'chai';
import Game from '../lib/game';
import $ from "jquery";
const expect = chai.expect;

describe('game', function () {
  after(function() {
    $('.game-table').remove();
  });

  context('start', function () {
    it('starts the game', function () {
      const game = new Game(2, 2);

      game.start();

      expect($('.game-table').length).to.equal(1);
      expect(game.clicks).to.equal(0);
      expect(game.interval > 0).to.equal(true);
    });

    it('starts click event handlers', function () {
      const game = new Game(2, 2);

      game.start();
      $('.game-table td').first().trigger('click');

      expect(game.clicks).to.equal(1);
    });
  });

  context('end', function () {
    it("resets the game's state", function () {
      const game = new Game(2, 2);
      game.start();
      $('.game-table td').first().trigger('click', 'td');

      game.end();

      expect($('.game-table').length).to.equal(0);
      expect(game.clicks).to.equal(0);
    });

    it('clears click event handlers', function () {
      const game = new Game(2, 2);
      expect(game.clicks).to.equal(0);

      game.start();
      $('.game-table td').first().trigger('click');

      expect(game.clicks).to.equal(1);

      game.end();
      $('.game-table td').first().trigger('click');

      expect(game.clicks).to.equal(0);
    });
  });
});

Add clear click events test


Template.postSubmit.events({
  'submit form': function(e) {
    e.preventDefault();

    var post = {
      url: $(e.target).find('[name=url]').val(),
      title: $(e.target).find('[name=title]').val()
    };

    post.slug = slugify(post.title);

    Meteor.call('postInsert', post, function(error, result) {
       // show this result but route anyway
      if (result.postExists)
        alert('This link has already been posted');
      Router.go('postPage', {_id: result._id});
    });
  }
});

Template.postSubmit.events({
  'submit form': function(e) {
    e.preventDefault();

    var post = {
      url: $(e.target).find('[name=url]').val(),
      title: $(e.target).find('[name=title]').val()
    };

    post.slug = slugify(post.title);

    Meteor.call('postInsert', post, function(error, result) {

       // show this result but route anyway
      if (result.postExists)
        alert('This link has already been posted');

      // Get new post to send user after submit (or error)
      var newPost = Posts.findOne({ _id: result._id });

      Router.go('postPage', { slug: newPost.slug });

    });
  }
});

Send to slug after looking up new post


const { compress } = require('csso').syntax;
const postcssToCsso = require('./lib/postcssToCsso.js');
const cssoToPostcss = require('./lib/cssoToPostcss.js');

module.exports = (options = {}) => ({
    postcssPlugin: 'postcss-csso',
    Once(root, { result, postcss }) {
        result.root = cssoToPostcss(compress(postcssToCsso(root), options).ast, postcss);
    }
});
module.exports.postcss = true;

const { compress } = require('csso').syntax;
const postcssToCsso = require('./lib/postcssToCsso.js');
const cssoToPostcss = require('./lib/cssoToPostcss.js');

module.exports = (options = {}) => ({
    postcssPlugin: 'postcss-csso',
    OnceExit(root, { result, postcss }) {
        result.root = cssoToPostcss(compress(postcssToCsso(root), options).ast, postcss);
    }
});
module.exports.postcss = true;

Replace `Once` with `OnceExit` to do the compression last


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

foam.CLASS({
  package: 'foam.nanos.ruler',
  name: 'RuleHistory',
  documentation: 'Represents rule execution history.',
  properties: [
    {
      class: 'Long',
      name: 'id',
      visibility: 'RO'
    },
    {
      class: 'DateTime',
      name: 'created',
      documentation: 'Creation date.'
    },
    {
      class: 'DateTime',
      name: 'lastModified',
      documentation: 'Last modified date.'
    },
    {
      class: 'Reference',
      of: 'foam.nanos.ruler.Rule',
      name: 'ruleId',
      documentation: 'The applied rule.'
    },
    {
      class: 'Object',
      name: 'ObjectId',
      visibility: 'RO',
      documentation: 'Id of the object onwhich rule is applied.'
    },
    {
      class: 'String',
      name: 'objectDaoKey',
      visibility: 'RO',
      documentation: 'DAO name of the object'
    },
    {
      class: 'Object',
      name: 'result',
      documentation: 'Result of rule execution.'
    }
  ]
});

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

foam.CLASS({
  package: 'foam.nanos.ruler',
  name: 'RuleHistory',
  documentation: 'Represents rule execution history.',
  properties: [
    {
      class: 'Long',
      name: 'id',
      visibility: 'RO'
    },
    {
      class: 'DateTime',
      name: 'created',
      documentation: 'Creation date.'
    },
    {
      class: 'DateTime',
      name: 'lastModified',
      documentation: 'Last modified date.'
    },
    {
      class: 'Reference',
      of: 'foam.nanos.ruler.Rule',
      name: 'ruleId',
      documentation: 'The applied rule.'
    },
    {
      class: 'Object',
      name: 'ObjectId',
      visibility: 'RO',
      documentation: 'Id of the object onwhich rule is applied.'
    },
    {
      class: 'String',
      name: 'objectDaoKey',
      visibility: 'RO',
      documentation: 'DAO name of the object'
    },
    {
      class: 'Object',
      name: 'result',
      documentation: 'Result of rule execution.',
      tableCellFormatter: function (value) {
        if (!!value) {
          this.add(value.toString());
        }
      }
    }
  ]
});

Format rule history result table cell


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

module.exports = {
  name: 'ember-cli-content-for-config'
};

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

function isObject(x) {
  return typeof x === 'object' && x !== null;
}

function getProp(obj, path) {
  if (!isObject(obj) || typeof path !== 'string') {
    return obj;
  }

  path = path.split('.');

  return getProp(obj[path.shift()], path.length && path.join('.'));
};

module.exports = {
  name: 'ember-cli-content-for-config',
  contentFor: function(type, config) {
    var path = type.split('.');

    if (path.shift() === 'config') {
      return getProp(config, path.join('.'));
    }
  }
};

Allow a user to get the content out of the config


var DeviceInformationLoader = function (require, exports, module) {
    var exec = require("cordova/exec");

    function DeviceInformation () {}

    DeviceInformation.prototype.get = function (successFunc, failFunc) {
        exec(successFunc, failFunc, "DeviceInformation","get",[]);
    };

    var deviceInformation = new DeviceInformation();
    module.exports = deviceInformation;
};

DeviceInformationLoader(require, exports, module);

cordova.define("cordova/plugin/DeviceInformation", DeviceInformationLoader);

var exec = require("cordova/exec");

var DeviceInformationLoader = function (require, exports, module) {

    function DeviceInformation () {}

    DeviceInformation.prototype.get = function (successFunc, failFunc) {
        exec(successFunc, failFunc, "DeviceInformation","get",[]);
    };

    var deviceInformation = new DeviceInformation();
    module.exports = deviceInformation;
};

DeviceInformationLoader(require, exports, module);

cordova.define("cordova/plugin/DeviceInformation", DeviceInformationLoader);

Move require outside of function call. Maybe this will help.


$(document).ready(function(){

  $('nav').children().eq(3).on('click', function(event){
    console.log("hi")
    $.ajax({

    })
  })

})

$(document).ready(function(){

  $('nav').children().eq(2).on('click', function(event){
    console.log("hi")
    $.ajax({

    })
  })

})

Fix jquery call to point to correct item/blog


process.stdin.resume();
process.stdin.setEncoding('utf8');

var input = '';

process.stdin.on('data', function(chunk) {
  input += chunk;
});

process.stdin.on('end', function() {

  var lines = input.trim().split('\n');

  if (lines.length > 0) {
    var max = lines[0];
    for (var i = 0; i < max; i++) {
      var n = i + 1;
      if ((n % 3 === 0) && (n % 5 === 0)) {
        console.log('Fizz Buzz');
      } else if (n % 3 === 0) {
        console.log('Fizz');
      } else if (n % 5 === 0) {
        console.log('Buzz');
      } else {
        console.log(n);
      }
    }
  }
});

process.stdin.resume();
process.stdin.setEncoding('utf8');

var input = '';

process.stdin.on('data', function(chunk) {
  input += chunk;
});

process.stdin.on('end', function() {

  var lines = input.trim().split('\n');

  if (lines.length > 0) {
    var max = parseInt(lines[0], 10);

    for (var i = 0; i < max; i++) {
      var n = i + 1;

      if ((n % 3 === 0) && (n % 5 === 0)) {
        console.log('Fizz Buzz');
      } else if (n % 3 === 0) {
        console.log('Fizz');
      } else if (n % 5 === 0) {
        console.log('Buzz');
      } else {
        console.log(n);
      }
    }
  }
});

Fix max number to use parseInt


import DS from 'ember-data';

export default DS.Transform.extend({
  deserialize: function(serialized) {
    var map = {
      0: 'New',
      4: 'Opened',
      8: 'Closed',
      10: 'Verified',
      20: 'Started',
      30: 'Finished',
    };
    return map[serialized];
  },

  serialize: function(deserialized) {
    var map = {
      'New': 0,
      'Opened': 4,
      'Closed': 8,
      'Verified': 10,
      'Started': 20,
      'Finished': 30,
    };
    return map[deserialized];
  }
});

import DS from 'ember-data';

export default DS.Transform.extend({
  deserialize: function(serialized) {
    var map = {
      0: 'New',
      2: 'Built',
      4: 'Opened',
      8: 'Closed',
      10: 'Verified',
      20: 'Started',
      30: 'Finished',
    };
    return map[serialized];
  },

  serialize: function(deserialized) {
    var map = {
      'New': 0,
      'Built': 2,
      'Opened': 4,
      'Closed': 8,
      'Verified': 10,
      'Started': 20,
      'Finished': 30,
    };
    return map[deserialized];
  }
});

Add built status to front-end transforms


// setup JSDOM
require('jsdom-global')()

// make document and global available globally
const jsdom = require('jsdom')
const { JSDOM } = jsdom
const { document } = (new JSDOM('')).window
global.document = document
global.window = global.document.defaultView

Object.keys(window).forEach((key) => {
  if (!(key in global)) {
    global[key] = window[key]
  }
})

// make common utils available globally as well
global.expect = require('expect')
global.sinon = require('sinon')
global._ = require('lodash')
const testUtils = require('vue-test-utils')
global.shallow = testUtils.shallow
global.mount = testUtils.mount

// setup JSDOM
require('jsdom-global')()

// make sure polyfill is loaded before generators
require('babel-polyfill')

// make document and global available globally
const jsdom = require('jsdom')
const { JSDOM } = jsdom
const { document } = (new JSDOM('')).window
global.document = document
global.window = global.document.defaultView

Object.keys(window).forEach((key) => {
  if (!(key in global)) {
    global[key] = window[key]
  }
})

// make common utils available globally as well
global.expect = require('expect')
global.sinon = require('sinon')
global._ = require('lodash')
const testUtils = require('vue-test-utils')
global.shallow = testUtils.shallow
global.mount = testUtils.mount

Make sure polyfill is loaded before generators


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

var jeetPath = path.join(__dirname, 'styl')

exports = module.exports = function (opts) {
  return function (style) {
    style.import(jeetPath);
  }
}

exports.libname = pkg.name;
exports.path = jeetPath;
exports.version = pkg.version;

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

var jeetPath = path.join(__dirname, 'styl')

exports = module.exports = function (opts) {
  var implicit = (opts && opts.implicit == false) ? false : true;

  return function (style) {
    style.include(__dirname);

    if (implicit) {
      style.import(jeetPath);
    }
  }
}

exports.libname = pkg.name;
exports.path = jeetPath;
exports.version = pkg.version;

Add option to implicitly import Jeet


var repeat = require('lodash.repeat');

module.exports = function(md, opts) {
  // default to being two spaces wide
  if (!opts || typeof opts.tabWidth === 'undefined') {
    tabWidth = 2;
  } else {
    tabWidth = Math.max(0, opts.tabWidth);
  }

  // patch the current rule, don't replace it completely
  var originalRule = md.renderer.rules.fence;
  md.renderer.rules.fence = function(tokens, idx, options, env, self) {
    tokens[idx].content = expandTabs(tokens[idx].content, tabWidth);
    return originalRule.call(this, tokens, idx, options, env, self);
  };

  // do the tab/space replacement
  function expandTabs(content, tabWidth) {
    var idx = 0;

    // while we're not at the end of the string:
    // - is the character at the current position a tab?
    // - yes, replace with spaces and move the current position forward
    // - no, jump to the character after the next newline
    while (idx > -1 && idx < content.length) {
      while (content[idx] === '\t') {
        content = content.substring(0, idx) + repeat(' ', tabWidth) + content.substring(idx+1);
        idx += tabWidth;
      }
      idx = content.indexOf('\n', idx) + 1;
    }

    return content;
  }

};

var repeat = require('lodash.repeat');

module.exports = function(md, opts) {
  // default to being two spaces wide
  var tabWidth;
  if (!opts || typeof opts.tabWidth === 'undefined') {
    tabWidth = 2;
  } else {
    tabWidth = Math.max(0, opts.tabWidth);
  }

  // patch the current rule, don't replace it completely
  var originalRule = md.renderer.rules.fence;
  md.renderer.rules.fence = function(tokens, idx, options, env, self) {
    tokens[idx].content = expandTabs(tokens[idx].content, tabWidth);
    return originalRule.call(this, tokens, idx, options, env, self);
  };

  // do the tab/space replacement
  function expandTabs(content, tabWidth) {
    var idx = 0;

    // while we're not at the end of the string:
    // - is the character at the current position a tab?
    // - yes, replace with spaces and move the current position forward
    // - no, jump to the character after the next newline
    while (idx > -1 && idx < content.length) {
      while (content[idx] === '\t') {
        content = content.substring(0, idx) + repeat(' ', tabWidth) + content.substring(idx+1);
        idx += tabWidth;
      }
      idx = content.indexOf('\n', idx) + 1;
    }

    return content;
  }

};

Declare tabWidth to stop it from leaking globally


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

module.exports = {
  name: 'roundcube-mail',
  isDevelopingAddon: function() {
    return true;
  },
  included: function (app) {
    this._super.included(app);

    app.import(app.bowerDirectory + '/dompurify/dist/purify.min.j');
  }
};

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

module.exports = {
  name: 'roundcube-mail',
  isDevelopingAddon: function() {
    return true;
  },
  included: function (app) {
    this._super.included(app);

    app.import(app.bowerDirectory + '/dompurify/dist/purify.min.js');
  }
};

Fix typo in bower import path; now correctly loads Dompurify into the source tree


'use strict';

const lost = require('lost');
const pxtorem = require('postcss-pxtorem');
const autoprefixer = require('autoprefixer');

module.exports = [
  lost(),
  pxtorem({
    rootValue: 16,
    unitPrecision: 5,
    propList: [
      'font',
      'font-size',
      'line-height',
      'letter-spacing',
      'margin',
      'margin-top',
      'margin-left',
      'margin-bottom',
      'margin-right',
      'padding',
      'padding-top',
      'padding-left',
      'padding-bottom',
      'padding-right',
      'border-radius',
      'width',
      'max-width'
    ],
    selectorBlackList: [],
    replace: true,
    mediaQuery: false,
    minPixelValue: 0
  }),
  autoprefixer()
];

'use strict';

const lost = require('lost');
const pxtorem = require('postcss-pxtorem');
const autoprefixer = require('autoprefixer');

module.exports = [
  lost(),
  pxtorem({
    rootValue: 16,
    unitPrecision: 5,
    propList: [
      'font',
      'font-size',
      'height',
      'line-height',
      'letter-spacing',
      'margin',
      'margin-top',
      'margin-left',
      'margin-bottom',
      'margin-right',
      'padding',
      'padding-top',
      'padding-left',
      'padding-bottom',
      'padding-right',
      'border-radius',
      'width',
      'max-width'
    ],
    selectorBlackList: [],
    replace: true,
    mediaQuery: false,
    minPixelValue: 0
  }),
  autoprefixer()
];

Include height in postcss pxtorem config


import Component from '@ember/component';
import {computed} from '@ember/object';
import {inject as service} from '@ember/service';

export default Component.extend({
    router: service(),
    config: service(),
    ghostPaths: service(),
    ajax: service(),
    billing: service(),

    subscription: null,

    showUpgradeButton: computed.equal('billing.subscription.status', 'trialing'),

    actions: {
        openBilling() {
            this.billing.openBillingWindow(this.router.currentURL, '/billing/plans');
        }
    }
});

import Component from '@ember/component';
import {computed} from '@ember/object';
import {inject as service} from '@ember/service';

export default Component.extend({
    router: service(),
    config: service(),
    ghostPaths: service(),
    ajax: service(),
    billing: service(),

    subscription: null,

    showUpgradeButton: computed.reads('billing.subscription.isActiveTrial'),

    actions: {
        openBilling() {
            this.billing.openBillingWindow(this.router.currentURL, '/billing/plans');
        }
    }
});

Use own isActiveTrial property for show upgrade CTA


Invitations = new Mongo.Collection("invitations");

InvitationsSchema = new SimpleSchema({
  emails: {
    type: [String],
    label: "E-mail address(es)",
    regex: SimpleSchema.RegEx.Email
  },
  file: {
    type: String,
    autoform: {
      afFieldInput: {
        type: "fileUpload",
        collection: "Files",
        label: "Choose a file"
      }
    }
  }
});

Invitations.attachSchema(InvitationsSchema);

Attach Invitations Schema to Invitations collection


'use strict';

angular.module('Teem')
  .directive('participate', function() {
    return {
      controller: [
      '$scope', '$element', '$attrs', 'SessionSvc', '$timeout', 'CommunitiesSvc',
      function($scope, $element, $attrs, SessionSvc, $timeout, CommunitiesSvc) {
        $scope.participateCopyOn  = $attrs.participateCopyOn;
        $scope.participateCopyOff = $attrs.participateCopyOff;

        $element.on('click', function($event) {
          SessionSvc.loginRequired($scope, function() {
            if ($scope.community.toggleParticipant) {
              $timeout(function() {
                $scope.community.toggleParticipant();
              });
            } else {
              CommunitiesSvc.find($scope.community.id).then(function(community) {
                $timeout(function() {
                  community.toggleParticipant();
                });
              });
            }
          });
          $event.stopPropagation();
        });
      }],
      templateUrl: 'participate.html'
    };
  });

'use strict';

angular.module('Teem')
  .directive('participate', function() {
    return {
      controller: [
      '$scope', '$element', '$attrs', 'SessionSvc', '$timeout', 'CommunitiesSvc',
      function($scope, $element, $attrs, SessionSvc, $timeout, CommunitiesSvc) {
        $scope.participateCopyOn  = $attrs.participateCopyOn;
        $scope.participateCopyOff = $attrs.participateCopyOff;

        $element.on('click', function($event) {
          SessionSvc.loginRequired($scope, function() {
            if ($scope.community.toggleParticipant) {
              $timeout(function() {
                $scope.community.toggleParticipant();
              });
            } else {
              CommunitiesSvc.find($scope.community.id).then(function(community) {
                $timeout(function() {
                  community.toggleParticipant();
                });
              });
            }
          });
        });
      }],
      templateUrl: 'participate.html'
    };
  });

Stop propatation here not longer needed


var element = {
    form: '#openAccForm',
    firstNameInput: '#fname',
    familyNameInput: '#lname',
    emailInput: '#Email',
    dateofBirthInputs: '???',
    firstLineAddressInput: '#Address1',
    townCityInput: '#AddressTown',
    telephoneInput: '#Tel',
    passwordInput: '#chooseapassword',
    secretAnswerInput: '#secretanswer',
    agreeToTermsCheckbox: '#tnc',
    openAccountButton: '#submit'
};

module.exports = {

    "createAccountRealMoneyFail": function (browser) {

        browser
            .url(browser.globals.url + '/c/linkto_acopening.cgi?actype=real')
            .waitForElementVisible(element.form, 5000)
            .click(element.openAccountButton)
            .pause(5000)
            .assert.containsText('body', 'enter your first name')
            .assert.containsText('body', 'use only letters')
            .assert.containsText('body', 'enter your email address')
            .assert.containsText('body', 'input a valid date')
            .assert.containsText('body', 'enter the first line of your home address')
            .assert.containsText('body', 'enter a town or city')
            .assert.containsText('body', 'nvalid telephone number')
            .assert.containsText('body', 'enter a password')
            .assert.containsText('body', 'secret answer is too short')
        .end();
    }
};

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

var element = {
    form: '#openAccForm',
    openAccountButton: '#submit'
},
    errorList = [
        'enter your first name',
        'use only letters',
        'enter your email address',
        'input a valid date',
        'enter the first line of your home address',
        'enter a town or city',
        'nvalid telephone number',
        'enter a password',
        'secret answer is too short',
        'must accept the terms and conditions'
    ];

module.exports.init = function (browser) {
    browser
        .url(browser.globals.url + '/c/linkto_acopening.cgi?actype=real')
        .waitForElementVisible(element.form, 5000)
        .click(element.openAccountButton)
        .pause(5000);
};

errorList.forEach(function (str) {
    module.exports[str] = function (browser) {
        browser.assert.containsText('body', str, "Checking for " + str);
    };
});

module.exports.end = function (browser) {
    browser.end();
};

Refactor CreateAccount test with configurable multi asserts


import {
  REMOVE_MEDIA_TO_TWEET,
  UPLOADED_MEDIA_TO_TWEET
} from '../constants/ActionTypes';

const initialState = {
  medias: [],
};

export default function mediaToTweet(state = initialState, action) {
  switch (action.type) {
    case REMOVE_MEDIA_TO_TWEET: {
      return { medias: state.medias.filter(m => m.id !== action.mediaId) }
    }

    case UPLOADED_MEDIA_TO_TWEET: {
      return { medias: [...state.medias, action.media] };
    }

    default:
      return state;
  }
}

import {
  POSTED_TWEET,
  REMOVE_MEDIA_TO_TWEET,
  UPLOADED_MEDIA_TO_TWEET,
} from '../constants/ActionTypes';

const initialState = {
  medias: [],
};

export default function mediaToTweet(state = initialState, action) {
  switch (action.type) {
    case POSTED_TWEET: {
      return initialState;
    }

    case REMOVE_MEDIA_TO_TWEET: {
      return { medias: state.medias.filter(m => m.id !== action.mediaId) }
    }

    case UPLOADED_MEDIA_TO_TWEET: {
      return { medias: [...state.medias, action.media] };
    }

    default:
      return state;
  }
}

Remove medias from NewTweetView if tweet is posted successfully


const Kaba = require("kaba");

module.exports = (new Kaba())
    .addJavaScriptEntries({
        "all-tests": "tests/build/all-tests.js",
    })
    .setOutputPath("tests/dist")
    .disableChunkSplitting()
    .disableFileNameHashing()
;

const Kaba = require("kaba");

module.exports = (new Kaba())
    .addJavaScriptEntries({
        "all-tests": "tests/build/all-tests.js",
    })
    .setOutputPath("tests/dist")
    .disableChunkSplitting()
    .disableFileNameHashing()
    .disableModuleConcatenation()
;

Disable module concatenation in the build file


const express = require('express');
const router = express.Router();

const db = require('../queries');

router.get('/api/todos', (req, res, next) => {
  db.getAllTodos()
    .then(todos => {
      res.render('index', { todos })
    })
    .catch(err => next(err))
});

router.post('/api/todos', (req, res, next) => {
  db.createTodo(req.body)
    .then(() => {
      console.log('success')
      res.redirect('/api/todos')
    })
    .catch(err => next(err))
  console.log(req.body)
});

router.post('/api/todos/:id', (req, res, next) => {
  db.updateTodo(req.params.id, req.body)
    .then(() => {
      res.redirect('/api/todos')
    })
    .catch(err => next(err))
});

router.post('/api/todos/remove/:id', (req, res, next) => {
  db.deleteTodo(req.params.id)
    .then(() => {
      res.redirect('/api/todos')
    })
    .catch(err => next(err))
});

module.exports = router;

const express = require('express');
const router = express.Router();

const db = require('../queries');

router.get('/api/todos', (req, res, next) => {
  db.getAllTodos()
    .then(todos => {
      res.render('index', { todos })
    })
    .catch(err => next(err))
});

router.get('/api/todos/:id', (req, res, next) => {
  db.getOneTodo(req.params.id)
    .then((todo) => {
      res.send(todo)
    })
    .catch(err => next(err))
})

router.post('/api/todos', (req, res, next) => {
  db.createTodo(req.body)
    .then((todo) => {
      res.send(JSON.stringify(todo))
    })
    .catch(err => next(err))
});

router.put('/api/todos/:id', (req, res, next) => {
  const id = req.params.id
  db.updateTodo(id, req.body)
    .then(() => {
      db.getOneTodo(id)
        .then(todo => res.send(JSON.stringify(todo)))
        .catch(err => next(err))
    })
    .catch(err => next(err))
});

router.delete('/api/todos/remove/:id', (req, res, next) => {
  const id = req.params.id
  db.deleteTodo(id)
    .then((response) => {
      res.statusCode = 200
      res.send(`Deleted ${response.rowCount} todo`)
    })
    .catch(err => next(err))
});

module.exports = router;

Change routes to send back objects to use with ajax


window.ga = window.ga || function(...args) { (ga.q = ga.q || []).push(args); }; ga.l = Number(new Date());

ga('create', $DIM_VERSION === 'release' ? 'UA-60316581-1' : 'UA-60316581-5', 'auto');
ga('set', 'dimension1', $DIM_VERSION);
ga('set', 'dimension2', $DIM_FLAVOR);

// If we're hooked into the router, let it send pageviews instead
if (!$featureFlags.googleAnalyticsForRouter) {
  ga('send', 'pageview');
}

window.ga = window.ga || function(...args) { (ga.q = ga.q || []).push(args); }; ga.l = Number(new Date());

ga('create', $DIM_FLAVOR === 'release' ? 'UA-60316581-1' : 'UA-60316581-5', 'auto');
ga('set', 'dimension1', $DIM_VERSION);
ga('set', 'dimension2', $DIM_FLAVOR);

// If we're hooked into the router, let it send pageviews instead
if (!$featureFlags.googleAnalyticsForRouter) {
  ga('send', 'pageview');
}

Use DIM_FLAVOR, not DIM_VERSION, to set GA


var Msg = require("../../models/msg");

module.exports = function(irc, network) {
	var client = this;
	irc.on("notice", function(data) {
		var lobby = network.channels[0];
		var from = data.from || "";
		if (data.to == "*" || data.from.indexOf(".") !== -1) {
			from = "";
		}
		var msg = new Msg({
			type: Msg.Type.NOTICE,
			from: from,
			text: data.message
		});
		lobby.messages.push(msg);
		client.emit("msg", {
			chan: lobby.id,
			msg: msg
		});
	});
};

var _ = require("lodash");
var Msg = require("../../models/msg");

module.exports = function(irc, network) {
	var client = this;
	irc.on("notice", function(data) {
		var target = data.to;
		if (target.toLowerCase() == irc.me.toLowerCase()) {
			target = data.from;
		}

		var chan = _.findWhere(network.channels, {name: target});
		if (typeof chan === "undefined") {
			chan = network.channels[0];
		}

		var from = data.from || "";
		if (data.to == "*" || data.from.indexOf(".") !== -1) {
			from = "";
		}
		var msg = new Msg({
			type: Msg.Type.NOTICE,
			from: from,
			text: data.message
		});
		chan.messages.push(msg);
		client.emit("msg", {
			chan: chan.id,
			msg: msg
		});
	});
};

Send NOTICE messages to the correct channel


var github = (function(){
  function escapeHtml(str) {
    return $('
').text(str).html();
  }
  function render(target, repos){
    var i = 0, fragment = '', t = $(target)[0];

    for(i = 0; i < repos.length; i++) {
      fragment += '
'+repos[i].name+'
'+escapeHtml(repos[i].description||'')+'

';
    }
    t.innerHTML = fragment;
  }
  return {
    showRepos: function(options){
      $.ajax({
          url: "https://api.github.com/users/"+options.user+"/repos?callback=?"
        , type: 'jsonp'
        , error: function (err) { $(options.target + ' li.loading').addClass('error').text("Error loading feed"); }
        , success: function(data) {
          var repos = [];
          if (!data || !data.data) { return; }
          for (var i = 0; i < data.data.length; i++) {
            if (options.skip_forks && data.data[i].fork) { continue; }
            repos.push(data.data[i]);
          }
          repos.sort(function(a, b) {
            var aDate = new Date(a.pushed_at).valueOf(),
                bDate = new Date(b.pushed_at).valueOf();

            if (aDate === bDate) { return 0; }
            return aDate > bDate ? -1 : 1;
          });

          if (options.count) { repos.splice(options.count); }
          render(options.target, repos);
        }
      });
    }
  };
})();

var github = (function(){
  function escapeHtml(str) {
    return $('
').text(str).html();
  }
  function render(target, repos){
    var i = 0, fragment = '', t = $(target)[0];

    for(i = 0; i < repos.length; i++) {
      fragment += '
'+repos[i].name+'
'+escapeHtml(repos[i].description||'')+'

';
    }
    t.innerHTML = fragment;
  }
  return {
    showRepos: function(options){
      $.ajax({
          url: "https://api.github.com/users/"+options.user+"/repos?sort=pushed;callback=?"
        , type: 'jsonp'
        , error: function (err) { $(options.target + ' li.loading').addClass('error').text("Error loading feed"); }
        , success: function(data) {
          var repos = [];
          if (!data || !data.data) { return; }
          for (var i = 0; i < data.data.length; i++) {
            if (options.skip_forks && data.data[i].fork) { continue; }
            repos.push(data.data[i]);
          }
          if (options.count) { repos.splice(options.count); }
          render(options.target, repos);
        }
      });
    }
  };
})();

Use the Github API repo sorting.


import React, { PropTypes } from 'react';
import VarOneStore from '../../stores/varOne-store';
import connectToStores from 'alt/utils/connectToStores';

@connectToStores
export default class VarOneLogModal extends React.Component {

  static propTypes = {
    msg: PropTypes.string,
    port: PropTypes.string,
    inputPort: PropTypes.string,
    failMessage: PropTypes.string
  }

  static getStores() {
    return [ VarOneStore ];
  }

  static getPropsFromStores() {
    return VarOneStore.getState();
  }

  render() {
    const failResultMessage = this.props.failMessage;
    return (

              <button type='button' className='close' data-dismiss='modal'>×</button>

varOne Log Message

              <textarea ref='textFailMessage' rows='10' cols='90' value={ failResultMessage }>
              </textarea>

    );
  }
}

import React, { PropTypes } from 'react';
import VarOneStore from '../../stores/varOne-store';
import connectToStores from 'alt/utils/connectToStores';

@connectToStores
export default class VarOneLogModal extends React.Component {

  static propTypes = {
    msg: PropTypes.string,
    port: PropTypes.string,
    inputPort: PropTypes.string,
    failMessage: PropTypes.string
  }

  static getStores() {
    return [ VarOneStore ];
  }

  static getPropsFromStores() {
    return VarOneStore.getState();
  }

  render() {
    const failResultMessage = this.props.failMessage;
    return (

              <button type='button' className='close' data-dismiss='modal'>×</button>

varOne Error Message

              <textarea ref='textFailMessage'
                rows='35' cols='90' value={ failResultMessage }>
              </textarea>

    );
  }
}

Modify error message log UI


(function() {
    'use strict';

    angular
        .module('yamcs.intf', []);
})();

(function() {
    'use strict';

    angular
        .module('yamcs.intf', [])
        .run(function (alarmsService, eventsService) {
            // Eagerly loading these injected services, to get the subscriptions running
            // Perhaps should research whether we should use angular providers instead.
        });
})();

Make sure subscriptions from top bar are running


"use strict";

angular.module(
  'arethusa', [
    'mm.foundation',
    'ngRoute',
    'arethusa.core',
    'arethusa.morph',
    'arethusa.hist'
  ],
  function($routeProvider, MAIN_ROUTE) {
    $routeProvider.when('/', MAIN_ROUTE);
  }
);

"use strict";

angular.module(
  'arethusa', [
    'mm.foundation',
    'ngRoute',
    'arethusa.core',
    'arethusa.morph',
    'arethusa.hist'
  ],
  function($routeProvider, MAIN_ROUTE) {
    $routeProvider.when('/', MAIN_ROUTE);
    $routeProvider.when('/:conf', MAIN_ROUTE);
  }
);

Allow to set the conf param through route matching


/* eslint no-console: 0 */
'use strict';

const phantom = require('phantom');

function* blockResourceLoading(page) {
    yield page.property('onResourceRequested', function(requestData, request) {
        var BLOCKED_RESOURCES = [
            /\.gif/gi,
            /\.png/gi,
            /\.css/gi,
            /^((?!(feuseradmin\.js|tinymce|jquery-)).)*\.js.*/gi
        ];
        var is_blacklisted_resource = BLOCKED_RESOURCES.some(function(r) {
            return r.test(requestData['url']);
        });

        if (is_blacklisted_resource) {
            request.abort();
        }
    });
}

function* fetchPage(url, instance) {
    let is_local_instance = false;
    if (!instance) {
        instance = yield * initPhantomInstance();
        is_local_instance = true;
    }

    const page = yield instance.createPage();
    yield * blockResourceLoading(page);

    yield page.open(url);
    let html = yield page.property('content');
    yield page.close();
    if (is_local_instance) {
        instance.exit();
    }
    return html;
}

function* initPhantomInstance() {
    return yield phantom.create();
}

module.exports = {
    blockResourceLoading: blockResourceLoading,
    fetchPage: fetchPage,
    initPhantomInstance: initPhantomInstance
};

/* eslint no-console: 0 */
'use strict';

const phantom = require('phantom');

function* blockResourceLoading(page) {
    yield page.property('onResourceRequested', function(requestData, request) {
        var BLOCKED_RESOURCES = [
            /\.gif/gi,
            /\.png/gi,
            /\.css/gi,
            /^((?!(feuseradmin\.js|tinymce|jquery-)).)*\.js.*/gi
        ];
        var is_blacklisted_resource = BLOCKED_RESOURCES.some(function(r) {
            return r.test(requestData['url']);
        });

        if (is_blacklisted_resource) {
            request.abort();
        }
    });
}

function* fetchPageContent(url, instance) {
    let is_local_instance = false;
    if (!instance) {
        instance = yield * initPhantomInstance();
        is_local_instance = true;
    }

    const page = yield instance.createPage();
    yield * blockResourceLoading(page);

    yield page.open(url);
    let html = yield page.property('content');
    yield page.close();
    if (is_local_instance) {
        instance.exit();
    }
    return html;
}

function* initPhantomInstance() {
    return yield phantom.create();
}

module.exports = {
    blockResourceLoading: blockResourceLoading,
    fetchPageContent: fetchPageContent,
    initPhantomInstance: initPhantomInstance
};

Rename fetchPage to fetchPageContent, since it returns the content of the page.


var app = require('app');
var chokidar = require('chokidar');
var extend = require('util')._extend;
var fs = require('fs');

var bootstrap = function(glob, options) {
  options = options || {};
  var browserWindows = [];
  var opts = extend({ignored: /node_modules|[\/\\]\./}, options);
  var watcher = chokidar.watch(glob, opts);

  var onChange = function() {
    browserWindows.forEach(function(bw) {
      bw.webContents.reloadIgnoringCache();
    });
  };

  app.on('browser-window-created', function(e, bw) {
    browserWindows.push(bw);
    var i = browserWindows.indexOf(bw);

    bw.on('closed', function() {
      browserWindows.splice(i, 1);
    });
  })

  // Preparing hard reset
  var eXecutable = options.electron;
  if(eXecutable && fs.existsSync(eXecutable)) {
    var proc = require('child_process');
    var path = require('path');

    var appPath = app.getAppPath();

    var config = require(path.join(appPath, 'package.json'));
    var mainFile = path.join(appPath, config.main);

    chokidar.watch(mainFile).on('change', function() {
      proc.spawn(eXecutable, [appPath]);
      // Kamikaze!
      app.quit();
    });
  } else {
    console.log('Electron could not be found. No hard resets for you!');
  }

  watcher.on('change', onChange);
};

module.exports = bootstrap;

const app = require('electron').app;
const chokidar = require('chokidar');
const extend = require('util')._extend;
const fs = require('fs');

var bootstrap = function(glob, options) {
  options = options || {};
  var browserWindows = [];
  var opts = extend({ignored: /node_modules|[\/\\]\./}, options);
  var watcher = chokidar.watch(glob, opts);

  var onChange = function() {
    browserWindows.forEach(function(bw) {
      bw.webContents.reloadIgnoringCache();
    });
  };

  app.on('browser-window-created', function(e, bw) {
    browserWindows.push(bw);
    var i = browserWindows.indexOf(bw);

    bw.on('closed', function() {
      browserWindows.splice(i, 1);
    });
  })

  // Preparing hard reset
  var eXecutable = options.electron;
  if(eXecutable && fs.existsSync(eXecutable)) {
    var proc = require('child_process');
    var path = require('path');

    var appPath = app.getAppPath();

    var config = require(path.join(appPath, 'package.json'));
    var mainFile = path.join(appPath, config.main);

    chokidar.watch(mainFile).on('change', function() {
      proc.spawn(eXecutable, [appPath]);
      // Kamikaze!
      app.quit();
    });
  } else {
    console.log('Electron could not be found. No hard resets for you!');
  }

  watcher.on('change', onChange);
};

module.exports = bootstrap;

Migrate to the latest way electron exposes app module


#!/usr/bin/env node

/*

    ___ usage ___ en_US ___
    usage: dots git issue create [options] [subject]

    desciption:

    Create a GitHub issue from the command line using the given subject. If the
    creation is successful, the new issue number is printed to standard out.

    ___ . ___
*/
// TODO Really should be `dots node package --travis` or something.
require('arguable')(module, require('cadence')(function (async, program) {
    var fs = require('fs')
    var ok = require('assert').ok
    var url = require('url')
    async(function () {
        fs.readFile('package.json', 'utf8', async())
    }, function (body) {
        var json = JSON.parse(body)
        var repository = json.repository
        ok(repository, 'repository defined')
        ok(repository.type == 'git', 'repository is git')
        var loc = url.parse(repository.url)
        ok(loc.host == 'github.com', 'repository is GitHub')
        var ident = loc.path.replace(/\..*?$/, '').split('/').slice(1)
        console.log('https://travis-ci.org/' + ident.join('/'))
    })
}))

#!/usr/bin/env node

/*

    ___ usage ___ en_US ___
    usage: dots git issue create [options] [subject]

    desciption:

    Create a GitHub issue from the command line using the given subject. If the
    creation is successful, the new issue number is printed to standard out.

    ___ . ___
*/
// TODO Really should be `dots node package --travis` or something.
require('arguable')(module, require('cadence')(function (async, program) {
    var fs = require('fs')
    var ok = require('assert').ok
    var url = require('url')
    async(function () {
        fs.readFile('package.json', 'utf8', async())
    }, function (body) {
        var json = JSON.parse(body)
        var repository = json.repository
        ok(repository, 'repository defined')
        ok(repository.type == 'git', 'repository is git')
        console.log('https://travis-ci.org/' + repository.url)
    })
}))

Use shorthand url to open Travis.


import get from 'lodash/get';

const isEmptyMetadata = (metadata) => {
  const keys = [
    'description',
    'citation',
    'source',
    'applicationProperties.cautions',
    'applicationProperties.function',
    'applicationProperties.resolution',
    'applicationProperties.geographic_coverage',
    'applicationProperties.frequency_of_updates',
    'applicationProperties.date_of_content',
    'applicationProperties.download_data',
    'applicationProperties.amazon_link',
    'applicationProperties.map_service',
    'applicationProperties.agol_link',
  ];

  return keys.every((key) => {
    const value = get(metadata, key);
    return value === '' || value === undefined || value === null;
  });
};

export { isEmptyMetadata };

import get from 'lodash/get';

const isEmptyMetadata = (metadata) => {
  const keys = [
    'description',
    'citation',
    'source',
    'applicationProperties.cautions',
    'applicationProperties.function',
    'applicationProperties.resolution',
    'applicationProperties.geographic_coverage',
    'applicationProperties.frequency_of_updates',
    'applicationProperties.date_of_content',
    'applicationProperties.download_data',
    'applicationProperties.amazon_link',
    'applicationProperties.map_service',
    'applicationProperties.agol_link',
  ];

  return keys.every((key) => {
    const value = get(metadata.attributes, key);
    return value === '' || value === undefined || value === null;
  });
};

export { isEmptyMetadata };

Fix a bug where the wrong metadata would be used


function resizeWhich(img) {
    img.removeAttr('style');

    if (window.innerWidth / img.width() < window.innerHeight / img.height()) {
        return 'width';
        console.log('Set width to 100%')
    } else {
        return 'height';
        console.log('Set height to 100%')
    }
}

$(document).ready(function() {
    $('.gallery__item').css('cursor', 'pointer');
    $('.gallery__item').click(function() {
        var thumb = $(this).find('img');
        var fullImg = $('<img />').attr('src', thumb.data('full-size'));

        $.featherlight(fullImg, {
            onResize: function() {
                var img = $('.featherlight-inner').removeClass('gallery__img');

                var prop = resizeWhich(img);
                var winHeight = window.innerHeight;
                var winWidth = window.innerWidth;
                var parent = img.parent();
                var padding = parseInt(parent.css('padding-top'));

                if (prop === 'height') {
                    var newHeight = (winHeight * .95) - 2 * padding - 1;
                    img.css('max-height', newHeight + 'px');
                } else {
                    var newWidth = (winWidth * .95) - 2 * padding - 1;
                    img.css('max-width', newWidth + 'px');
                }
            }
        });
    });
});

function calcMaxHeight(parent) {
    var winHeight = window.innerHeight;
    var padding = parseInt(parent.css('padding-top')) + parseInt(parent.css('margin-top'));

    return (winHeight * .95) - (2 * padding) - 1;
}

function calcMaxWidth(parent) {
    var winWidth = window.innerWidth;
    var padding = parseInt(parent.css('padding-left')) + parseInt(parent.css('margin-left'));

    return (winWidth * .95) - (2 * padding) - 1;
}

function resizeImg(img) {
    img.removeAttr('style');

    var parent = img.parent();
    var maxHeight = calcMaxHeight(parent);
    var maxWidth = calcMaxWidth(parent);

    if (maxWidth / img.width() < maxHeight / img.height()) {
        img.css('max-width', maxWidth + 'px');
    } else {
        img.css('max-height', maxHeight + 'px');
    }
}

$(document).ready(function() {
    $('.gallery__item').css('cursor', 'pointer');
    $('.gallery__item').click(function() {
        var thumb = $(this).find('img');
        var fullImg = $('<img />').attr('src', thumb.data('full-size'));

        $.featherlight(fullImg, {
            beforeOpen: function() {
                $('.featherlight-inner').removeClass('gallery__img');
            },
            onResize: function() {
                var img = $('.featherlight-inner');
                resizeImg(img);
            }
        });
    });
});

Fix issue with lightbox resizing.


const eol = require('eol');
const commentsUtil = require('../utils/comments');

module.exports = function(params) {
    params = params || {};
    const regex = commentsUtil.getRegex(params.customTags);
    const commentsRegex = new RegExp('^\\s*#' + regex + '$', 'mig');

    return function parse(contents, file) {
        const comments = [];

        eol.split(contents).forEach(function(line, index) {
            const match = commentsRegex.exec(line);
            const comment = commentsUtil.prepareComment(match, index + 1, file);
            if (!comment) {
                return;
            }
            comments.push(comment);
        });
        return comments;
    };
};

const eol = require('eol');
const commentsUtil = require('../utils/comments');

module.exports = function({ customTags } = {}) {
    const regex = commentsUtil.getRegex(customTags);
    const commentsRegex = new RegExp('^\\s*#' + regex + '$', 'mig');

    return function parse(contents, file) {
        const comments = [];

        eol.split(contents).forEach(function(line, index) {
            const match = commentsRegex.exec(line);
            const comment = commentsUtil.prepareComment(match, index + 1, file);
            if (!comment) {
                return;
            }
            comments.push(comment);
        });
        return comments;
    };
};

Use defaults in coffe parser


const indentRegex = /^\s+/

const getIndent = (plain, cursorPos) => {
  const startSlice = plain.slice(0, cursorPos)
  const lastNewline = startSlice.lastIndexOf('\n') + 1
  const lineSlice = startSlice.slice(lastNewline)
  const indentation = lineSlice.match(indentRegex)[0]

  return indentation || ''
}

export default getIndent

const indentRegex = /^\s+/

const getIndent = (plain, cursorPos) => {
  const startSlice = plain.slice(0, cursorPos)
  const lastNewline = startSlice.lastIndexOf('\n') + 1
  const lineSlice = startSlice.slice(lastNewline)
  const matches = lineSlice.match(indentRegex)
  if (matches === null) {
    return ''
  }

  return matches[0] || ''
}

export default getIndent

Fix newline throw for empty indentation


import React, { Component, PropTypes } from 'react';
import { connect } from 'react-redux';
import { loadConversation } from 'redux/modules/conversationModule';
// COMPONENTS
import CommentsList from './CommentsList';
import { LoadingScreen } from 'components';

const mappedState = ({ conversation }) => ({
  conversationId: conversation.id,
  comments: conversation.comments,
  loadingConversation: conversation.loadingConversation,
  conversationLoaded: conversation.conversationLoaded,
  loadConversationError: conversation.loadConversationError
});

const mappedActions = { loadConversation };

@connect(mappedState, mappedActions)
class Conversation extends Component {
  static propTypes = {
    articleId: PropTypes.number.isRequired,
    conversationId: PropTypes.number,
    comments: PropTypes.array.isRequired,
    loadingConversation: PropTypes.bool.isRequired,
    conversationLoaded: PropTypes.bool.isRequired,
    loadConversationError: PropTypes.bool.isRequired,
    loadConversation: PropTypes.func.isRequired
  }

  componentWillMount() {
    this.loadComments();
  }

  loadComments = () => {
    this.props.loadConversation(this.props.articleId);
  }

  render() {
    return (
      <LoadingScreen loading={this.props.loadingConversation}>
        <div>
          <CommentsList
            comments={this.props.comments}
            showReloadList={this.props.loadConversationError !== null}
            onReloadList={this.loadComments}
          />
        </div>
      </LoadingScreen>
    );
  }
}

export default Conversation;

import React, { Component, PropTypes } from 'react';
import { connect } from 'react-redux';
import { loadConversation } from 'redux/modules/conversationModule';
// COMPONENTS
import CommentsList from './CommentsList';
import { LoadingScreen } from 'components';

const mappedState = ({ conversation }) => ({
  conversationId: conversation.id,
  comments: conversation.comments,
  loadingConversation: conversation.loadingConversation,
  conversationLoaded: conversation.conversationLoaded,
  loadConversationError: conversation.loadConversationError
});

const mappedActions = { loadConversation };

@connect(mappedState, mappedActions)
class Conversation extends Component {
  static propTypes = {
    articleId: PropTypes.number.isRequired,
    conversationId: PropTypes.number,
    comments: PropTypes.array.isRequired,
    loadingConversation: PropTypes.bool.isRequired,
    conversationLoaded: PropTypes.bool.isRequired,
    loadConversationError: PropTypes.bool.isRequired,
    loadConversation: PropTypes.func.isRequired
  }

  componentWillMount() {
    this.loadComments();
  }

  loadComments = () => {
    this.props.loadConversation(this.props.articleId);
  }

  render() {
    return (
      <LoadingScreen loading={this.props.loadingConversation}>
        <div>
          <CommentsList
            comments={this.props.comments}
            showReloadList={this.props.loadConversationError}
            onReloadList={this.loadComments}
          />
        </div>
      </LoadingScreen>
    );
  }
}

export default Conversation;

Fix “reload convrsation” button’s state


import { observable, action } from 'mobx';

class CrowdsalePageStore {

  @observable maximumSellableTokens;
  @observable maximumSellableTokensInWei;
  @observable investors;
  @observable ethRaised;
  @observable weiRaised;
  @observable rate;
  @observable tokensSold;
  @observable tokenAmountOf;
  @observable startBlock
  @observable endDate;

  @action setProperty = (property, value) => {
    this[property] = value
  }
}

export default CrowdsalePageStore;

import { observable, action } from 'mobx';

class CrowdsalePageStore {

  @observable maximumSellableTokens;
  @observable maximumSellableTokensInWei;
  @observable investors;
  @observable ethRaised;
  @observable weiRaised;
  @observable rate;
  @observable tokensSold;
  @observable tokenAmountOf;
  @observable tiers = []
  @observable ticks = []

  @action setProperty = (property, value) => {
    this[property] = value
  }

  @action setTier = (tier) => {
    console.log(tier)
    this.tiers.push(tier)
    this.sortTiers()
    this.buildTicksCollection()
  }

  @action sortTiers = () => {
    this.tiers = this.tiers.sort((previous, current) => current.startDate >= previous.startDate ? -1 : 1)
  }

  @action buildTicksCollection = () => {
    // assumes that list is sorted
    this.ticks = this.tiers.reduce((ticks, tier, index) => {
      const previousTickIndex = ticks.findIndex(tick => tick.type === 'end' && tick.time === tier.startDate)

      if (previousTickIndex === -1) {
        ticks.push({
          type: 'start',
          time: tier.startDate,
          order: index + 1
        })
      }

      ticks.push({
        type: 'end',
        time: tier.endDate,
        order: index + 1
      })

      return ticks
    }, [])
      .filter(tick => (tick.time - Date.now()) > 0)
    console.log(this.ticks)
  }

  @action extractNextTick = () => {
    return this.ticks.shift()
  }
}

export default CrowdsalePageStore;

Add tiers and ticks collections to crowdsalePageStore


export default class EventService {
  constructor($http) {
    this.$inject = ['$http'];
    this.$http = $http;
    this.events = [];
  }

  loadEventsByPlanId(planId) {
    return this.$http.get(`/api/events/${planId}`).then(response => this.events = response.data);
  }

  getEvent(eventId) { return this.events.find(event => event._id === eventId); }

  submitNewEvent(event) { return this.$http.post('/api/event', event); }

  updateEvent(eventId, updatedEvent) {
    if (typeof eventId === 'object' && updatedEvent === undefined) {
      updatedEvent = eventId;
      eventId = updatedEvent._id;
    }
    return this.$http.post(`/api/event/${eventId}`, updatedEvent);
  }

  deleteEvent(eventId) { return this.$http.delete(`/api/event/${eventId}`); }

  promoteEvent(eventId) { return this.$http.put(`/event/${eventId}/promote`); }

  downvoteEvent(eventId) { return this.$http.put(`/event/${eventId}/downvote`); }

  upvoteEvent(eventId) { return this.$http.put(`/event/${eventId}/upvote`); }

}

export default class EventService {
  constructor($http) {
    this.$inject = ['$http'];
    this.$http = $http;
    this.events = [];
  }

  loadEventsByPlanId(planId) {
    return this.$http.get(`/api/events/${planId}`).then(response => this.events = response.data);
  }

  getEvent(eventId) { return this.events.find(event => event._id === eventId); }

  submitNewEvent(event) { return this.$http.post('/api/event', event); }

  updateEvent(eventId, updatedEvent) {
    if (typeof eventId === 'object' && updatedEvent === undefined) {
      updatedEvent = eventId;
      eventId = updatedEvent._id;
    }
    return this.$http.put(`/api/event/${eventId}`, updatedEvent);
  }

  deleteEvent(eventId) { return this.$http.delete(`/api/event/${eventId}`); }

  promoteEvent(eventId) { return this.$http.put(`/event/${eventId}/promote`); }

  downvoteEvent(eventId) { return this.$http.put(`/event/${eventId}/downvote`); }

  upvoteEvent(eventId) { return this.$http.put(`/event/${eventId}/upvote`); }

}

Fix POST to PUT on method


const Sequelize = require('sequelize');

const database = process.env.DATABASE_URL;

const db = new Sequelize(database, {
  logging: false
});

module.exports = {
  Sequelize,
  db
};

const Sequelize = require('sequelize');

const database = process.env.DATABASE_URL;

if (!database) {
  throw new Error(`
  $DATABASE_URL is not defined in your environment!
  This is required in order to connect to MySQL and should have the format:
  mysql://username:password@hostname/database
  where :password is optional for no password.

  If running from the command line, you would need to type something like:
  export DATABASE_URL=mysql://root@localhost/test
  in the terminal before starting the server.

  If running from VS Code, you need to add:
  "env": {
    "DATABASE_URL": "mysql://root@localhost/test"
  }
  as a parameter to your configurations.

  If running anything else, look up how to add environment variables.

  These environment variables should already be defined for the Travis
  and Heroku scripts.
  `);
}

const db = new Sequelize(database, {
  logging: false
});

module.exports = {
  Sequelize,
  db
};

Throw a descriptive error when $DATABASE_URL is undefined


#!/usr/bin/env node

const path = require("path");
const fs = require("fs-extra");
const should = require("should");

const LATEST = "2";

function generateScript() {
    return new Promise((resolve, reject) => {
        const packages = [
            "node-red-util",
            "node-red-runtime",
            "node-red-registry",
            "node-red-nodes",
            "node-red-editor-client",
            "node-red-editor-api",
            "node-red"
        ];
        const rootPackage = require(path.join(__dirname,"..","package.json"));
        const version = rootPackage.version;

        const versionParts = version.split(".");
        let tagArg = "";
        if (versionParts[0] !== LATEST) {
            tagArg = `--tag v${versionParts[0]}-maintenance`
        } else if (/-/.test(version))  {
            tagArg = "--tag next"
        }

        const lines = [];

        packages.forEach(name => {
            lines.push(`npm publish ${name}-${version}.tgz ${tagArg}\n`);
        })
        resolve(lines.join(""))
    });
}

if (require.main === module) {
    generateScript().then(output => {
        console.log(output);
    });
} else {
    module.exports = generateScript;
}

#!/usr/bin/env node

const path = require("path");
const fs = require("fs-extra");
const should = require("should");

const LATEST = "2";

function generateScript() {
    return new Promise((resolve, reject) => {
        const packages = [
            "@node-red/util",
            "@node-red/runtime",
            "@node-red/registry",
            "@node-red/nodes",
            "@node-red/editor-client",
            "@node-red/editor-api",
            "node-red"
        ];
        const rootPackage = require(path.join(__dirname,"..","package.json"));
        const version = rootPackage.version;

        const versionParts = version.split(".");
        let updateNextToLatest = false;
        let tagArg = "";
        if (versionParts[0] !== LATEST) {
            tagArg = `--tag v${versionParts[0]}-maintenance`
        } else if (/-/.test(version))  {
            tagArg = "--tag next"
        } else {
            updateNextToLatest = true;
        }

        const lines = [];

        packages.forEach(name => {
            const tarName = name.replace(/@/,"").replace(/\//,"-")
            lines.push(`npm publish ${tarName}-${version}.tgz ${tagArg}\n`);
            if (updateNextToLatest) {
                lines.push(`npm dist-tag add ${name}@${version} next\n`);
            }
        })
        resolve(lines.join(""))
    });
}

if (require.main === module) {
    generateScript().then(output => {
        console.log(output);
    });
} else {
    module.exports = generateScript;
}

Update gen-publish script to update 'next' tag for main releases


var FOAM_SOURCES = [
  'core/i18n.js',
  'core/stdlib.js',
  'core/io.js',
  'core/writer.js',
  'core/socket.js',
  'core/base64.js',
  'core/encodings.js',
  'core/utf8.js',
  'core/async.js',
  'core/parse.js',
  'core/event.js',
  'core/JSONUtil.js',
  'core/XMLUtil.js',
  'core/context.js',
  'core/JSONParser.js',
  'core/TemplateUtil.js',
  'core/ChromeEval.js',
  'core/FOAM.js',
  'core/FObject.js',
  'core/BootstrapModel.js',
  'core/mm1Model.js',
  'core/mm2Property.js',
  'core/mm3Types.js',
  'core/mm4Method.js',
  'core/mm5Misc.js',
  'core/LayoutTraits.js',
  'core/value.js',
  'core/view.js',
  'core/view2.js',
  'core/layout.js',
  'core/daoView.js',
  'core/ChoiceView.js',
  'core/DetailView.js',
  'core/TableView.js',
  'core/cview.js',
  'core/cview2.js',
  'core/RichTextView.js',
  'core/listchoiceview.js',
  'core/scroll.js',
  'core/CViewPrimitives.js',
  'core/Diagramming.js',
  'core/HTMLParser.js',
  'core/mlang.js',
  'core/mlang2.js',
  'core/QueryParser.js',
  'core/search.js',
  'core/oam.js',
  'core/visitor.js',
  'core/messaging.js',
  'core/dao.js',
  'core/dao2.js',
  'core/arrayDAO.js',
  'core/ClientDAO.js',
  'core/diff.js',
  'core/SplitDAO.js',
  'core/index.js',
  'core/StackView.js',
  'core/MementoMgr.js',
  'core/DAOController.js',
  'core/ThreePaneController.js',
  'core/experimental/protobufparser.js',
  'core/experimental/protobuf.js',
  'core/models.js',
  'core/touch.js',
  'core/glang.js',
  'core/oauth.js',
  'core/busy.js',
  'lib/bookmarks/bookmark.js',
  'core/turntable.js',
  'core/ChromeApp.js',
  'core/ChromeStorage.js',
  'core/SyncManager.js',
  'core/CORE.js'
];

var FOAM_SOURCES = files.filter(function(m) {
  if ( Array.isArray(m) ) return m[1]();
  return true;
}).map(function(m) {
  if ( Array.isArray(m) ) m = m[0];
  return 'core/' + m + '.js';
});

Replace FOAM_SOURCES with a filtered list of the core files from FOAMmodels.js so its always up to date.


'use strict';

togglbutton.render('#headerContainer:not(.toggl)',
  { observe: true },
  function (elem) {
    const getDescription = function () {
      // entity: incident
      const ticketnumber = $('input[data-id="ticketnumber.fieldControl-text-box-text"]');
      const ticketname = $('input[data-id="title.fieldControl-text-box-text"]');
      if ((ticketnumber) || (ticketname)) {
        return (ticketnumber ? ticketnumber.title + ' ' : '') + (ticketname ? ticketname.title : '');
      } else {
        // other entities
        const header = $('#headerContainer');
        if (!header) {
          return '';
        }
        const title = $('h1', header);
        if (!title) {
          return '';
        }
        return (title ? title.textContent : '');
      }
    };
    const link = togglbutton.createTimerLink({
      className: 'dynamics365',
      description: getDescription
    });
    elem.appendChild(link);
  });

'use strict';

togglbutton.render(
  '[data-id="ticketnumber.fieldControl-text-box-text"]:not(.toggl)',
  { observe: true },
  function (elem) {
    const header = $('#headerContainer');
    const getDescription = function () {
      // entity: incident
      const ticketnumber = $(
        'input[data-id="ticketnumber.fieldControl-text-box-text"]'
      );
      const ticketname = $('input[data-id="title.fieldControl-text-box-text"]');
      if (ticketnumber || ticketname) {
        return (
          (ticketnumber ? ticketnumber.title + ' ' : '') +
          (ticketname ? ticketname.title : '')
        );
      } else {
        // other entities
        if (!header) {
          return '';
        }
        const title = $('h1', header);
        if (!title) {
          return '';
        }
        return title ? title.textContent : '';
      }
    };
    const link = togglbutton.createTimerLink({
      className: 'dynamics365',
      description: getDescription
    });
    header.appendChild(link);
  }
);

Fix observed element for Dynamics 365


'use strict';

const { emitEvent } = require('../../events');
const { mapValues, omit } = require('../../utilities');

// Create event when all protocol-specific servers have started
const emitStartEvent = async function ({ servers, runtimeOpts }) {
  const message = 'Server is ready';
  const info = getPayload({ servers, runtimeOpts });
  const startPayload = await emitEvent({
    type: 'start',
    phase: 'startup',
    message,
    info,
    runtimeOpts,
    async: false,
  });
  return { startPayload };
};

const getPayload = function ({ servers, runtimeOpts }) {
  // Remove `server` from event payload as it is not serializable
  const serversA = mapValues(servers, serverInfo => omit(serverInfo, 'server'));
  return { servers: serversA, runtimeOpts };
};

module.exports = {
  emitStartEvent,
};

'use strict';

const { emitEvent } = require('../../events');
const { mapValues, omit } = require('../../utilities');

// Create event when all protocol-specific servers have started
const emitStartEvent = async function ({ servers, runtimeOpts }) {
  const message = 'Server is ready';
  const info = getPayload({ servers, runtimeOpts });
  const startPayload = await emitEvent({
    type: 'start',
    phase: 'startup',
    message,
    info,
    runtimeOpts,
    async: false,
  });
  return { startPayload };
};

// Remove some properties from event payload as they are not serializable,
// or should not be made immutable
const getPayload = function ({ servers, runtimeOpts }) {
  const serversA = mapValues(servers, serverInfo => omit(serverInfo, 'server'));
  const runtimeOptsA = omit(runtimeOpts, 'idl');
  return { servers: serversA, runtimeOpts: runtimeOptsA };
};

module.exports = {
  emitStartEvent,
};

Fix GraphQL introspection query crashing


import graphqlShorthandParser from 'graphql-shorthand-parser';

// we wrap the parser to get useful error messages
// see: https://github.com/pegjs/pegjs/issues/314

// @p shorthand parser
// @data string for parsing
const parse = (data) => {
  console.time('Parse time');
  try {
    return graphqlShorthandParser.parse(data);
  } catch (e) {
    if (e instanceof graphqlShorthandParser.SyntaxError) {
      console.log('Error in input file:');
      const errLine = e.location.start.line;
      const errCol = e.location.start.column;
      const line = data.split(/\r\n|\r|\n/)[errLine - 1];
      console.log(line);
      console.log(`${dup(' ', errCol)}^`);
      console.log(`Line ${errLine}, column ${errCol}: ${e.message}`);
      console.log(e.stack);
      throw e;
    } else {
      console.log(e);
      console.log(e.stack);
    }
  }
  return null;
};

// helper function that repeats a string count times
const dup = (str, count) => {
  return new Array(count).join(str);
};

const SyntaxError = graphqlShorthandParser.SyntaxError;

export { parse, SyntaxError };

import graphqlShorthandParser from 'graphql-shorthand-parser';

// we wrap the parser to get useful error messages
// see: https://github.com/pegjs/pegjs/issues/314

// @p shorthand parser
// @data string for parsing
const parse = (data) => {
  try {
    return graphqlShorthandParser.parse(data);
  } catch (e) {
    if (e instanceof graphqlShorthandParser.SyntaxError) {
      const message = [];
      message.push('Error in input file:\n');
      const errLine = e.location.start.line;
      const errCol = e.location.start.column;
      const line = data.split(/\r\n|\r|\n/)[errLine - 1];
      message.push(line);
      message.push(`${dup(' ', errCol)}^`);
      message.push(`Line ${errLine}, column ${errCol}: ${e.message}`);
      e.message = message.join('\n');
    }
    throw e;
  }
};

// helper function that repeats a string count times
const dup = (str, count) => {
  return new Array(count).join(str);
};

const SyntaxError = graphqlShorthandParser.SyntaxError;

export { parse, SyntaxError };

Add message to error instead of printing to console


'use babel'

import promisify from '..'

describe('Promisify', function() {
  it('succeeds properly', function() {
    const callback = function(arg1, arg2) {
      expect(arg1).toBe('something')
      arg2(null, 'wow')
    }
    waitsForPromise(function() {
      return promisify(callback)('something').then(function(retVal) {
        expect(retVal).toBe(retVal)
      })
    })
  })

  it('throws properly', function() {
    const callback = function(arg1, arg2) {
      expect(arg1).toBe('something')
      arg2('wow')
    }
    waitsForPromise(function() {
      return promisify(callback)('something').then(function() {
        expect(false).toBe(true)
      }, function(error) {
        expect(error).toBe('wow')
      })
    })
  })
})

'use babel'

import promisify from '..'

describe('Promisify', function() {
  it('succeeds properly', function() {
    const callback = function(arg1, arg2) {
      expect(arg1).toBe('something')
      arg2(null, 'wow')
    }
    waitsForPromise(function() {
      return promisify(callback)('something').then(function(retVal) {
        expect(retVal).toBe(retVal)
      })
    })
  })

  it('throws properly', function() {
    const callback = function(arg1, arg2) {
      expect(arg1).toBe('something')
      arg2('wow')
    }
    waitsForPromise(function() {
      return promisify(callback)('something').then(function() {
        expect(false).toBe(true)
      }, function(error) {
        expect(error).toBe('wow')
      })
    })
  })
  it('works when Array.from is available', function() {
    const _ = Array.from
    Array.from = function(array) {
      return Array.prototype.slice.call(array)
    }
    const callback = function(arg1, arg2) {
      expect(arg1).toBe('something')
      arg2(null, 'wow')
    }
    waitsForPromise(function() {
      return promisify(callback)('something').then(function(retVal) {
        expect(retVal).toBe(retVal)
        Array.from = _
      })
    })
  })
  it('works when Array.from is not available', function() {
    const _ = Array.from
    Array.from = null
    const callback = function(arg1, arg2) {
      expect(arg1).toBe('something')
      arg2(null, 'wow')
    }
    waitsForPromise(function() {
      return promisify(callback)('something').then(function(retVal) {
        expect(retVal).toBe(retVal)
        Array.from = _
      })
    })
  })
})

Add specs for both envs


(function () {
  'use strict';

  // Courses controller
  angular
    .module('courses')
    .controller('CoursesController', CoursesController);

  CoursesController.$inject = ['$scope', '$state', '$window', 'Authentication', 'courseResolve', 'DepartmentsService'];

  function CoursesController ($scope, $state, $window, Authentication, course, DepartmentsService) {
    var vm = this;

    vm.authentication = Authentication;
    vm.departments = DepartmentsService.query();
    vm.course = course;
    vm.error = null;
    vm.form = {};
    vm.remove = remove;
    vm.save = save;

    // Remove existing Course
    function remove() {
      if ($window.confirm('Are you sure you want to delete?')) {
        vm.course.$remove($state.go('admin.courses.list'));
      }
    }

    // Save Course
    function save(isValid) {
      if (!isValid) {
        $scope.$broadcast('show-errors-check-validity', 'vm.form.courseForm');
        return false;
      }
      // TODO: move create/update logic to service
      if (vm.course._id) {
        vm.course.$update(successCallback, errorCallback);
      } else {
        vm.course.$save(successCallback, errorCallback);
      }

      function successCallback(res) {
        $state.go('admin.courses.list');
      }

      function errorCallback(res) {
        vm.error = res.data.message;
      }
    }
  }
}());

(function () {
  'use strict';

  // Courses controller
  angular
    .module('courses')
    .controller('CoursesController', CoursesController);

  CoursesController.$inject = ['$scope', '$state', '$window', 'Authentication', 'courseResolve', 'DepartmentsService'];

  function CoursesController ($scope, $state, $window, Authentication, course, DepartmentsService) {
    var vm = this;

    vm.authentication = Authentication;
    vm.departments = DepartmentsService.query();
    vm.course = course;
    vm.error = null;
    vm.form = {};
    vm.remove = remove;
    vm.save = save;

    // Remove existing Course
    function remove() {
      if ($window.confirm('Are you sure you want to delete?')) {
        vm.course.$remove($state.go('admin.courses.list'));
      }
    }

    // Save Course
    function save(isValid) {
      if (!isValid) {
        $scope.$broadcast('show-errors-check-validity', 'vm.form.courseForm');
        return false;
      }

      if (vm.authentication.user.roles.indexOf('admin') < 0 && vm.authentication.user.department !== undefined) {
        vm.course.department = vm.authentication.user.department;
      }

      // TODO: move create/update logic to service
      if (vm.course._id) {
        vm.course.$update(successCallback, errorCallback);
      } else {
        vm.course.$save(successCallback, errorCallback);
      }

      function successCallback(res) {
        $state.go('admin.courses.list');
      }

      function errorCallback(res) {
        vm.error = res.data.message;
      }
    }
  }
}());

Fix roles for Courses API


import React from 'react'
import PersonGroup from '../../../../components/person-group'
import {sortPeople} from '../../../../../shared/utils'

export default Episode

function Episode({episodeData}) {
  const {
    date,
    title,
    time,
    dateDisplay,
    guests = [],
    descriptionHTML,
  } = episodeData
  const sortedGuests = sortPeople(guests)
  return (

        <a href={`/episodes/${date}`}>{title}

        {dateDisplay} at {time}

      <PersonGroup people={sortedGuests} />

  )
}

import React from 'react'
import PersonGroup from '../../../../components/person-group'
import RSVPIcon from '../../../../components/rsvp-icon'
import {sortPeople} from '../../../../../shared/utils'

export default Episode

function Episode({episodeData}) {
  const {
    date,
    title,
    time,
    dateDisplay,
    guests = [],
    descriptionHTML,
    hangoutUrl,
  } = episodeData
  const sortedGuests = sortPeople(guests)
  return (

        <a href={`/episodes/${date}`}>{title}

          <RSVPIcon hangoutUrl={hangoutUrl} />
          {' ' + dateDisplay} at {time}

      <PersonGroup people={sortedGuests} />

  )
}

Add +calendar to the home page