var union    = require('union'),
    ecstatic = require('ecstatic');

var port = process.env.PORT || 8080;

//
// ### redirect(res)
// World's simplest redirect function
//
function redirect(res) {
  var url  = 'http://2015.empirenode.org',
      body = '
301. Redirecting to ' + url + '
';

  res.writeHead(301, { 'content-type': 'text/html', location: url });
  res.end(body);
}

var server = union.createServer({
  before: [
    function (req, res) {
      var host = req.headers.host,
          parts = host.split('.');

      console.log('%s - %s%s', req.method, host, req.url);
      if (process.env.NODE_ENV === 'production'
        && (parts.length !== 3 || parts[0] === 'www')) {
        return redirect(res);
      }

      res.emit('next');
    },
    ecstatic({
      root: __dirname + '/public',
      defaultExt: true
    }),
    function (req, res) {
      return redirect(res);
    }
  ]
});

server.listen(port, function () {
  console.log('Listening on %s', port);
});

var union    = require('union'),
    ecstatic = require('ecstatic');

var port = process.env.PORT || 8080;

//
// ### redirect(res)
// World's simplest redirect function
//
function redirect(res) {
  var url  = 'http://2015.empirenode.org',
      body = '
301. Redirecting to ' + url + '
';

  res.writeHead(301, { 'content-type': 'text/html', location: url });
  res.end(body);
}

var server = union.createServer({
  before: [
    function (req, res) {
      var host = req.headers.host || '',
          parts = host.split('.');

      console.log('%s - %s%s', req.method, host, req.url);
      if (process.env.NODE_ENV === 'production'
        && (parts.length !== 3 || parts[0] === 'www')) {
        return redirect(res);
      }

      res.emit('next');
    },
    ecstatic({
      root: __dirname + '/public',
      defaultExt: true
    }),
    function (req, res) {
      return redirect(res);
    }
  ]
});

server.listen(port, function () {
  console.log('Listening on %s', port);
});

Check for lack of host header.


import React from 'react'
import PropTypes from 'prop-types'
import WidgetSettings from './WidgetSettingsContainer'
import SettingsChildWrapper from '../SettingsChildWrapperComponent'
import FullScreenProgress from 'general/FullScreenProgress'

class WidgetsSettings extends React.Component {
  constructor (props) {
    super(props)

    this.state = {
      userWidgets: null
    }
  }

  componentDidMount () {
    const { user } = this.props

    const userWidgets = {}
    var node

    for (var index in user.widgets.edges) {
      node = user.widgets.edges[index].node
      userWidgets[node.name] = node
    }

    this.setState({
      userWidgets: userWidgets
    })
  }

  render () {
    const { user, app, showError } = this.props

    // We need to wait for the
    // userWidgetsMap to be created before
    // mounting the WidgetSettings.
    if (!this.state.userWidgets) {
      return (<FullScreenProgress />)
    }

    const self = this
    return (
      <SettingsChildWrapper>
        <div style={{ paddingTop: 0 }} >
          {app.widgets.edges.map((edge, index) => {
            return (<WidgetSettings
              key={index}
              user={user}
              appWidget={edge.node}
              widget={self.state.userWidgets[edge.node.name]}
              showError={showError} />)
          })}
        </div>
      </SettingsChildWrapper>
    )
  }
}

WidgetsSettings.propTypes = {
  user: PropTypes.object.isRequired,
  app: PropTypes.object.isRequired,
  showError: PropTypes.func.isRequired
}

export default WidgetsSettings

import React from 'react'
import PropTypes from 'prop-types'
import WidgetSettings from './WidgetSettingsContainer'
import SettingsChildWrapper from '../SettingsChildWrapperComponent'
import FullScreenProgress from 'general/FullScreenProgress'

class WidgetsSettings extends React.Component {
  constructor (props) {
    super(props)

    this.state = {
      userWidgets: null
    }
  }

  componentDidMount () {
    const { user } = this.props

    const userWidgets = {}
    var node

    for (var index in user.widgets.edges) {
      node = user.widgets.edges[index].node
      userWidgets[node.name] = node
    }

    this.setState({
      userWidgets: userWidgets
    })
  }

  render () {
    const { user, app, showError } = this.props

    // We need to wait for the
    // userWidgetsMap to be created before
    // mounting the WidgetSettings.
    if (!this.state.userWidgets) {
      return (<FullScreenProgress />)
    }

    const self = this
    return (
      <SettingsChildWrapper>
        <div style={{ paddingTop: 0 }} >
          {app.widgets.edges.map((edge, index) => {
            return (<WidgetSettings
              key={index}
              user={user}
              appWidget={edge.node}
              widget={self.state.userWidgets[edge.node.name] || null}
              showError={showError} />)
          })}
        </div>
      </SettingsChildWrapper>
    )
  }
}

WidgetsSettings.propTypes = {
  user: PropTypes.object.isRequired,
  app: PropTypes.object.isRequired,
  showError: PropTypes.func.isRequired
}

export default WidgetsSettings

Allow a user’s widget data to be null on the settings page.


function setAriaCurrent() {
	var title = document.title;
	if title== "Comunidad hispanohablante de NVDA" {
		var id = "inicio"
	}
	document.getElementById(id).setAttribute("aria-current", "page");
}

setAriaCurrent();

function setAriaCurrent() {
	var title = document.title;
	if title== "Comunidad hispanohablante de NVDA" {
		var id = "inicio"
	}
	document.getElementById(id).removeAttribute("accesskey");
	document.getElementById(id).setAttribute("aria-current", "page");
}

setAriaCurrent();

Test why aria-current is not read: remove accesskey


import keys from 'ember-metal/keys';

/**
  Merge the contents of two objects together into the first object.

  ```javascript
  Ember.merge({first: 'Tom'}, {last: 'Dale'}); // {first: 'Tom', last: 'Dale'}
  var a = {first: 'Yehuda'};
  var b = {last: 'Katz'};
  Ember.merge(a, b); // a == {first: 'Yehuda', last: 'Katz'}, b == {last: 'Katz'}
  ```

  @method merge
  @for Ember
  @param {Object} original The object to merge into
  @param {Object} updates The object to copy properties from
  @return {Object}
*/
export default function merge(original, updates) {
  if (!updates || typeof updates !== 'object') {
    return original;
  }

  var props = keys(updates);
  var prop;
  var length = props.length;

  for (var i = 0; i < length; i++) {
    prop = props[i];
    original[prop] = updates[prop];
  }

  return original;
}

export function assign(original, ...args) {
  for (let i=0, l=args.length; i<l; i++) {
    let arg = args[i];
    if (!arg) { continue; }

    for (let prop in arg) {
      if (arg.hasOwnProperty(prop)) { original[prop] = arg[prop]; }
    }
  }

  return original;
}

import keys from 'ember-metal/keys';

/**
  Merge the contents of two objects together into the first object.

  ```javascript
  Ember.merge({first: 'Tom'}, {last: 'Dale'}); // {first: 'Tom', last: 'Dale'}
  var a = {first: 'Yehuda'};
  var b = {last: 'Katz'};
  Ember.merge(a, b); // a == {first: 'Yehuda', last: 'Katz'}, b == {last: 'Katz'}
  ```

  @method merge
  @for Ember
  @param {Object} original The object to merge into
  @param {Object} updates The object to copy properties from
  @return {Object}
*/
export default function merge(original, updates) {
  if (!updates || typeof updates !== 'object') {
    return original;
  }

  var props = keys(updates);
  var prop;
  var length = props.length;

  for (var i = 0; i < length; i++) {
    prop = props[i];
    original[prop] = updates[prop];
  }

  return original;
}

export function assign(original, ...args) {
  for (let i=0, l=args.length; i<l; i++) {
    let arg = args[i];
    if (!arg) { continue; }

    let updates = keys(arg);

    for (let i=0, l=updates.length; i<l; i++) {
      let prop = updates[i];
      original[prop] = arg[prop];
    }
  }

  return original;
}

Change `assign` to not use a for-in loop


if(!window.jQuery) window.$ = window.Zepto = require('zepto-browserify').$;

require('./components/polyfills.js')();

let warn = (msg) => {
    if (!window || !window.console) {
        return;
    }
    window.console.warn(msg);
};

if (typeof Object.assign !== 'function') {
    require('object.assign/shim')();
}

window.Storage = require('showcar-storage');
window.Pager = require('./components/pager.js');

require('showcar-icons');

const ctor = document.createElement('as24-tracking').constructor;

if (ctor === HTMLElement || ctor === HTMLUnknownElement) {
    // only requiring showcar-tracking when it was not already included before
    require('showcar-tracking');
}

require('./components/custom-dropdown.js');

$(_ => {
    require('./components/navigation.js');
    require('./components/rotating-arrow.js')();
    require('./components/sticky.js')();
    require('./components/collapse.js')();
    require('./components/scroll.js');
});

if (!window.notification) {
    window.notification = require('./components/notification.js');
} else {
    warn('window.notification is already registered.');
}

if(!window.jQuery) window.$ = window.Zepto = require('zepto-browserify').$;

require('./components/polyfills.js')();

let warn = (msg) => {
    if (!window || !window.console) {
        return;
    }
    window.console.warn(msg);
};

if (typeof Object.assign !== 'function') {
    require('object.assign/shim')();
}

throw new Error('test!');

window.Storage = require('showcar-storage');
window.Pager = require('./components/pager.js');

require('showcar-icons');

const ctor = document.createElement('as24-tracking').constructor;

if (ctor === HTMLElement || ctor === HTMLUnknownElement) {
    // only requiring showcar-tracking when it was not already included before
    require('showcar-tracking');
}

require('./components/custom-dropdown.js');

$(_ => {
    require('./components/navigation.js');
    require('./components/rotating-arrow.js')();
    require('./components/sticky.js')();
    require('./components/collapse.js')();
    require('./components/scroll.js');
});

if (!window.notification) {
    window.notification = require('./components/notification.js');
} else {
    warn('window.notification is already registered.');
}

Throw new error in order to test nightmare js setup


$(function(){
  $( "input[type='submit']" ).click(function() {
    $("form[data-remote]").on("ajax:success", function(e, data, status, xhr) {
      $('.flash-notice').show(300);
      $(":input:not(input[type=submit])").val("");
      $( ":input:enabled" ).focus(function() {
        $(".flash-notice").hide(1000);
      });
    })
  });
});

$(function(){
  $('#submit-button').click(function() {
    $("form[data-remote]").on("ajax:success", function(e, data, status, xhr) {
      $('.flash-notice').show(300);
      $(":input:not(input[type=submit])").val("");
      $( ":input:enabled" ).focus(function() {
        $(".flash-notice").hide(1000);
      });
    })
  });
});

Use proper target for button


(function() {
  // Define filter operations
  window.shared || (window.shared = {});
  var Env = window.shared.Env;

  window.shared.MixpanelUtils = {
    isMixpanelEnabled: function() {
      return (window.mixpanel && Env.shouldReportAnalytics);
    },
    registerUser: function(currentEducator) {
      if (!Mixpanel.isMixpanelEnabled()) return;

      try {
        window.mixpanel.register({
          'deployment_key': Env.deploymentKey,
          'educator_id': currentEducator.id,
          'educator_is_admin': currentEducator.admin,
          'educator_school_id': currentEducator.school_id
        });
      }
      catch (err) {
        console.error(err);
      }
    },
    track: function(key, attrs) {
      if (!Mixpanel.isMixpanelEnabled()) return;
      try {
        return window.mixpanel.track(key, attrs);
      }
      catch (err) {
        console.error(err);
      }
    }
  };
})();

