'use strict';

var expect          = require('chai').expect;
var sinon           = require('sinon');
var sinonAsPromised = require('sinon-as-promised');
var angular         = require('angular');

describe('sinon-as-promised node example', function () {

  var $timeout;
  beforeEach(angular.mock.inject(function ($rootScope, $q, _$timeout_) {
    sinonAsPromised($q);
    sinonAsPromised.setScheduler(function (fn) {
      $rootScope.$evalAsync(fn);
    });
    $timeout = _$timeout_;
  }));

  it('can create a stub that resolves', function () {
    var stub = sinon.stub().resolves('value');
    var fulfilled = false;
    stub().then(function (value) {
      fulfilled = true;
      expect(value).to.equal('value');
    });
    expect(fulfilled).to.be.false;
    $timeout.flush();
    expect(fulfilled).to.be.true;
  });

});

'use strict';

var expect          = require('chai').expect;
var sinon           = require('sinon');
var sinonAsPromised = require('sinon-as-promised');
var angular         = require('angular');

describe('sinon-as-promised angular example', function () {

  var $timeout;
  beforeEach(angular.mock.inject(function ($rootScope, $q, _$timeout_) {
    sinonAsPromised($q);
    sinonAsPromised.setScheduler(function (fn) {
      $rootScope.$evalAsync(fn);
    });
    $timeout = _$timeout_;
  }));

  it('can create a stub that resolves', function () {
    var stub = sinon.stub().resolves('value');
    var fulfilled = false;
    stub().then(function (value) {
      fulfilled = true;
      expect(value).to.equal('value');
    });
    expect(fulfilled).to.be.false;
    $timeout.flush();
    expect(fulfilled).to.be.true;
  });

});

Fix label on angular example's describe block


module.exports = function( grunt ) {

    "use strict";

    var pkg = grunt.file.readJSON( "package.json" );

    grunt.initConfig( {

        pkg: pkg,

        clean: [ "dist/" ],

        copy: {
            build: {
                options: {
                    process: function( content ) {
                        return content.replace( /@VERSION/g, pkg.version );
                    }
                },
                files: [
                    {
                        expand: true,
                        flatten: true,
                        cwd: "src/",
                        src: [ "**" ],
                        dest: "dist/"
                    }
                ]
            }
        },

        uglify: {
            options: {
                banner: "/*! <%=pkg.name %> | <%= pkg.version %> | <%= grunt.template.today('yyyy-mm-dd') %> */\n"
            },
            build: {
                files: {
                    "dist/jquery.ui.pinpad.min.js": "dist/jquery.ui.pinpad.js"
                }
            }
        },

        cssmin: {
            build: {
                src: "dist/jquery.ui.pinpad.css",
                dest: "dist/jquery.ui.pinpad.min.css"
            }
        }

    });

    grunt.loadNpmTasks( "grunt-contrib-clean" );
    grunt.loadNpmTasks( "grunt-contrib-copy" );
    grunt.loadNpmTasks( "grunt-contrib-uglify" );
    grunt.loadNpmTasks( "grunt-contrib-cssmin" );

    grunt.registerTask( "default", [ "clean", "copy", "uglify", "cssmin" ] );

};

module.exports = function( grunt ) {

    "use strict";

    var pkg = grunt.file.readJSON( "package.json" );

    grunt.initConfig( {

        pkg: pkg,

        clean: [ "dist/" ],

        copy: {
            build: {
                options: {
                    process: function( content ) {
                        return content.replace( /@VERSION/g, pkg.version );
                    }
                },
                files: [
                    {
                        expand: true,
                        flatten: true,
                        filter: "isFile",
                        cwd: "src/",
                        src: [ "**" ],
                        dest: "dist/"
                    }
                ]
            }
        },

        uglify: {
            options: {
                banner: "/*! <%=pkg.name %> | <%= pkg.version %> | <%= grunt.template.today('yyyy-mm-dd') %> */\n"
            },
            build: {
                files: {
                    "dist/jquery.ui.pinpad.min.js": "dist/jquery.ui.pinpad.js"
                }
            }
        },

        cssmin: {
            build: {
                src: "dist/jquery.ui.pinpad.css",
                dest: "dist/jquery.ui.pinpad.min.css"
            }
        }

    });

    grunt.loadNpmTasks( "grunt-contrib-clean" );
    grunt.loadNpmTasks( "grunt-contrib-copy" );
    grunt.loadNpmTasks( "grunt-contrib-uglify" );
    grunt.loadNpmTasks( "grunt-contrib-cssmin" );

    grunt.registerTask( "default", [ "clean", "copy", "uglify", "cssmin" ] );

};

Exclude directories during copy process


module.exports = function(grunt) {

    "use strict";

    var browserifyFiles = {
        "./dist/build.js": "./src/*.js"
    };

    grunt.initConfig({
        pkg: grunt.file.readJSON("package.json"),
        browserify: {
            prod: {
                files: browserifyFiles
            }
        }
    });

    grunt.loadNpmTasks("grunt-browserify");

    grunt.registerTask("build", ["browserify:prod"]);
};

module.exports = function(grunt) {

    "use strict";

    var browserifyFiles = {
        "./dist/build.js": "./src/*.js"
    };

    grunt.initConfig({
        pkg: grunt.file.readJSON("package.json"),
        browserify: {
            prod: {
                files: browserifyFiles
            }
        },
        watch: {
            scripts: {
                files: ["src/*.js"],
                tasks: ["build"]
            }
        }
    });

    grunt.loadNpmTasks("grunt-browserify");
    grunt.loadNpmTasks("grunt-contrib-watch");

    grunt.registerTask("build", ["browserify:prod"]);
};

Add settings for watch & load task


module.exports = function(grunt) {
	grunt.initConfig({
		bowerPkg: grunt.file.readJSON('bower.json'),
		karma: {
			unit: {
				configFile: 'karma.conf.js',
				singleRun: true,
				browsers: ['PhantomJS']
			}
		},
		cssmin: {
			build: {
				options: {
					banner: '/* <%= bowerPkg.name %> v<%= bowerPkg.version %> (<%= bowerPkg.homepage %>) */'
				},
				src: 'src/progress-button.css',
				dest: 'dist/progress-button.min.css'
			}
		},
		uglify: {
			options: {
				banner: '/* <%= bowerPkg.name %> v<%= bowerPkg.version %> (<%= bowerPkg.homepage %>) */\n'
			},
			build: {
				src: 'src/progress-button.js',
				dest: 'dist/progress-button.min.js'
			}
		}
	})

	grunt.loadNpmTasks('grunt-contrib-cssmin');
	grunt.loadNpmTasks('grunt-contrib-uglify')
	grunt.loadNpmTasks('grunt-karma')

	grunt.registerTask('build', ['cssmin', 'uglify'])
	grunt.registerTask('test', ['karma'])
	grunt.registerTask('default', [])
}

module.exports = function(grunt) {
	grunt.initConfig({
		bowerPkg: grunt.file.readJSON('bower.json'),
		karma: {
			unit: {
				configFile: 'karma.conf.js',
				singleRun: true,
				browsers: ['PhantomJS']
			}
		},
		cssmin: {
			build: {
				options: {
					banner: '/* <%= bowerPkg.name %> v<%= bowerPkg.version %> (<%= bowerPkg.homepage %>) */'
				},
				src: 'src/progress-button.css',
				dest: 'dist/progress-button.min.css'
			}
		},
		uglify: {
			options: {
				banner: '/* <%= bowerPkg.name %> v<%= bowerPkg.version %> (<%= bowerPkg.homepage %>) */\n'
			},
			build: {
				src: 'src/progress-button.js',
				dest: 'dist/progress-button.min.js'
			}
		}
	})

	grunt.loadNpmTasks('grunt-contrib-cssmin');
	grunt.loadNpmTasks('grunt-contrib-uglify')
	grunt.loadNpmTasks('grunt-karma')

	grunt.registerTask('build', ['cssmin', 'uglify'])
	grunt.registerTask('test', ['karma'])
	grunt.registerTask('default', ['build'])
}

Set ‘build’ as the default Grunt task


module.exports = function(grunt) {
  'use strict';

  // Project configuration.
  grunt.initConfig({
    pkg: grunt.file.readJSON('package.json'),
    jshint: {
      all: [
        'Gruntfile.js',
        'app.js',
        'lib/**/*.js',
        'routes/*.js',
        'test/*.js',
        'public/js/three-script.js'
      ]
    },
    stylus: {
      compile : {
        files : {
          'public/css/style.css' : 'public/css/*.styl'
        }
      }
    }
  });

  // Load the plugins.
  grunt.loadNpmTasks('grunt-contrib-jshint');
  grunt.loadNpmTasks('grunt-contrib-stylus');

  // Default task(s).
  grunt.registerTask('default', ['jshint', 'stylus']);
};

module.exports = function(grunt) {
  'use strict';

  // Project configuration.
  grunt.initConfig({
    pkg: grunt.file.readJSON('package.json'),
    jshint: {
      all: [
        'Gruntfile.js',
        'app.js',
        'db.js',
        'lib/**/*.js',
        'routes/*.js',
        'test/*.js',
        'public/js/three-script.js'
      ]
    },
    stylus: {
      compile : {
        files : {
          'public/css/style.css' : 'public/css/*.styl'
        }
      }
    }
  });

  // Load the plugins.
  grunt.loadNpmTasks('grunt-contrib-jshint');
  grunt.loadNpmTasks('grunt-contrib-stylus');

  // Default task(s).
  grunt.registerTask('default', ['jshint', 'stylus']);
};

Add db.js to jshint task


export function cssSupports (property, value) {
  if (CSS) {
    return CSS.supports(property, value)
  }

  return toCamelCase(property) in document.body.style
}

export function toCamelCase (cssProperty) {
  cssProperty = cssProperty
    .split('-')
    .filter(word => !!word)
    .map(word => word[0].toUpperCase() + word.substr(1))
    .join('')

  return cssProperty[0].toLowerCase() + cssProperty.substr(1)
}

export function cssSupports (property, value) {
  if (typeof CSS !== 'undefined') {
    return CSS.supports(property, value)
  }

  if (typeof document === 'undefined') {
    return false;
  }

  return toCamelCase(property) in document.body.style
}

export function toCamelCase (cssProperty) {
  cssProperty = cssProperty
    .split('-')
    .filter(word => !!word)
    .map(word => word[0].toUpperCase() + word.substr(1))
    .join('')

  return cssProperty[0].toLowerCase() + cssProperty.substr(1)
}

Modify css support check to work better with SSR


module.exports = function(grunt) {
    grunt.loadNpmTasks("grunt-mocha-test");
    grunt.loadNpmTasks("grunt-mocha-istanbul");

    var testOutputLocation = process.env.CIRCLE_TEST_REPORTS || "test_output";
    var artifactsLocation = process.env.CIRCLE_ARTIFACTS || "build_artifacts";
    grunt.initConfig({
        mochaTest: {
            test: {
                src: ["test/**/*.js"]
            },
            ci: {
                src: ["test/**/*.js"],
                options: {
                    reporter: "xunit",
                    captureFile: testOutputLocation + "/mocha/results.xml",
                    quiet: true
                }
            }
        },
        mocha_istanbul: {
            coverage: {
                src: ["test/**/*.js"],
                options: {
                    coverageFolder: artifactsLocation + "/coverage",
                    check: {
                        lines: 100,
                        statements: 100,
                        branches: 100,
                        functions: 100
                    },
                    reportFormats: ["lcov"]
                }
            }
        }
    });

    grunt.registerTask("test", ["mochaTest:test", "mocha_istanbul"]);
    grunt.registerTask("ci-test", ["mochaTest:ci", "mocha_istanbul"]);
    grunt.registerTask("default", "test");
};

