'use strict';

module.exports = function (gulp, $) {

  gulp.task('scripts:setup', function () {
    return gulp.src('source')
      .pipe($.symlink('examples/source', { force: true }));
  });

  gulp.task('jscs', function () {
    return gulp.src([
      'source/**/*.js',
      'examples/js/*.js'
    ])
      .pipe($.jscs());
  });

  gulp.task('jshint', function () {
    return gulp.src([
        'source/**/*.js',
        'examples/js/*.js'
      ])
      .pipe($.jshint())
      .pipe($.jshint.reporter('jshint-stylish'))
      .pipe($.jshint.reporter('fail'));
  });

  gulp.task('concat', function () {
    return gulp.src([
        'source/**/*.js',
        '!**/*.spec.js'
      ])
      .pipe($.concat('angular-ui-tree.js'))
      .pipe(gulp.dest('dist'));
  });

  gulp.task('uglify', ['concat'], function () {
    return gulp.src('dist/angular-ui-tree.js')
      .pipe($.uglify({
        preserveComments: 'some'
      }))
      .pipe($.rename('angular-ui-tree.min.js'))
      .pipe(gulp.dest('dist'));
  });

  gulp.task('karma', function () {
    var server = new $.karma.Server({
      configFile: __dirname + '/../karma.conf.js',
      singleRun: true
    }, function (err) {
      process.exit(err ? 1 : 0);
    });
    return server.start();
  });
};

'use strict';

module.exports = function (gulp, $) {

  gulp.task('scripts:setup', function () {
    return gulp.src('source')
      .pipe($.symlink('examples/source'));
  });

  gulp.task('jscs', function () {
    return gulp.src([
      'source/**/*.js',
      'examples/js/*.js'
    ])
      .pipe($.jscs());
  });

  gulp.task('jshint', function () {
    return gulp.src([
        'source/**/*.js',
        'examples/js/*.js'
      ])
      .pipe($.jshint())
      .pipe($.jshint.reporter('jshint-stylish'))
      .pipe($.jshint.reporter('fail'));
  });

  gulp.task('concat', function () {
    return gulp.src([
        'source/**/*.js',
        '!**/*.spec.js'
      ])
      .pipe($.concat('angular-ui-tree.js'))
      .pipe(gulp.dest('dist'));
  });

  gulp.task('uglify', ['concat'], function () {
    return gulp.src('dist/angular-ui-tree.js')
      .pipe($.uglify({
        preserveComments: 'some'
      }))
      .pipe($.rename('angular-ui-tree.min.js'))
      .pipe(gulp.dest('dist'));
  });

  gulp.task('karma', function () {
    var server = new $.karma.Server({
      configFile: __dirname + '/../karma.conf.js',
      singleRun: true
    }, function (err) {
      process.exit(err ? 1 : 0);
    });
    return server.start();
  });
};

Fix symlink creation for the examples


(function(buster, fn, when) {

var assert = buster.assert;

function f(x, y) {
	return x + y;
}

buster.testCase('when/function', {

	'apply': {
		'should return a promise': function() {
			var result = fn.apply(f, [1, 2]);
			assert(result && typeof result.then === 'function');
		},

		'should accept values for arguments': function() {
			var result = fn.apply(f, [1, 2]);
			return when(result, function(result) {
				assert.equals(result, 3);
			});
		},

	},

	'call': {
		'should return a promise': function() {
			var result = fn.call(f, 1, 2);
			assert(result && typeof result.then === 'function');
		},

		'should accept values for arguments': function() {
			var result = fn.call(f, 1, 2);
			return when(result, function(result) {
				assert.equals(result, 3);
			});
		},

	},

	'bind': {
		'should return a function': function() {
			assert.isFunction(fn.bind(f, null));
		}

	}

});

})(
	this.buster  || require('buster'),
	this.when_fn || require('../function'),
	this.when    || require('../when')
);

(function(buster, fn, when) {

var assert = buster.assert;

function assertIsPromise(something) {
  var message = 'Object is not a promise';
  buster.assert(when.isPromise(something), message);
}

function f(x, y) {
	return x + y;
}

buster.testCase('when/function', {

	'apply': {
		'should return a promise': function() {
			var result = fn.apply(f, [1, 2]);
			assertIsPromise(result);
		},

		'should accept values for arguments': function() {
			var result = fn.apply(f, [1, 2]);
			return when(result, function(result) {
				assert.equals(result, 3);
			});
		},

	},

	'call': {
		'should return a promise': function() {
			var result = fn.call(f, 1, 2);
			assertIsPromise(result);
		},

		'should accept values for arguments': function() {
			var result = fn.call(f, 1, 2);
			return when(result, function(result) {
				assert.equals(result, 3);
			});
		},

	},

	'bind': {
		'should return a function': function() {
			assert.isFunction(fn.bind(f, null));
		}

	}

});

})(
	this.buster  || require('buster'),
	this.when_fn || require('../function'),
	this.when    || require('../when')
);

Remove duplication and extra knowledge


"use strict";

var assert = require('assert');
var Browser = require('zombie');
var app = require('../../app');
var config = require('config');

Browser.localhost(config.hosting_server.host, config.server.port);

describe("basic headless test", function() {
  var server, browser;

  before(function() {
    server = app.listen(config.server.port);
  });

  after(function() {
    server.close();
  });

  beforeEach(function() {
    browser = new Browser();
  });

  it("loads front page", function(done) {
    browser.visit('/', function() {
      assert(
        browser.text(".hosting-app-welcome h1.text-center") === "Welcome to PopIt",
        "welcome text present"
      );
      done();
    });
  });
});

"use strict";

var assert = require('assert');
var Browser = require('zombie');
var app = require('../../app');
var config = require('config');

Browser.localhost(config.hosting_server.host, config.server.port);

describe("basic headless test", function() {
  this.timeout(5000);
  var server, browser;

  before(function() {
    server = app.listen(config.server.port);
  });

  after(function() {
    server.close();
  });

  beforeEach(function() {
    browser = new Browser();
  });

  it("loads front page", function(done) {
    browser.visit('/', function() {
      assert(
        browser.text(".hosting-app-welcome h1.text-center") === "Welcome to PopIt",
        "welcome text present"
      );
      done();
    });
  });
});

Increase timeout on UI tests


#!/usr/bin/env node

var path   = require('path')
  , target = require(path.join(process.cwd(), process.argv[2]))

var result = require('../lib/execute')(target)

if (!result.success) {
  console.log(target.feature, 'is not supported in this environment')
  result.log.forEach(print)
  process.exit(1)
}

function print(entry, i, log) {
  var ok    = entry[0]
    , pre   = i === (log.length - 1)? '└' : '├'
    , tick  = ok? '✔' : '✘'
    , info  = entry[1]
    , error = entry[2] || ''

  if (ok) {
    console.log(pre, tick, info)
  } else {
    console.error(pre, tick, info, error)
  }
}

#!/usr/bin/env node

var path   = require('path')

try {
  var target = require(path.join(process.cwd(), process.argv[2]))
    , result = require('../lib/execute')(target)

  if (!result.success) {
    console.error(target.feature, 'is not supported in this environment')
    result.log.forEach(print)
    process.exit(1)
  }
} catch (e) {
  console.error('Unable to load tests; feature detection failed!')
  process.exit(1)
}

function print(entry, i, log) {
  var ok    = entry[0]
    , pre   = i === (log.length - 1)? '└' : '├'
    , tick  = ok? '✔' : '✘'
    , info  = entry[1]
    , error = entry[2]? '(' + entry[2] + ')' : ''

  if (ok) {
    console.log(pre, tick, info)
  } else {
    console.error(pre, tick, info, error)
  }
}

Improve error handling and reporting in executable.


app.factory('Category', function($resource) {
  return $resource('/api/categories/:id',
    { id: '@resource_id' }, {
      'search': { method: 'GET', isArray: true }
  });
});

app.factory('SessionService', function($http, $state, $location) {
  var bogusSession = injectedSession
    , pre
    , status;

  function init() {            // Logged in  : Logged out
    pre = checkSession() ? bogusSession.user : { name: 'Logged out', uid: '0' };
              status = pre.uid != "0" ? true : false;
  }

  function checkSession() {
    if(bogusSession != null) return Object.keys(bogusSession).length !== 0;
    else return false;
  }

  init();

  return {
    pre: function() { return pre; },
    status: function() { return status; },
    logout: function() {
      $http.get("/logout").then(function (data, status, headers, config) {
        bogusSession = null; init();
        if($state.current.name != 'signin') $state.go("index");
      })
    },
    checkAuth: function() {
      if(this.status() == false && $state.current.name != 'signin')
        $location.path('/');
      else
        $location.path('/home');
    }
  };
});

Create angular Category $resource service for data handling


import history from '!json!../assets/data/history.json'
import metadata from '!json!../assets/data/metadata.json'
// Loading latest season in the main bundle itself
import latestSeasonData from '!json!../assets/data/season-latest.json'

const seasonDataCtx = require.context(
  'file-loader!../assets/data/',
  false,
  /^\.\/.*\.json$/
)

const distDataCtx = require.context(
  'file-loader!../assets/data/distributions/',
  false,
  /^\.\/.*\.json$/
)

const seasonDataUrls = seasonDataCtx.keys().reduce((acc, key) => {
  if (key.startsWith('./season-')) {
    // Identifier is like '2013-2014'
    acc[key.slice(9, -5)] = req(key)
  }
  return acc
}, {})

const distDataUrls = distDataCtx.keys().reduce((acc, key) => {
  if (key.startsWith('./season-')) {
    // Identifier is like '2013-2014-hhs10'
    acc[key.slice(9, -5)] = req(key)
  }
  return acc
}, {})

export { seasonDataUrls, distDataUrls, latestSeasonData, history, metadata }

import history from '!json!../assets/data/history.json'
import metadata from '!json!../assets/data/metadata.json'
// Loading latest season in the main bundle itself
import latestSeasonData from '!json!../assets/data/season-latest.json'

const seasonDataCtx = require.context(
  'file-loader!../assets/data/',
  false,
  /^\.\/.*\.json$/
)

const distDataCtx = require.context(
  'file-loader!../assets/data/distributions/',
  false,
  /^\.\/.*\.json$/
)

const seasonDataUrls = seasonDataCtx.keys().reduce((acc, key) => {
  if (key.startsWith('./season-')) {
    // Identifier is like '2013-2014'
    acc[key.slice(9, -5)] = seasonDataCtx(key)
  }
  return acc
}, {})

const distDataUrls = distDataCtx.keys().reduce((acc, key) => {
  if (key.startsWith('./season-')) {
    // Identifier is like '2013-2014-hhs10'
    acc[key.slice(9, -5)] = distDataCtx(key)
  }
  return acc
}, {})

export { seasonDataUrls, distDataUrls, latestSeasonData, history, metadata }

Fix reference to context in require.context


var program = require('commander');
var AWS = require('aws-sdk');
var versions = require('./lib/versions');
var stacks = require('./lib/stacks');

AWS.config.update({region: 'us-east-1'});

program
  .parse(process.argv);

var name = program.args[0];
if (!name) {
  program.help();
  process.exit(1);
}

stacks.get(AWS, name, function(err, stack) {
  if (err) {
    console.error(err);
    process.exit(1);
  }

  versions.list(AWS, name, function(err, versions) {
    if (err) {
      console.error(err);
      process.exit(1);
    }

    var version = versions.filter(function(version) {
      return (version.AppVersion === program.version);
    })[0];

    if (!version) {
      console.error('Failed to find version with id', program.version);
      process.exit(1);
    }

    console.log(version)

  });
});

var program = require('commander');
var AWS = require('aws-sdk');
var versions = require('./lib/versions');
var stacks = require('./lib/stacks');

AWS.config.update({region: 'us-east-1'});

program
  .parse(process.argv);