(function() {
  // Define filter operations
  window.shared || (window.shared = {});
  var Env = window.shared.Env;

  window.shared.MixpanelUtils = {
    isMixpanelEnabled: function() {
      return (window.mixpanel && Env.shouldReportAnalytics);
    },
    registerUser: function(currentEducator) {
      if (!MixpanelUtils.isMixpanelEnabled()) return;

      try {
        window.mixpanel.register({
          'deployment_key': Env.deploymentKey,
          'educator_id': currentEducator.id,
          'educator_is_admin': currentEducator.admin,
          'educator_school_id': currentEducator.school_id
        });
      }
      catch (err) {
        console.error(err);
      }
    },
    track: function(key, attrs) {
      if (!MixpanelUtils.isMixpanelEnabled()) return;
      try {
        return window.mixpanel.track(key, attrs);
      }
      catch (err) {
        console.error(err);
      }
    }
  };
})();

Fix bug with guarding for MixPanel in demo env


function routerConfig ($stateProvider, $urlRouterProvider) {
  'ngInject';
  $stateProvider
    .state('home', {
      url: '/',
      templateUrl: 'app/main/main.html',
      controller: 'MainController',
      controllerAs: 'main'
    })
    .state('ambr', {
      url: '/ambr',
      templateUrl: 'app/projects/ambr.html',
      controller: 'AmbrController',
      controllerAs: 'ambr'
    })
    .state('brixton', {
      url: '/brixton',
      templateUrl: 'app/projects/brixton.html',
      controller: 'BrixtonController',
      controllerAs: 'brixton'
    })
    .state('eufn', {
      url: '/eufn',
      templateUrl: 'app/projects/eufn.html',
      controller: 'EufnController',
      controllerAs: 'eufn'
    })
    .state('lambeth', {
      url: '/lambeth',
      templateUrl: 'app/projects/lambeth.html',
      controller: 'LambethController',
      controllerAs: 'lambeth'
    })
    .state('lancastrians', {
      url: '/lancastrians',
      templateUrl: 'app/projects/lancastrians.html',
      controller: 'LancastriansController',
      controllerAs: 'lancastrians'
    })
    .state('layla', {
      url: '/layla',
      templateUrl: 'app/projects/layla.html',
      controller: 'LaylaController',
      controllerAs: 'layla'
    })
    .state('saffron', {
      url: '/saffron',
      templateUrl: 'app/projects/saffron.html',
      controller: 'SaffronController',
      controllerAs: 'saffron'
    });

  $urlRouterProvider.otherwise('/');
}

export default routerConfig;

function routerConfig ($stateProvider, $urlRouterProvider) {
  'ngInject';
  $stateProvider
    .state('home', {
      url: '/',
      templateUrl: 'app/main/main.html',
      controller: 'MainController',
      controllerAs: 'main'
    })
    .state('ambr', {
      url: '/ambr',
      templateUrl: 'app/projects/ambr.html'
    })
    .state('brixton', {
      url: '/brixton',
      templateUrl: 'app/projects/brixton.html'
    })
    .state('eufn', {
      url: '/eufn',
      templateUrl: 'app/projects/eufn.html'
    })
    .state('lambeth', {
      url: '/lambeth',
      templateUrl: 'app/projects/lambeth.html'
    })
    .state('lancastrians', {
      url: '/lancastrians',
      templateUrl: 'app/projects/lancastrians.html'
    })
    .state('layla', {
      url: '/layla',
      templateUrl: 'app/projects/layla.html'
    })
    .state('saffron', {
      url: '/saffron',
      templateUrl: 'app/projects/saffron.html'
    });

  $urlRouterProvider.otherwise('/');
}

export default routerConfig;

Remove references to controllers from project page routing


/* eslint-env mocha */
/* eslint-disable func-names, prefer-arrow-callback */

import { createUser, removeUser } from './helpers.js'

describe('APP - Log In', () => {
  beforeEach(() => removeUser());

  it('redirect to login @watch', () => {
    browser.url('http://localhost:3000')
           .waitForExist('.Login');

    expect(browser.getUrl()).to.equal('http://localhost:3000/login');
  });

  it('should allow us to login @watch', () => {
    createUser();

    browser.url('http://localhost:3000')
           .waitForExist('.Login');
    browser.setValue('[name="identifiant"]', 'admin@admin.com')
           .setValue('[name="password"]', 'password')
           .click('[type="submit"]');

    browser.waitForExist('.Index');
    expect(browser.getUrl()).to.equal('http://localhost:3000/');
  });
});

/* eslint-env mocha */
/* eslint-disable func-names, prefer-arrow-callback */

import { createUser, removeUser } from './helpers';

describe('APP - Log In', () => {
  beforeEach(() => removeUser());

  it('redirect to login @watch', () => {
    browser.url('http://localhost:3000')
           .waitForExist('.Login');

    expect(browser.getUrl()).to.equal('http://localhost:3000/login');
  });

  it('should allow us to login @watch', () => {
    createUser();

    browser.url('http://localhost:3000')
           .waitForExist('.Login');
    browser.setValue('[name="identifiant"]', 'admin@admin.com')
           .setValue('[name="password"]', 'password')
           .click('[type="submit"]');

    browser.waitForExist('.Index');
    expect(browser.getUrl()).to.equal('http://localhost:3000/');
  });
});

Update Acceptance test, fix import to app login


var webpack = require('webpack');

module.exports = {
  entry: './entry',
  externals: {
    // Add global variables you would like to import
    'react': 'React',
    'react-router': 'ReactRouter',
    'react-router-ssr': 'ReactRouterSSR',
    'react-meteor-data': 'ReactMeteorData',
    'blaze-to-react': 'BlazeToReact'
  },
  resolve: {
    extensions: ['', '.js', '.jsx', '.json', '.css', '.scss']
  },
  module: {
    loaders: [
      { test: /\.jsx?$/, loader: 'babel', query: { stage: 0 }, exclude: /node_modules/ },
      { test: /\.css$/, loader: 'style!css' },
      { test: /\.scss$/, loader: 'style!css!sass' },
      { test: /\.(png|jpe?g)(\?.*)?$/, loader: 'url?limit=8182' },
      { test: /\.(svg|ttf|woff|eot)(\?.*)?$/, loader: 'file' }
    ]
  }
};

var webpack = require('webpack');

module.exports = {
  entry: './entry',
  externals: {
    // Add global variables you would like to import
    'react': 'React',
    'react-router': 'ReactRouter',
    'react-router-ssr': 'ReactRouterSSR',
    'react-meteor-data': 'ReactMeteorData',
    'blaze-to-react': 'BlazeToReact'
  },
  resolve: {
    extensions: ['', '.js', '.jsx', '.json', '.css', '.scss']
  },
  module: {
    loaders: [
      { test: /\.jsx?$/, loader: 'babel', query: { stage: 0 }, exclude: /node_modules/ },
      { test: /\.css$/, loader: 'style-collector!css' },
      { test: /\.scss$/, loader: 'style-collector!css!sass' },
      { test: /\.(png|jpe?g)(\?.*)?$/, loader: 'url?limit=8182' },
      { test: /\.(svg|ttf|woff|eot)(\?.*)?$/, loader: 'file' }
    ]
  }
};

Fix CSS loaders for ssr


PANDA.views.DatasetsResults = Backbone.View.extend({
    template: PANDA.templates.datasets_results,
    pager_template: PANDA.templates.pager,

    initialize: function(options) {
        _.bindAll(this, "render");

        this.search = options.search;
        this.search.datasets.bind("reset", this.render);
    },

    render: function() {
        context = this.search.datasets.meta;
        context["settings"] = PANDA.settings;

        context["query"] = this.search.query;
        context["category"] = this.search.category;

        context["datasets"] = this.search.datasets.results()["datasets"];

        // Remove any lingering modal from previous usage
        $("#modal-dataset-search").remove();

        this.el.html(this.template(context));

        // Enable result sorting
        $("#datasets-results table").tablesorter({
            cssHeader: "no-sort-header",
            cssDesc: "sort-desc-header",
            cssAsc: "sort-asc-header",
            headers: {
                0: { sorter: "text" },
                1: { sorter: false },
                2: { sorter: "text" },
                3: { sorter: false }
            },
            textExtraction: function(node) {
                return $(node).children(".sort-value").text();
            }
        });

        // Create new modal
        $("#modal-dataset-search").modal({
            keyboard: true
        });
    }
});

PANDA.views.DatasetsResults = Backbone.View.extend({
    template: PANDA.templates.datasets_results,
    pager_template: PANDA.templates.pager,

    initialize: function(options) {
        _.bindAll(this, "render");

        this.search = options.search;
        this.search.datasets.bind("reset", this.render);
    },

    render: function() {
        context = this.search.datasets.meta;
        context["settings"] = PANDA.settings;

        context["query"] = this.search.query;
        context["category"] = this.search.category;

        context["datasets"] = this.search.datasets.results()["datasets"];

        this.el.html(this.template(context));

        // Enable result sorting
        $("#datasets-results table").tablesorter({
            cssHeader: "no-sort-header",
            cssDesc: "sort-desc-header",
            cssAsc: "sort-asc-header",
            headers: {
                0: { sorter: "text" },
                1: { sorter: false },
                2: { sorter: "text" },
                3: { sorter: false }
            },
            textExtraction: function(node) {
                return $(node).children(".sort-value").text();
            }
        });
    }
});

Remove extraneous references to modal.


// Disable auto discover for all elements:
//Dropzone.autoDiscover = false;
var submissionDropzone;

Dropzone.options.submissionDropzone = {
    url: "/images/upload",
    maxFilesize: 2,
    paramName: "uploadfile",
    maxThumbnailFilesize: 2,
    clickable: true,
    maxFiles: 1,
    acceptedFiles: "image/jpeg,image/png",
    thumbnailWidth: 250,

    resize: function(file) {
        var ratio = 250 / file.width;
        var imgHeight = file.height * ratio;
        if (imgHeight > 250) {
            imgHeight = 250; //constrain
        }

        var resizeInfo = {
            srcX: 0,
            srcY: 0,
            trgX: 0,
            trgY: 0,
            srcWidth: file.width,
            srcHeight: file.height,
            trgWidth: this.options.thumbnailWidth,
            trgHeight: imgHeight
        };

        $('.dz-details img').height(imgHeight);

        return resizeInfo;
    },

    init: function() {
        submissionDropzone = this;

        this.on('success', function(file, json) {
            $("#imagefile").val(json.img);
        });

        this.on('error', function(file, errorMessage, xhr) {
            $(".dz-error-mark").click(function() {
                submissionDropzone.removeFile(file);
            });
        });

        this.on('addedfile', function(file) {

        });

        this.on('drop', function(file) {

        });
    }
};

// Disable auto discover for all elements:
//Dropzone.autoDiscover = false;
var submissionDropzone;

Dropzone.options.submissionDropzone = {
    url: "/images/upload",
    maxFilesize: 3,
    paramName: "uploadfile",
    maxThumbnailFilesize: 3,
    clickable: true,
    maxFiles: 1,
    acceptedFiles: "image/jpeg,image/png",
    thumbnailWidth: 250,

    resize: function(file) {
        var ratio = 250 / file.width;
        var imgHeight = file.height * ratio;
        if (imgHeight > 250) {
            imgHeight = 250; //constrain
        }

        var resizeInfo = {
            srcX: 0,
            srcY: 0,
            trgX: 0,
            trgY: 0,
            srcWidth: file.width,
            srcHeight: file.height,
            trgWidth: this.options.thumbnailWidth,
            trgHeight: imgHeight
        };

        $('.dz-details img').height(imgHeight);

        return resizeInfo;
    },

    init: function() {
        submissionDropzone = this;

        this.on('success', function(file, json) {
            $("#imagefile").val(json.img);
        });

        this.on('error', function(file, errorMessage, xhr) {
            $(".dz-error-mark").click(function() {
                submissionDropzone.removeFile(file);
            });
        });

        this.on('addedfile', function(file) {

        });

        this.on('drop', function(file) {

        });
    }
};

