'use strict';

var assert = require('assert');
var harness = require('./harness.js');

describe('#cluster management', function() {
  function allTests(H) {
    it('should be able to access a cluster manager', function () {
      var cluster = new H.lib.Cluster(H.connstr);
      var clusterMgr = cluster.manager(H.muser, H.mpass);
      assert(clusterMgr);
    });

    it('should be able to list buckets', function () {
      var cluster = new H.lib.Cluster(H.connstr);
      var clusterMgr = cluster.manager(H.muser, H.mpass);
      clusterMgr.listBuckets(function (err, list) {
        assert(!err);
        assert(list);
      });
    });
  }
  describe('#RealBucket', function() {
    allTests.bind(this, harness);

    it('should not be able to list buckets with wrong password', function (done) {
      var cluster = new harness.lib.Cluster(harness.connstr);
      var clusterMgr = cluster.manager(harness.muser, 'junk');
      clusterMgr.listBuckets(function (err, list) {
        assert(err);
        assert.equal(err.message, 'operation failed (401)');
        assert(!list);
        done();
      });
    });
  });
  describe('#MockBucket', allTests.bind(this, harness.mock));
});

'use strict';

var assert = require('assert');
var harness = require('./harness.js');

describe('#cluster management', function() {
  function allTests(H) {
    it('should be able to access a cluster manager', function () {
      var cluster = new H.lib.Cluster(H.connstr);
      var clusterMgr = cluster.manager(H.muser, H.mpass);
      assert(clusterMgr);
    });

    it('should be able to list buckets', function () {
      var cluster = new H.lib.Cluster(H.connstr);
      var clusterMgr = cluster.manager(H.muser, H.mpass);
      clusterMgr.listBuckets(function (err, list) {
        assert(!err);
        assert(list);
      });
    });
  }
  describe('#RealBucket', function() {
    allTests.call(this, harness);

    it('should not be able to list buckets with wrong password', function (done) {
      var cluster = new harness.lib.Cluster(harness.connstr);
      var clusterMgr = cluster.manager(harness.muser, 'junk');
      clusterMgr.listBuckets(function (err, list) {
        assert(err);
        assert.equal(err.message, 'operation failed (401)');
        assert(!list);
        done();
      });
    });
  });
  describe('#MockBucket', allTests.bind(this, harness.mock));
});

Fix bug with cluster management on real buckets


import { module, test } from 'qunit';
import { setupTest } from 'ember-qunit';

module('Unit | Serializer | curriculum inventory sequence block', function(hooks) {
  setupTest(hooks);

  test('start and end date are formatted during serialization', function(assert) {
    const record = this.owner.lookup('service:store').createRecord('curriculum-inventory-sequence-block');
    record.set('startDate', new Date());
    record.set('endDate', new Date());
    const serializedRecord = record.serialize();
    const pattern = /\d{4}-\d{2}-\d{2}/;
    assert.ok(serializedRecord.startDate.match(pattern));
    assert.ok(serializedRecord.endDate.match(pattern));
  });

  test('empty start and end date are not formatted during serialization', function(assert) {
    const record = this.owner.lookup('service:store').createRecord('curriculum-inventory-sequence-block');
    record.set('startDate', null);
    record.set('endDate', null);
    const serializedRecord = record.serialize();
    assert.equal(serializedRecord.startDate, null);
    assert.equal(serializedRecord.endDate, null);
  });
});

import { module, test } from 'qunit';
import { setupTest } from 'ember-qunit';

module('Unit | Serializer | curriculum inventory sequence block', function(hooks) {
  setupTest(hooks);

  test('start and end date are formatted during serialization', function(assert) {
    const record = this.owner.lookup('service:store').createRecord('curriculum-inventory-sequence-block');
    record.set('startDate', new Date());
    record.set('endDate', new Date());
    const serializedRecord = record.serialize();
    const pattern = /\d{4}-\d{2}-\d{2}/;
    assert.ok(serializedRecord.data.attributes.startDate.match(pattern));
    assert.ok(serializedRecord.data.attributes.endDate.match(pattern));
  });

  test('empty start and end date are not formatted during serialization', function(assert) {
    const record = this.owner.lookup('service:store').createRecord('curriculum-inventory-sequence-block');
    record.set('startDate', null);
    record.set('endDate', null);
    const serializedRecord = record.serialize();
    assert.equal(serializedRecord.data.attributes.startDate, null);
    assert.equal(serializedRecord.data.attributes.endDate, null);
  });
});

Test serialization in json api format


'use strict';

var gulp = require('gulp');
var eslint = require('gulp-eslint');
var excludeGitignore = require('gulp-exclude-gitignore');
var jsonlint = require("gulp-jsonlint");
var mocha = require('gulp-mocha');
var istanbul = require('gulp-istanbul');
var plumber = require('gulp-plumber');

gulp.task('set-test-env', function(cb) {
    process.env.NODE_ENV = 'test';
    cb();
});

gulp.task('json', ['set-test-env'], function(cb) {
    gulp.src(["**/*.json", "!./node_modules/**/*.json"])
        .pipe(jsonlint())
        .pipe(jsonlint.reporter());
    cb();
});

gulp.task('static', function() {
    gulp.src('source/*.js')
        .pipe(excludeGitignore())
        .pipe(eslint())
        .pipe(eslint.format())
        .pipe(eslint.failAfterError());
});

gulp.task('test', ['set-test-env'], function(cb) {
    var mochaErr;

    gulp.src('test/**/*.js')
        .pipe(plumber())
        .pipe(mocha({reporter: 'spec'}))
        .on('error', function(err) {
            mochaErr = err;
        })
        .pipe(istanbul.writeReports())
        .on('end', function() {
            cb(mochaErr);
        });
});

gulp.task('default', ['set-test-env', 'json', 'static', 'test']);

'use strict';

var gulp = require('gulp');
var eslint = require('gulp-eslint');
var excludeGitignore = require('gulp-exclude-gitignore');
var jsonlint = require("gulp-jsonlint");
var mocha = require('gulp-mocha');
var istanbul = require('gulp-istanbul');
var plumber = require('gulp-plumber');

gulp.task('set-test-env', function(cb) {
    process.env.NODE_ENV = 'test';
    cb();
});

gulp.task('json', ['set-test-env'], function(cb) {
    gulp.src(["**/*.json", "!./node_modules/**/*.json"])
        .pipe(jsonlint())
        .pipe(jsonlint.reporter());
    cb();
});

gulp.task('lint', function() {
    gulp.src('source/*.js')
        .pipe(excludeGitignore())
        .pipe(eslint())
        .pipe(eslint.format())
        .pipe(eslint.failAfterError());
});

gulp.task('test', ['set-test-env'], function(cb) {
    var mochaErr;

    gulp.src('test/**/*.js')
        .pipe(plumber())
        .pipe(mocha({reporter: 'spec'}))
        .on('error', function(err) {
            mochaErr = err;
        })
        .pipe(istanbul.writeReports())
        .on('end', function() {
            cb(mochaErr);
        });
});

gulp.task('default', ['set-test-env', 'json', 'lint', 'test']);

Rename Gulp task for improved clarity


#!/usr/bin/env node

exports.command = {
  description: 'get an attribute for a project',
  arguments: '<project> <attribute>'
};

if (require.main !== module) {
  return;
}

var storage = require('../lib/storage.js');
var utilities = require('../lib/utilities.js');

var program = utilities.programDefaults('get', '<project> <attribute>');

program.option('-p, --porcelain', 'Get the value in a machine-readable way');

program.parse(process.argv);

storage.setup(function () {
  if (program.args.length !== 2) {
    console.error('Please specify a project and attribute.');

    process.exit(1);
  }

  var name = program.args[0];
  var attribute = program.args[1];

  var project = storage.getProjectOrDie(name);

  if (attribute === 'directory') {
    project.directory = utilities.expand(project.directory);
  }

  if (program.porcelain) {
    console.log(project[attribute]);
  } else {
    console.log('%s:%s: "%s"', project.name, attribute, project[attribute]);
  }
});

#!/usr/bin/env node

exports.command = {
  description: 'get an attribute for a project',
  arguments: '<project> <attribute>'
};

if (require.main !== module) {
  return;
}

var storage = require('../lib/storage.js');
var utilities = require('../lib/utilities.js');

var program = utilities.programDefaults('get', '<project> <attribute>');

program.option('-p, --porcelain', 'Get the value in a machine-readable way');

program.parse(process.argv);

storage.setup(function () {
  if (program.args.length !== 2) {
    console.error('Please specify a project and attribute.');

    process.exit(1);
  }

  var name = program.args[0];
  var attribute = program.args[1];

  var project = storage.getProjectOrDie(name);

  if (attribute === 'directory') {
    project.directory = utilities.expand(project.directory);
  }

  if (program.porcelain) {
    console.log(project[attribute] || '');
  } else {
    console.log('%s:%s: "%s"', project.name, attribute,
                project[attribute] || '');
  }
});

Fix display of undefined attributes


/* global describe, it */

'use strict';

require('should');
var pff = require('..');

describe('specifiers', function () {
    it('should substitute once in pattern', function () {
        pff('%s', 'result').should.equal('result');
    });

    it('should substitute twice in pattern', function () {
        pff('%s%s', 'one', 'two').should.equal('onetwo');
    });

    it('should substitute in the middle of pattern', function () {
        pff('one %s three', 'two').should.equal('one two three');
    });

    it('should substitute only patterns with defined values', function () {
        pff('one two %s %s', 'three').should.equal('one two three %s');
    });

    it('should ignore additional values for substitution', function () {
        pff('one two %s', 'three', 'four').should.equal('one two three');
    });

    it('should substitute %d', function () {
        pff('%d', 1).should.equal('1');
    });

    it('should substitute %d in the middle of pattern', function () {
        pff('one %d three', 2).should.equal('one 2 three');
    });
});

/* global describe, it */

'use strict';

require('should');
var pff = require('..');

describe('specifiers', function () {
    it('should substitute once in pattern', function () {
        pff('%s', 'result').should.equal('result');
    });

    it('should substitute twice in pattern', function () {
        pff('%s%s', 'one', 'two').should.equal('onetwo');
    });

    it('should substitute in the middle of pattern', function () {
        pff('one %s three', 'two').should.equal('one two three');
    });

    it('should substitute only patterns with defined values', function () {
        pff('one two %s %s', 'three').should.equal('one two three %s');
    });

    it('should ignore additional values for substitution', function () {
        pff('one two %s', 'three', 'four').should.equal('one two three');
    });

    it('should substitute %d', function () {
        pff('%d', 1).should.equal('1');
    });

    it('should substitute %d in the middle of pattern', function () {
        pff('one %d three', 2.4).should.equal('one 2 three');
    });

    it('should substitute %d with flooring', function () {
        pff('one %d three', 2.8).should.equal('one 2 three');
    });
});

Add tests for %d specifier


ReactiveDict._migratedDictData = {}; // name -> data
ReactiveDict._dictsToMigrate = {}; // name -> ReactiveDict

ReactiveDict._loadMigratedDict = function (dictName) {
  if (_.has(ReactiveDict._migratedDictData, dictName))
    return ReactiveDict._migratedDictData[dictName];

  return null;
};

ReactiveDict._registerDictForMigrate = function (dictName, dict) {
  if (_.has(ReactiveDict._dictsToMigrate, dictName))
    throw new Error("Duplicate ReactiveDict name: " + dictName);

  ReactiveDict._dictsToMigrate[dictName] = dict;
};