var name = program.args[0];
if (!name) {
  program.help();
  process.exit(1);
}

var deleteVersion = program.args[1];
if (!deleteVersion) {
  program.help();
  process.exit(1);
}

stacks.get(AWS, name, function(err, stack) {
  if (err) {
    console.error(err);
    process.exit(1);
  }

  versions.list(AWS, name, function(err, results) {
    if (err) {
      console.error(err);
      process.exit(1);
    }

    var version = results.filter(function(version) {
      return (version.AppVersion === deleteVersion);
    })[0];

    if (!version) {
      console.error('Failed to find version with id', deleteVersion);
      process.exit(1);
    }

    versions.remove(AWS, version.StackName, function(err) {
      if (err) {
        console.error(err);
        process.exit(1);
      }
    });
  });
});

Delete version works now in cli


var wordpress = require( "../" );

module.exports = function( grunt ) {

var client = (function() {
	var _client;

	return function() {
		if ( !_client ) {
			var config = grunt.config( "wordpress" );
			config.verbose = grunt.option( "verbose" ) || false;

			_client = wordpress.createClient( config );
			_client.log = function() {
				grunt.log.writeln.apply( grunt.log, arguments );
			};
			_client.logError = function() {
				grunt.log.error.apply( grunt.log, arguments );
			};
		}

		return _client;
	};
})();

grunt.registerTask( "wordpress-validate", function() {
	var done = this.async();
	client().validate(function( error ) {
		if ( error ) {
			grunt.log.error( error );
			return done( false );
		}

		done();
	});
});

grunt.registerTask( "wordpress-sync", function() {
	this.requires( "wordpress-validate" );

	var done = this.async();
	client().sync(function( error ) {
		if ( error ) {
			grunt.log.error( error );
			return done( false );
		}

		done();
	});
});

grunt.registerTask( "wordpress-publish", "wordpress-validate wordpress-sync" );
grunt.registerTask( "wordpress-deploy", "build-wordpress wordpress-publish" );
grunt.registerTask( "deploy", "wordpress-deploy" );

};

var wordpress = require( "../" );

module.exports = function( grunt ) {

var client = (function() {
	var _client;

	return function() {
		if ( !_client ) {
			var config = grunt.config( "wordpress" );
			config.verbose = grunt.option( "verbose" ) || false;

			_client = wordpress.createClient( config );
			_client.log = function() {
				grunt.log.writeln.apply( grunt.log, arguments );
			};
			_client.logError = function() {
				grunt.log.error.apply( grunt.log, arguments );
			};
		}

		return _client;
	};
})();

grunt.registerTask( "wordpress-validate", function() {
	client().validate( this.async() );
});

grunt.registerTask( "wordpress-sync", function() {
	this.requires( "wordpress-validate" );
	client().sync( this.async() );
});

grunt.registerTask( "wordpress-publish", [ "wordpress-validate", "wordpress-sync" ] );
grunt.registerTask( "wordpress-deploy", [ "build-wordpress", "wordpress-publish" ] );
grunt.registerTask( "deploy", [ "wordpress-deploy" ] );

};

Update tasks to Grunt 0.4 API


import test from 'ava';
import Registry from '../src/registry';

test('Registry.once registers one handler to singles', t => {

    let registry = Registry([]);
    registry.once('enter', () => {});

    t.true(registry.singles.enter.length === 1);

});

test('Registry.once handlers are removed after invocation', t => {

    let registry = Registry([document.body]);
    registry.once('enter', () => {})
        .emit('enter', document.body);

    t.true(!registry.singles.enter.length);

});

import test from 'ava';
import Registry from '../src/registry';

test('Registry.once registers one handler to singles', t => {

    let registry = Registry([]);

    registry.once('enter', () => {});
    t.true(registry.singles.enter.length === 1);

    registry.once('exit', () => {});
    t.true(registry.singles.exit.length === 1);

    registry.once('enter', () => {});
    t.true(registry.singles.enter.length === 2);

});

Update Registry.once tests to match Registry.on


var https = require('https'),
    fs = require('fs'),
    options = {
        hostname: 'angular.io',
        path: '/docs/ts/latest/api/api-list.json',
        method: 'GET'
    },

    file = fs.createWriteStream('src/data/api-list.json'),

    req = https.request(options, function(res) {
        if (res.statusCode === 200) {
            console.log('Download ok');
        }
        res.on('data', function(d) {
            file.write(d);
        });
    });

req.end();

req.on('error', function(e) {
    console.error(e);
});

var https = require('https'),
    fs = require('fs'),
    options = {
        hostname: 'angular.io',
        path: '/generated/docs/api/api-list.json',
        method: 'GET'
    },

    file = fs.createWriteStream('src/data/api-list.json'),

    req = https.request(options, function(res) {
        if (res.statusCode === 200) {
            console.log('Download ok');
        }
        res.on('data', function(d) {
            file.write(d);
        });
    });

req.end();

req.on('error', function(e) {
    console.error(e);
});

Update api-list JSON with new angular.io


'use strict';

var express = require('express');

/*
 * Require Some Controller
 */
// var controller = require()

module.exports = function(app) {

  var router = express.Router()

/*
 * Routing logic
 */

  // router.get('/', controller.index )
  // router.post('/', controller.create )
  // router.get('/:param', controller.show )
  // router.put('/:param', controller.update )
  // router.delete('/:param', controller.delete )

  app.use('/api/<%= names.slug %>', router)
};

;(function(){
    'use strict';

    var express = require('express');

    /*
     * Require Some Controller
     */

    // var controller = require()

    /**
     * [exports description]
     * @param  {[type]} app [description]
     * @return {[type]}     [description]
     */
    module.exports = function(app) {

      var router = express.Router()

      /*
       * Routing logic
       */

      // router.get('/', controller.index )
      // router.post('/', controller.create )
      // router.get('/:param', controller.show )
      // router.put('/:param', controller.update )
      // router.delete('/:param', controller.delete )

      app.use('/api/<%= names.slug %>', router)
    };

}).call(this);

Update server-route sub-generator; Refactor Code, wrap in IFFE


/* @flow */

import defineType, { assertNodeType } from "./index";

defineType("AwaitExpression", {
  builder: ["argument"],
  visitor: ["argument"],
  aliases: ["Expression", "Terminatorless"],
  fields: {
    argument: {
      validate: assertNodeType("Expression"),
    }
  }
});

defineType("BindExpression", {
  visitor: ["object", "callee"],
  fields: {
    // todo
  }
});

defineType("Decorator", {
  visitor: ["expression"],
  fields: {
    expression: {
      validate: assertNodeType("Expression")
    }
  }
});

defineType("DoExpression", {
  visitor: ["body"],
  aliases: ["Expression"],
  fields: {
    body: {
      validate: assertNodeType("BlockStatement")
    }
  }
});

defineType("ExportDefaultSpecifier", {
  visitor: ["exported"],
  aliases: ["ModuleSpecifier"],
  fields: {
    exported: {
      validate: assertNodeType("Identifier")
    }
  }
});

defineType("ExportNamespaceSpecifier", {
  visitor: ["exported"],
  aliases: ["ModuleSpecifier"],
  fields: {
    exported: {
      validate: assertNodeType("Identifier")
    }
  }
});

defineType("RestProperty", {
  visitor: ["argument"],
  aliases: ["UnaryLike"],
  fields: {
    argument: {
      validate: assertNodeType("LVal")
    }
  }
});

defineType("SpreadProperty", {
  visitor: ["argument"],
  aliases: ["UnaryLike"],
  fields: {
    argument: {
      validate: assertNodeType("Expression")
    }
  }
});

/* @flow */

import defineType, { assertNodeType } from "./index";

defineType("AwaitExpression", {
  builder: ["argument"],
  visitor: ["argument"],
  aliases: ["Expression", "Terminatorless"],
  fields: {
    argument: {
      validate: assertNodeType("Expression"),
    }
  }
});

defineType("BindExpression", {
  visitor: ["object", "callee"],
  aliases: ["Expression"],
  fields: {
    // todo
  }
});

defineType("Decorator", {
  visitor: ["expression"],
  fields: {
    expression: {
      validate: assertNodeType("Expression")
    }
  }
});

defineType("DoExpression", {
  visitor: ["body"],
  aliases: ["Expression"],
  fields: {
    body: {
      validate: assertNodeType("BlockStatement")
    }
  }
});

defineType("ExportDefaultSpecifier", {
  visitor: ["exported"],
  aliases: ["ModuleSpecifier"],
  fields: {
    exported: {
      validate: assertNodeType("Identifier")
    }
  }
});

defineType("ExportNamespaceSpecifier", {
  visitor: ["exported"],
  aliases: ["ModuleSpecifier"],
  fields: {
    exported: {
      validate: assertNodeType("Identifier")
    }
  }
});

defineType("RestProperty", {
  visitor: ["argument"],
  aliases: ["UnaryLike"],
  fields: {
    argument: {
      validate: assertNodeType("LVal")
    }
  }
});

defineType("SpreadProperty", {
  visitor: ["argument"],
  aliases: ["UnaryLike"],
  fields: {
    argument: {
      validate: assertNodeType("Expression")
    }
  }
});

Add Expression alias to BindExpression


const utilities = require('./utilities');

const limited   = (card) => card.limited ? ' | limited' : '';
const unique    = (card) => card.unique ? ' | unique' : '';
const points    = (card) => `${card.points}pt${card.points > 1 ? 's': ''}`;
const multiCard = (card) => `\n• ${card.name} (${card.slot}) use \`/card ${card.name} (${card.slot})\` or \`/card #id ${utilities.cardId(card)}\``;

function cardReduce(currentString, card) {
	if(typeof currentString === 'object') {
		currentString = cardReduce('', currentString);
	}

	return currentString + multiCard(card);
}

const templates = {
	// Template for a single card
	card: (card) => `${card.name} (${card.slot}${limited(card)}${unique(card)}): ${points(card)}\n${card.text}`,

	// Template for multiple cards
	multiple: (cards, query) => {
		let cardString = cards.reduce(cardReduce);

		return `I found more than one card matching *'${query.text}'*:${cardString}`;
	},

	// Template for no results
	notFound: (query) => `Sorry i couldn't find a card for *'${query.text}'*`
};

module.exports = templates;

const utilities = require('./utilities');

const limited   = (card) => card.limited ? ' | limited' : '';
const unique    = (card) => card.unique ? ' | unique' : '';
const points    = (card) => `${card.points}pt${card.points > 1 ? 's': ''}`;
const multiCard = (card) => `\n• *${card.name}* (${card.slot}) use \`/card ${card.name} (${card.slot})\` or \`/card #id ${utilities.cardId(card)}\``;

function cardReduce(currentString, card) {
	if(typeof currentString === 'object') {
		currentString = cardReduce('', currentString);
	}

	return currentString + multiCard(card);
}

const templates = {
	// Template for a single card
	card: (card) => `${card.name} (${card.slot}${limited(card)}${unique(card)}): ${points(card)}\n${card.text}`,

	// Template for multiple cards
	multiple: (cards, query) => {
		let cardString = cards.reduce(cardReduce);

		return `I found more than one card matching *'${query.text}'*:${cardString}`;
	},

	// Template for no results
	notFound: (query) => `Sorry i couldn't find a card for *'${query.text}'*`
};

module.exports = templates;

Make the card name more clear on a multiple result


angular.module('sgApp')
  .controller('ElementCtrl', function($scope, $stateParams, $state,
    $rootScope, Styleguide) {

    var section = $stateParams.section.split('.'),
      reference = section[0] + '.' + section[1],
      modifier = section[2];

    Styleguide.get()
      .success(function(data) {
        var result = data.sections.filter(function(item) {
          return reference == item.reference;
        }),
        element = result[0];
        if (modifier) {
          element = element.modifiers[modifier - 1];
        }
        $scope.section = element;
      })
      .error(function() {
        console.log('error');
      });

  });