Increase default max image size to 3 MB. This should allow uploads from most phones with their default settings.


angular.module( 'App.Game.RatingGrowl' ).service( 'Game_RatingGrowl', function( App, Api, Growls )
{
	this.show = function( game )
	{
		// Don't show when not logged in.
		if ( !App.user ) {
			return;
		}

		// Don't show if ratings are disabled for the game.
		if ( !game.ratings_enabled ) {
			return;
		}

		// Don't show when this is the developer of the game.
		if ( App.user.id == game.developer.id ) {
			return;
		}

		Api.sendRequest( '/web/discover/games/ratings/get-user-rating/' + game.id, null, { detach: true } )
			.then( function( payload )
			{
				// If there is a rating for this user already, don't show the growl.
				if ( payload.rating ) {
					return;
				}

				Growls.success( {
					sticky: true,
					templateUrl: '/app/components/game/rating-growl/rating-growl.html',
					controller: 'Game_RatingGrowlCtrl',
					resolve: {
						game: function()
						{
							return game;
						}
					}
				} );
			} );
	};
} );

angular.module( 'App.Game.RatingGrowl' ).service( 'Game_RatingGrowl', function( App, Api, Growls )
{
	this.show = function( game )
	{
		// Don't show when not logged in.
		if ( !App.user ) {
			return;
		}

		// Don't show if ratings are disabled for the game.
		if ( !game.ratings_enabled ) {
			return;
		}

		// Don't show when this is the developer of the game.
		if ( App.user.id == game.developer.id ) {
			return;
		}

		Api.sendRequest( '/web/discover/games/ratings/get-user-rating/' + game.id, null, { detach: true } )
			.then( function( payload )
			{
				// If there is a rating for this user already, don't show the growl.
				if ( payload.rating ) {
					return;
				}

				Growls.info( {
					sticky: true,
					templateUrl: '/app/components/game/rating-growl/rating-growl.html',
					controller: 'Game_RatingGrowlCtrl',
					resolve: {
						game: function()
						{
							return game;
						}
					}
				} );
			} );
	};
} );

Use info growl for game rating growl. It fits better with the component.


/**
 * Login page
 */
'use strict';
var $ = require('jquery');

var passwordForms = require('js/passwordForms');

var activateToggleBox = function () {
    var el = $(this);

    if (el.hasClass('toggle-box-muted')) {
        $('.toggle-box-active').removeClass('toggle-box-active').addClass('toggle-box-muted');
        el.removeClass('toggle-box-muted').addClass('toggle-box-active');
    }
};

$(document).ready(function() {
    new passwordForms.SignUp('#signUpScope');
});

$('.toggle-box').on('click', activateToggleBox);
$('.toggle-box').on('focus', '*', function() {
    activateToggleBox.apply( $(this).parents('.toggle-box') );
});

/**
 * Login page
 */
'use strict';
var $ = require('jquery');

var passwordForms = require('js/passwordForms');

var activateToggleBox = function () {
    var el = $(this);

    if (el.hasClass('toggle-box-muted')) {
        $('.toggle-box-active').removeClass('toggle-box-active').addClass('toggle-box-muted');
        el.removeClass('toggle-box-muted').addClass('toggle-box-active');
    }
};

$(document).ready(function() {
    var campaign = window.contextVars.campaign;
    new passwordForms.SignUp('#signUpScope', campaign);
});

$('.toggle-box').on('click', activateToggleBox);
$('.toggle-box').on('focus', '*', function() {
    activateToggleBox.apply( $(this).parents('.toggle-box') );
});

Put back accidentally removed `campaign`.


// Delete these three lines once you have your environment variables set
// The environment-configured 'keen' variable is set in layout.erb

Keen.ready(function(){

  // ----------------------------------------
  // Pageviews Area Chart
  // ----------------------------------------
  var repos_timeline = new Keen.Query("maximum", {
    eventCollection: "repository_snapshots",
    targetProperty: "stargazers_count",
    interval: "daily",
    groupBy: "full_name",
    timeframe: "this_210_hours"
  });

  keenClient.draw(repos_timeline, document.getElementById("chart-01"), {
    chartType: "areachart",
    title: false,
    height: 250,
    width: "auto",
    chartOptions: {
      chartArea: {
        height: "85%",
        left: "5%",
        top: "5%",
        width: "80%"
      },
      isStacked: false
    }
  });

  var repos_now = new Keen.Query("maximum", {
    eventCollection: "repository_snapshots",
    targetProperty: "stargazers_count",
    groupBy: "full_name",
    timeframe: "this_1_hour"
  });

  keenClient.draw(repos_now, document.getElementById("chart-02"), {
    chartType: "piechart",
    title: false,
    height: 250,
    width: "auto",
    chartOptions: {
      chartArea: {
        height: "85%",
        left: "5%",
        top: "5%",
        width: "100%"
      }
    }
  });

});

// Delete these three lines once you have your environment variables set
// The environment-configured 'keen' variable is set in layout.erb

Keen.ready(function(){

  // ----------------------------------------
  // Pageviews Area Chart
  // ----------------------------------------
  var repos_timeline = new Keen.Query("maximum", {
    eventCollection: "repository_snapshots",
    targetProperty: "stargazers_count",
    interval: "daily",
    groupBy: "full_name",
    timeframe: "last_210_hours"
  });

  keenClient.draw(repos_timeline, document.getElementById("chart-01"), {
    chartType: "areachart",
    title: false,
    height: 250,
    width: "auto",
    chartOptions: {
      chartArea: {
        height: "85%",
        left: "5%",
        top: "5%",
        width: "80%"
      },
      isStacked: false
    }
  });

  var repos_now = new Keen.Query("maximum", {
    eventCollection: "repository_snapshots",
    targetProperty: "stargazers_count",
    groupBy: "full_name",
    timeframe: "last_1_hour"
  });

  keenClient.draw(repos_now, document.getElementById("chart-02"), {
    chartType: "piechart",
    title: false,
    height: 250,
    width: "auto",
    chartOptions: {
      chartArea: {
        height: "85%",
        left: "5%",
        top: "5%",
        width: "100%"
      }
    }
  });

});

Use last hour to avoid empty interval


(function() {
  var selector = "run_link";
  var waiting_id = null;
  var counting_id = null;
  var timer_dom = 'counter';
  var wait_dom = 'please_wait';
  var wait_secs = 20;
  var counter = 0;

  var hideWait = function() {
    waiting_id = null;
    clearInterval(counting_id);
    counting_id = null;
    $(timer_dom).update('completed');
    $(wait_dom).hide();
  }

  var update_counter = function() {
    $(timer_dom).update(counter);
    counter = counter -1;
  }
  var showWait = function() {
    if (waiting_id || counting_id) {
      return;
    }
    waiting_id = setTimeout(hideWait,wait_secs * 1000);
    $(timer_dom).update(wait_secs);
    counting_id = setInterval(update_counter,1000);
    counter=wait_secs;
    $(wait_dom).show();
  };

  document.observe("dom:loaded", function() {
    $$("." + selector).each(function(item) {
      item.observe("click", showWait);
    });
  });
}());

var showWait = function() {
  var selector = "run_link";
  var waiting_id = null;
  var counting_id = null;
  var timer_dom = 'counter';
  var wait_dom = 'please_wait';
  var wait_secs = 20;
  var counter = 0;

  var hideWait = function() {
    waiting_id = null;
    clearInterval(counting_id);
    counting_id = null;
    $(timer_dom).update('completed');
    $(wait_dom).hide();
  }

  var update_counter = function() {
    $(timer_dom).update(counter);
    counter = counter -1;
  }
  if (waiting_id || counting_id) {
    return;
  }
  waiting_id = setTimeout(hideWait,wait_secs * 1000);
  $(timer_dom).update(wait_secs);
  counting_id = setInterval(update_counter,1000);
  counter=wait_secs;
  $(wait_dom).show();
};

document.observe("dom:loaded", function() {
  $$(".run_link").each(function(item) {
    item.observe("click", showWait);
  });
});

Make showWait() available to other scripts.


define(["backbone", "utils"], function(B, $u) {
    return B.View.extend({
        initialize: function(options) {
            if (options.url)
                this.template = $u.templateWithUrl(options.url);
            else if (options.template)
                this.template = function(_, cb) {
                    cb(options.template());
                };

            if (!this.template)
                throw new Error("You must initialize a modal view with a URL or template.");

            this.showingClass = options.showingClass || "showing";
            this.container = options.container || "#modal";
            this.shouldShow = false;
        },

        el: "#modal-contents",

        getContainer: function() {
            return this.$el.closest(this.container);
        },

        getContext: function() {
            return {model: this.model,
                    collection: this.collection};
        },

        _update: function() {
            this.getContainer()
                .toggleClass(this.showingClass, this.shouldShow)
                .toggle(this.shouldShow);

            if (this.shouldShow && this.wasRendered)
                this.wasRendered();
        },

        render: function() {
            if (this.shouldShow) {
                var self = this;
                this.template(
                    this.getContext(),
                    function(html) {
                        self.$el.html(html);
                        self._update();
                    });
            }

            return this;
        },

        show: function() {
            this.shouldShow = true;
            this.render();
        },

        hide: function() {
            this.shouldShow = false;
            this._update();
        }
    });
});

define(["backbone", "jquery", "utils"], function(B, $, $u) {
    return B.View.extend({
        initialize: function(options) {
            if (options.url)
                this.template = $u.templateWithUrl(options.url);
            else if (options.template)
                this.template = function(_, cb) {
                    cb(options.template());
                };

            if (!this.template)
                throw new Error("You must initialize a modal view with a URL or template.");

            this.showingClass = options.showingClass || "showing";
            this.container = options.container || "#modal";
            this.shouldShow = false;
        },

        el: "#modal-contents",

        getContainer: function() {
            return this.$el.closest(this.container);
        },

        getContext: function() {
            return {model: this.model,
                    collection: this.collection};
        },

        _update: function() {
            this.getContainer()
                .toggleClass(this.showingClass, this.shouldShow)
                .toggle(this.shouldShow);

            $(document.body).toggleClass("modal", this.shouldShow);

            if (this.shouldShow && this.wasRendered)
                this.wasRendered();
        },

        render: function() {
            if (this.shouldShow) {
                var self = this;
                this.template(
                    this.getContext(),
                    function(html) {
                        self.$el.html(html);
                        self._update();
                    });
            }

            return this;
        },

        show: function() {
            this.shouldShow = true;
            this.render();
        },

        hide: function() {
            this.shouldShow = false;
            this._update();
        }
    });
});

Add modal class to body when a modal view is shown


import { expect } from 'chai';
import { addTodo } from './common.js';

describe('App', function() {
  beforeEach(function() {
    addTodo('buy cheddar');
    addTodo('buy chorizo');
    addTodo('buy bacon');
  });

  describe('todos', function() {
    it('should not be marked as completed after being added', function() {
      // We can use getAttribute without map after
      // https://github.com/webdriverio/wdio-sync/issues/43 is fixed.
      const states = $$('.todo .toggle').map(c => c.getAttribute('checked'));

      expect(states).to.have.length(3);

      expect(states.every(state => state === 'true')).to.be.false;
    });

    it('should be marked as completed when checking them', function() {
      browser.element('.todo=buy chorizo').click('.toggle');

      expect(browser.element('.todo=buy chorizo').element('.toggle')
        .getAttribute('checked')).to.equal('true');
    });
  });
});

import { expect } from 'chai';
import { addTodo } from './common.js';