if (Package.reload) {
  // Put old migrated data into ReactiveDict._migratedDictData,
  // where it can be accessed by ReactiveDict._loadMigratedDict.
  var migrationData = Package.reload.Reload._migrationData('reactive-dict');
  if (migrationData && migrationData.dicts)
    ReactiveDict._migratedDictData = migrationData.dicts;

  // On migration, assemble the data from all the dicts that have been
  // registered.
  Package.reload.Reload._onMigrate('reactive-dict', function () {
    var dictsToMigrate = ReactiveDict._dictsToMigrate;
    var dataToMigrate = {};

    for (var dictName in dictsToMigrate)
      dataToMigrate[dictName] = dictsToMigrate[dictName]._getMigrationData();

    return [true, {dicts: dataToMigrate}];
  });
}

ReactiveDict._migratedDictData = {}; // name -> data
ReactiveDict._dictsToMigrate = {}; // name -> ReactiveDict

ReactiveDict._loadMigratedDict = function (dictName) {
  if (_.has(ReactiveDict._migratedDictData, dictName))
    return ReactiveDict._migratedDictData[dictName];

  return null;
};

ReactiveDict._registerDictForMigrate = function (dictName, dict) {
  if (_.has(ReactiveDict._dictsToMigrate, dictName))
    throw new Error("Duplicate ReactiveDict name: " + dictName);

  ReactiveDict._dictsToMigrate[dictName] = dict;
};

if (Meteor.isClient && Package.reload) {
  // Put old migrated data into ReactiveDict._migratedDictData,
  // where it can be accessed by ReactiveDict._loadMigratedDict.
  var migrationData = Package.reload.Reload._migrationData('reactive-dict');
  if (migrationData && migrationData.dicts)
    ReactiveDict._migratedDictData = migrationData.dicts;

  // On migration, assemble the data from all the dicts that have been
  // registered.
  Package.reload.Reload._onMigrate('reactive-dict', function () {
    var dictsToMigrate = ReactiveDict._dictsToMigrate;
    var dataToMigrate = {};

    for (var dictName in dictsToMigrate)
      dataToMigrate[dictName] = dictsToMigrate[dictName]._getMigrationData();

    return [true, {dicts: dataToMigrate}];
  });
}

Fix ReactiveDict on server (for iron:router)


import React, { PropTypes } from 'react';

export default function Link(props) {
  const { handleClick, href } = props;
  const fn = (e) => {
    e.preventDefault();
    handleClick(href);
  };

  return (
    <a {...props} onClick={fn} />
  );
}

Link.propTypes = {
  handleClick: PropTypes.func,
  href: PropTypes.string,
};

import React, { PropTypes } from 'react';

export default function Link(props) {
  const { handleClick, href } = props;
  const fn = (e) => {
    e.preventDefault();
    e.stopPropagation();
    handleClick(href);
  };

  return (
    <a {...props} onClick={fn} />
  );
}

Link.propTypes = {
  handleClick: PropTypes.func,
  href: PropTypes.string,
};

Stop link events from bubbling


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

var angular = require('angular');

var module = angular.module('sba-applications-logfile', ['sba-applications']);
global.sbaModules.push(module.name);

module.run(function ($sce, $http, ApplicationViews) {

  ApplicationViews.register({
    order: 1,
    title: $sce.trustAsHtml('<i class="fa fa-file-text-o fa-fw"></i>Log'),
    href: '/api/applications/{id}/logfile',
    target: '_blank',
    show: function (application) {
      if (!application.managementUrl || !application.statusInfo.status || application.statusInfo.status === 'OFFLINE') {
        return false;
      }
      return $http.head('api/applications/' + application.id + '/logfile').then(function () {
        return true;
      }).catch(function () {
        return false;
      });
    }
  });

});

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

var angular = require('angular');

var module = angular.module('sba-applications-logfile', ['sba-applications']);
global.sbaModules.push(module.name);

module.run(function ($sce, $http, ApplicationViews) {

  ApplicationViews.register({
    order: 1,
    title: $sce.trustAsHtml('<i class="fa fa-file-text-o fa-fw"></i>Log'),
    href: 'api/applications/{id}/logfile',
    target: '_blank',
    show: function (application) {
      if (!application.managementUrl || !application.statusInfo.status || application.statusInfo.status === 'OFFLINE') {
        return false;
      }
      return $http.head('api/applications/' + application.id + '/logfile').then(function () {
        return true;
      }).catch(function () {
        return false;
      });
    }
  });

});

Use relative url for logfile


var Events = {
	add: function(obj, events) {
		for (var type in events) {
			var func = events[type];
			if (obj.addEventListener) {
				obj.addEventListener(type, func, false);
			} else if (obj.attachEvent) {
				// Make a bound closure that calls on the right object and
				// passes on the global event object as a parameter.
				obj.attachEvent('on' + type, func.bound = function() {
					func.call(obj, window.event);
				});
			}
		}
	},

	remove: function(obj, events) {
		for (var type in events) {
			var func = events[type];
			if (obj.removeEventListener) {
				obj.removeEventListener(type, func, false);
			} else if (obj.detachEvent) {
				// Remove the bound closure instead of func itself
				obj.detachEvent('on' + type, func.bound);
			}
		}
	},

	getPoint: function(event) {
		return Point.create(
			event.pageX || event.clientX + document.documentElement.scrollLeft,
			event.pageY || event.clientY + document.documentElement.scrollTop
		);
	},

	getOffset: function(event) {
		var point = Events.getPoint(event);
		// Remove target offsets from page coordinates
		for (var el = event.target || event.srcElement; el;
				point.x -= el.offsetLeft, point.y -= el.offsetTop,
				el = el.offsetParent);
		return point;
	}
};

var Events = {
	add: function(obj, events) {
		for (var type in events) {
			var func = events[type];
			if (obj.addEventListener) {
				obj.addEventListener(type, func, false);
			} else if (obj.attachEvent) {
				// Make a bound closure that calls on the right object and
				// passes on the global event object as a parameter.
				obj.attachEvent('on' + type, func.bound = function() {
					func.call(obj, window.event);
				});
			}
		}
	},

	remove: function(obj, events) {
		for (var type in events) {
			var func = events[type];
			if (obj.removeEventListener) {
				obj.removeEventListener(type, func, false);
			} else if (obj.detachEvent) {
				// Remove the bound closure instead of func itself
				obj.detachEvent('on' + type, func.bound);
			}
		}
	},

	getPoint: function(event) {
		return Point.create(
			event.pageX || event.clientX + document.documentElement.scrollLeft,
			event.pageY || event.clientY + document.documentElement.scrollTop
		);
	},

	getOffset: function(event) {
		var point = Events.getPoint(event);
		// Remove target offsets from page coordinates
		for (var el = event.target || event.srcElement; el;
				point.x -= el.offsetLeft, point.y -= el.offsetTop,
				el = el.offsetParent) {}
		return point;
	}
};

Make it clear it's an empty loop.


'use strict';

const PouchDocument = require('./pouch-document');
const joi = require('joi');

/**
 * @class Project
 * @extends PouchDocument
 * @constructor
 * @property {string} name
 * @property {string=} consortiumId
 * @property {(File[])=} files
 * @property {string=} metaFile Full path to the project's metadata CSV
 */
class Project extends PouchDocument {}

Project.schema = Object.assign({
  name: joi.string().min(1).regex(/[a-zA-Z]+/, 'at least one character')
    .required(),
  consortiumId: joi.string().optional(),
  files: joi.alternatives().try(joi.array()).default([]),
  metaFile: joi.array(),
  metaFilePath: joi.string().min(2).optional(),
  metaCovariateMapping: joi.object().default({}),
}, PouchDocument.schema);

module.exports = Project;

'use strict';

const PouchDocument = require('./pouch-document');
const joi = require('joi');

/**
 * @class Project
 * @extends PouchDocument
 * @constructor
 * @property {string} name
 * @property {string=} consortiumId
 * @property {(File[])=} files
 * @property {string=} metaFile Full path to the project's metadata CSV
 */
class Project extends PouchDocument {}

Project.schema = Object.assign({
  name: joi.string().min(1).regex(/[a-zA-Z]+/, 'at least one character')
    .required(),

  /**
   * A project's consortium's `activeComputationInputs` are stashed on the
   * `computationInputs` property to ensure the user properly configures the
   * project for the computation run. coinstac-client-core compares the
   * consortium's computation inputs to the project's prior to run and throws
   * when they don't match.
   *
   * @todo Find better method for guaranteeing project-to-computation input
   * alignment.
   *
   * {@link https://github.com/MRN-Code/coinstac/issues/151}
   */
  computationInputs: joi.array().default([]),
  consortiumId: joi.string().optional(),
  files: joi.alternatives().try(joi.array()).default([]),
  metaFile: joi.array(),
  metaFilePath: joi.string().min(2).optional(),
  metaCovariateMapping: joi.object().default({}),
}, PouchDocument.schema);

module.exports = Project;

Add `computationInputs` property to `Project` model.


'use strict';
/* global require */
/* global process */

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

var mkdirSync = function (path) {
  try {
    fs.mkdirSync (path);
  } catch (e) {
    if (e.code !== 'EEXIST') {
      throw e;
    }
  }
};

var cwd = process.cwd ().replace (/\\/g, '/');
var suffix = '/node_modules/mai-chai';

if (cwd.endsWith (suffix)) {
  var root = cwd.substr (0, cwd.length - suffix.length);
  var test = path.join (root, 'test');
  var files = fs.readdirSync (path.join (cwd, 'templates'));

  mkdirSync (test);
  mkdirSync (path.join (root, 'src'));
  mkdirSync (path.join (root, 'src.test'));

  var regex = /\r\n/g;
  var newline = '\n';
  if (process.platform === 'win32') {
    regex = /[^\r]\n/g;
    newline = '\r\n';
  }

  files.forEach (function (file) {
    var data = fs.readFileSync (path.join (cwd, 'templates', file));
    if (file.substr (0, 2) === '--') {
      var name = file.substr (2, file.length - 2);
      fs.writeFileSync (path.join (root, name), data.toString ().replace (regex, newline));
    } else {
      fs.writeFileSync (path.join (test, file), data.toString ().replace (regex, newline));
    }
  });
}

'use strict';
/* global require */
/* global process */

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

var mkdirSync = function (path) {
  try {
    fs.mkdirSync (path);
  } catch (e) {
    if (e.code !== 'EEXIST') {
      throw e;
    }
  }
};

var cwd = process.cwd ().replace (/\\/g, '/');
var suffix = '/node_modules/mai-chai';

if (cwd.endsWith (suffix)) {
  var root = cwd.substr (0, cwd.length - suffix.length);
  var test = path.join (root, 'test');
  var files = fs.readdirSync (path.join (cwd, 'templates'));

  mkdirSync (test);
  mkdirSync (path.join (root, 'src'));
  mkdirSync (path.join (root, 'src.test'));

  var regex = /\r\n/g;
  var newline = '\n';
  if (process.platform === 'win32') {
    regex = /([^\r])\n/g;
    newline = '$1\r\n';
  }

  files.forEach (function (file) {
    var data = fs.readFileSync (path.join (cwd, 'templates', file));
    if (file.substr (0, 2) === '--') {
      var name = file.substr (2, file.length - 2);
      fs.writeFileSync (path.join (root, name), data.toString ().replace (regex, newline));
    } else {
      fs.writeFileSync (path.join (test, file), data.toString ().replace (regex, newline));
    }
  });
}

Fix regex for win32 platform


import React, { PureComponent } from 'react'
import { connect } from 'react-redux'
import { showCompareView } from '../../actions/index'
import Button from '../../components/common/button'

export class ShowCompareButton extends PureComponent {
  render () {
    const { answer, onClick } = this.props
    return (
      <Button className='select-answer' onClick={() => onClick(answer.get('embeddableKey'))} disabled={!answer.get('selectedForCompare')}>
        Compare/project
      </Button>
    )
  }
}

const mapDispatchToProps = (dispatch, ownProps) => {
  return {
    onClick: (embeddableKey) => dispatch(showCompareView(embeddableKey))
  }
}

