function feedPaperPage(req, res) {
  res.render('feedpaper.html', {
    layout: 'layout',
    locals: {
      categories: exports.cache.categoryList
    }
  });
}

function feedData(req, res) {
  exports.cache.getFeed(req.params.feedId, function (err, articles) {
    if (err) {
      res.status(500).send(err.message);
    } else {
      res.json(articles);
    }
  });
}

function articleData(req, res) {
  var url = req.url.match(/http.+$/);
  exports.cache.getArticle(url, function (err, article) {
    if (err) {
      res.status(500).send(err.message);
    } else {
      res.json(article);
    }
  });
}

exports.feedPaperPage = feedPaperPage;
exports.feedData      = feedData;
exports.articleData   = articleData;

function feedPaperPage(req, res) {
  res.render('feedpaper.html',
    { categories: exports.cache.categoryList },
    function (err, html) {
      res.send(html);
    }
  );
}

function feedData(req, res) {
  exports.cache.getFeed(req.params.feedId, function (err, articles) {
    if (err) {
      res.status(500).send(err.message);
    } else {
      res.json(articles);
    }
  });
}

function articleData(req, res) {
  var url = req.url.match(/http.+$/);
  exports.cache.getArticle(url, function (err, article) {
    if (err) {
      res.status(500).send(err.message);
    } else {
      res.json(article);
    }
  });
}

exports.feedPaperPage = feedPaperPage;
exports.feedData      = feedData;
exports.articleData   = articleData;

Change feedPaperPage handler to work with Express 4.x .


goog.provide('monoid.LoadRaceButton');

goog.require('goog.ui.Component');
goog.require('goog.dom.classlist');

goog.scope(function(){

/**
 * @constructor
 * @extends {goog.ui.Component}
 */
monoid.LoadRaceButton = function() {
  goog.base(this);
};
var LoadRaceButton = monoid.LoadRaceButton;
goog.inherits(LoadRaceButton, goog.ui.Component);

/** @override */
LoadRaceButton.prototype.createDom = function() {
  goog.base(this, 'createDom');

  var element = this.getElement();

  var label = this.dom_.createDom('div', 'load-race-button-label');
  this.dom_.setTextContent(label, 'Load race data:');
  this.dom_.appendChild(element, label);

  var button = this.dom_.createDom('input', {type: 'file'});
  goog.dom.classlist.add(button, 'load-race-button');
  this.dom_.appendChild(element, button);
};
});

goog.provide('monoid.LoadRaceButton');

goog.require('goog.ui.Component');
goog.require('goog.dom.classlist');
goog.require('goog.fs');
goog.require('goog.fs.FileReader');

goog.scope(function(){

/**
 * @constructor
 * @extends {goog.ui.Component}
 */
monoid.LoadRaceButton = function() {
  goog.base(this);

  /** @private {goog.fs.FileReader} */
  this.fileReader_ = new goog.fs.FileReader();
  this.getHandler().listen(this.fileReader_, goog.fs.FileReader.EventType.LOAD,
                           this.handleLoad_);
  this.registerDisposable(this.fileReader_);
};
var LoadRaceButton = monoid.LoadRaceButton;
goog.inherits(LoadRaceButton, goog.ui.Component);

/** @override */
LoadRaceButton.prototype.createDom = function() {
  goog.base(this, 'createDom');

  var element = this.getElement();

  var label = this.dom_.createDom('div', 'load-race-button-label');
  this.dom_.setTextContent(label, 'Load race data:');
  this.dom_.appendChild(element, label);

  var button = this.dom_.createDom('input', {type: 'file'});
  goog.dom.classlist.add(button, 'load-race-button');
  this.dom_.appendChild(element, button);
  this.getHandler().listen(button, goog.events.EventType.CHANGE, this.handleInputChanged_);
};

/**
 * @param {goog.events.BrowserEvent} e
 * @private
 */
LoadRaceButton.prototype.handleInputChanged_ = function(e) {
  this.fileReader_.readAsText(e.target.files[0]);
};

/**
 * @param {goog.fs.ProgressEvent} e
 * @private
 */
LoadRaceButton.prototype.handleLoad_ = function(e) {
  var test = this.dom_.createElement('div');
  this.dom_.setTextContent(test, this.fileReader_.getResult());
  this.dom_.appendChild(this.getElement(), test);
};
});

Add ability to read files.


var Promise = require('bluebird');
var AWS = require('aws-sdk');
var credentials = new AWS.SharedIniFileCredentials({profile: 'lilybin'});
AWS.config.credentials = credentials;
AWS.config.region = 'us-west-2'; // See aws/aws-sdk-js#473
var lambda = new AWS.Lambda({apiVersion: '2015-03-31'});

module.exports = {
  compile: function(key, body, version) {
    version = (version === 'unstable') ? 'unstable' : 'stable';

    return new Promise(function (fulfill, reject) {
      lambda.invoke({
        FunctionName: 'lilybin-' + version,
        Payload: JSON.stringify({
          body   : body,
          key    : key
        })
      }, function (err, data) {
        if (err) return reject(err);
        var payload = JSON.parse(data.Payload);
        if (data.FunctionError) {
          return reject(new Error(payload.errorMessage));
        }
        fulfill(payload);
      });
    });
  },
};

var Promise = require('bluebird');
var AWS = require('aws-sdk');
AWS.config.credentials = new AWS.EC2MetadataCredentials();
AWS.config.region = 'us-west-2'; // See aws/aws-sdk-js#473
var lambda = new AWS.Lambda({apiVersion: '2015-03-31'});

module.exports = {
  compile: function(key, body, version) {
    version = (version === 'unstable') ? 'unstable' : 'stable';

    return new Promise(function (fulfill, reject) {
      lambda.invoke({
        FunctionName: 'lilybin-' + version,
        Payload: JSON.stringify({
          body   : body,
          key    : key
        })
      }, function (err, data) {
        if (err) return reject(err);
        var payload = JSON.parse(data.Payload);
        if (data.FunctionError) {
          return reject(new Error(payload.errorMessage));
        }
        fulfill(payload);
      });
    });
  },
};

Use EC2 metadata for credentials.


Fix module for NodeJS - apped_dir_name


const {knex} = require('../util/database').connect();

const newComment = (action) =>  knex('comments').insert({
  user_id: action.client.id,
  feed_item_id: action.feedItemId,
  text: action.text,
})
.catch((err) => {
  if (err.constraint === 'comments_feed_item_id_foreign') {
    const error = new Error('No such feed item id');
    error.status = 400;
    throw error;
  }

  throw err;
});

export {
  newComment,
};

const {knex} = require('../util/database').connect();

const newComment = (action) =>  knex('comments').insert({
  user_id: action.client.id,
  feed_item_id: action.feedItemId,
  text: action.text,
})
.catch((err) => {
  if (err.constraint === 'comments_feed_item_id_foreign') {
    const error = new Error('No such feed item id');
    error.status = 404;
    throw error;
  }

  throw err;
});

export {
  newComment,
};

Return 404 instead of 400 when invalid feedItemId is provided


/*!
 * Wef
 * Copyright (c) 2011 Pablo Escalada
 * MIT Licensed
 */

/**
 * wef module
 */
(function(global) {
    var wef = function() {
        return new wef.prototype.init();
    };
    wef.prototype = {
        constructor:wef,
        version: "0.0.1",
        init: function() {
            return this;
        }
    };
    wef.fn = wef.prototype;
    wef.prototype.init.prototype = wef.prototype;

    wef.fn.extend = function (receiver, giver) {
        var tmp = receiver;
        //both must be objects
        if (typeof receiver === "object" && typeof giver === "object") {
            if (tmp === null) {
                tmp = {};
            }
            if (receiver === null) {
                return tmp;
            }
            for (var property in giver) {
                tmp[property] = giver[property];
            }
            return tmp;
        }
        wef.f.error("InvalidArgumentException: incorrect argument type");
        return null;
    };

    wef.fn.isFunction = function (obj) {
        return typeof obj == "function";
    };

    wef.fn.isString = function (obj) {
        return typeof obj == "string";
    };

    wef.fn.error = function (message) {
        throw new Error(message);
    };

    //registering global variable
    if (global.wef) {
        throw new Error("wef has already been defined");
    } else {
        global.wef = wef();
    }
})(window);

/*!
 * Wef
 * Copyright (c) 2011 Pablo Escalada
 * MIT Licensed
 */

/**
 * wef module
 */
(function () {
    var wef = function () {
        return new wef.prototype.init();
    };

    wef.prototype = {
        constructor:wef,
        version:"0.0.1",
        init:function () {
            return this;
        }
    };

    wef.fn = wef.prototype;

    wef.prototype.init.prototype = wef.prototype;

    wef.fn.extend = function (receiver, giver) {
        var tmp = receiver, property;
        //both must be objects
        if (typeof receiver === "object" && typeof giver === "object") {
            if (tmp === null) {
                tmp = {};
            }
            if (receiver === null) {
                return tmp;
            }
            for (property in giver) {
                tmp[property] = giver[property];
            }
            return tmp;
        }
        wef.f.error("InvalidArgumentException: incorrect argument type");
        return null;
    };

    wef.fn.isFunction = function (obj) {
        return typeof obj == "function";
    };

    wef.fn.isString = function (obj) {
        return typeof obj == "string";
    };

    wef.fn.error = function (message) {
        throw new Error(message);
    };

    //registering global variable
    if (window.wef) {
        throw new Error("wef has already been defined");
    } else {
        window.wef = wef();
    }

})();

Fix module call and refactor


//
// parameters:
//   canvas    - The object related to the Canvas api call to be made. ie search_account_domains
//   params    - The params passed to Canvas
//   body      - The body of the request. Used for POST and PUT
//   localData - An object containing data that the action will hold onto for local usage
//               but will not be passed to the server.
export default function(canvas, params, body, localData = {}) {
  return {
    type: canvas.type,
    canvas,
    params,
    body,
    localData
  };
}

//
// parameters:
//   canvas    - The object related to the Canvas api call to be made. ie search_account_domains
//   params    - The params passed to Canvas
//   body      - The body of the request. Used for POST and PUT
//   localData - An object containing data that the action will hold onto for local usage
//               but will not be passed to the server.
export default function(canvas, params, body, localData = {}) {
  return {
    lms_proxy_call_type: canvas.type,
    canvas,
    params,
    body,
    localData
  };
}

Use a more descriptive param for canvas type


module.exports = function() {
    return {
        _defaults: {
            bg: false
        },

        /**
         * Clean the app distribution folder.
         */
        app_clean: {
            execOpts: {
                cwd: '.'
            },
            cmd: 'rm -Rf <%= distScriptsPath %>/*'
        },

        /**
         * Copy app HTML template to distribution folder.
         */
        copy_app_template: {
            execOpts: {
                cwd: '.'
            },
            cmd: 'cp <%= appMainTemplate %> <%= distPath %>'
        },

        /*
         * Push local (master) branch to remote master. Push to subtree remotes also.
         */
        push_2_master: {
            execOpts: {
                cwd: '../../../'
            },
            cmd: [
                'git push origin master',
                'git push bitbucket master',
            ].join(';')
        }
    };
};

module.exports = function() {
    return {
        _defaults: {
            bg: false
        },

        /**
         * Clean the app distribution folder.
         */
        app_clean: {
            execOpts: {
                cwd: '.'
            },
            cmd: 'rm -Rf <%= distScriptsPath %>/*'
        },

        /**
         * Copy app HTML template to distribution folder.
         */
        copy_app_template: {
            execOpts: {
                cwd: '.'
            },
            cmd: 'cp <%= appMainTemplate %> <%= distPath %>'
        },

        /*
         * Push local (master) branch to remote master. Push to subtree remotes also.
         */
        push_2_master: {
            execOpts: {
                cwd: '../../'
            },
            cmd: [
                'git push origin master',
                'git push bitbucket master',
            ].join(';')
        }
    };
};

Fix push-master grunt task for angular project.