describe('App', function() {
  beforeEach(function() {
    addTodo('buy cheddar');
    addTodo('buy chorizo');
    addTodo('buy bacon');
  });

  describe('todos', function() {
    it('should not be marked as completed after being added', function() {
      // We can use getAttribute without map after
      // https://github.com/webdriverio/wdio-sync/issues/43 is fixed.
      const states = $$('.todo .toggle').map(c => c.getAttribute('checked'));

      expect(states).to.have.length(3);

      expect(states.every(state => state === 'true')).to.be.false;
    });

    it('should be marked as completed when checking them', function() {
      $$('.todo .toggle').forEach(toggle => toggle.click());

      const states = browser.elements('input[type=checkbox]').getAttribute('checked');

      expect(states).to.have.length(3);

      expect(states.every(state => state === 'true')).to.be.true;
    });
  });
});

Check all the todos and not just one


// This is the default, render wrapper. If you're looking to provide your
// own, it might look something like this:
// ```
// /* In React */
// export default (component) => <Wrapper>{component}</Wrapper>
// ```
// ```
// /* In a plain-js project */
// export default (html) => '<div>' + html + '</div>';
//
import React from 'react'; // eslint-disable-line import/no-extraneous-dependencies

class ErrorBoundary extends React.Component {
  constructor(props) {
    super(props);
    this.state = {};
  }

  static getDerivedStateFromError(error) {
    return { error };
  }

  componentDidCatch(error) {
    const { component, variant } = this.props;
    console.error(`Caught error while rendering component "${component}", variant "${variant}"`);
    console.error(error);
  }

  render() {
    const { error } = this.state;
    const { component, variant, children } = this.props;
    if (error) {
      return React.createElement(
        'pre',
        null,
        `Error when rendering component=${component}, variant${variant}\n\n${error.stack}`,
      );
    }

    return children;
  }
}
export default (result, { component, variant }) =>
  React.createElement(ErrorBoundary, { component, variant }, result);

// This is the default, render wrapper. If you're looking to provide your
// own, it might look something like this:
// ```
// /* In React */
// export default (component) => <Wrapper>{component}</Wrapper>
// ```
// ```
// /* In a plain-js project */
// export default (html) => '<div>' + html + '</div>';
//
import React from 'react'; // eslint-disable-line import/no-extraneous-dependencies

class ErrorBoundary extends React.Component {
  constructor(props) {
    super(props);
    this.state = {};
  }

  static getDerivedStateFromError(error) {
    return { error };
  }

  componentDidCatch(error) {
    const { component, variant } = this.props;
    console.error(
      `Caught error while rendering component "${component}", variant "${variant}"`,
    );
    console.error(error);
  }

  render() {
    const { error } = this.state;
    const { component, variant, children } = this.props;
    if (error) {
      return React.createElement(
        'pre',
        {
          style: {
            whiteSpace: 'pre-wrap',
            wordBreak: 'break-all',
            fontSize: 'small',
          },
        },
        `Error when rendering component=${component}, variant${variant}\n\n${error.stack}`,
      );
    }

    return children;
  }
}
export default (result, { component, variant }) =>
  React.createElement(ErrorBoundary, { component, variant }, result);

Improve styling of error boundary <pre> output


'use strict';

/**
 * @module code-project/handler/select-messaging-platform
 */

/**
 * Stores the messaging platform information
 * @param {Request} request - Hapi Request
 * @param {Reply} reply - Hapi Reply
 * @returns {Null} responds with a redirect
 */
function selectMessagingPlatform (request, reply) {
    const {prefix} = request.route.realm.modifiers.route;

    request
        .yar
        .set({
            'slack-team-access-token': request.payload.accessToken,
            'slack-team-course-channel-names': request.payload.courseChannelNames,
            'slack-team-team-channel-names': request.payload.teamChannelNames
        });

    reply().redirect(`${prefix}/recipe/code-project/choose-assessment-system`);
}

module.exports = selectMessagingPlatform;

'use strict';

/**
 * @module code-project/handler/select-messaging-platform
 */

/**
 * Stores the messaging platform information
 * @param {Request} request - Hapi Request
 * @param {Reply} reply - Hapi Reply
 * @returns {Null} responds with a redirect
 */
function selectMessagingPlatform (request, reply) {
    const {prefix} = request.route.realm.modifiers.route;

    request
        .yar
        .set({
            'slack-project-access-token': request.payload.accessToken,
            'slack-project-course-channel-names': request.payload.courseChannelNames,
            'slack-project-team-channel-names': request.payload.teamChannelNames,
            'slack-project-use-slack': request.payload.useSlack
        });

    reply().redirect(`${prefix}/recipe/code-project/choose-assessment-system`);
}

module.exports = selectMessagingPlatform;

Rename cookie keys to be consistent with naming


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

'use strict';

var exec = require('cordova/exec'),
    MediaPlugin = require('cordova-plugin-toast.MediaPlugin');

function MediaPluginHLS () {
    MediaPlugin.apply(this, arguments);
    this.name = 'MediaPluginHLS';
}

MediaPluginHLS.prototype = new MediaPlugin();

MediaPluginHLS.prototype.onAttachToMedia = function (media) {
    var me = this;

    media.registerHook('beforeopen', function (media, args) {
        exec(null, null, 'toast.Media', 'setStreamingProperty', {
            'mediaOption': me.options.Options
        });
    });
};

module.exports = MediaPluginHLS;

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

'use strict';

var exec = require('cordova/exec'),
    MediaPlugin = require('cordova-plugin-toast.MediaPlugin');

function MediaPluginHLS () {
    MediaPlugin.apply(this, arguments);
    this.name = 'MediaPluginHLS';
}

MediaPluginHLS.prototype = new MediaPlugin();

MediaPluginHLS.prototype.onAttachToMedia = function (media) {
    var me = this;

    media.registerHook('beforeopen', function (media, args) {
        exec(null, null, 'toast.Media', 'setStreamingProperty', {
            'mediaOption': me.options.OPTIONS
        });
    });
};

module.exports = MediaPluginHLS;

Change the property name of mediaoption


import MJMLColumnElement from './decorators/MJMLColumnElement'
import React, { Component } from 'react'
import _ from 'lodash'

/**
 * Displays a customizable divider
 */
@MJMLColumnElement({
  tagName: 'mj-divider',
  attributes: {
    'border-color': '#000000',
    'border-style': 'solid',
    'border-width': '4px',
    'horizontal-spacing': '10px',
    'padding-bottom': '10px',
    'padding-left': '25px',
    'padding-right': '25px',
    'padding-top': '10px',
    'vertical-spacing': '30px',
    'width': '150px'
  }
})
class Divider extends Component {

  static baseStyles = {
    p: {
      fontSize: '1px'
    }
  };

  getStyles() {
    const { mjAttribute } = this.props

    return _.merge({}, this.constructor.baseStyles, {
      p: {
        borderTop: `${mjAttribute('border-width')} ${mjAttribute('border-style')} ${mjAttribute('border-color')}`,
        width: "100%"
      }
    })
  }

  render() {
    this.styles = this.getStyles()

    return <p className="outlook-divider-fix" style={this.styles.p} />
  }

}

export default Divider

import MJMLColumnElement from './decorators/MJMLColumnElement'
import React, { Component } from 'react'
import _ from 'lodash'

/**
 * Displays a customizable divider
 */
@MJMLColumnElement({
  tagName: 'mj-divider',
  attributes: {
    'border-color': '#000000',
    'border-style': 'solid',
    'border-width': '4px',
    'horizontal-spacing': '10px',
    'padding-bottom': '10px',
    'padding-left': '25px',
    'padding-right': '25px',
    'padding-top': '10px',
    'vertical-spacing': '30px',
    'width': '150px'
  }
})
class Divider extends Component {

  static baseStyles = {
    p: {
      fontSize: '1px',
      margin: '0'
    }
  };

  getStyles() {
    const { mjAttribute } = this.props

    return _.merge({}, this.constructor.baseStyles, {
      p: {
        borderTop: `${mjAttribute('border-width')} ${mjAttribute('border-style')} ${mjAttribute('border-color')}`,
        width: "100%"
      }
    })
  }

  render() {
    this.styles = this.getStyles()

    return <p className="outlook-divider-fix" style={this.styles.p} />
  }

}

export default Divider

Set margin 0 for divider


'use strict';

// Development specific configuration
// ==================================
module.exports = {
  // MongoDB connection options
  mongo: {
    uri: process.env.MONGOLAB_URI || 'mongodb://localhost:27017/easydownload-dev'
  },

  itemCron: '*/5 * * * * *',
  thingCron: '*/10 * * * * *',
  esCron: '*/20 * * * * *',

  elasticSearch: {
    host: 'localhost:9200',
    index: 'mongoindex',
    type: 'thing',
    loglevel: 'warning',
    scanLimit : 30,

    "ikAugAnalyzer": {
      "analysis": {
        "analyzer": {
          "ik_aug": {
            "type": "custom"
            , "tokenizer" : "ik"
            , "use_smart" : true
          }
        }
      }
    },

    mapping: {
      "thing": {
        "_all": {
          "indexAnalyzer": "ik_aug",
          "searchAnalyzer": "ik_aug",
          "term_vector": "no",
          "store": "false"
        },
        "properties": {
          "title": {
            "type": "string",
            "store": "no",
            "term_vector": "with_positions_offsets",
            "indexAnalyzer": "ik_aug",
            "searchAnalyzer": "ik_aug",
            "include_in_all": "true",
            "boost": 8
          }
        }
      }
    }
  },

  seedDB: true
};

'use strict';

// Development specific configuration
// ==================================
module.exports = {
  // MongoDB connection options
  mongo: {
    uri: process.env.MONGOLAB_URI || 'mongodb://localhost:27017/easydownload-dev'
  },

  itemCron: '*/5 * * * * *',
  thingCron: '*/10 * * * * *',
  esCron: '*/20 * * * * *',

  elasticSearch: {
    host: 'misscatandzuozuo.info:19200',
    index: 'mongoindex',
    type: 'thing',
    loglevel: 'warning',
    scanLimit : 30,

    "ikAugAnalyzer": {
      "analysis": {
        "analyzer": {
          "ik_aug": {
            "type": "custom"
            , "tokenizer" : "ik"
            , "use_smart" : true
          }
        }
      }
    },

    mapping: {
      "thing": {
        "_all": {
          "indexAnalyzer": "ik_aug",
          "searchAnalyzer": "ik_aug",
          "term_vector": "no",
          "store": "false"
        },
        "properties": {
          "title": {
            "type": "string",
            "store": "no",
            "term_vector": "with_positions_offsets",
            "indexAnalyzer": "ik_aug",
            "searchAnalyzer": "ik_aug",
            "include_in_all": "true",
            "boost": 8
          }
        }
      }
    }
  },

  seedDB: true
};

Add some comments in dockerfile


'use strict';

/**
 * Base Plugin class that all subsequent Plugins should be based on.
 * @class
 * @memberOf Defiant
 */
class Plugin {
  /**
   * @constructor
   * @param {Defiant.Engine} engine The app engine.
   * @returns {Defiant.Plugin} The plugin that was instantiated.
   */
  constructor(engine) {
    /**
     * @member {Defiant.Engine} Defiant.Plugin#engine The app engine.
     */
    this.engine = engine;
    /**
     * @member {String} Defiant.Plugin#id A unique string identifier for this
     *   plugin.  It defaults to the constructor name.
     */
    this.id = this.constructor.name;
    /**
     * @member {number} Defiant.Plugin#weight
     *   The weight of the plugin as seen by the app engine's
     *   [plugin registry]{@link Defiant.Engine#pluginRegistry}.
     */
    this.weight = 0;
    return this;
  }

  /**
   * All plugins will be initialized in order of their weight by
   * {@link Defiant.Engine#init}.
   * @function
   * @async
   */
  async init() {
    // Just a stub.
  }
}

