// @flow
import type { TodoItem } from "./model/TodoItem";

import React from "react";
import ContentAdd from "material-ui/svg-icons/content/add";
import TextField from "material-ui/TextField";
import Paper from "material-ui/Paper";
import FloatingActionButton from "material-ui/FloatingActionButton";

import logo from "./logo.svg";
import TodoList from "./TodoListContainer";
import TodoToolbar from "./TodoToolbarContainer";

import "./App.css";

type Props = {
  items: Array<TodoItem>,
  addTodo: (text: string) => void
};

const App = ({ items, addTodo }: Props) => {
  let inputTextField;

  const addItem = () => {
    const input = inputTextField.input;
    if (input.value) {
      addTodo(input.value);
      input.value = "";
    }
  };

  return (
    <div className="App">
      <TodoToolbar />

      <TodoList items={items} />

      <div
        style={{
          display: "flex",
          justifyContent: 'space-between',
          alignItems: "center",
          padding: '32px'
        }}
      >
        <TextField
          ref={node => (inputTextField = node)}
          hintText="Enter you note"
          style={{
            flex: 1,
            margin: '0 16px'
          }}
        />
        <FloatingActionButton
          onClick={addItem}
        >
          <ContentAdd />
        </FloatingActionButton>
      </div>
    </div>
  );
};

export default App;

// @flow
import type { TodoItem } from "./model/TodoItem";

import React from "react";
import ContentAdd from "material-ui/svg-icons/content/add";
import TextField from "material-ui/TextField";
import Paper from "material-ui/Paper";
import FloatingActionButton from "material-ui/FloatingActionButton";

import logo from "./logo.svg";
import TodoList from "./TodoListContainer";
import TodoToolbar from "./TodoToolbarContainer";

import "./App.css";

type Props = {
  items: Array<TodoItem>,
  addTodo: (text: string) => void
};

const App = ({ items, addTodo }: Props) => {
  let inputTextField;

  const addItem = () => {
    const input = inputTextField.input;
    if (input.value) {
      addTodo(input.value);
      input.value = "";
    }
  };

  return (
    <div className="App">
      <TodoToolbar />

      <TodoList items={items} />

      <div
        style={{
          display: "flex",
          justifyContent: "space-between",
          alignItems: "center",
          padding: "32px"
        }}
      >
        <TextField
          ref={node => (inputTextField = node)}
          onKeyPress={ev => {
            if (ev.key === "Enter") {
              addItem()
            }
          }}
          autoFocus={true}
          hintText="Enter you note"
          style={{
            flex: 1,
            margin: "0 16px"
          }}
        />
        <FloatingActionButton onClick={addItem}>
          <ContentAdd />
        </FloatingActionButton>
      </div>
    </div>
  );
};

export default App;

Save note on Enter key press, autofocus the input field


module.exports = function(context) {

    "use strict";

    return {
        "Identifier": function(node) {
            if (node.name === "foo") {
                context.report(node, "Identifier cannot be names 'foo'.");
            }
        }
    };

};

module.exports = function(context) {

    "use strict";

    return {
        "Identifier": function(node) {
            if (node.name === "foo") {
                context.report(node, "Identifier cannot be named 'foo'.");
            }
        }
    };

};

Correct a typo in the error message in tests


import {PropTypes} from 'react'

function lazy(fn) {
  let cachedFn
  return (...args) => (cachedFn || (cachedFn = fn()))(...args)
}

const field = PropTypes.shape({
  name: PropTypes.string,
  type: type
})

const type = PropTypes.shape({
  name: PropTypes.string,
  title: PropTypes.string,
  description: PropTypes.string,
  placeholder: PropTypes.string,
  type: lazy(() => type),
  to: lazy(() => PropTypes.arrayOf(type)),
  fields: lazy(() => PropTypes.arrayOf(field)),
  of: lazy(() => PropTypes.arrayOf(type))
})

const validation = {
  fields: PropTypes.objectOf(lazy(() => PropTypes.shape(validation))),
  messages: PropTypes.arrayOf(PropTypes.shape({
    type: PropTypes.oneOf(['error', 'warning']),
    id: PropTypes.string,
    message: PropTypes.string
  }))
}

const schema = PropTypes.shape({
  name: PropTypes.string,
  fields: PropTypes.arrayOf(type)
})

export default {
  type,
  field,
  schema,
  validation
}

import {PropTypes} from 'react'

function lazy(fn) {
  let cachedFn
  return (...args) => (cachedFn || (cachedFn = fn()))(...args)
}

let type

const field = PropTypes.shape({
  name: PropTypes.string,
  type: lazy(() => type)
})

type = PropTypes.shape({
  name: PropTypes.string,
  title: PropTypes.string,
  description: PropTypes.string,
  placeholder: PropTypes.string,
  type: lazy(() => type),
  to: lazy(() => PropTypes.arrayOf(type)),
  fields: lazy(() => PropTypes.arrayOf(field)),
  of: lazy(() => PropTypes.arrayOf(type))
})

const validation = {
  fields: PropTypes.objectOf(lazy(() => PropTypes.shape(validation))),
  messages: PropTypes.arrayOf(PropTypes.shape({
    type: PropTypes.oneOf(['error', 'warning']),
    id: PropTypes.string,
    message: PropTypes.string
  }))
}

const schema = PropTypes.shape({
  name: PropTypes.string,
  fields: PropTypes.arrayOf(type)
})

export default {
  type,
  field,
  schema,
  validation
}

Make type propType of field lazy


import Ember from 'ember';
import AuthenticatedRouteMixin from 'ember-simple-auth/mixins/authenticated-route-mixin';
/**
  The checkout route.

 **To Override:** You'll need to run the routes generator:

 ```bash
 ember g yebo-ember-storefront-routes
 ```

 This will install all of the Yebo Ember Storefront route files into your
 host application at `app/routes/yebo/*.js`, ready to be extended or
 overriden.

 @class Checkout
 @namespace Route
 @extends Ember.Component
 */
export default Ember.Route.extend(AuthenticatedRouteMixin, {
  redirect: function(model) {
    this.get('yebo').get('checkouts').on('orderCompleted', (order)=> {
      // orderNumber;
      this.transitionTo(this.get("yebo.config.afterCompletePath")) ;
    });
    // var currentOrder = this.yebo.get('currentOrder');
    // if (!currentOrder) {
    //   this.transitionTo('yebo.index');
    // }
  },
});

import Ember from 'ember';
import AuthenticatedRouteMixin from 'ember-simple-auth/mixins/authenticated-route-mixin';
/**
  The checkout route.

 **To Override:** You'll need to run the routes generator:

 ```bash
 ember g yebo-ember-storefront-routes
 ```

 This will install all of the Yebo Ember Storefront route files into your
 host application at `app/routes/yebo/*.js`, ready to be extended or
 overriden.

 @class Checkout
 @namespace Route
 @extends Ember.Component
 */
export default Ember.Route.extend(AuthenticatedRouteMixin, {
  redirect: function(model) {
    this.get('yebo').get('checkouts').on('orderCompleted', (order)=> {
      // @todo Redirect to the order details page
      this.transitionTo('yebo');
    });
  },
});

Change the wrong config route to a static route for now


//  Copyright 2015 Fabian Dietenberger

'use-strict'

var q = require('q'),
    request = require('request');

var hypemResolver = {};

hypemResolver.getByUrl = function (hypemUrl) {
    var resultUrl = "";
    return resultUrl;
};

hypemResolver.getById = function (hypemId) {
    return this.getByUrl("http://hypem.com/track/" + hypemId);
};

module.exports = hypemResolver;

//  Copyright 2015 Fabian Dietenberger

'use-strict';

var q = require('q'),
    request = require('request');

var hypemResolver = {};

hypemResolver.getByUrl = function (hypemUrl) {
    var resultUrl = "https://soundcloud.com/griz/summer-97-ft-muzzy-bearr";
    return resultUrl;
};

hypemResolver.getByUrlAsync = function (hypemUrl, callback) {
    process.nextTick(function () {
        callback(hypemResolver.getByUrl(hypemUrl));
    });
};

hypemResolver.getById = function (hypemId) {
    return this.getByUrl("http://hypem.com/track/" + hypemId);
};

hypemResolver.getByIdAsync = function (hypemId, callback) {
    process.nextTick(function () {
        callback(hypemResolver.getById(hypemId));
    });
};

module.exports = hypemResolver;

Add async methods to get the URL


/**
 * Created by liuxizi.xu on 2/2/17.
 */
'use strict';
/*global angular:false */

angular.module('argus.services.downloadHelper', [])
.service('DownloadHelper', function () {
	this.downloadFile = function (data, filename) {
		var blob = new Blob([data], {type: "text/plain;charset=utf-8"});
		saveAs(blob, filename);
	};
});

/**
 * Created by liuxizi.xu on 2/2/17.
 */
'use strict';
/*global angular:false */

angular.module('argus.services.downloadHelper', [])
.service('DownloadHelper', function () {
	this.downloadFile = function (data, filename) {
		var blob = new Blob([data], {type: "text/plain"});
		saveAs(blob, filename);
	};
});

Fix downloaded file has invisible characters issue


/**
 * Flint
 * By: Ivan Montiel
 *
 * TODO support reference checks
 * TODO support cancelling by using `new Flint();`
 */
(function () {
  /**
   * Wait for globals to be defined by other scripts being loaded.
   *
   * Usage: Flint(function () { ... }, global1, global2);
   *
   */

  Flint = function (callback) {
    var self = this;
    var args = arguments;

    if (arguments.length == 0) {
      return;
    }

    if (arguments.length == 1)  {
      callback();
      return;
    }

    var defined = true;
    for (var i = 1; i < arguments.length; i++) {
      // Support dot notation in argument
      var arg = arguments[i].split('.');
      var deepValue = window[arg[0]];

      if (typeof deepValue == 'undefined') {
        defined = false;
        break;
      }

      for (var j = 1; j < arg.length; j++) {
        deepValue = deepValue[arg[j]];

        if (typeof deepValue == 'undefined') {
          defined = false;
          break;
        }
      }
    }

    if (!defined) {
      self.timeout = setTimeout(function () {
        Flint.apply(self, args);
      }, 10);
      return self;
    }

    callback();
  };

  Flint.prototype.cancel = function () {
    clearTimeout(this.timeout);
  }
})();

/**
 * Flint
 * By: Ivan Montiel
 *
 * TODO support reference checks
 */
(function () {
  /**
   * Wait for globals to be defined by other scripts being loaded.
   *
   * Usage: Flint(function () { ... }, global1, global2);
   *
   */

  Flint = function (callback) {
    var self = this;
    var args = arguments;

    if (arguments.length == 0) {
      return;
    }

    if (arguments.length == 1)  {
      callback();
      return;
    }

    var defined = true;
    for (var i = 1; i < arguments.length; i++) {
      // Support dot notation in argument
      var arg = arguments[i].split('.');
      var deepValue = window[arg[0]];

      if (typeof deepValue == 'undefined') {
        defined = false;
        break;
      }

      for (var j = 1; j < arg.length; j++) {
        deepValue = deepValue[arg[j]];

        if (typeof deepValue == 'undefined') {
          defined = false;
          break;
        }
      }
    }

    if (!defined) {
      self.timeout = setTimeout(function () {
        Flint.apply(self, args);
      }, 10);
      return self;
    }

    callback();
  };

  Flint.prototype.cancel = function () {
    clearTimeout(this.timeout);
  }
})();

Remove TODO that was completed.


// @flow
import { Parser, type AcornNode } from 'acorn';

export const ACORN_OPTIONS = {
	ecmaVersion: 2019,
	sourceType: 'module',
};

/**
 * Parse source code with Acorn and return AST, returns undefined in case of errors
 */
export default function getAst(code: string, plugins?: Function[] = []): ?AcornNode {
	const parser = Parser.extend(...plugins);

	try {
		return parser.parse(code, ACORN_OPTIONS);
	} catch (err) {
		return undefined;
	}
}