const ShowCompareContainer = connect(null, mapDispatchToProps)(ShowCompareButton)
export default ShowCompareContainer

import React, { PureComponent } from 'react'
import { connect } from 'react-redux'
import { showCompareView, trackEvent } from '../../actions/index'
import CompareView from '../../components/report/compare-view'
import Report from '../../components/report/report'
import DataFetchError from '../../components/report/data-fetch-error'
import LoadingIcon from '../../components/report/loading-icon'
import getReportTree from '../../selectors/report-tree'
import getCompareViewData from '../../selectors/compare-view-data'
import Button from '../../components/common/button'

export class ShowCompareButton extends PureComponent {

  onCompareButtonClick = () => {
    const { reportTree, answer, onClick, trackEvent } = this.props
    trackEvent('Report', 'Compare/Project', reportTree.get('name'))
    return onClick(answer.get('embeddableKey'))
  }

  render () {
    const { reportTree, answer, onClick, trackEvent } = this.props
    return (
      <Button className='select-answer' onClick={this.onCompareButtonClick} disabled={!answer.get('selectedForCompare')}>
        Compare/project
      </Button>
    )
  }
}

function mapStateToProps (state) {
  const data = state.get('data')
  const error = data.get('error')
  const dataDownloaded = !error && !!data.get('lastUpdated')
  return {
    reportTree: dataDownloaded && getReportTree(state)
  }
}

const mapDispatchToProps = (dispatch, ownProps) => {
  return {
    onClick: (embeddableKey) => dispatch(showCompareView(embeddableKey)),
    trackEvent: (category, action, label) => dispatch(trackEvent(category, action, label))
  }
}

const ShowCompareContainer = connect(mapStateToProps, mapDispatchToProps)(ShowCompareButton)
export default ShowCompareContainer

Add event tracking for answer comparison


/**
 * @ngdoc controller
 * @name Umbraco.Editors.Dictionary.CreateController
 * @function
 *
 * @description
 * The controller for creating dictionary items
 */
function DictionaryCreateController($scope, $location, dictionaryResource, navigationService, notificationsService, formHelper, appState) {

    var vm = this;

    vm.itemKey = "";

    vm.createItem = createItem;

    function createItem() {

        if (formHelper.submitForm({ scope: $scope, formCtrl: this.createDictionaryForm })) {

            var node = $scope.currentNode;

            dictionaryResource.create(node.id, vm.itemKey).then(function (data) {
                navigationService.hideMenu();

                // set new item as active in tree
                var currPath = node.path ? node.path : "-1";
                navigationService.syncTree({ tree: "dictionary", path: currPath + "," + data, forceReload: true, activate: true });

                // reset form state
                formHelper.resetForm({ scope: $scope, formCtrl: this.createDictionaryForm });

                // navigate to edit view
                var currentSection = appState.getSectionState("currentSection");
                $location.path("/" + currentSection + "/dictionary/edit/" + data);

            }, function (err) {
                formHelper.resetForm({ scope: $scope, formCtrl: this.createDictionaryForm, hasErrors: true });
                if (err.data && err.data.message) {
                    notificationsService.error(err.data.message);
                    navigationService.hideMenu();
                }
            });
        }
    }
}

angular.module("umbraco").controller("Umbraco.Editors.Dictionary.CreateController", DictionaryCreateController);

/**
 * @ngdoc controller
 * @name Umbraco.Editors.Dictionary.CreateController
 * @function
 *
 * @description
 * The controller for creating dictionary items
 */
function DictionaryCreateController($scope, $location, dictionaryResource, navigationService, notificationsService, formHelper, appState) {

    var vm = this;

    vm.itemKey = "";

    vm.createItem = createItem;

    function createItem() {

        if (formHelper.submitForm({ scope: $scope, formCtrl: $scope.createDictionaryForm })) {

            var node = $scope.currentNode;

            dictionaryResource.create(node.id, vm.itemKey).then(function (data) {
                navigationService.hideMenu();

                // set new item as active in tree
                var currPath = node.path ? node.path : "-1";
                navigationService.syncTree({ tree: "dictionary", path: currPath + "," + data, forceReload: true, activate: true });

                // reset form state
                formHelper.resetForm({ scope: $scope, formCtrl: $scope.createDictionaryForm });

                // navigate to edit view
                var currentSection = appState.getSectionState("currentSection");
                $location.path("/" + currentSection + "/dictionary/edit/" + data);

            }, function (err) {
                formHelper.resetForm({ scope: $scope, formCtrl: $scope.createDictionaryForm, hasErrors: true });
                if (err.data && err.data.message) {
                    notificationsService.error(err.data.message);
                    navigationService.hideMenu();
                }
            });
        }
    }
}

angular.module("umbraco").controller("Umbraco.Editors.Dictionary.CreateController", DictionaryCreateController);

Fix JS error preventing newly created dictionary items from opening automatically


/**
 * Generates a string of 16 random Ascii characters using the native
 * `crypto` library and `btoa`.
 *
 * For IE11 it uses the prefixed `msCrypto` library. In case no crypto
 * library exists in the current environment an empty string will be
 * returned.
 *
 * @returns {string} 16 random Ascii characters, empty in case the
 * `crypto` library is not available.
 */
export default function () {
  // Backwards compatible with msCrypto in IE11
  const cryptoLib = crypto ||
    msCrypto; // eslint-disable-line no-undef

  if (!cryptoLib) {
    return '';
  }

  // Create an array of 16 8-bit unsigned integers
  const list = new Uint8Array(16);
  // Populate the array with random values
  cryptoLib.getRandomValues(list);

  // Binary to Ascii (btoa) converts our (character representation
  // of) our binary data to an Ascii string
  return btoa(Array
    .from(list) // convert to normal array
    .map(n => String.fromCharCode(n)) // convert each integer to a character
    .join('')); // convert to a string of characters
}

/**
 * Generates a string of 16 random Ascii characters using the native
 * `crypto` library and `btoa`.
 *
 * For IE11 it uses the prefixed `msCrypto` library. In case no crypto
 * library exists in the current environment an empty string will be
 * returned.
 *
 * @returns {string} 16 random Ascii characters, empty in case the
 * `crypto` library is not available.
 */
export default function () {
  // Backwards compatible with msCrypto in IE11
  const cryptoLib = window.crypto ||
    window.msCrypto; // eslint-disable-line no-undef

  if (!cryptoLib) {
    return '';
  }

  // Create an array of 16 8-bit unsigned integers
  const list = new Uint8Array(16);
  // Populate the array with random values
  cryptoLib.getRandomValues(list);

  // Binary to Ascii (btoa) converts our (character representation
  // of) our binary data to an Ascii string
  return btoa(Array
    .from(list) // convert to normal array
    .map(n => String.fromCharCode(n)) // convert each integer to a character
    .join('')); // convert to a string of characters
}

Fix for IE aborting on non existing global


var Cylon = require("cylon");

Cylon.api("http", {host: "0.0.0.0", ssl: false});

Cylon.robot({
  name: "edison",
  connections: {
    edison: { adaptor: 'intel-iot' }
  },

  devices: {
    buzzer: { driver: 'led', pin: 4 },
    display: { driver: 'upm-jhd1313m1'}
  },

  writeToScreen: function(message) {
  	this.display.setCursor(0,0);
		this.display.write(message);
  },

  turnOn: function() {
  	this.buzzer.turnOn();
  	this.writeToScreen("Alarm ON");
  },

  turnOff: function() {
  	this.buzzer.turnOff();
  	this.writeToScreen("Ready...");
  },

	commands: function() {
    return {
      turn_on: this.turnOn,
      turn_off: this.turnOff
    };
  },

  work: function() {
  	this.writeToScreen("Ready...");
  }
});

Cylon.start();

var Cylon = require("cylon");

Cylon.api("http", {host: "0.0.0.0", ssl: false});

Cylon.robot({
  name: "edison",
  connections: {
    edison: { adaptor: 'intel-iot' }
  },

  devices: {
    buzzer: { driver: 'led', pin: 4 },
    display: { driver: 'upm-jhd1313m1'}
  },

  writeToScreen: function(message) {
  	this.display.setCursor(0,0);
		this.display.write(message);
  },

  turnOn: function() {
  	this.buzzer.turnOn();
  	this.writeToScreen("Alarm ON");
    return "ok";
  },

  turnOff: function() {
  	this.buzzer.turnOff();
  	this.writeToScreen("Ready...");
    return "ok";
  },

	commands: function() {
    return {
      turn_on: this.turnOn,
      turn_off: this.turnOff
    };
  },

  work: function() {
  	this.writeToScreen("Ready...");
  }
});

Cylon.start();

Return 'ok' to display successful value


import major from './major'
import concentration from './concentration'
import emphasis from './emphasis'
import degree from './degree'

import _ from 'lodash'
let areas = [major, concentration, emphasis, degree]
let allAreas = _(areas).map(_.toArray).flatten().value()
// console.log(allAreas)

/**
 * Finds an area of study.
 *
 * @param {Object} args - a lodash filter object.
 * @returns {Object} - An area of study.
 */
let find = (args={}) => {
	return _.find(allAreas, args)
}

export {major, concentration, emphasis, degree, find}

import _ from 'lodash'

import major from './major'
import concentration from './concentration'
import emphasis from './emphasis'
import degree from './degree'

let areas = [major, concentration, emphasis, degree]
let allAreas = _(areas)
	.map(_.toArray)
	.flatten()
	.value()

let areaNotFound = {
	title: 'Not Found',
	years: [null, null],
	id: 'a-notfound',
	type: 'not-found',
	departmentAbbr: 'NOTFOUND',
	check: null,
}
Object.freeze(areaNotFound)

/**
 * Finds an area of study.
 *
 * @param {String} id - the id to find.
 * @param {Number} yearOfGraduation - the year the student matriculated.
 * @returns {Object} - an area of study.
 */
let findAreaOfStudy = (id, yearOfGraduation) => {
	let area = _.find(allAreas, (area) => {
		if (!area.id || area.id !== id)
			return false

		if (!area.years)
			return false

		let [startYear, endYear] = area.years

		let yearIsBetween = false
		if (_.isNull(endYear) && startYear <= yearOfGraduation)
			yearIsBetween = true
		else if (endYear >= yearOfGraduation && startYear <= yearOfGraduation)
			yearIsBetween = true

		return yearIsBetween
	})

	return area || areaNotFound
}

export default findAreaOfStudy

Check for years when finding areas


#!/usr/bin/env node

// This script removes the build artifacts of ignored contracts.

const fs = require('fs');
const path = require('path');
const cp = require('child_process');
const match = require('micromatch');

function readJSON (path) {
  return JSON.parse(fs.readFileSync(path));
}

cp.spawnSync('npm', ['run', 'compile'], { stdio: 'inherit' });

const pkgFiles = readJSON('package.json').files;

// Get only negated patterns.
const ignorePatterns = pkgFiles
  .filter(pat => pat.startsWith('!'))
// Remove the negation part. Makes micromatch usage more intuitive.
  .map(pat => pat.slice(1));

const ignorePatternsSubtrees = ignorePatterns
// Add **/* to ignore all files contained in the directories.
  .concat(ignorePatterns.map(pat => path.join(pat, '**/*')));

const artifactsDir = 'build/contracts';

let n = 0;

for (const artifact of fs.readdirSync(artifactsDir)) {
  const fullArtifactPath = path.join(artifactsDir, artifact);
  const { sourcePath: fullSourcePath } = readJSON(fullArtifactPath);
  const sourcePath = path.relative('.', fullSourcePath);

  const ignore = match.any(sourcePath, ignorePatternsSubtrees);

  if (ignore) {
    fs.unlinkSync(fullArtifactPath);
    n += 1;
  }
}