module.exports = Plugin;

'use strict';

/**
 * Base Plugin class that all subsequent Plugins should be based on.
 * @class
 * @memberOf Defiant
 */
class Plugin {
  /**
   * @constructor
   * @param {Defiant.Engine} engine
   *   The app engine.
   * @returns {Defiant.Plugin}
   *   The plugin that was instantiated.
   */
  constructor(engine) {
    /**
     * @member {Defiant.Engine} Defiant.Plugin#engine
     *   The app engine.
     */
    this.engine = engine;
    /**
     * @member {String} Defiant.Plugin#id
     *   A unique string identifier for this plugin.  It defaults to the
     *   constructor name.
     */
    this.id = this.constructor.name;
    /**
     * @member {number} Defiant.Plugin#weight
     *   The weight of the plugin as seen by the app engine's
     *   [plugin registry]{@link Defiant.Engine#pluginRegistry}.
     */
    this.weight = 0;
    return this;
  }

  /**
   * All plugins will be initialized in order of their weight by
   * {@link Defiant.Engine#init}.
   * @function
   * @async
   */
  async init() {
    // Just a stub.
  }
}

module.exports = Plugin;

Standardize Jsdoc style in Defiant.Plugin


import { differenceInCalendarDays, startOfDay, endOfDay, min, max } from 'proton-shared/lib/date-fns-utc';

export const splitDayEventsInInterval = ({ events = [], min: minDate, max: maxDate }) => {
    return events.reduce((acc, { start, end, isAllDay, isAllPartDay }, i) => {
        const startDate = startOfDay(max(start, minDate));
        const endDate = endOfDay(min(end, maxDate));

        if (startDate >= endDate) {
            return acc;
        }

        // For part day events, lock it into one day even if it happens to span multiple days, but not considered an all day part event.
        const calendarDaysDifference = isAllDay || isAllPartDay ? differenceInCalendarDays(endDate, startDate) : 0;
        const startIndex = differenceInCalendarDays(startDate, minDate);

        acc.push({
            idx: i,
            start: startIndex,
            end: startIndex + calendarDaysDifference + 1
        });

        return acc;
    }, []);
};

import { differenceInCalendarDays, startOfDay, endOfDay, min, max } from 'proton-shared/lib/date-fns-utc';

export const splitDayEventsInInterval = ({ events = [], min: minDate, max: maxDate }) => {
    return events.reduce((acc, { start, end, isAllDay, isAllPartDay }, i) => {
        const startDate = startOfDay(max(start, minDate));
        const endDate = endOfDay(min(end, maxDate));

        const isDisplayedMultipleDays = isAllDay || isAllPartDay;

        if (startDate >= endDate) {
            return acc;
        }

        // Part day events that are not displayed as all day events that start before the date range, are already displayed in a previous row.
        if (!isDisplayedMultipleDays && startDate > start) {
            return acc;
        }

        const calendarDaysDifference = isDisplayedMultipleDays ? differenceInCalendarDays(endDate, startDate) : 0;
        const startIndex = differenceInCalendarDays(startDate, minDate);

        acc.push({
            idx: i,
            start: startIndex,
            end: startIndex + calendarDaysDifference + 1
        });

        return acc;
    }, []);
};

Fix part day events spanning two days being displayed on two rows


const FS = require('fs');
const Path = require('path');

// auto load all other js files in folder
const DIR = Path.join(__dirname, '/');

FS.readdirSync(DIR).forEach(function (file) { // eslint-disable-line no-sync
  var name = file.replace('.js', '');
  var path = Path.join('./', name);

  if (file.match(/.+\.js/g) !== null && file !== 'index.js') {
    exports[name] = require(path); // eslint-disable-line global-require
  }
});

const FS = require('fs');
const Path = require('path');

// auto load all other js files in folder
const DIR = Path.join(__dirname, '/');

FS.readdirSync(DIR).forEach(function (file) { // eslint-disable-line no-sync
  var name = file.replace('.js', '');
  var path = Path.join(__dirname, name);

  if (file.match(/.+\.js/g) !== null && file !== 'index.js') {
    exports[name] = require(path); // eslint-disable-line global-require
  }
});

Make correct reference to engines in the `engines` directory


import Marked from 'marked'

export const orderedNotes = state => {
  return state.notes.sort((a, b) => {
    var aOrder = state.notesOrder[a['.key']]
    var bOrder = state.notesOrder[b['.key']]
    if (aOrder === undefined || aOrder === null) return -1
    if (aOrder === bOrder) return 0
    if (aOrder < bOrder) return -1
    return 1
  })
}

export const markdown = state => text => {
  return Marked(text)
}

let mdRenderer = new Marked.Renderer()
mdRenderer.image = function (href, title, text) {
  return '

'
}
mdRenderer.link = function (href, title, text) {
  return '' + text + ''
}
Marked.setOptions({
  renderer: mdRenderer
})

import Marked from 'marked'

export const orderedNotes = state => {
  return state.notes.slice(0).sort((a, b) => {
    const aOrder = state.notesOrder[a['.key']]
    const bOrder = state.notesOrder[b['.key']]
    if (aOrder === undefined || aOrder === null) return -1
    if (aOrder === bOrder) return 0
    if (aOrder < bOrder) return -1
    return 1
  })
}

export const markdown = state => text => {
  return Marked(text)
}

let mdRenderer = new Marked.Renderer()
mdRenderer.image = function (href, title, text) {
  return '

'
}
mdRenderer.link = function (href, title, text) {
  return '' + text + ''
}
Marked.setOptions({
  renderer: mdRenderer
})

Return copy of notes sorted


/*
  Bootstrap components and api on DOM Load
*/

/* global Turbolinks document */
import Relay from 'react-relay';
import {
  mountComponents,
  unmountComponents,
} from './utils/componentMounter';

import Authentication from './helpers/authentication.es6';

const auth = new Authentication();
const setupNetworkLayer = () => {
  Relay.injectNetworkLayer(
    new Relay.DefaultNetworkLayer('/graphql', {
      credentials: 'same-origin',
      fetchTimeout: 30000,
      retryDelays: [5000],
      headers: {
        'X-CSRF-Token': auth.csrfToken(),
      },
    })
  );
};

const renderComponents = () => {
  document.addEventListener('DOMContentLoaded', () => {
    if (typeof Turbolinks !== 'undefined' && typeof Turbolinks.controller !== 'undefined') {
      setupNetworkLayer();
      document.addEventListener('turbolinks:render', unmountComponents);
      document.addEventListener('turbolinks:load', mountComponents);
    } else {
      setupNetworkLayer();
      mountComponents();
    }
  });
};

export default renderComponents;

/*
  Bootstrap components and api on DOM Load
*/

/* global Turbolinks document */
import Relay from 'react-relay';
import {
  mountComponents,
  unmountComponents,
} from './utils/componentMounter';

import Authentication from './helpers/authentication.es6';

const setupNetworkLayer = () => {
  const auth = new Authentication();
  Relay.injectNetworkLayer(
    new Relay.DefaultNetworkLayer('/graphql', {
      credentials: 'same-origin',
      fetchTimeout: 30000,
      retryDelays: [5000],
      headers: {
        'X-CSRF-Token': auth.csrfToken(),
      },
    })
  );
};

const renderComponents = () => {
  document.addEventListener('DOMContentLoaded', () => {
    if (typeof Turbolinks !== 'undefined' && typeof Turbolinks.controller !== 'undefined') {
      setupNetworkLayer();
      document.addEventListener('turbolinks:render', unmountComponents);
      document.addEventListener('turbolinks:load', mountComponents);
    } else {
      setupNetworkLayer();
      mountComponents();
    }
  });
};

export default renderComponents;

Move authentication block inside network layer setup


$(function() {
    var speakersBH = new Bloodhound({
        datumTokenizer: Bloodhound.tokenizers.obj.whitespace('name'),
        queryTokenizer: Bloodhound.tokenizers.whitespace,
        remote: {
            url: '/events/speakers/suggest?q=%QUERY',
            filter: function(response) {
                return response.speakers;
            },
        }
    });

    speakersBH.initialize();

    $('.js-speakers-typeahead').typeahead(null, {
        name: 'speakers',
        displayKey: 'name',
        source: speakersBH.ttAdapter()
    });
});

$(function() {
    var speakersBH = new Bloodhound({
        datumTokenizer: Bloodhound.tokenizers.obj.whitespace('name'),
        queryTokenizer: Bloodhound.tokenizers.whitespace,
        remote: {
            url: '/events/speakers/suggest?q=%QUERY',
            filter: function(response) {
                return response.speakers;
            },
        }
    });
    speakersBH.initialize();

    var speakerTemplate = _.template('<a href="#" data-id="<%= id %>" class="js-speaker-suggestion list-group-item">+<%= name %>');
    var $suggestedSpeakers = $('.js-suggested-speakers-list');
    var cachedSuggestions = {};
    $('.js-speakers-typeahead').on('keyup', function(ev) {
        var query = ev.target.value;
        speakersBH.get(query, function(suggestions) {
            $suggestedSpeakers.html('');
            _.each(suggestions, function(suggestion) {
                cachedSuggestions[suggestion.id] = suggestion;
                $suggestedSpeakers.append(speakerTemplate(suggestion));
            });
        });
    });
    $suggestedSpeakers.on('click', '.js-speaker-suggestion', function(ev) {
        ev.preventDefault();
        var $suggestion = $(ev.target);
        $suggestion.addClass('disabled');
        var speakerID = $suggestion.data('id');
        var speaker = cachedSuggestions[speakerID];
        document.addSpeaker(speaker);
    });
    document.listenForRemoval(function(id) {
        $suggestedSpeakers.find("[data-id='"+id+"']").removeClass('disabled');
    });
});

Remove the typeahead and only use bloodhound


'use strict';

const fs = require('fs');

function read(filename) {
  return fs.readFileSync(filename, {'encoding': 'utf8'});
}

exports.cname = {
  'build': function(test) {
    const actual = read('tmp/CNAME');
    const expected = read('test/expected/CNAME');

    test.equal(actual, expected, 'should build the CNAME file');

    test.done();
  },
};

'use strict';

const fs = require('fs');

function read(filename) {
  return fs.readFileSync(filename, {'encoding': 'utf8'});
}

exports.cname = {
  'build': (test) => {
    const actual = read('tmp/CNAME');
    const expected = read('test/expected/CNAME');

    test.equal(actual, expected, 'should build the CNAME file');

    test.done();
  },
};

Use arrow function in test


var merge = require('./merge');
var isPlainObject = require('./isPlainObject');

var hash = JSON.stringify;

function applyTransforms(transforms, declarations, transformCache, result) {
    var property;

    for (property in declarations) {
        var value = declarations[property];

        if (property in transforms) {
            var transform = transforms[property];
            var isFunction = typeof transform === 'function';
            var key = property + (isFunction ? ':' + hash(value) : '');

            if (!(key in transformCache)) {
                transformCache[key] = merge(
                    Object.create(null),
                    isFunction ? transform(value) : transform
                );
                applyTransforms(transforms, transformCache[key], transformCache, transformCache[key]);
            }

            if (declarations !== transformCache[key]) {
                merge(result, transformCache[key]);
            }
        }
        else if (isPlainObject(value)) {
            if (!isPlainObject(result[property])) {
                result[property] = Object.create(null);
            }
            applyTransforms(transforms, value, transformCache, result[property]);
        }
        else {
            result[property] = value;
        }
    }

    return result;
}

module.exports = function (transforms, declarations, transformCache) {
    var result = Object.create(null);
    applyTransforms(transforms, declarations, transformCache, result);
    return result;
};

var merge = require('./merge');
var isPlainObject = require('./isPlainObject');