angular.module('sgApp')
  .controller('ElementCtrl', function($scope, $stateParams, $state,
    $rootScope, Styleguide) {

    var section = $stateParams.section.split('.'),
      reference,
      modifier;

    if (section.length == 3) {
      modifier = section[2];
      reference = section[0] + '.' + section[1];
    } else {
      reference = $stateParams.section;
    }

    Styleguide.get()
      .success(function(data) {
        var result = data.sections.filter(function(item) {
          return reference == item.reference;
        }),
        element = result[0];
        if (modifier) {
          element = element.modifiers[modifier - 1];
        }
        $scope.section = element;
      })
      .error(function() {
        console.log('error');
      });

  });

Fix fullscreen to work also with first level items


Package.describe({
  summary: "meteor sql bindings for server-side calls"
});

Npm.depends({ "newrelic": "1.0.1" });

Package.on_use(function(api) {
  if (api.export) api.export('newrelic', 'server');
  api.add_files('newrelic_npm.js', 'server');
});

Package.describe({
  summary: "meteor sql bindings for server-side calls"
});

Npm.depends({ "newrelic": "1.x.x" });

Package.on_use(function(api) {
  if (api.export) api.export('newrelic', 'server');
  api.add_files('newrelic_npm.js', 'server');
});

Enable latest version of newrelic agent


import sortBy from 'lodash/sortBy';
import { validateAssessmentItem } from '../../utils';

function sorted(items) {
  return sortBy(items, ['order']);
}

export function getNodeAssessmentItems(state) {
  return function(contentNodeId) {
    return sorted(Object.values(state.assessmentItemsMap[contentNodeId]));
  };
}

export function getNodeAssessmentItemErrors(state) {
  return function(contentNodeId) {
    return getNodeAssessmentItems(state)(contentNodeId).map(validateAssessmentItem);
  };
}

export function getInvalidNodeAssessmentItemsCount(state) {
  return function(contentNodeId) {
    return getNodeAssessmentItemErrors(state)(contentNodeId).filter(arr => arr.length).length;
  };
}

export function getAssessmentItemsAreValid(state) {
  return function(contentNodeId) {
    return getInvalidNodeAssessmentItemsCount(state)(contentNodeId) === 0;
  };
}

import { validateAssessmentItem } from '../../utils';

export function getNodeAssessmentItems(state) {
  return function(contentNodeId) {
    if (!state.assessmentItemsMap[contentNodeId]) {
      return [];
    }

    const items = Object.values(state.assessmentItemsMap[contentNodeId]);
    return items.sort((item1, item2) => (item1.order > item2.order ? 1 : -1));
  };
}

export function getNodeAssessmentItemErrors(state) {
  return function(contentNodeId) {
    return getNodeAssessmentItems(state)(contentNodeId).map(validateAssessmentItem);
  };
}

export function getInvalidNodeAssessmentItemsCount(state) {
  return function(contentNodeId) {
    return getNodeAssessmentItemErrors(state)(contentNodeId).filter(arr => arr.length).length;
  };
}

export function getAssessmentItemsAreValid(state) {
  return function(contentNodeId) {
    return getInvalidNodeAssessmentItemsCount(state)(contentNodeId) === 0;
  };
}

Use native sort + treat empty data


/**
 * Created by larryjones on 6/22/17.
 */

import React from 'react';
import { mount } from 'enzyme';
import Voting from './Voting';

describe('Voting', () => {
  it('renders a pair of buttons', () => {
    const props = {
      pair: ['Trainspotting', '28 Days Later'],
      voteFor: jest.fn()
    };
    const cut = mount(<Voting {...props} />);

    expect(cut.find('div.voting > button').length).toBe(2);
  });

  it('each button has the correct label', () => {
    const props = {
      pair: ['Trainspotting', '28 Days Later'],
      voteFor: jest.fn()
    };
    const cut = mount(<Voting {...props} />);

    expect(cut.find('div.voting > button')
              .map((b) => b.text())).toEqual(['Trainspotting', '28 Days Later']);
  });

  it('each button has a callback function', () => {
    const props = {
      pair: ['Trainspotting', '28 Days Later'],
      voteFor: jest.fn()
    };
    const cut = mount(<Voting {...props} />);

    cut.find('div.voting > button').at(0).simulate('click');

    expect(props.voteFor).toHaveBeenCalledWith('Trainspotting');
  });
});

/**
 * Created by larryjones on 6/22/17.
 */

import React from 'react';
import { mount } from 'enzyme';
import Voting from './Voting';

describe('Voting', () => {
  it('renders a pair of buttons', () => {
    const props = {
      pair: ['Trainspotting', '28 Days Later'],
      voteFor: jest.fn()
    };
    const cut = mount(<Voting {...props} />);

    expect(cut.find('div.voting > button').length).toBe(2);
  });

  it('each button has the correct label', () => {
    const props = {
      pair: ['Trainspotting', '28 Days Later'],
      voteFor: jest.fn()
    };
    const cut = mount(<Voting {...props} />);

    expect(cut.find('div.voting > button')
              .map((b) => b.text())).toEqual(['Trainspotting', '28 Days Later']);
  });

  it('each button has a callback function', () => {
    const props = {
      pair: ['Trainspotting', '28 Days Later'],
      voteFor: jest.fn()
    };
    const cut = mount(<Voting {...props} />);

    cut.find('div.voting > button').at(0).simulate('click');

    expect(props.voteFor).toHaveBeenCalledWith('Trainspotting');
  });

  it('disables buttons when user has voted', () => {
    const props = {
      pair: ['Trainspotting', '28 Days Later'],
      hasVoted: 'Trainspotting'
    };
    const cut = mount(<Voting {...props} />);

    expect(cut.find('div.voting > button[disabled]').length).toBe(2);
  });
});

Verify that buttons are disabled after voting


import _ from 'lodash';
import { FETCH_POSTS, FETCH_POST } from '../actions';

export default function(state = {}, action) {
  switch (action.type) {
    case FETCH_POST:
      const post = action.payload.data;
      // const newState = { ...state  };
      // newState[post.id] = post;
      // return newState;

      return { ...state, [action.payload.data.id]: action.payload.data };
    case FETCH_POSTS:
      return _.mapKeys(action.payload.data, 'id');
    default:
      return state;
  }
}

import _ from 'lodash';
import { FETCH_POSTS, FETCH_POST, DELETE_POST } from '../actions';

export default function(state = {}, action) {
  switch (action.type) {
    case DELETE_POST:
      return _.omit(state, action.payload);
    case FETCH_POST:
      const post = action.payload.data;
      // const newState = { ...state  };
      // newState[post.id] = post;
      // return newState;

      return { ...state, [action.payload.data.id]: action.payload.data };
    case FETCH_POSTS:
      return _.mapKeys(action.payload.data, 'id');
    default:
      return state;
  }
}

Add DELETE_POST to reducer function


export default ({ colors, fonts }) => {
  const defaultStyles = {
    fontFamily: fonts.default,
    textDecoration: 'none',
    color: colors.text.link,
  };

  return {
    default: defaultStyles,
    inverted: {
      ...defaultStyles,
      color: colors.white,
    },
    underlined: {
      ...defaultStyles,
      textDecoration: 'underline',
    },
  };
};

export default ({ colors, fonts }) => {
  const defaultStyles = {
    fontFamily: fonts.default,
    textDecoration: 'none',
    color: colors.text.link,
    '&:hover, &:active': {
      color: colors.text.link,
      textDecoration: 'none',
    },
    '&:focus': {
      color: colors.text.link,
      outline: 'none',
      textDecoration: 'underline',
    },
  };

  return {
    default: defaultStyles,
    inverted: {
      ...defaultStyles,
      color: colors.white,
    },
    underlined: {
      ...defaultStyles,
      textDecoration: 'underline',
    },
  };
};

Add active, hover, focus styles to Link


function arrayToList(arr) {
    var list = null;
    for (var i = arr.length - 1; i >= 0; --i)
        list = { value: arr[i], rest: list };
    return list;
}

function listToArray(list) {
    var arr = [];
    var myList = list;
    while (myList) {            // More elegant: for (var node = list; node; node = node.rest)
        arr.push(myList.value);
        myList = myList.rest;
    }
    return arr;
}

function prepend(num, list) {
    return { value: num, rest: list };
}

function nth(list, num) {
    var node = list;
    for (var i = 0; i < num; ++i) {
        if (! node.rest)
            return undefined;
        node = node.rest;
    }
    return node.value;
}

function nthRecurse(list, num) {
    if (! list)
        return undefined;
    if (num === 0)
        return list.value;
    return nthRecurse(list.rest, num - 1);
}

function arrayToList(array) {
    let list = null;
    for (let i = array.length - 1; i >= 0; --i) {
        list = {value: array[i], rest: list};
    }
    return list;
}

function listToArray(list) {
    let array = [];
    for (let node = list; node; node = node.rest) {
        array.push(node.value);
    }
    return array;
}

function prepend(element, list) {
    return arrayToList([element, ...listToArray(list)]);
}

function nth(list, n) {
    if (n === 0) return list.value;
    else return list.rest ? nth(list.rest, n-1) : null;
}

Add Chapter 04, exercise 3


Package.describe({
  name: 'hubaaa:easy-meteor-settings',
  version: '0.1.0',
  // Brief, one-line summary of the package.
  summary: "Easily read deep values from Meteor.settings using 'a.b.c'",
  // 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.versionsFrom('1.1.0.2');

  api.use([
    'meteor',
    'underscore',
    'coffeescript',
    'practicalmeteor:loglevel@1.2.0_2',
    'practicalmeteor:chai@2.1.0_1',
    'practicalmeteor:underscore-deep@0.9.2'
  ]);

  api.addFiles('namespace.coffee');
  api.addFiles('EasyMeteorSettings.coffee');
});

Package.onTest(function(api) {
  api.use([
    'underscore',
    'coffeescript',
    'practicalmeteor:mocha@2.1.0_3'
  ]);

  api.use('hubaaa:easy-meteor-settings@0.1.0');

  api.addFiles('EasyMeteorSettingsTest.coffee');
});

Package.describe({
  name: 'hubaaa:easy-meteor-settings',
  version: '0.1.1',
  // Brief, one-line summary of the package.
  summary: "Easily read deep values from Meteor.settings using 'a.b.c'",
  // URL to the Git repository containing the source code for this package.
  git: 'https://github.com/hubaaa/meteor-easy-meteor-settings',
  // 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.versionsFrom('1.1.0.2');

  api.use([
    'meteor',
    'underscore',
    'coffeescript',
    'practicalmeteor:loglevel@1.2.0_2',
    'practicalmeteor:chai@2.1.0_1',
    'practicalmeteor:underscore-deep@0.9.2'
  ]);

  api.addFiles('namespace.coffee');
  api.addFiles('EasyMeteorSettings.coffee');
});

Package.onTest(function(api) {
  api.use([
    'underscore',
    'coffeescript',
    'practicalmeteor:mocha@2.1.0_3'
  ]);

  api.use('hubaaa:easy-meteor-settings@0.1.1');

  api.addFiles('EasyMeteorSettingsTest.coffee');
});

Update git url and bump version to 0.1.1


Package.describe({
  summary: "S3 signed uploads for meteorjs.",
  version: "0.0.1",
  git: "https://github.com/jimmiebtlr/s3-signed-upload.git"
});

Package.on_use(function(api){
  api.use(['mrt:aws-sdk@0.2.0'],'server');
  api.use(['aldeed:simple.schema']);
  api.export(['uploadFile'],'client');
  api.export(['S3SignedUploadTmp'],'server');

  api.add_files([
    'client.js'
  ], 'client');

  api.add_files([
    'server.js'
  ], 'server');
});

Package.describe({
  summary: "S3 signed uploads for meteorjs.",
  version: "0.0.1",
  git: "https://github.com/jimmiebtlr/s3-signed-upload.git"
});