// @flow
import { Parser, type AcornNode } from 'acorn';
import Logger from 'glogg';

const logger = Logger('rsg');

export const ACORN_OPTIONS = {
	ecmaVersion: 2019,
	sourceType: 'module',
};

/**
 * Parse source code with Acorn and return AST, returns undefined in case of errors
 */
export default function getAst(code: string, plugins?: Function[] = []): ?AcornNode {
	const parser = Parser.extend(...plugins);

	try {
		return parser.parse(code, ACORN_OPTIONS);
	} catch (err) {
		logger.debug(`Acorn cannot parse example code: ${err.message}\n\nCode:\n${code}`);
		return undefined;
	}
}

Add debug logging for Acorn parsing errors


'use strict';

describe('SimulationService', function () {
    describe('sortDroits', function() {
        var DROITS_DESCRIPTION = { acs: { id: 'acs' }, apl: { id: 'apl' }};
        var service;

        beforeEach(function() {
            module('ddsApp');
            module(function($provide) {
                $provide.constant('droitsDescription', DROITS_DESCRIPTION);
            });
            inject(function(SimulationService) {
                service = SimulationService;
            });
        });

        it('should sort eligible and non eligible droits', function() {
            var openfiscaResult = { acs: 10 };
            var droits = service.sortDroits(openfiscaResult);
            expect(droits.droitsEligibles).toEqual({ acs: { id: 'acs', montant: 10 } });
            expect(droits.droitsNonEligibles).toEqual({ apl: { id: 'apl' } });
            expect(droits.droitsInjectes).toEqual({});
        });

        it('should sort eligible and injected droits', function() {
            var openfiscaResult = { acs: 10, apl: null };
            var droits = service.sortDroits(openfiscaResult);
            expect(droits.droitsEligibles).toEqual({ acs: { id: 'acs', montant: 10 } });
            expect(droits.droitsNonEligibles).toEqual({});
            expect(droits.droitsInjectes).toEqual({ apl: { id: 'apl' } });
        });
    });
});

'use strict';

describe('SimulationService', function () {
    describe('sortDroits', function() {
        var DROITS_DESCRIPTION = {
            acs: { id: 'acs' },
            apl: { id: 'apl' },
            ass: { id: 'ass'}
        };
        var service, droits, openfiscaResult;

        beforeEach(function() {
            module('ddsApp');
            module(function($provide) {
                $provide.constant('droitsDescription', DROITS_DESCRIPTION);
            });
            inject(function(SimulationService) {
                service = SimulationService;
            });
            openfiscaResult = { acs: 10, apl: null };
            droits = service.sortDroits(openfiscaResult);
        });

        it('should extract eligibles droits from openfisca result', function() {
            expect(droits.droitsEligibles).toEqual({ acs: { id: 'acs', montant: 10 } });
        });

        it('should extract injected droits', function() {
            expect(droits.droitsInjectes).toEqual({ apl: { id: 'apl' } });
        });

        it('should extract non eligibles droits', function() {
            expect(droits.droitsNonEligibles).toEqual({ ass: { id: 'ass' } });
        });
    });
});

Divide test into single-it tests


import HttpStatus from 'http-status-codes';

/**
 * Build error response for validation errors.
 *
 * @param  {error} err
 * @return {array|object}
 */
function buildError(err) {
  // Validation errors
  if (err.isJoi || err instanceof SyntaxError) {
    return {
      code: HttpStatus.BAD_REQUEST,
      message: HttpStatus.getStatusText(HttpStatus.BAD_REQUEST),
      details: err.details && err.details.map(err => {
        return {
          message: err.message,
          param: err.path
        };
      })
    };
  }

  // HTTP errors
  if (err.isBoom) {
    return {
      code: err.output.statusCode,
      message: err.output.payload.message || err.output.payload.error
    };
  }

  // Return INTERNAL_SERVER_ERROR for all other cases
  return {
    code: HttpStatus.INTERNAL_SERVER_ERROR,
    message: HttpStatus.getStatusText(HttpStatus.INTERNAL_SERVER_ERROR)
  };
}

export default buildError;

import HttpStatus from 'http-status-codes';

/**
 * Build error response for validation errors.
 *
 * @param  {error} err
 * @return {array|object}
 */
function buildError(err) {
  // Validation errors
  if (err.isJoi || err instanceof SyntaxError) {
    return {
      code: HttpStatus.BAD_REQUEST,
      message: HttpStatus.getStatusText(HttpStatus.BAD_REQUEST),
      details: err.details && err.details.map(err => {
        return {
          message: err.message,
          param: err.path.join('.')
        };
      })
    };
  }

  // HTTP errors
  if (err.isBoom) {
    return {
      code: err.output.statusCode,
      message: err.output.payload.message || err.output.payload.error
    };
  }

  // Return INTERNAL_SERVER_ERROR for all other cases
  return {
    code: HttpStatus.INTERNAL_SERVER_ERROR,
    message: HttpStatus.getStatusText(HttpStatus.INTERNAL_SERVER_ERROR)
  };
}

export default buildError;

Join error path from Joi


import Ember from 'ember';
import { module, test } from 'qunit';
import startApp from '../helpers/start-app';

var application;

module('Acceptance: DocumentTitle', {
  beforeEach: function() {
    application = startApp();
  },

  afterEach: function() {
    Ember.run(application, 'destroy');
  }
});

test('static title doesn\'t bubble', function(assert) {
  assert.expect(1);

  visit('/about');
  var router = application.__container__.lookup('router:main');

  andThen(function() {
    assert.equal(router._title, 'About Us', 'It doesn\'t bubble up');
  });
});

test('bubbling title tokens', function(assert) {
  assert.expect(1);

  visit('/team');
  var router = application.__container__.lookup('router:main');

  andThen(function() {
    assert.equal(router._title, 'The Team - My Blog', 'The title token bubbles up');
  });
});

test('dynamic title based on a model', function(assert) {
  assert.expect(1);

  visit('/posts');
  var router = application.__container__.lookup('router:main');

  andThen(function() {
    assert.equal(router._title, 'Ember is omakase - Posts - My Blog');
  });
});

import Ember from 'ember';
import { module, test } from 'qunit';
import startApp from '../helpers/start-app';

var application, router;

module('Acceptance: DocumentTitle', {
  beforeEach: function() {
    application = startApp();
    router = application.__container__.lookup('router:main');
  },

  afterEach: function() {
    Ember.run(application, 'destroy');
  }
});

test('static title doesn\'t bubble', function(assert) {
  assert.expect(1);

  visit('/about');

  andThen(function() {
    assert.equal(router._title, 'About Us', 'It doesn\'t bubble up');
  });
});

test('bubbling title tokens', function(assert) {
  assert.expect(1);

  visit('/team');

  andThen(function() {
    assert.equal(router._title, 'The Team - My Blog', 'The title token bubbles up');
  });
});

test('dynamic title based on a model', function(assert) {
  assert.expect(1);

  visit('/posts');

  andThen(function() {
    assert.equal(router._title, 'Ember is omakase - Posts - My Blog');
  });
});

Refactor router lookup in tests


import { Accounts } from 'meteor/accounts-base';
import {GlobalSettings} from './GlobalSettings';

Accounts.emailTemplates.siteName = GlobalSettings.getSiteName();
Accounts.emailTemplates.from     = Accounts.emailTemplates.siteName + '<' + GlobalSettings.getDefaultEmailSenderAddress() + '>';

Accounts.emailTemplates.verifyEmail = {
    subject() {
        return '[' + Accounts.emailTemplates.siteName + '] Verify Your Email Address';
    },
    text( user, url ) {
        let emailAddress   = user.emails[0].address,
            urlWithoutHash = url.replace( '#/', '' ),
            emailBody      = 'To verify your email address ' + emailAddress + ' visit the following link:\n\n' +
                            urlWithoutHash +
                            '\n\n If you did not request this verification, please ignore this email. ' +
                            'If you feel something is wrong, please contact your admin team';

        return emailBody;
    }
};

Accounts.emailTemplates.resetPassword = {
    subject() {
        return '[' + Accounts.emailTemplates.siteName + '] Reset Your Password';
    },
    text( user, url ) {
        let emailAddress   = user.emails[0].address,
            urlWithoutHash = url.replace( '#/', '' ),
            emailBody      = 'To reset your password for ' + emailAddress + ' visit the following link:\n\n' +
                            urlWithoutHash +
                            '\n\n If you did not request to reset your password, please ignore this email. ' +
                            'If you feel something is wrong, please contact your admin team';

        return emailBody;
    }
};

import { Accounts } from 'meteor/accounts-base';
import { GlobalSettings } from './GlobalSettings';

function setupEmailTemplatesForAccounts() {
    Accounts.emailTemplates.siteName = GlobalSettings.getSiteName();
    Accounts.emailTemplates.from = Accounts.emailTemplates.siteName + '<' + GlobalSettings.getDefaultEmailSenderAddress() + '>';

    Accounts.emailTemplates.verifyEmail = {
        subject() {
            return '[' + Accounts.emailTemplates.siteName + '] Verify Your Email Address';
        },
        text(user, url) {
            let emailAddress = user.emails[0].address,
                urlWithoutHash = url.replace('#/', ''),
                emailBody = 'To verify your email address ' + emailAddress + ' visit the following link:\n\n' +
                    urlWithoutHash +
                    '\n\n If you did not request this verification, please ignore this email. ' +
                    'If you feel something is wrong, please contact your admin team';

            return emailBody;
        }
    };

    Accounts.emailTemplates.resetPassword = {
        subject() {
            return '[' + Accounts.emailTemplates.siteName + '] Reset Your Password';
        },
        text(user, url) {
            let emailAddress = user.emails[0].address,
                urlWithoutHash = url.replace('#/', ''),
                emailBody = 'To reset your password for ' + emailAddress + ' visit the following link:\n\n' +
                    urlWithoutHash +
                    '\n\n If you did not request to reset your password, please ignore this email. ' +
                    'If you feel something is wrong, please contact your admin team';

            return emailBody;
        }
    };

}

if (GlobalSettings.isEMailDeliveryEnabled()) {
    setupEmailTemplatesForAccounts();
}

Set up email templates for the accounts package only if email delivery is enabled


import moment from 'moment';
import granularities from 'models/geogranularities';
import frequencies from 'models/frequencies';
import resource_types from 'models/resource_types';

export default {
    filters: {
        /**
         * Display a date range in the shorter possible manner.
         */
        daterange: function(range) {
            if (!range || !range.start) {
                return;
            }
            const start = moment(range.start);
            const end = range.end ? moment(range.end) : undefined;
            const start_label = start.format('YYYY');
            const end_label = end.format('YYYY');

            return end_label
                ? this._('{start_label}-{end_label}', {start_label, end_label, interpolation: { escapeValue: false }})
                : start_label;
        },
        frequency_label(dataset) {
            if (dataset && dataset.frequency) {
                return frequencies.by_id(dataset.frequency).label;
            }
        },
        resource_type_label(resource) {
            if (resource && resource.type) {
                const resource_type = resource_types.by_id(resource.type);
                if (!resource_type) return 'Type not found';
                return resource_type.label;
            }
        },
        granularity_label(dataset) {
            if (dataset && dataset.spatial && dataset.spatial.granularity) {
                return granularities.by_id(dataset.spatial.granularity).name;
            }
        }
    }
};

import moment from 'moment';
import granularities from 'models/geogranularities';
import frequencies from 'models/frequencies';
import resource_types from 'models/resource_types';