module.exports = function(grunt) {
    grunt.loadNpmTasks("grunt-mocha-test");
    grunt.loadNpmTasks("grunt-mocha-istanbul");

    var testOutputLocation = process.env.CIRCLE_TEST_REPORTS || "test_output";
    var artifactsLocation = process.env.CIRCLE_ARTIFACTS || "build_artifacts";
    grunt.initConfig({
        mochaTest: {
            test: {
                src: ["test/**/*.js"]
            },
            ci: {
                src: ["test/**/*.js"],
                options: {
                    reporter: "xunit",
                    captureFile: testOutputLocation + "/mocha/results.xml",
                    quiet: true
                }
            }
        },
        mocha_istanbul: {
            coverage: {
                src: ["test/**/*.js"],
                options: {
                    coverageFolder: artifactsLocation,
                    check: {
                        lines: 100,
                        statements: 100,
                        branches: 100,
                        functions: 100
                    },
                    reportFormats: ["lcov"]
                }
            }
        }
    });

    grunt.registerTask("test", ["mochaTest:test", "mocha_istanbul"]);
    grunt.registerTask("ci-test", ["mochaTest:ci", "mocha_istanbul"]);
    grunt.registerTask("default", "test");
};

Change artifact location to suite CircleCI


module.exports = function (grunt) {

	grunt.initConfig({

		pkg: grunt.file.readJSON('package.json')
	});

	grunt.registerTask('default', []);
};

module.exports = function (grunt) {

	grunt.initConfig({

		pkg: grunt.file.readJSON('package.json'),

		clean: {
			build: {
				src: "build"
			}
		},

		copy: {
			build: {
				files: [
					{
						expand: true,
						cwd: 'source/',
						src: ['cleverlay.css', 'cleverlay.js'],
						dest: 'build/'
					}
				]
			}
		}
	});

	grunt.loadNpmTasks('grunt-contrib-clean');
	grunt.loadNpmTasks('grunt-contrib-copy');

	grunt.registerTask('default', []);
};

Add clean and copy tasks for Grunt.


/**
 * Sustainable Solutions (NZ) Ltd. 2021
 * mSupply Mobile
 */

import Realm from 'realm';

export class MedicineAdministrator extends Realm.Object {
  get displayString() {
    return `${this.code ?? ''} ${this.firstName ?? ''}${this.lastName ?? ''}`.trim();
  }
}

MedicineAdministrator.schema = {
  name: 'MedicineAdministrator',
  primaryKey: 'id',
  properties: {
    id: 'string',
    firstName: { type: 'string', optional: true },
    lastName: { type: 'string', optional: true },
    code: { type: 'string', optional: true },
    transactionBatches: {
      type: 'linkingObjects',
      objectType: 'TransactionBatch',
      property: 'medicineAdministrator',
    },
  },
};

export default MedicineAdministrator;

/**
 * Sustainable Solutions (NZ) Ltd. 2021
 * mSupply Mobile
 */

import Realm from 'realm';

export class MedicineAdministrator extends Realm.Object {
  get displayString() {
    return `${this.code ?? ''} ${this.firstName ?? ''} ${this.lastName ?? ''}`.trim();
  }
}

MedicineAdministrator.schema = {
  name: 'MedicineAdministrator',
  primaryKey: 'id',
  properties: {
    id: 'string',
    firstName: { type: 'string', optional: true },
    lastName: { type: 'string', optional: true },
    code: { type: 'string', optional: true },
    transactionBatches: {
      type: 'linkingObjects',
      objectType: 'TransactionBatch',
      property: 'medicineAdministrator',
    },
  },
};

export default MedicineAdministrator;

Add space between first and last names


/*
 * grunt-generate-configs
 * https://github.com/creynders/grunt-generate-configs
 *
 * Copyright (c) 2014 Camille Reynders
 * Licensed under the MIT license.
 */

'use strict';

var inquirer = require('inquirer');
var writeFiles = require('./lib/writeFiles');

module.exports = function(grunt){

    // Please see the Grunt documentation for more information regarding task
    // creation: http://gruntjs.com/creating-tasks

    grunt.registerTask('generate_configs', 'Splits your grunt configuration into separate files', function(){
        var done = this.async();
        var opts = {
            target : grunt.option('target') || 'config',
            type : grunt.option('type') || 'json',
            data : grunt.config.data,
            log : grunt.log.writeln
        };

        if(grunt.file.exists(opts.target)){
            inquirer.prompt(
            [
                {
                    type    : "confirm",
                    message : "A directory '" + opts.target + "' already exists, its files will be overwritten.\nAre you sure you want to continue?",
                    name    : "overwrite",
                    default : false
                }
            ], function(answers){
                if(answers.overwrite){
                    writeFiles(opts);
                    done(true);
                }else{
                    done(false);
                }
            });
        }else{
            writeFiles(opts);
            done(true);
        }
    });
};

/*
 * grunt-generate-configs
 * https://github.com/creynders/grunt-generate-configs
 *
 * Copyright (c) 2014 Camille Reynders
 * Licensed under the MIT license.
 */

'use strict';

var inquirer = require('inquirer');
var writeFiles = require('./lib/writeFiles');

module.exports = function(grunt){

    // Please see the Grunt documentation for more information regarding task
    // creation: http://gruntjs.com/creating-tasks

    grunt.registerTask('generate_configs', 'Splits your grunt configuration into separate files', function(){
        var done = this.async();
        var type;
        if(grunt.option('json')){
            type = 'json';
        }
        if(grunt.option('js')){
            type = 'js';
        }
        if(grunt.option('coffee')){
            type = 'coffee';
        }
        if(grunt.option('yaml')){
            type = 'yaml';
        }
        var type = type || grunt.option('type') || 'json';
        var opts = {
            target : grunt.option('target') || 'config',
            type : type,
            data : grunt.config.data,
            log : grunt.log.writeln
        };

        if(grunt.file.exists(opts.target)){
            inquirer.prompt(
            [
                {
                    type    : "confirm",
                    message : "A directory '" + opts.target + "' already exists, its files will be overwritten.\nAre you sure you want to continue?",
                    name    : "overwrite",
                    default : false
                }
            ], function(answers){
                if(answers.overwrite){
                    writeFiles(opts);
                    done(true);
                }else{
                    done(false);
                }
            });
        }else{
            writeFiles(opts);
            done(true);
        }
    });
};

Add js, coffee, yaml, json flags


var gulp   = require('gulp');
var jshint = require('gulp-jshint');
var jscs   = require('gulp-jscs');

gulp.task('jshint', function() {
  return gulp.src('./lib/*.js')
    .pipe(jshint())
    .pipe(jshint.reporter('jshint-stylish'));
});

gulp.task('jscs', function () {
  return gulp.src('./lib/*.js')
    .pipe(jscs());
});

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

'use strict';

var gulp   = require('gulp');
var jshint = require('gulp-jshint');
var jscs   = require('gulp-jscs');

var src = [
  './lib/*.js',
  'index.js',
  'gulpfile.js'
];

gulp.task('jshint', function() {
  return gulp.src(src)
    .pipe(jshint())
    .pipe(jshint.reporter('jshint-stylish'));
});

gulp.task('jscs', function () {
  return gulp.src(src)
    .pipe(jscs());
});

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

Update gulp file to lint more src


// grab our gulp packages
var gulp  = require("gulp");
var gutil = require("gulp-util");
var uglify = require("gulp-uglify");
var rename = require("gulp-rename");
var concat = require("gulp-concat");

gulp.task("concat", function() {
  return gulp.src(["bliss.shy.js", "bliss._.js"])
	.pipe(concat("bliss.js"))
	.pipe(gulp.dest("."));
});

gulp.task("minify", function() {
	var u = uglify();
	u.on("error", function(error) {
		console.error(error);
		u.end();
	});

	return gulp.src(["bliss.shy.js", "bliss.js"])
	.pipe(u)
	.pipe(rename({ suffix: ".min" }))
	.pipe(gulp.dest("."));
});

gulp.task("watch", function() {
	gulp.watch(["*.js", "!bliss.js", "!*.min.js"], ["concat", "minify"]);
});

gulp.task("default", gulp.series("concat", "minify"));

// grab our gulp packages
var gulp  = require("gulp");
var gutil = require("gulp-util");
var uglify = require("gulp-uglify");
var rename = require("gulp-rename");
var concat = require("gulp-concat");

gulp.task("concat", function() {
  return gulp.src(["bliss.shy.js", "bliss._.js"])
	.pipe(concat("bliss.js"))
	.pipe(gulp.dest("."));
});

gulp.task("minify", function() {
	var u = uglify();
	u.on("error", function(error) {
		console.error(error);
		u.end();
	});

	return gulp.src(["bliss.shy.js", "bliss.js"])
	.pipe(u)
	.pipe(rename({ suffix: ".min" }))
	.pipe(gulp.dest("."));
});

gulp.task("watch", function() {
	gulp.watch(["*.js", "!bliss.js", "!*.min.js"], gulp.series("concat", "minify"));
});

gulp.task("default", gulp.series("concat", "minify"));

Fix remaining gulp 3.x syntax


var fs = require('fs'),
	sys = require('sys'),
    exec = require('child_process').exec;

var deployer = exports.deployer = function (project, callback) {
	var tempFile = __dirname + '/tmp/' + Math.random() + '.js';
	fs.writeFile(tempFile, project.lastHandler.code, function(err) {
		if (err) throw err;
		console.log(sys.inspect('node '+tempFile));
		node = exec('node '+tempFile, function (error, stdout, stderr) {
			sys.print('stdout: ' + stdout);
			sys.print('stderr: ' + stderr);
			if (error !== null) {
				console.log('exec error: ' + error);
			}
			callback(stdout, stderr);
		});
	});

}

var fs = require('fs'),
	sys = require('sys'),
    exec = require('child_process').exec;

var deployer = exports.deployer = function (project, callback) {
	var tempFile = __dirname + '/tmp/' + Math.random() + '.js';
	fs.writeFile(tempFile, project.lastHandler.code, function(err) {
		if (err) throw err;
		node = exec('node ' + tempFile, function (error, stdout, stderr) {
			if (error !== null) {
				console.log('exec error: ' + error);
			}
			fs.unlink(tempFile, function (err) {
				if (err)
					console.log("Unable to delete " + tempFile + ".");
				callback(stdout, stderr);
			});
		});
	});

}

Remove the temp file after deployment.


import React, { PureComponent } from 'react';
import PropTypes from 'prop-types';
import cx from 'classnames';
import theme from './theme.css';

import Box from '../box';
import { TextSmall } from '../typography';

class ProgressStep extends PureComponent {
  render() {
    const { label, active, completed, onClick } = this.props;
    const classNames = cx(theme['step'], {
      [theme['is-active']]: active,
      [theme['is-completed']]: completed,
      [theme['is-clickable']]: onClick,
    });
    return (
      <Box className={classNames} onClick={onClick}>
        <TextSmall className={theme['step-label']}>{label}</TextSmall>
        <span className={theme['status-bullet']} />
      </Box>
    );
  }
}

ProgressStep.propTypes = {
  /** The label for the progress step */
  label: PropTypes.string.isRequired,
  /** Whether or not the step is active */
  active: PropTypes.bool.isRequired,
  /** Whether or not the step has been completed */
  completed: PropTypes.bool.isRequired,
  /** Callback function that is fired when the progress step is clicked */
  onClick: PropTypes.func,
};

ProgressStep.defaultProps = {
  active: false,
  completed: false,
};

export default ProgressStep;

import React, { PureComponent } from 'react';
import PropTypes from 'prop-types';
import cx from 'classnames';
import theme from './theme.css';

import Box from '../box';
import { TextSmall } from '../typography';

class ProgressStep extends PureComponent {
  render() {
    const { label, active, completed, onClick } = this.props;
    const classNames = cx(theme['step'], {
      [theme['is-active']]: active,
      [theme['is-completed']]: completed,
      [theme['is-clickable']]: onClick,
    });
    return (
      <Box className={classNames}>
        <TextSmall className={theme['step-label']}>{label}</TextSmall>
        <span className={theme['status-bullet']} onClick={onClick} />
      </Box>
    );
  }
}

ProgressStep.propTypes = {
  /** The label for the progress step */
  label: PropTypes.string.isRequired,
  /** Whether or not the step is active */
  active: PropTypes.bool.isRequired,
  /** Whether or not the step has been completed */
  completed: PropTypes.bool.isRequired,
  /** Callback function that is fired when the progress step is clicked */
  onClick: PropTypes.func,
};

ProgressStep.defaultProps = {
  active: false,
  completed: false,
};

export default ProgressStep;

Move the onClick from the wrapper to the status bullet