Package.on_use(function(api){
  api.use(['mrt:aws-sdk@0.2.0'],'server');
  api.use(['aldeed:simple-schema@0.7.0']);
  api.export(['uploadFile'],'client');
  api.export(['S3SignedUploadTmp'],'server');

  api.add_files([
    'client.js'
  ], 'client');

  api.add_files([
    'server.js'
  ], 'server');
});

Attach version to simple schema.


const e = require('eris')

const fs = require('fs')

// Please edit config.json with your token.
var conf = JSON.parse(fs.readFileSync('../config.json', 'utf8'))

// You may change this desc, the owner, and prefix.
const bot = new e.CommandClient(conf.token, {}, {
  description: 'A test bot made with Eris',
  owner: 'lold#4960',
  prefix: '&&'
})

bot.on('ready', () => {
  console.log('dbot - discord responded with ready')
})

bot.connect()

const e = require('eris');

const fs = require('fs');

// Please edit config.json with your token.
var conf = JSON.parse(fs.readFileSync('config.json', 'utf8'));

// You may change this desc, the owner, and prefix.
const bot = new e.CommandClient(conf.token, {}, {
    description: "A test bot made with Eris",
    owner: "lold#4960",
    prefix: "&&"
});

bot.on("ready", () => {
    console.log("dbot - discord responded with ready");
});

bot.registerCommand("about", "my name is dbot and I live in a computer. https://github.com/xshotD/dbot", {
    description: "who am I? run this to find out",
    fullDescription: ":thinking:"
});

bot.connect();

Add in previously uncommited stuff


'use strict';

angular.module('products').controller('ProductsController', ['$scope', '$stateParams', '$location', 'Products',
	function($scope, $stateParams, $location, Products) {

    // List Categories
    $scope.find = function() {
      $scope.products = Products.query();
    };

    // CREATE new Category
    $scope.create = function() {
      // Create new Product object
      var product = new Products ({
        category: this.category,
        name: this.name,
        quantityPerUnit: this.quantityPerUnit,
        unitPrice: this.unitPrice,
        unitsInStock: this.unitsInStock,
        unitsOnOrder: this.unitsOnOrder
      });

      // Redirect after save
      product.$save(function(response) {
        $location.path('products/' + response._id);

        // Clear form fields
        $scope.name = '';
      }, function(errorResponse) {
        $scope.error = errorResponse.data.message;
      });
    };

	}
]);

'use strict';

angular.module('products').controller('ProductsController', ['$scope', '$stateParams', '$location', 'Products',
	function($scope, $stateParams, $location, Products) {

    // List Products
    $scope.find = function() {
      $scope.products = Products.query();
    };

    // CREATE new Product
    $scope.create = function() {
      // Create new Product object
      var product = new Products ({
        category: this.category,
        name: this.name,
        quantityPerUnit: this.quantityPerUnit,
        unitPrice: this.unitPrice,
        unitsInStock: this.unitsInStock,
        unitsOnOrder: this.unitsOnOrder
      });

      // Redirect after save
      product.$save(function(response) {
        $location.path('products/' + response._id);

        // Clear form fields
        $scope.name = '';
      }, function(errorResponse) {
        $scope.error = errorResponse.data.message;
      });
    };

    // READ single Product
    $scope.findOne = function() {
      $scope.product = Products.get({
        productId: $stateParams.productId
      });
    };

	}
]);

Add Read single Product method to Angular Controller.


module("About Asserts (topics/about_asserts.js)");

test("ok", function() {
    ok(__ === true, 'what will satisfy the ok assertion?');
});

test("not ok", function() {
    ok(__ === false, 'what is a false value?');
});

test("equal", function() {
    equal(__, 1 + 1, 'what will satisfy the equal assertion?');
});

module("About Asserts (topics/about_asserts.js)");

test("ok", function() {
    ok(true === true, 'what will satisfy the ok assertion?');
});

test("not ok", function() {
    ok(false === false, 'what is a false value?');
});

test("equal", function() {
    equal(2, 1 + 1, 'what will satisfy the equal assertion?');
});

Complete asserts tests to pass


function checkForPeevskiDomain(url) {
	var currentDomains = [
		'monitor.bg',
		'telegraph.bg',
		'politika.bg',
		'europost.eu',
		'europost.bg',
		'borbabg.com',
		'tv7.bg',
		'news7.bg',
		'super7.bg',
		'3bay.bg',
		'econ.bg',
		'inews.bg',
		'jenite.bg',
		'div.bg',
		'sporta.bg',
		'fitwell.bg',
		'sportuvaj.bg',
		'peika.bg',
		'kulinar.bg',
		'sever.bg',
		'get.bg',
		'bnews.bg',
		'bsport.bg',
		'blife.bg',
		'bpost.bg',
		'bmobile.bg',
		'blitz.bg',
		'standartnews.com',
		'struma.bg',
		'marica.bg',
		'vsekiden.com',
		'novinar.bg'
	];

	var urlPattern;
	for (var i = 0; i < currentDomains.length; i++) {
		urlPattern = new RegExp("^http(s)?\\:\\/\\/([^\\/]+\\.)?" + currentDomains[i]);
		if (urlPattern.test(url)) {
			return currentDomains[i];
		}
	}

	return false;
};

function checkForPeevskiDomain(url) {
	var blockedDomains = [
		'monitor.bg',
		'telegraph.bg',
		'politika.bg',
		'europost.eu',
		'europost.bg',
		'borbabg.com',
		'tv7.bg',
		'news7.bg',
		'super7.bg',
		'3bay.bg',
		'econ.bg',
		'inews.bg',
		'jenite.bg',
		'div.bg',
		'sporta.bg',
		'fitwell.bg',
		'sportuvaj.bg',
		'peika.bg',
		'kulinar.bg',
		'sever.bg',
		'get.bg',
		'bnews.bg',
		'bsport.bg',
		'blife.bg',
		'bpost.bg',
		'bmobile.bg',
		'blitz.bg',
		'standartnews.com',
		'struma.bg',
		'marica.bg',
		'vsekiden.com',
		'novinar.bg'
	];

	var urlPattern;
	for (var i = 0; i < blockedDomains.length; i++) {
		urlPattern = new RegExp("^http(s)?\\:\\/\\/([^\\/]+\\.)?" + blockedDomains[i]);
		if (urlPattern.test(url)) {
			return blockedDomains[i];
		}
	}

	return false;
};

Rename a variable for clarity


"use strict";

const assert = require('assert');
const xpath = require('fontoxpath');
const utils = require('./utils');

const mei = utils.getTestMeiDom('barnumbers.mei');

describe("bar numbers", function() {
    it("exports @n attributes", function() {
        const nAttributes = xpath.evaluateXPath("//*:measure/@n", mei);
        assert.deepEqual(nAttributes, ['1', '2', '3', '4'], "@n is simply ascending");
    });
    it("exports @label attributes", function() {
        const nAttributes = xpath.evaluateXPath("//*:measure", mei).map(m => m.getAttribute('label'));
        assert.deepEqual(nAttributes, [null, null, '5', '1'], "@n is simply ascending");
    });
});

"use strict";

const assert = require('assert');
const xpath = require('fontoxpath');
const utils = require('./utils');

const mei = utils.getTestMeiDom('barnumbers.mei');

describe("bar numbers", function() {
    it("exports @n attributes", function() {
        const nAttributes = xpath.evaluateXPath("//*:measure/@n", mei);
        assert.deepEqual(nAttributes, ['1', '2', '3', '4'], "@n is simply ascending");
    });
    it("exports @label attributes", function() {
        const nAttributes = xpath.evaluateXPath("//*:measure", mei).map(m => m.getAttribute('label'));
        assert.deepEqual(nAttributes, [null, null, '5', '1'], "@label represents displayed bar numbers");
    });
});

Fix test description in barnumbers test


var mongoose     = require('mongoose');
var Schema       = mongoose.Schema;

var UserSchema   = new Schema(
{
    lastName : String,
    firstName : String,
    username : String,
    email : String,
    passwordHash : String,
    privateKey : String,
    picture : String,
    level : String,
    description : String,
    created : Date,
    birthday : Date,
    universityGroup : String,
    website : String,
});

module.exports = mongoose.model('User', UserSchema);

var mongoose     = require('mongoose');
var Schema       = mongoose.Schema;

var UserSchema   = new Schema(
{
    lastName : String,
    firstName : String,
    username : String,
    email : String,
    passwordHash : String,
    privateKey : String,
    picture : {type : String, default : null},
    level : {type : String, default : 1},
    description : {type : String, default : ''},
    created : {type : Date, default : Date.now},
    birthday : {type : Date, default : new Date(1970,1,1)},
    universityGroup : {type : String, default : 'Inconnu'},
    website : {type : String, default : null}
});

module.exports = mongoose.model('User', UserSchema);

Set defaults value to fields


'use strict';

var config = require('./server/config');
var items = require('./server/data/items');
var express = require('express');
var handlebars = require('express-handlebars');
var app = express();

// Middlewares configuration
var oneWeek = 604800000;
app.use(express.static('build'));
app.use(express.static('data', { maxAge: oneWeek, extensions: ['png'] }));

// Handlebars configuration
app.set('views', 'client/views');
app.engine('.hbs', handlebars({
    extname: '.hbs',
    partialsDir: ['client/views/partials/', 'client/views/components/'],
    defaultLayout: __dirname + '/client/views/_layout.hbs'
}));
app.set('view engine', '.hbs');

// Starts application listening
app.listen(config.port, (err) => {
    console.log('running on ' + config.port);
});

// Restore isActive value for every items
app.use(function (req, res, next) {
    items.forEach(function (item) {
        item.isActive = false;
    });
    next();
});

// Regsiters routes
app.use('/', require('./server/routes/sitemapRoute'));
app.use('/', require('./server/routes/indexRoute'));
app.use('/', require('./server/routes/itemRoute'));

'use strict';

var config = require('./server/config');
var items = require('./server/data/items');
var express = require('express');
var handlebars = require('express-handlebars');
var app = express();

// Middlewares configuration
var oneDay = 86400000;
app.use(express.static('build', { maxAge: oneDay }));

// Handlebars configuration
app.set('views', 'client/views');
app.engine('.hbs', handlebars({
    extname: '.hbs',
    partialsDir: ['client/views/partials/', 'client/views/components/'],
    defaultLayout: __dirname + '/client/views/_layout.hbs'
}));
app.set('view engine', '.hbs');

// Starts application listening
app.listen(config.port, (err) => {
    console.log('running on ' + config.port);
});

// Regsiters routes
app.use('/', require('./server/routes/sitemapRoute'));
app.use('/', require('./server/routes/indexRoute'));
app.use('/', require('./server/routes/itemRoute'));

Remove useless code +cache improvement


var fs = require('fs');

// This is the flatfile.db() function. It instantiates a local database asynchronously.
var db = function(filename, callback) {

	fs.readFile(filename, {encoding: 'utf8'}, function(err, data) {
		if (err) {
			// If an error occurred, stop execution and return the error for handling.
			callback(err, null);
		}

		// Parse data
		data = JSON.parse(data);

		// Adds various library-specific data handles
		data._filename = filename;

		// Adds the save function
		data.save = function(callback) {
			var filename = data._filename,
				dataCopy = JSON.parse(JSON.stringify(data)); // We have to do it this way in order to clone the object instead of reference it.

			// Remove the library-specific data handles
			delete dataCopy._filename;
			delete dataCopy.save;

			// Get string
			dataCopy = JSON.stringify(dataCopy);

			// Write to the file
			fs.writeFile(filename, dataCopy, function(err) {
				callback(err);
			});
		};

		callback(null, data);
	});
};

module.exports = {
	db: db
};

var fs = require('fs');