export default {
    filters: {
        /**
         * Display a date range in the shorter possible manner.
         */
        daterange: function(range) {
            if (!range || !range.start) {
                return;
            }
            const start = moment(range.start);
            const end = range.end ? moment(range.end) : undefined;
            const start_label = start.format('YYYY');
            const end_label = end.format('YYYY');

            return end_label
                ? `${start_label}-${end_label}`
                : start_label;
        },
        frequency_label(dataset) {
            if (dataset && dataset.frequency) {
                return frequencies.by_id(dataset.frequency).label;
            }
        },
        resource_type_label(resource) {
            if (resource && resource.type) {
                const resource_type = resource_types.by_id(resource.type);
                if (!resource_type) return 'Type not found';
                return resource_type.label;
            }
        },
        granularity_label(dataset) {
            if (dataset && dataset.spatial && dataset.spatial.granularity) {
                return granularities.by_id(dataset.spatial.granularity).name;
            }
        }
    }
};

Remove i18n of the JS daterange filter


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

var User = require('../models/user');

module.exports = function(app, mountPoint) {
  router.get('/', function(req, res) {
    User.find(function(err, data) {
      if (err) throw err;
      res.json(data);
    });
  });

  router.post('/', function(req, res) {
    User.create(req.body, function(err, data) {
      if (err) throw err;
      res.json(data);
    });
  });

  router.put('/', function(req, res) {
    User.update({_id: req.body._id}, req.body, function(err, data) {
      if (err) throw err;
      res.json(data);
      console.log(req.body);
    });
  });

  router.delete('/', function(req, res) {
    User.remove({_id: req.body._id}, function(err, data) {
      if (err) throw err;
      console.log('The user has been removed');
    });
  });

  app.use(mountPoint, router)
}

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

var User = require('../models/user');

module.exports = function(app, mountPoint) {
  router.get('/', function(req, res) {
    User.find(function(err, data) {
      if (err) throw err;
      res.json(data);
    });
  });

  router.get('/:id', function(req, res) {
    User.findOne({_id: req.body._id}, function(err, data) {
      if (err) throw err;
      res.json(data);
    });
  });

  router.post('/', function(req, res) {
    User.create(req.body, function(err, data) {
      if (err) throw err;
      res.json(data);
    });
  });

  router.put('/', function(req, res) {
    User.update({_id: req.body._id}, req.body, function(err, data) {
      if (err) throw err;
      res.json(data);
      console.log(req.body);
    });
  });

  router.delete('/', function(req, res) {
    User.remove({_id: req.body._id}, function(err, data) {
      if (err) throw err;
      console.log('The user has been removed');
    });
  });

  app.use(mountPoint, router)
}

Add search for a user to later modify a profile


import _ from 'lodash'
import Reflux from 'reflux'
import StateMixin from'reflux-state-mixin'
import DatapointActions from 'actions/DatapointActions'

var DatapointStore = Reflux.createStore({

  mixins: [StateMixin.store],

  listenables: DatapointActions,

  datapoints: {
    meta: null,
    raw: null,
    index: null
  },

  getInitialState () {
    return this.datapoints
  },

  onRootStore (store) {
    this.campaignIndex = store.campaignIndex
    this.chartIndex = store.chartIndex
    this.locationIndex = store.locationIndex
    this.indicatorIndex = store.indicatorIndex
    this.officeIndex = store.officeIndex
  },

  // =========================================================================== //
  //                              API CALL HANDLERS                              //
  // =========================================================================== //

  // ============================  Fetch  Datapoints  ========================== //
  onFetchDatapoints () {
    this.setState({ raw: [] })
  },
  onFetchDatapointsCompleted (response) {
    this.datapoints.meta = response.meta
    this.datapoints.raw = response.objects
    this.datapoints.index = _.indexBy(this.datapoints.raw, 'id')
    this.setState(this.datapoints)
  },
  onFetchDatapointsFailed (error) {
    this.setState({ error: error })
  }
})

export default DatapointStore

import _ from 'lodash'
import Reflux from 'reflux'
import StateMixin from'reflux-state-mixin'
import DatapointActions from 'actions/DatapointActions'

var DatapointStore = Reflux.createStore({

  mixins: [StateMixin.store],

  listenables: DatapointActions,

  datapoints: {
    meta: null,
    raw: null,
    index: null
  },

  getInitialState () {
    return this.datapoints
  },

  // =========================================================================== //
  //                              API CALL HANDLERS                              //
  // =========================================================================== //

  // ============================  Fetch  Datapoints  ========================== //
  onFetchDatapoints () {
    this.setState({ raw: [] })
  },
  onFetchDatapointsCompleted (response) {
    this.datapoints.meta = response.meta
    this.datapoints.raw = response.objects
    this.datapoints.index = _.indexBy(this.datapoints.raw, 'id')
    this.setState(this.datapoints)
  },
  onFetchDatapointsFailed (error) {
    this.setState({ error: error })
  }
})

export default DatapointStore

Remove root store from datapoint store


/*
 * This Source Code is subject to the terms of the Mozilla Public License
 * version 2.0 (the "License"). You can obtain a copy of the License at
 * http://mozilla.org/MPL/2.0/.
 */

"use strict";

let {$} = require("./utils");

exports.disableResetHandlers = false;

function setCommandHandler(element, handler)
{
  if (typeof element == "string")
    element = $(element);
  let wrapper = (event) =>
  {
    event.preventDefault();
    handler.call(element, event);
  };
  element.addEventListener("click", wrapper);
}
exports.setCommandHandler = setCommandHandler;

function setSubmitHandler(element, handler)
{
  if (typeof element == "string")
    element = $(element);
  let wrapper = event =>
  {
    if (event.defaultPrevented)
      return;

    event.preventDefault();
    handler.call(element, event);
  };
  element.addEventListener("submit", wrapper);
}
exports.setSubmitHandler = setSubmitHandler;

function setResetHandler(element, handler)
{
  if (typeof element == "string")
    element = $(element);
  let wrapper = (event) =>
  {
    if (exports.disableResetHandlers)
      return;

    handler.call(element, event);
  };
  element.addEventListener("reset", wrapper);
}
exports.setResetHandler = setResetHandler;

document.addEventListener("keydown", event =>
{
  // This currently doesn't work in Firefox: https://bugzil.la/1443758
  if (event.key == "Escape")
  {
    let activePanel = require("./utils").getActivePanel();
    if (activePanel)
    {
      let button = $(activePanel).querySelector("button[type='reset']");
      if (button)
      {
        button.click();
        event.preventDefault();
      }
    }
  }
});

Make sure that pressing Escape doesn't close the pop-up when there is a cancel button - at least in Chrome


import Ember from "ember";
import ENV from '../../config/environment';
// global _gaq //

export default Ember.ArrayController.extend({
  needs: [ "proposal", "application" ],
  proposal: Ember.computed.alias("controllers.proposal.model"),
  isOpen: Ember.computed.alias("controllers.proposal.isOpen"),
  currentUser: Ember.computed.alias("controllers.application.session.currentUser"),

  myVote: function() {
    return this.get("model").filterBy("usurped", false).filterBy("withdrawn", false).findBy("user", this.get("currentUser"));
  }.property("model.@each.user", "currentUser"),
  canVote: function() {
    return (!this.get("myVote"));
  }.property("myVote"),
  actions: {
    registerVote: function(type) {
      if(this.get("canVote")) {
        var _this = this;
        var p = _this.get("proposal");
        this.set("justVoted", true);
        Ember.$.post(ENV.api + "/votes", {
          vote: {
            type: type,
            proposal_id: p.get('id'),
          },
        }).then(function() {
          _this.send("log", "votes", type);
        });
        _this.send("randomProposal", p.get("id"));
      }
    },
    withdrawVote: function() {
      var _this = this;
      Ember.$.post(ENV.api + "/votes/" + this.get("myVote.id") + "/withdraw",
      {}, function(data) {
        _this.store.pushPayload('proposal', data);
      });
    },
  },

});

import Ember from "ember";
import ENV from '../../config/environment';
// global _gaq //

export default Ember.ArrayController.extend({
  needs: [ "proposal", "application" ],
  proposal: Ember.computed.alias("controllers.proposal.model"),
  isOpen: Ember.computed.alias("controllers.proposal.isOpen"),
  currentUser: Ember.computed.alias("controllers.application.currentUser"),

  myVote: function() {
    return this.get("model").filterBy("usurped", false).filterBy("withdrawn", false).findBy("user", this.get("currentUser"));
  }.property("model.@each.user", "currentUser"),
  canVote: function() {
    return (!this.get("myVote"));
  }.property("myVote"),
  actions: {
    registerVote: function(type) {
      if(this.get("canVote")) {
        var _this = this;
        var p = _this.get("proposal");
        this.set("justVoted", true);
        Ember.$.post(ENV.api + "/votes", {
          vote: {
            type: type,
            proposal_id: p.get('id'),
          },
        }).then(function() {
          _this.send("log", "votes", type);
        });
        _this.send("randomProposal", p.get("id"));
      }
    },
    withdrawVote: function() {
      var _this = this;
      Ember.$.post(ENV.api + "/votes/" + this.get("myVote.id") + "/withdraw",
      {}, function(data) {
        _this.store.pushPayload('proposal', data);
      });
    },
  },

});

Fix the vote buttons which weren't appearing when signed in


import _ from 'lodash';
import path from 'path';

export default (pattern) => {
    const filename = pattern.to;

    return pattern.toType !== 'file' && (
        path.extname(filename) === '' ||
        _.last(filename) === path.sep ||
        _.last(filename) === '/' ||
        pattern.toType === 'dir'
    );
};

import _ from 'lodash';
import path from 'path';

export default (pattern) => {
    const filename = pattern.to || '';

    return pattern.toType !== 'file' && (
        path.extname(filename) === '' ||
        _.last(filename) === path.sep ||
        _.last(filename) === '/' ||
        pattern.toType === 'dir'
    );
};

Fix ERROR in Path must be a string. Received undefined


// Publication who send back everything, use it carrefully
Meteor.publish('games', function() {
	return Games.find();
});

// Publication who send back the last 3 live games
Meteor.publish('last3LiveGames', function() {
	return Games.find({
		state: {
			$nin: ['gameEnded', 'notStarted']
		},
		privateGame: false
	}, {
		limit: 3
	});
});

// Publication who send back the last 3 ended games
Meteor.publish('last3EndedGames', function() {
	return Games.find({
		state: 'gameEnded',
		privateGame: false
	}, {
		limit: 3
	});
});

// Send back one game
Meteor.publish('oneGame', function(gameId) {
	return Games.find({
		_id: gameId
	});
});

// Send back only the user's games
Meteor.publish('myGames', function(userId) {
	return Games.find({
		userId: userId
	});
});

// Send back only the games that have been created less than 7 days ago
Meteor.publish('liveGames', function() {
	return Games.find({
		privateGame: false,
		state: {
			$nin: ['notStarted']
		}
	});
});

// Publication who send back everything, use it carrefully
Meteor.publish('games', function() {
	return Games.find();
});

// Publication who send back the last 3 live games
Meteor.publish('last3LiveGames', function() {
	return Games.find({
		gameState: {
			$nin: ['gameEnded', 'notStarted']
		},
		privateGame: false
	}, {
		limit: 3,
		sort: {
			createdAt: -1
		}
	});
});

// Publication who send back the last 3 ended games
Meteor.publish('last3EndedGames', function() {
	return Games.find({
		gameState: 'gameEnded',
		privateGame: false
	}, {
		limit: 3,
		sort: {
			createdAt: -1
		}
	});
});

// Send back one game
Meteor.publish('oneGame', function(gameId) {
	return Games.find({
		_id: gameId
	});
});

// Send back only the user's games
Meteor.publish('myGames', function(userId) {
	return Games.find({
		userId: userId
	});
});

// Send back only the games that have been created less than 7 days ago
Meteor.publish('liveGames', function() {
	return Games.find({
		privateGame: false,
		state: {
			$nin: ['notStarted']
		}
	});
});

Update publications for the home page


---
---