var hash = JSON.stringify;

function applyTransforms(transforms, declarations, transformCache, result) {
    var property;

    for (property in declarations) {
        var value = declarations[property];

        if (property in transforms) {
            var transform = transforms[property];
            var isFunction = typeof transform === 'function';
            var key = property + (isFunction ? ':' + hash(value) : '');

            if (!(key in transformCache)) {
                transformCache[key] = merge(
                    Object.create(null),
                    isFunction ? transform(value) : transform
                );
                applyTransforms(transforms, transformCache[key], transformCache, transformCache[key]);
            }

            merge(result, transformCache[key]);
        }
        else if (isPlainObject(value)) {
            if (!isPlainObject(result[property])) {
                result[property] = Object.create(null);
            }
            applyTransforms(transforms, value, transformCache, result[property]);
        }
        else {
            result[property] = value;
        }
    }

    return result;
}

module.exports = function (transforms, declarations, transformCache) {
    var result = Object.create(null);
    applyTransforms(transforms, declarations, transformCache, result);
    return result;
};

Remove a now useless test


// Define DrupalAjaxRequest
var DrupalAjaxRequest = (function () {
  var fetch = function(feed_id, callback) {
    Zepto.ajax(
        {
          url: Kiosk.contentUrl(feed_id),
          dataType: 'jsonp',
          type: 'GET',
          cache: false,
          success: function (result) {
            callback(result);
          }
        }
    );
  }

  return {
    fetch : fetch
  }
})();

// Define DrupalAjaxRequest
var DrupalAjaxRequest = (function () {
  var fetchNode = function(node_id, callback) {
    Zepto.ajax(
      {
        url: Kiosk.contentUrl('node'),
        dataType: 'jsonp',
        data: {nid: node_id},
        type: 'GET',
        cache: false,
        success: function (result) {
          callback(result);
        }
      }
    );
  }

  var fetchCollections = function(callback) {
    Zepto.ajax(
      {
        url: Kiosk.contentUrl('collections'),
        dataType: 'jsonp',
        type: 'GET',
        cache: false,
        success: function (result) {
          callback(result);
        }
      }
    );
  }

  return {
    fetchNode : fetchNode,
    fetchCollections: fetchCollections
  }
})();

Add a method for fetching a node, and collections list (artifacts).


import { expect } from 'chai';
import intercept from 'intercept-stdout';
import { IO, resolveValue, triggerWarnings } from '../src/input';
import { String as Text } from '../src/types';
import { compileES6 } from '../src/compiler';

describe('input.js', () => {
    describe('Value parsing', () => {
        it('should get a simple string', () => {
            const value = resolveValue('test', Text, null, true);
            expect(value).to.equals('test');
        });

        it('should return null for a required string', () => {
            const value = resolveValue('', Text, null, true);
            expect(value).to.be.null;
        });

        it('should accept the default value of a string', () => {
            const value = resolveValue('', Text, 'alaska', false);
            expect(value).to.equals('alaska');
        });
    });

    describe('Warnings', () => {
        it('should trigger a warning when has named parameter language', () => {
            let result = '';
            const stopReading = intercept(text => result += text);
            const io = IO();
            triggerWarnings(io, { language: {} })
                .then(() => {
                    stopReading();
                    expect(result).to.match(/Warning: don't use context.params.language/);
                });
        });
    });
});

import { expect } from 'chai';
import intercept from 'intercept-stdout';
import { IO, resolveValue } from '../src/input';
import { String as Text } from '../src/types';
import { compileES6 } from '../src/compiler';

describe('input.js', () => {
    describe('Value parsing', () => {
        it('should get a simple string', () => {
            const value = resolveValue('test', Text, null, true);
            expect(value).to.equals('test');
        });

        it('should return null for a required string', () => {
            const value = resolveValue('', Text, null, true);
            expect(value).to.be.null;
        });

        it('should accept the default value of a string', () => {
            const value = resolveValue('', Text, 'alaska', false);
            expect(value).to.equals('alaska');
        });
    });
});

Remove warning section from input


module.exports = function (config) {
    config.set({
        browsers: [ 'PhantomJS' ],
        plugins: [
            'karma-mocha',
            'karma-coverage',
            'karma-coveralls',
            'karma-phantomjs-launcher',
            'karma-mocha-reporter'
        ],
        frameworks: [ 'mocha' ],
        reporters: [ 'mocha', 'coverage', 'coveralls' ],
        client: {
            mocha: {
                ui: 'bdd'
            }
        },
        basePath: '../',
        files: [
            'node_modules/angular/angular.js',
            'node_modules/angular-mocks/angular-mocks.js',
            'node_modules/sinon/pkg/sinon-1.17.4.js',
            'node_modules/unexpected/unexpected.js',
            'node_modules/unexpected-sinon/lib/unexpected-sinon.js',
            'src/*.js',
            'src/**/*.js',
            'test/bootstrap.js',
            'test/**/*.spec.js'
        ],
        preprocessors: {
            'src/**/*.js': [ 'coverage' ]
        },
        coverageReporter: {
            type : 'lcov',
            dir : 'coverage/'
        },
        singleRun: true
    });
};

module.exports = function (config) {
    config.set({
        browsers: [ 'PhantomJS' ],
        plugins: [
            'karma-mocha',
            'karma-coverage',
            'karma-coveralls',
            'karma-phantomjs-launcher',
            'karma-mocha-reporter'
        ],
        frameworks: [ 'mocha' ],
        reporters: [ 'mocha', 'coverage', 'coveralls' ],
        client: {
            mocha: {
                ui: 'bdd'
            }
        },
        basePath: '../',
        files: [
            'node_modules/angular/angular.js',
            'node_modules/angular-mocks/angular-mocks.js',
            'node_modules/sinon/pkg/sinon.js',
            'node_modules/unexpected/unexpected.js',
            'node_modules/unexpected-sinon/lib/unexpected-sinon.js',
            'src/*.js',
            'src/**/*.js',
            'test/bootstrap.js',
            'test/**/*.spec.js'
        ],
        preprocessors: {
            'src/**/*.js': [ 'coverage' ]
        },
        coverageReporter: {
            type : 'lcov',
            dir : 'coverage/'
        },
        singleRun: true
    });
};

Fix path to packaged sinon test dependency


import React, {Component, PropTypes} from 'react';
import {connect} from 'react-redux';
import {bindActionCreators} from 'redux';
import {fetchHome} from '../actions/homeActions';

class Home extends Component {
 // Ajax call to get API data before the component mounts
 componentWillMount() {
   this.props.fetchHome("home");
 }

  render() {
    const {name, slogan, image_url} = this.props.home;
    return(

{name}

{slogan}

    );
  }
}

// Map State To Props
function mapStateToProps({home}) {
  return {home};
}

// Bind actions and dispatch them
function mapDispatchToProps(dispatch) {
  return bindActionCreators({fetchHome}, dispatch);
}

//  Home Props Validation
Home.propTypes = {
  fetchHome: PropTypes.func,
  home: PropTypes.object
};

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

import React, {Component, PropTypes} from 'react';
import {connect} from 'react-redux';
import {bindActionCreators} from 'redux';
import {fetchHome} from '../actions/homeActions';

class Home extends Component {
 // Ajax call to get API data before the component mounts
 componentWillMount() {
   this.props.fetchHome("home");
 }

  render() {
    // Destructure the home object
    const {name, slogan, image_url} = this.props.home;
    return(

{name}

{slogan}

    );
  }
}

// Map State To Props
function mapStateToProps({home}) {
  return {home};
}

// Bind actions and dispatch them
function mapDispatchToProps(dispatch) {
  return bindActionCreators({fetchHome}, dispatch);
}

//  Home Props Validation
Home.propTypes = {
  fetchHome: PropTypes.func,
  home: PropTypes.object
};

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

Add destructuring comment to home container


Meteor.startup(() => {
  if (process.env.CARD_OPENED_WEBHOOK_ENABLED) {
    Meteor.settings.public.CARD_OPENED_WEBHOOK_ENABLED = true;
  }
});

Meteor.startup(() => {
  if (process.env.CARD_OPENED_WEBHOOK_ENABLED === 'true') {
    Meteor.settings.public.CARD_OPENED_WEBHOOK_ENABLED = true;
  } else {
    Meteor.settings.public.CARD_OPENED_WEBHOOK_ENABLED = false;
  }
});

Fix Card Opened Webhook can not be disabled.


import Vue from 'vue'
import {
  default as VueMdl,
  MdlCheckbox,
  MdlBadge,
  components,
  directives
} from '../../../src/vue-mdl'

describe('Register', () => {
  it('exports single components', () => {
    MdlCheckbox.should.exist.and.be.an.Object
    MdlCheckbox.should.have.property('template')
  })

  it('exports single directives', () => {
    MdlBadge.should.exist.and.be.an.Object
    MdlBadge.should.have.property('bind')
  })

  it('exports all components', () => {
    components.should.exist.and.be.an.Object
    components.should.have.property('MdlCheckbox')
    components.MdlCheckbox.should.eql(MdlCheckbox)
  })

  it('exports all directives', () => {
    directives.should.exist.and.be.an.Object
    directives.should.have.property('MdlBadge')
    directives.MdlBadge.should.eql(MdlBadge)
  })

  it('exports a Vue plugin', () => {
    VueMdl.should.have.property('install')
    ;(() => Vue.use(VueMdl)).should.not.throw()
  })

  it('has registered components', () => {
    Vue.component('MdlCheckbox').should.exist
  })

  it('has registered directives', () => {
    Vue.directive('MdlBadge').should.exist
  })
})

import Vue from 'vue'
import {
  default as VueMdl,
  MdlCheckbox,
  MdlBadge,
  components,
  directives
} from '../../../src/vue-mdl'

describe('Register', () => {
  it('exports single components', () => {
    MdlCheckbox.should.exist.and.be.an.Object
    MdlCheckbox.should.have.property('render')
  })

  it('exports single directives', () => {
    MdlBadge.should.exist.and.be.an.Object
    MdlBadge.should.have.property('bind')
  })

  it('exports all components', () => {
    components.should.exist.and.be.an.Object
    components.should.have.property('MdlCheckbox')
    components.MdlCheckbox.should.eql(MdlCheckbox)
  })

  it('exports all directives', () => {
    directives.should.exist.and.be.an.Object
    directives.should.have.property('MdlBadge')
    directives.MdlBadge.should.eql(MdlBadge)
  })

  it('exports a Vue plugin', () => {
    VueMdl.should.have.property('install')
    ;(() => Vue.use(VueMdl)).should.not.throw()
  })

  it('has registered components', () => {
    Vue.component('MdlCheckbox').should.exist
  })

  it('has registered directives', () => {
    Vue.directive('MdlBadge').should.exist
  })
})

Fix for high level registration test.


module.exports = {
	package: {
		files: [
			{
				cwd: '<%= paths.source %>',
				src: [
					'**',
					'!<%= paths.js %>/source/**',
					'!includes/wpcom.php',
					'!templates/wpcom-*'
				],
				dest: '<%= paths.tmp %>',
				expand: true
			}
		]
	},
	wpcom: {
		files: [
			{
				cwd: '<%= paths.source %>',
				src: [
					'**',
					'!<%= paths.js %>/source/**',
					'!audiotheme/**',
					'!includes/back-compat.php',
					'!includes/plugins/**',
					'includes/plugins/jetpack.php',
					'!includes/vendor/class-audiotheme-themenotice.php',
					'!includes/wporg.php',
					'!languages/**'
				],
				dest: '<%= paths.wpcom %>',
				expand: true
			}
		]
	}
};

module.exports = {
	package: {
		files: [
			{
				cwd: '<%= paths.source %>',
				src: [
					'**',
					'!<%= paths.js %>/source/**',
					'!includes/wpcom.php',
					'!**/wpcom-*'
				],
				dest: '<%= paths.tmp %>',
				expand: true
			}
		]
	},
	wpcom: {
		files: [
			{
				cwd: '<%= paths.source %>',
				src: [
					'**',
					'!<%= paths.js %>/source/**',
					'!audiotheme/**',
					'!includes/back-compat.php',
					'!includes/plugins/**',
					'includes/plugins/jetpack.php',
					'!includes/vendor/class-audiotheme-themenotice.php',
					'!includes/wporg.php',
					'!languages/**'
				],
				dest: '<%= paths.wpcom %>',
				expand: true
			}
		]
	}
};

Tweak the pattern to exclude WP.com templates from self-hosted packages.


const os = require('os');
const pathModule = require('path');
const Application = require('spectron').Application;
const electron = require('electron');

const rootDir = pathModule.join(__dirname, '../..');

function getElectronLocation() {
  const electronModuleDir = pathModule.join(rootDir, 'node_modules/electron');
  const electronBinaryLocation
    = pathModule.join(electronModuleDir, getElectronBinaryLocationRelativeToElectronModuleDir());
  return electronBinaryLocation;
}

function getElectronBinaryLocationRelativeToElectronModuleDir() {
  const platform = process.env.npm_config_platform || os.platform();

  switch (platform) {
    case 'darwin':
      return 'dist/Electron.app/Contents/MacOS/Electron';
    case 'freebsd':
    case 'linux':
      return 'dist/electron';
    case 'win32':
      return 'dist/electron.exe';
    default:
      throw new Error(`Electron builds are not available on platform: ${platform}`);
  }
}

const electronLocation = getElectronLocation();

function getSpectronApp() {
  return new Application({
    path: electron,
    args: [rootDir]
  });
}

module.exports = {
  getSpectronApp: getSpectronApp
};

const pathModule = require('path');
const Application = require('spectron').Application;
const electron = require('electron');

const rootDir = pathModule.join(__dirname, '../..');

function getSpectronApp() {
  return new Application({
    path: electron,
    args: [rootDir]
  });
}

module.exports = {
  getSpectronApp: getSpectronApp
};

Delete unused code related to electron binary location detection.


'use strict';

const template = require('../../views/pages/ErrorPage.hbs');

class ErrorPage {
	constructor() {
		this.container = document.createElement('div');
		this.container.classList.add('error-page-container');

		this.render();
	}

	render() {
		this.container.innerHTML = template();
	}

	show(error) {
		this.container.classList.add('show');

		let errorMessage = this.container.querySelector('.error-message');
		errorMessage.innerHTML = error.message;

	}
}
module.exports = ErrorPage;

'use strict';

const template = require('../../views/pages/ErrorPage.hbs');

class ErrorPage {
	constructor() {
		this.container = document.createElement('div');
		this.container.classList.add('error-page-container');

		this.render();
	}

	render() {
		this.container.innerHTML = template();
	}

	show(error) {
		this.container.classList.add('show');

		let errorMessage = this.container.querySelector('.error-message');
		errorMessage.innerHTML = error.message;
	}

	hidden() {
		this.container.classList.remove('show');
	}
}
module.exports = ErrorPage;

Add hidden method to err page


'use strict';

var _       = require('lodash');
var fs      = require('fs');
var utility = require('../utility');

describe('custom markup', function() {
  before(function() {
    var testHTML = document.querySelectorAll('#custom-markup .hljs');

    this.blocks = _.map(testHTML, 'innerHTML');
  });

  it('should replace tabs', function() {
    var filename = utility.buildPath('expect', 'tabreplace.txt'),

        expected = fs.readFileSync(filename, 'utf-8'),
        actual   = this.blocks[0];

    actual.should.equal(expected);
  });

  it('should keep custom markup', function() {
    var filename = utility.buildPath('expect', 'custommarkup.txt'),

        expected = fs.readFileSync(filename, 'utf-8'),
        actual   = this.blocks[1];

    actual.should.equal(expected);
  });

  it('should keep custom markup and replace tabs', function() {
    var filename = utility.buildPath('expect', 'customtabreplace.txt'),

        expected = fs.readFileSync(filename, 'utf-8'),
        actual   = this.blocks[2];

    actual.should.equal(expected);
  });

  it('should keep the same amount of void elements (
,
, ...)', function() {
    var filename = utility.buildPath('expect', 'brInPre.txt'),

        expected = fs.readFileSync(filename, 'utf-8'),
        actual   = this.blocks[3];

    actual.should.equal(expected);
  });
});