module.exports = function(app, settings) {
    app.get('/status.json', function(req, res) {
        res.send({ status: 'true' });
    });

    /**
     * Inspect fields
     */
    app.get('/:mapfile_64/fields.json', function(req, res) {
    });

    /**
     * Inspect data
     */
    app.get('/:mapfile_64/data.json', function(req, res) {
    });

    /**
     * Inspect layer
     */
    app.get('/:mapfile_64/:layer_64/layer.json', function(req, res) {
    });

    /**
     * Inspect field values
     */
    app.get('/:mapfile_64/:layer_64/:feature_64/values.json', function(req, res) {
    });
}

module.exports = function(app, settings) {
    app.get('/status.json', function(req, res) {
        res.send({ status: 'true' });
    });

    /**
     * @TODO expose available fonts (and other 'abilities').
     */
    app.get('/abilities.json', function(req, res) {
        res.send({ fonts: [] });
    });

    /**
     * Inspect fields
     */
    app.get('/:mapfile_64/fields.json', function(req, res) {
    });

    /**
     * Inspect data
     */
    app.get('/:mapfile_64/data.json', function(req, res) {
    });

    /**
     * Inspect layer
     */
    app.get('/:mapfile_64/:layer_64/layer.json', function(req, res) {
    });

    /**
     * Inspect field values
     */
    app.get('/:mapfile_64/:layer_64/:feature_64/values.json', function(req, res) {
    });
}

Add placeholder abilities route callback.


var nconf = require('nconf');

var config = nconf.file({
	file: 'defaults.json',
	dir: __dirname,
	search: true
}).env();

Object.keys(config.get()).filter(function (key) {
	return !!key.match(/^SOFTAP_.*$/)
}).forEach(function (key) {
	config.set(key.replace('SOFTAP_', '').toLowerCase(), config.get(key))
})

module.exports = config;

var nconf = require('nconf');

var config = nconf.file({
	file: 'defaults.json',
	dir: __dirname,
	search: true
}).env().argv();

Object.keys(config.get()).filter(function (key) {
	return !!key.match(/^SOFTAP_.*$/)
}).forEach(function (key) {
	config.set(key.replace('SOFTAP_', '').toLowerCase(), config.get(key))
})

module.exports = config;

Allow use of argv for setting config options


import { h } from './vdom';

export const bold = {
  name: 'bold',
  selector: 'strong, b',
  vdom: children => {children},
};

export const italics = {
  name: 'italic',
  selector: 'em, i',
  vdom: children => {children},
};

export const link = {
  name: 'link',
  selector: 'a[href]',
  attr: node => node.href,
  vdom: (children, attr) => {children},
};

import { h } from './vdom';

export const bold = {
  name: 'bold',
  selector: 'strong, b',
  vdom: children => {children},
};

export const italics = {
  name: 'italic',
  selector: 'em, i',
  vdom: children => {children},
};

export const link = {
  name: 'link',
  selector: 'a[href]',
  dom: node => node.href,
  vdom: (children, attr) => {children},
};

Fix bug reading out links


module.exports.pre = function(version, packagePath) {
	return Promise.resolve(true);
};

module.exports.post = function(version, packagePath) {
	return Promise.resolve();
};

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

function rename(from, to) {
	return new Promise(function(resolve, reject) {
		fs.rename(from, to, function(err) {
			if (err) {
				reject(err);
			} else {
				resolve();
			}
		});
	});
}

module.exports.pre = function(version, packagePath) {
	if (process.platform !== 'win32') {
		return Promise.resolve(true);
	}

	return rename(packagePath, path.join(packagePath, '..', 'package-old2'))
		.then(function() {
			return new Promise(function(resolve) {
				setTimeout(resolve, 3000);
			});
		})
		.then(function() {
			fs.writeFileSync(packagePath, 'test');
			return new Promise(function(resolve) {
				setTimeout(resolve, 3000);
			});
		})
		.then(function() {
			return true;
		});
};

module.exports.post = function(version, packagePath) {
	return Promise.resolve();
};

Update hook to try getting around the update issues on renaming files in use for windows


angular.module('anol.map')

.provider('MapService', [function() {
    var _view;

    var buildMapConfig = function(layers, controls) {
        var map = new ol.Map(angular.extend({}, {
            'controls': controls
        }));
        map.setView(_view);
        angular.forEach(layers, function(layer) {
            map.addLayer(layer);
        });
        return map;
    };

    this.addView = function(view) {
        _view = view;
    };

    this.$get = ['LayersService', 'ControlsService', function(LayersService, ControlsService) {
        var MapService = function() {
            this.map = undefined;
        };
        MapService.prototype.getMap = function() {
            if(angular.isUndefined(this.map)) {
                this.map = buildMapConfig(
                    LayersService.layers,
                    ControlsService.controls
                );
                LayersService.registerMap(this.map);
            }
            return this.map;
        };
        return new MapService();
    }];
}]);

angular.module('anol.map')

.provider('MapService', [function() {
    var _view;

    var buildMapConfig = function(layers, controls) {
        var map = new ol.Map(angular.extend({}, {
            controls: controls,
            layers: layers
        }));
        map.setView(_view);
        return map;
    };

    this.addView = function(view) {
        _view = view;
    };

    this.$get = ['LayersService', 'ControlsService', function(LayersService, ControlsService) {
        var MapService = function() {
            this.map = undefined;
        };
        MapService.prototype.getMap = function() {
            if(angular.isUndefined(this.map)) {
                this.map = buildMapConfig(
                    LayersService.layers,
                    ControlsService.controls
                );
                LayersService.registerMap(this.map);
            }
            return this.map;
        };
        return new MapService();
    }];
}]);

Add layers to map options


function addSkill() {
  $("#new_skill").on("submit", function (event) {
    event.preventDefault();
    var $target = $(event.target);
    console.log($target)
    if ($('#skill_title').val() === "") {
      alert("No skill specified")
    } else {
      $.ajax({
        url: $target.attr("action") + '.json',
        type: "post",
        data: $target.serialize()
      }).done(function (response) {
        $('.skills').append(response.title + "<br>");
        $target[0][2].value = "";
      });
    }
  });
}

function addSkill() {
  $("#new_skill").on("submit", function (event) {
    event.preventDefault();
    var $target = $(event.target);
    console.log($target);
    if ($('#skill_title').val() === "") {
      alert("No skill specified");
    } else {
      $.ajax({
        url: $target.attr("action") + '.json',
        type: "post",
        data: $target.serialize()
      }).done(function (response) {
        $('.skills').append(response.title + " - Time remaining: 24:00:00" + "<br>");
        $target[0][2].value = "";
      });
    }
  });
}

Add semicolons and add temporary string for time remaining


/*
 * Note: putImageData and getImageData is currently only supported on Android.
 */

var page = new tabris.Page({
  title: "Canvas",
  topLevel: true
});

new tabris.Canvas({
  layoutData: {left: 10, top: 10, right: 10, bottom: 10}
}).on("resize", function(canvas, bounds) {
  var ctx = canvas.getContext("2d", bounds.width, bounds.height);
  ctx.strokeStyle = "rgb(78, 154, 217)";
  ctx.lineWidth = 10;
  ctx.moveTo(20, 20);
  ctx.lineTo(bounds.width - 40, bounds.height - 40);
  ctx.stroke();

  // draw image
  ctx.putImageData(createImageData(80, 40), 100, 100);

  // copy region
  var data = ctx.getImageData(0, 0, 100, 100);
  ctx.putImageData(data, 180, 100);

}).appendTo(page);

page.open();

function createImageData(width, height) {
  var array = [];
  for (var y = 0; y < height; y++) {
    for (var x = 0; x < width; x++) {
      var alpha = x % 20 > y % 20 ? 255 : 0;
      array.push(200, 0, 0, alpha);
    }
  }
  return new ImageData(new Uint8ClampedArray(array), width, height);
}

Remove note that imageData is only available on Android


var veyron = require('veyron');
var context = veyron.context;

veyron.init(function(err, rt){
  if (err) throw err;

  rt.bindTo('pingpong', function(err, s){
    if (err) throw err;

    var ctx = new context.Context();

    s.ping(ctx, 'PING', function(err, pong){
      if (err) throw err;

      console.log(pong);
      process.exit(0);
    });
  });
});

var veyron = require('veyron');
var context = veyron.context;

veyron.init(function(err, rt){
  if (err) throw err;

  var ctx = new context.Context();

  rt.bindTo(ctx, 'pingpong', function(err, s){
    if (err) throw err;

    s.ping(ctx, 'PING', function(err, pong){
      if (err) throw err;

      console.log(pong);
      process.exit(0);
    });
  });
});

Fix the playground shell-test by passing a context.


import { Button } from './Widgets';

export default class Ready extends React.Component {
    ready(r) {
        ws.send({cmd: r ? 'ready' : 'not_ready'});
    }
    render() {
        const r = this.ready;
        const btn =
        !this.props.ready ?
            <Button bg="green" onClick={r.bind(this, true)}>
                {this.props.readyText ? this.props.readyText : 'Ready'}
            </Button>
        : this.props.state === 'Waiting' ?
            <Button bg="red" onClick={r.bind(this, false)}>Not Ready</Button>
        :   <Button bg="green">Waiting...</Button>;
        return btn;
    }
}

import { Button } from './Widgets';

export default class Ready extends React.Component {
    ready(r) {
        ws.send({cmd: r ? 'ready' : 'not_ready'});
    }
    render() {
        const r = this.ready;
        const btn =
        !this.props.ready ?
            <Button onClick={r.bind(this, true)}
                style={{background: "radial-gradient(circle, orange 40%, red)"}}>
              {this.props.readyText ? this.props.readyText : 'Ready'}
            </Button>
        : this.props.state === 'Waiting' ?
            <Button bg="red" onClick={r.bind(this, false)}>Not Ready</Button>
        :   <Button bg="green">Waiting...</Button>;
        return btn;
    }
}

Change ready button to be a gradient to stand out


/* @flow */
import React from 'react';
import { GoogleMap } from 'react-google-maps';
import ScriptjsLoader from 'react-google-maps/lib/async/ScriptjsLoader';
import { latLon } from '../../../types/map';

type Props = {
  spanFullPage: ?boolean,
  defaultCenter: latLon,
};

/** @class Map */
function Map(props : Props) {
  const containerElement = (
    <div
      style={{
        height: '200px',
        ...(props.spanFullPage ? { position: 'absolute', left: 0, top: 0, right: 0, bottom: 0, height: '100%', width: '100%' } : {}),
      }}
    />
  );

  const googleMapElement = (
    <GoogleMap
      defaultZoom={15}
      defaultCenter={props.defaultCenter}
    />
  );

  return (
    <ScriptjsLoader
      hostname={'maps.googleapis.com'}
      pathname={'/maps/api/js'}
      query={{ v: '3.0' }}
      loadingElement={
        <div />
      }
      containerElement={containerElement}
      googleMapElement={googleMapElement}
    />
  );
}

export default Map;

/* @flow */
import React from 'react';
import { GoogleMap } from 'react-google-maps';
import ScriptjsLoader from 'react-google-maps/lib/async/ScriptjsLoader';
import { latLon } from '../../../types/map';

type Props = {
  spanFullPage: ?boolean,
  defaultCenter: latLon,
};

/** @class Map */
function Map(props : Props) {
  const containerElement = (
    <div
      style={{
        height: '200px',
        ...(props.spanFullPage ? { position: 'absolute', left: 0, top: 0, right: 0, bottom: 0, height: '100%', width: '100%' } : {}),
      }}
    />
  );

  const googleMapElement = (
    <GoogleMap
      defaultZoom={15}
      defaultCenter={props.defaultCenter}
    />
  );

  return (
    <ScriptjsLoader
      hostname={'maps.googleapis.com'}
      pathname={'/maps/api/js'}
      query={{ key: process.env.GOOGLE_MAPS_API_KEY, v: '3.0' }}
      loadingElement={
        <div />
      }
      containerElement={containerElement}
      googleMapElement={googleMapElement}
    />
  );
}

export default Map;

Use API key from Env when loading google maps script


import { assert } from 'chai';
import getPetitionResponseStatusTitle from 'helpers/getPetitionResponseStatusTitle';