{% include bower_components/moment/min/moment.min.js %}
{% include bower_components/fullcalendar/dist/fullcalendar.min.js %}
{% include js/jquery.jqgoogleforms.min.js %}
{% include bower_components/smoothstate/jquery.smoothState.min.js %}
{% include bower_components/lunr.js/lunr.min.js %}
{% include js/modernizr.js %}
{% include js/scripts.js %}
{% include js/lunrfeed.js %}
{% include js/calendar.js %}

---
---

{% include js/modernizr.js %}
{% include bower_components/moment/min/moment.min.js %}
{% include bower_components/fullcalendar/dist/fullcalendar.min.js %}
{% include js/jquery.jqgoogleforms.min.js %}
{% include bower_components/smoothstate/jquery.smoothState.min.js %}
{% include bower_components/lunr.js/lunr.min.js %}
{% include js/scripts.js %}
{% include js/lunrfeed.js %}
{% include js/calendar.js %}

Move modernizr to front of js


function(doc) {
    if(doc.doc_type == "CommCareCase") {
        if (doc.indices) {
            for (var i = 0; i < doc.indices.length; i++) {
                emit([doc.domain, doc._id, "index"], doc.indices[i]);

                var reverse_index = {};
                for (var key in doc.indices[i]){
                    if (doc.indices[i].hasOwnProperty(key)){
                        reverse_index[key] = doc.indices[i][key];
                    }
                }
                reverse_index.referenced_id = doc._id;
                reverse_index.relationship = reverse_index.relationship || "child";

                // Emit "child" relationship if there is no relationship set
                var relationship = doc.indices[i].relationship || "child";
                emit([doc.domain, doc.indices[i].referenced_id, "reverse_index", relationship],
                     reverse_index);
            }
        }
    }
}

function(doc) {
    if(doc.doc_type == "CommCareCase") {
        if (doc.indices) {
            for (var i = 0; i < doc.indices.length; i++) {
                var index = {};
                for (var key in doc.indices[i]){
                    if (doc.indices[i].hasOwnProperty(key)){
                        index[key] = doc.indices[i][key];
                    }
                }
                index.relationship = index.relationship || "child";
                emit([doc.domain, doc._id, "index"], index);

                var reverse_index = {};
                for (var key in doc.indices[i]){
                    if (doc.indices[i].hasOwnProperty(key)){
                        reverse_index[key] = doc.indices[i][key];
                    }
                }
                reverse_index.referenced_id = doc._id;
                reverse_index.relationship = reverse_index.relationship || "child";

                // Emit "child" relationship if there is no relationship set
                var relationship = doc.indices[i].relationship || "child";
                emit([doc.domain, doc.indices[i].referenced_id, "reverse_index", relationship],
                     reverse_index);
            }
        }
    }
}

Include relationship on forwards index


angular.module('controllers', [])

.controller('NavCtrl', function ($scope, $state) {

  $scope.state = $state.current.name

})

.controller('GalleryCtrl', function ($scope, json, responsive) {

  var data = json.data
  var width = 200
  var max = 5
  var resizeTimer

  $scope.resolution = 0

  $scope.columns = function () {
    console.log($scope.resolution);
    return new Array($scope.resolution);
  }

  $scope.chunk = function (column, resolution) {
    var round = -1
    return data.slice(0).filter(function (x, i, a) {
      round++
      if (round >= resolution) round = 0
      if (round == column) return true
    })
  }

  responsive.run(
    function () {
      $scope.$apply()
    },
    function (resolution) {
      var number = Math.round($(window).width() / width)
      if (number > max) number = max
      $scope.resolution = number
    }
  )

})

    }), 250)

  })

})

.controller('PerformanceCtrl', function ($scope) {

})

.controller('ContactCtrl', function ($scope) {

})

angular.module('controllers', [])

.controller('NavCtrl', function ($scope, $state) {

  $scope.state = $state.current.name

})

.controller('GalleryCtrl', function ($scope, json, responsive) {

  var data = json.data
  var width = 200
  var max = 5
  var resizeTimer

  $scope.resolution = 0

  $scope.columns = function () {
    console.log($scope.resolution);
    return new Array($scope.resolution);
  }

  $scope.chunk = function (column, resolution) {
    var round = -1
    return data.slice(0).filter(function (x, i, a) {
      round++
      if (round >= resolution) round = 0
      if (round == column) return true
    })
  }

  responsive.run(
    function () {
      $scope.$apply()
    },
    function (resolution) {
      var number = Math.round($(window).width() / width)
      if (number > max) number = max
      $scope.resolution = number
    }
  )

})

  })

})

.controller('PerformanceCtrl', function ($scope) {

})

.controller('ContactCtrl', function ($scope) {

})

Update gallery ctrl with responsive service


exports.up = knex =>
  knex.schema.createTable('user', (table) => {
    table.increments('id');
    table.string('username').unique().notNullable();
    table.string('name');
    table.timestamp('created_at').defaultTo(knex.fn.now());
    table.index('username');
  }).createTable('password', (table) => {
    table.increments('id');
    table.integer('user_id').references('user.id').notNullable().onDelete('CASCADE');
    table.string('password_hash').notNullable();
    table.timestamp('updated_at').defaultTo(knex.fn.now());
    table.boolean('valid').defaultsTo(true);
  }).createTable('email', (table) => {
    table.increments('id');
    table.integer('user_id').references('user.id').notNullable().onDelete('CASCADE');
    table.string('address').unique().notNullable();
    table.boolean('verified').defaultsTo(false);
  }).createTable('role', (table) => {
    table.increments('id');
    table.string('name').unique().notNullable();
  }).createTable('user_role', (table) => {
    table.integer('user_id').references('user.id').notNullable();
    table.integer('role_id').references('role.id').notNullable();
  });

exports.down = (knex, Promise) =>
  Promise.all([
    knex.schema.dropTable('user'),
    knex.schema.dropTable('password'),
    knex.schema.dropTable('email'),
    knex.schema.dropTable('role'),
    knex.schema.dropTable('user_role'),
  ]);

exports.up = knex =>
  knex.schema.createTable('user', (table) => {
    table.increments('id');
    table.string('username').unique().notNullable();
    table.string('name');
    table.boolean('is_admin');
    table.timestamp('created_at').defaultTo(knex.fn.now());
    table.index('username');
  })
  .createTable('password', (table) => {
    table.increments('id');
    table.integer('user_id').references('user.id').notNullable().onDelete('CASCADE');
    table.string('password_hash').notNullable();
    table.timestamp('updated_at').defaultTo(knex.fn.now());
    table.boolean('valid').defaultsTo(true);
  })
  .createTable('email', (table) => {
    table.increments('id');
    table.integer('user_id').references('user.id').notNullable().onDelete('CASCADE');
    table.string('address').unique().notNullable();
    table.boolean('verified').defaultsTo(false);
  })
  .createTable('role', (table) => {
    table.increments('id');
    table.string('name').unique().notNullable();
  })
  .createTable('user_role', (table) => {
    table.integer('user_id').references('user.id').notNullable();
    table.integer('role_id').references('role.id').notNullable();
  });

exports.down = (knex, Promise) =>
  Promise.all([
    knex.schema.dropTable('user'),
    knex.schema.dropTable('password'),
    knex.schema.dropTable('email'),
    knex.schema.dropTable('role'),
    knex.schema.dropTable('user_role'),
  ]);

Add is_admin property to user migration.


import React from 'react';
import {Router} from 'react-router'
import routes from '../shared/routes/Routes';
import configureStore from '../shared/redux/store/Store';
import {Provider} from 'react-redux';
import {syncHistoryWithStore} from 'react-router-redux';
import {browserHistory} from 'react-router'
import ReactDOM from 'react-dom';
import injectTapEventPlugin from 'react-tap-event-plugin';
import lightBaseTheme from 'material-ui/styles/baseThemes/lightBaseTheme';
import MuiThemeProvider from 'material-ui/styles/MuiThemeProvider';
import getMuiTheme from 'material-ui/styles/getMuiTheme';

import './styles/Styles.less';

injectTapEventPlugin();

const store = configureStore();
const history = syncHistoryWithStore(browserHistory, store);

ReactDOM.render(
    <Provider store={store}>
        <MuiThemeProvider muiTheme={getMuiTheme(lightBaseTheme)}>
            <Router history={history} routes={routes}/>
        </MuiThemeProvider>
    </Provider>,
    document.getElementById('app')
);

import React from 'react';
import {Router} from 'react-router'
import routes from '../shared/routes/Routes';
import configureStore from '../shared/redux/store/Store';
import {Provider} from 'react-redux';
import {syncHistoryWithStore} from 'react-router-redux';
import {browserHistory} from 'react-router'
import ReactDOM from 'react-dom';
import injectTapEventPlugin from 'react-tap-event-plugin';
import lightBaseTheme from 'material-ui/styles/baseThemes/lightBaseTheme';
import MuiThemeProvider from 'material-ui/styles/MuiThemeProvider';
import getMuiTheme from 'material-ui/styles/getMuiTheme';
import numbro from 'numbro';
import { numbroLocalizer } from 'redux-autoform';

import './styles/Styles.less';

numbroLocalizer(numbro);
injectTapEventPlugin();

const store = configureStore();
const history = syncHistoryWithStore(browserHistory, store);

ReactDOM.render(
    <Provider store={store}>
        <MuiThemeProvider muiTheme={getMuiTheme(lightBaseTheme)}>
            <Router history={history} routes={routes}/>
        </MuiThemeProvider>
    </Provider>,
    document.getElementById('app')
);

Fix bug with input int and float not letting user enter nothing


;(function(exports) {
  var Game = function(autoFocus) {
    var coq = new Coquette(this, "canvas", 500, 150, "#000", autoFocus);

    coq.entities.create(Person, { pos:{ x:250, y:40 }, color:"#099" }); // paramour
    coq.entities.create(Person, { pos:{ x:256, y:110 }, color:"#f07", // player
      update: function() {
        if (coq.inputter.isDown(coq.inputter.UP_ARROW)) {
          this.pos.y -= 0.4;
        }
      },
      collision: function(other) {
        other.pos.y = this.pos.y; // follow the player
      }
    });
  };

  var Person = function(_, settings) {
    for (var i in settings) {
      this[i] = settings[i];
    }
    this.size = { x:9, y:9 };
    this.draw = function(ctx) {
      ctx.fillStyle = settings.color;
      ctx.fillRect(this.pos.x, this.pos.y, this.size.x, this.size.y);
    };
  };

  exports.Game = Game;
})(this);

;(function(exports) {
  var SimpleGame = function(autoFocus) {
    var coq = new Coquette(this, "simple-canvas", 500, 150, "#000", autoFocus);

    coq.entities.create(Person, { center: { x:250, y:40 }, color:"#099" }); // paramour
    coq.entities.create(Person, { center: { x:256, y:110 }, color:"#f07", // player
      update: function() {
        if (coq.inputter.isDown(coq.inputter.UP_ARROW)) {
          this.center.y -= 0.4;
        }
      },
      collision: function(other) {
        other.center.y = this.center.y; // follow the player
      }
    });
  };

  var Person = function(_, settings) {
    for (var i in settings) {
      this[i] = settings[i];
    }
    this.size = { x:9, y:9 };
    this.draw = function(ctx) {
      ctx.fillStyle = settings.color;
      ctx.fillRect(this.center.x, this.center.y, this.size.x, this.size.y);
    };
  };

  exports.SimpleGame = SimpleGame;
})(this);

Update simple demo to use center, not pos


/* global angular */
(function () {
    "use strict";
    angular
        .module("helloApp.controllers", [])
        .controller("signUpCtrl", signUpController);

    signUpController.$inject = ["$scope", "$http", "$log", "toastr"];
    function signUpController ($scope, $http, $log, toast) {
        $scope.doSignup = function (model) {
            $http.post("/signup", model)
                .then(function(response) {
                    toast.success('User with id: ' + response.data.Id + ' created', 'Alls ok');
                });
        }

        $scope.testFunction = function () {
            return true;
        };
    }
})();