console.error(`Removed ${n} mock artifacts`);

#!/usr/bin/env node

// This script removes the build artifacts of ignored contracts.

const fs = require('fs');
const path = require('path');
const cp = require('child_process');
const match = require('micromatch');

function readJSON (path) {
  return JSON.parse(fs.readFileSync(path));
}

cp.spawnSync('npm', ['run', 'compile'], { stdio: 'inherit' });

const pkgFiles = readJSON('package.json').files;

// Get only negated patterns.
const ignorePatterns = pkgFiles
  .filter(pat => pat.startsWith('!'))
// Remove the negation part and initial slash. Makes micromatch usage more intuitive.
  .map(pat => pat.slice(1).replace(/^\//, ''));

const ignorePatternsSubtrees = ignorePatterns
// Add **/* to ignore all files contained in the directories.
  .concat(ignorePatterns.map(pat => path.join(pat, '**/*')));

const artifactsDir = 'build/contracts';

let n = 0;

for (const artifact of fs.readdirSync(artifactsDir)) {
  const fullArtifactPath = path.join(artifactsDir, artifact);
  const { sourcePath: fullSourcePath } = readJSON(fullArtifactPath);
  const sourcePath = path.relative('.', fullSourcePath);

  const ignore = match.any(sourcePath, ignorePatternsSubtrees);

  if (ignore) {
    fs.unlinkSync(fullArtifactPath);
    n += 1;
  }
}

console.error(`Removed ${n} mock artifacts`);

Fix ignoring mock artifacts on release


document.addEventListener('DOMContentLoaded', function() {
  var liMenu = document.querySelectorAll('#apps a'),
      colors = ['bluebg','purplebg','redbg','orangebg','greenbg','darkbluebg','lightbluebg','yellowbg','lightpinkbg'],
      addMailAlias = document.getElementById('add-mailalias'),
      addMaildrop = document.getElementById('add-maildrop'),
      formMailAlias = document.getElementById('form-add-mail-alias'),
      formMailDrop = document.getElementById('form-add-mail-drop');

  [].forEach.call(liMenu, function(el, i) {
    var text = el.textContent,
        splitText = text.split("");

    el.classList.add(colors[i]);
    el.querySelector('.first-letter').setAttribute('data-first-letter',splitText[0]+splitText[1]);
  });

  addMailAlias.addEventListener('click', function(){
    var inputAlias = document.querySelector('.mailalias-input');
    formMailAlias.insertBefore(inputAlias.cloneNode(true), addMailAlias);
  });

  addMaildrop.addEventListener('click', function(){
    var inputDrop = document.querySelector('.maildrop-input');
    formMailDrop.insertBefore(inputDrop.cloneNode(true), addMaildrop);
  });
});

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

  // Variables
  var liMenu = document.querySelectorAll('#apps a')
    , colors = ['bluebg','purplebg','redbg','orangebg','greenbg','darkbluebg','lightbluebg','yellowbg','lightpinkbg']
    , addMailAlias = document.getElementById('add-mailalias')
    , addMaildrop = document.getElementById('add-maildrop')
  ;

  [].forEach.call(liMenu, function(el, i) {
    // Add color class.
    el.classList.add(colors[i]);
    // Set first-letter data attribute.
    el.querySelector('.first-letter').setAttribute('data-first-letter',el.textContent.substring(0, 2));
  });

  addMailAlias.addEventListener('click', function(){
    // Clone last input.
    var inputAliasClone = document.querySelector('.mailalias-input').cloneNode(true);
    // Empty value.
    inputAliasClone.value = '';
    // Append to form-group.
    addMailAlias.parentNode.insertBefore(inputAliasClone, addMailAlias);
  });

  addMaildrop.addEventListener('click', function(){
    // Clone last input.
    var inputDropClone = document.querySelector('.maildrop-input').cloneNode(true);
    // Empty value.
    inputDropClone.value = '';
    // Append to form-group.
    addMaildrop.parentNode.insertBefore(inputDropClone, addMaildrop);
  });

});

Clean javascript. Empty value when cloning inputs.


"use strict";

var reducible = require("reducible/reducible")
var reduce = require("reducible/reduce")
var isReduced = require("reducible/is-reduced")
var end = require("reducible/end")

function delay(source, ms) {
  ms = ms || 6 // Minimum 6ms, as on less dispatch order becomes unreliable
  return reducible(function reduceDelayed(next, result) {
    var timeout = 0
    var ended = false
    reduce(source, function reduceDelaySource(value) {
      setTimeout(function delayed() {
        if (!ended) {
          timeout = timeout - ms
          result = next(value, result)
          if (isReduced(result)) {
            ended = true
            next(end)
          }
        }
      }, timeout = timeout + ms)
      return result
    })
  })
}

module.exports = delay

"use strict";

var reducible = require("reducible/reducible")
var reduce = require("reducible/reduce")
var isReduced = require("reducible/is-reduced")
var end = require("reducible/end")
var setTimeout = require("timers").setTimeout

function delay(source, ms) {
  ms = ms || 6 // Minimum 6ms, as on less dispatch order becomes unreliable
  return reducible(function reduceDelayed(next, result) {
    var timeout = 0
    var ended = false
    reduce(source, function reduceDelaySource(value) {
      setTimeout(function delayed() {
        if (!ended) {
          timeout = timeout - ms
          result = next(value, result)
          if (isReduced(result)) {
            ended = true
            next(end)
          }
        }
      }, timeout = timeout + ms)
      return result
    })
  })
}

module.exports = delay

Remove dependency on setTimeout global.


var path = require('path')
var CopyWebpackPlugin = require('copy-webpack-plugin')
var HtmlWebPackPlugin = require('html-webpack-plugin')

const production = process.env.NODE_ENV === 'production'

module.exports = {
  entry: ['./src/app.js'],
  output: {
    path: path.resolve(__dirname, 'build'),
    filename: 'app_bundle.js'
  },
  module: {
    loaders: [{
      loader: 'babel-loader',
      exclude: /node_modules/,
      test: /\.js$/,
      query: {
        presets: ['es2015', 'react', 'stage-0']
      }
    }]
  },
  plugins: [
    new CopyWebpackPlugin([{
      from: './src/static/'
    }]),
    new HtmlWebPackPlugin({
      title: 'MOOCFetcher',
      template: './src/templates/index.html',
      inject: false,
      production: production
    })
  ]
}

/*
 * USAGE:
 *
 * Make sure you use the right way to invoke webpack, because the config
 * file relies on it to determine if development or production settings
 * must be used.
 *
 * Development: webpack -d
 *
 * Production: NODE_ENV=production webpack -p
 *
 */

var path = require('path')
var webpack = require('webpack')
var CopyWebpackPlugin = require('copy-webpack-plugin')
var HtmlWebPackPlugin = require('html-webpack-plugin')

const production = process.env.NODE_ENV === 'production'

module.exports = {
  entry: ['./src/app.js'],
  output: {
    path: path.resolve(__dirname, production ? 'dist' : 'build'),
    filename: 'app_bundle.js',
    sourceMapFilename: 'app_bundle.js.map'
  },
  module: {
    loaders: [{
      loader: 'babel-loader',
      exclude: /node_modules/,
      test: /\.js$/,
      query: {
        presets: ['es2015', 'react', 'stage-0']
      }
    }]
  },
  plugins: [
    new CopyWebpackPlugin([{
      from: './src/static/'
    }]),
    new HtmlWebPackPlugin({
      title: 'MOOCFetcher',
      template: './src/templates/index.html',
      inject: false,
      production: production
    })
  ]
}

Add separate steps for production and development


Blend.defineClass('Blend.layout.container.Stacked', {
    extend: 'Blend.layout.container.Fit',
    alias: 'layout.stacked',
    cssPrefix: 'stacked',
    activeItem: null,
    init: function () {
        var me = this;
        me.callParent.apply(me, arguments);
        me.view.setActiveItem = function () {
            me.setActiveItem.apply(me, arguments);
        }
    },
    getVisibleItemIndex: function () {
        var me = this, index;
        me.activeItem = me.activeItem || (me.view.items.length !== 0 ? 0 : null);
        if (Blend.isObject(me.activeItem) && me.activeItem.$className$) {
            /**
             * Need to find the index from view items
             */
            Blend.foreach(me.view.items, function (view, idx) {
                if (view === me.activeItem) {
                    index = idx;
                    return false; // will stop the iteration;
                }
            });
        } else {
            index = me.activeItem;
        }
        return index;
    },
    setActiveItem: function (item) {
        var me = this;
        if (item !== me.activeItem) {
            me.activeItem = item;
            me.performLayout();
        }
    }
});

Blend.defineClass('Blend.layout.container.Stacked', {
    extend: 'Blend.layout.container.Fit',
    alias: 'layout.stacked',
    cssPrefix: 'stacked',
    activeItem: null,
    init: function () {
        var me = this;
        me.callParent.apply(me, arguments);
        /**
         * Install the setActiveItem to the view for convenience
         * @returns {undefined}
         */
        me.view.setActiveItem = function () {
            me.setActiveItem.apply(me, arguments);
        }
    },
    getVisibleItemIndex: function () {
        var me = this, index;
        me.activeItem = me.activeItem || (me.view.items.length !== 0 ? 0 : null);
        if (Blend.isObject(me.activeItem) && me.activeItem.$className$) {
            /**
             * Need to find the index from view items
             */
            Blend.foreach(me.view.items, function (view, idx) {
                if (view === me.activeItem) {
                    index = idx;
                    return false; // will stop the iteration;
                }
            });
        } else {
            index = me.activeItem;
        }
        return index;
    },
    setActiveItem: function (item) {
        var me = this;
        if (item !== me.activeItem) {
            me.activeItem = item;
            me.performLayout();
        }
    }
});

Install the setActiveItem function to the view for convenience


var contextMenu = require("context-menu");
var tabs = require("tabs");

exports.main = function(options, callback) {
    var contextMenuImage = contextMenu.Item({
        label: "Open Expanded Link",
        context: [contextMenu.URLContext("*.twitter.com"), contextMenu.SelectorContext("a[href]")],
        contentScript: 'self.on("click", function (node, data) {' +
                       ' self.postMessage(node.title);' +
                       '});',
        onMessage: function (data) {
            tabs.open(data);
        }
    });
};

console.log("The add-on is running.");

var contextMenu = require("context-menu");
var tabs = require("tabs");

exports.main = function(options, callback) {
    var contextMenuImage = contextMenu.Item({
        label: "Open Expanded Link",
        context: [contextMenu.URLContext("*.twitter.com"), contextMenu.SelectorContext("a[href].twitter-timeline-link")],
        contentScript: "self.on('click', function (node, data) {" +
                       " self.postMessage(node.getAttribute('data-ultimate-url'));" +
                       "});",
        onMessage: function (data) {
            tabs.open(data);
        }
    });
};

console.log("The add-on is running.");

Make the attribute retreval and the context less hackish


'use strict';

var _path = require('path');

// Get full path to nuget.exe
module.exports = function path() {
    return _path.resolve(__dirname,'../nuget.exe');
}

'use strict';

var _path = require('path');

// Get full path to nuget.exe
module.exports = function path() {
    return '"' + _path.resolve(__dirname,'../nuget.exe') + '"';
}

Allow package to work in directory with a space in its name


module.exports = {
  dropExcludedProperties: function(propertyList, originalObject) {
    return propertyList.reduce(function(incompleteObject, property) {
      incompleteObject[property] = originalObject[property];
      return incompleteObject;
    }, {});
  },

  requireNotVagueFlag: function(req) {
    if (req.body.notVague === 'true') {
      // Now the flag won't be left hanging around
      req.body = {};
    } else if (Object.getOwnPropertyNames(req.body).length === 0) {
      return new Error('Possibly too vague: use notVague=true to enforce');
    }

    return null;
  },

  requireFields: function(req, requiredFields) {
    let missingFields = requiredFields.reduce(function(mf, rf) {
      return req.body[rf] === undefined ? mf.concat(rf) : mf;
    }, []);

    // Early exit in case of missing fields
    if (missingFields.length !== 0) {
      return new Error(`Missing fields: ${missingFields}`);
    }
  }
};