Tinytest.add("stylus - presence", function(test) {

  var div = document.createElement('div');
  Blaze.renderComponent(Template.stylus_test_presence, div);
  div.style.display = 'block';
  document.body.appendChild(div);

  var p = div.querySelector('p');
  var leftBorder = getStyleProperty(p, 'border-left-style');
  test.equal(leftBorder, "dashed");

  document.body.removeChild(div);
});

Tinytest.add("stylus - @import", function(test) {
  var div = document.createElement('div');
  Blaze.renderComponent(Template.stylus_test_import, div);
  div.style.display = 'block';
  document.body.appendChild(div);

  var p = div.querySelector('p');
  test.equal(getStyleProperty(p, 'font-size'), "20px");
  test.equal(getStyleProperty(p, 'border-left-style'), "dashed");

  document.body.removeChild(div);
});

Tinytest.add("stylus - presence", function(test) {

  var div = document.createElement('div');
  Blaze.render(Template.stylus_test_presence).attach(div);
  div.style.display = 'block';
  document.body.appendChild(div);

  var p = div.querySelector('p');
  var leftBorder = getStyleProperty(p, 'border-left-style');
  test.equal(leftBorder, "dashed");

  document.body.removeChild(div);
});

Tinytest.add("stylus - @import", function(test) {
  var div = document.createElement('div');
  Blaze.render(Template.stylus_test_import).attach(div);
  div.style.display = 'block';
  document.body.appendChild(div);

  var p = div.querySelector('p');
  test.equal(getStyleProperty(p, 'font-size'), "20px");
  test.equal(getStyleProperty(p, 'border-left-style'), "dashed");

  document.body.removeChild(div);
});

Fix remaining tests (less and stylus)


module.exports = function (compose) {
  return function () {
    const inc = n => n + 1
    const dbl = n => n * 2

    it('composes right to left', function () {
      compose(inc, dbl, 20).should.equal(41)
      compose(dbl, inc, 20).should.equal(42)
    })
  }
}

module.exports = function (compose) {
  return function () {
    const inc = (n) => n + 1
    const dbl = (n) => n * 2

    it('composes right to left', function () {
      compose(inc, dbl, 20).should.equal(41)
      compose(dbl, inc, 20).should.equal(42)
    })
  }
}

Clean up code, add parens


/**
 * Copyright (c) 2017
 *  Thomas Müller <thomas.mueller@tmit.eu>
 *
 * This file is licensed under the Affero General Public License version 3 or later.
 * See the COPYING-README file.
 */

$(document).ready(function() {

	// You can Customize here
	window.$buoop = {
		vs: { i: 10, f: 45, o: 0, s: 8, c: 45 },
		reminder: 0,                  // after how many hours should the message reappear
		test: false,                    // true = always show the bar (for testing)
		newwindow: true,                // open link in new window/tab
		url: null,                      // the url to go to after clicking the notification
		noclose:true                  // Do not show the "ignore" button to close the notification
	};

	var path = OC.filePath('core','vendor','browser-update/browser-update.js');
	$.getScript(path);
});

/**
 * Copyright (c) 2017
 *  Thomas Müller <thomas.mueller@tmit.eu>
 *
 * This file is licensed under the Affero General Public License version 3 or later.
 * See the COPYING-README file.
 */

$(document).ready(function() {

	// You can Customize here
	window.$buoop = {
		vs: { i: 10, f: 54, o: 0, s: 9, c: 60 },
		reminder: 0,                  // after how many hours should the message reappear
		test: false,                    // true = always show the bar (for testing)
		newwindow: true,                // open link in new window/tab
		url: null,                      // the url to go to after clicking the notification
		noclose:true                  // Do not show the "ignore" button to close the notification
	};

	var path = OC.filePath('core','vendor','browser-update/browser-update.js');
	$.getScript(path);
});

Update minimum supported browser versions


require('./basics');
require('./compose');
require('./convert-constructor');
require('./init');
require('./immutability');
require('./is-stamp');
require('./props-safety');
require('./refs');
require('./state');
require('./v1-compatibility');

require('./basics');
require('./compose');
require('./convert-constructor');
require('./init');
require('./immutability');
require('./is-stamp');
require('./props-safety');
require('./refs');
require('./stampit-aliases');
require('./state');
require('./v1-compatibility');

Add new file to tests list.


var chai = require("chai");

var DSLEngine = require("../src/dslEngine");
var TokenAlreadyInsertException = require(
    "../src/utils/tokenAlreadyInsertException"
);
var NoTokenConnectedException = require(
    "../src/utils/noTokenConnectedException"
);

describe("Token", () => {
    var engine;
    var token;

    before(() => {
	   engine = new DSLEngine();
    });

    describe("#createToken", () => {
        it("should return a not undefined token", () => {
            token = engine.generateToken(undefined);
            chai.expect(token).to.not.undefined;
        });
    });

    describe("#insertToken", () => {
	   it("shoud not throw any exception when push a token", () => {
	       chai.expect(
                engine.pushToken.bind(engine, token)
	       ).to.not.throw();
	   });
    });

    describe("#insertTokenOneOtherTime", () => {
	   it("should throw exception TokenAlreadyInsertException", () => {
	       chai.expect(engine.pushToken.bind(engine, token)).to.throw(
		      TokenAlreadyInsertException
	       );
	   });
    });

    describe("#removeToken", () => {
        it("shoud return the same token", () => {
            chai.expect(engine.ejectSafelyToken()).to.equal(token);
        });
    });

    describe("#removeTokenOneOtherTime", () => {
        it("shoud throw exception NoTokenConnectedException", () => {
            chai.expect(engine.ejectSafelyToken.bind(engine)).to.throw(
                NoTokenConnectedException
            );
        });
    });
});

var chai = require("chai");

var engine = require("../src/dslEngine");
var TokenAlreadyInsertException = require(
    "../src/utils/tokenAlreadyInsertException"
);
var NoTokenConnectedException = require(
    "../src/utils/noTokenConnectedException"
);

describe("Token", () => {
    var token;

    describe("#createToken", () => {
        it("should return a not undefined token", () => {
            token = engine.generateToken(undefined);
            chai.expect(token).to.not.undefined;
        });
    });

    describe("#insertToken", () => {
	   it("shoud not throw any exception when push a token", () => {
	       chai.expect(
                engine.pushToken.bind(engine, token)
	       ).to.not.throw();
	   });
    });

    describe("#insertTokenOneOtherTime", () => {
	   it("should throw exception TokenAlreadyInsertException", () => {
	       chai.expect(engine.pushToken.bind(engine, token)).to.throw(
		      TokenAlreadyInsertException
	       );
	   });
    });

    describe("#removeToken", () => {
        it("shoud return the same token", () => {
            chai.expect(engine.ejectSafelyToken()).to.equal(token);
        });
    });

    describe("#removeTokenOneOtherTime", () => {
        it("shoud throw exception NoTokenConnectedException", () => {
            chai.expect(engine.ejectSafelyToken.bind(engine)).to.throw(
                NoTokenConnectedException
            );
        });
    });
});

Remove instantation of the engine


import { useEffect, useState } from 'react';

export default function useDarkMode() {
  const [theme, setTheme] = useState('light');
  const setMode = mode => {
    localStorage.setItem('theme', mode);
    setTheme(mode);
  };

  function themeToggler() {
    theme === 'light' ? setMode('dark') : setMode('light');
  }

  useEffect(() => {
    const localTheme = localStorage.getItem('theme');
    localTheme ? setTheme(localTheme) : setMode('light');
  }, []);

  return [theme, themeToggler];
}

import { useState } from 'react';

export default function useDarkMode() {
  const [theme, setTheme] = useState(localStorage.getItem('theme') || 'light');
  const setMode = mode => {
    localStorage.setItem('theme', mode);
    setTheme(mode);
  };

  function themeToggler() {
    theme === 'light' ? setMode('dark') : setMode('light');
  }

  return [theme, themeToggler];
}

Fix initial page load light theme flash


(function (window, jQuery) {
  $("tr.file-diff-line > td.diff-line-num-addition.empty-cell").each(function (index, cell) {
    var line = $(cell).parent().attr('data-line');
    var path = $(cell).parent().attr('data-path');

    // Fill in the path to the repo on your computer here
    var repoRoot = '/Users/nick/reporoot/';
    var openInTextmateUrl = 'txmt://open?url=file://' + repoRoot + path + '&line=' + line;
    $(cell).html('Txmt');
  })
}(window, jQuery));

(function (window, jQuery) {
  // Fill in the path to the repo on your computer here
  var repoRoot = '/Users/nick/reporoot/';

  $('#files > .file').each(function (index, fileView) {
    var path = $(fileView).children('[data-path]').eq(0).attr('data-path');

    $(fileView).find('[data-line-number]').off().click(function () {
      var line = $(this).attr('data-line-number');
      var openInTextmateUrl = 'txmt://open?url=file://' + repoRoot + path + '&line=' + line;
      window.open(openInTextmateUrl, '_self');
    });
  });
}(window, jQuery));

Update to work with latest github UI changes


'use strict';

var FusebillResource = require('../FusebillResource');
var utils = require('../utils');
var fusebillMethod = FusebillResource.method;

module.exports = FusebillResource.extend({

  path: 'customers',
  includeBasic: [
    'create', 'list', 'retrieve', 'update', 'del',
  ],

  /**
   * Customer: Subscription methods
   */

  listSubscriptions: fusebillMethod({
    method: 'GET',
    path: '/{customerId}/subscriptions',
    urlParams: ['customerId'],
  }),

  retrieveSubscription: fusebillMethod({
    method: 'GET',
    path: '/{customerId}/subscriptions/{subscriptionId}',
    urlParams: ['customerId', 'subscriptionId'],
  }),

  /**
   * Customer: Email Preference methods
   */

  listEmailPreferences: fusebillMethod({
    method: 'GET',
    path: '/{customerId}/CustomerEmailPreferences',
    urlParams: ['customerId'],
  }),

  /**
   * Customer: Invoice methods
   */

  listInvoices: fusebillMethod({
    method: 'GET',
    path: '/{customerId}/invoices',
    urlParams: ['customerId'],
  }),

  listDraftInvoices: fusebillMethod({
    method: 'GET',
    path: '/{customerId}/DraftInvoices',
    urlParams: ['customerId'],
  }),

  listPaymentMethods: fusebillMethod({
    method: 'GET',
    path: '/{customerId}/paymentmethods',
    urlParams: ['customerId'],
  }),

});

'use strict';

var FusebillResource = require('../FusebillResource');
var utils = require('../utils');
var fusebillMethod = FusebillResource.method;

module.exports = FusebillResource.extend({

  path: 'customers',
  includeBasic: [
    'create', 'list', 'retrieve', 'update', 'del',
  ],

  /**
   * Customer: Subscription methods
   */

  listSubscriptions: fusebillMethod({
    method: 'GET',
    path: '/{customerId}/subscriptions',
    urlParams: ['customerId'],
  }),

  retrieveSubscription: fusebillMethod({
    method: 'GET',
    path: '/{customerId}/subscriptions/{subscriptionId}',
    urlParams: ['customerId', 'subscriptionId'],
  }),

  /**
   * Customer: Email Preference methods
   */

  listEmailPreferences: fusebillMethod({
    method: 'GET',
    path: '/{customerId}/CustomerEmailPreferences',
    urlParams: ['customerId'],
  }),

  /**
   * Customer: Invoice methods
   */

  listInvoices: fusebillMethod({
    method: 'GET',
    path: '/{customerId}/invoices',
    urlParams: ['customerId'],
  }),

  listDraftInvoices: fusebillMethod({
    method: 'GET',
    path: '/{customerId}/DraftInvoices',
    urlParams: ['customerId'],
  }),

  listPaymentMethods: fusebillMethod({
    method: 'GET',
    path: '/{customerId}/paymentmethods',
    urlParams: ['customerId'],
  }),

  /**
   * Customer: Credit Cards
   */

  listCreditCards: fusebillMethod({
    method: 'GET',
    path: '/{customerId}/CreditCards',
    urlParams: ['customerId'],
  }),

});

Add method to retrieve customer's credit cards


'use strict';