/* global angular */
(function () {
    "use strict";
    angular
        .module("helloApp.controllers", [])
        .controller("signUpCtrl", signUpController);

    signUpController.$inject = ["$scope", "$http", "$log", "toastr"];
    function signUpController ($scope, $http, $log, toast) {
        $scope.doSignup = function (model) {
            $http.post("/signup", model)
                .then(function(response) {
                    toast.success("User with id: " + response.data.Id + " created", "Alls ok");
                    $scope.model = null;
                });
        }

        $scope.testFunction = function () {
            return true;
        };
    }
})();

Clear form scope on success


import thunkMiddleware from 'redux-thunk'
import { createStore, applyMiddleware, compose } from 'redux'
import rootReducer from './reducers'

// import { createLogger } from 'redux-logger'
// const loggerMiddleware = createLogger()

export default createStore(
  rootReducer /* preloadedState, */,
  compose(
    applyMiddleware(
      thunkMiddleware // lets us dispatch() functions
      // loggerMiddleware // log actions
    ),
    window.__REDUX_DEVTOOLS_EXTENSION__ && window.__REDUX_DEVTOOLS_EXTENSION__()
  )
)

import thunkMiddleware from 'redux-thunk'
import { createStore, applyMiddleware, compose } from 'redux'
import rootReducer from './reducers'

// import { createLogger } from 'redux-logger'
// const loggerMiddleware = createLogger()
const composeEnhancers = window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__ || compose
export default createStore(
  rootReducer,
  composeEnhancers(
    applyMiddleware(
      thunkMiddleware // lets us dispatch() functions
      // loggerMiddleware // log actions
    )
  )
)

Modify redux tool loading to work w/Jest


import isEqual from 'lodash/isEqual'

import { collectionAttachedSchema } from '../schemaHelper'

const collectionLabel = collection =>
  collection._name ? `collection named "${collection._name}"` : '#{this}'

function assetTestMongo() {
  const collection = this._obj
  this.assert(
    collection.constructor && collection.constructor.name === 'TestMongo',
    'expected an instance of TestMongo',
    'expected not an instance of TestMongo',
  )
}

export const plugin = (chai) => {
  const Assertion = chai.Assertion

  Assertion.addProperty('schema', function schemaProperty() {
    assetTestMongo()
    const collection = this._obj
    const label = collectionLabel(collection)
    this.assert(
      collectionAttachedSchema(collection),
      `expected ${label} to have an attached schema`,
      `expected ${label} to not have an attached schema`,
    )
  })

  Assertion.addMethod('index', function indexMethod(fields) {
    assetTestMongo()
    const collection = this._obj
    const label = collectionLabel(collection)
    const matchingIndex = collection.indexes.find((idx) => {
      const keys = Object.entries(idx.keys)
      return fields.every((f, i) => typeof f === 'string' ? f === keys[i][0] : isEqual(Object.entries(f)[0], keys[i]))
    })
    this.assert(
      matchingIndex,
      `expected ${label} to have an index #{exp} from #{act}`,
      `expected ${label} to not have an index #{exp}`,
      fields,
      collection.indexes.map(idx => idx.keys),
    )
  })

}

import isEqual from 'lodash/isEqual'

import { collectionAttachedSchema } from '../schemaHelper'

const collectionLabel = collection =>
  collection._name ? `collection named "${collection._name}"` : '#{this}'

const assetTestMongo = (collection, chai) => {
  chai.assert(
    collection.constructor && collection.constructor.name === 'TestMongo',
    'expected an instance of TestMongo',
    'expected not an instance of TestMongo',
  )
}

export const plugin = (chai) => {
  const Assertion = chai.Assertion

  Assertion.addProperty('schema', function schemaProperty() {
    const collection = this._obj
    assetTestMongo(collection, this)
    const label = collectionLabel(collection)
    this.assert(
      collectionAttachedSchema(collection),
      `expected ${label} to have an attached schema`,
      `expected ${label} to not have an attached schema`,
    )
  })

  Assertion.addMethod('index', function indexMethod(fields) {
    const collection = this._obj
    assetTestMongo(collection, this)
    const label = collectionLabel(collection)
    const matchingIndex = collection.indexes.find((idx) => {
      const keys = Object.entries(idx.keys)
      return fields.every((f, i) => typeof f === 'string' ? f === keys[i][0] : isEqual(Object.entries(f)[0], keys[i]))
    })
    this.assert(
      matchingIndex,
      `expected ${label} to have an index #{exp} from #{act}`,
      `expected ${label} to not have an index #{exp}`,
      fields,
      collection.indexes.map(idx => idx.keys),
    )
  })

}

Check in Chai assertions that they have been given TestMongo


import Ember from 'ember';

export default Ember.Component.extend({
  tagName: 'input',

  setupBootstrapDatepicker: function() {
    var self = this,
        element = this.$();

    element.
      datepicker({
        autoclose: this.get('autoclose') || true,
        format: this.get('format') || 'dd.mm.yyyy',
        weekStart: this.get('weekStart') || 1,
        todayHighlight: this.get('todayHighlight') || false,
        todayBtn: this.get('todayBtn') || false
      }).
      on('changeDate', function(event) {
        Ember.run(function() {
          self.didSelectDate(event);
        });
      });

    if (value) {
      element.datepicker('setDate', new Date(this.get('value')));
    };
  }.on('didInsertElement'),

  teardownBootstrapDatepicker: function() {
    // no-op
  }.on('willDestroyElement'),

  didSelectDate: function(event) {
    var date = this.$().datepicker('getUTCDate');
    this.set('value', date.toISOString());
  }
});

import Ember from 'ember';

export default Ember.Component.extend({
  tagName: 'input',

  setupBootstrapDatepicker: function() {
    var self = this,
        element = this.$(),
        value = this.get('value');

    element.
      datepicker({
        autoclose: this.get('autoclose') || true,
        format: this.get('format') || 'dd.mm.yyyy',
        weekStart: this.get('weekStart') || 1,
        todayHighlight: this.get('todayHighlight') || false,
        todayBtn: this.get('todayBtn') || false
      }).
      on('changeDate', function(event) {
        Ember.run(function() {
          self.didSelectDate(event);
        });
      });

    if (value) {
      element.datepicker('setDate', new Date(value));
    };
  }.on('didInsertElement'),

  teardownBootstrapDatepicker: function() {
    // no-op
  }.on('willDestroyElement'),

  didSelectDate: function(event) {
    var date = this.$().datepicker('getUTCDate');
    this.set('value', date.toISOString());
  }
});

Fix bug when the input field can't access value


import queryString from 'query-string';

export const getFromQuery = (key, defaultValue = null) => {
  let value = queryString.parse(location.search)[key];
  return value || defaultValue;
};

export const getAttributesFromQuery = (exclude) => {
  // Exclude parameter is used to exclude other query string parameters than
  // product attribute filters.
  const urlParams = queryString.parse(location.search);
  let attributes = [];
  Object.keys(urlParams).forEach(key => {
    if (!exclude.includes(key)) {
      if (Array.isArray(urlParams[key])) {
        const values = urlParams[key];
        values.map((valueSlug) => {
          attributes.push(`${key}:${valueSlug}`);
        });
      } else {
        const valueSlug = urlParams[key];
        attributes.push(`${key}:${valueSlug}`);
      }
    }
  });
  return attributes;
};

export const ensureAllowedName = (name, allowed) => {
  let origName = name;
  if (name && name[0] === '-') {
    name = name.substr(1, name.length);
  }
  return name in allowed ? origName : null;
};

import queryString from 'query-string';

export const getFromQuery = (key, defaultValue = null) => {
  let value = queryString.parse(location.search)[key];
  return value || defaultValue;
};

export const getAttributesFromQuery = (exclude) => {
  // Exclude parameter is used to exclude other query string parameters than
  // product attribute filters.
  const urlParams = queryString.parse(location.search);
  let attributes = [];
  Object.keys(urlParams).forEach(key => {
    if (!exclude.includes(key)) {
      if (Array.isArray(urlParams[key])) {
        const values = urlParams[key];
        values.map((valueSlug) => {
          attributes.push(`${key}:${valueSlug}`);
        });
      } else {
        const valueSlug = urlParams[key];
        attributes.push(`${key}:${valueSlug}`);
      }
    }
  });
  return attributes;
};

export const ensureAllowedName = (name, allowed) => {
  let origName = name;
  if (name && name[0] === '-') {
    name = name.substr(1, name.length);
  }
  return allowed.indexOf(name) > -1 ? origName : null;
};

Fix checking if array contains a string


module.exports = {
	options			: {
		buildDir		: "build/releases",
		cacheDir		: "build/cache",
		version			: "<%= package.config['node-webkit-version'] %>"
	},
	win				: {
		options			: { win: true },
		src				: "build/tmp/**"
	},
	osx				: {
		options			: { osx: true },
		src				: "build/tmp/**"
	},
	linux32			: {
		options			: { linux32: true },
		src				: "build/tmp/**"
	},
	linux64			: {
		options			: { linux64: true },
		src				: "build/tmp/**"
	}
};

module.exports = {
	options			: {
		buildDir		: "build/releases",
		cacheDir		: "build/cache",
		version			: "<%= package.config['node-webkit-version'] %>"
	},
	win				: {
		options			: { win: true },
		src				: "build/tmp/**"
	},
	osx				: {
		options			: { osx: true, macIcns: "build/resources/icons/icon-512.icns" },
		src				: "build/tmp/**"
	},
	linux32			: {
		options			: { linux32: true },
		src				: "build/tmp/**"
	},
	linux64			: {
		options			: { linux64: true },
		src				: "build/tmp/**"
	}
};

Include the app icon in the osx build


import _ from 'underscore'
import search from 'api/lib/elasticsearch.coffee'
import { matchAll } from 'api/apps/search/queries'
const { NODE_ENV } = process.env

// GET /api/search
export const index = (req, res, next) => {
  const env = NODE_ENV === 'production' ? 'production' : 'staging'
  const index = _.map(req.query.type.split(','), term => {
    return `${term}_${env}`
  }).join(',')
  search.client.search({
    index,
    body: {
      query: matchAll(req.query.term)
    }},
    (error, response) => {
      if (error) {
        return console.log(`nothing matched for: ${req.query.term}`)
      }
      return res.send(response.hits)
    }
  )
}

import _ from 'underscore'
import search from 'api/lib/elasticsearch.coffee'
import { matchAll } from 'api/apps/search/queries'
const { NODE_ENV } = process.env

// GET /api/search
export const index = (req, res, next) => {
  const env = NODE_ENV === 'production' ? 'production' : 'staging'
  let index = ''
  if (req.query.type) {
    index = _.map(req.query.type.split(','), term => {
      return `${term}_${env}`
    }).join(',')
  }
  search.client.search({
    index,
    body: {
      query: matchAll(req.query.term)
    }},
    (error, response) => {
      if (error) {
        return console.log(`nothing matched for: ${req.query.term}`)
      }
      return res.send(response.hits)
    }
  )
}

Fix test by allowing empty index


'use strict';

describe.only('babelmark responder app', function () {
  var app;

  var PORT    = 5005;
  var request = require('supertest')('http://127.0.0.1:' + PORT);

  before(function (done) {
    app = require('child_process').execFile(
      'node',
      [ '../support/babelmark-responder.js' ],
      {
        cwd: __dirname,
        env: Object.assign({}, process.env, { PORT: PORT })
      }
    );

    // Wait a bit until app bind port
    setTimeout(done, 1000);
  });

  it('ping root', function () {
    return request
      .get('/')
      .expect(200)
      .expect(/<!DOCTYPE html>/i);
  });

  it('do request', function () {
    return request
      .get('/?text=foo')
      .expect(200)
      .expect({
        html: '
foo
\n',
        name: 'markdown-it',
        version: require('../package.json').version
      });
  });

  after(function () {
    if (app) app.kill();
  });
});

'use strict';