module.exports = {
  dropExcludedProperties: function(propertyList, originalObject) {
    return propertyList.reduce(function(incompleteObject, property) {
      incompleteObject[property] = originalObject[property];
      return incompleteObject;
    }, {});
  },

  requireNotVagueFlag: function(req) {
    if (req.body.notVague === 'true') {
      // Now the flag won't be left hanging around
      req.body = {};
    } else if (Object.getOwnPropertyNames(req.body).length === 0) {
      return new Error('Possibly too vague: use notVague=true to enforce');
    }

    return null;
  },

  requireFields: function(req, requiredFields) {
    let missingFields = requiredFields.reduce(function(mf, rf) {
      return req.body[rf] === undefined ? mf.concat(rf) : mf;
    }, []);

    let missingFormattedFields = missingFields.join(', ');

    // Early exit in case of missing fields
    if (missingFields.length !== 0) {
      return new Error(`Missing fields: ${missingFormattedFields}`);
    }
  }
};

Format missing fields in errors more nicely


import React from 'react';

import HeaderPrimary from './header-primary';
import HeaderSecondary from './header-secondary';
// import Footer from './footer';
import MobileMenu from './mobile-menu';
import LoadingSpinner from '../utility/loading-spinner';

class MainLayout extends React.Component {
  getChildContext() {
    const { location, currentUser } = this.props;

    return {
      location,
      currentUser,
    };
  }

  render() {
    const mobileMenuLinks = ['feeds', 'browse', 'post', 'discuss', 'profile'];

    if (this.props.currentUser === undefined) {
      return <LoadingSpinner className="loading-spinner-box" />;
    }

    return (

        <HeaderPrimary />
        <HeaderSecondary />

            {this.props.children}

        {/*  */}
        <MobileMenu links={mobileMenuLinks} />

    );
  }
}

MainLayout.propTypes = {
  location: React.PropTypes.object.isRequired,
  currentUser: React.PropTypes.object,
  children: React.PropTypes.element,
};

MainLayout.childContextTypes = {
  location: React.PropTypes.object.isRequired,
  currentUser: React.PropTypes.object,
};

export default MainLayout;

import React from 'react';

import HeaderPrimary from './header-primary';
import HeaderSecondary from './header-secondary';
// import Footer from './footer';
import MobileMenu from './mobile-menu';

class MainLayout extends React.Component {
  getChildContext() {
    const { location, currentUser } = this.props;

    return {
      location,
      currentUser,
    };
  }

  render() {
    const mobileMenuLinks = ['feeds', 'browse', 'post', 'discuss', 'profile'];

    return this.props.currentUser === undefined ? null : (

        <HeaderPrimary />
        <HeaderSecondary />

            {this.props.children}

        {/*  */}
        <MobileMenu links={mobileMenuLinks} />

    );
  }
}

MainLayout.propTypes = {
  location: React.PropTypes.object.isRequired,
  currentUser: React.PropTypes.object,
  children: React.PropTypes.element,
};

MainLayout.childContextTypes = {
  location: React.PropTypes.object.isRequired,
  currentUser: React.PropTypes.object,
};

export default MainLayout;

Remove loading spinner for user subscription wait


var ActivatingListItem = Ember.Component.extend({
    tagName: 'li',
    classNameBindings: ['active'],
    active: false
});

export default ActivatingListItem;

var ActivatingListItem = Ember.Component.extend({
    tagName: 'li',
    classNameBindings: ['active'],
    active: false,

    unfocusLink: function () {
        this.$('a').blur();
    }.on('click')
});

export default ActivatingListItem;

Fix active menu state on settings navigation


(function(){
  window.addEventListener("message", function(e) {
    if (e.origin !== "https://fragdenstaat.de" && e.origin !== "http://localhost:8000") {return;}

    var iframeId = e.data[0];
    var iframe = document.getElementById(iframeId);
    var data      = e.data[2];
    iframe.style.height = data + 'px';
  }, false)
}())

(function(){
  window.addEventListener("message", function(e) {
    if (e.origin !== "https://media.fragdenstaat.de" && e.origin !== "https://fragdenstaat.de" && e.origin !== "http://localhost:8000") {return;}

    var iframeId = e.data[0];
    var iframe = document.getElementById(iframeId);
    var data      = e.data[2];
    iframe.style.height = data + 'px';
  }, false)
}())

Add new domain to message origin checking


'use strict';

module.exports = function(app) {
  app.controller('DashboardController', ['$rootScope', '$scope', 'GameService', 'UserService', 'FriendService', 'StatsService', function($rs, $scope, gameService, userService, friendService, statsService) {

    const ctrl = this;
    $scope.stats = statsService.data;
    $scope.friendsData = friendService.data;
    ctrl.user = userService.data.user;
    $scope.leaderboard = [];

    ctrl.init = function() {
      statsService.getByDocOrUserId(ctrl.user._id);
      friendService.getAllFriends(ctrl.user.email)
        .then(() => {
          $scope.leaderboard = $scope.friendsData.friends.sort((a, b) => {
            return a.stats.handicapActual > b.stats.handicapActual;
          });
          $scope.$digest();
        });
    };

    ctrl.init();
  }]);
};

'use strict';

module.exports = function(app) {
  app.controller('DashboardController', ['$rootScope', '$scope', 'GameService', 'UserService', 'FriendService', 'StatsService', function($rs, $scope, gameService, userService, friendService, statsService) {

    const ctrl = this;
    $scope.stats = statsService.data;
    $scope.friendsData = friendService.data;
    $scope.statsData = statsService.data;
    ctrl.user = userService.data.user;
    $scope.leaderboard = [];

    ctrl.init = function() {
      statsService.getByDocOrUserId(ctrl.user._id);
      friendService.getAllFriends(ctrl.user.email)
        .then(() => {
          statsService.getByDocOrUserId(ctrl.user._id)
            .then(() => {
              //TODO: consider attaching user stats data onto user object in userService
              ctrl.user.stats = $scope.statsData.userStats;
              $scope.leaderboard = $scope.friendsData.friends;
              $scope.leaderboard.push(ctrl.user);
              $scope.leaderboard.sort((a, b) => {
                return a.stats.handicapActual > b.stats.handicapActual;
              });
              $scope.$digest();
            });
        });
    };

    ctrl.init();
  }]);
};

Add user to leaderboard array before it is sorted by lowest handicap.


$(document).ready(function () {

  var sessionDefaultDuration = 300;
  var sessionDurationElement = $('#session-duration');
  var sessionDurationTimepickerElement = $('#session-duration-timepicker');

  sessionDurationElement.val(sessionDefaultDuration);

  sessionDurationTimepickerElement.timepicker({
    defaultTime: [Math.floor(sessionDefaultDuration / 3600), Math.floor(sessionDefaultDuration / 60 % 60)].join(':'),
    minuteStep: 5,
    showMeridian: false
  }).on('changeTime.timepicker', function (event) {
    sessionDurationElement.val(event.time.hours * 3600 + event.time.minutes * 60);
  });
});

$(document).ready(function () {

  var sessionDefaultDuration = 900;
  var sessionDurationElement = $('#session-duration');
  var sessionDurationTimepickerElement = $('#session-duration-timepicker');

  sessionDurationElement.val(sessionDefaultDuration);

  sessionDurationTimepickerElement.timepicker({
    defaultTime: [Math.floor(sessionDefaultDuration / 3600), Math.floor(sessionDefaultDuration / 60 % 60)].join(':'),
    minuteStep: 5,
    showMeridian: false
  }).on('changeTime.timepicker', function (event) {
    sessionDurationElement.val(event.time.hours * 3600 + event.time.minutes * 60);
  });
});

Set session default duration to 15 minutes


"use strict";

module.exports = function getPropertyDescriptor(object, property) {
    var proto = object;
    var descriptor;

    while (proto && !(descriptor = Object.getOwnPropertyDescriptor(proto, property))) {
        proto = Object.getPrototypeOf(proto);
    }
    return descriptor;
};

"use strict";

module.exports = function getPropertyDescriptor(object, property) {
    var proto = object;
    var descriptor;
    var isOwn = object && Object.getOwnPropertyDescriptor(object, property);

    while (proto && !(descriptor = Object.getOwnPropertyDescriptor(proto, property))) {
        proto = Object.getPrototypeOf(proto);
    }

    if (descriptor) {
        descriptor.isOwn = isOwn;
    }

    return descriptor;
};

Add custom prop isOwn to descriptor


export function initialize(container, application) {
  application.inject('service:head-tags', 'router', 'router:main');
}

export default {
  name: 'head-tags',
  initialize: initialize
};

import { instanceInitializer } from '../instance-initializers/head-tags';

export function initialize(registry, application) {
  application.inject('service:head-tags', 'router', 'router:main');

  // Ember >= 1.12
  if (application.instanceInitializer) {
    return;
  }

  // Ember < 1.12
  instanceInitializer(registry, application);
}

export default {
  name: 'head-tags',
  initialize: initialize
};

Make instance initializer backwards compatible


import React from 'react';
import {Decorator as Cerebral} from 'cerebral-view-react';
import styles from './styles.css';

const boilerplates = {
  'react': 'React',
  'cerebral': 'Cerebral',
  'redux': 'Redux',
  'cycle': 'Cycle JS',
  'vue': 'Vue JS'
};

@Cerebral()
class Boilerplates extends React.Component {
  render() {
    return (
      <div className={styles.wrapper}>
        {Object.keys(boilerplates).map((key, index) => {
          return (
            <div
              className={styles.boilerplate}
              key={index}
              onClick={() => this.props.signals.bin.boilerplateClicked({name: key})}>
              {boilerplates[key]}
            </div>
          );
        })}
      </div>
    );
  }
}

 export default Boilerplates;

import React from 'react';
import {Decorator as Cerebral} from 'cerebral-view-react';
import styles from './styles.css';

const boilerplates = {
  'react': 'React',
  'cerebral': 'Cerebral',
  'redux': 'Redux',
  'cycle': 'Cycle JS',
  'vue': 'Vue JS',
  'angular': 'Angular'
};

@Cerebral()
class Boilerplates extends React.Component {
  render() {
    return (
      <div className={styles.wrapper}>
        {Object.keys(boilerplates).map((key, index) => {
          return (
            <div
              className={styles.boilerplate}
              key={index}
              onClick={() => this.props.signals.bin.boilerplateClicked({name: key})}>
              {boilerplates[key]}
            </div>
          );
        })}
      </div>
    );
  }
}

 export default Boilerplates;

Revert "Revert "Added angular boilerplate""


var EverestConfig = {
	endpoint: {

		base: "http://everest-build:8082" /*,
		patient: 	// show ability to extend
		*/
	}
};

var EverestConfig = {
	endpoint: {

		base: "http://healthcare-demo:8081" /*,
		patient: 	// show ability to extend
		*/
	}
};

Update root URL for REST services...


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

function authenticate(username, password) {
    console.log('Authenticating as ' + username);
    return true;
}
container.sasl_server_mechanisms.enable_plain(authenticate);
var server = container.listen({'port':process.env.LISTENPORT, 'require_sasl': true});
console.log('Listening on port ' + process.env.LISTENPORT);
container.on('connection_open', function (context) {
    var authenticatedIdentity = { 'sub' : context.connection.sasl_transport.username };
    var properties = context.connection.local.open.properties || {};
    properties["authenticated-identity"] = authenticatedIdentity;
    context.connection.local.open.properties = properties;
    context.connection.close();
});
container.on('disconnected', function (context) {
});