module.exports = function(grunt) {

    grunt.initConfig({
        pkg: grunt.file.readJSON('package.json'),
        jshint: {
            allFiles: ['Gruntfile.js', 'lib/**/*.js', 'test/**/*.js', 'index.js'],
            options: {
                jshintrc: '.jshintrc',
            }
        },
        mochacli: {
            all: ['test/**/*.js'],
            options: {
                reporter: 'spec',
                ui: 'tdd'
            }
        },
        'mocha_istanbul': {
            coveralls: {
                src: [ 'test/lib', 'test/lib/utils' ],
                options: {
                    coverage: true,
                    legend: true,
                    check: {
                        lines: 100,
                        statements: 99
                    },
                    root: './lib',
                    reportFormats: ['lcov']
                }
            }
        }
    })

    grunt.event.on('coverage', function(lcov, done){
        require('coveralls').handleInput(lcov, function(error) {
            if (error) {
                console.log(error)
                return done(error)
            }
            done()
        })
    })

    // Load the plugins
    grunt.loadNpmTasks('grunt-contrib-jshint')
    grunt.loadNpmTasks('grunt-mocha-cli')
    grunt.loadNpmTasks('grunt-mocha-istanbul')

    // Configure tasks
    grunt.registerTask('coveralls', ['mocha_istanbul:coveralls'])
    grunt.registerTask('default', ['test'])

    grunt.registerTask('test', ['mochacli', 'jshint', 'coveralls'])
}

'use strict';

module.exports = function(grunt) {

    grunt.initConfig({
        pkg: grunt.file.readJSON('package.json'),
        jshint: {
            allFiles: ['Gruntfile.js', 'lib/**/*.js', 'test/**/*.js', 'index.js'],
            options: {
                jshintrc: '.jshintrc',
            }
        },
        mochacli: {
            all: ['test/**/*.js'],
            options: {
                reporter: 'spec',
                ui: 'tdd'
            }
        },
        'mocha_istanbul': {
            coveralls: {
                src: [ 'test/lib', 'test/lib/utils' ],
                options: {
                    coverage: true,
                    legend: true,
                    check: {
                        lines: 95,
                        statements: 96
                    },
                    root: './lib',
                    reportFormats: ['lcov']
                }
            }
        }
    })

    grunt.event.on('coverage', function(lcov, done){
        require('coveralls').handleInput(lcov, function(error) {
            if (error) {
                console.log(error)
                return done(error)
            }
            done()
        })
    })

    // Load the plugins
    grunt.loadNpmTasks('grunt-contrib-jshint')
    grunt.loadNpmTasks('grunt-mocha-cli')
    grunt.loadNpmTasks('grunt-mocha-istanbul')

    // Configure tasks
    grunt.registerTask('coveralls', ['mocha_istanbul:coveralls'])
    grunt.registerTask('default', ['test'])

    grunt.registerTask('test', ['mochacli', 'jshint', 'coveralls'])
}

Adjust coverage values to current coverage level


'use strict';

module.exports = function(grunt) {

    grunt.initConfig({
        pkg: grunt.file.readJSON('package.json'),
        jshint: {
            allFiles: ['Gruntfile.js', 'lib/**/*.js', 'test/**/*.js', 'index.js'],
            options: {
                jshintrc: '.jshintrc',
            }
        },
        mochacli: {
            all: ['test/**/*.js'],
            options: {
                reporter: 'spec',
                ui: 'tdd'
            }
        },
        'mocha_istanbul': {
            coveralls: {
                src: [ 'test/lib', 'test/lib/utils' ],
                options: {
                    coverage: true,
                    legend: true,
                    check: {
                        lines: 85,
                        statements: 84
                    },
                    root: './lib',
                    reportFormats: ['lcov']
                }
            }
        }
    })

    grunt.event.on('coverage', function(lcov, done){
        require('coveralls').handleInput(lcov, function(error) {
            if (error) {
                console.log(error)
                return done(error)
            }
            done()
        })
    })

    // Load the plugins
    grunt.loadNpmTasks('grunt-contrib-jshint')
    grunt.loadNpmTasks('grunt-mocha-cli')
    grunt.loadNpmTasks('grunt-mocha-istanbul')

    // Configure tasks
    grunt.registerTask('coveralls', ['mocha_istanbul:coveralls'])
    grunt.registerTask('default', ['test'])

    grunt.registerTask('test', ['mochacli', 'jshint', 'coveralls'])
}

'use strict';

module.exports = function(grunt) {

    grunt.initConfig({
        pkg: grunt.file.readJSON('package.json'),
        jshint: {
            allFiles: ['Gruntfile.js', 'lib/**/*.js', 'test/**/*.js', 'index.js'],
            options: {
                jshintrc: '.jshintrc',
            }
        },
        mochacli: {
            all: ['test/**/*.js'],
            options: {
                reporter: 'spec',
                ui: 'tdd'
            }
        },
        'mocha_istanbul': {
            coveralls: {
                src: [ 'test/lib', 'test/lib/utils' ],
                options: {
                    coverage: true,
                    legend: true,
                    check: {
                        lines: 97,
                        statements: 98
                    },
                    root: './lib',
                    reportFormats: ['lcov']
                }
            }
        }
    })

    grunt.event.on('coverage', function(lcov, done){
        require('coveralls').handleInput(lcov, function(error) {
            if (error) {
                console.log(error)
                return done(error)
            }
            done()
        })
    })

    // Load the plugins
    grunt.loadNpmTasks('grunt-contrib-jshint')
    grunt.loadNpmTasks('grunt-mocha-cli')
    grunt.loadNpmTasks('grunt-mocha-istanbul')

    // Configure tasks
    grunt.registerTask('coveralls', ['mocha_istanbul:coveralls'])
    grunt.registerTask('default', ['test'])

    grunt.registerTask('test', ['mochacli', 'jshint', 'coveralls'])
}

Update coverage thresholds to current values


module.exports = function(grunt) {

  // Initialize global configuration variables.
  var config = grunt.file.readJSON('Gruntconfig.json');
  grunt.initConfig({
    config: config
  });

  // Load all included tasks.
  grunt.loadTasks(__dirname + '/tasks');

  // Define the default task to fully build and configure the project.
  var tasksDefault = [
    'clean:default',
    'mkdir:init',
    'drush:make',
    'symlink:profiles',
    'symlink:modules',
    'symlink:themes',
    'clean:sites',
    'symlink:sites',
    'mkdir:files',
    'copy:static'
  ];
  if (grunt.config.get(['composer', 'update'])) {
    tasksDefault.unshift('composer:update');
  }
  if (grunt.config.get('compass')) {
    tasksDefault.push('compass');
  }
  if (grunt.config.get('behat')) {
    tasksDefault.push('behat');
  }
  grunt.registerTask('default', tasksDefault);

};

module.exports = function(grunt) {

  // Initialize global configuration variables.
  var config = grunt.file.readJSON('Gruntconfig.json');
  grunt.initConfig({
    config: config
  });

  // Load all included tasks.
  grunt.loadTasks(__dirname + '/tasks');

  // Define the default task to fully build and configure the project.
  var tasksDefault = [
    'clean:default',
    'mkdir:init',
    'validate',
    'drush:make',
    'symlink:profiles',
    'symlink:modules',
    'symlink:themes',
    'clean:sites',
    'symlink:sites',
    'mkdir:files',
    'copy:static'
  ];
  if (grunt.config.get(['composer', 'update'])) {
    tasksDefault.unshift('composer:update');
  }
  if (grunt.config.get('compass')) {
    tasksDefault.push('compass');
  }
  if (grunt.config.get('behat')) {
    tasksDefault.push('behat');
  }
  grunt.registerTask('default', tasksDefault);

};

Add validate task to the default build process.


'use strict';