// This is the flatfile.db() function. It instantiates a local database asynchronously.
var db = function(filename, callback) {

	fs.readFile(filename, {encoding: 'utf8'}, function(err, data) {
		if (err) {
			// If an error occurred, stop execution and return the error for handling.
			callback(err, null);
		}

		// Parse data
		data = JSON.parse(data);

		// Adds various library-specific data handles
		data._filename = filename;

		// Adds the save function
		data.save = function(callback) {
			var filename = data._filename,
				dataCopy = JSON.parse(JSON.stringify(data)); // We have to do it this way in order to clone the object instead of reference it.

			// Remove the library-specific data handles
			delete dataCopy._filename;
			delete dataCopy.save;

			// Get string
			dataCopy = JSON.stringify(dataCopy);

			// Write to the file
			fs.writeFile(filename, dataCopy, function(err) {
				callback(err);
			});
		};

		// Make _filename and save non-enumerable so that they dont mess up data.length
		Object.defineProperties(data, {
			'_filename': {
				enumerable: false
			},
			'save': {
				enumerable: false
			}
		});

		callback(null, data);
	});
};

module.exports = {
	db: db
};

Make _filename and save non-enumerable and fixed indentation


class PGroup {
	constructor(array) {
		this.members = array;
	}

	add(element) {
		return new PGroup(this.members.slice().push(element));
	}

	delete(element) {
		return new PGroup(this.members.filter(e => e !== element));
	}

	has(element) {
		return this.members.includes(element);
	}
}

PGroup.empty = new PGroup([]);

class PGroup {
	constructor(array) {
		this.members = array;
	}

	add(element) {
		if (this.has(element)) return this;
		return new PGroup(this.members.concat([element]));
	}

	delete(element) {
		if (!this.has(element)) return this;
		return new PGroup(this.members.filter(e => e !== element));
	}

	has(element) {
		return this.members.includes(element);
	}
}

PGroup.empty = new PGroup([]);

Fix add and delete methods


import React, {Component} from 'react'

import GraphiQL from 'graphiql'
import 'graphiql/graphiql.css'

import Breadcrumbs from 'components/Breadcrumbs'

export default class extends Component {
	static pageProps = {
		useNavigation: false,
		fluidContainer: true,
	}

	fetch(params) {
		return fetch('/graphql', {
			credentials: 'same-origin',
			method: 'POST',
			headers: {'Content-Type': 'application/json', 'Accept': 'application/json'},
			body: JSON.stringify(params),
		}).then(response => response.json())
	}

	render() {
		return <div>
			<Breadcrumbs items={[['Run GraphQL queries', '/graphiql']]} />
			<GraphiQL fetcher={this.fetch} />
		</div>
	}
}

import React, {Component} from 'react'

import Breadcrumbs from 'components/Breadcrumbs'

var GraphiQL = null

export default class extends Component {
	static pageProps = {
		useNavigation: false,
		fluidContainer: true,
	}

	componentDidMount() {
		if (GraphiQL)
			return

		require.ensure([], () => {
			GraphiQL = require('graphiql')
			require('graphiql/graphiql.css')
			this.forceUpdate()
		})
	}

	fetch(params) {
		return fetch('/graphql', {
			credentials: 'same-origin',
			method: 'POST',
			headers: {'Content-Type': 'application/json', 'Accept': 'application/json'},
			body: JSON.stringify(params),
		}).then(response => response.json())
	}

	render() {
		return <div>
			<Breadcrumbs items={[['Run GraphQL queries', '/graphiql']]} />
			{GraphiQL ? <GraphiQL fetcher={this.fetch} /> : "Loading..."}
		</div>
	}
}

Move graphiql to a separate bundle to save 150kb


/*
 * To change this license header, choose License Headers in Project Properties.
 * To change this template file, choose Tools | Templates
 * and open the template in the editor.
 */
(function () {
    define([], function () {
        var appRun = function ($rootScope, $sessionStorage, $state, $uibModal, authService, editableOptions) {
            editableOptions.theme = 'bs3';
            $rootScope.loginOngoing = false;
            $rootScope.$on("event:auth-forbidden", function () {
                $state.go("app.dashboard");
            });
            $rootScope.$on("event:auth-loginRequired", function () {
                delete $sessionStorage.oauth;
                var modalScope = $rootScope.$new();
                if (!$rootScope.loginOngoing) {
                    $rootScope.loginOngoing = true;
                    modalScope.modalInstance = $uibModal.open({
                        animation: true,
                        templateUrl: "login.html",
                        controller: "loginController",
                        controllerAs: "login",
                        scope: modalScope,
                        backdrop: "static",
                        size: "md"
                    });
                    modalScope.modalInstance.result.then(
                            function () {
                                authService.loginConfirmed();
                                $rootScope.loginOngoing = false;
                            }
                    );
                }
            });
            $state.go("login");
        };
        appRun.$inject = ["$rootScope", "$sessionStorage", "$state", "$uibModal", "authService", "editableOptions"];
        return appRun;
    });
})();

/*
 * To change this license header, choose License Headers in Project Properties.
 * To change this template file, choose Tools | Templates
 * and open the template in the editor.
 */
(function () {
    define([], function () {
        var appRun = function ($rootScope, $sessionStorage, $state, $uibModal, authService, editableOptions) {
            editableOptions.theme = 'bs3';
            $rootScope.loginOngoing = false;
            $rootScope.$on("event:auth-forbidden", function () {
                $state.go("app.dashboard");
            });
            $rootScope.$on("event:auth-loginRequired", function () {
                delete $sessionStorage.oauth;
                var modalScope = $rootScope.$new();
                if (!$rootScope.loginOngoing) {
                    $rootScope.loginOngoing = true;
                    modalScope.modalInstance = $uibModal.open({
                        animation: true,
                        backdrop: "static",
                        templateUrl: "login.html",
                        controller: "loginController",
                        controllerAs: "login",
                        scope: modalScope,
                        keyboard: false,
                        size: "md"
                    });
                    modalScope.modalInstance.result.then(
                            function () {
                                authService.loginConfirmed();
                                $rootScope.loginOngoing = false;
                            }
                    );
                }
            });
            $state.go("login");
        };
        appRun.$inject = ["$rootScope", "$sessionStorage", "$state", "$uibModal", "authService", "editableOptions"];
        return appRun;
    });
})();

FIX : fenêtre login ne se ferme plus sur clic en dehors popup ou esc


var defaults = [
  {
    title: 'iPad 2',
    ch: {
      dpr: '1.0'
    }
  },
  {
    title: 'Nexus S',
    ch: {
      dpr: '1.5'
    }
  },
  {
    title: 'Nexus 4',
    ch: {
      dpr: '2.0'
    }
  },
  {
    title: 'Nexus 4 (160px image width)',
    ch: {
      dpr: '2.0',
      dw: '160'
    }
  }
];

var defaults = [
  {
    title: 'iPad 2',
    ch: {
      dpr: '1.0'
    }
  },
  {
    title: 'Nexus S',
    ch: {
      dpr: '1.5'
    }
  },
  {
    title: 'Nexus 4',
    ch: {
      dpr: '2.0'
    }
  },
  {
    title: 'Nexus 4 (160px image width)',
    ch: {
      dpr: '2.0',
      rw: '160'
    }
  }
];

Update CH-DW -> CH-RW to match latest spec


Router.configure({
  trackPageView: true
});

Router.map( function () {
  this.route('home', {
    path: '/',
    template: 'home'
  });
  this.route('about', {
    path: '/about',
    template: 'about'
  });
  this.route('cesium', {
    path: '/cesium',
    template: 'cesium'
  });
  this.route('lightstreamer', {
    path: '/lightstreamer',
    template: 'lightstreamer'
  });
});

Add tracking to all pages


/**
 * Test task.
 * Replaces all t3b_template strings and other meta-data with the data
 * specified inside the 'package.json'. (Should be run after downloading the extension).
 */

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

	grunt.registerTask("test", function() {
		// Replace general text-strings and paths.
		grunt.task.run("connect:karma", "karma:test");
	});
};

/**
 * Test task.
 * Run JS unit tests via Jasmine & Karma, and create coverage reports for all browsers.
 */

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

	grunt.registerTask("test", function() {
		grunt.task.run("connect:karma", "karma:test");
	});
};

Add a proper file description for the 'test' grunt task


import React, { Component, PropTypes } from 'react';
import IconText from '../IconText';
import css from './ButtonIconText.css';

class ButtonIconText extends Component {
  constructor(props) {
    super(props);

    this.state = {
      hovering: false,
    };
  }

  hover(hovering) {
    this.setState({
      hovering,
    });
  }

  render() {
    const { onClick, ...other } = this.props;
    const { hovering } = this.state;
    return (
      <button
        className={css.button}
        onClick={onClick}
        onMouseEnter={() => this.hover(true)}
        onMouseLeave={() => this.hover(false)}
        {...other}
      >
        <IconText hovering={hovering} {...other} />
      </button>
    );
  }
}

ButtonIconText.propTypes = {
  onClick: PropTypes.func.isRequired,
};

export default ButtonIconText;

import React, { Component, PropTypes } from 'react';
import IconText from '../IconText';
import css from './ButtonIconText.css';

class ButtonIconText extends Component {
  constructor(props) {
    super(props);

    this.state = {
      hovering: false,
    };
  }

  hover(hovering) {
    this.setState({
      hovering,
    });
  }

  render() {
    const { onClick, ...other } = this.props;
    const { hovering } = this.state;
    return (
      <button
        className={css.button}
        onClick={onClick}
        onMouseEnter={() => this.hover(true)}
        onMouseLeave={() => this.hover(false)}
      >
        <IconText hovering={hovering} {...other} />
      </button>
    );
  }
}

ButtonIconText.defaultProps = {
  disabled: false,
};

ButtonIconText.propTypes = {
  onClick: PropTypes.func.isRequired,
  disabled: PropTypes.bool,
};

export default ButtonIconText;

Make ButtonIconText button proptypes explicit


import path from 'path'

const library = 'style-script'
const srcPath = path.resolve(__dirname, 'src')
const libPath = path.resolve(__dirname, 'lib')

export default {
  devtool: 'source-map',

  context: srcPath,

  entry: 'index.js',

  resolve: {
    root: srcPath,
  },

  output: {
    library,
    libraryTarget: 'commonjs2',
    filename: `${ library }.js`,
    path: libPath,
  },

  module: {
    loaders: [
      {
        test: /\.js$/,
        include: srcPath,
        loader: 'babel',
      },
    ],
  },
}

import path from 'path'

const library = 'style-script'
const srcPath = path.resolve(__dirname, 'src')
const libPath = path.resolve(__dirname, 'lib')

export default {
  devtool: 'source-map',

  context: srcPath,

  entry: 'index.js',

  resolve: {
    root: srcPath,
  },

  output: {
    library,
    libraryTarget: 'commonjs2',
    filename: `${ library }.js`,
    path: libPath,
  },

  module: {
    loaders: [
      {
        test: /\.js$/,
        include: srcPath,
        loader: 'babel',
      },
    ],
  },

  externals: {
    lodash: '_',
  },
}

Add `lodash` to `externals` option of webpack


var FS = require('fs')

  , FILESTREAM = require('../index')

exports["#newReadStream() method"] = {

  "creates new ReadStream instance": function (test) {
    var stream = FILESTREAM.newPath(__filename).newReadStream()
    test.expect(1);
    test.ok(stream instanceof FS.ReadStream);
    stream.close(test.done);
    return;
  },

  "with options": function (test) {
    var path = FILESTREAM.newPath(__dirname, 'fixtures', 'test.ini')
      , buff = ''
      , stream = path.newReadStream({encoding: 'base64'})

    stream.on('data', function (chunk) {
      buff += chunk;
    });

    stream.on('end', function () {
      test.strictEqual(buff, 'Zm9vPWJhcgo=');
      return test.done();
    })

    return;
  }
};