Add handler for disconnect to silence log


define([
  'extensions/collections/collection'
],
function (Collection) {
  return Collection.extend({
    comparator: 'title',

    alphabetise: function (filter) {
      var groups = { count: 0 };

      filter = filter || {};

      this.each(function (model) {
        var title = model.get('title').toUpperCase(),
            department = model.get('department') || { title: '', abbr: '' },
            textFilter = (filter.text || '').toUpperCase();

        if (!textFilter || title.indexOf(textFilter) > -1 || department.abbr.toUpperCase().indexOf(textFilter) > -1 || department.title.toUpperCase().indexOf(textFilter) > -1) {
          var key = title.substr(0, 1);
          groups[key] = groups[key] || [];
          groups[key].push(model.toJSON());
          groups.count++;
        }
      });
      return groups;
    },
    filterDashboards: function () {
      var types = _.isArray(arguments[0]) ? arguments[0] : _.toArray(arguments);
      return _.map(this.filter(function (service) {
        return types.indexOf(service.get('dashboard-type')) > -1;
      }), function (m) { return m.toJSON(); });
    }
  }, {
    SERVICES: ['transaction', 'high-volume-transaction', 'other', 'service-group']
  });
});

define([
  'extensions/collections/collection'
],
function (Collection) {
  return Collection.extend({
    comparator: 'title',

    alphabetise: function (filter) {
      var groups = { count: 0 };

      filter = filter || {};
      var textFilter = (filter.text || '').toUpperCase();

      var filteredDashboards = this.filter(function (dashboard) {
        var title = dashboard.get('title').toUpperCase(),
            department = dashboard.get('department') || { title: '', abbr: '' };

        // Remove the dashboard from the list if it doesn't match the text filter
        var textSearchFields = [title, department.abbr.toUpperCase(), department.title.toUpperCase()];
        if (textFilter && textSearchFields.join(' ').indexOf(textFilter) === -1) {
          return false;
        }

        return true;
      });

      _.each(filteredDashboards, function (model) {
        var key = model.get('title').toUpperCase().substr(0, 1);
        groups[key] = groups[key] || [];
        groups[key].push(model.toJSON());
        groups.count++;
      });

      return groups;
    },

    filterDashboards: function () {
      var types = _.isArray(arguments[0]) ? arguments[0] : _.toArray(arguments);
      return _.map(this.filter(function (service) {
        return types.indexOf(service.get('dashboard-type')) > -1;
      }), function (m) { return m.toJSON(); });
    }
  }, {
    SERVICES: ['transaction', 'high-volume-transaction', 'other', 'service-group']
  });
});

Refactor dashboard collection for multiple filters


var Promise = require('bluebird'),
    request = require('request');

function rp(options) {
        var statusCodes = {
        'GET' : [200],
        'HEAD' : [200],
        'PUT' : [200, 201],
        'POST' : [200, 201],
        'DELETE' : [200, 201]
    }, c = {}, i;
    if (typeof options === 'string') {
        c.uri = options;
        c.method = 'GET';
    }
    if (typeof options === 'object') {
        for (i in options) {
            if (options.hasOwnProperty(i)) {
                c[i] = options[i];
            }
        }
    }
    c.method = c.method || 'GET';
    return new Promise(function (resolve, reject) {
        request(c, function (error, response, body) {
            if (error) {
                reject(error);
            } else if (c.simple && (statusCodes[c.method].indexOf(response.statusCode) > -1)) {
                reject(response.statusCode);
            } else {
                if (c.transform && typeof c.transform === 'function') {
                    resolve(c.transform(body));
                } else {
                    resolve(body);
                }
            }
        });
    });
}

module.exports = function (a) {
    if (a) return rp(a);
};

var Promise = require('bluebird'),
    request = require('request');

function rp(options) {
        var statusCodes = {
        'GET' : [200],
        'HEAD' : [200],
        'PUT' : [200, 201],
        'POST' : [200, 201],
        'DELETE' : [200, 201]
    }, c = {simple: true}, i;
    if (typeof options === 'string') {
        c.uri = options;
        c.method = 'GET';
    }
    if (typeof options === 'object') {
        for (i in options) {
            if (options.hasOwnProperty(i)) {
                c[i] = options[i];
            }
        }
    }
    c.method = c.method || 'GET';
    return new Promise(function (resolve, reject) {
        request(c, function (error, response, body) {
            if (error) {
                reject(error);
            } else if (c.simple && (statusCodes[c.method].indexOf(response.statusCode) > -1)) {
                reject(response.statusCode);
            } else {
                if (c.transform && typeof c.transform === 'function') {
                    resolve(c.transform(body));
                } else {
                    resolve(body);
                }
            }
        });
    });
}

module.exports = function (a) {
    if (a) return rp(a);
};

Make simple option true by default as specified in README


/* eslint-env browser */

;(function() {
  try {
    const onMessage = ({ data }) => {
      if (!data.wappalyzer) {
        return
      }

      const { technologies } = data.wappalyzer || {}

      removeEventListener('message', onMessage)

      postMessage({
        wappalyzer: {
          js: technologies.reduce((technologies, { name, chains }) => {
            chains.forEach((chain) => {
              const value = chain
                .split('.')
                .reduce(
                  (value, method) =>
                    value && value.hasOwnProperty(method)
                      ? value[method]
                      : undefined,
                  window
                )

              if (value !== undefined) {
                technologies.push({
                  name,
                  chain,
                  value:
                    typeof value === 'string' || typeof value === 'number'
                      ? value
                      : !!value
                })
              }
            })

            return technologies
          }, [])
        }
      })
    }

    addEventListener('message', onMessage)
  } catch (e) {
    // Fail quietly
  }
})()

/* eslint-env browser */

;(function() {
  try {
    const onMessage = ({ data }) => {
      if (!data.wappalyzer) {
        return
      }

      const { technologies } = data.wappalyzer || {}

      removeEventListener('message', onMessage)

      postMessage({
        wappalyzer: {
          js: technologies.reduce((technologies, { name, chains }) => {
            chains.forEach((chain) => {
              const value = chain
                .split('.')
                .reduce(
                  (value, method) =>
                    value && value instanceof Object && value.hasOwnProperty(method)
                      ? value[method]
                      : undefined,
                  window
                )

              if (value !== undefined) {
                technologies.push({
                  name,
                  chain,
                  value:
                    typeof value === 'string' || typeof value === 'number'
                      ? value
                      : !!value
                })
              }
            })

            return technologies
          }, [])
        }
      })
    }

    addEventListener('message', onMessage)
  } catch (e) {
    // Fail quietly
  }
})()

Fix error in console need to check type before call hasOwnProperty


import React from 'react';

import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
import Button from 'react-bootstrap/Button';

/**
 * {@link RawTripData} defined originally in `ViewTrips/save-trip-modal.js`.
 */

/**
 * Component that opens the edit trip modal upon click.
 *
 * @property {Object} props These are the props for this component:
 * @property {string} props.tripId The document id associated with the trip.
 * @property {RawTripData} props.tripFormData Re-package trip document
 *     data to pass to SaveTripModal when filling out form input default values.
 * @property {Function} props.handleEditTrip Event handler responsible for
 *     displaying the edit trip modal.
 */
const EditTripButton = (props) => {
  return (
    <Button
      type='button'
      variant='link'
      onClick={() => props.handleEditTrip(props.tripId, props.formattedTripData)}
    >
      <FontAwesomeIcon icon='edit' className='fa-icon'/>
    </Button>
  );
}

export default EditTripButton;

import React from 'react';

import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
import Button from 'react-bootstrap/Button';

/**
 * {@link TripData} defined originally in `ViewTrips/trip.js`.
 */

/**
 * Component that opens the edit trip modal upon click.
 *
 * @property {Object} props These are the props for this component:
 * @property {string} props.tripId The document id associated with the trip.
 * @property {TripData} props.tripData The current trip document data.
 * @property {Function} props.handleEditTrip Event handler responsible for
 *     displaying the edit trip modal.
 */
const EditTripButton = (props) => {
  return (
    <Button
      type='button'
      variant='link'
      onClick={() => props.handleEditTrip(props.tripId, props.tripData)}
    >
      <FontAwesomeIcon icon='edit' className='fa-icon'/>
    </Button>
  );
}

export default EditTripButton;

Update handleEditTrip to take prop tripData rather than rawTripData.


Template.notifications.helpers({
  notifications: function() {
    return Notifications.find({ userId: Meteor.userId() });
  },
  notificationCount: function() {
    return Notifications.find({ userId: Meteor.userId() }).count();
  }
});

Template.notifications.events({

});

Template.notifications.helpers({
  notifications: function() {
    return Notifications.find({ userId: Meteor.userId() });
  },
  notificationCount: function() {
    return Notifications.find({ userId: Meteor.userId() }).count();
  }
});

Template.notifications.events({
  'click #accept-button': function (e) {
    var self = this;

    var request = {
      userId: self.userId,
      requestingUserId: self.requestingUserId,
      requestId: self.extraFields.requestId,
      projectId: self.extraFields.projectId,
      notificationId: self._id
    };

    Meteor.call('addUserToProject', request, function(err, result) {
      if (err) {
        console.log(err.reason);
      }
    });
  }
});

Add click event to accept request


import { isWebGLSupported } from './core/utils';
import CanvasRenderer from './core/renderers/canvas/CanvasRenderer';
import WebGLRenderer from './core/renderers/webgl/WebGLRenderer';
import settings from './core/settings';
import { SCALE_MODES } from './core/const';

export default class VisualServer {
    constructor() {
        this.is_initialized = false;

        this.renderer = null;
    }

    init(config) {
        if (this.is_initialized) {
            return;
        }
        this.is_initialized = true;

        if (!config.force_canvas && isWebGLSupported()) {
            this.renderer = new WebGLRenderer(config);
        }
        else {
            this.renderer = new CanvasRenderer(config);
        }

        if (config.scale_mode === 'linear') {
            settings.SCALE_MODE = SCALE_MODES.LINEAR;
        }
        else {
            settings.SCALE_MODE = SCALE_MODES.NEAREST;
        }
    }
    render(viewport) {
        this.renderer.render(viewport, undefined, true, undefined, true);
    }
}

import { isWebGLSupported } from './core/utils';
import CanvasRenderer from './core/renderers/canvas/CanvasRenderer';
import WebGLRenderer from './core/renderers/webgl/WebGLRenderer';
import settings from './core/settings';
import { SCALE_MODES } from './core/const';

export default class VisualServer {
    constructor() {
        this.is_initialized = false;

        this.renderer = null;
    }

    init(config) {
        if (this.is_initialized) {
            return;
        }
        this.is_initialized = true;

        if (config.scale_mode === 'linear') {
            settings.SCALE_MODE = SCALE_MODES.LINEAR;
        }
        else {
            settings.SCALE_MODE = SCALE_MODES.NEAREST;
        }

        if (!config.force_canvas && isWebGLSupported()) {
            this.renderer = new WebGLRenderer(config);
        }
        else {
            this.renderer = new CanvasRenderer(config);
        }
    }
    render(viewport) {
        this.renderer.render(viewport, undefined, true, undefined, true);
    }
}

Update graphic settings before renderer is created