describe('getPetitionsPageTitle', () => {
  it('returns the title for a pending response', () => {
    const actual = getPetitionResponseStatusTitle({ pending: true });
    const expected = 'Pending response from Gemeinde';

    assert.equal(actual, expected);
  });

  it('returns the title for an arrived response', () => {
    const actual = getPetitionResponseStatusTitle({ pending: false });
    const expected = 'Response from Gemeinde';

    assert.equal(actual, expected);
  });
});

import { assert } from 'chai';
import getPetitionResponseStatusTitle from 'helpers/getPetitionResponseStatusTitle';

describe('getPetitionsPageTitle', () => {
  it('returns the title for a pending response', () => {
    const actual = getPetitionResponseStatusTitle({ pending: true });
    const expected = 'Pending official response';

    assert.equal(actual, expected);
  });

  it('returns the title for an arrived response', () => {
    const actual = getPetitionResponseStatusTitle({ pending: false });
    const expected = 'Official response';

    assert.equal(actual, expected);
  });
});

Fix response status title helper test


define([
  'extensions/controllers/module',
  'common/views/visualisations/completion_rate',
  'common/collections/user-satisfaction'
],
function (ModuleController, UserSatisfactionView, UserSatisfactionCollection) {
  var UserSatisfactionModule = ModuleController.extend({
    visualisationClass: UserSatisfactionView,
    collectionClass: UserSatisfactionCollection,
    clientRenderOnInit: true,
    requiresSvg: true,
    collectionOptions: function () {
      return {
        id: 'user_satisfaction',
        title: 'User satisfaction',
        sortBy: '_timestamp:ascending',
        limit: 0,
        min: 1,
        max: 5,
        totalAttr: 'totalRatings',
        valueAttr: this.model.get('value-attribute'),
        period: this.model.get('period') || 'day',
        duration: this.model.get('duration') || 63,
        trim: this.model.get('trim') || 8,
        axes: _.merge({
          x: {
            label: 'Date',
            key: '_start_at',
            format: 'date'
          },
          y: [
            {
              label: this.model.get('title'),
              key: 'rating',
              format: 'percent'
            }
          ]
        }, this.model.get('axes'))
      };
    },
    visualisationOptions: function () {
      return {
        totalAttr: 'totalRatings',
        valueAttr: this.model.get('value-attribute')
      };
    }
  });

  return UserSatisfactionModule;
});

define([
  'extensions/controllers/module',
  'common/views/visualisations/completion_rate',
  'common/collections/user-satisfaction'
],
function (ModuleController, UserSatisfactionView, UserSatisfactionCollection) {
  var UserSatisfactionModule = ModuleController.extend({
    visualisationClass: UserSatisfactionView,
    collectionClass: UserSatisfactionCollection,
    clientRenderOnInit: true,
    requiresSvg: true,
    collectionOptions: function () {
      return {
        id: 'user_satisfaction',
        title: 'User satisfaction',
        sortBy: '_timestamp:ascending',
        limit: 0,
        min: 1,
        max: 5,
        totalAttr: 'totalRatings',
        valueAttr: this.model.get('value-attribute'),
        period: this.model.get('period') || 'day',
        duration: this.model.get('duration') || 30,
        trim: this.model.get('trim') || true,
        axes: _.merge({
          x: {
            label: 'Date',
            key: '_start_at',
            format: 'date'
          },
          y: [
            {
              label: this.model.get('title'),
              key: 'rating',
              format: 'percent'
            }
          ]
        }, this.model.get('axes'))
      };
    },
    visualisationOptions: function () {
      return {
        totalAttr: 'totalRatings',
        valueAttr: this.model.get('value-attribute')
      };
    }
  });

  return UserSatisfactionModule;
});

Set default duration of user satisfaction graph to 30 days


import React from 'react';

import SchemaForm from './SchemaForm';
import SchemaFormUtil from '../utils/SchemaFormUtil';

const METHODS_TO_BIND = [
  'handleFormChange',
  'validateForm'
];

class JobForm extends SchemaForm {
  constructor() {
    super();

    METHODS_TO_BIND.forEach((method) => {
      this[method] = this[method].bind(this);
    });
  }

  componentWillMount() {
    this.multipleDefinition = this.getNewDefinition();
    // On edit hide the id field.
    if (this.props.edit) {
      this.multipleDefinition.general.definition.forEach(function (definition) {
        if (definition.name === 'id') {
          definition.formElementClass = 'hidden';
        }
      });
    }
    this.props.getTriggerSubmit(this.handleExternalSubmit);
  }

  getDataTriple() {
    let model = this.triggerTabFormSubmit();
    return {
      model: SchemaFormUtil.processFormModel(model, this.multipleDefinition)
    };
  }

  validateForm() {
    // TODO: Overwrite the default behaviour until DCOS-7669 is fixed.
    return true;
  }
}

JobForm.defaultProps = {
  className: 'multiple-form row',
  getTriggerSubmit: function () {},
  onChange: function () {},
  schema: {}
};

JobForm.propTypes = {
  className: React.PropTypes.string,
  edit: React.PropTypes.bool,
  getTriggerSubmit: React.PropTypes.func,
  onChange: React.PropTypes.func,
  schema: React.PropTypes.object
};

module.exports = JobForm;

import React from 'react';

import SchemaForm from './SchemaForm';
import SchemaFormUtil from '../utils/SchemaFormUtil';

const METHODS_TO_BIND = [
  'handleFormChange',
  'validateForm'
];

class JobForm extends SchemaForm {
  constructor() {
    super();

    METHODS_TO_BIND.forEach((method) => {
      this[method] = this[method].bind(this);
    });
  }

  componentWillMount() {
    this.multipleDefinition = this.getNewDefinition();
    // On edit hide the id field.
    if (this.props.isEdit) {
      this.multipleDefinition.general.definition.forEach(function (definition) {
        if (definition.name === 'id') {
          definition.formElementClass = 'hidden';
        }
      });
    }
    this.props.getTriggerSubmit(this.handleExternalSubmit);
  }

  getDataTriple() {
    let model = this.triggerTabFormSubmit();
    return {
      model: SchemaFormUtil.processFormModel(model, this.multipleDefinition)
    };
  }

  validateForm() {
    // TODO: Overwrite the default behaviour until DCOS-7669 is fixed.
    return true;
  }
}

JobForm.defaultProps = {
  className: 'multiple-form row',
  getTriggerSubmit: function () {},
  isEdit: false,
  onChange: function () {},
  schema: {}
};

JobForm.propTypes = {
  className: React.PropTypes.string,
  isEdit: React.PropTypes.bool,
  getTriggerSubmit: React.PropTypes.func,
  onChange: React.PropTypes.func,
  schema: React.PropTypes.object
};

module.exports = JobForm;

Rename edit prop and add default value


// ==UserScript==
// @name       LinkedIn hide email contacts from "People You May Know"
// @namespace  http://mathemaniac.org
// @version    1.1.0
// @description  Hides those annoying email contacts from PYMK
// @match      http://www.linkedin.com/people/pymk*
// @match      https://www.linkedin.com/people/pymk*
// @copyright  2015, Sebastian Paaske Tørholm
// @require    https://ajax.googleapis.com/ajax/libs/jquery/1.10.1/jquery.min.js
// ==/UserScript==

$(function () {
    $('ul.people-cards-list li[id*="@"]').remove();

    // Remove when autoloading new ones
    var MutationObserver = window.MutationObserver || window.WebKitMutationObserver;
    var config = { childList: true, characterData: false, attributes: false, subtree: true };
    var observer = new MutationObserver( function (mutations) {
        mutations.forEach( function (mutation) {
            if (mutation.addedNodes) {
                $(mutation.addedNodes).each( function () {
                    if ($(this).attr('id').match(/card-.*@.*/)) {
                        console.log("removing ", $(this).attr('id'));
                        $(this).remove();
                    }
                } );
            }
        });
    });

    $('ul.people-cards-list').each( function () { observer.observe(this, config); } );
});

// ==UserScript==
// @name       LinkedIn hide email contacts from "People You May Know"
// @namespace  http://mathemaniac.org
// @version    1.2.0
// @description  Hides those annoying email contacts from PYMK
// @match      https://www.linkedin.com/mynetwork*
// @copyright  2015-2017, Sebastian Paaske Tørholm
// @require    https://ajax.googleapis.com/ajax/libs/jquery/1.10.1/jquery.min.js
// ==/UserScript==

$(function () {
    $('.mn-pymk-list__card:has(.mn-person-info__guest-handle)').remove();

    // Remove when autoloading new ones
    var MutationObserver = window.MutationObserver || window.WebKitMutationObserver;
    var config = { childList: true, characterData: false, attributes: false, subtree: true };
    var observer = new MutationObserver( function (mutations) {
        mutations.forEach( function (mutation) {
            if (mutation.addedNodes) {
                $(mutation.addedNodes).each( function () {
                    if ($(this).hasClass('mn-person-info__guest-handle')) {
                        console.log("removing card for ", $(this).text());
                        $(this).parents('.mn-pymk-list__card').remove();
                    }
                } );
            }
        });
    });

    $('.mn-pymk-list__cards').each( function () { observer.observe(this, config); } );
});

Update LinkedIn PYMK script to work with new page


import classnames from 'classnames'
import Education from '../education/Education'
import Names from '../names/Names'
import React from 'react'
import {TabContent, TabPane, Nav, NavItem, NavLink} from 'reactstrap'

export default class Tabs extends React.Component {
  constructor(props) {
    super(props)
    this.tabs = ['Имя', 'Образование', 'Опыт работы', 'Результаты']

    this.toggle = this.toggle.bind(this)
    this.state = {
      activeTab: 0
    }
  }

  render() {
    return (
        <div className="cv-tabs">
          <Nav tabs>
            {this.tabs.map((tab, idx) => (
                <NavItem key={tab}>
                  <NavLink
                      className={classnames({active: this.state.activeTab === idx})}
                      onClick={()=>this.toggle(idx)}>
                    {tab}
                  </NavLink>
                </NavItem>
            ))}
          </Nav>
          <TabContent activeTab={this.state.activeTab}>
            <TabPane tabId={0}>
              <Names/>
            </TabPane>
            <TabPane tabId={1}>
              <Education/>
            </TabPane>
          </TabContent>
        </div>
    )
  }

  toggle(tab) {
    if (this.state.activeTab !== tab) {
      this.setState({
        activeTab: tab
      })
    }
  }
}

import classnames from 'classnames'
import Education from '../education/Education'
import Names from '../names/Names'
import React from 'react'
import {TabContent, TabPane, Nav, NavItem, NavLink} from 'reactstrap'

export default class Tabs extends React.Component {
  constructor(props) {
    super(props)
    this.tabs = ['Имя', 'Образование', 'Опыт работы', 'Результаты']

    this.toggle = this.toggle.bind(this)
    this.state = {
      activeTab: 0
    }
  }

  render() {
    return (
        <div className="cv-tabs">
          <Nav tabs>
            {this.tabs.map((tab, idx) => (
                <NavItem
                    key={tab}
                    className={classnames({active: this.state.activeTab === idx})}>
                  <NavLink
                      onClick={()=>this.toggle(idx)}>
                    {tab}
                  </NavLink>
                </NavItem>
            ))}
          </Nav>
          <TabContent activeTab={this.state.activeTab}>
            <TabPane tabId={0}>
              <Names/>
            </TabPane>
            <TabPane tabId={1}>
              <Education/>
            </TabPane>
          </TabContent>
        </div>
    )
  }

  toggle(tab) {
    if (this.state.activeTab !== tab) {
      this.setState({
        activeTab: tab
      })
    }
  }
}

Move active class from link to tab


import React, { PropTypes } from 'react'
import { Link } from 'react-router'
import classnames from 'classnames'

import Date from '../../components/Date'
import Tag from '../../components/Tag'
import styles from './PostItem.scss'

const PostItem = ({__url, date, title, draft, tags, description}) => {
  const articleClass = classnames({
    [styles.article]: true,
    [styles.draft]: draft
  })

  const linkClass = classnames({
    [styles.title]: true,
    [styles.titleDraft]: draft
  })

  return (

      <Link
        to={__url}
        className={linkClass}
      >
        {title}
      </Link>

        {
          tags &&
          Array.isArray(tags) &&
            tags.map((tag) =>
              <Tag key={tag} name={tag} />
            )
        }
        <Date date={date} className={styles.date} />

      {
        description &&

{description}

      }

  )
}