exports["#newWriteStream() method"] = {
  setUp: function (done) {
    // Do the cleanup.
    try {
      FS.unlinkSync('/tmp/test-writestream-file.txt');
    } catch (e) { }

    return done();
  },

  "creates a new WriteStream instance": function (test) {
    var stream = FILESTREAM.newPath('/tmp/test-writestream-file.txt').newWriteStream()
    test.expect(1);
    test.ok(stream instanceof FS.WriteStream);
    stream.close(test.done);
    return;
  },

  "with options": function (test) {
    var path = FILESTREAM.newPath('/tmp/test-writestream-file.txt')

      // Unlike Node.js, encoding defaults to 'utf8' (instead of null).
      , stream = path.newWriteStream()

    stream.on('finish', function () {
      path.read({encoding: 'utf8'}).then(testContent, test.done);
    });

    function testContent(content) {
      test.strictEqual(content, 'foobar');
      return test.done();
    }

    stream.write('foobar');
    stream.end();
    return;
  }
};

Rebuild tests for stream methods.


const router = require('koa-router');

const userController = require('../controller/user');

exports.init = (router) => {
  router
  .post('/auth/login', userController.login)
  .post('/auth/register', userController.create)
  .get('/users', userController.show)
}

const router = require('koa-router');

const userController = require('../controller/user');

exports.init = (router) => {
  router
  .post('/auth/sessions', userController.login)
  .post('/auth/users', userController.create)
  .get('/users', userController.show)
}

Update login and signup api url


'use strict';

const config = require(`${__dirname}/../config/init`);

/**
 * makeAllRowRequestBody(str)
 * @param string field Field to query parameters from.
 * @param string from Date to query results from.
 * @param string to Date to query results to.
 * @return object Request to be performed body.
 */
exports.makeAllRowRequest = function make(field, from, to, response) {
  config.client.search({
    index: config.index,
    type: config.type,
    body: {
      size: 0,
      query: {
        bool: {
          must: [
            {
              range: {
                "@timestamp": { from, to },
              },
            },
          ],
        },
      },
      aggs: {
        results: {
          terms: {
            size: 10,
            field,
          },
        },
      },
    },
  }).then((body) => {
    response.json({ success: true, hits: body.aggregations.results.buckets });
  }, (error) => {
    response.json({ success: false, message: error.message });
  });
};

Change the way we retrieve statistics


import React from 'react';

import BrowserWarning from './BrowserWarning';
import { shallowWithIntl } from 'utils/testUtils';

describe('pages/browser-warning/BrowserWarning', () => {
  function getWrapper() {
    return shallowWithIntl(<BrowserWarning />);
  }

  test('renders a browser warning div', () => {
    const div = getWrapper().find('div');
    expect(div.length).toBe(1);
  });

  test('renders a browser warning paragraph', () => {
    const p = getWrapper().find('p');
    expect(p.length).toBe(2);
  });

  test('renders all specified browser links', () => {
    const a = getWrapper().find('a');
    expect(a.length).toBe(6);
  });
});

import React from 'react';

import BrowserWarning from './BrowserWarning';
import { shallowWithIntl } from 'utils/testUtils';

describe('pages/browser-warning/BrowserWarning', () => {
  function getWrapper() {
    return shallowWithIntl(<BrowserWarning />);
  }

  test('renders a browser warning div', () => {
    const div = getWrapper().find('div');
    expect(div.length).toBe(1);
  });

  test('renders a browser warning paragraph', () => {
    const p = getWrapper().find('p');
    expect(p.length).toBe(3);
  });

  test('renders all specified browser links', () => {
    const a = getWrapper().find('a');
    expect(a.length).toBe(9);
  });
});

Update test for browser warning component


'use strict';

angular.module('ngQuestionnaires.questionnaireNewController', [
        'ng'
    ])
    .controller('questionnaireNewController', [
        '$scope',
        '$cacheFactory',
        function ($scope, $cacheFactory) {
            $scope.action = 'New';

            $scope.questions = $cacheFactory.get('data').get('questions');

            $scope.addQuestion = function () {
                // store questionnaire inputs in cache service
                // store return to new questionnaire in cache service
                // navigate to new question form
                // on save new question,
                //  persist state and check for return to value in cache service,
                //  then navigate there
                // on cancel new question,
                //  check for return to value in cache service,
                //  then navigate there
                // on returning to new questionnaire form,
                //  check cache service for questionnaire inputs and display them,
                //  then remove from cache service
            };
        }]);

'use strict';

angular.module('ngQuestionnaires.questionnaireNewController', [
        'ng'
    ])
    .controller('questionnaireNewController', [
        '$scope',
        '$cacheFactory',
        '$location',
        function ($scope, $cacheFactory, $location) {
            $scope.action = 'New';

            var questionnaire = $cacheFactory.get('data').get('questionnaire');
            if (questionnaire !== undefined) {
                $scope.questionnaire = questionnaire;
                $cacheFactory.get('data').remove('questionnaire');
            }

            $scope.questions = $cacheFactory.get('data').get('questions');

            $scope.addQuestion = function () {
                $cacheFactory.get('data').put('questionnaire', $scope.questionnaire);
                $cacheFactory.get('data').put('returnTo', $location.url());
                $location.url('/questions/new');
            };
        }]);

Use cache factory to save questionnaire and return url on add new question


import pg from 'pg-promise';
import dbConfig from '../../../../server/api/util/db-config';

const pgp = pg();

const TABLE_NAME = 'transaction';

function getInsertQuery(values) {
  return {
    name: 'transactions_create_one',
    text: `INSERT INTO ${TABLE_NAME} (description, value, date) VALUES ($1, $2, $3)`,
    values: [values.description, values.value, values.date]
  };
}

describe('A mock test', () => {
  it('should pass', done => {
    const query = getInsertQuery({
      value: '10.20'
    });

    var db = pgp(dbConfig());

    db.none(query)
      .then(() => {
        return db.any(`SELECT * FROM ${TABLE_NAME}`)
          .then(result => {
            expect(result).to.have.length(1);
            done();
          });
      })
      .catch(() => {
        throw new Error('fail');
        done();
      });
  });
});

describe('Another mock test', () => {
  it('should pass', done => {
    const query = getInsertQuery({
      value: '1000.00',
      description: 'test'
    });

    var db = pgp(dbConfig());

    db.none(query)
      .then(() => {
        return db.any(`SELECT * FROM ${TABLE_NAME}`)
          .then(result => {
            expect(result).to.have.length(1);
            done();
          });
      })
      .catch(() => {
        throw new Error('fail');
        done();
      });
  });
});

import pg from 'pg-promise';
import dbConfig from '../../../../server/api/util/db-config';

const pgp = pg();

const TABLE_NAME = 'transaction';

function getInsertQuery(values) {
  return {
    name: 'transactions_create_one',
    text: `INSERT INTO ${TABLE_NAME} (description, value, date) VALUES ($1, $2, $3)`,
    values: [values.description, values.value, values.date]
  };
}

describe('Transactions', () => {
  // This loads our fixture data
  beforeEach(() => {
    const queries = [
      getInsertQuery({ value: '10.20'}),
      getInsertQuery({ value: '1000.20', description: 'test'})
    ];

    const db = pgp(dbConfig());
    return Promise.all(queries.map(q => db.none(q)));
  });

  describe('A mock test', () => {
    it('should pass', () => {
      var db = pgp(dbConfig());

      return db.any(`SELECT * FROM ${TABLE_NAME}`)
        .then(result => {
          expect(result).to.have.length(2);
        });
    });
  });
});

Restructure integration test to load fixture data in beforeEach


'use strict';

var element  = require('../valid-html-element')
  , randomId = require('time-uuid');

module.exports = function () {
	if (element(this).id) return this.id;
	return (this.id = this.nodeName.toLowerCase() + '-' + randomId());
};

'use strict';

var element    = require('../valid-html-element')
  , randomUniq = require('time-uuid');

module.exports = function () {
	if (element(this).id) return this.id;
	return (this.id = this.nodeName.toLowerCase() + '-' + randomUniq());
};

Use plain random generator instead of time based