// karma.conf.js
module.exports = function(config) {
  config.set({
    basePath : '../../',
    frameworks: ['jasmine'],
    files : [
      'bower_components/underscore/underscore.js',
      'bower_components/angular/angular.js',
      'bower_components/angular-cookies/angular-cookies.js',
      'bower_components/angular-route/angular-route.js',
      'bower_components/angular-sanitize/angular-sanitize.js',
      'bower_components/angular-mocks/angular-mocks.js',
      'bower_components/angular-toastr/dist/angular-toastr.min.js',
      'bower_components/angular-bootstrap/ui-bootstrap-tpls.min.js',
      'bower_components/moment/min/moment.min.js',
      'bower_components/highlightjs/highlight.pack.js',
      'js/**/*.js',
      'test/karma/**/*.js'
    ],
    reporters: ['junit', 'coverage', 'dots'],
    coverageReporter: {
      type: 'html',
      dir: 'build/coverage/'
    },
    preprocessors: {
      'js/**/*.js': ['coverage']
    },
    junitReporter: {
      outputFile: 'build/karma/test-results.xml'
    },
    port: 8876,
    colors: true,
    logLevel: config.LOG_INFO,
    autoWatch: true,
    browsers: ['PhantomJS'],
    captureTimeout: 60000,
    singleRun: true
  });
};

// karma.conf.js
module.exports = function(config) {
  config.set({
    basePath : '../../',
    frameworks: ['jasmine'],
    files : [
      'bower_components/underscore/underscore.js',
      'bower_components/angular/angular.js',
      'bower_components/angular-bootstrap/ui-bootstrap-tpls.min.js',
      'bower_components/angular-cookies/angular-cookies.js',
      'bower_components/angular-mocks/angular-mocks.js',
      'bower_components/angular-moment/angular-moment.js',
      'bower_components/angular-route/angular-route.js',
      'bower_components/angular-sanitize/angular-sanitize.js',
      'bower_components/angular-toastr/dist/angular-toastr.min.js',
      'bower_components/moment/min/moment.min.js',
      'bower_components/highlightjs/highlight.pack.js',
      'js/**/*.js',
      'test/karma/**/*.js'
    ],
    reporters: ['junit', 'coverage', 'dots'],
    coverageReporter: {
      type: 'html',
      dir: 'build/coverage/'
    },
    preprocessors: {
      'js/**/*.js': ['coverage']
    },
    junitReporter: {
      outputFile: 'build/karma/test-results.xml'
    },
    port: 8876,
    colors: true,
    logLevel: config.LOG_INFO,
    autoWatch: true,
    browsers: ['PhantomJS'],
    captureTimeout: 60000,
    singleRun: true
  });
};

Add angular-moment dependency to karma


ManageIQ.angular.app.component('miqButton', {
  bindings: {
    name: '@',
    enabled: '<',
    enabledTitle: '@?',
    disabledTitle: '@?',
    primary: '<?',
    xs: '<?',
    onClick: '&',
  },
  controllerAs: 'vm',
  controller: function() {
    this.buttonClicked = function(event) {
      if (this.enabled) {
        this.onClick();
      }
      event.preventDefault();
    };
    this.setTitle = function() {
      if (this.enabledTitle || this.disabledTitle) {
        this.title = this.enabled ? this.enabledTitle : this.disabledTitle;
      }
    };
    this.$onInit = function() {
      this.setTitle();
    };
    this.$onChanges = function(changes) {
      if (changes.enabled) {
        this.enabled = changes.enabled.currentValue;
        this.setTitle();
      }
    };
  },
  template: [
    '<button',
    'controllerAs="vm"',
    'ng-class="{btn: true, \'btn-primary\': vm.primary, \'btn-xs\': vm.xs, \'btn-default\': !vm.primary, disabled: !vm.enabled}"',
    'ng-click="vm.buttonClicked($event)"',
    'ng-attr-title="{{vm.title}}"',
    'ng-attr-alt="{{vm.title}}">',
    '{{vm.name}}',
    '</button>'
  ].join("\n")
});

ManageIQ.angular.app.component('miqButton', {
  bindings: {
    name: '@',
    enabled: '<',
    enabledTitle: '@?',
    disabledTitle: '@?',
    primary: '<?',
    xs: '<?',
    onClick: '&',
  },
  controllerAs: 'vm',
  controller: function() {
    this.buttonClicked = function(event) {
      if (this.enabled) {
        this.onClick();
      }
      event.preventDefault();
      event.target.blur();
    };
    this.setTitle = function() {
      if (this.enabledTitle || this.disabledTitle) {
        this.title = this.enabled ? this.enabledTitle : this.disabledTitle;
      }
    };
    this.$onInit = function() {
      this.setTitle();
    };
    this.$onChanges = function(changes) {
      if (changes.enabled) {
        this.enabled = changes.enabled.currentValue;
        this.setTitle();
      }
    };
  },
  template: [
    '<button',
    'controllerAs="vm"',
    'ng-class="{btn: true, \'btn-primary\': vm.primary, \'btn-xs\': vm.xs, \'btn-default\': !vm.primary, disabled: !vm.enabled}"',
    'ng-click="vm.buttonClicked($event)"',
    'ng-attr-title="{{vm.title}}"',
    'ng-attr-alt="{{vm.title}}">',
    '{{vm.name}}',
    '</button>'
  ].join("\n")
});

Remove the sustained focus on the button after a click action


import RpmMeter, { PULSE_EVENT } from "spin-bike-rpm-meter";

let rpmMeter = new RpmMeter();

rpmMeter.start().then((stop) => {
	console.log(stop);

	rpmMeter.on(PULSE_EVENT, (params) => {
		console.log(params);

	});
});

import RpmMeter, { PULSE_EVENT } from "spin-bike-rpm-meter";

const rpmMeter = new RpmMeter();

// Start listening to the "pulses". Asks for permission to the microphone. Run start() before
// starting to cycle on the spin bike.
rpmMeter.start().then((stop) => {
	console.log('Starting listening to pulses...');

	// After each revolution on the bike, a PULSE_EVENT is emitted containing some useful data about it.
	rpmMeter.on(PULSE_EVENT, (pulseData) => {
		console.log(pulseData);

		// This is how the pulseData might look
		//pulseData = {
		//	'timestamp': 1454060831,
		//	'secondsBetweenPulses': 0.8,
		//	'rpm': 75
		//};

	});

	// Stop listening to the "pulses" after 10 seconds.
	setTimeout(() => {
		stop();

		console.log('Stopped listening to pulses...');
	}, 10000);
});

Update example with comments, stop after 10 secs


/*
 * HomePage
 *
 * This is the first thing users see of our App, at the '/' route
 *
 * NOTE: while this component should technically be a stateless functional
 * component (SFC), hot reloading does not currently support SFCs. If hot
 * reloading is not a neccessity for you then you can refactor it and remove
 * the linting exception.
 */

import React from 'react';
import { connect } from 'react-redux';
import { createStructuredSelector } from 'reselect';
import { selectPlugins } from 'containers/App/selectors';
import LeftMenu from 'containers/LeftMenu';
import Header from 'components/Header/index';
import Content from 'containers/Content';
import styles from './syles.scss';

export class HomePage extends React.Component { // eslint-disable-line react/prefer-stateless-function
  render() {
    return (

        <LeftMenu {...this.props}></LeftMenu>

          <Content {...this.props}>

                Welcome to your Strapi application admin panel!

          </Content>

    );
  }
}

HomePage.propTypes = {
  plugins: React.PropTypes.object,
};

const mapStateToProps = createStructuredSelector({
  plugins: selectPlugins(),
});

function mapDispatchToProps(dispatch) {
  return {
    dispatch,
  };
}

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

/*
 * HomePage
 *
 * This is the first thing users see of our App, at the '/' route
 *
 * NOTE: while this component should technically be a stateless functional
 * component (SFC), hot reloading does not currently support SFCs. If hot
 * reloading is not a neccessity for you then you can refactor it and remove
 * the linting exception.
 */

import React from 'react';
import { connect } from 'react-redux';
import { createStructuredSelector } from 'reselect';
import { selectPlugins } from 'containers/App/selectors';
import LeftMenu from 'containers/LeftMenu';
import Header from 'components/Header/index';
import Content from 'containers/Content';
import styles from './syles.scss';

export class HomePage extends React.Component { // eslint-disable-line react/prefer-stateless-function
  render() {
    return (

        <LeftMenu {...this.props}></LeftMenu>

          <Content {...this.props}>

          </Content>

    );
  }
}

HomePage.propTypes = {
  plugins: React.PropTypes.object,
};

const mapStateToProps = createStructuredSelector({
  plugins: selectPlugins(),
});

function mapDispatchToProps(dispatch) {
  return {
    dispatch,
  };
}

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

Remove admin home page text


'use strict';

// CartsCheckout controller
var cartsApp = angular.module('carts');

cartsApp.controller('CartCheckoutController', ['$scope', '$http', '$location', 'Authentication',
	function($scope, $http, $location, Authentication) {

		//$scope.authentication = Authentication;
		$scope.user = Authentication.user;

		// If user is not signed in then redirect back home
		if (!$scope.user) $location.path('/');

		$scope.completeTransaction = function() {
			var params = $location.search();
			$http.post('/cart/checkout/complete/transaction', params).success(function (response) {
				$location.path('/board/posts');
				console.log('Success Paid!');
			});
		};

		$scope.cancelTransaction = function() {
			$http.post('/cart/checkout/cancel/transaction', {status: 'cancelled'}).success(function (response) {
				$location.path('/board/posts');
				console.log('Cancel Transaction!');
			});
		};
	}
]);

'use strict';

// CartsCheckout controller
var cartsApp = angular.module('carts');

cartsApp.controller('CartCheckoutController', ['$scope', '$http', '$location', 'Authentication',
	function($scope, $http, $location, Authentication) {

		//$scope.authentication = Authentication;
		$scope.user = Authentication.user;

		// If user is not signed in then redirect back home
		if (!$scope.user) $location.path('/');

		$scope.completeTransaction = function() {
			var params = $location.search();
			$http.post('/cart/checkout/complete/transaction', params);
			$location.path('/board/posts');
			console.log('Success Paid!');
		};

		$scope.cancelTransaction = function() {
			$http.post('/cart/checkout/cancel/transaction', {status: 'cancelled'});
			$location.path('/board/posts');
			console.log('Cancel Transaction!');
		};
	}
]);

Fix Cancel and Complete Transaction


// Copyright (c) 2014 Titanium I.T. LLC. All rights reserved. For license, see "README" or "LICENSE" file.
(function() {
	"use strict";

	// Uncomment and modify the following list to cause the build to fail unless these browsers are tested.

	module.exports = [
		//"IE 8.0.0 (Windows XP)",
		//"IE 9.0.0 (Windows 7)",
		//"Firefox 23.0.0 (Mac OS X 10.8)",
		//"Chrome 29.0.1547 (Mac OS X 10.8.5)",
		//"Safari 6.0.5 (Mac OS X 10.8.5)",
		//"Mobile Safari 6.0.0 (iOS 6.1)"
	];

}());

// Copyright (c) 2014 Titanium I.T. LLC. All rights reserved. For license, see "README" or "LICENSE" file.
(function() {
	"use strict";

	// Uncomment and modify the following list to cause the build to fail unless these browsers are tested.

	module.exports = [
		"IE 9.0.0 (Windows 7)",
		"IE 11.0.0 (Windows 7)",
		"Firefox 36.0.0 (Mac OS X 10.10)",
		"Chrome 41.0.2272 (Mac OS X 10.10.2)",
		"Safari 8.0.4 (Mac OS X 10.10.2)",
		"Mobile Safari 8.0.0 (iOS 10.10.2)"
	];

}());

Build fails unless all browsers are tested


import React from 'react'
import { inject, observer } from 'mobx-react'
import SortIcon from 'material-ui-icons/Sort'
import IconButton from 'material-ui/IconButton'

const iconStyle = {
  width: '2rem',
  height: '2rem'
}

@inject('arrangeStore')
@observer
export default class Tools extends React.Component {
  render () {
    const { arrangeStore: { sortTabs } } = this.props
    return (
      <div style={{
        display: 'flex'
      }}>
        <IconButton
          style={iconStyle}
          onClick={sortTabs}>
          <SortIcon />
        </IconButton>
      </div>
    )
  }
}