'use strict';

var _       = require('lodash');
var fs      = require('fs');
var utility = require('../utility');

var handleFile = utility.handleExpectedFile;

describe('custom markup', function() {
  before(function() {
    var testHTML = document.querySelectorAll('#custom-markup .hljs');

    this.blocks = _.map(testHTML, 'innerHTML');
  });

  it('should replace tabs', function(done) {
    var filename = utility.buildPath('expect', 'tabreplace.txt'),
        actual   = this.blocks[0];

    fs.readFile(filename, 'utf-8', handleFile(actual, done));
  });

  it('should keep custom markup', function(done) {
    var filename = utility.buildPath('expect', 'custommarkup.txt'),
        actual   = this.blocks[1];

    fs.readFile(filename, 'utf-8', handleFile(actual, done));
  });

  it('should keep custom markup and replace tabs', function(done) {
    var filename = utility.buildPath('expect', 'customtabreplace.txt'),
        actual   = this.blocks[2];

    fs.readFile(filename, 'utf-8', handleFile(actual, done));
  });

  it('should keep the same amount of void elements (
,
, ...)', function(done) {
    var filename = utility.buildPath('expect', 'brInPre.txt'),
        actual   = this.blocks[3];

    fs.readFile(filename, 'utf-8', handleFile(actual, done));
  });
});

Change custom markup to asynchronous testing


function getRibbon(links) {

	for(var i=0;i<links.length;i++){
		var difference = links[i].ribbon[0].target.line - links[i].ribbon[0].source.line;
		if(difference==1){
			var diagonal = d3.svg.diagonal().source(function(d) {
				return {
					"x" : d.source.x,
					"y" : d.source.y
				};
			}).target(function(d) {
				return {
					"x" : d.target.x,
					"y" : d.target.y
				};
				}).projection(function(d) {
					return [ d.x, d.y ];
				});
			var path1 = diagonal(links[0]);
			var path2 = diagonal(links[1]).replace(/^M/, 'L');
			var shape = path1 + path2 + 'Z';
			return shape;
		}
	}
}

function testLineDifference(links){
	for(var i=0;i<links.length;i++){
		var difference = links[i].ribbon[0].target.line - links[i].ribbon[0].source.line;
		if(difference==1){

		}
	}
}

function getRibbon(links) {

	for(var i=0;i<links.length;i++){
		var difference = links[i].ribbon[0].target.line - links[i].ribbon[0].source.line;
		if(difference==1){
			var diagonal = d3.svg.diagonal().source(function(d) {
				return {
					"x" : d.source.x,
					"y" : d.source.y
				};
			}).target(function(d) {
				return {
					"x" : d.target.x,
					"y" : d.target.y
				};
				}).projection(function(d) {
					return [ d.x, d.y ];
				});
			var path1 = diagonal(links[0]);
			var path2 = diagonal(links[1]).replace(/^M/, 'L');
			var shape = path1 + path2 + 'Z';
			return shape;
		}
	}
}

function testLineDifference(links){
	for(var i=0;i<links.length;i++){
		var difference = links[i].ribbon[0].target.line - links[i].ribbon[0].source.line;
		if(difference==1){
			return true;
		}
	}
}

Test jetzt erfolgreich: Funktion testLineDifference existiert und scheint auch die richtig Rechenoperation durchzuführen, aber das wird jetzt noch genauer untersucht


/**
 * @type {string}
 */
export const themesUrl = 'https://geomapfish-demo-dc.camptocamp.com/sbrunner24/themes?version=2&background=background';

/**
 * @type {string}
 */
export const projection = 'EPSG:21781';

/**
 * @type {Array.<number>}
 */
export const resolutions = [250, 100, 50, 20, 10, 5, 2, 1, 0.5, 0.25, 0.1, 0.05];

/**
 * @type {Array.<number>}
 */
export const extent =  [420000, 30000, 660000, 350000];

/**
 * @type {string}
 */
export const backgroundLayer = 'asitvd.fond_gris_sans_labels';

/**
 * @type {string}
 */
export const themesUrl = 'https://geomapfish-demo-dc.camptocamp.com/2.4/themes?version=2&background=background';

/**
 * @type {string}
 */
export const projection = 'EPSG:21781';

/**
 * @type {Array.<number>}
 */
export const resolutions = [250, 100, 50, 20, 10, 5, 2, 1, 0.5, 0.25, 0.1, 0.05];

/**
 * @type {Array.<number>}
 */
export const extent =  [420000, 30000, 660000, 350000];

/**
 * @type {string}
 */
export const backgroundLayer = 'asitvd.fond_gris_sans_labels';

FIX themesUrl - use 2.4


export default from './WindowScroller'
export WindowScroller from './WindowScroller'
export IS_SCROLLING_TIMEOUT from './utils/onScroll'

export default from './WindowScroller'
export WindowScroller from './WindowScroller'
export {IS_SCROLLING_TIMEOUT} from './utils/onScroll'

Correct export to named import, rather then default


'use strict';

const _sinon = require('sinon');
const _clone = require('clone');

/**
 * Helper class that helps construct mock config objects.
 */
class ConfigMockBuilder {
    /**
     * @param {Object} [props={}] An optional config object that contains
     *        the config parameters represented by the mock
     */
    constructor(props) {
        if (!props || typeof props !== 'object') {
            props = {};
        }
        this._mock = {
            get: function() {},
            _props: _clone(props)
        };
        _sinon.stub(this._mock, 'get', (propPath) => {
            const reducer = (obj, propName) => {
                return (obj && typeof obj === 'object')? obj[propName]:undefined;
            };
            return propPath.split('.').reduce(reducer, this._mock._props);
        });
    }

    /**
     * Gets a reference to the mock object represented by this class.
     *
     * @return {Object} Reference to the mock object.
     */
    get mock() {
        return this._mock;
    }
}

module.exports = ConfigMockBuilder;

'use strict';

const _sinon = require('sinon');
const _clone = require('clone');

/**
 * Helper class that helps construct mock config objects.
 */
class ConfigMockBuilder {
    /**
     * @param {Object} [props={}] An optional config object that contains
     *        the config parameters represented by the mock
     */
    constructor(props) {
        if (!props || typeof props !== 'object') {
            props = {};
        }
        this._mock = {
            get: function() {},
            _addProperties: (props) => {
                return this.addProperties(props).mock;
            },
            _props: _clone(props)
        };
        _sinon.stub(this._mock, 'get', (propPath) => {
            const reducer = (obj, propName) => {
                return (obj && typeof obj === 'object')? obj[propName]:undefined;
            };
            return propPath.split('.').reduce(reducer, this._mock._props);
        });
    }
    /**
     * Adds additional config properties to the existing config store.
     * These properties can then be retrieved as if they were a part
     * of the config object ex: config.get('prop.path.name'). Any existing
     * properties with the same name/path will be overwritten.
     *
     * @param {Object} props The properties to add to the config
     *
     * @return {Object} A reference to the builder - can be used for
     *         chaining.
     */
    addProperties(props) {
        if(!props || (props instanceof Array) || typeof props !== 'object') {
            throw new Error('Invalid properties specified (arg #1)');
        }
        this.mock._props = Object.assign(this.mock._props, props);

        return this;
    }

    /**
     * Gets a reference to the mock object represented by this class.
     *
     * @return {Object} Reference to the mock object.
     */
    get mock() {
        return this._mock;
    }
}