PostItem.propTypes = {
  __url: PropTypes.string.isRequired,
  date: PropTypes.string.isRequired,
  title: PropTypes.string.isRequired,
  description: PropTypes.string,
  draft: PropTypes.boolean,
  tags: PropTypes.array
}

export default PostItem

import React, { PropTypes } from 'react'
import { Link } from 'react-router'
import classnames from 'classnames'

import Date from '../../components/Date'
import Tag from '../../components/Tag'
import styles from './PostItem.scss'

const PostItem = ({__url, date, title, draft, tags, description}) => {
  const articleClass = classnames(styles.article, {
    [styles.draft]: draft
  })

  const linkClass = classnames(styles.title, {
    [styles.titleDraft]: draft
  })

  return (

      <Link
        to={__url}
        className={linkClass}
      >
        {title}
      </Link>

        {
          tags &&
          Array.isArray(tags) &&
            tags.map((tag) =>
              <Tag key={tag} name={tag} />
            )
        }
        <Date date={date} className={styles.date} />

      {
        description &&

{description}

      }

  )
}

PostItem.propTypes = {
  __url: PropTypes.string.isRequired,
  date: PropTypes.string.isRequired,
  title: PropTypes.string.isRequired,
  description: PropTypes.string,
  draft: PropTypes.boolean,
  tags: PropTypes.array
}

export default PostItem

Refactor classnames usage a little bit


Package.describe({
  name: 'rate-limit',
  version: '1.0.8',
  // Brief, one-line summary of the package.
  summary: 'An algorithm for rate limiting anything',
  // URL to the Git repository containing the source code for this package.
  git: '',
  // By default, Meteor will default to using README.md for documentation.
  // To avoid submitting documentation, set this field to null.
  documentation: 'README.md'
});

Package.onUse(function(api) {
  api.use('underscore');
  api.use('random');
  api.addFiles('rate-limit.js');
  api.export("RateLimiter");
});

Package.onTest(function(api) {
  api.use('test-helpers', ['client', 'server']);
  api.use('underscore');
  api.use('random');
  api.use('ddp-rate-limiter');
  api.use('tinytest');
  api.use('rate-limit');
  api.use('ddp-common');
  api.addFiles('rate-limit-tests.js');
});

Package.describe({
  name: 'rate-limit',
  version: '1.0.8-rc.4',
  // Brief, one-line summary of the package.
  summary: 'An algorithm for rate limiting anything',
  // URL to the Git repository containing the source code for this package.
  git: '',
  // By default, Meteor will default to using README.md for documentation.
  // To avoid submitting documentation, set this field to null.
  documentation: 'README.md'
});

Package.onUse(function(api) {
  api.use('underscore');
  api.use('random');
  api.addFiles('rate-limit.js');
  api.export("RateLimiter");
});

Package.onTest(function(api) {
  api.use('test-helpers', ['client', 'server']);
  api.use('underscore');
  api.use('random');
  api.use('ddp-rate-limiter');
  api.use('tinytest');
  api.use('rate-limit');
  api.use('ddp-common');
  api.addFiles('rate-limit-tests.js');
});

Add `-rc.n` suffix to `rate-limit`.


import React from 'react';
import { Text, TouchableOpacity, TouchableNativeFeedback } from 'react-native';
import { platformAndroid } from '../../util/general';

class Touchable extends React.Component {
	constructor(props) {
		super(props);
	}

	render() {
		if (this.props.onPress && this.props.children) {
			if (platformAndroid()) {
				return (
					<TouchableNativeFeedback onPress={this.props.onPress} style={this.props.customStyle}>
						{this.props.children}
					</TouchableNativeFeedback>
				);
			} else {
				return (
					<TouchableOpacity onPress={this.props.onPress} activeOpacity={.7} style={this.props.customStyle}>
						{this.props.children}
					</TouchableOpacity>
				);
			}
		} else {
			return null;
		}
	}
}

export default Touchable;

import React from 'react';
import { Text, TouchableOpacity, TouchableNativeFeedback } from 'react-native';
import { platformAndroid } from '../../util/general';

class Touchable extends React.Component {
	constructor(props) {
		super(props);
	}

	render() {
		if (this.props.onPress && this.props.children) {
			if (platformAndroid()) {
				return (
					<TouchableNativeFeedback onPress={this.props.onPress} style={this.props.style}>
						{this.props.children}
					</TouchableNativeFeedback>
				);
			} else {
				return (
					<TouchableOpacity onPress={this.props.onPress} activeOpacity={.7} style={this.props.style}>
						{this.props.children}
					</TouchableOpacity>
				);
			}
		} else {
			return null;
		}
	}
}

export default Touchable;

Change touchable customStyle to style


'use strict';

module.exports = {
  up: (queryInterface, Sequelize) => {
    queryInterface.createTable('Documents', {
      id: {
        allowNull: false,
        autoIncrement: true,
        primaryKey: true,
        type: Sequelize.INTEGER
      },
      title: {
        type: Sequelize.STRING
      },
      accessType: {
        type: Sequelize.STRING
      },
      content: {
        type: Sequelize.TEXT
      },
      owner: {
        type: Sequelize.STRING
      },
      createdAt: {
        allowNull: false,
        type: Sequelize.DATE
      },
      updatedAt: {
        allowNull: false,
        type: Sequelize.DATE
      },
      userId: {
        type: Sequelize.INTEGER,
        onDelete: 'CASCADE',
        references: {
          model: 'Users',
          key: 'id',
          as: 'userId',
        },
      },
    });
  },
  down: (queryInterface, Sequelize) => {
    queryInterface.dropTable('Documents');
  }
};

'use strict';

module.exports = {
  up: (queryInterface, Sequelize) => {
    queryInterface.createTable('Documents', {
      id: {
        allowNull: false,
        autoIncrement: true,
        primaryKey: true,
        type: Sequelize.INTEGER
      },
      title: {
        type: Sequelize.STRING
      },
      accessType: {
        type: Sequelize.STRING
      },
      content: {
        type: Sequelize.TEXT
      },
      owner: {
        type: Sequelize.STRING
      },
      createdAt: {
        allowNull: false,
        type: Sequelize.DATEONLY
      },
      updatedAt: {
        allowNull: false,
        type: Sequelize.DATEONLY
      },
      userId: {
        type: Sequelize.INTEGER,
        onDelete: 'CASCADE',
        references: {
          model: 'Users',
          key: 'id',
          as: 'userId',
        },
      },
    });
  },
  down: (queryInterface, Sequelize) => {
    queryInterface.dropTable('Documents');
  }
};

Modify createdAt and updatedAt field type to dateonly


import expect from 'expect';
import filter from '../src/filter';

describe('filter', () => {
  it('should keep items the callback returns truthy for', () => {
    const array = [1, 2, 3, 4];
    const callback = (_, item) => item % 2;
    const result = filter(array, callback);

    expect(result).toContain(1);
    expect(result).toContain(3);
    expect(result).toNotContain(2);
    expect(result).toNotContain(4);
  });

  it('should pass argument to callback', () => {
    const array = [1];
    const callback = (argument) => {
      expect(argument).toEqual('foo');
    };

    filter(array, callback, 'foo');
  });
});

import expect from 'expect';
import filter from '../src/filter';

describe('filter', () => {
  it('should keep items the callback returns truthy for', () => {
    const array = [1, 2, 3, 4];
    const callback = (_, item) => item % 2;
    const result = filter(array, callback);

    expect(result).toContain(1);
    expect(result).toContain(3);
    expect(result).toNotContain(2);
    expect(result).toNotContain(4);
  });

  it('should pass argument to callback', () => {
    const array = [1];
    const callback = (argument) => {
      expect(argument).toEqual('foo');
    };

    filter(array, callback, 'foo');
  });

  it('should iterate over items added while filtering', () => {
    const array = [1];
    let pushed = false;
    const callback = () => {
      if (!pushed) {
        array.push(2);
        pushed = true;
      }
      return true;
    };

    const result = filter(array, callback);

    expect(result).toEqual([1, 2]);
  });
});

Add test for filter change.


// Update with your config settings.

module.exports = {

  development: {
    client: 'pg',
    connection: 'postgres://localhost/happyhourpower',
    useNullAsDefault: true,
    migrations: {
      directory: './db/migrations',
    },
    seeds: {
      directory: './db/seeds/dev',
    },
  },

  test: {
    client: 'pg',
    connection: process.env.DATABASE_URL || 'postgres://localhost/happyhourpowertest',
    useNullAsDefault: true,
    migrations: {
      directory: './db/migrations',
    },
    seeds: {
      directory: './db/test/seeds',
    },
  },

  production: {
    client: 'pg',
    connection: `${process.env.DATABASE_URL}?ssl=true`,
    migrations: {
      directory: './db/migrations',
    },
    useNullAsDefault: true,
  },
};

// Update with your config settings.

module.exports = {

  development: {
    client: 'pg',
    connection: 'postgres://localhost/happyhourpower',
    useNullAsDefault: true,
    migrations: {
      directory: './db/migrations',
    },
    seeds: {
      directory: './db/seeds/dev',
    },
  },

  test: {
    client: 'pg',
    connection: process.env.DATABASE_URL || 'postgres://localhost/happyhourpowertest',
    useNullAsDefault: true,
    migrations: {
      directory: './db/migrations',
    },
    seeds: {
      directory: './db/seeds/test',
    },
  },

  production: {
    client: 'pg',
    connection: `${process.env.DATABASE_URL}?ssl=true`,
    migrations: {
      directory: './db/migrations',
    },
    useNullAsDefault: true,
  },
};

Update KNEX setup file to point to the correct location of test seed data.


const { authenticate } = require('@feathersjs/authentication');

const globalHooks = require('../../../hooks');
const { canRead } = require('../utils/filePermissionHelper');

const restrictToCurrentUser = (hook) => {
	const { params: { account: { userId } }, result: { data: files } } = hook;
	const permissionPromises = files.map((f) => canRead(userId, f)
		.then(() => f)
		.catch(() => undefined));

	return Promise.all(permissionPromises)
		.then((allowedFiles) => {
			hook.result.data = allowedFiles;
			return hook;
		});
};

exports.before = {
	all: [authenticate('jwt')],
	find: [globalHooks.hasPermission('FILESTORAGE_VIEW')],
	get: [globalHooks.hasPermission('FILESTORAGE_VIEW')],
	create: [globalHooks.hasPermission('FILESTORAGE_CREATE')],
	update: [globalHooks.hasPermission('FILESTORAGE_EDIT')],
	patch: [globalHooks.hasPermission('FILESTORAGE_EDIT')],
	remove: [globalHooks.hasPermission('FILESTORAGE_REMOVE')],
};

exports.after = {
	all: [],
	find: [restrictToCurrentUser],
	get: [],
	create: [],
	update: [],
	patch: [],
	remove: [],
};

const { authenticate } = require('@feathersjs/authentication');
const { discard } = require('feathers-hooks-common');

const globalHooks = require('../../../hooks');
const { canRead } = require('../utils/filePermissionHelper');

const restrictToCurrentUser = (hook) => {
	const { params: { account: { userId } }, result: { data: files } } = hook;
	const permissionPromises = files.map((f) => canRead(userId, f)
		.then(() => f)
		.catch(() => undefined));

	return Promise.all(permissionPromises)
		.then((allowedFiles) => {
			hook.result.data = allowedFiles;
			return hook;
		});
};

exports.before = {
	all: [authenticate('jwt')],
	find: [globalHooks.hasPermission('FILESTORAGE_VIEW')],
	get: [globalHooks.hasPermission('FILESTORAGE_VIEW')],
	create: [globalHooks.hasPermission('FILESTORAGE_CREATE')],
	update: [globalHooks.hasPermission('FILESTORAGE_EDIT')],
	patch: [globalHooks.hasPermission('FILESTORAGE_EDIT')],
	remove: [globalHooks.hasPermission('FILESTORAGE_REMOVE')],
};