describe('babelmark responder app', function () {
  var app;

  var PORT    = 5005;
  var request = require('supertest')('http://127.0.0.1:' + PORT);

  before(function (done) {
    app = require('child_process').execFile(
      'node',
      [ '../support/babelmark-responder.js' ],
      {
        cwd: __dirname,
        env: Object.assign({}, process.env, { PORT: PORT })
      }
    );

    // Wait a bit until app bind port
    setTimeout(done, 1000);
  });

  it('ping root', function () {
    return request
      .get('/')
      .expect(200)
      .expect(/<!DOCTYPE html>/i);
  });

  it('do request', function () {
    return request
      .get('/?text=foo')
      .expect(200)
      .expect({
        html: '
foo
\n',
        name: 'markdown-it',
        version: require('../package.json').version
      });
  });

  after(function () {
    if (app) app.kill();
  });
});

Enable tests, disabled in prev commit


exports.name = 'banuser';
exports.hidden = true;
exports.enabled = true;
exports.matchStart = true;
exports.handler = function(data) {
    //my room is idiots
    //if (config.database.usedb && admincheck(data.userid)) {
    if (config.database.usedb && data.userid == config.admin) {
        //Get name and userid
        var givenname = data.text.substring(8);
        client.query('SELECT userid FROM (SELECT * FROM ' + config.database.dbname + '.' + config.database.tablenames.user
            + ' WHERE username LIKE ?) a ORDER BY lastseen DESC', [givenname], function select(error, results, fields) {
            if (results != null && results.length > 0) {
                addToBanList(results[0]['userid'], givenname, data.name);
            }
        });
    }
}

function addToBanList(userid, name, bannedby) {
    client.query('INSERT INTO ' + config.database.dbname + '.' + config.database.tablenames.user + ' SET userid = ?, banned_by = ?, timestamp = NOW()',
            [userid, bannedby]);
    bot.speak(name + ' (UID ' + userid + ') has been banned by ' + bannedby + '.');
    bot.boot(userid, 'You have been banned by ' + bannedby + '.');
}

exports.name = 'banuser';
exports.hidden = true;
exports.enabled = true;
exports.matchStart = true;
exports.handler = function(data) {
    //my room is idiots
    //if (config.database.usedb && admincheck(data.userid)) {
    if (config.database.usedb && data.userid == config.admin) {
        //Get name and userid
        var givenname = data.text.substring(8);
        client.query('SELECT userid FROM (SELECT * FROM ' + config.database.dbname + '.' + config.database.tablenames.user
            + ' WHERE username LIKE ?) a ORDER BY lastseen DESC', [givenname], function select(error, results, fields) {
            if (results != null && results.length > 0) {
                addToBanList(results[0]['userid'], givenname, data.name);
            }
        });
    }
}

function addToBanList(userid, name, bannedby) {
    client.query('INSERT INTO ' + config.database.dbname + '.' + config.database.tablenames.banned + ' SET userid = ?, banned_by = ?, timestamp = NOW()',
            [userid, bannedby]);
    bot.speak(name + ' (UID ' + userid + ') has been banned by ' + bannedby + '.');
    bot.boot(userid, 'You have been banned by ' + bannedby + '.');
}

Fix banning to insert to correct table


if (typeof global === "object") {
    global.expect = require("chai").expect
    require("chai").use(require("sinon-chai"))
    global.sinon = require("sinon")
} else if (typeof window === "object") {
    window.expect = chai.expect
    chai.use("sinon-chai")
}

if (typeof global === "object") {
    global.expect = require("chai").expect
    require("chai").use(require("sinon-chai"))
    global.sinon = require("sinon")
} else if (typeof window === "object") {
    window.expect = chai.expect
}

Disable sinon in browser tests.


var Backbone = require('backbone');

var ModalContainerView = Backbone.View.extend({
  events: {
    'mousedown': 'closeOnOutsideClick'
  },

  initialize: function() {

  },

  closeOnOutsideClick: function(e) {
    if (this.modal && this.$el.is(e.target) && this.modal.closable) {
      this.closeCurrentModal();
    }
  },

  showBackground: function() {
    this.$el.removeClass('disabled');
  },

  hideBackground: function() {
    this.$el.addClass('disabled');
  },

  empty: function() {
    this.$el.html('');
  },

  setCurrentModal: function(ModalType, path) {
    // Load HTML then create view object asynchronously
    $.get(path).done(function(html) {
      this.$el.html(html);
      this.showBackground();

      this.modal = new ModalType({ el: this.$('.js-modal') });
      this.modal.on('close', this.closeCurrentModal.bind(this));
    }.bind(this));
  },

  closeCurrentModal: function() {
    this.hideBackground();
    this.empty();
    this.modal.remove();
    this.modal = null;
  }
});

module.exports = ModalContainerView;

var Backbone = require('backbone');

var ModalContainerView = Backbone.View.extend({
  events: {
    'mousedown': 'closeOnOutsideClick'
  },

  initialize: function() {

  },

  closeOnOutsideClick: function(e) {
    if (this.modal && this.$el.is(e.target) && this.modal.closable) {
      this.closeCurrentModal();
    }
  },

  showBackground: function() {
    this.$el.removeClass('disabled');
  },

  hideBackground: function() {
    this.$el.addClass('disabled');
  },

  empty: function() {
    this.$el.html('');
  },

  setCurrentModal: function(ModalType, path) {
    var topMargin = 150;
    var scrollTop = $(window).scrollTop();

    // Load HTML then create view object asynchronously
    $.get(path).done(function(html) {
      this.$el.html(html);
      this.showBackground();

      this.modal = new ModalType({ el: this.$('.js-modal') });
      this.modal.$el.css('marginTop', topMargin + scrollTop);
      this.modal.on('close', this.closeCurrentModal.bind(this));
    }.bind(this));
  },

  closeCurrentModal: function() {
    this.hideBackground();
    this.empty();
    this.modal.remove();
    this.modal = null;
  }
});

module.exports = ModalContainerView;

Make sure modals are correctly positioned


import config from './rollup.config';

config.dest = 'dist/coffee-lex.umd.js';
config.format = 'umd';
config.moduleName = 'coffeelex';

export default config;

import config from './rollup.config';

config.dest = 'dist/coffee-lex.umd.js';
config.format = 'umd';
config.moduleName = 'coffeelex';
config.outro = 'Object.defineProperty(exports, \'__esModule\', { value: true });';

export default config;

Mark the UMD module as an ES module.


/*!
 * YUI Onyx
 * Copyright 2011 Yahoo! Inc.
 * Licensed under the BSD license.
 */

/**
 * Returns a Vows topic function, which will return
 * a MockWritableStream, a fake Writable Stream.
 *
 * Implements writeHead, like a http.ServerResponse.
 * Only the (status, headers) signature is implemented.
 *
 * Verify with these instance variables:
 *
 * - $store: Written data, from write
 * - $status: HTTP status code, from writeHead
 * - $headers: HTTP header object, from writeHead
 *
 * @return {Function} Vows topic function
 */
module.exports = function () {
    return function () {
        function MockWritableStream () {
            this.$store = "";
        }
        var proto = MockWritableStream.prototype;
        proto.writeHead = function (status, /* msg, */ headers) {
            this.$status = status;
            this.$headers = headers;
        };
        proto.end = function (input) {
            if (input) this.write(input);
            this.$end = true;
        };
        proto.write = function (input) {
            if (this.$end) throw new Error("Unable to write: closed.");
            if (Buffer.isBuffer(input)) {
                this.$store += input.toString("utf8");
            } else {
                this.$store += input;
            }
        };
        return new MockWritableStream;
    }
}

/*!
 * YUI Onyx
 * Copyright 2011 Yahoo! Inc.
 * Licensed under the BSD license.
 */

/**
 * Returns a Vows topic function, which will return
 * a MockWritableStream, a fake Writable Stream.
 *
 * Implements writeHead, like a http.ServerResponse.
 * Only the (status, headers) signature is implemented.
 *
 * Verify with these instance variables:
 *
 * - $store: Written data, from write
 * - $status: HTTP status code, from writeHead
 * - $headers: HTTP header object, from writeHead
 *
 * @return {Function} Vows topic function
 */
module.exports = function () {
    return function () {
        function MockWritableStream () {
            this.$store = "";
        }

        var proto = MockWritableStream.prototype;

        proto.writeHead = function (status, /* msg, */ headers) {
            this.$status = status;
            this.$headers = headers;
        };

        proto.end = function (input) {
            if (input) {
                this.write(input);
            }
            this.$end = true;
        };

        proto.write = function (input) {
            if (this.$end) {
                throw new Error("Unable to write: closed.");
            }

            if (Buffer.isBuffer(input)) {
                this.$store += input.toString("utf8");
            } else {
                this.$store += input;
            }
        };

        return new MockWritableStream();
    };
};

Add semicolons and blocks to mock Writable Stream.


import {assert} from 'chai';
import R from 'ramda';
import transformHead from '../../src/util/transform-head.js';

describe('#transformHead', () => {
  it('should transform the head of a string', () => {
    assert.equal(transformHead(R.toUpper, 'hello'), 'Hello');
    assert.equal(transformHead(R.toLower, 'Hello'), 'hello');
  });
});

import {assert} from 'chai';
import upperCase from '../../src/util/upper-case.js';
import lowerCase from '../../src/util/lower-case.js';
import transformHead from '../../src/util/transform-head.js';

describe('#transformHead', () => {
  it('should transform the head of a string', () => {
    assert.equal(transformHead(upperCase, 'hello'), 'Hello');
    assert.equal(transformHead(lowerCase, 'Hello'), 'hello');
  });
});

Refactor transformHead test to use custom functions


/*exported axe, commons */
/*global axeFunction, module, define */
// exported namespace for aXe
var axe = axe || {};
axe.version = '<%= pkg.version %>';

if (typeof define === 'function' && define.amd) {
	define([], function () {
		'use strict';
		return axe;
	});
}
if (typeof module === 'object' && module.exports && typeof axeFunction.toString === 'function') {
    axe.source = '(' + axeFunction.toString() + ')(this, this.document);';
    module.exports = axe;
}
if (typeof window.getComputedStyle === 'function') {
    window.axe = axe;
}
// local namespace for common functions
var commons;

function SupportError(error) {
	this.name = 'SupportError';
	this.cause = error.cause;
	this.message = `\`${error.cause}\` - feature unsupported in your environment.`;
	if (error.ruleId) {
		this.ruleId = error.ruleId;
		this.message += ` Skipping ${this.ruleId} rule.`;
	}
	this.stack = (new Error()).stack;
}
SupportError.prototype = Object.create(Error.prototype);
SupportError.prototype.constructor = SupportError;

/*exported axe, commons */
/*global axeFunction, module, define */
// exported namespace for aXe
var axe = axe || {};
axe.version = '<%= pkg.version %>';

if (typeof define === 'function' && define.amd) {
	define([], function () {
		'use strict';
		return axe;
	});
}
if (typeof module === 'object' && module.exports && typeof axeFunction.toString === 'function') {
    axe.source = '(' + axeFunction.toString() + ')(typeof window === "object" ? window : this);';
    module.exports = axe;
}
if (typeof window.getComputedStyle === 'function') {
    window.axe = axe;
}
// local namespace for common functions
var commons;

function SupportError(error) {
	this.name = 'SupportError';
	this.cause = error.cause;
	this.message = `\`${error.cause}\` - feature unsupported in your environment.`;
	if (error.ruleId) {
		this.ruleId = error.ruleId;
		this.message += ` Skipping ${this.ruleId} rule.`;
	}
	this.stack = (new Error()).stack;
}
SupportError.prototype = Object.create(Error.prototype);
SupportError.prototype.constructor = SupportError;

Update axe.source to work with Firefox webdriver


#!/usr/bin/env node
// daab login

var program = require('commander');
var auth = require('./auth');
var direct = require("direct-js").DirectAPI.getInstance();