var path = 'StyleManager/view/';
define([path + 'PropertyView', 'StyleManager/model/Property'],
  function(PropertyView, Property) {

    return {
      run : function(){

          describe('PropertyView', function() {

            var $fixtures;
            var $fixture;
            var model;
            var view;

            before(function () {
              $fixtures  = $("#fixtures");
              $fixture   = $('

');
            });

            beforeEach(function () {
              model = new Property();
              view = new PropertyView({
                model: model
              });
              $fixture.empty().appendTo($fixtures);
              $fixture.html(view.render().el);
            });

            afterEach(function () {
              //view.remove(); // strange errors ???
            });

            after(function () {
              $fixture.remove();
            });

            it('Rendered correctly', function() {
              var prop = view.el;
              $fixture.get(0).querySelector('.property').should.be.ok;
              prop.querySelector('.label').should.be.ok;
            });

        });
      }
    };

});

var path = 'StyleManager/view/';
define([path + 'PropertyView', 'StyleManager/model/Property', 'DomComponents/model/Component'],
  function(PropertyView, Property, Component) {

    return {
      run : function(){

          describe('PropertyView', function() {

            var $fixtures;
            var $fixture;
            var model;
            var view;

            before(function () {
              $fixtures  = $("#fixtures");
              $fixture   = $('

');
            });

            beforeEach(function () {
              component = new Component();
              model = new Property();
              view = new PropertyView({
                model: model
              });
              $fixture.empty().appendTo($fixtures);
              $fixture.html(view.render().el);
            });

            afterEach(function () {
              //view.remove(); // strange errors ???
            });

            after(function () {
              $fixture.remove();
            });

            it('Rendered correctly', function() {
              var prop = view.el;
              $fixture.get(0).querySelector('.property').should.be.ok;
              prop.querySelector('.label').should.be.ok;
            });

            it('Rendered correctly', function() {
              var prop = view.el;
              $fixture.get(0).querySelector('.property').should.be.ok;
              prop.querySelector('.label').should.be.ok;
            });

        });
      }
    };

});

Add Component to StyleManager tests


'use strict';

var Hapi = require('hapi');
var routes = require('./routes');
var config = require('./config');
var server = new Hapi.Server();

server.connection({
  port:config.SERVER_PORT,
  routes:{cors:true}
});

server.route(routes);

function startServer() {
  server.start(function() {
    console.log('Server running at:', server.info.uri);
  });
}

function stopServer() {
  server.stop(function() {
    console.log('Server stopped');
  });
}

module.exports.start = startServer;

module.exports.stop =  stopServer;

'use strict';

var Hapi = require('hapi');
var routes = require('./routes');
var config = require('./config');
var server = new Hapi.Server();

server.connection({
  port:config.SERVER_PORT,
  routes:{cors:{credentials:true}}
});

server.route(routes);

function startServer() {
  server.start(function() {
    console.log('Server running at:', server.info.uri);
  });
}

function stopServer() {
  server.stop(function() {
    console.log('Server stopped');
  });
}

module.exports.start = startServer;

module.exports.stop =  stopServer;

Change CORS settings to credentials:true


const express = require('express')
const app = express()
const path = require('path')
const nunjucks = require('nunjucks')

// Set up App
const appViews = [
  path.join(__dirname, '/app/views/'),
  path.join(__dirname, '/app/templates/')
]

nunjucks.configure(appViews, {
  autoescape: true,
  express: app,
  noCache: true,
  watch: true
})

// Set views engine
app.set('view engine', 'html')

// Serve static content for the app from the "public" directory
app.use('/public', express.static(path.join(__dirname, '/public')))

// Send assetPath to all views
app.use(function (req, res, next) {
  res.locals.asset_path = '/public/'
  next()
})

// Render views/index
app.get('/', function (req, res) {
  res.render('index')
})

// Log when app is running
app.listen(3000, function () {
  console.log('App is running...')
})

const express = require('express')
const app = express()
const path = require('path')
const nunjucks = require('nunjucks')

// Set up App
const appViews = [
  path.join(__dirname, '/app/views/'),
  path.join(__dirname, '/app/templates/')
]

nunjucks.configure(appViews, {
  autoescape: true,
  express: app,
  noCache: true,
  watch: true
})

// Set views engine
app.set('view engine', 'html')

// Serve static content for the app from the "public" directory
app.use('/public', express.static(path.join(__dirname, '/public')))

// Send assetPath to all views
app.use(function (req, res, next) {
  res.locals.asset_path = '/public/'
  next()
})

// Render views/index
app.get('/', function (req, res) {
  res.render('index')
})

// Log when app is running
app.listen(3000, function () {
  console.log('GOV.UK Frontend Alpha\n')
  console.log('Listening on port 3000   url: http://localhost:3000')
})

Update the output when the app is running


'use strict';

module.exports = {
  format: require('./format'),
  listing: require('./listing'),
  add: require('./add')
};

'use strict';

// Load requirements
const pq = require('promise-queue');

// Build the module structure
module.exports = {

  // Create paused queue instance
  items: new pq(0, Infinity),

  // Total number of items added to the queue
  total: 0,

  // Current task number being run
  current: 0,

  // Load our sub-modules
  format: require('./format'),
  listing: require('./listing'),
  add: require('./add')
};

Add promise queue to queue handler


var express = require('express');
var http = require('http');
var path    = require("path");
var fs = require('fs');
var _ = require('underscore');
var ldap = require('./modules/ldap');
var app = express();
var portNumber = 3001;

// app.use(express.static('public'));

app.use(function(req, res, next) {
  res.header("Access-Control-Allow-Origin", "*");
  res.header("Access-Control-Allow-Headers", "Origin, X-Requested-With, Content-Type, Accept");
  next();
});

app.get("/", function (request, response) { response.send({ status: 'OK' }); })
app.get('/employees', ldap.findAll);
app.get('/employees/:id([0-9]+)', ldap.findById);
app.get('/employees/:id([0-9]+)/team', ldap.team);
app.get('/employees/newcomers', ldap.newcomers);
app.get('/employees/apprentices', ldap.apprentices);
app.get('/employees/count', ldap.count);

app.use(express.static(__dirname));
app.listen(portNumber);

console.log("Responding server listening on port " + portNumber);

var express = require('express');
var http = require('http');
var path    = require("path");
var fs = require('fs');
var _ = require('underscore');
var ldap = require('./modules/ldap');
var app = express();
var portNumber = process.env.PORT || 3001;

// app.use(express.static('public'));

app.use(function(req, res, next) {
  res.header("Access-Control-Allow-Origin", "*");
  res.header("Access-Control-Allow-Headers", "Origin, X-Requested-With, Content-Type, Accept");
  next();
});

app.get("/", function (request, response) { response.send({ status: 'OK' }); })
app.get('/employees', ldap.findAll);
app.get('/employees/:id([0-9]+)', ldap.findById);
app.get('/employees/:id([0-9]+)/team', ldap.team);
app.get('/employees/newcomers', ldap.newcomers);
app.get('/employees/apprentices', ldap.apprentices);
app.get('/employees/count', ldap.count);

app.use(express.static(__dirname));
app.listen(portNumber);

console.log("Responding server listening on port " + portNumber);

Customize port to listen to


'use strict'

console.log("Welcome to Chatcat");

'use strict'

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

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

app.get('/', (req, res, next) => {
  res.send('
Hello Express!
');
});

app.get('/dashboard', (req, res, next) => {
  res.send('
This is the dashboard page
');
});

app.listen(app.get('port'), () => {
  console.log('Chatchat running on port:' + app.get('port'));
});

Complete lecture 31: add express application and base routing functionalities


$('body').scrollspy({
    target: '.bs-docs-sidebar',
    offset: 40
});

a https://wiki.selfhtml.org/wiki/JavaScript/Objekte/RegExp https://encrypted.google.com/search?hl=de&q=javascript%20regex%20whitespace%20slash%20match

Ein Text mit einem Link und 28 Zeichen delta http://without-systemd.org/wiki/index.php/Main_Page wird richtig berechnet nehme ich an. Aber wird er auch korrekt abgeschnitten?
Ein Text mit einem Link und 28 Zeichen delta ....................... wird richtig berechnet nehme ich an. Aber wird er auch korrekt abgeschnitten?

if ($('textarea#content').length > 0) {
	var regex = new RegExp('https?://[^\\s]*', 'g');
	var subst = new Array(24).join('.');

	$('label[for="content"]').append('
(0 of 140 chars)
');

	$('textarea#content').on('input propertychange', function() {
		var content = $(this).val().replace(regex, subst);
		var length = $(this).val().length - content.length;

		$(this).val($(this).val().substring(0, 140 + length));
		$('span#charcount').html($(this).val().replace(regex, subst).length);
	});
}

Add JS char counter for Tweet content


module.exports = function () {
    StructureSpawn.prototype.createCustomCreep =
    function(energy, roleName) {
        var numberOfParts = Math.floor(energy / 350);
        var body = [];
            for (let i = 0; i < numberOfParts; i++) {
                body.push(WORK);
            }
            for (let i = 0; i < numberOfParts; i++) {
                body.push(WORK);
            }
            for (let i = 0; i < numberOfParts; i++) {
                body.push(CARRY);
            }
            for (let i = 0; i < numberOfParts; i++) {
                body.push(MOVE);
            }
            for (let i = 0; i < numberOfParts; i++) {
                body.push(MOVE);
            }
        return this.createCreep(body, undefined, {role: roleName, working: false });
    };

};

module.exports = function () {
    StructureSpawn.prototype.createCustomCreep =
    function(energy, roleName) {
        var numberOfParts = Math.floor(energy / 350);
        var body = [];
            for (let i = 0; i < numberOfParts; i++) {
                body.push(WORK);
            }
            for (let i = 0; i < numberOfParts; i++) {
                body.push(WORK);
            }
            for (let i = 0; i < numberOfParts; i++) {
                body.push(CARRY);
            }
            for (let i = 0; i < numberOfParts; i++) {
                body.push(MOVE);
            }
            for (let i = 0; i < numberOfParts; i++) {
                body.push(MOVE);
            }
            for (let i = 0; i < numberOfParts; i++) {
                body.push(MOVE);
            }
        return this.createCreep(body, undefined, {role: roleName, working: false });
    };

};

WORK WORK CARRY MOVE MOVE MOVE


// @flow
import React from 'react';

type Props = {
  executionCount: any,
  running: boolean,
};

export default class Inputs extends React.PureComponent {
  props: Props;

  render(): ?React.Element<any> {
    const { executionCount, running } = this.props;
    const count = !executionCount ? ' ' : executionCount;
    const input = running ? '*' : count;
    return (
      <div className="prompt">
        [{input}]
      </div>
    );
  }
}

// @flow
import React from 'react';

type Props = {
  executionCount: any,
  running: boolean,
};

export default function Inputs(props: Props): ?React.Element<any> {
  const { executionCount, running } = props;
  const count = !executionCount ? ' ' : executionCount;
  const input = running ? '*' : count;

  return (
    <div className="prompt">
      [{input}]
    </div>
  );
}

Rewrite component into stateless function


#!/usr/bin/env node

var fs = require("fs");
var glob = require("glob");
var ts = require("typescript");
var tsa = require("../lib/Analyzer");
var o = require("../lib/JsonOutput");

var arg = process.argv.slice(2)[0];
const fileNames = glob(arg, {}, function(err, files) {
    if (err) {
        console.log(err);
        process.exit(1);
    }

    var outputter = new o.Output.JsonOutput("output.json");
    var analyzer = new tsa.SonarTypeScript.Analyzer(outputter);

    files.forEach(fileName => {
        console.log("Parsing: " + fileName);

        var sourceFile = ts.createSourceFile(
            fileName,
            fs.readFileSync(fileName).toString(),
            ts.ScriptTarget.ES6,
            /*setParentNodes */ true);

        analyzer.analyzeFile(sourceFile);
    });

    outputter.close();
});

#!/usr/bin/env node

var fs = require("fs");
var glob = require("glob");
var ts = require("typescript");
var tsa = require("../lib/Analyzer");
var o = require("../lib/JsonOutput");

var arg = process.argv.slice(2)[0];
const fileNames = glob(arg, {}, function(err, files) {
    if (err) {
        console.log(err);
        process.exit(1);
    }

    var outputter = new o.Output.JsonOutput("ts-analysis-results.json");
    var analyzer = new tsa.SonarTypeScript.Analyzer(outputter);

    files.forEach(fileName => {
        console.log("Parsing: " + fileName);

        var sourceFile = ts.createSourceFile(
            fileName,
            fs.readFileSync(fileName).toString(),
            ts.ScriptTarget.ES6,
            /*setParentNodes */ true);

        analyzer.analyzeFile(sourceFile);
    });

    outputter.close();
});

Use a more sensible output file name


"use strict";

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

// install request
const request = require('request');

const PORT = process.env.PORT || 3000;
const rovi = process.env.ROVI_APIKEY || '';

// API page with CORS header
app.get('/api/:artist/:sig', (req, res) => {
  const artist = req.params.artist;
  const sig = req.params.sig;

  console.log(artist);
  console.log(sig);
  console.log(rovi);
  console.log(PORT);
  console.log(process.env);

  const url = "http://api.rovicorp.com/search/v2.1/music/search?apikey=" + rovi + "&sig=" + sig + "&query=" + artist + "&entitytype=artist&size=1&include=images,musicbio,discography,videos";

  console.log(url);

  request.get(url, (err, response, body) => {
    if (err) throw err;

    res.header('Access-Control-Allow-Origin', '*');
    res.send(JSON.parse(body));
  });
});

app.listen(PORT, () => {
  console.log(`Server running on port ${PORT}`);
});

"use strict";

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

// install request
const request = require('request');

const PORT = process.env.PORT || 3000;
const rovi = process.env.ROVI_APIKEY || '';

// API page with CORS header
app.get('/api/:artist/:sig', (req, res) => {
  const artist = req.params.artist;
  const sig = req.params.sig;

  console.log(artist);
  console.log(sig);
  console.log(rovi);
  console.log(PORT);
  console.log(process.env);

  const url = "http://api.rovicorp.com/search/v2.1/music/search?apikey=" + rovi + "&sig=" + sig + "&query=" + artist + "&entitytype=artist&size=1&include=images,musicbio,discography,videos&formatid=16";

  console.log(url);

  request.get(url, (err, response, body) => {
    if (err) throw err;

    res.header('Access-Control-Allow-Origin', '*');
    res.send(JSON.parse(body));
  });
});

app.listen(PORT, () => {
  console.log(`Server running on port ${PORT}`);
});

Change file image size parameter


var courses = new Bloodhound({
  datumTokenizer: Bloodhound.tokenizers.obj.whitespace('keywords'),
  queryTokenizer: Bloodhound.tokenizers.whitespace,
  prefetch: '/data/courses.json'
});

$('#bloodhound .typeahead').typeahead({
  hint: true,
  highlight: true,
  minLength: 1
},
{
  name: 'courses',
  display: 'title',
  source: courses
}).on('typeahead:selected', function (e, datum) {
  document.location.href += datum.url;
});

var courses = new Bloodhound({
  datumTokenizer: Bloodhound.tokenizers.obj.whitespace('keywords'),
  queryTokenizer: Bloodhound.tokenizers.whitespace,
  prefetch: '/data/courses.json'
});

$('#bloodhound .typeahead').typeahead({
  hint: true,
  highlight: true,
  minLength: 1
},
{
  name: 'courses',
  display: 'title',
  source: courses
}).on('typeahead:render', function (e) {
  $('#bloodhound').find('.tt-selectable:first').addClass('tt-cursor');
  $('#bloodhound').find('.tt-selectable').on('mouseover', function (e) {
    $('#bloodhound').find('.tt-selectable:first').removeClass('tt-cursor');
  });
}).on('typeahead:selected', function (e, datum) {
  document.location.href += datum.url;
});

Select first option by default


import React from 'react';

const tally = (measurements, buffer) => {
  const tallied = measurements.reduce((accum, value) => {
    accum[value+buffer] = (accum[value+buffer] || 0) + 1;
    return accum;
  }, {});
  return (

          Measurement + Buffer
          Count

        { Object.keys(tallied).map((m) => (
          <tr key={`t-${m}`}>
            {m}
            {tallied[m]}

        ))}

  );
};

const MeasurementsTally = ({measurements = [], buffer=0}) => measurements.length > 0 ? tally(measurements, buffer) : null;

export default MeasurementsTally;

import React from 'react';

const tally = (measurements, buffer) => {
  const tallied = measurements.reduce((accum, value) => {
    accum[value+buffer] = (accum[value+buffer] || 0) + 1;
    return accum;
  }, {});
  return (

          Measurement + Buffer
          Count

        { Object.keys(tallied).sort().map((m) => (
          <tr key={`t-${m}`}>
            {m}
            {tallied[m]}

        ))}

  );
};

const MeasurementsTally = ({measurements = [], buffer=0}) => measurements.length > 0 ? tally(measurements, buffer) : null;

export default MeasurementsTally;

Sort tallied measurements for consistent display


orion.attributes.registerAttribute('createdBy', {
  previewTemplate: 'createdByPreview',
  getSchema: function(options) {
    return {
      type: String,
      index: 1,
      autoform: {
        omit: true
      },
      optional: true,
      autoValue: function() {
        if (this.isInsert) {
          return this.userId;
        } else if (this.isUpsert) {
          return { $setOnInsert: this.userId };
        } else {
          this.unset();
        }
      }
    };
  }
});

if (Meteor.isServer) {
  Meteor.publish('userProfileForCreatedByAttributeColumn', function(userId) {
    check(userId, String);
    return Meteor.users.find({ _id: userId }, { fields: { profile: 1 } });
  });
}
if (Meteor.isClient) {
  ReactiveTemplates.onRendered('attributePreview.createdBy', function() {
    this.subscribe('userProfileForCreatedByAttributeColumn', this.data.value);
  });
  ReactiveTemplates.helpers('attributePreview.createdBy', {
    name: function() {
      var user = Meteor.users.findOne(this.value);
      return user && user.profile.name;
    }
  });
}

orion.attributes.registerAttribute('createdBy', {
  previewTemplate: 'createdByPreview',
  getSchema: function(options) {
    return {
      type: String,
      index: 1,
      autoform: {
        omit: true,
      },
      optional: true,
      autoValue: function() {
        if (this.isInsert) {
          return this.userId;
        } else if (this.isUpsert) {
          return { $setOnInsert: this.userId };
        } else {
          this.unset();
        }
      },
    };
  },
});

if (Meteor.isServer) {
  Meteor.publish('userProfileForCreatedByAttributeColumn', function(userId) {
    check(userId, String);
    return Meteor.users.find({ _id: userId }, { fields: { profile: 1 } });
  });
}

if (Meteor.isClient) {
  ReactiveTemplates.onRendered('attributePreview.createdBy', function() {
    this.subscribe('userProfileForCreatedByAttributeColumn', this.data.value);
  });

  ReactiveTemplates.helpers('attributePreview.createdBy', {
    name: function() {
      var user = Meteor.users.findOne(this.value);
      return user && user.profile && user.profile.name;
    },
  });
}

Fix bug with createdBy attribute column


// This is a manifest file that'll be compiled into application.js, which will include all the files
// listed below.
//
// Any JavaScript/Coffee file within this directory, lib/assets/javascripts, vendor/assets/javascripts,
// or vendor/assets/javascripts of plugins, if any, can be referenced here using a relative path.
//
// It's not advisable to add code directly here, but if you do, it'll appear at the bottom of the
// compiled file.
//
// Read Sprockets README (https://github.com/sstephenson/sprockets#sprockets-directives) for details
// about supported directives.
//
//= require jquery
//= require jquery_ujs
//= require turbolinks
//= require_tree .

// This is a manifest file that'll be compiled into application.js, which will include all the files
// listed below.
//
// Any JavaScript/Coffee file within this directory, lib/assets/javascripts, vendor/assets/javascripts,
// or vendor/assets/javascripts of plugins, if any, can be referenced here using a relative path.
//
// It's not advisable to add code directly here, but if you do, it'll appear at the bottom of the
// compiled file.
//
// Read Sprockets README (https://github.com/sstephenson/sprockets#sprockets-directives) for details
// about supported directives.
//
//= require jquery
//= require jquery_ujs
//= require_tree .

Stop trying to be fast.


import {createStore, applyMiddleware, compose} from 'redux'
import {routerMiddleware} from 'react-router-redux'
import {browserHistory} from 'react-router'
import makeReducer from '../universal/redux/makeReducer'

export default initialState => {
  let store
  const reducer = makeReducer()
  const reduxRouterMiddleware = routerMiddleware(browserHistory)
  const middlewares = [
    reduxRouterMiddleware,
  ]

  if (process.env.NODE_ENV !== 'production') {
    store = createStore(reducer, initialState, applyMiddleware(...middlewares))
  } else {
    const devtoolsExt = global.devToolsExtension && global.devToolsExtension()
    if (!devtoolsExt) {
      // We don't have the Redux extension in the browser, show the Redux logger
      const createLogger = require('redux-logger')
      const logger = createLogger({
        level: 'info',
        collapsed: true
      })
      middlewares.push(logger)
    }
    store = createStore(reducer, initialState, compose(
      applyMiddleware(...middlewares),
      devtoolsExt || (f => f)
    ))
  }
  return store
}

import {createStore, applyMiddleware, compose} from 'redux'
import {routerMiddleware} from 'react-router-redux'
import {browserHistory} from 'react-router'
import makeReducer from '../universal/redux/makeReducer'

export default initialState => {
  let store
  const reducer = makeReducer()
  const reduxRouterMiddleware = routerMiddleware(browserHistory)
  const middlewares = [
    reduxRouterMiddleware,
  ]

  if (process.env.NODE_ENV === 'production') {
    store = createStore(reducer, initialState, applyMiddleware(...middlewares))
  } else {
    const devtoolsExt = global.devToolsExtension && global.devToolsExtension()
    if (!devtoolsExt) {
      // We don't have the Redux extension in the browser, show the Redux logger
      const createLogger = require('redux-logger')
      const logger = createLogger({
        level: 'info',
        collapsed: true
      })
      middlewares.push(logger)
    }
    store = createStore(reducer, initialState, compose(
      applyMiddleware(...middlewares),
      devtoolsExt || (f => f)
    ))
  }
  return store
}

Fix redux devtools not being loaded.


/**
 * External dependencies
 */
import { storiesOf } from '@storybook/react';
import { set } from 'lodash';

/**
 * Internal dependencies
 */
import UserInputApp from '../assets/js/components/user-input/UserInputApp';
import { WithTestRegistry, createTestRegistry } from '../tests/js/utils';

storiesOf( 'User Input', module )
	.add( 'User Input Page', () => {
		// Set the featureFlag.
		set( global, 'featureFlags.widgets.userInput.enabled', true );
		return (
			<WithTestRegistry callback={ createTestRegistry }>
				<UserInputApp />
			</WithTestRegistry>
		);
	} )
	.add( 'No Access', () => {
		// Set the featureFlag.
		set( global, 'featureFlags.widgets.userInput.enabled', false );
		return (
			<WithTestRegistry callback={ createTestRegistry }>
				<UserInputApp />
			</WithTestRegistry>
		);
	} );

/**
 * External dependencies
 */
import { storiesOf } from '@storybook/react';

/**
 * Internal dependencies
 */
import UserInputApp from '../assets/js/components/user-input/UserInputApp';
import { WithTestRegistry, createTestRegistry } from '../tests/js/utils';

storiesOf( 'User Input', module )
	.add( 'UserInputApp', () => {
		// Set the featureFlag.
		global.featureFlags = { widgets: { userInput: { enabled: true } } };
		return (
			<WithTestRegistry callback={ createTestRegistry }>
				<UserInputApp />
			</WithTestRegistry>
		);
	} );

Set the featureFlag without using lodash.


import highlight from 'flarum/helpers/highlight';
import ActionButton from 'flarum/components/action-button';

export default class DiscussionsSearchResults {
  constructor() {
    this.results = {};
  }

  search(string) {
    this.results[string] = [];
    return app.store.find('discussions', {q: string, page: {limit: 3}, include: 'relevantPosts,relevantPosts.discussion'}).then(results => {
      this.results[string] = results;
    });
  }

  view(string) {
    return [
      m('li.dropdown-header', 'Discussions'),
      m('li', ActionButton.component({
        icon: 'search',
        label: 'Search all discussions for "'+string+'"',
        href: app.route('index', {q: string}),
        config: m.route
      })),
      (this.results[string] && this.results[string].length) ? this.results[string].map(discussion => {
        var post = discussion.relevantPosts()[0];
        return m('li.discussion-search-result', {'data-index': 'discussions'+discussion.id()},
          m('a', { href: app.route.discussion(discussion, post.number()), config: m.route },
            m('div.title', highlight(discussion.title(), string)),
            m('div.excerpt', highlight(post.contentPlain().substring(0, 100), string))
          )
        );
      }) : ''
    ];
  }
}

import highlight from 'flarum/helpers/highlight';
import truncate from 'flarum/utils/truncate';
import ActionButton from 'flarum/components/action-button';

export default class DiscussionsSearchResults {
  constructor() {
    this.results = {};
  }

  search(string) {
    this.results[string] = [];
    return app.store.find('discussions', {q: string, page: {limit: 3}, include: 'relevantPosts,relevantPosts.discussion'}).then(results => {
      this.results[string] = results;
    });
  }

  view(string) {
    return [
      m('li.dropdown-header', 'Discussions'),
      m('li', ActionButton.component({
        icon: 'search',
        label: 'Search all discussions for "'+string+'"',
        href: app.route('index', {q: string}),
        config: m.route
      })),
      (this.results[string] && this.results[string].length) ? this.results[string].map(discussion => {
        var post = discussion.relevantPosts()[0];
        return m('li.discussion-search-result', {'data-index': 'discussions'+discussion.id()},
          m('a', { href: app.route.discussion(discussion, post.number()), config: m.route },
            m('div.title', highlight(discussion.title(), string)),
            m('div.excerpt', highlight(truncate(post.contentPlain(), 100), string))
          )
        );
      }) : ''
    ];
  }
}

Use truncate for search suggestions


function hideAnnouncement(announcement_created_at) {
  createCookie(announcement_created_at, 'hidden', 1);
  $("#announcement").parent().slideUp();
}

// http://www.quirksmode.org/js/cookies.html
function createCookie(name,value,days) {
  if (days) {
    var date = new Date();
    date.setTime(date.getTime()+(days*24*60*60*1000));
    var expires = "; expires="+date.toGMTString();
  }
  else var expires = "";
  document.cookie = name+"="+value+expires+"; path=/";
}

function hideAnnouncement(announcement_created_at) {
  createCookie(announcement_created_at, 'hidden', 365);
  $("#announcement").parent().slideUp();
}

// http://www.quirksmode.org/js/cookies.html
function createCookie(name,value,days) {
  if (days) {
    var date = new Date();
    date.setTime(date.getTime()+(days*24*60*60*1000));
    var expires = "; expires="+date.toGMTString();
  }
  else var expires = "";
  document.cookie = name+"="+value+expires+"; path=/";
}

Change cookie expiration to 1 year (instead of 1 day).