module.exports = ConfigMockBuilder;

Add utility method to set config properties


$(document).ready(function() {
  // This is called after the document has loaded in its entirety
  // This guarantees that any elements we bind to will exist on the page
  // when we try to bind to them

  // See: http://docs.jquery.com/Tutorials:Introducing_$(document).ready()
});

$(document).ready(function() {
  $("#insert-li").on('click', function(event) {
    $("#list").append("
derp-element
");
  });

  $("ul").on('click', function(event) {
    event.preventDefault();
    console.log("event bubbling saved the day");
  });
});

Add JS to demo event bubbling for JULIA CASTRO


<% if (router) {%><%- imports(
  { 'react': ['React']
  , 'react-dom': ['ReactDOM']
  , './components/$paramName': [name]
  , 'react-router': [false, ['Router', 'Route']] }) %>

<% if (append){%>let mountNode = document.createElement('div')
document.body.appendChild(mountNode)
<%} else {%>let mountNode = document.body
<%}%>
ReactDOM.render((
  <Router>
    <Route path="/" component={<%= name %>}>

    </Route>
  </Router>
), mountNode)<%} else {%><%- imports(
  { 'react': ['React']
  , './components/$paramName': [name] }) %>

<% if (append){%>let mountNode = document.createElement('div')
document.body.appendChild(mountNode)
<%} else {%>let mountNode = document.body
<%}%>
ReactDOM.render(<<%= name %> />, mountNode)<%}%>

<% if (router) {%><%- imports(
  { 'react': ['React']
  , 'react-dom': ['ReactDOM']
  , './components/$paramName': [name]
  , 'react-router': [false, ['Router', 'Route']] }) %>

<% if (append){%>let mountNode = document.createElement('div')
document.body.appendChild(mountNode)
<%} else {%>let mountNode = document.body
<%}%>
ReactDOM.render((
  <Router>
    <Route path="/" component={<%= name %>}>

    </Route>
  </Router>
), mountNode)<%} else {%><%- imports(
  { 'react': ['React']
  , 'react-dom': ['ReactDOM']
  , './components/$paramName': [name] }) %>

<% if (append){%>let mountNode = document.createElement('div')
document.body.appendChild(mountNode)
<%} else {%>let mountNode = document.body
<%}%>
ReactDOM.render(<<%= name %> />, mountNode)<%}%>

Add react-dom to routerless renderer


var assert = require('assert');
var expect = require('chai').expect;

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

describe('Doorman', function () {
  it('should expose a constructor', function () {
    assert(typeof Doorman, 'function');
  });
});

var assert = require('assert');
var expect = require('chai').expect;

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

describe('Doorman', function () {
  it('should expose a constructor', function () {
    assert(typeof Doorman, 'function');
  });

  it('can initialize a connection', function (done) {
    var doorman = new Doorman();

    doorman.on('ready', done);

    doorman.start();
  });
});

Add a test for ready event


$(function () {
	var base = '/';
	var initialTitle = document.title;

	$(Z).on('Z:publicationchange', function (ev, publication) {
		document.title = [publication.title, initialTitle].join(': ');
		if (typeof history.replaceState === 'function') {
			history.replaceState(publication, publication.title, Z.slugify(publication));
			$(Z).trigger('Z:statechange');
		}
	});

	// Check if document was loaded with a path
	$(Z).on('Z:ready', function (ev, publications) {
		var initialSlug = window.location.pathname.replace(base, '');
		if (initialSlug.length !== '') {
			for (var i=0, l=publications.length; i<l; i++) {
				if (initialSlug === Z.slugify(publications[i])) {
					$(Z).trigger('Z:publicationchange', publications[i]);
					break;
				}
			}
		}
	});
});

$(function () {
	var initialTitle = document.title;

	$(Z).on('Z:publicationchange', function (ev, publication) {
		document.title = [publication.title, initialTitle].join(': ');
		if (typeof history.replaceState === 'function') {
			history.replaceState(publication, publication.title, Z.slugify(publication));
			$(Z).trigger('Z:statechange');
		}
	});

	// Check if document was loaded with a path
	$(Z).on('Z:ready', function (ev, publications) {
		var initialSlug = window.location.pathname.split('/').pop();
		if (initialSlug !== '') {
			for (var i=0, l=publications.length; i<l; i++) {
				if (initialSlug === Z.slugify(publications[i])) {
					$(Z).trigger('Z:publicationchange', publications[i]);
					break;
				}
			}
		}
	});
});

Fix generated pages not showing publication on load


import {EventEmitter} from 'events'

export default class Player extends EventEmitter {

	constructor(game) {
		super();
		this.game = game;
		this.piece = undefined;
		this.type = "player"

		if(this.onMyTurn && typeof this.onMyTurn === "function")
			this.on('play', () => this.onMyTurn(...arguments))
	}

	makeMove(x, y) {
		if(this.game.board.isEmpty(x,y))
			this.emit('make move', x, y)
	}

	setGame(game) {
		this.game = game;
	}

}

import {EventEmitter} from 'events'

export default class Player extends EventEmitter {

	constructor(game, options) {
		super()
		this.game = game
		this.piece = undefined
		this.type = "player"
		this.options = options || {}

		if(this.onMyTurn && typeof this.onMyTurn === "function")
			this.on('play', (board, turn) => this.onMyTurn(board, turn))
	}

	makeMove(x, y) {
		if(this.game.board.isEmpty(x,y))
			this.emit('make move', x, y)
	}

	setGame(game) {
		this.game = game;
	}

}

Update base player class to accept options as a second parameter to its constructor


/* ************************************ */
/* Server                               */
/* ************************************ */
'use strict';

var path = require('path');
var gulp = require('gulp');
var conf = require('./conf');

var $ = require('gulp-load-plugins')({
  pattern: ['browser-*']
});

var proxyMiddleware = require('http-proxy-middleware');

function browserSyncInit(baseDir, browser) {
  browser = browser === undefined ? 'default' : browser;

  var routes = null;
  routes = {
    '/bower_components': 'bower_components',
    '/dist': 'dist',
    '/assets': 'workspace/assets'
  };

  var server = {
    baseDir: baseDir,
    routes: routes
  };

  $.browserSync.instance = $.browserSync.init({
    port: 7001,
    startPath: '/',
    server: server,
    browser: browser,
    ui: {
      port: 7002
    }
  });
}

$.browserSync.use($.browserSyncSpa({
  selector: '[ng-app]'// Only needed for angular apps
}));

gulp.task('serve', ['workspace', 'watch'], function () {
  browserSyncInit(conf.paths.workspace);
});

// If rebuilding the library on every change is using too much cpu,
// or you want to manually build.
gulp.task('serve:noreload', ['workspace'], function () {
  browserSyncInit(conf.paths.workspace);
});

gulp.task('serve:nobuild', function () {
  browserSyncInit(conf.paths.workspace);
});

Add gulp serve no build task


var App = window.App || {};

App.Create = (function ($, w) {

    'use strict';

    function Create ($el, options) {

        this.options = $.extend({}, Create.defaults, options);

        $($el).midgardCreate();

        w.Backbone.sync = this.sync.bind(this);
    }

    // Override Backbone.sync
    Create.prototype.sync = function(method, model, options) {

        // only update implemented
        if (method !== 'update') {
            return options.error(model);
        }

        this.id = Create.extractId(model.id);

        $.ajax({
            url: '/pages/' + this.id,
            type: 'put',
            data: model.attributes,
            success: function () {
                options.success(model);
            },
            error: function () {
                options.error(model);
            }
        });

    };

    Create.extractId = function(id) {
        return parseInt(id.replace(/<\/pages\/(\d+)>/, '$1'), 10);
    };

    Create.defauts = {

    };

    return Create;

}(jQuery, window));

var App = window.App || {};

App.Create = (function ($, w) {

    'use strict';

    function Create ($el, options) {

        this.options = $.extend({}, Create.defaults, options);

        $($el).midgardCreate();

        w.Backbone.sync = this.sync.bind(this);
    }

    // Override Backbone.sync
    Create.prototype.sync = function(method, model, options) {

        // only update implemented
        if (method !== 'update') {
            return options.error(model);
        }

        this.id = Create.extractId(model.id);

        $.ajax({
            url: '/pages/' + this.id,
            type: 'put',
            data: model.toJSON(),
            dataType: 'json',
            success: function () {
                options.success(model);
            },
            error: function () {
                options.error(model);
            }
        });

    };

    Create.extractId = function(id) {
        return parseInt(id.replace(/<\/pages\/(\d+)>/, '$1'), 10);
    };

    Create.defauts = {

    };

    return Create;

}(jQuery, window));

Send to server as json


exports.Program = (node, generator) => {
  // Program nodes should not have their own mapping.
  node.body.forEach(bodyNode => {
    generator.generate(bodyNode);
  });
};

exports.File = (node, generator) => {
  generator.generate(node.program);
};

exports.Program = (node, generator) => {
  if (node.directives && node.directives.length) {
    node.directives.forEach(directiveNode => {
      generator.generate(directiveNode);
    });
  }

  // Program nodes should not have their own mapping.
  node.body.forEach(bodyNode => {
    generator.generate(bodyNode);
  });
};

exports.Directive = (node, generator) => {
  generator.generate(node.value);
};

exports.DirectiveLiteral = (node, generator) => {
  generator.advance(`"${node.value}";`);
};

exports.File = (node, generator) => {
  generator.generate(node.program);
};

Support program directives (like "use strict").


/* eslint no-process-exit:0 */
import fsp from 'fs-promise';
import _ from 'lodash';
import * as issueHandler from './issue-handler';
import {findProjectKey} from './jira-operations';
import {getJiraAPI} from './jira-connection';

export function getIssueReference(msgToParse, prjKey) {
  let pattern = RegExp(`${prjKey}-\\d+`, 'g');
  let commentPattern = RegExp(`^#.*$`, 'gm');

  msgToParse = msgToParse.replace(commentPattern, '');
  let references = msgToParse.match(pattern);

  return _.unique(references);
}

export function getCommitMsg(path) {
  return getJiraAPI()
    .then(jiraAPI =>
    {
      return fsp.readFile(path, {encoding:'utf8'})
        .then(fileContents =>
          getIssueReference(fileContents, findProjectKey(jiraAPI))
        )
        .then(issues =>
          issueHandler.issueStrategizer(issues)
        );
    });
}

export function precommit(path) {
  return getCommitMsg(path)
    .then(() => 0)
    .catch(err => {
      console.error(err);
      return 1;
    });
}

/* eslint no-process-exit:0 */
import fsp from 'fs-promise';
import _ from 'lodash';
import * as issueHandler from './issue-handler';
import {findProjectKey} from './jira-operations';
import {getJiraAPI} from './jira-connection';

export function getIssueReference(msgToParse, prjKey) {
  let pattern = RegExp(`${prjKey}-\\d+`, 'g');
  let commentPattern = RegExp(`^#.*$`, 'gm');

  msgToParse = msgToParse.replace(commentPattern, '');
  let references = msgToParse.match(pattern);

  return _.unique(references);
}

export function getCommitMsg(path) {
  return getJiraAPI()
    .then(jiraAPI =>
    {
      return fsp.readFile(path, {encoding:'utf8'})
        .then(fileContents =>
          getIssueReference(fileContents, findProjectKey(jiraAPI))
        )
        .then(issues =>
          issueHandler.issueStrategizer(issues)
        );
    });
}

export function precommit(path) {
  return getCommitMsg(path)
    .then(() => 0)
    .catch(err => {
      if (process.env.NODE_ENV === 'development') {
        console.error(err.stack);
      } else {
        console.error(err.toString());
      }
      return 1;
    });
}

Add development mode error logging