program
  .allowUnknownOption()
  .parse(process.argv);

if (auth.hasToken()) {
  console.log('Already logged in.');
  process.exit(1);
}

direct.setOptions({
  host:'api.direct4b.com', endpoint:'wss://api.direct4b.com/albero-app-server/api'
});
direct.listen();

direct.on('access_token_changed', function(token) {
  auth.setToken(token);
  console.log('logged in.');
  process.exit();
});

#!/usr/bin/env node
// daab login

var program = require('commander');
var auth = require('./auth');
var direct = require("direct-js").DirectAPI.getInstance();
var proxyURL = process.env.HUBOT_DIRECT_PROXY_URL || process.env.HTTPS_PROXY || process.env.HTTP_PROXY;

program
  .allowUnknownOption()
  .parse(process.argv);

if (auth.hasToken()) {
  console.log('Already logged in.');
  process.exit(1);
}

direct.setOptions({
  host:'api.direct4b.com', endpoint:'wss://api.direct4b.com/albero-app-server/api', proxyURL: proxyURL
});
direct.listen();

direct.on('access_token_changed', function(token) {
  auth.setToken(token);
  console.log('logged in.');
  process.exit();
});

Enable proxy settings if a user logs in.


const r = require('../../../db.js');
const i18n = require('i18n');

module.exports.info = {
	aliases: [
		'locale',
		'lang'
	]
};

module.exports.command = (message) => {
	if (message.input) {
		r.table('i18n')
			.insert({
				id: message.inbox,
				lang: message.input
			}, {
				conflict: 'update'
			})
			.run(r.conn, (err) => {
				if (err) {
					message.channel.createMessage(message.__('err_generic'));
				} else {
					message.setLocale(message.input);
					message.channel.createMessage(message.__('locale_set', { locale: message.input }));
				}
			});
	} else {
		message.channel.createMessage(message.__('locale_incorrect', { locales: Object.keys(i18n.getCatalog()).join(', ') }));
	}
};

const r = require('../../../db.js');
const i18n = require('i18n');

module.exports.info = {
	aliases: [
		'locale',
		'lang',
		'i18n'
		'langue',
		'langage'
	]
};

module.exports.command = (message) => {
	if (message.input && Object.keys(i18n.getCatalog()).includes(message.input)) {
		r.table('i18n')
			.insert({
				id: message.inbox,
				lang: message.input
			}, {
				conflict: 'update'
			})
			.run(r.conn, (err) => {
				if (err) {
					message.channel.createMessage(message.__('err_generic'));
				} else {
					message.setLocale(message.input);
					message.channel.createMessage(message.__('locale_set', { locale: message.input }));
				}
			});
	} else {
		message.channel.createMessage(message.__('locale_incorrect', { locales: Object.keys(i18n.getCatalog()).join(', ') }));
	}
};

Check if language is valid


const gulp = require('gulp');
const mocha = require('gulp-mocha');
const tests = require('../test/unit.test');

gulp.task('test', function() {
    return gulp
        .src(tests)
        .pipe(mocha({
            bail: true
        }))
        .once('end', function() {
            process.exit();
        });
});

const gulp = require('gulp');
const mocha = require('gulp-mocha');
const tests = require('../test/unit.test');

gulp.task('test', function() {
    return gulp
        .src(tests)
        .pipe(mocha({
            bail: true,
            "expose-gc": true
        }))
        .once('end', function() {
            process.exit();
        });
});

ADD harmony for gc in tests


/* global config, fs, module, project */

module.exports = function(grunt) {
	grunt.registerTask('cleanup', function() {
		var remFiles = function(dir) {
			fs.readdirSync(dir).forEach(function(file) {
				var loc = dir + file;

				if (file.charAt(0) !== '.' && fs.lstatSync(loc).isFile()) {
					fs.unlinkSync(loc);
				}
			});
		}

		// Ensure the temp directory exists
		fs.emptyDirSync(config.paths.temp);

		// Remove temporary files
		remFiles(config.paths.temp);

		// Remove compiled assets
		remFiles(config.paths.css);
		remFiles(config.paths.js);

		if (project.script.sourceMaps === true) {
			// Create source map directory if needed
			fs.ensureDir(config.paths.maps);

			// Remove source maps
			remFiles(config.paths.maps);
		}
	});
};

/* global config, fs, module, project */

module.exports = function(grunt) {
	grunt.registerTask('cleanup', function() {
		var remFiles = function(dir) {
			fs.readdirSync(dir).forEach(function(file) {
				var loc = dir + file;

				if (file.charAt(0) !== '.' && fs.lstatSync(loc).isFile()) {
					fs.unlinkSync(loc);
				}
			});
		}

		// Ensure the temp directory exists
		fs.emptyDirSync(config.paths.temp);

		// Remove temporary files
		remFiles(config.paths.temp);

		// Remove compiled assets
		remFiles(config.paths.css);
		remFiles(config.paths.js);

		if (project.script.sourceMaps === true) {
			// Create source map directory if needed
			fs.ensureDirSync(config.paths.maps);

			// Remove source maps
			remFiles(config.paths.maps);
		}
	});
};

Make sourcemap directory is synchronously created


var path = require('path');

module.exports = {
    entry: './test/build/index.js',
    output: {
        path: __dirname + "/build",
        filename: "bundle.js",
        publicPath: "./build/"
    },
    bail: true,
    module: {
        loaders: [
            { test: /\.css$/, loader: 'style-loader!css-loader' },
            { test: /\.md$/, loader: 'raw-loader'},
            { test: /\.html$/, loader: "file?name=[name].[ext]" },
            { test: /\.ipynb$/, loader: 'json-loader' },
            { test: /\.json$/, loader: 'json-loader' },
        ],
    }
}

var path = require('path');

module.exports = {
    entry: './test/build/index.js',
    output: {
        path: __dirname + "/build",
        filename: "bundle.js",
        publicPath: "./build/"
    },
    bail: true,
    module: {
        loaders: [
            { test: /\.css$/, loader: 'style-loader!css-loader!postcss-loader' },
            { test: /\.md$/, loader: 'raw-loader'},
            { test: /\.html$/, loader: "file?name=[name].[ext]" },
            { test: /\.ipynb$/, loader: 'json-loader' },
            { test: /\.json$/, loader: 'json-loader' },
        ],
    },
    postcss: function () {
        return [
            require('postcss-import'),
            require('postcss-cssnext')
        ];
    }
}

Load CSS correctly in tests


import ApolloClient, { createNetworkInterface } from 'apollo-client';

const client = new ApolloClient({
  networkInterface: createNetworkInterface({
    uri: '/graphql',
  }),
  queryDeduplication: true,
  reduxRootSelector: state => state.apollo,
});

export default function createApolloClient() {
  return client;
}

import ApolloClient, { createNetworkInterface } from 'apollo-client';

const client = new ApolloClient({
  networkInterface: createNetworkInterface({
    uri: '/graphql',
    opts: {
      credentials: 'include',
    },
  }),
  queryDeduplication: true,
  reduxRootSelector: state => state.apollo,
});

export default function createApolloClient() {
  return client;
}

Include credentials with Apollo request


"use strict";

module.exports.ajax1 = (event, context, callback) => {
  setTimeout(() => {
    callback(null, { statusCode: 200, body: "ajax-response-1" });
  }, 100);
};
module.exports.ajax2 = (event, context, callback) => {
  setTimeout(() => {
    callback(null, { statusCode: 200, body: "ajax-response-2" });
  }, 100);
};

"use strict";

module.exports.ajax1 = (event, context, callback) => {
  setTimeout(() => {
    callback(null, {
      statusCode: 200,
      body: "ajax-response-1",
      headers: {
        "Access-Control-Allow-Origin": "*" // Required for CORS support to work
      }
    });
  }, 100);
};
module.exports.ajax2 = (event, context, callback) => {
  setTimeout(() => {
    callback(null, {
      statusCode: 200,
      body: "ajax-response-2",
      headers: {
        "Access-Control-Allow-Origin": "*" // Required for CORS support to work
      }
    });
  }, 100);
};

Add CORS wildcard host to lambda endpoint


import Ember from 'ember';

export default Ember.Route.extend({
  model() {
    return this.store.createRecord('support-ticket');
  }
});

import Ember from 'ember';

export default Ember.Route.extend({
  model() {
    return this.store.createRecord('support-ticket');
  },

  // Controllers are singletons so state must be cleared.
  setupController(controller, model) {
    this._super(controller, model);
    controller.set('didValidate', false);
  }
});

Reset the contact form validation between transitions.


//= require spec_helper
//= require spec_helper_functions
//= require fn.lalolib_spec
//= require fn.parsed_biom_spec
//= require fn.project_spec
//= require functions_spec
//= require newick_spec
//= require parse_mapping_file_spec
//= require viewer_functions_spec
//= require viewer_spec
//= require viewer_draw
//= require iroki/ary_spec

Add iroki ary spec to everything wrapper


import React, {Component} from 'react';
import logo_calzaweb_mini from '../../img/logo-calzaweb-mini.png'

class Footer extends Component {
	render() {
	  return(

				 Versión 0.1.0

	  );
  }
}

export default Footer;

import React, {Component} from 'react';
import logo_calzaweb_mini from '../../img/logo-calzaweb-mini.png'

class Footer extends Component {
	render() {
		const pStyle = {
			color: 'dodgerblue'
		};

	  return(

 Versión 0.1.0

	  );
  }
}

export default Footer;

Tag a changed for p.


module.exports = function(Backbone, _, SearchShop) {
  return Backbone.View.extend({
    tagName: 'div',
    className: 'row',
    template: '
<%= title %>
',
    events: {
      'click .name' : 'remove'
    },
    initialize: function() {
      this.render();
      // If this models contents change, we re-render
      this.model.on('change', function(){
        this.render();
      }, this);
    },
    render : function() {
      // Render this view and return its context.
      this.$el.html( _.template( this.template, this.model.toJSON() ));
      return this;
    },
    remove: function() {
      // Fade out item out from the shopping cart list
      this.$el.fadeOut(500, function(){
        // Remove it from the DOM on completetion
        $(this).remove();
      });
      // Remove the model for this view from the Shopping Cart Items collection
      SearchShop.cartItems.remove(this.model);
    }
  });
}

Add cart title class to cart item view.


(function(Modules) {
  "use strict";

  let normalize = (string) => string.toLowerCase().replace(/ /g,'');

  let filter = ($searchBox, $targets) => () => {

    let query = normalize($searchBox.val());

    $targets.each(function() {

      let content = $('.live-search-relevant', this).text() || $(this).text();

      if ($(this).has(':checked').length) {
        $(this).show();
        return;
      }

      if (query == '') {
        $(this).css('display', '');
        return;
      }

      $(this).toggle(
        normalize(content).indexOf(normalize(query)) > -1
      );

    });

  };

  Modules.LiveSearch = function() {

    this.start = function(component) {

      let $component = $(component);

      let $searchBox = $('input', $component);

      let filterFunc = filter(
        $searchBox,
        $($component.data('targets'))
      );

      $searchBox.on('keyup input', filterFunc);

      filterFunc();

    };

  };

})(window.GOVUK.Modules);

(function(Modules) {
  "use strict";

  let normalize = (string) => string.toLowerCase().replace(/ /g,'');

  let filter = ($searchBox, $targets) => () => {

    let query = normalize($searchBox.val());

    $targets.each(function() {

      let content = $('.live-search-relevant', this).text() || $(this).text();

      if ($(this).has(':checked').length) {
        $(this).show();
        return;
      }

      if (query == '') {
        $(this).css('display', '');
        return;
      }

      $(this).toggle(
        normalize(content).indexOf(normalize(query)) > -1
      );

    });

    // make sticky JS recalculate its cache of the element's position
    // because live search can change the height document
    if ('stickAtBottomWhenScrolling' in GOVUK) {
      GOVUK.stickAtBottomWhenScrolling.recalculate();
    }

  };

  Modules.LiveSearch = function() {

    this.start = function(component) {

      let $component = $(component);

      let $searchBox = $('input', $component);

      let filterFunc = filter(
        $searchBox,
        $($component.data('targets'))
      );

      $searchBox.on('keyup input', filterFunc);

      filterFunc();

    };

  };

})(window.GOVUK.Modules);