import React from 'react'
import { inject, observer } from 'mobx-react'
import Tooltip from 'material-ui/Tooltip'
import SortIcon from 'material-ui-icons/Sort'
import IconButton from 'material-ui/IconButton'

const iconStyle = {
  width: '2rem',
  height: '2rem'
}

@inject('arrangeStore')
@observer
export default class Tools extends React.Component {
  render () {
    const { arrangeStore: { sortTabs } } = this.props
    return (
      <div style={{
        display: 'flex'
      }}>
        <Tooltip title='Sort Tabs' placement='left'>
          <IconButton
            style={iconStyle}
            onClick={sortTabs}>
            <SortIcon />
          </IconButton>
        </Tooltip>
      </div>
    )
  }
}

Add Tooltip for sort icon


Meteor.methods({
  addProject: function(project) {
    project.createdAt = new Date();
    Projects.insert(project);
  }
});

Meteor.methods({
  addProject: function(project) {
    _.extend(project, {createdAt: new Date()});
    Projects.insert(project);
  }
});

Use underscore .extend() to add fields instead of being a moron


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

// Static files directory
app.use(express.static('public'));

// Request handlers
app.get('/', function(req, res) {
    res.redirect(307, '/notman');
});

var pages = ['notman', 'greenhouse'];

app.get('/:identifier', function(req, res) {
	if (pages.indexOf(req.params.identifier) != -1) {
		res.sendfile("public/client.html")
	} else {
		res.status(404).send('Not found');
	}
});

var port = process.env.port || 3000;
app.listen(port);

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

// Static files directory
app.use(express.static('public'));

// Request handlers
app.get('/', function(req, res) {
    res.redirect(307, '/notman');
});

var pages = ['notman', 'greenhouse'];

app.get('/:identifier', function(req, res) {
	if (pages.indexOf(req.params.identifier) != -1) {
		res.sendfile("public/client.html")
	} else {
		res.status(404).send('Not found');
	}
});

var port = process.env.port || 3000;
app.listen(port, function() {
	console.log("smartpaces-server is listening on port", port);
});

Add console log for debug.


/* jshint node: true, browser: false */
'use strict';

// CREATE HTTP SERVER AND PROXY

var app     = require('express')();
var proxy   = require('http-proxy').createProxyServer({});

proxy.on('error', function(e) {
    console.error(e);
}); //ignore errors

// LOAD CONFIGURATION

var oneDay   = 86400000;
var cacheTag = Math.random() * 0x80000000 | 0;

app.use(require('morgan')('dev'));
app.use(require('compression')());

app.set('port', process.env.PORT || 2000);
app.use('/favicon.ico', require('serve-static')(__dirname + '/favicon.ico', { maxAge:    oneDay }));

// Configure routes

app.use('/app',   require('serve-static')(__dirname + '/app'));
app.all('/app/*', function(req, res) { res.status(404).send(); } );
app.all('/api/*', function(req, res) { proxy.web(req, res, { target: 'https://api.cbd.int:443', secure: false } ); } );

// Configure index.html

app.get('/*', function (req, res) {
    res.cookie('cachetag', cacheTag);
    res.sendFile(__dirname + '/app/index.html');
});

// Start server

app.listen(app.get('port'), function () {
	console.log('Server listening on %j', this.address());
});

/* jshint node: true, browser: false */
'use strict';

// CREATE HTTP SERVER AND PROXY

var app     = require('express')();
var proxy   = require('http-proxy').createProxyServer({});

proxy.on('error', function(e) {
    console.error(e);
}); //ignore errors

// LOAD CONFIGURATION

var oneDay   = 86400000;
var cacheTag = Math.random() * 0x80000000 | 0;

app.use(require('morgan')('dev'));

app.set('port', process.env.PORT || 2000);
app.use('/favicon.ico', require('serve-static')(__dirname + '/favicon.ico', { maxAge:    oneDay }));

// Configure routes

app.use('/app',   require('serve-static')(__dirname + '/app'));
app.all('/app/*', function(req, res) { res.status(404).send(); } );
app.all('/api/*', function(req, res) { proxy.web(req, res, { target: 'https://api.cbd.int:443', secure: false } ); } );

// Configure index.html

app.get('/*', function (req, res) {
    res.cookie('cachetag', cacheTag);
    res.sendFile(__dirname + '/app/index.html');
});

// Start server

app.listen(app.get('port'), function () {
	console.log('Server listening on %j', this.address());
});

Remove compression (done at NGINX level)


tabris.load(function() {

  if (device.platform === "windows") {

    tabris.registerWidget("Drawer", {

      _type: "tabris.Drawer",

      _supportsChildren: true,

      _create: function() {
        tabris.ui._setCurrentDrawer(this);
        this._super("_create", arguments);
        this._setParent(tabris.ui);
        return this;
      },

      _properties: {
        win_displayMode: {
          type: ["choice", ["overlay", "compactOverlay"]],
          default: "overlay"
        },
        win_buttonBackground: {
          type: "color",
          default: null
        }
      },

      open: function() {
        this._nativeCall("open", {});
        return this;
      },

      close: function() {
        this._nativeCall("close", {});
        return this;
      },

      dispose: function() {
        tabris.ui._setCurrentDrawer(null);
        this._super("dispose", arguments);
      }

    });

  }

});

tabris.load(function() {

  if (device.platform === "windows") {

    tabris.registerWidget("Drawer", {

      _type: "tabris.Drawer",

      _supportsChildren: true,

      _create: function() {
        tabris.ui._setCurrentDrawer(this);
        this._super("_create", arguments);
        this._setParent(tabris.ui);
        return this;
      },

      _properties: {
        win_displayMode: {
          type: ["choice", ["overlay", "compactOverlay"]],
          default: "overlay"
        },
        win_buttonBackground: {
          type: "color",
          default: null
        },
        win_buttonTheme: {
          type: ["choice", ["light", "dark", "default"]],
          default: "default"
        }
      },

      open: function() {
        this._nativeCall("open", {});
        return this;
      },

      close: function() {
        this._nativeCall("close", {});
        return this;
      },

      dispose: function() {
        tabris.ui._setCurrentDrawer(null);
        this._super("dispose", arguments);
      }

    });

  }

});

Add support for windows-only drawer property "win_buttonTheme"


'use stirct'

var opbeat = require('opbeat').start()
var uuid = require('node-uuid')
var AWS = require('aws-sdk')
var Printer = require('ipp-printer')

var port = process.env.PORT || 3000

var s3 = new AWS.S3()
var printer = new Printer({ name: 'printbin', port: port, zeroconf: false })

printer.on('job', function (job) {
  var key = uuid.v4() + '.ps'

  console.log('processing job %d (key: %s)', job.id, key)

  job.on('end', function () {
    console.log('done reading job %d (key: %s)', job.id, key)
  })

  var params = {
    Bucket: 'watson-printbin',
    ACL: 'public-read',
    ContentType: 'application/postscript',
    StorageClass: 'REDUCED_REDUNDANCY',
    Key: key,
    Body: job
  }

  s3.upload(params, function (err, data) {
    if (err) return opbeat.captureError(err)
    console.log('done uploading job %d (key: %s)', job.id, key)
  })
})

'use stirct'

var opbeat = require('opbeat').start()
var uuid = require('node-uuid')
var AWS = require('aws-sdk')
var Printer = require('ipp-printer')

var port = process.env.PORT || 3000

var s3 = new AWS.S3()
var printer = new Printer({ name: 'printbin', port: port, zeroconf: false })

printer.on('job', function (job) {
  var key = uuid.v4() + '.ps'

  console.log('processing job %d (key: %s)', job.id, key)

  job.on('end', function () {
    console.log('done reading job %d (key: %s)', job.id, key)
  })

  var params = {
    Bucket: 'watson-printbin',
    ACL: 'public-read',
    ContentType: 'application/postscript',
    StorageClass: 'REDUCED_REDUNDANCY',
    Metadata: { name: job.name },
    Key: key,
    Body: job
  }

  s3.upload(params, function (err, data) {
    if (err) return opbeat.captureError(err)
    console.log('done uploading job %d (key: %s)', job.id, key)
  })
})

Add job document name as metadata to S3 object


/* @flow */

import type Site from '../site';

import React from 'react';
import ReactDOMServer from 'react-dom/server';

import Page from '../components/Page';
import { skipMeta, skipDirectories } from './skip-items';

export default function wrapReact(site : Site) : Site {
    return site.mapWithFilters([skipMeta, skipDirectories], (item) => {
        const DOCTYPE = '<!doctype html>';
        const html = DOCTYPE + '\n' + ReactDOMServer.renderToStaticMarkup(
            <Page item={item} />
        );
        return item.withContent(html);
    });
}

/* @flow */

import type Site from '../site';

import React from 'react';
import ReactDOMServer from 'react-dom/server';

import Page from '../components/Page';
import { skipMeta, skipDirectories } from './skip-items';

const DOCTYPE = '<!doctype html>';

export default function wrapReact(site : Site) : Site {
    return site.mapWithFilters([skipMeta, skipDirectories], (item) => {
        const html = DOCTYPE + '\n' + ReactDOMServer.renderToStaticMarkup(
            <Page item={item} />
        );
        return item.withContent(html);
    });
}

Move DOCTYPE definition out of the function


var sass = require('node-sass'),
    path = require('path'),
    fs = require('fs');

var handledBaseFolderNames = {
    'bower_components': 'bower_components',
    'node_modules': 'node_modules'
};

function customImporter (url, prev, done) {
    var baseFolderName = url.split(path.sep)[0];

    if (handledBaseFolderNames[baseFolderName]) {
        if (!endsWith(url, '.scss')) {
            url += '.scss';
        }
        return findInParentDir(url, prev, done);
    }

    return sass.NULL;
}

function endsWith(str, suffix) {
    return str.indexOf(suffix, str.length - suffix.length) !== -1;
}

function findInParentDir(relativePath, startingDirPath, done) {
    var dirToTry = path.join(startingDirPath, '..');
    var pathToTry = path.join(dirToTry, relativePath);

    fs.access(pathToTry, fs.R_OK, function(err) {
        if (err) {
            if (pathToTry === ('/' + relativePath)) {
                done(new Error('File not found: ' + relativePath));
            } else {
                return findInParentDir(relativePath, dirToTry, done);
            }
        } else {
            done({ file: pathToTry });
        }
    });
}

module.exports = customImporter;

var sass = require('node-sass'),
    path = require('path'),
    fs = require('fs');

var handledBaseFolderNames = {
    'bower_components': 'bower_components',
    'node_modules': 'node_modules'
};

function customImporter (url, prev, done) {
    var baseFolderName = url.split(path.sep)[0];

    if (handledBaseFolderNames[baseFolderName]) {
        if (!endsWith(url, '.scss')) {
            url += '.scss';
        }
        findInParentDir(url, prev, done);
    }

    return sass.NULL;
}

function endsWith(str, suffix) {
    return str.indexOf(suffix, str.length - suffix.length) !== -1;
}

function findInParentDir(relativePath, startingDirPath, done) {
    // For node modules we may want to try using require.resolve() here instead.
    var dirToTry = path.join(startingDirPath, '..');
    var pathToTry = path.join(dirToTry, relativePath);

    fs.access(pathToTry, fs.R_OK, function(err) {
        if (err) {
            if (pathToTry === ('/' + relativePath)) {
                done(new Error('File not found: ' + relativePath));
            } else {
                return findInParentDir(relativePath, dirToTry, done);
            }
        } else {
            done({ file: pathToTry });
        }
    });
}

module.exports = customImporter;

Remove unneeded return + add comment about require.resolve()