module.exports = function (grunt) {

  require('load-grunt-tasks')(grunt);
  require('time-grunt')(grunt);

  var appConfig = {
    src: 'src',
    dist: 'dist'
  };

  grunt.initConfig({

    paths: appConfig,

    watch: {
      js: {
        files: ['<%= paths.src %>/**/*.js', 'Gruntfile.js'],
        tasks: ['newer:jshint:src', 'newer:jscs:src']
      }
    },

    jshint: {
      options: {
        jshintrc: '.jshintrc',
        reporter: require('jshint-stylish')
      },
      src: {
        src: [
          'Gruntfile.js',
          '<%= paths.src %>/**/*.js'
        ]
      }
    },

    jscs: {
      options: {
        config: './.jscsrc'
      },
      src: {
        src: ['<%= paths.src %>/scripts/**/*.js']
      }
    }
  });

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

'use strict';

module.exports = function (grunt) {

  require('load-grunt-tasks')(grunt);
  require('time-grunt')(grunt);

  var appConfig = {
    src: 'src',
    dist: 'dist'
  };

  grunt.initConfig({

    paths: appConfig,

    watch: {
      js: {
        files: ['<%= paths.src %>/**/*.js', 'Gruntfile.js'],
        tasks: ['newer:jshint:src', 'newer:jscs:src']
      }
    },

    jshint: {
      options: {
        jshintrc: '.jshintrc',
        reporter: require('jshint-stylish')
      },
      src: {
        src: [
          'Gruntfile.js',
          '<%= paths.src %>/**/*.js'
        ]
      }
    },

    jscs: {
      options: {
        config: './.jscsrc'
      },
      src: {
        src: ['<%= paths.src %>/scripts/**/*.js']
      }
    },

    bump: {
      options: {
        files: ['package.json'],
        commitFiles: ['package.json'],
        commitMessage: 'Bump version to v%VERSION%',
        push: false
      }
    },
  });

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

Configure the Grunt bump task


module.exports = function(grunt) {

  grunt.initConfig({
    pkg: grunt.file.readJSON('package.json'),
    jshint: {
      files: ['Gruntfile.js', 'src/js/**']
    },
    watch: {
      files: ['src/**'],
      tasks: ['jshint', 'build'],
      options: {
        livereload: true,
      },
    },
    browserify: {
      dist: {
        src: 'src/js/app.js',
        dest: 'dist/<%= pkg.name %>.js'
      }
    },
    sync: {
      dist: {
        expand: true,
        cwd: 'src/',
        src: ['**', '!js/**'],
        dest: 'dist/'
      },
    },
    clean: ['dist'],
    connect: {
      server: {
        options: {
          port: 9001,
          base: 'dist',
          livereload: true,
          open: true
        }
      }
    }
  });

  // Load all modules
  require('load-grunt-tasks')(grunt);

  grunt.registerTask('build',
    ['browserify', 'sync']
  );

  grunt.registerTask('default',
    ['jshint', 'clean', 'build', 'connect', 'watch']
  );

};

'use strict';

module.exports = function(grunt) {

  grunt.initConfig({
    pkg: grunt.file.readJSON('package.json'),
    jshint: {
      files: ['Gruntfile.js', 'src/js/**'],
      options: {
        jshintrc: true
      }
    },
    watch: {
      files: ['src/**'],
      tasks: ['jshint', 'build'],
      options: {
        livereload: true,
      },
    },
    browserify: {
      dist: {
        src: 'src/js/app.js',
        dest: 'dist/<%= pkg.name %>.js'
      }
    },
    sync: {
      dist: {
        expand: true,
        cwd: 'src/',
        src: ['**', '!js/**'],
        dest: 'dist/'
      },
    },
    clean: ['dist'],
    connect: {
      server: {
        options: {
          port: 9001,
          base: 'dist',
          livereload: true,
          open: true
        }
      }
    }
  });

  // Load all modules
  require('load-grunt-tasks')(grunt);

  grunt.registerTask('build',
    ['browserify', 'sync']
  );

  grunt.registerTask('default',
    ['jshint', 'clean', 'build', 'connect', 'watch']
  );

};

Make jshint actually use .jshintrc.


module.exports = function(grunt) {
  "use strict";
  grunt.loadNpmTasks('grunt-contrib-requirejs');

  grunt.initConfig({
    pkg: grunt.file.readJSON('package.json'),
    requirejs: {
      compile: {
        options: {
          baseUrl: "js/",
          mainConfigFile: "static/js/config.js",
          appDir: "static/",
          dir: "static-built/",
          skipDirOptimize: true,
          generateSourceMaps: true,
          findNestedDependencies: true,
          preserveLicenseComments: false,
          uglify2: {
            mangle: false
          },
          optimize: "uglify2",
          optimizeCss: "none",
          name: "main"
        }
      }
    }
  });
};

module.exports = function(grunt) {
  "use strict";
  grunt.loadNpmTasks('grunt-contrib-requirejs');

  grunt.initConfig({
    pkg: grunt.file.readJSON('package.json'),
    requirejs: {
      compile: {
        options: {
          baseUrl: "js/",
          mainConfigFile: "static/js/config.js",
          appDir: "static/",
          dir: "static-built/",
          skipDirOptimize: true,
          generateSourceMaps: false,
          findNestedDependencies: true,
          preserveLicenseComments: false,
          uglify2: {
            mangle: false
          },
          optimize: "uglify2",
          optimizeCss: "none",
          name: "main"
        }
      }
    }
  });
};

Disable sourcemap generation for optimized static (sourcemap is 3mb)


'use strict';
module.exports = function (sequelize, DataTypes) {
    var Event = sequelize.define('event', {
        id: {
            type: DataTypes.INTEGER,
            autoIncrement: true,
            primaryKey: true
        },
        date: {
            type: DataTypes.DATEONLY
        },
        day: {
            type: DataTypes.INTEGER(1)
        },
        from: {
            type: DataTypes.STRING(5)
        },
        to: {
            type: DataTypes.STRING(5)
        },
        activity: {
            type: DataTypes.STRING
        },
        type: {
            type: DataTypes.STRING
        },
        tutorId: {
            type: DataTypes.INTEGER
        },
        placeId: {
            type: DataTypes.INTEGER
        },
        groupId: {
            type: DataTypes.INTEGER
        },
        note: {
            type: DataTypes.STRING
        },
        blocks: {
            type: DataTypes.INTEGER
        },
        deleted: {
            type: DataTypes.BOOLEAN
            defaultValue: false
        }
    }, {
        indexes: [{
            name: 'unique',
            unique: true,
            fields: ['date',
                'from',
                'to',
                'activity',
                'type',
                'tutorId',
                'placeId',
                'groupId',
                'blocks']
        }],
        timestamps: true
    });
    return Event;
};

'use strict';
module.exports = function (sequelize, DataTypes) {
    var Event = sequelize.define('event', {
        id: {
            type: DataTypes.INTEGER,
            autoIncrement: true,
            primaryKey: true
        },
        date: {
            type: DataTypes.DATEONLY
        },
        day: {
            type: DataTypes.INTEGER(1)
        },
        from: {
            type: DataTypes.STRING(5)
        },
        to: {
            type: DataTypes.STRING(5)
        },
        activity: {
            type: DataTypes.STRING
        },
        type: {
            type: DataTypes.STRING
        },
        tutorId: {
            type: DataTypes.INTEGER
        },
        placeId: {
            type: DataTypes.INTEGER
        },
        groupId: {
            type: DataTypes.INTEGER
        },
        note: {
            type: DataTypes.STRING
        },
        blocks: {
            type: DataTypes.INTEGER
        },
        deleted: {
            type: DataTypes.BOOLEAN,
            defaultValue: false
        }
    }, {
        indexes: [{
            name: 'unique',
            unique: true,
            fields: ['date',
                'from',
                'to',
                'activity',
                'type',
                'tutorId',
                'placeId',
                'groupId',
                'blocks']
        }],
        timestamps: true
    });
    return Event;
};

Fix - missing comma :/


function showTab() {
    var hash = window.location.hash;

    if(hash) {
        $('ul.nav-tabs a[href="' + hash + '"]').tab('show');
    } else {
        $('ul.nav-tabs li:first a').tab('show');
    }
}

$(function(){
  showTab();

  $('.nav-tabs a').click(function (e) {
    $(this).tab('show');
    window.location.hash = this.hash;
  });

  $('.panel-collapse:first').addClass('in');

  window.onpopstate = function(evt) {
      showTab();
  }
});

function showTab() {
    var hash = window.location.hash;

    if(hash) {
        $('ul.nav-tabs a[href="' + hash + '"]').tab('show');
    } else {
        $('ul.nav-tabs li:first a').tab('show');
    }
}

$(function(){
    showTab();

    $('.nav-tabs a').click(function (e) {
        e.preventDefault();

        $(this).tab('show');
        var scrollmem = $('body').scrollTop();
        window.location.hash = this.hash;
        $('html,body').scrollTop(scrollmem);
    });

    $('.panel-collapse:first').addClass('in');

    window.onpopstate = function(evt) {
        showTab();
    }
});

Fix window scrolling when switching tabs


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

moduleForComponent('layers-dialogs/attributes/edit', 'Integration | Component | layers dialogs/attributes/edit', {
  integration: true
});

test('temporary test', function(assert) {
  // Set any properties with this.set('myProperty', 'value');
  // Handle any actions with this.on('myAction', function(val) { ... });
  assert.equal('', '');
});

import { moduleForComponent, test } from 'ember-qunit';
import hbs from 'htmlbars-inline-precompile';

moduleForComponent('layers-dialogs/attributes/edit', 'Integration | Component | layers dialogs/attributes/edit', {
  integration: true
});

test('it renders', function(assert) {
  assert.expect(2);

  // Set any properties with this.set('myProperty', 'value');
  // Handle any actions with this.on('myAction', function(val) { ... });

  this.set('data', {
    field: 'test field'
  });
  this.set('fieldTypes', {
    field: 'string'
  });
  this.set('visible', true);
  this.on('onEditRowDialogApprove', function(data) {
    assert.equal(data.field, 'test field');
  });

  this.render(hbs`
    {{layers-dialogs/attributes/edit
      visible=visible
      data=data
      fieldTypes=fieldTypes
      approve=(action "onEditRowDialogApprove")
    }}`);

  // Component is rendered outside the testing container
  let $window = this.$().closest('#ember-testing-container')
    .siblings('.ui.modals').find('.flexberry-edit-layer-attributes-dialog');
  assert.ok($window.find('.flexberry-textbox').length === 1, 'It renders one input');
  $window.find('.approve.button').click();
});

Add layer attributes edit window test


define(function() {

  'use strict';

  var DELTAS = new Int8Array([0, -49, -36, -25, -16, -9, -4, -1, 0, 1, 4, 9, 16, 25, 36, 49]);

  function open(item) {
    return item.getBytes().then(function(bytes) {
      var samples = new Uint8Array(2 * (bytes.length - 20));
      var value = 128;
      for (var i = 0; i < samples.length; i++) {
        var index = 20 + i >> 1;
        value += (i % 2) ? DELTAS[bytes[index] >> 4] : DELTAS[bytes[index] & 0xf];
        value |= 0;
        samples[i] = (value << 24 >> 24) + 128;
      }
      item.setRawAudio({
        channels: 1,
        samplingRate: 11000,
        bytesPerSample: 1,
        samples: samples,
      });
    });
  }

  return open;

});

define(function() {

  'use strict';

  var DELTAS = new Int8Array([0, -49, -36, -25, -16, -9, -4, -1, 0, 1, 4, 9, 16, 25, 36, 49]);

  function open(item) {
    return item.getBytes().then(function(bytes) {
      var samples = new Uint8Array(2 * (bytes.length - 20));
      var value = 128;
      for (var i = 0; i < samples.length; i++) {
        var index = 20 + i >> 1;
        value += (i % 2) ? DELTAS[bytes[index] >> 4] : DELTAS[bytes[index] & 0xf];
        value |= 0;
        samples[i] = value & 0xff; // (value << 24 >> 24) + 128;
      }
      item.setRawAudio({
        channels: 1,
        samplingRate: 11000,
        bytesPerSample: 1,
        samples: samples,
      });
    });
  }

  return open;

});

Change back to unsigned samples


/**
 * Copyright 2013-present, Facebook, Inc.
 * All rights reserved.
 *
 * This source code is licensed under the BSD-style license found in the
 * LICENSE file in the root directory of this source tree. An additional grant
 * of patent rights can be found in the PATENTS file in the same directory.
 *
 * @providesModule ReactUMDEntry
 */

'use strict';

var React = require('React');

// `version` will be added here by the React module.
var ReactUMDEntry = Object.assign({
  __SECRET_INTERNALS_DO_NOT_USE_OR_YOU_WILL_BE_FIRED: {
    ReactCurrentOwner: require('ReactCurrentOwner'),
  },
}, React);

if (__DEV__) {
  Object.assign(
    ReactUMDEntry.__SECRET_INTERNALS_DO_NOT_USE_OR_YOU_WILL_BE_FIRED,
    {
      // ReactComponentTreeHook should not be included in production.
      ReactComponentTreeHook: require('ReactComponentTreeHook'),
      getNextDebugID: require('getNextDebugID'),
    }
  );
}

module.exports = ReactUMDEntry;

/**
 * Copyright 2013-present, Facebook, Inc.
 * All rights reserved.
 *
 * This source code is licensed under the BSD-style license found in the
 * LICENSE file in the root directory of this source tree. An additional grant
 * of patent rights can be found in the PATENTS file in the same directory.
 *
 * @providesModule ReactUMDEntry
 */

'use strict';

var React = require('React');

// `version` will be added here by the React module.
var ReactUMDEntry = Object.assign(React, {
  __SECRET_INTERNALS_DO_NOT_USE_OR_YOU_WILL_BE_FIRED: {
    ReactCurrentOwner: require('ReactCurrentOwner'),
  },
});

if (__DEV__) {
  Object.assign(
    ReactUMDEntry.__SECRET_INTERNALS_DO_NOT_USE_OR_YOU_WILL_BE_FIRED,
    {
      // ReactComponentTreeHook should not be included in production.
      ReactComponentTreeHook: require('ReactComponentTreeHook'),
      getNextDebugID: require('getNextDebugID'),
    }
  );
}

module.exports = ReactUMDEntry;

Fix erroneous PropTypes access warning


'use strict';

const moment = require('moment');

var isEmpty = function(s) {
  return s === '';
}

var parseDate = function(date) {
  return date;
}

module.exports = {
  isEmpty: isEmpty,
  parseDate: parseDate
}

'use strict';

const moment = require('moment');

var isEmpty = function(e) {
  return typeof(s) == 'string' ? e === '' : e.length == 0
}

var parseDate = function(date) {
  return date;
}

module.exports = {
  isEmpty: isEmpty,
  parseDate: parseDate
}

Update isEmpty() to check array too


import skin from "../skins/base-2.91.wsz";
import audio from "../mp3/llama-2.91.mp3";

/* global SENTRY_DSN */

const { hash } = window.location;
let config = {};
if (hash) {
  try {
    config = JSON.parse(decodeURIComponent(hash).slice(1));
  } catch (e) {
    console.error("Failed to decode config from hash: ", hash);
  }
}

// Turn on the incomplete playlist window
export const skinUrl = config.skinUrl === undefined ? skin : config.skinUrl;
export const audioUrl = config.audioUrl === undefined ? audio : config.audioUrl;
export const noMarquee = config.noMarquee || false;
export const hideAbout = config.hideAbout || false;
export const elementSource = config.elementSource || false;
export const initialState = config.initialState || undefined;
export const sentryDsn = SENTRY_DSN;

import skin from "../skins/base-2.91.wsz";
import audio from "../mp3/llama-2.91.mp3";

/* global SENTRY_DSN */

const { hash } = window.location;
let config = {};
if (hash) {
  try {
    config = JSON.parse(decodeURIComponent(hash).slice(1));
  } catch (e) {
    console.error("Failed to decode config from hash: ", hash);
  }
}

// Turn on the incomplete playlist window
export const skinUrl = config.skinUrl === undefined ? skin : config.skinUrl;
export const audioUrl = config.audioUrl === undefined ? audio : config.audioUrl;
export const noMarquee = config.noMarquee || false;
export const hideAbout = config.hideAbout || false;
export const elementSource = config.elementSource || true;
export const initialState = config.initialState || undefined;
export const sentryDsn = SENTRY_DSN;

Use the elementSource by default


var spawn = require('child_process').spawn;
var path = require('path');
var chai = require('chai');
var expect = chai.expect;

// chai.use(require('chai-fs'));
chai.should();

const MOCK_PROJECT_DIR = path.join(process.cwd(), 'test', 'mock-project');
const STYLE_SRC_DIR = path.join(MOCK_PROJECT_DIR, '_source', 'styles');
const STYLE_DEST_DIR = path.join(MOCK_PROJECT_DIR, 'public', '_client', 'styles');

process.chdir(MOCK_PROJECT_DIR);

function runGulpTask(options, callback) {

    var gulp = spawn('gulp', options)

    gulp.on('close', function() {
        callback();
    });

}

describe('As a user of the cartridge-sass module', function() {

	this.timeout(10000);

	describe('when `gulp sass` is run', function() {

		before(function(done) {
			runGulpTask(['sass'], done)
		})

		it('should generate the main.scss file', function() {
			expect(true).to.be.true;
		})

	})

})

var spawn = require('child_process').spawn;
var path = require('path');
var chai = require('chai');
var expect = chai.expect;

chai.use(require('chai-fs'));
chai.should();

const MOCK_PROJECT_DIR = path.join(process.cwd(), 'test', 'mock-project');
const STYLE_SRC_DIR = path.join(MOCK_PROJECT_DIR, '_source', 'styles');
const STYLE_DEST_DIR = path.join(MOCK_PROJECT_DIR, 'public', '_client', 'styles');

process.chdir(MOCK_PROJECT_DIR);

function runGulpTask(options, callback) {

    var gulp = spawn('gulp', options)

    gulp.on('close', function() {
        callback();
    });

}

describe('As a user of the cartridge-sass module', function() {

	this.timeout(10000);

	describe('when `gulp sass` is run', function() {

		before(function(done) {
			runGulpTask(['sass'], done)
		})

		it('should generate the main.scss file in the _source dir', function() {
			var mainScssFilePath = path.join(STYLE_SRC_DIR, 'main.scss');

			expect(mainScssFilePath).to.be.a.file();
		})

		it('should compile the main.css file in the public styles folder', function() {
			var mainCssFilePath = path.join(STYLE_DEST_DIR, 'main.css');

			expect(mainCssFilePath).to.be.a.file();
		})

		it('should generate the the main.map.css sourcemap file in the public styles folder', function() {
			var mainCssSourceMapFilePath = path.join(STYLE_DEST_DIR, 'main.map.css');

			expect(mainCssSourceMapFilePath).to.be.a.file();
		})

	})

})

Add in tests to check for file output for sass


$(document).ready(function(){
	$('.list_item').hover(function(){
		//mouse enter
		$(this).fadeTo('fast', .5, function(){
			});
		},
		//mouse exit
		function(){
			$('.list_item').fadeTo('fast', 1, function(){
			});
	});
	$('#contact-links li').hover(function(){
		//mouse enter
		$(this).fadeTo('fast', .5, function(){
			});
		},
		//mouse exit
		function(){
			$('#contact-links li').fadeTo('fast', 1, function(){
			});
	});
});

$(document).ready(function(){
	$('.list_item').hover(function(){
		//mouse enter
		$(this).fadeTo('fast', .5, function(){
			});
		},
		//mouse exit
		function(){
			$('.list_item').fadeTo(0, 1, function(){
			});
	});
	$('#contact-links li').hover(function(){
		//mouse enter
		$(this).fadeTo('fast', .5, function(){
			});
		},
		//mouse exit
		function(){
			$('#contact-links li').fadeTo(0, 1, function(){
			});
	});
});

Speed up mouse hover animations.


Tempplate.testing.helpers({
  helper1 () {

    let test1 = hello.world()
    // xd

  }
})

console.log('hello')
console.log('hello')
console.log('hello')
var a = 1
var b = 2
var c = 3

function foo(a, b) {
  console.log('hello')
  console.log('hello')
  console.log('hello')
  var a = 1
  var b=2
  var c=3
}

function bar() {
  var a = 1
  var b = 2
  var c = 3
  console.log('hello')
  console.log('hello')
  console.log('hello')
}

console.log('hello')
console.log('hello')
console.log('hello')
var a = 1
var b = 2
var c = 3

a++

var d = 4;

++a

var e = 5;

Tempplate.testing.helpers({
  helper1 () {

    let test1 = hello.world()
    // xd

  }
})

console.log('hello')
console.log('hello')
console.log('hello')
var a = 1
var b = 2
var c = 3

function foo(a, b) {
  console.log('hello')
  console.log('hello')
  console.log('hello')
  var a = 1
  var b=2
  var c=3
}

function bar() {
  var a = 1
  var b = 2
  var c = 3
  console.log('hello')
  console.log('hello')
  console.log('hello')
}

console.log('hello')
console.log('hello')
console.log('hello')
var a = 1
var b = 2
var c = 3

a++

var d = 4;

a++

if (a) {}

Update semicolon less coding style


const supportedCodes = {
    '0'  : 'Preschool',
    '1'  : 'Primary education',
    '2'  : 'Lower secondary education',
    '3'  : 'Upper secondary education',
    '55' : 'Vocational education',
    '6'  : 'Bachelor\'s / undergraduate',
    '7'  : 'Masters / postgraduate',
    '8'  : 'Doctoral',
    '9'  : 'Other',
};

export default {

    educationLevels: supportedCodes,

    getEducationLevel: function(code) {
        let levelName = supportedCodes[code];

        // if not found try to match against prefix
        while (code && !levelName) {
            code = code.slice(0, -1);
            levelName = supportedCodes[code];
        }

        return levelName || 'Unknown';
    },

};

const supportedCodes = {
    '0'  : 'Preschool',
    '1'  : 'Primary education',
    '2'  : 'Lower secondary education',
    '3'  : 'Upper secondary education',
    '55' : 'Vocational education',
    '6'  : 'Bachelor\'s / undergraduate',
    '7'  : 'Masters / postgraduate',
    '8'  : 'Doctoral',
    '9'  : 'Other',
    '98' : 'Easy Read', // not part of spec
};

export default {

    educationLevels: supportedCodes,

    getEducationLevel: function(code) {
        let levelName = supportedCodes[code];

        // if not found try to match against prefix
        while (code && !levelName) {
            code = code.slice(0, -1);
            levelName = supportedCodes[code];
        }

        return levelName || 'Unknown';
    },

};

Add Easy Read to the list of education levels


'use strict';

let constants = require('./constants');
let util = require('./util');

module.exports = {
    create,
};

class List {}

util.createMethods(List.prototype, constants.propTypes.LIST, [
    'pop',
    'shift',
    'push',
    'unshift',
    'splice',
], true);

function create(realmId, info) {
    let meta = util.createList(List.prototype, realmId, info, true);
    let list = Object.create(meta);

    Object.preventExtensions(list);

    return list;
}

'use strict';

let constants = require('./constants');
let util = require('./util');

module.exports = {
    create,
};

class List {}

util.createMethods(List.prototype, constants.propTypes.LIST, [
    'pop',
    'shift',
    'push',
    'unshift',
    'splice',
], true);

function create(realmId, info) {
    let meta = util.createList(List.prototype, realmId, info, true);
    let list = Object.create(meta);

    // This will make attempts at assigning to out-of-bounds indices throw an exception.
    Object.preventExtensions(list);

    return list;
}

Add comment about usage of preventExtensions


var fs = require('fs');

var exports = {
  saved: {
    videos: {},
  },
};

var FILENAME = 'lethe-data.json';

exports.read = function() {
  try {
    fs.readFile(FILENAME, 'utf8', (err, data) => {
      if (err) {
        // Probably the file just doesn't exist, so don't do anything else
        console.log(err);
      } else {
        exports.saved = JSON.parse(data);
      }
    });
  } catch (e) {
    console.log(e);
  }
};

exports.write = function() {
  fs.writeFile(FILENAME, JSON.stringify(exports.saved), (err) => {
    if (err) {
      console.log(err);
    }
  });
};

exports.possiblyRetrieveVideo = function(argument) {
  if (exports.saved.videos.hasOwnProperty(argument)) return exports.saved.videos[argument].vid;
  else return argument;
};

module.exports = exports;

var fs = require('fs');

var exports = {
  saved: {
    videos: {},
  },
};

var FILENAME = 'lethe-data.json';

exports.read = function() {
  try {
    fs.readFile(FILENAME, 'utf8', (err, data) => {
      if (err) {
        if (err.message.indexOf('ENOENT') > -1) {
          // File doesn't exist
          console.log(`The lethe-data.json file doesn't exist! This is not an error.`);
        } else {
          console.log(err);
        }
      } else {
        exports.saved = JSON.parse(data);
      }
    });
  } catch (e) {
    console.log(e);
  }
};

exports.write = function() {
  fs.writeFile(FILENAME, JSON.stringify(exports.saved), (err) => {
    if (err) {
      console.log(err);
    }
  });
};

exports.possiblyRetrieveVideo = function(argument) {
  if (exports.saved.videos.hasOwnProperty(argument)) return exports.saved.videos[argument].vid;
  else return argument;
};

module.exports = exports;

Add an actual message for a missing lethe-data.json


/**
 * Object representing a XRoad message from which request and response objects
 * inherit from.
 *
 * @module xroad4js/xrmsg
 */

'use strict';

var xml2js = require('xml2js'),
  Builder = xml2js.Builder,
  builder = new Builder(),
  parseString = xml2js.parseString;

/**
 * Creates a new XRMsg
 */
function XRMsg (msg) {
  var xrmsg;

  if (typeof msg !== 'undefined' && msg !== null) {
    xrmsg = msg;
  } else {
    xrmsg = {};
    xrmsg.envelope = {
      header: {
        client: {},
        service: {}
      },
      body: {}
    };
  }

  /**
   * Transfrom data from JS to XRoad SOAP
   *
   * TODO: Validate self
   *
   * @param {Object} obj
   * @param {Function} cb
   * @return {String}
   */
  xrmsg.toXR = function (obj, cb) {
    var xml, callback;

    if ((obj !== null || typeof obj !== 'undefined') &&
        typeof cb === 'function') {
      xml = builder.buildObject(obj);
      callback = cb;
    } else if (obj !== null || typeof obj !== 'undefined') {
      xml = builder.buildObject(xrmsg.envelope);
      callback = obj;
    } else {
      throw new Error('Invalid arguments to toXR function');
    }

    return callback(null, xml);
  };

  /**
   * Parse XRoad SOAP to JS
   *
   * Provides defaults if options argument is not specified.
   *
   * @param {String} xml
   * @param {Object} a
   * @param {Function} b
   * @return {Object}
   */
  xrmsg.fromXR = function (xml, a, b) {
    var options, callback;

    if (xml !== null || xml !== undefined) {
      if (typeof a === 'function' && (b === null || b === undefined)) {
        callback = a;

        /** Default options for parseString */
        options = {
          explicitArray: false
        };

      } else if (typeof a === 'object' && typeof b === 'function') {
        callback = b;
        options = a;
      } else {
        throw new Error('Invalid arguments for fromXR function.');
      }
    } else {
      throw new Error('No XML argument for parsing.');
    }

    return parseString(xml, options, callback);
  };

  return xrmsg;
}

module.exports = XRMsg;

/**
 * Object representing a XRoad message from which request and response objects
 * inherit from.
 *
 * @module xroad4js/xrmsg
 */

'use strict';

/**
 * Creates a new XRMsg
 */
function XRMsg (msg) {
  var xrmsg;

  if (typeof msg !== 'undefined' && msg !== null) {
    xrmsg = msg;
  } else {
    xrmsg = {};
    xrmsg.envelope = {
      header: {
        client: {},
        service: {}
      },
      body: {}
    };
  }

  return xrmsg;
}

module.exports = XRMsg;

Remove functions from the object.


// !!
// This module defines ProseMirror's document model, the data
// structure used to define and inspect content documents. It
// includes:
//
// * The [node](#Node) type that represents document elements
//
// * The [schema](#Schema) types used to tag and constrain the
//   document structure
//
// This module does not depend on the browser API being available
// (i.e. you can load it into any JavaScript environment).

export {Node, PosContext} from "./node"
export {ResolvedPos} from "./resolvedpos"
export {Fragment} from "./fragment"
export {Slice, ReplaceError} from "./replace"
export {Mark} from "./mark"

export {SchemaSpec, Schema, SchemaError,
        NodeType, Block, Textblock, Inline, Text,
        MarkType, Attribute, NodeKind} from "./schema"
export {defaultSchema, Doc, BlockQuote, OrderedList, BulletList, ListItem,
        HorizontalRule, Paragraph, Heading, CodeBlock, Image, HardBreak,
        CodeMark, EmMark, StrongMark, LinkMark} from "./defaultschema"

export {findDiffStart, findDiffEnd} from "./diff"

export {ModelError} from "./error"

// !!
// This module defines ProseMirror's document model, the data
// structure used to define and inspect content documents. It
// includes:
//
// * The [node](#Node) type that represents document elements
//
// * The [schema](#Schema) types used to tag and constrain the
//   document structure
//
// This module does not depend on the browser API being available
// (i.e. you can load it into any JavaScript environment).

export {Node} from "./node"
export {ResolvedPos} from "./resolvedpos"
export {Fragment} from "./fragment"
export {Slice, ReplaceError} from "./replace"
export {Mark} from "./mark"

export {SchemaSpec, Schema, SchemaError,
        NodeType, Block, Textblock, Inline, Text,
        MarkType, Attribute, NodeKind} from "./schema"
export {defaultSchema, Doc, BlockQuote, OrderedList, BulletList, ListItem,
        HorizontalRule, Paragraph, Heading, CodeBlock, Image, HardBreak,
        CodeMark, EmMark, StrongMark, LinkMark} from "./defaultschema"

export {findDiffStart, findDiffEnd} from "./diff"

export {ModelError} from "./error"

Remove export of nonexisting class


"use strict";

var from = require("array-from");

function isSubset(s1, s2, compare) {
    // FIXME: IE11 doesn't support Array.from
    // Potential solutions:
    // - contribute a patch to https://github.com/Volox/eslint-plugin-ie11#readme
    // - https://github.com/mathiasbynens/Array.from (doesn't work with matchers)
    var values1 = from(s1);
    var values2 = from(s2);

    for (var i = 0; i < values1.length; i++) {
        var includes = false;

        for (var j = 0; j < values2.length; j++) {
            if (compare(values2[j], values1[i])) {
                includes = true;
                break;
            }
        }

        if (!includes) {
            return false;
        }
    }

    return true;
}

module.exports = isSubset;

"use strict";

function isSubset(s1, s2, compare) {
    var allContained = true;
    s1.forEach(function(v1) {
        var includes = false;
        s2.forEach(function(v2) {
            if (compare(v2, v1)) {
                includes = true;
            }
        });
        allContained = allContained && includes;
    });

    return allContained;
}

module.exports = isSubset;

Fix failing Set tests in IE11


var path = require('path');
var webpack = require('webpack');
var HtmlWebpackPlugin = require('html-webpack-plugin');

var publicPath = '';
var outputPath = path.join(__dirname, 'dist');

module.exports = {
  devtool: 'source-map',
  entry: [
    'webpack-dev-server/client?http://localhost:8080',
    'webpack/hot/only-dev-server',
    './index',
  ],
  output: {
    path: outputPath,
    filename: 'bundle.js',
    publicPath,
  },
  resolve: {
    extensions: ['', '.js', '.jsx', '.ts', '.tsx'],
    modulesDirectories: ['node_modules'],
  },
  plugins: [
    new webpack.HotModuleReplacementPlugin(),
    new HtmlWebpackPlugin({
      template: 'index.html',
    }),
  ],
  module: {
    loaders: [
      {
        test: /\.tsx?$/,
        loaders: ['react-hot', 'babel', 'ts'],
        include: __dirname,
      },
      {
        test: /\.css?$/,
        loaders: ['style', 'css?modules&sourceMap&localIdentName=[name]__[local]___[hash:base64:5]'],
        include: __dirname,
      },
    ]
  },
  stats: {
    chunkModules: false,
  },
  devServer: {
    contentBase: outputPath,
    publicPath,
    hot: true,
    historyApiFallback: true
  },
};

var path = require('path');
var webpack = require('webpack');
var HtmlWebpackPlugin = require('html-webpack-plugin');

var publicPath = '';
var outputPath = path.join(__dirname, 'dist');

module.exports = {
  devtool: 'source-map',
  entry: [
    'webpack-dev-server/client?http://localhost:8080',
    'webpack/hot/only-dev-server',
    './index',
  ],
  output: {
    path: outputPath,
    filename: 'bundle.js',
    publicPath,
  },
  resolve: {
    extensions: ['', '.js', '.jsx', '.ts', '.tsx'],
    modulesDirectories: ['node_modules'],
  },
  plugins: [
    new webpack.HotModuleReplacementPlugin(),
    new HtmlWebpackPlugin({
      template: 'index.html',
    }),
  ],
  module: {
    loaders: [
      {
        test: /\.tsx?$/,
        loaders: ['react-hot', 'babel', 'ts'],
        include: __dirname,
      },
      {
        test: /\.css?$/,
        loaders: ['style', 'css?modules&sourceMap&localIdentName=[name]__[local]___[hash:base64:5]'],
        include: __dirname,
      },
    ]
  },
  stats: {
    chunkModules: false,
  },
  devServer: {
    contentBase: outputPath,
    publicPath,
    hot: true,
    historyApiFallback: true,
    stats: {
      chunkModules: false,
    },
  },
};

Hide chunk modules in webpack-dev-server


module.exports = {
  extends: ['@commitlint/config-conventional'],
  rules: {
    'subject-case': [
      2,
      'never',
      ['upper-case', 'camel-case', 'kebab-case', 'pascal-case', 'snake-case']
    ]
  }
};

module.exports = {
  extends: ['@commitlint/config-conventional'],
  rules: {
    'subject-case': [
      2,
      'never',
      ['upper-case', 'camel-case', 'kebab-case', 'snake-case']
    ]
  }
};

Allow single-word commit message subjects


/**
 * Camera: what the player sees
 */
class Camera {
    /**
     * Constructor
     *
     * @param {Corridor} rooms
     * @param {Number} fov
     * @param {Number} width
     * @param {Number} height
     * @param {Number} scale
     * @param {Object} stage
     */
    constructor(rooms, fov, width, height, scale, stage) {
        this.rooms = rooms;
        this.fov = fov;
        this.screen = { width, height };
        this.scale = scale;

        this.graphic = new PIXI.Graphics();

        stage.addChild(this.graphic);
    }

    /**
     * Draw the player view at its current position
     *
     * @param {Number} position
     */
    draw(position) {
        const start = (position - fov/2) * this.scale;
        const end = (position + fov/2) * this.scale;
        const rooms = this.rooms.filter(start, end);

        for (var i = rooms.length - 1; i >= 0; i--) {
            this.drawRoom(start, end, rooms[i]);
        }
    }

    /**
     * Draw a room into the camera
     *
     * @param {Number} start
     * @param {Number} end
     * @param {Room} room
     */
    drawRoom(start, end, room) {

    }
}

export default Camera;

/**
 * Camera: what the player sees
 */
class Camera {
    /**
     * Constructor
     *
     * @param {Corridor} rooms
     * @param {Number} fov
     * @param {Number} width
     * @param {Number} height
     * @param {Number} scale
     * @param {Object} stage
     */
    constructor(rooms, fov, width, height, scale, stage) {
        this.rooms = rooms;
        this.width = fov;
        this.halfWidth = fov / 2;
        this.screen = { width, height };
        this.scale = scale;

        this.graphic = new PIXI.Graphics();

        stage.addChild(this.graphic);
    }

    /**
     * Draw the player view at its current position
     *
     * @param {Number} position
     */
    draw(position) {
        const start = (position - this.halfWidth) * this.scale;
        const end = (position + this.halfWidth) * this.scale;
        const rooms = this.rooms.filter(start, end);

        for (var i = rooms.length - 1; i >= 0; i--) {
            this.drawRoom(start, end, rooms[i]);
        }
    }

    /**
     * Draw a room into the camera
     *
     * @param {Number} start
     * @param {Number} end
     * @param {Room} room
     */
    drawRoom(start, end, room) {
        console.log(room);
    }
}

export default Camera;

Fix fov is not defined


"use strict";
/*global window*/

var strictEncode = null;
var strictDecode = null;

if (window && typeof window.btoa === "function") { // When running in a browser.

    strictEncode = function (unencoded) {
        return window.btoa(unencoded);
    };

    strictDecode = function (encoded) {
        return window.atob(encoded);
    };

} else { // When running in Node.

    strictEncode = function (unencoded) {
        return new Buffer(unencoded).toString("base64");
    };

    strictDecode = function (encoded) {
        return new Buffer(encoded, "base64");
    };
}

module.exports = {
    strictEncode: strictEncode,
    strictDecode: strictDecode,

    urlSafeEncode: function (unencoded) {
        return strictEncode(unencoded).replace("+", "-").replace("/", "_");
    },

    urlSafeDecode: function (encoded) {
        return strictDecode(encoded.replace("-", "+").replace("_", "/"));
    }
};

"use strict";
/*global window*/

var strictEncode = null;
var strictDecode = null;

if (typeof window !== "undefined" && typeof window.btoa === "function") { // When running in a browser.

    strictEncode = function (unencoded) {
        return window.btoa(unencoded);
    };

    strictDecode = function (encoded) {
        return window.atob(encoded);
    };

} else { // When running in Node.

    strictEncode = function (unencoded) {
        return new Buffer(unencoded).toString("base64");
    };

    strictDecode = function (encoded) {
        return new Buffer(encoded, "base64");
    };
}

module.exports = {
    strictEncode: strictEncode,
    strictDecode: strictDecode,

    urlSafeEncode: function (unencoded) {
        return strictEncode(unencoded).replace("+", "-").replace("/", "_");
    },

    urlSafeDecode: function (encoded) {
        return strictDecode(encoded.replace("-", "+").replace("_", "/"));
    }
};

Fix for detecting `window` when running in node.


import { win, isIframe } from 'classes/helpers';
import { eventBus } from './eventbus';
import UndoStack from 'classes/UndoStack';

const trackedMutations = {
	updateFieldValue: 'Update to block field',
	addBlock: 'Added block to page',
	reorderBlocks: 'Reordered blocks on page',
	deleteBlock: 'Deleted block on page'
};

const undoStack = new UndoStack({ lock: true });

const undoRedo = store => {
	if(isIframe) {
		return;
	}

	undoStack.setUndoRedo(pageData => {
		store.commit('setPage', JSON.parse(pageData));
		eventBus.$emit('block:hideHoverOverlay', null);
		eventBus.$emit('block:hideSelectedOverlay', null);
	});

	undoStack.setCallback(({ canUndo, canRedo }) => {
		store.commit('updateUndoRedo', { canUndo, canRedo });
	});

	store.subscribe((mutation, state) => {
		if(!trackedMutations[mutation.type]) {
			return;
		}

		undoStack.add(state.page.pageData);
	});
};

export default undoRedo;

export const undoStackInstance = isIframe ?
	win.top.astroUndoStack : (win.astroUndoStack = undoStack);

import { win, isIframe } from 'classes/helpers';
import { eventBus } from './eventbus';
import UndoStack from 'classes/UndoStack';

const trackedMutations = {
	updateFieldValue: 'Update to block field',
	addBlock: 'Added block to page',
	reorderBlocks: 'Reordered blocks on page',
	deleteBlock: 'Deleted block on page'
};

const undoStack = new UndoStack({ lock: true });

const undoRedo = store => {
	if(isIframe) {
		return;
	}

	undoStack.setUndoRedo(pageData => {
		store.commit('setPage', JSON.parse(pageData));
		eventBus.$emit('block:hideHoverOverlay');
		eventBus.$emit('block:updateBlockOverlays');
	});

	undoStack.setCallback(({ canUndo, canRedo }) => {
		store.commit('updateUndoRedo', { canUndo, canRedo });
	});

	store.subscribe((mutation, state) => {
		if(!trackedMutations[mutation.type]) {
			return;
		}

		undoStack.add(state.page.pageData);
	});
};

export default undoRedo;

export const undoStackInstance = isIframe ?
	win.top.astroUndoStack : (win.astroUndoStack = undoStack);

Make sure triggering undo/redo properly updates overlays.


var express = require('express');
var bodyParser = require('body-parser');

var email = require('../email.js');

var router = express.Router();
//'use strict';
//const nodemailer = require('nodemailer');

//router.use(bodyParser.raw({ type: 'text/xml' }));

/* GET home page. */
router.get('/', function(req, res, next) {
  res.render('index.hbs', { title: 'Home' });
});

router.post('/submitemail', (req, res)=> {
  console.log(req.body.email);
  email.sendWelcomeMessage(req.body.email);
  res.send('Email sent');
});

module.exports = router;

var express = require('express');
var bodyParser = require('body-parser');

var email = require('../email.js');

var router = express.Router();
//'use strict';
//const nodemailer = require('nodemailer');

//router.use(bodyParser.raw({ type: 'text/xml' }));

/* GET home page. */
router.get('/', function(req, res, next) {
  res.render('index.hbs', { title: 'Home' });
});

router.post('/submitemail', (req, res)=> {
  console.log(req.body.email);
  email.sendWelcomeMessage(req.body.email);
  email.subscribeEmail();
  res.send('Email sent');
});

module.exports = router;

Include call to MailChimp POST


var gulp = require('gulp');
var watch = require('gulp-watch');
var uglify = require('gulp-uglify');
var print = require('gulp-print');
var tsc  = require('gulp-tsc');

gulp.task('ts_server', function() {
  return gulp.src( [ '*.ts', '**/*.ts', '!./public/**/*.ts', '!./node_modules/**/*.ts' ] )
          .pipe( print(function(filepath) { return "TSC server side: " + filepath; } ) )
          .pipe( tsc( {  module: 'amd', target: 'ES5', sourcemap: false, emitError: false } ) )
          .pipe(gulp.dest('./'))
          .pipe( print(function(filepath) { return "Result: " + filepath; } ) );
});

gulp.task('ts_client', function() {
  return gulp.src( [ './public/**/*.ts' ] )
          .pipe( print( function(filepath) { return "Tsc client side: " + filepath; } ) )
          .pipe( tsc( {  module: 'amd', target: 'ES5', sourcemap: true, emitError: false } ) )
          .pipe(gulp.dest('./'));
});

// Default Task
gulp.task( 'default', ['ts_server','ts_client'] );

var gulp = require('gulp');
var watch = require('gulp-watch');
var uglify = require('gulp-uglify');
var print = require('gulp-print');
var tsc  = require('gulp-tsc');

gulp.task('ts_server', function() {
  return gulp.src( [ '*.ts', '**/*.ts', '!./public/**/*.ts', '!./node_modules/**/*.ts' ] )
          .pipe( print(function(filepath) { return "TSC server side: " + filepath; } ) )
          .pipe( tsc( {  module: 'commonjs', target: 'ES5', sourcemap: false, emitError: false } ) )
          .pipe(gulp.dest('./'))
          .pipe( print(function(filepath) { return "Result: " + filepath; } ) );
});

gulp.task('ts_client', function() {
  return gulp.src( [ './public/**/*.ts' ] )
          .pipe( print( function(filepath) { return "Tsc client side: " + filepath; } ) )
          .pipe( tsc( {  module: 'amd', target: 'ES5', sourcemap: true, emitError: false } ) )
          .pipe(gulp.dest('./'));
});

// Default Task
gulp.task( 'default', ['ts_server','ts_client'] );

Change module type on typescript for server side compilation to commonjs


const bodyParser        = require('body-parser');
const dotenv            = require('dotenv');
const express           = require('express');
const methodOverride    = require('method-override');
const mongoose          = require('mongoose');
const morgan            = require('morgan');
const qr                = require('qrcode');

//  When not launching production mode, we want to load our
//  local .env file; otherwise Docker-Compose uses env_file.
const env = process.env.NODE_ENV || 'development';
if (env === 'development') {
    dotenv.config();
}
const DB_URI = process.env.QR_DB;

console.log('Starting in', env, 'mode.');
console.log('Connecting to database on: ', DB_URI);

// Connect to MongoDB
mongoose.Promise = Promise;
mongoose.connect(DB_URI).then(() => {

    const server = express();
    server.set('env', env);
    server.use(bodyParser.json());
    server.use(methodOverride());
    server.use(morgan('dev'));

    // Routing
    const router = express.Router();
    router.post('/push/register', require('./routes/register'));
    router.post('/push/new', require('./routes/push'));

    server.use(router);
    server.listen(3000, () => {
        console.log('Express Push Device ID service listening on port 3000');
    });

}).catch(console.error.bind(console, 'connection error:'));

const bodyParser        = require('body-parser');
const dotenv            = require('dotenv');
const express           = require('express');
const methodOverride    = require('method-override');
const mongoose          = require('mongoose');
const morgan            = require('morgan');
const qr                = require('qrcode');

//  When not launching production mode, we want to load our
//  local .env file; otherwise Docker-Compose uses env_file.
const env = process.env.NODE_ENV || 'development';
if (env === 'development') {
    dotenv.config();
}
const DB_URI = process.env.QR_DB;

console.log('Starting in', env, 'mode.');
console.log('Connecting to database on: ', DB_URI);

// Connect to MongoDB
mongoose.Promise = Promise;
mongoose.connect(DB_URI).then(() => {

    const server = express();
    server.set('env', env);
    server.use(bodyParser.json());
    server.use(methodOverride());
    server.use(morgan('common'));

    // Routing
    const router = express.Router();
    router.post('/push/register', require('./routes/register'));
    router.post('/push/new', require('./routes/push'));

    server.use(router);
    server.listen(3000, () => {
        console.log('Express Push Device ID service listening on port 3000');
    });

}).catch(console.error.bind(console, 'connection error:'));

Switch push service to use 'common' logging


/*jshint quotmark: false */

"use strict";

var rio = require("../lib/rio"),
    vows = require("vows"),
    assert = require("assert");

var isEnablePlaybackMode = process.env.CI === "true";

vows.describe("Image tests").addBatch({
    "first image test": {
        topic: function () {
            rio.enablePlaybackMode(isEnablePlaybackMode, {
                fileName: "test/image-test.bin"
            });

            rio.evaluate('filename <- tempfile("plot", fileext = ".png")\n' +
                'png(filename)\n' +
                'plot(1:10)\n' +
                'dev.off()\n' +
                'image <- readBin(filename, "raw", 9999)\n' +
                'unlink(filename)\n' +
                'image\n', {
                callback: this.callback
            });
        },

        "get image": function (err, topic) {
            if (!err) {
                assert.equal(topic.length, 2259);
            }
        }
    }

}).export(module);

/*jshint quotmark: false */

"use strict";

var rio = require("../lib/rio"),
    vows = require("vows"),
    assert = require("assert");

var isEnablePlaybackMode = process.env.CI === "true";

vows.describe("Image tests").addBatch({
    "first image test": {
        topic: function () {
            rio.enablePlaybackMode(isEnablePlaybackMode, {
                fileName: "test/image-test.bin"
            });

            rio.evaluate('filename <- tempfile("plot", fileext = ".png")\n' +
                'png(filename)\n' +
                'plot(1:10)\n' +
                'dev.off()\n' +
                'image <- readBin(filename, "raw", 29999)\n' +
                'unlink(filename)\n' +
                'image\n', {
                callback: this.callback
            });
        },

        "get image": function (err, topic) {
            if (!err) {
                // the length depends on graphics system used
                assert(topic.length >= 2259);
            }
        }
    }

}).export(module);

Fix the image test depending on the image length.


const fs = require('fs')

module.exports.init = function init(config, cb) {
  const path = config.path

  const readF = (cb) => {
    fs.readFile(path, 'utf8', (err, data) => {
      if(err) {
        return cb(err)
      }

      const dataArray = data.split('\n')

      if(dataArray.some(entry => entry === undefined || entry.split('=').length < 2)) {
        return cb(new Error(`File ${path} contains malformed entries.`))
      }

      const variables = dataArray.reduce((obj, entry) => {
        const splitEntry = entry.split('=')
        obj[splitEntry[0]] = splitEntry[1]
      }, {})

      cb(null, variables)
    })
  }

  cb(null, {read: readF})
}

const fs = require('fs')

module.exports.init = function init(config, cb) {
  const path = config.path

  const readF = (cb) => {
    fs.readFile(path, 'utf8', (err, data) => {
      if(err) {
        return cb(err)
      }

      const dataArray = data.split('\n')

      if(dataArray.some(entry => entry === undefined || entry.split('=').length < 2)) {
        return cb(new Error(`File ${path} contains malformed entries.`))
      }

      const variables = dataArray.reduce((obj, entry) => {
        const splitEntry = entry.split('=')
        obj[splitEntry[0]] = splitEntry[1]
        return obj
      }, {})

      cb(null, variables)
    })
  }

  cb(null, {read: readF})
}

Fix to return obj in reduce operation


var Roadmap = require('./roadmapModel.js'),
    handleError = require('../../util.js').handleError;

module.exports = {

  createRoadmap : function (req, res, next) {
    var newRoadmap = req.body;
    Roadmap(newRoadmap).save()
      .then(function(dbResults){
        res.status(201).json(dbResults);
      })
      .catch(handleError(next));
  },
  getRoadmaps : function (req, res, next) {
    Roadmap.find({})
      .then(function(dbResults){
        res.status(200).json(dbResults);
      })
      .catch(handleError(next));
  },
  getRoadmapByID : function (req, res, next) {
    var _id = req.params.roadmapID;
    Roadmap.findById(_id)
      .then(function(dbResults){
        res.json(dbResults);
      })
      .catch(handleError(next));
  },
  updateRoadmap : function (req, res, next) {
    var _id = req.params.roadmapID;
    var updateCommand = req.body;
    Roadmap.findByIdAndUpdate(_id, updateCommand)
      .then(function(dbResults){
        res.json(dbResults);
      })
      .catch(handleError(next));
  },
  deleteRoadmap : function (req, res, next) {

  }
};

var Roadmap = require('./roadmapModel.js'),
    handleError = require('../../util.js').handleError;

module.exports = {

  createRoadmap : function (req, res, next) {
    var newRoadmap = req.body;
    Roadmap(newRoadmap).save()
      .then(function(dbResults){
        res.status(201).json(dbResults);
      })
      .catch(handleError(next));
  },
  getRoadmaps : function (req, res, next) {
    Roadmap.find({})
      .then(function(dbResults){
        res.status(200).json(dbResults);
      })
      .catch(handleError(next));
  },
  getRoadmapByID : function (req, res, next) {
    var _id = req.params.roadmapID;
    Roadmap.findById(_id)
      .then(function(dbResults){
        res.json(dbResults);
      })
      .catch(handleError(next));
  },
  updateRoadmap : function (req, res, next) {
    var _id = req.params.roadmapID;
    var updateCommand = req.body;
    Roadmap.findByIdAndUpdate(_id, updateCommand)
      .then(function(dbResults){
        res.json(dbResults);
      })
      .catch(handleError(next));
  },
  deleteRoadmap : function (req, res, next) {
    var _id = req.params.roadmapID;
    Roadmap.findByIdAndRemove(_id)
      .then(function(dbResults){
        res.json(dbResults);
      })
      .catch(handleError(next));
  }
};

Add route handler for DELETE /api/roadmaps/:roadmapID


var $ = document.getElementById.bind(document);
var exampleTests = require('./example-tests');
var Main = require('../src/main/main-controller');

var providedByAceEditor = function() {/* noop() */};
var isMac = navigator.platform.indexOf('Mac') === 0;
var metaKey = isMac ? 'Meta' : 'Control';
var shortcuts = [
  [[metaKey, 'S'], executeTestCode, 'Save+Run'],
  [[metaKey, 'D'], providedByAceEditor(), 'Delete line'],
  [[metaKey, 'Shift', 'D'], providedByAceEditor(), 'Duplicate line'],
  [[metaKey, '/'], providedByAceEditor(), 'Comment in/out line'],
  [['Shift', 'F6'], refactoringRename, 'Rename (refactoring)']
];

var main = new Main($('tddbin'), {
  initialContent: exampleTests.simplePassingTestCode,
  iframeSrcUrl: '../src/test-runner/mocha/spec-runner.html',
  shortcuts: shortcuts
});

function executeTestCode() {
  main.runEditorContent();
}
function refactoringRename() {
  main.placeCursorsForRenaming();
}

var $ = document.getElementById.bind(document);
var exampleTests = require('./example-tests');
var Main = require('../src/main/main-controller');

var providedByAceEditor = function() {/* noop() */};
var isMac = navigator.platform.indexOf('Mac') === 0;
var metaKey = isMac ? 'Meta' : 'Control';
var shortcuts = [
  [[metaKey, 'S'], executeTestCode, 'Save+Run'],
  [[metaKey, 'D'], providedByAceEditor(), 'Delete line'],
  [[metaKey, 'Shift', 'D'], providedByAceEditor(), 'Duplicate line'],
  [[metaKey, '/'], providedByAceEditor(), 'Comment in/out line'],

  [[metaKey, 'I', 'E'], providedByAceEditor(), '???'],
  [[metaKey, 'I', 'I'], providedByAceEditor(), '???'],
  [[metaKey, 'I', 'E', 'E'], providedByAceEditor(), '???'],

  [['Shift', 'F6'], refactoringRename, 'Rename (refactoring)']
];

var main = new Main($('tddbin'), {
  initialContent: exampleTests.simplePassingTestCode,
  iframeSrcUrl: '../src/test-runner/mocha/spec-runner.html',
  shortcuts: shortcuts
});

function executeTestCode() {
  main.runEditorContent();
}
function refactoringRename() {
  main.placeCursorsForRenaming();
}

Add some shortcuts we currently have in tddbin.


// Copyright (c) 2015 Uber Technologies, Inc.
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
'use strict';

var safeParse = require('../../lib/util.js').safeParse;

module.exports = function createLookupHandler(ringpop) {
    return function handleLookup(arg1, arg2, hostInfo, callback) {
        var body = safeParse(arg2.toString());
        if (body && body.key) {
            callback(null, null, JSON.stringify({
                dest: ringpop.lookup(body.key)
            }));
        }
    };
};

// Copyright (c) 2015 Uber Technologies, Inc.
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
'use strict';

var safeParse = require('../../lib/util.js').safeParse;
var errors = require('../../lib/errors.js');

module.exports = function createLookupHandler(ringpop) {
    return function handleLookup(arg1, arg2, hostInfo, callback) {
        var body = safeParse(arg2);
        if (body && body.key) {
            return callback(null, null, JSON.stringify({
                dest: ringpop.lookup(body.key)
            }));
        } else {
            return callback(errors.ArgumentRequiredError({ argument: 'key' }));
        }
    };
};

Return an error when `key` is not in body.


Meteor.publish('posts', function(options) {
  return Posts.find({}, options);
});

Meteor.publish('comments', function(postId) {
  check(postId, String);
  return Comments.find({ postId: postId });
});

Meteor.publish('notifications', function() {
  return Notifications.find({ userId: this.userId, read: false });
});

Meteor.publish('singlePost', function(id) {
  check(id, String)
  return Posts.find(id);
});

Add publication for a single post


export { default } from './__content__'
export { default as EditorOld } from './editor-old'
export { default as EditorNew } from './editor-new.connected'
export { default as EditorSlate } from './editor-slate'
export { default as ActionButton } from './action-button'
export { default as Layer } from './layer'

export { default } from './__content__'
export { default as EditorOld } from './editor-old.connected'
export { default as EditorNew } from './editor-new.connected'
export { default as EditorSlate } from './editor-slate'
export { default as ActionButton } from './action-button'
export { default as Layer } from './layer'

Fix import connected to old editor


const CACHE = 'network-or-cache'

self.addEventListener('install', evt => {
  console.log('Service worker is being installed.')
})

self.addEventListener('activate', evt => {
  console.log('Service worker is being activated.')
});

self.addEventListener('fetch', evt => {
  evt.respondWith(fromNetwork(evt.request, 500).catch(() => {
    return fromCache(evt.request)
  }))
})

function fromNetwork(request, timeout) {
  return new Promise((fulfill, reject) => {
    const timeoutId = setTimeout(reject, timeout)
    fetch(request).then(response => {
      clearTimeout(timeoutId)

      if(!response || response.status !== 200 || request.method !== 'GET') {
        return fulfill(response)
      }

      // IMPORTANT: Clone the response. A response is a stream
      // and because we want the browser to consume the response
      // as well as the cache consuming the response, we need
      // to clone it so we have two streams.
      const responseToCache = response.clone()

      caches.open(CACHE)
        .then(cache => {
          cache.put(request, responseToCache);
        })

      return fulfill(response)
    }, reject)
  })
}

function fromCache(request) {
  return caches.open(CACHE).then(cache => {
    return cache.match(request).then(matching => {
      return matching || Promise.reject('no-match')
    })
  })
}

const CACHE = 'network-or-cache'
const TIMEOUT = 50

self.addEventListener('install', evt => {
  console.log('Service worker is being installed.')
})

self.addEventListener('activate', evt => {
  console.log('Service worker is being activated.')
});

self.addEventListener('fetch', evt => {
  evt.respondWith(fromNetwork(evt.request, TIMEOUT).catch(() => {
    return fromCache(evt.request)
  }))
})

function fromNetwork(request, timeout) {
  return new Promise((fulfill, reject) => {
    const timeoutId = setTimeout(reject, timeout)
    fetch(request).then(response => {
      clearTimeout(timeoutId)

      if(!response || response.status !== 200 || request.method !== 'GET') {
        return fulfill(response)
      }

      // IMPORTANT: Clone the response. A response is a stream
      // and because we want the browser to consume the response
      // as well as the cache consuming the response, we need
      // to clone it so we have two streams.
      const responseToCache = response.clone()

      caches.open(CACHE)
        .then(cache => {
          cache.put(request, responseToCache);
        })

      return fulfill(response)
    }, reject)
  })
}

function fromCache(request) {
  return caches.open(CACHE).then(cache => {
    return cache.match(request).then(matching => {
      return matching || Promise.reject('no-match')
    })
  })
}

Decrease timeout to make app start faster


jQuery.ajaxSetup({
    cache : false
});

jQuery.fn.isInViewport = function (offset) {
    var elementTop, elementBottom, viewportTop, viewportBottom;

    if (typeof offset === "undefined") {
        offset = 0;
    }

    elementTop = jQuery(this).offset().top;
    elementBottom = elementTop + jQuery(this).outerHeight();
    viewportTop = jQuery(window).scrollTop();
    viewportBottom = viewportTop + jQuery(window).height();

    return elementBottom - offset > viewportTop && elementTop + offset < viewportBottom;
};

jQuery.debounce = function (delay, callback) {
    var timeout = null;

    return function () {
        var _arguments = arguments;

        if (timeout) {
            clearTimeout(timeout);
        }

        timeout = setTimeout(function () {
            callback.apply(null, _arguments);
            timeout = null;
        }, delay);
    };
}

jQuery.ajaxSetup({
    cache : false
});

jQuery.fn.isInViewport = function (offset) {
    var elementTop, elementBottom, viewportTop, viewportBottom;

    if (!jQuery(this).length) {
        return;
    }

    // set undefined offset to 0
    if (typeof offset === "undefined") {
        offset = 0;
    }

    elementTop = jQuery(this).offset().top;
    elementBottom = elementTop + jQuery(this).outerHeight();
    viewportTop = jQuery(window).scrollTop();
    viewportBottom = viewportTop + jQuery(window).height();

    return (elementBottom - offset) > viewportTop && (elementTop + offset) < viewportBottom;
};

jQuery.debounce = function (delay, callback) {
    var timeout = null;

    return function () {
        var _arguments = arguments;

        if (timeout) {
            clearTimeout(timeout);
        }

        timeout = setTimeout(function () {
            callback.apply(null, _arguments);
            timeout = null;
        }, delay);
    };
}

Adjust function isInViewport to be more fail-safe.


class City {
	constructor(name,x,y,game) {
		this.name = name;
		this.x = x;
		this.y = y;

        this.sprite = game.add.sprite(
            Math.floor(this.x * game.world.width),
            Math.floor(this.y * game.world.height),
            'city'
        );
        this.sprite.name = this.name
        this.sprite.inputEnabled = true;
        this.sprite.input.useHandCursor = true;
        this.sprite.events.onInputUp.add(function (e) {
            e.game.state.clickedCity = e;
            e.game.state.current = e
            console.log(e);
        }, this);
	}
}

class City extends Phaser.Sprite {
	constructor(name,x,y,game) {

        super(game, Math.floor(x * game.world.width), Math.floor(y * game.world.height), 'city')
        this.game.add.existing(this)

		this.name = name;

        this.inputEnabled = true;
        this.input.useHandCursor = true;
        this.events.onInputUp.add(function (e) {
            this.game.state.states.overworld.clickedCity = this;
            this.game.state.states.overworld.current = this;
        }, this);
	}
}

Make City extend Phaser.Sprite for ease of use/consistency


var app = require("app");
var BrowserWindow = require("browser-window");
var Menu = require("menu");
var mainWindow = null;

app.on("window-all-closed", function(){
  app.quit();
});

app.on("ready", function () {
  mainWindow = new BrowserWindow({
      width: 475,
      height: 370,
      "min-width": 475,
      "min-height": 370
  });
  mainWindow.loadUrl("https://usecamino.com");
  mainWindow.on("closed", function () {
      mainWindow =  null;
  });

  // Create the Application's main menu
  var template = [{
      label: "Application",
      submenu: [
          { label: "About Application", selector: "orderFrontStandardAboutPanel:" },
          { type: "separator" },
          { label: "Quit", accelerator: "Command+Q", click: function() { app.quit(); }}
      ]}, {
      label: "Edit",
      submenu: [
          { label: "Undo", accelerator: "CmdOrCtrl+Z", selector: "undo:" },
          { label: "Redo", accelerator: "Shift+CmdOrCtrl+Z", selector: "redo:" },
          { type: "separator" },
          { label: "Cut", accelerator: "CmdOrCtrl+X", selector: "cut:" },
          { label: "Copy", accelerator: "CmdOrCtrl+C", selector: "copy:" },
          { label: "Paste", accelerator: "CmdOrCtrl+V", selector: "paste:" },
          { label: "Select All", accelerator: "CmdOrCtrl+A", selector: "selectAll:" }
      ]}
  ];

  Menu.setApplicationMenu(Menu.buildFromTemplate(template));
});

var menubar = require('menubar');
var electron = require('electron');
var Menu = require("menu");

var mb = menubar({
  "index": 'https://usecamino.com',
  "preloadWindow": true,
  "width": 475,
  "height": 370,
  "icon": "icon.png",
  "show-dock-icon": false
});

mb.on('ready', function ready () {
  console.log('app is running!');
});

mb.on('after-create-window', function() {
  // Add copy-paste, undo and quit keyboard shortcuts
  var shortcuts = [{
    label: "Shortcuts",
    submenu: [
        { label: "Undo", accelerator: "CmdOrCtrl+Z", selector: "undo:" },
        { label: "Redo", accelerator: "Shift+CmdOrCtrl+Z", selector: "redo:" },
        { type: "separator" },
        { label: "Cut", accelerator: "CmdOrCtrl+X", selector: "cut:" },
        { label: "Copy", accelerator: "CmdOrCtrl+C", selector: "copy:" },
        { label: "Paste", accelerator: "CmdOrCtrl+V", selector: "paste:" },
        { label: "Select All", accelerator: "CmdOrCtrl+A", selector: "selectAll:" },
        { label: "Quit", accelerator: "Command+Q", click: function() { app.quit(); }}
    ]}
  ];

  Menu.setApplicationMenu(Menu.buildFromTemplate(shortcuts));
})

Revert to docked app + keyboard shortcuts