exports.after = {
	all: [discard('securityCheck.requestToken', 'thumbnailRequestToken')],
	find: [restrictToCurrentUser],
	get: [],
	create: [],
	update: [],
	patch: [],
	remove: [],
};

Remove request tokens from service responses


var css = '\
.pr-toolbar {\
    position: static !important;\
}\
'

var style = document.createElement('style')
style.appendChild(document.createTextNode(css))
document.getElementsByTagName('head')[0].appendChild(style)

$('.Popover.js-notice').remove();

$(document).scroll(function() {
    $('.is-stuck').removeClass('is-stuck');
})
$(function() {
    $('.is-stuck').removeClass('is-stuck');
})

var css = '\
.pr-toolbar {\
    position: static !important;\
}\
.Popover.js-notice, .Popover-message { display: none !important }\
'

var style = document.createElement('style')
style.appendChild(document.createTextNode(css))
document.getElementsByTagName('head')[0].appendChild(style)

$(document).scroll(function() {
    $('.is-stuck').removeClass('is-stuck');
})
$(function() {
    $('.is-stuck').removeClass('is-stuck');
})

Kill off more github popups


var jsdom = require('../../lib/jsdom');
var path = require('path');

function again() {
  jsdom.env('test', [
    path.resolve(__dirname, '../jquery-fixtures/jquery-1.6.2.js'
  ], function(errors, window) {
    window.close();
    setTimeout(again, 0);
  });
}

again();

var jsdom = require('../../lib/jsdom');
var path = require('path');

function again() {
  jsdom.env('test', [
    path.resolve(__dirname, '../jquery-fixtures/jquery-1.6.2.js')
  ], function(errors, window) {
    window.close();
    setTimeout(again, 0);
  });
}

again();

Fix syntax by adding missing parenthesis.


import { GltfLoader, Resource } from "../../Source/Cesium.js";
import { ModelExperimental } from "../../Source/Cesium.js";
import createScene from "../createScene.js";

describe("Scene/ModelExperimental", function () {
  var boxTexturedGlbUrl =
    "./Data/Models/GltfLoader/BoxTextured/glTF-Binary/BoxTextured.glb";

  var scene;

  beforeAll(function () {
    scene = createScene();
  });

  it("initializes from Uint8Array", function () {
    spyOn(GltfLoader.prototype, "load");

    var resource = Resource.createIfNeeded(boxTexturedGlbUrl);
    var loadPromise = resource.fetchArrayBuffer();
    return loadPromise.then(function (buffer) {
      var model = new ModelExperimental({
        gltf: new Uint8Array(buffer),
      });

      expect(GltfLoader.prototype.load).toHaveBeenCalled();
      model._readyPromise.then(function () {
        expect(model._sceneGraph).toBeDefined();
        expect(model._resourcesLoaded).toEqual(true);
      });
    });
  });
});

import { GltfLoader, Resource } from "../../Source/Cesium.js";
import { ModelExperimental } from "../../Source/Cesium.js";

describe("Scene/ModelExperimental", function () {
  var boxTexturedGlbUrl =
    "./Data/Models/GltfLoader/BoxTextured/glTF-Binary/BoxTextured.glb";

  it("initializes from Uint8Array", function () {
    spyOn(GltfLoader.prototype, "load");

    var resource = Resource.createIfNeeded(boxTexturedGlbUrl);
    var loadPromise = resource.fetchArrayBuffer();
    return loadPromise.then(function (buffer) {
      var model = new ModelExperimental({
        gltf: new Uint8Array(buffer),
      });

      expect(GltfLoader.prototype.load).toHaveBeenCalled();
      model._readyPromise.then(function () {
        expect(model._sceneGraph).toBeDefined();
        expect(model._resourcesLoaded).toEqual(true);
      });
    });
  });
});

Remove unused scene from unit test


// New Relic Server monitoring support
if ( process.env.NEW_RELIC_ENABLED ) {
  require( "newrelic" );
}

const restify = require('restify');
const applyRoutes = require('./routes');
const logger = require('./lib/logger')
const middleware = require('./lib/middleware')
const package = require('../package')

const server = restify.createServer({
  name: 'openbadger',
  version: package.version,
  log: logger,
});

server.pre(restify.pre.sanitizePath());
server.use(restify.acceptParser(server.acceptable));
server.use(restify.queryParser({mapParams: false}));
server.use(restify.bodyParser({mapParams: false, rejectUnknown: true}));
server.use(middleware.verifyRequest())
server.use(middleware.attachResolvePath())
server.use(middleware.attachErrorLogger())

applyRoutes(server);

module.exports = server;

if (!module.parent) {
  server.listen(process.env.PORT || 8080, function () {
    console.log('%s listening at %s', server.name, server.url);
  });
}

// New Relic Server monitoring support
if ( process.env.NEW_RELIC_ENABLED ) {
  require( "newrelic" );
}

const restify = require('restify');
const applyRoutes = require('./routes');
const logger = require('./lib/logger')
const middleware = require('./lib/middleware')
const package = require('../package')

const server = restify.createServer({
  name: package.name,
  version: package.version,
  log: logger,
});

server.pre(restify.pre.sanitizePath());
server.use(restify.acceptParser(server.acceptable));
server.use(restify.queryParser({mapParams: false}));
server.use(restify.bodyParser({mapParams: false, rejectUnknown: true}));
server.use(middleware.verifyRequest())
server.use(middleware.attachResolvePath())
server.use(middleware.attachErrorLogger())

applyRoutes(server);

module.exports = server;

if (!module.parent) {
  server.listen(process.env.PORT || 8080, function () {
    console.log('%s listening at %s', server.name, server.url);
  });
}

Use `package.name` in the app definition.


var game = {};

game.initialise = function() {
    common.require('draw');
    common.require('level');
    common.require('objects');
    common.require('userInterface');

    window.requestAnimFrame(game.drawLoop());
};

game.drawLoop = function() {
    requestAnimationFrame(game.drawLoop);
    draw(userInterface.elements.canvas, level.get(), 0, 0);
};

var game = {};

game.initialise = function() {
    common.require('draw');
    common.require('level');
    common.require('objects');
    common.require('userInterface');

    window.requestAnimFrame(game.drawLoop());
};

game.drawLoop = function() {
    requestAnimationFrame(game.drawLoop);
    draw(userInterface.elements.canvas, level.get(), objects.list(), 0, 0);
};

Call of draw function has been corrected


//IIFE containing main tasks of page
(function(){
  'use strict';

  var url = "https://api.etsy.com/v2/listings/active.js?api_key=l3usap5o1yhtiz130c55s1fq&keywords=ninjas&includes=Images,Shop&sort_on=score&limit=48";
  fetchJSONP(url, app);

  //Fetches JSONP data from Etsy API
  function fetchJSONP(url, callback) {
    var callbackName = 'jsonp_callback_' + Math.round(100000 * Math.random());
    var script = document.createElement('script');

    window[callbackName] = function(data) {
        delete window[callbackName];
        document.body.removeChild(script);
        callback(data);
    };

    script.src = url + (url.indexOf('?') >= 0 ? '&' : '?') + 'callback=' + callbackName;
    document.body.appendChild(script);
  }

  //function that will delegate other tasks to helper functions to manipulate data recieved from API
  function app(response) {
    var results = response.results;
    console.log(results);
  }

})();

Load API request object into function app() and successfully log response.results to console


/* global element, by */
'use strict';
var common = require('../common/common');
var navigation = require('../common/navigation');
var genericForm = require('../generic_form/generic_form');

var goToCloudImageList = function() {
  navigation.go('main', 'admin');
  navigation.go('admin', 'cloud-images');
};
module.exports.goToCloudImageList = goToCloudImageList;

var addNewCloudImage = function(name, osType, osArch, osDistribution, osVersion, numCPUs, diskSize, memSize) {
  goToCloudImageList();
  element(by.id('new-cloud-image-button')).click();
  genericForm.sendValueToPrimitive('name', name, false, 'input');
  genericForm.sendValueToPrimitive('osType', osType, false, 'select');
  genericForm.sendValueToPrimitive('osArch', osArch, false, 'select');
  genericForm.sendValueToPrimitive('osDistribution', osDistribution, false, 'input');
  genericForm.sendValueToPrimitive('osVersion', osVersion, false, 'input');
  genericForm.sendValueToPrimitive('numCPUs', numCPUs, false, 'input');
  genericForm.sendValueToPrimitive('diskSize', diskSize, false, 'input');
  genericForm.sendValueToPrimitive('memSize', memSize, false, 'input');
  element(by.binding('GENERIC_FORM.SAVE')).click();
  common.dismissAlert();
};
module.exports.addNewCloudImage = addNewCloudImage;

/* global element, by */
'use strict';
var common = require('../common/common');
var navigation = require('../common/navigation');
var genericForm = require('../generic_form/generic_form');

var goToCloudImageList = function() {
  navigation.go('main', 'admin');
  navigation.go('admin', 'cloud-images');
};
module.exports.goToCloudImageList = goToCloudImageList;

var addNewCloudImage = function(name, osType, osArch, osDistribution, osVersion, numCPUs, diskSize, memSize) {
  goToCloudImageList();
  element(by.id('new-cloud-image-button')).click();
  genericForm.sendValueToPrimitive('name', name, false, 'input');
  genericForm.sendValueToPrimitive('osType', osType, false, 'select');
  genericForm.sendValueToPrimitive('osArch', osArch, false, 'select');
  genericForm.sendValueToPrimitive('osDistribution', osDistribution, false, 'input');
  genericForm.sendValueToPrimitive('osVersion', osVersion, false, 'input');
  genericForm.sendValueToPrimitive('numCPUs', numCPUs, false, 'input');
  genericForm.sendValueToPrimitive('diskSize', diskSize, false, 'input');
  genericForm.sendValueToPrimitive('memSize', memSize, false, 'input');
  element(by.binding('GENERIC_FORM.SAVE')).click();
  common.dismissAlertIfPresent();
};
module.exports.addNewCloudImage = addNewCloudImage;

Fix errrors on Firefox when dismiss toaster


var serviceName = 'radiodan-http';

if(process.platform === 'linux') {
  var avahi = require('avahi_pub');

  module.exports.advertise = function(radiodan, port) {
    radiodan.create().player.discover().then(function(players) {
      var txtRecord = { players: JSON.stringify(players) },
          service = {
            name: serviceName, type: '_radiodan-http._tcp',
            port: port, data: txtRecord
          };

      avahi.publish(service);
    });
  };
} else {
  var mdns = require('mdns'),
    serviceType = mdns.makeServiceType({
      name: serviceName, protocol: 'tcp', subtypes: ['radiodanv1']
    });

  console.log('serviceType', process.platform, serviceType.toString());

  module.exports.advertise = function(radiodan, port) {
    var utils  = radiodan.utils,
        logger = utils.logger(__filename),
        txtRecord,
        mdnsAd;

    radiodan.create().player.discover().then(function(players) {
      txtRecord = {
        players: JSON.stringify(players)
      };

      mdnsAd = mdns.createAdvertisement(
        serviceType.toString(), port, {txtRecord: txtRecord}
        );

      mdnsAd.start();
    }).then(null, utils.failedPromiseHandler(logger));
  };
}

var os = require('os'),
    hostname = os.hostname(),
    serviceName = 'radiodan-http';

if(process.platform === 'linux') {
  var avahi = require('avahi_pub');

  module.exports.advertise = function(radiodan, port) {
    radiodan.create().player.discover().then(function(players) {
      var txtRecord = { players: JSON.stringify(players) },
          service = {
            name: hostname, type: '_'+serviceName+'._tcp',
            port: port, data: txtRecord
          };

      avahi.publish(service);
    });
  };
} else {
  var mdns = require('mdns'),
    serviceType = mdns.makeServiceType({
      name: serviceName, protocol: 'tcp'
    });

  console.log('serviceType', process.platform, serviceType.toString());

  module.exports.advertise = function(radiodan, port) {
    var utils  = radiodan.utils,
        logger = utils.logger(__filename),
        txtRecord,
        mdnsAd;

    radiodan.create().player.discover().then(function(players) {
      txtRecord = {
        players: JSON.stringify(players)
      };

      mdnsAd = mdns.createAdvertisement(
        serviceType.toString(), port, {txtRecord: txtRecord}
        );

      mdnsAd.start();
    }).then(null, utils.failedPromiseHandler(logger));
  };
}

Add hostname to avahi_pub advertisement method


var performCommand = function(event) {
	if(event.command === 'hibernateTabs') {
		var c = 0;

		var xmlHttp = new XMLHttpRequest();
		xmlHttp.open('GET', safari.extension.baseURI + 'lib/hibernationPage/index.html', true);
		xmlHttp.onreadystatechange = function() {
			if(xmlHttp.readyState === 4) {
				var html = xmlHttp.responseText;

				_.each(safari.application.browserWindows, function(win) {
					_.each(win.tabs, function(tab) {
						if(tab === win.activeTab) return;
						if(!tab.url.match(/^https?:\/\//)) return;

						_.delay(function() {
							var pageInfo = {
								url: tab.url,
								title: tab.title
							};

							var pageHtml = html.replace(/\{\/\*pageInfoObject\*\/\}/, JSON.stringify(pageInfo));
							var dataURL = 'data:text/html;charset=utf-8,' + encodeURIComponent(pageHtml);
							tab.url = dataURL;
						}, c * 100);

						c++;
					});
				});
			}
		};
		xmlHttp.send(null);
	}
};

safari.application.addEventListener('command', performCommand, false);

var performCommand = function(event) {
	if(event.command === 'hibernateTabs') {
		var c = 0;

		var xmlHttp = new XMLHttpRequest();
		xmlHttp.open('GET', safari.extension.baseURI + 'lib/hibernationPage/index.html', true);
		xmlHttp.onreadystatechange = function() {
			if(xmlHttp.readyState === 4) {
				var html = xmlHttp.responseText;

				_.each(safari.application.browserWindows, function(win) {
					_.each(win.tabs, function(tab) {
						if(typeof tab.url !== 'undefined') {
							if(tab === win.activeTab) return;
							if(!tab.url.match(/^https?:\/\//)) return;

							_.delay(function() {
								var pageInfo = {
									url: tab.url,
									title: tab.title
								};

								var pageHtml = html.replace(/\{\/\*pageInfoObject\*\/\}/, JSON.stringify(pageInfo));
								var dataURL = 'data:text/html;charset=utf-8,' + encodeURIComponent(pageHtml);
								tab.url = dataURL;
							}, c * 100);

							c++;
						}
					});
				});
			}
		};
		xmlHttp.send(null);
	}
};

safari.application.addEventListener('command', performCommand, false);

Check if tab’s url is defined before hibernating


import Promise from "./rsvp/promise";
import EventTarget from "./rsvp/events";
import denodeify from "./rsvp/node";
import all from "./rsvp/all";
import race from "./rsvp/race";
import hash from "./rsvp/hash";
import rethrow from "./rsvp/rethrow";
import defer from "./rsvp/defer";
import { config, configure } from "./rsvp/config";
import map from "./rsvp/map";
import resolve from "./rsvp/resolve";
import reject from "./rsvp/reject";
import asap from "./rsvp/asap";
import filter from "./rsvp/filter";

config.async = asap; // default async is asap;

function async(callback, arg) {
  config.async(callback, arg);
}

function on() {
  config.on.apply(config, arguments);
}

function off() {
  config.off.apply(config, arguments);
}

export { Promise, EventTarget, all, race, hash, rethrow, defer, denodeify, configure, on, off, resolve, reject, async, map, filter };

import Promise from "./rsvp/promise";
import EventTarget from "./rsvp/events";
import denodeify from "./rsvp/node";
import all from "./rsvp/all";
import race from "./rsvp/race";
import hash from "./rsvp/hash";
import rethrow from "./rsvp/rethrow";
import defer from "./rsvp/defer";
import { config, configure } from "./rsvp/config";
import map from "./rsvp/map";
import resolve from "./rsvp/resolve";
import reject from "./rsvp/reject";
import asap from "./rsvp/asap";
import filter from "./rsvp/filter";

config.async = asap; // default async is asap;

function async(callback, arg) {
  config.async(callback, arg);
}

function on() {
  config.on.apply(config, arguments);
}

function off() {
  config.off.apply(config, arguments);
}

// Set up instrumentation through `window.__PROMISE_INTRUMENTATION__`
if (typeof window !== 'undefined' && typeof window.__PROMISE_INSTRUMENTATION__ === 'object') {
  var callbacks = window.__PROMISE_INSTRUMENTATION__;
  configure('instrument', true);
  for (var eventName in callbacks) {
    if (callbacks.hasOwnProperty(eventName)) {
      on(eventName, callbacks[eventName]);
    }
  }
}

export { Promise, EventTarget, all, race, hash, rethrow, defer, denodeify, configure, on, off, resolve, reject, async, map, filter };

Add window.__PROMISE_INSTRUMENTATION__ flag to register RSVP.on callbacks


import settings from './settings'

module.exports = {
  findDistance: (pos1, pos2) => {
    return Math.sqrt(Math.pow(pos2.x - pos1.x, 2) + Math.pow(pos2.y - pos1.y, 2))
  },

  limitPositions: (x,y,r) => {
    x = Math.max(r, Math.min(x, (settings.width - r)));
    y = Math.max(r, Math.min(y, (settings.height - r)));
    return [x,y];
  },

  chooseOne: function(args) {
    args = Array.prototype.slice.call(arguments);
    if(args.length === 1 && typeof Array.isArray(args[0])) {
      return args[0][Math.floor(Math.random() * args[0].length)]
    }
    return args[Math.floor(Math.random() * args.length)];
  },

  randomInt: (limit) => {
    return Math.floor(Math.random() * limit);
  },

  getAvgPosition: (eve) => {
    for(var j = 0; j < eve.bodyParts.length; j++) {
      xPos += eve.bodyParts[j].pos.x;
      yPos += eve.bodyParts[j].pos.y;
    }
    var pos = {
      x:xPos / eve.bodyParts.length,
      y:yPos / eve.bodyParts.length,
    }
    return pos;
  }
}

import settings from './settings'

module.exports = {
  findDistance: (pos1, pos2) => {
    return Math.sqrt(Math.pow(pos2.x - pos1.x, 2) + Math.pow(pos2.y - pos1.y, 2))
  },

  limitPositions: (x,y,r) => {
    x = Math.max(r, Math.min(x, (settings.width - r)));
    y = Math.max(r, Math.min(y, (settings.height - r)));
    return [x,y];
  },

  chooseOne: function(args) {
    args = Array.prototype.slice.call(arguments);
    if(args.length === 1 && typeof Array.isArray(args[0])) {
      return args[0][Math.floor(Math.random() * args[0].length)]
    }
    return args[Math.floor(Math.random() * args.length)];
  },

  randomInt: (limit) => {
    return Math.floor(Math.random() * limit);
  },

  getAvgPosition: (eve) => {
    var xPos = 0;
    var yPos = 0;
    for(var j = 0; j < eve.bodyParts.length; j++) {
      xPos += eve.bodyParts[j].pos.x;
      yPos += eve.bodyParts[j].pos.y;
    }
    var pos = {
      x:xPos / eve.bodyParts.length,
      y:yPos / eve.bodyParts.length,
    }
    return pos;
  }
}

Add helper function to get average postition of eve


'use strict';
module.exports = function(sequelize, DataTypes) {
  var campaign = sequelize.define('campaign', {
    name: DataTypes.STRING,
    fromName: DataTypes.STRING,
    fromEmail: DataTypes.STRING,
    emailSubject: DataTypes.STRING,
    emailBody: DataTypes.TEXT,
    slug: DataTypes.STRING
  }, {
    classMethods: {
      associate: function(models) {
        // associations can be defined here
        campaign.belongsTo(models.user);
        campaign.belongsTo(models.list);
        campaign.hasOne(models.campaignanalytics);
      }
    }
  });
  return campaign;
};

'use strict';
module.exports = function(sequelize, DataTypes) {
  var campaign = sequelize.define('campaign', {
    name: DataTypes.STRING,
    fromName: DataTypes.STRING,
    fromEmail: DataTypes.STRING,
    emailSubject: DataTypes.STRING,
    emailBody: DataTypes.TEXT,
    slug: DataTypes.STRING
  }, {
    classMethods: {
      associate: function(models) {
        // associations can be defined here
        campaign.belongsTo(models.user);
        campaign.belongsTo(models.list);
        campaign.hasMany(models.campaignsubscriber);
        campaign.hasOne(models.campaignanalytics);
      }
    }
  });
  return campaign;
};

Fix Campaign relationship with CampaignSubscriber


//
//   Copyright 2009-2015 Ilkka Oksanen <iao@iki.fi>
//
//   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';

const _ = require('lodash'),
      redis = require('../lib/redis').createClient(),
      notification = require('../lib/notification');

exports.sendSet = function*(userId, sessionId) {
    let settings = (yield redis.hgetall(`settings:${userId}`)) || {};

    _.defaults(settings, {
        activeDesktop: 0
    });

    let user = yield redis.hgetall(`user:${userId}`);

    let command = {
        id: 'SET',
        settings: {
            activeDesktop: parseInt(settings.activeDesktop),
            email: user.email,
            emailConfirmed: user.emailConfirmed === 'true'
        }
    };

    if (sessionId) {
        yield notification.send(userId, sessionId, command);
    } else {
        yield notification.broadcast(userId, command);
    }
};

//
//   Copyright 2009-2015 Ilkka Oksanen <iao@iki.fi>
//
//   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';

const _ = require('lodash'),
      redis = require('../lib/redis').createClient(),
      notification = require('../lib/notification');

exports.sendSet = function*(userId, sessionId) {
    let settings = (yield redis.hgetall(`settings:${userId}`)) || {};

    _.defaults(settings, {
        activeDesktop: 0,
        theme: 'default'
    });

    let user = yield redis.hgetall(`user:${userId}`);

    let command = {
        id: 'SET',
        settings: {
            activeDesktop: parseInt(settings.activeDesktop),
            theme: settings.theme,
            email: user.email,
            emailConfirmed: user.emailConfirmed === 'true'
        }
    };

    if (sessionId) {
        yield notification.send(userId, sessionId, command);
    } else {
        yield notification.broadcast(userId, command);
    }
};

Send theme setting during startup


const fs = require('fs');
const cp = require('child_process');
const readline = require('readline');
const commandLineArgs = require('command-line-args');

const outDir = './clips';

const optionDefinitions = [
  { name: 'delimiter', alias: 'd', type: String },
  { name: 'infile', alias: 'i', type: String },
];
const options = commandLineArgs(optionDefinitions);

var filename = options.infile;
var delimiter = options.hasOwnProperty('delimiter') ? options.delimiter : '---';

if (!options.hasOwnProperty('infile')) {
  // Can't do anything useful without input
  return;
}

const fs = require('fs');
const cp = require('child_process');
const im = require('imagemagick');
const readline = require('readline');
const commandLineArgs = require('command-line-args');

const outDir = './clips';

const optionDefinitions = [
  { name: 'delimiter', alias: 'd', type: String },
  { name: 'font', alias: 'f', type: String },
  { name: 'music', alias: 'm', type: String },
  { name: 'text', alias: 't', type: String },
];
const options = commandLineArgs(optionDefinitions);

var text = options.text;
var font = options.font;
var music = options.music;
var delimiter = options.hasOwnProperty('delimiter') ? options.delimiter : '---';

if (!options.hasOwnProperty('font')
 || !options.hasOwnProperty('music')
 || !options.hasOwnProperty('text')) {
  // Can't do anything useful without input
  return;
}

var lineReader = readline.createInterface({
  terminal: false,
  input: fs.createReadStream(filename)
});
var screenful = '';
lineReader.on('line', function (line) {
}

Add obvious arguments and start on main code


/* SDK Modules */
const simplePrefs = require('sdk/simple-prefs');

/* Constants */
const _24HOUR_PREF = '24hour';
const TIME_MSG = 'time';

/**
 * Initializes time by sending time options to the content scripts.
 */
exports.initTime = function(worker) {
    var options = {
        _24HOUR_PREF: simplePrefs.prefs[_24HOUR_PREF]
    };
    worker.port.emit(TIME_MSG, options);
}

/* SDK Modules */
const simplePrefs = require('sdk/simple-prefs');

/* Constants */
const _24HOUR_PREF = '24hour';
const TIME_MSG = 'time';

/**
 * Initializes time by sending time options to the content scripts.
 */
exports.initTime = function(worker) {
    var options = {}
    options[_24HOUR_PREF] = simplePrefs.prefs[_24HOUR_PREF];
    worker.port.emit(TIME_MSG, options);
}

Fix 24-hour clock preference not working


goog.provide('ol.style.Text');

/**
 * @constructor
 * @param {ol.style.TextOptions} options Options.
 */
ol.style.Text = function(options) {

  /**
   * @type {string|undefined}
   */
  this.font = options.font;

  /**
   * @type {string|undefined}
   */
  this.text = options.text;

  /**
   * @type {string|undefined}
   */
  this.textAlign = options.textAlign;

  /**
   * @type {string|undefined}
   */
  this.textBaseline = options.textBaseline;

  /**
   * @type {number}
   */
  this.width = options.width;
};

/**
 * @param {ol.style.Text} textStyle1 Text style 1.
 * @param {ol.style.Text} textStyle2 Text style 2.
 * @return {boolean} Equals.
 */
ol.style.Text.equals = function(textStyle1, textStyle2) {
  if (!goog.isNull(textStyle1)) {
    if (!goog.isNull(textStyle2)) {
      return textStyle1 === textStyle2 || (
          textStyle1.font == textStyle2.font &&
          textStyle1.text == textStyle2.text &&
          textStyle1.textAlign == textStyle2.textAlign &&
          textStyle1.textBaseline == textStyle2.textBaseline);
    } else {
      return false;
    }
  } else {
    if (!goog.isNull(textStyle2)) {
      return false;
    } else {
      return true;
    }
  }
};

goog.provide('ol.style.Text');

/**
 * @constructor
 * @param {ol.style.TextOptions} options Options.
 */
ol.style.Text = function(options) {

  /**
   * @type {string|undefined}
   */
  this.font = options.font;

  /**
   * @type {string|undefined}
   */
  this.text = options.text;

  /**
   * @type {string|undefined}
   */
  this.textAlign = options.textAlign;

  /**
   * @type {string|undefined}
   */
  this.textBaseline = options.textBaseline;
};

/**
 * @param {ol.style.Text} textStyle1 Text style 1.
 * @param {ol.style.Text} textStyle2 Text style 2.
 * @return {boolean} Equals.
 */
ol.style.Text.equals = function(textStyle1, textStyle2) {
  if (!goog.isNull(textStyle1)) {
    if (!goog.isNull(textStyle2)) {
      return textStyle1 === textStyle2 || (
          textStyle1.font == textStyle2.font &&
          textStyle1.text == textStyle2.text &&
          textStyle1.textAlign == textStyle2.textAlign &&
          textStyle1.textBaseline == textStyle2.textBaseline);
    } else {
      return false;
    }
  } else {
    if (!goog.isNull(textStyle2)) {
      return false;
    } else {
      return true;
    }
  }
};

Remove unused ol.style.Text width property


var _ = require('lodash'),
    Promise = require('bluebird');

module.exports.show = function(params) {
  var path = "/statuses/show.json?id=" + params.id;
  return this.get(path, params);
};

module.exports.lookup = function(params) {
  var self = this;
  var ids = _.chunk(params.id, 100);
  delete params.id;

  return Promise.resolve(ids)
  .map(function(ids) {
    var path = "/statuses/lookup.json?id=" + ids;
    return self.get(path, params)
    .then(function(res) {return { data: res };})
    .catch(function(e)  {return { error: e };});
  }).reduce(function(obj, res) {
    if (res.data) {
      obj.data.push(res.data);
    }
    if (res.error) {
      obj.errors.push(res.error);
    }
    return obj;
  }, {data: [], errors: []})
  .then(function(result) {
    result.data = _.flatten(result.data);
    result.errors = _.flatten(result.errors);
    return result;
  });
};

var _ = require('lodash'),
    Promise = require('bluebird');

module.exports.userTimeline = function(params) {
  return this.get("/statuses/user_timeline.json", params);
}

module.exports.show = function(params) {
  var path = "/statuses/show.json?id=" + params.id;
  return this.get(path, params);
};

module.exports.lookup = function(params) {
  var self = this;
  var ids = _.chunk(params.id, 100);
  delete params.id;

  return Promise.resolve(ids)
  .map(function(ids) {
    var path = "/statuses/lookup.json?id=" + ids;
    return self.get(path, params)
    .then(function(res) {return { data: res };})
    .catch(function(e)  {return { error: e };});
  }).reduce(function(obj, res) {
    if (res.data) {
      obj.data.push(res.data);
    }
    if (res.error) {
      obj.errors.push(res.error);
    }
    return obj;
  }, {data: [], errors: []})
  .then(function(result) {
    result.data = _.flatten(result.data);
    result.errors = _.flatten(result.errors);
    return result;
  });
};

Add user timeline API method.


import babel  from 'rollup-plugin-babel';
import eslint from 'rollup-plugin-eslint';
import uglify from 'rollup-plugin-uglify';

export default {
	entry: 'src/game.js',
	dest: 'build/invaders.min.js',
	format: 'iife',
	sourceMap: 'inline',
	plugins: [
		eslint( {
			exclude: ['src/lib/Three.js', 'src/node_modules/**']
		} ),
		babel( {
			exclude: ['src/node_modules/**']
		} ),
		(process.env.NODE_ENV === 'production' && uglify())
	]
};

import babel  from 'rollup-plugin-babel';
import eslint from 'rollup-plugin-eslint';
import uglify from 'rollup-plugin-uglify';

export default {
	entry: 'src/game.js',
	dest: 'build/invaders.min.js',
	format: 'iife',
	sourceMap: 'inline',
	plugins: [
		eslint( {
			exclude: ['src/lib/Three.js']
		} ),
		babel(),
		(process.env.NODE_ENV === 'production' && uglify())
	]
};

Remove unnecessary excludes of src/node_modules


(function() {
    angular
        .module("HdmiSwitch", ["ngResource", "ngMaterial"])
        .factory("Mode", function($http){
            var mode = {};
            mode.address = "192.168.1.181:23";
            mode.switch = function(newMode){
                return $http.post("/switch-mode", {
                    mode: newMode,
                    address: mode.address
                });
            };
            return mode;
        })
        .factory("Connection", function($timeout){
            var w = null;
            var connect;
            connect = function(){
                var w;
                if (w === null) {
                    w = new WebSocket("ws://" + location.host + "/socket");
                }
                return w;
            };
            return {
                connect: function(callback){
                    var w = connect();
                    w.onmessage = function(){
                        var args = [].slice.call(arguments);
                        return $timeout(function(){
                            return callback.apply(null, args);
                        });
                    };
                }
            };
        })
        .controller("HdmiSwitchCtrl", function(Mode, Connection){
            var vm = this;
            vm.mode = Mode;
            vm.messages = "";
            Connection.connect(function(message){
                var d = new Date();
                vm.messages = d.getHours() + ":" + d.getMinutes() + ":" + d.getSeconds() + " " + message.data + "\n" + vm.messages;
            });
            return vm;
        });
})();

(function() {
    angular
        .module("HdmiSwitch", ["ngResource", "ngMaterial"])
        .factory("Mode", function($http){
            var mode = {};
            mode.address = "192.168.1.181:23";
            mode.switch = function(newMode){
                return $http.post("/switch-mode", {
                    mode: newMode,
                    address: mode.address
                });
            };
            return mode;
        })
        .factory("Connection", function($timeout){
            var socket = null;
            var connect;
            connect = function(){
                var socket;
                if (socket === null) {
                    socket = new WebSocket("ws://" + location.host + "/socket");
                }
                return socket;
            };
            return {
                connect: function(callback){
                    var socket = connect();
                    socket.onmessage = function(){
                        var args = [].slice.call(arguments);
                        return $timeout(function(){
                            return callback.apply(null, args);
                        });
                    };
                }
            };
        })
        .controller("HdmiSwitchCtrl", function(Mode, Connection){
            var vm = this;
            vm.mode = Mode;
            vm.messages = "";
            Connection.connect(function(message){
                var d = new Date();
                vm.messages = d.getHours() + ":" + d.getMinutes() + ":" + d.getSeconds() + " " + message.data + "\n" + vm.messages;
            });
            return vm;
        });
})();

Use more descriptive variable name.


#!/usr/bin/env node

var stdin = require('get-stdin')
var parseTorrent = require('../')

function usage () {
  console.error('Usage: parse-torrent /path/to/torrent')
  console.error('       parse-torrent magnet_uri')
  console.error('       parse-torrent --stdin')
}

function error (err) {
  console.error(err.message)
  process.exit(1)
}

var arg = process.argv[2]

if (!arg) {
  console.error('Missing required argument')
  usage()
  process.exit(1)
}

if (arg === '--stdin' || arg === '-') stdin.buffer().then(onTorrentId).catch(error)
else if (arg === '--version' || arg === '-v') console.log(require('../package.json').version)
else onTorrentId(arg)

function onTorrentId (torrentId) {
  parseTorrent.remote(torrentId, function (err, parsedTorrent) {
    if (err) return error(err)

    delete parsedTorrent.info
    delete parsedTorrent.infoBuffer
    delete parsedTorrent.infoHashBuffer
    console.log(JSON.stringify(parsedTorrent, undefined, 2))
  })
}

#!/usr/bin/env node

const stdin = require('get-stdin')
const parseTorrent = require('../')

function usage () {
  console.error('Usage: parse-torrent /path/to/torrent')
  console.error('       parse-torrent magnet_uri')
  console.error('       parse-torrent --stdin')
}

function error (err) {
  console.error(err.message)
  process.exit(1)
}

const arg = process.argv[2]

if (!arg) {
  console.error('Missing required argument')
  usage()
  process.exit(1)
}

if (arg === '--stdin' || arg === '-') stdin.buffer().then(onTorrentId).catch(error)
else if (arg === '--version' || arg === '-v') console.log(require('../package.json').version)
else onTorrentId(arg)

function onTorrentId (torrentId) {
  parseTorrent.remote(torrentId, function (err, parsedTorrent) {
    if (err) return error(err)

    delete parsedTorrent.info
    delete parsedTorrent.infoBuffer
    delete parsedTorrent.infoHashBuffer
    console.log(JSON.stringify(parsedTorrent, undefined, 2))
  })
}

Use const instead of var


export const IMAGE_MIME_TYPES = ['image/png', 'image/jpeg', 'image/gif', 'image/bmp', 'image/webp', 'image/x-icon', 'image/*'];

function isImage(file) {
    const type = (file.type || '').toLowerCase();

    return (IMAGE_MIME_TYPES.indexOf(type) !== -1);
}

function isDoc() {
    return true;
}

export default function getCompareFunction(type) {
    if (type === 'images') {
        return isImage;
    }

    return isDoc;
}

export const IMAGE_MIME_TYPES = ['image/png', 'image/jpeg', 'image/gif', 'image/bmp', 'image/webp', 'image/x-icon', 'image/*'];
export const AUDIO_MIME_TYPES = ['audio/mp3', 'audio/mpeg3', 'audio/x-mpeg-3', 'audio/midi', 'audio/mpeg', 'audio/webm', 'audio/ogg', 'audio/wav', 'audio/x-mpeg', 'audio/x-wav', 'audio/*'];
export const VIDEO_MIME_TYPES = ['video/mp4', 'video/webm', 'video/ogg', 'video/x-flv', 'application/x-mpegURL', 'video/MP2T', 'video/3gpp', 'video/quicktime', 'video/x-msvideo', 'video/x-ms-wmv', 'video/*'];

function isOfType(types) {
    return (file) => {
        const type = (file.type || '').toLowerCase();

        return (types.indexOf(type) !== -1);
    };
}

function isDoc() {
    return true;
}

export default function getCompareFunction(type) {
    if (type === 'images') {
        return isOfType(IMAGE_MIME_TYPES);
    }

    if (type === 'audio') {
        return isOfType(AUDIO_MIME_TYPES);
    }

    if (type === 'video') {
        return isOfType(VIDEO_MIME_TYPES);
    }

    return isDoc;
}

Add mime-type checker for audio and video