Fix sticky with find as you type


frappe.provide('frappe.utils');
/**
 * Simple EventEmitterMixin which uses jQuery's event system
 */
const EventEmitterMixin = {
	init() {
		this.jq = jQuery(this);
	},

	trigger(evt, data) {
		!this.jq && this.init();
		this.jq.trigger(evt, data);
	},

	once(evt, handler) {
		!this.jq && this.init();
		this.jq.one(evt, (e, data) => handler(data));
	},

	on(evt, handler) {
		!this.jq && this.init();
		this.jq.bind(evt, (e, data) => handler(data));
	},

	off(evt, handler) {
		!this.jq && this.init();
		this.jq.unbind(evt, (e, data) => handler(data));
	}
}

frappe.utils.make_event_emitter = function(object) {
	Object.assign(object, EventEmitterMixin);
	return object;
};

export default EventEmitterMixin;

frappe.provide('frappe.utils');
/**
 * Simple EventEmitterMixin which uses jQuery's event system
 */
const EventEmitterMixin = {
	init() {
		this.jq = jQuery({});
	},

	trigger(evt, data) {
		!this.jq && this.init();
		this.jq.trigger(evt, data);
	},

	once(evt, handler) {
		!this.jq && this.init();
		this.jq.one(evt, (e, data) => handler(data));
	},

	on(evt, handler) {
		!this.jq && this.init();
		this.jq.bind(evt, (e, data) => handler(data));
	},

	off(evt, handler) {
		!this.jq && this.init();
		this.jq.unbind(evt, (e, data) => handler(data));
	}
}

frappe.utils.make_event_emitter = function(object) {
	Object.assign(object, EventEmitterMixin);
	return object;
};

export default EventEmitterMixin;

Make event emitter from blank object


var allowedGlobals = require('./allowed-globals'),
    UNDEF = 'undefined',
    cache;

/**
 * Get global object based on what is available
 * @private
 */
module.exports = function () {
    if (typeof global !== UNDEF && global !== null && global.Array) {
        return global;
    }

    if (typeof window !== UNDEF && window !== null && window.Array) {
        return window;
    }

    // now that the global and window is not present, we load them by creating functions
    // since that is expensive, we cache it
    if (cache) { return cache; }
    cache = {};

    allowedGlobals.forEach(function (prop) {
        var value = Function('return (typeof ' + // eslint-disable-line no-new-func
            prop + ' !== "undefined") && ' + prop + ' || undefined')();
        value && (cache[prop] = value);
    });

    return cache;
};

var allowedGlobals = require('./allowed-globals'),
    UNDEF = 'undefined',
    cache;

/**
 * Get global object based on what is available
 * @private
 */
module.exports = function () {
    if (typeof global !== UNDEF && global !== null && global.Array) {
        return global;
    }

    if (typeof window !== UNDEF && window !== null && window.Array) {
        return window;
    }

    // now that the global and window is not present, we load them by creating functions
    // since that is expensive, we cache it
    if (cache) { return cache; }
    cache = {};

    allowedGlobals.forEach(function (prop) {
        var value = Function('return (typeof ' + // eslint-disable-line no-new-func
            prop + ' !== "undefined") ? ' + prop + ' : undefined')();
        value && (cache[prop] = value);
    });

    return cache;
};

Fix typeof check for allowed globals


var Strategy = require('passport-github').Strategy,
    Promise  = require('mongoose').Promise,
    https    = require('https'),
    steps    = require('./steps'),
    config   = require('../config').keys.github;

var prepareGitHub = function (profile, accessToken) {
  var promise = new Promise;

  // photos
  profile.photos = [ { value: profile._json.avatar_url } ];

  // emails
  var request = https.get({
    headers:  { 'Authorization': 'token ' + accessToken },
    hostname: 'api.github.com',
    path:     '/user/emails'
  }, function (res) {
    res.on('readable', function () {
      var chunk,
          json = '',
          emails;
      while (chunk = res.read()) json += chunk;
      profile.emails = JSON.parse(json).map(function (email) {
        return { value: email };
      });
      promise.fulfill(profile);
    });
  });

  request.on('error', promise.reject);

  return promise;
};

module.exports = new Strategy(
  {
    scope:        'user',
    clientID:     config.id,
    clientSecret: config.secret
  },
  function gitHubStrategyVerifier (accessToken, refreshToken, profile, resolveVerification)
  {
    prepareGitHub(profile, accessToken)
      .then(steps.find)
      .then(steps.prepare)
      .then(steps.save)
      .onResolve(resolveVerification);
  }
);

var Strategy = require('passport-github').Strategy,
    Promise  = require('mongoose').Promise,
    https    = require('https'),
    steps    = require('./steps'),
    config   = require('../config').keys.github;

var prepareGitHub = function (profile, accessToken) {
  var promise = new Promise;

  // photos
  profile.photos = [ { value: profile._json.avatar_url } ];

  // emails
  var request = https.get({
    hostname:  'api.github.com',
    path:      '/user/emails',
    headers: {
      'Authorization':  'token ' + accessToken,
      'UserAgent':      'marek-saji/standupper'
    }
  }, function (res) {
    res.on('readable', function () {
      var chunk,
          json = '',
          emails;
      while (chunk = res.read()) json += chunk;
      profile.emails = JSON.parse(json).map(function (email) {
        return { value: email };
      });
      promise.fulfill(profile);
    });
  });

  request.on('error', promise.reject);

  return promise;
};

module.exports = new Strategy(
  {
    scope:        'user',
    clientID:     config.id,
    clientSecret: config.secret
  },
  function gitHubStrategyVerifier (accessToken, refreshToken, profile, resolveVerification)
  {
    prepareGitHub(profile, accessToken)
      .then(steps.find)
      .then(steps.prepare)
      .then(steps.save)
      .onResolve(resolveVerification);
  }
);

Send UserAgent header, when using GitHub api


import { test, moduleForComponent } from 'ember-qunit';

moduleForComponent('file-overview', 'Integration | Component | file overview', {
  unit: true
});

test('tapping button fires an external action', function(assert) {
  assert.expect(1);
  var component = this.subject();
  assert.deepEqual(component.get('chartOptions'),{ "animation": { "animateRotate": false }, "legend": { "display": false } }, "Chart Options");
});

import { test, moduleForComponent } from 'ember-qunit';

moduleForComponent('file-overview', 'Integration | Component | file overview', {
  unit: true
});

test('tapping button fires an external action', function(assert) {
  assert.expect(1);
  var component = this.subject();
  assert.deepEqual(component.get('chartOptions'),{ "animation": { "animateRotate": false }, "legend": { "display": false }, "responsive": false }, "Chart Options");
});

Fix test related to chartjs


(function() {
    'use strict';

    angular.module('app', [
            'Core',
            'Directives'
        ])
        .config(function($stateProvider, $urlRouterProvider, $compileProvider) {
            $urlRouterProvider.otherwise('/');
            $compileProvider.imgSrcSanitizationWhitelist(/^\s*(https?|file|blob|cdvfile|content):|data:image\//);
        }).run(function(/* $state */$rootScope) {
        	$rootScope.$on('$stateChangeStart', function(evnt, toState, toParams, fromState, fromParams) {
		        // If you want to stop the state change event, use:
		        // 		evnt.preventDefault();
		        // If you want to change state:
		        // 		evnt.preventDefault();
		        // 		$state.go('statename');		<= Do remember to add $state as dependency on 'run' as shown above
		    });
        }

})()

(function() {
    'use strict';

    angular.module('app', [
            'Core',
            'Directives'
        ])
        .config(function($stateProvider, $urlRouterProvider, $compileProvider) {
            $urlRouterProvider.otherwise('/');
            $compileProvider.imgSrcSanitizationWhitelist(/^\s*(https?|file|blob|cdvfile|content):|data:image\//);
        }).run(function(/* $state */$rootScope) {
        	$rootScope.$on('$stateChangeStart', function(evnt, toState, toParams, fromState, fromParams) {
		        // If you want to stop the state change event, use:
		        // 		evnt.preventDefault();
		        // If you want to change state:
		        // 		evnt.preventDefault();
		        // 		$state.go('statename');		<= Do remember to add $state as dependency on 'run' as shown above
		    });
        });
})()

Fix syntax error where function is not closed properly


import React from 'react'

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

    this.state = {
      lessonId: this.props.params.lessonId,
      className: 'get this from the DB query',
      polls: ['get this from the DB query']
    };
  }

  render(){
    return (

{this.state.className}

Polls for {this.state.className}

)
  }

  componentWillMount(){
    //get the poll responses from the lessonId given in the URL param
    //set the className from the DB query
  }

const AddPoll = (hide) => {
  // add to state
  // post to DB
  return (

      <button>Thumbs Check</button>
      <button>Multiple Choice</button>

  )
};

module.exports = LessonData;

import React from 'react'

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

    this.state = {
      lessonId: this.props.params.lessonId,
      className: 'get this from the DB query',
      polls: ['get this from the DB query']
    };
  }

  render(){
    return (

{this.state.className}

Polls for {this.state.className}

      <button>Add thumbs check</button>
      <button>Add multiple choice</button>

)
  }

  componentWillMount(){
    //get the poll responses from the lessonId given in the URL param
    //set the className from the DB query
  }

const AddPoll = (hide) => {
  // add to state
  // post to DB
  return (

      <button>Thumbs Check</button>
      <button>Multiple Choice</button>

  )
};

module.exports = LessonData;

Create anchor tag for logout


'use strict';

/**
 * @ngdoc service
 * @name applicationApp.pouchdb
 * @description
 * # pouchdb
 * Service in the applicationApp.
 */
angular.module('applicationApp')
  .factory('pouchdb', function () {
      /*globals PouchDB*/
      PouchDB.sync('notes', 'http://127.0.0.1:5984/notes');
      return new PouchDB('notes');})

'use strict';

/**
 * @ngdoc service
 * @name applicationApp.pouchdb
 * @description
 * # pouchdb
 * Service in the applicationApp.
 */
angular.module('applicationApp')
  .factory('pouchdb', function () {
      /*globals PouchDB*/
      PouchDB.sync('notelis', 'http://127.0.0.1:5984/notes',{live:true});
      return new PouchDB('notelis');})

Change databse name to 'notelis'


/*
===============================================================================================

    Unify Project

    Homepage: unify-project.org
    License: MIT + Apache (V2)
    Copyright: 2012, Sebastian Fastner, Mainz, Germany, http://unify-training.com

===============================================================================================
*/

(function() {

  var rgba = function(red,green,blue,alpha) {
    return "rgba(" + red + "," + green + "," + blue + "," + alpha + ")";
  };
  var rgb = function(red,green,blue) {
    return "rgb(" + red + "," + green + "," + blue + ")";
  };
  var colorFnt;

  if (core.Env.getValue("engine") == "trident") {
    var version = /MSIE.(\d+)/.exec(navigator.userAgent);
    if (version[1] && parseInt(version[1],10) < 9) {
      colorFnt = rgb;
    } else {
      colorFnt = rgba;
    }
  } else {
    colorFnt = rgba;
  }

  /**
   * Creates browser specific gradients
   */
  core.Module('unify.bom.Color', {

    /*
    ----------------------------------------------------------------------------
      STATICS
    ----------------------------------------------------------------------------
    */

    /**
     * Creates a browser specific color code based upon @red {Integer}, @green {Integer},
     * @blue {Integer} and @alpha {Float}, ignoring alpha on IE<9
     */
    rgba: colorFnt
  });

})();

Fix color function to support rgba on IE9 and up