var middleware  = require('../middleware');
var Router      = require('express').Router();
var request     = require('superagent');

const IMG_URL = process.env.IMG_URL || 'http://localhost:9003';

Router.route('/version/api')
    .get(middleware.authenticate('user', 'guest'))
    .get(function(req, res) {
        var version = process.env.VERSION || 'unknown';
        version = {"version":version};
        return res.json(200, version);
    });

Router.route('/version/img')
    .get(middleware.authenticate('user', 'guest'))
    .get(function(req, res) {
        var version = {"version":"unknown"};
        request.get(IMG_URL+'/version')
            .end(function(err, response){
                version = {"version":response.body.version};
                return res.json(200, version);
            });
    });

module.exports = Router;

var middleware  = require('../middleware');
var Router      = require('express').Router();
var request     = require('superagent');

const IMG_URL = process.env.IMG_URL || 'http://localhost:9003';

Router.route('/version/api')
    .get(middleware.authenticate('user', 'guest'))
    .get(function(req, res) {
        var version = process.env.VERSION || 'unknown';
        version = {"version":version};
        return res.json(200, version);
    });

Router.route('/version/img')
    .get(middleware.authenticate('user', 'guest'))
    .get(function(req, res) {
        var version = {"version":"unknown"};
        request.get(IMG_URL+'/version')
            .end(function(err, response){
                try{
                    version = {"version":response.body.version};
                } catch(exception) {
                    version = {"version":"error"};
                }
                return res.json(200, version);
            });
    });

module.exports = Router;

Handle error in case img service is down.


var fs = require('fs');

module.exports = function postContent(post) {
    var fd = fs.openSync(post, 'rs');
    var contents = fs.readFileSync(post, {encoding: 'utf8'});
    contents = contents.replace(/^-+\r?(\n[^-]+)+\n-+/, '');
    return contents.trim();
};

var fs = require('fs');

module.exports = function postContent(post) {
    var fd = fs.openSync(post, 'rs');
    var contents = fs.readFileSync(post, {encoding: 'utf8'});
    contents = contents.replace(/^-+(\r?\n[^-].+)+\r?\n-+/, '');
    return contents.trim();
};

Fix regex for stripping post frontmatter


module.exports = function (sch) {
  var noComments = function (line) {
    var i = line.indexOf('//')
    return i > -1 ? line.slice(0, i) : line
  }

  var noMultilineComments = function () {
    var inside = false
    return function (token) {
      if (token === '/*') {
        inside = true
        return false
      }
      if (token === '*/') {
        inside = false
        return false
      }
      return !inside
    }
  }

  var trim = function (line) {
    return line.trim()
  }

  return sch
    .replace(/([;,{}=\[\]<>]|\/\*|\*\/)/g, ' $1 ')
    .split(/\n/)
    .map(trim)
    .filter(Boolean)
    .map(noComments)
    .filter(Boolean)
    .join('\n')
    .split(/\s+|\n+/gm)
    .filter(noMultilineComments())
}

module.exports = function (sch) {
  var noComments = function (line) {
    var i = line.indexOf('//')
    return i > -1 ? line.slice(0, i) : line
  }

  var noMultilineComments = function () {
    var inside = false
    return function (token) {
      if (token === '/*') {
        inside = true
        return false
      }
      if (token === '*/') {
        inside = false
        return false
      }
      return !inside
    }
  }

  var trim = function (line) {
    return line.trim()
  }

  return sch
    .replace(/([;,{}=\[\]<>]|\/\*|\*\/)/g, ' $1 ')
    .split(/\n/)
    .map(trim)
    .filter(Boolean)
    .map(noComments)
    .map(trim)
    .filter(Boolean)
    .join('\n')
    .split(/\s+|\n+/gm)
    .filter(noMultilineComments())
}

Fix tokenizing of comment after closing brace of last message


module.exports = {
    '/':{
      controller: require('../../../controllers/application-country'),
      fields: ['apply-uk', 'application-country'],
      controller: require('../../../controllers/go-overseas'),
      backLink: '/../priority_service_170215/get-urgent-passport/premium-online',
      next: '/what-do-you-want-to-do', /* if Yes is selected */
      nextAlt: '../not-eligible', /* if they are from Germany/France */
      nextAltAlt:'what-do-you-want-to-do-overseas'
    },
    '/what-do-you-want-to-do': {
        fields: ['what-to-do'],
        backLink: './',
        next: '/dob'
    },
    '/dob': {
      fields: ['age-day', 'age-year', 'age-month'],
      controller: require('../../../controllers/go-overseas'),
      backLink: './what-do-you-want-to-do',
      next: '/../filter', /* if they are from the UK */
      nextAlt: '../overseas', /* if they are from Germany/France */
      nextAltAlt:'../overseas-not-eligible' /* if they are from Afganistan */
    }
};

module.exports = {
    '/':{
      controller: require('../../../controllers/application-country'),
      fields: ['apply-uk', 'application-country'],
      controller: require('../../../controllers/go-overseas'),
      backLink: '/../priority_service_170315/get-urgent-passport/premium-online',
      next: '/what-do-you-want-to-do', /* if Yes is selected */
      nextAlt: '../not-eligible', /* if they are from Germany/France */
      nextAltAlt:'what-do-you-want-to-do-overseas'
    },
    '/what-do-you-want-to-do': {
        fields: ['what-to-do'],
        backLink: './',
        next: '/dob'
    },
    '/dob': {
      fields: ['age-day', 'age-year', 'age-month'],
      controller: require('../../../controllers/go-overseas'),
      backLink: './what-do-you-want-to-do',
      next: '/../filter', /* if they are from the UK */
      nextAlt: '../overseas', /* if they are from Germany/France */
      nextAltAlt:'../overseas-not-eligible' /* if they are from Afganistan */
    }
};

Fix back link on filter-common


/** @namespace H5PUpgrades */
var H5PUpgrades = H5PUpgrades || {};

H5PUpgrades['H5P.Agamotto'] = (function ($) {
  return {
    1: {
      3: function (parameters, finished) {
        // Update image items
        parameters.items = parameters.items.map( function (item) {
          // Create new image structure
          var newImage = {
            library: 'H5P.Image 1.0',
            // We avoid using H5P.createUUID since this is an upgrade script and H5P function may change
            subContentId: 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function(char) {
              var random = Math.random()*16|0, newChar = char === 'x' ? random : (random&0x3|0x8);
              return newChar.toString(16);
            }),
            params: {
              alt: item.labelText || '',
              contentName: 'Image',
              title: item.labelText || '',
              file: item.image
            }
          };

          // Compose new item
          item = {
            description: item.description,
            image: newImage,
            labelText: item.labelText
          };

          return item;
        });

        finished(null, parameters);
      }
    }
  };
})(H5P.jQuery);

/** @namespace H5PUpgrades */
var H5PUpgrades = H5PUpgrades || {};

H5PUpgrades['H5P.Agamotto'] = (function ($) {
  return {
    1: {
      3: function (parameters, finished) {
        // Update image items
        if (parameters.items) {
          parameters.items = parameters.items.map( function (item) {
            // Create new image structure
            var newImage = {
              library: 'H5P.Image 1.0',
              // We avoid using H5P.createUUID since this is an upgrade script and H5P function may change
              subContentId: 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function(char) {
                var random = Math.random()*16|0, newChar = char === 'x' ? random : (random&0x3|0x8);
                return newChar.toString(16);
              }),
              params: {
                alt: item.labelText || '',
                contentName: 'Image',
                title: item.labelText || '',
                file: item.image
              }
            };

            // Compose new item
            item = {
              description: item.description,
              image: newImage,
              labelText: item.labelText
            };

            return item;
          });
        }

        finished(null, parameters);
      }
    }
  };
})(H5P.jQuery);

Fix upgrade script: Check for empty items


// ==UserScript==
// @name        FPL Iwon scroller
// @namespace   mikemanger
// @grant       none
// @include     https://fplcomps.iwn.io/*/*
// @updateURL   https://raw.githubusercontent.com/mikemanger/userscripts/master/fpl-iwon-scroller.user.js
// @downloadURL https://raw.githubusercontent.com/mikemanger/userscripts/master/fpl-iwon-scroller.user.js
// @version     1.0.0
// ==/UserScript==

function auto_scrolling() {
	window.scrollTo(0, document.body.scrollHeight);
}
setInterval(auto_scrolling, 500);

// ==UserScript==
// @name        FPL Iwon scroller
// @namespace   mikemanger
// @grant       none
// @include     https://fplcomps.iwn.io/*/*
// @updateURL   https://raw.githubusercontent.com/mikemanger/userscripts/master/fpl-iwon-scroller.user.js
// @downloadURL https://raw.githubusercontent.com/mikemanger/userscripts/master/fpl-iwon-scroller.user.js
// @version     1.0.1
// ==/UserScript==

function auto_scrolling() {
	window.scrollTo(0, document.body.scrollHeight);
}
setInterval(auto_scrolling, 500);

jQuery(document).ready(function(){
	jQuery('.submitButton').click();
});

Add clicker to fpl script


import test from 'tape'
import chunkify from './index'

test('should require an array', t => {
  t.throws(() => {
    chunkify.array()
  }, /Usage: chunkify.array\(Array array, Function fn, \[Object] options\) - bad array/);
  t.end()
});

test('should require a function', t => {
  t.throws(() => {
    chunkify.array([])
  }, /Usage: chunkify.array\(Array array, Function fn, \[Object] options\) - bad fn/);
  t.end()
});

test.skip('should delegate options deserialization', t => {

});

test.skip('should return a promise', t => {

});

test.skip('should not invoke fn when given an empty array', t => {

});

test.skip('should invoke fn successively between "chunk" iterations', t => {

});

test.skip('should yield after "chunk" iterations', t => {

});

test.skip('should start again after yielding in "delay" milliseconds', t => {

});

test.skip('should resolve with the array after processing completes', t => {

});

test.skip('should reject the promise if an error is thrown', t => {

});

test.skip('should not yield after "chunk" iterations if processing is complete', t => {

});

import test from 'tape'
import chunkify from './index'
import ChunkifyOptions from './options'
import sinon from 'sinon'

let spy_ChunkifyOptions_of = (callback) => {
  let spy = sinon.spy(ChunkifyOptions, 'of');
  callback(spy);
  ChunkifyOptions.of.restore()
};

test('should require an array', t => {
  t.throws(() => {
    chunkify.array()
  }, /Usage: chunkify.array\(Array array, Function fn, \[Object] options\) - bad array/);
  t.end()
});

test('should require a function', t => {
  t.throws(() => {
    chunkify.array([])
  }, /Usage: chunkify.array\(Array array, Function fn, \[Object] options\) - bad fn/);
  t.end()
});

test('should delegate options deserialization', t => {
  let options = {};

  spy_ChunkifyOptions_of((chunkify_options) => {
    chunkify.array([], function() {}, options);
    t.ok(chunkify_options.calledWith(options));
  });

  t.end()
});

test.skip('should return a promise', t => {

});

test.skip('should not invoke fn when given an empty array', t => {

});

test.skip('should invoke fn successively between "chunk" iterations', t => {

});

test.skip('should yield after "chunk" iterations', t => {

});

test.skip('should start again after yielding in "delay" milliseconds', t => {

});

test.skip('should resolve with the array after processing completes', t => {

});

test.skip('should reject the promise if an error is thrown', t => {

});

test.skip('should not yield after "chunk" iterations if processing is complete', t => {

});

Test delegation of options parsing


import BaseController from 'portia-web/controllers/base-controller';

export default BaseController.extend({
    breadCrumb: null,
    _breadCrumb: null,
});

import BaseController from '../base-controller';

export default BaseController.extend({
    breadCrumb: null,
    _breadCrumb: null,
});

Use relative path for template


describe('idai field app', function() {

    function createObject() {

        return clickCreateObject()
                .then(selectTypeObject)
                .then(typeInIdentifier)
                .then(saveObject);
    }

    function clickCreateObject() {
        return element(by.id('object-overview-button-create-object')).click()
    }

    function selectTypeObject() {
        return element(by.id('create-object-option-0')).click();
    }

    function typeInIdentifier() {
        return element(by.id('object-edit-input-identifier')).clear().sendKeys('1').sendKeys('2');
    }

    function saveObject() {
        return element(by.id('object-edit-button-save-object')).click();
    }

    beforeEach(function(){
        browser.get('/');
    });

    it('should create a new object of first listed type ', function() {
        createObject()
            .then(function(){
                expect(element(by.id('object-overview-identifier-0')).getText()).toEqual("12");
            });
    });

    it('should warn if an existing id is used ', function() {
        createObject()
            .then(createObject)
            .then(function(){
                expect(element(by.id('message-0')).getText()).
                    toEqual("Objekt Identifier existiert bereits. Bei Klick auf ein anderes Objekt wird der ursprüngliche Zustand wiederhergestellt.");
            });
    });
});

describe('idai field app', function() {

    function createObject() {

        return clickCreateObject()
                .then(selectTypeObject)
                .then(typeInIdentifier)
                .then(saveObject);
    }

    function clickCreateObject() {
        return element(by.id('object-overview-button-create-object')).click()
    }

    function selectTypeObject() {
        return element(by.id('create-object-option-0')).click();
    }

    function typeInIdentifier() {
        return element(by.id('object-edit-input-identifier')).clear().sendKeys('1').sendKeys('2');
    }

    function typeInIdentifierInSearchField() {
        return element(by.id('object-search')).clear().sendKeys('1').sendKeys('2');
    }

    function saveObject() {
        return element(by.id('object-edit-button-save-object')).click();
    }

    beforeEach(function(){
        browser.get('/');
    });

    it('should create a new object of first listed type ', function() {
        createObject()
            .then(function(){
                expect(element(by.id('object-overview-identifier-0')).getText()).toEqual("12");
            });
    });

    it('should warn if an existing id is used ', function() {
        createObject()
            .then(createObject)
            .then(function(){
                expect(element(by.id('message-0')).getText()).
                    toEqual("Objekt Identifier existiert bereits. Bei Klick auf ein anderes Objekt wird der ursprüngliche Zustand wiederhergestellt.");
            });
    });

    it('should find it by its identifier', function() {
        createObject()
            .then(typeInIdentifierInSearchField)
            .then(function(){
                expect(element(by.id('object-overview-identifier-0')).getText()).toEqual("12");
            });
    });
});

Add basic e2e for search field.


/**
 * Created by Peter on 2016/10/23.
 */
// CBoard settings
var settings = {
    preferredLanguage: "cn" // en/cn: Switch language to Chinese
}

/**
 * Created by Peter on 2016/10/23.
 */
// CBoard settings
var settings = {
    preferredLanguage: "en" // en/cn: Switch language to Chinese
}

Change default language to English


var test = require('tap').test;
var RenderedTarget = require('../../src/sprites/rendered-target');
var Sprite = require('../../src/sprites/sprite');

test('clone effects', function (t) {
    // Create two clones and ensure they have different graphic effect objects.
    // Regression test for Github issue #224
    var spr = new Sprite();
    var a = new RenderedTarget(spr, null);
    var b = new RenderedTarget(spr, null);
    t.ok(a.effects !== b.effects);
    t.end();
});

test('#stopAll clears graphics effects', function (t) {
    var spr = new Sprite();
    var a = new RenderedTarget(spr, null);
    var effectName = 'brightness';
    a.setEffect(effectName, 100);
    a.onStopAll();
    t.equals(a.effects[effectName], 0);
    t.end();
});

Add test for gfx reset on stop


function runTest()
{
    FBTest.sysout("issue5504.START");

    FBTest.openNewTab(basePath + "html/5504/issue5504.html", function(win)
    {
        FBTest.openFirebug();
        var panel = FBTest.selectPanel("html");

        // Get the selected element and execute "New Attribute" action on it.
        var nodeBox = getSelectedNodeBox();
        FBTest.executeContextMenuCommand(nodeBox, "htmlNewAttribute", function()
        {
            var editor = panel.panelNode.getElementsByClassName("textEditorInner").item(0);
            FBTest.compare("", editor.value, "The default value must be an empty string");
            FBTest.testDone("issue5504.DONE");
        });
    });
}

// xxxHonza: use the one from FBTest (should be in FBTest 1.10b5)
function getSelectedNodeBox()
{
    var panel = FBTest.getPanel("html");
    return panel.panelNode.querySelector(".nodeBox.selected");
}

function runTest()
{
    FBTest.sysout("issue5504.START");

    FBTest.openNewTab(basePath + "html/5504/issue5504.html", function(win)
    {
        FBTest.openFirebug();
        var panel = FBTest.selectPanel("html");

        // Get the selected element and execute "New Attribute" action on it.
        var nodeBox = getSelectedNodeBox();
        FBTest.executeContextMenuCommand(nodeBox, "htmlNewAttribute", function()
        {
            // Wait till the inline editor is available.
            var config = {tagName: "input", classes: "textEditorInner"};
            FBTest.waitForDisplayedElement("html", config, function(editor)
            {
                FBTest.compare("", editor.value, "The default value must be an empty string");
                FBTest.testDone("issue5504.DONE");
            });
        });
    });
}

// xxxHonza: use the one from FBTest (should be in FBTest 1.10b5)
function getSelectedNodeBox()
{
    var panel = FBTest.getPanel("html");
    return panel.panelNode.querySelector(".nodeBox.selected");
}

Fix FBTest, the inline editor can be opened async on Mac


import { Kettlepaw, Zeboot } from 'CONTRIBUTORS';
import { change, date } from 'common/changelog';

export default [
  change(date(2020, 12, 18), 'Fixed compiler errors affecing FrenziedRegen and AntiFillerSpam', Kettlepaw),
  change(date(2020, 10, 18), 'Converted legacy listeners to new event filters', Zeboot),
];

import { Kettlepaw, Zeboot } from 'CONTRIBUTORS';
import { change, date } from 'common/changelog';

export default [
  change(date(2020, 12, 18), 'Fixed compiler errors affecting FrenziedRegen and AntiFillerSpam', Kettlepaw),
  change(date(2020, 10, 18), 'Converted legacy listeners to new event filters', Zeboot),
];

Correct a typo in the new changelog entry


const RateLimiter = require("rolling-rate-limiter");
const prettyMs = require('pretty-ms');
const config = require('../config');

const validKeys = ['key1','key2', 'key3', 'key4', 'key5']; //TODO replace API keys in request with JWT-token?
const limiter = RateLimiter({
    interval: 60*60*1000, //1 hour in miliseconds
    maxInInterval: config.max_requests_per_hour
});
const validator = {
    validateKey: function(req, res, next){
        if(validKeys.indexOf(req.query.key)===-1){
            res.status(401);
            res.json({"status":"401", "message":"invalid credentials"});
            return;
        }
        next();
    },
    validateRate: function(req, res, next){
        const timeLeft = limiter(req.query.key);
        if (timeLeft > 0) {
            // limit was exceeded, action should not be allowed
            // timeLeft is the number of ms until the next action will be allowed
            res.status(429);
            res.json({"status":"429", "message":"Request limit excceded- try after "+prettyMs(timeLeft)});
        } else {
            next();
            // limit was not exceeded, action should be allowed
        }
    }
};

module.exports = validator;

const RateLimiter = require("rolling-rate-limiter");
const prettyMs = require('pretty-ms');
const config = require('../config');
const validKeys = process.env.CLIENT_KEYS.split(',');
const limiter = RateLimiter({
    interval: 60*60*1000, //1 hour in miliseconds
    maxInInterval: config.max_requests_per_hour
});
const validator = {
    validateKey: function(req, res, next){
        if(validKeys.indexOf(req.query.key)===-1){
            res.status(401);
            res.json({"status":"401", "message":"invalid credentials"});
            return;
        }
        next();
    },
    validateRate: function(req, res, next){
        const timeLeft = limiter(req.query.key);
        if (timeLeft > 0) {
            // limit was exceeded, action should not be allowed
            // timeLeft is the number of ms until the next action will be allowed
            res.status(429);
            res.json({"status":"429", "message":"Request limit excceded- try after "+prettyMs(timeLeft)});
        } else {
            next();
            // limit was not exceeded, action should be allowed
        }
    }
};

module.exports = validator;

Read client keys from environment variable rather than hardcoding in source


var assert = require('assert'),
    manyToManyFixture = require('../../support/fixtures/model/context.manyToMany.fixture'),
    Model = require('../../../lib/waterline/model');

describe('instance methods', function() {
  describe('association setters', function() {

    /////////////////////////////////////////////////////
    // TEST SETUP
    ////////////////////////////////////////////////////

    var model;

    before(function() {
      model = new Model(manyToManyFixture(), {});
    });

    /////////////////////////////////////////////////////
    // TEST METHODS
    ////////////////////////////////////////////////////

    it('should allow new associations to be added using the add function', function() {
      var person = new model({ name: 'foobar' });

      person.bars.add(1);
      assert(person.associations.bars.addModels.length === 1);
    });

    it('should allow new associations to be removed using the remove function', function() {
      var person = new model({ name: 'foobar' });

      person.bars.remove(1);
      assert(person.associations.bars.removeModels.length === 1);
    });

  });
});

var assert = require('assert'),
    manyToManyFixture = require('../../support/fixtures/model/context.manyToMany.fixture'),
    Model = require('../../../lib/waterline/model');

describe('instance methods', function() {
  describe('association setters', function() {

    /////////////////////////////////////////////////////
    // TEST SETUP
    ////////////////////////////////////////////////////

    var model;

    before(function() {
      model = new Model(manyToManyFixture(), {});
    });

    /////////////////////////////////////////////////////
    // TEST METHODS
    ////////////////////////////////////////////////////

    it('should allow new associations to be added using the add function', function() {
      var person = new model({ name: 'foobar' });

      person.bars.add(1);
      assert(person.associations.bars.addModels.length === 1);
    });

    it('should allow new associations to be added using the add function and an array', function() {
      var person = new model({ name: 'foobar' });

      person.bars.add( [ 1, 2, 3 ] );
      assert(person.associations.bars.addModels.length === 3);
    });

    it('should allow new associations to be removed using the remove function', function() {
      var person = new model({ name: 'foobar' });

      person.bars.remove(1);
      assert(person.associations.bars.removeModels.length === 1);
    });

    it('should allow new associations to be removed using the remove function and an array', function() {
      var person = new model({ name: 'foobar' });

      person.bars.remove( [ 1, 2, 3 ] );
      assert(person.associations.bars.removeModels.length === 3);
    });
  });
});

Add unit tests on .add() and .remove()


/**
 * @fileoverview Bootstrapper for local development.
 *
 * This script can be used during development instead of the compiled firebase-simple-login.js.
 * It pulls in the raw source files, so no compilation step is required (though if you
 * change any dependencies, e.g. by adding goog.require statements, you'll need to
 * run compile-js.sh to rebuild deps.js)
 *
 * This script pulls in google closure's base.js and our dependencies file
 * automatically. All other required scripts will be pulled in based on goog.require
 * statements.
 */

// Figure out the base path of firebase-simple-login-local.js.
var basePath = null;
var scripts = document.getElementsByTagName('script');
for (var i = scripts.length - 1; i >= 0; i--) {
  var src = scripts[i].src;
  var l = src.indexOf('firebase-simple-login-local.js');
  if (l != -1) {
    basePath = src.substr(0, l);
    break;
  }
}

if (basePath === null)
  throw "Couldn't determine location of firebase-simple-login-local.js.  WHAT DID YOU DO!?!?!";

document.write('<script type="text/javascript" src="' + basePath + '../../build/closure/library/closure/goog/base.js"></script>');
document.write('<script type="text/javascript" src="' + basePath + '../deps.js"></script>');
document.write('<script type="text/javascript" src="' + basePath + 'firebase-simple-login-require.js"></script>');

/**
 * @fileoverview Bootstrapper for local development.
 *
 * This script can be used during development instead of the compiled firebase-simple-login.js.
 * It pulls in the raw source files, so no compilation step is required (though if you
 * change any dependencies, e.g. by adding goog.require statements, you'll need to
 * run compile-js.sh to rebuild deps.js)
 *
 * This script pulls in google closure's base.js and our dependencies file
 * automatically. All other required scripts will be pulled in based on goog.require
 * statements.
 */

// Figure out the base path of firebase-simple-login-local.js.
var basePath = null;
var scripts = document.getElementsByTagName('script');
for (var i = scripts.length - 1; i >= 0; i--) {
  var src = scripts[i].src;
  var l = src.indexOf('firebase-simple-login-local.js');
  if (l != -1) {
    basePath = src.substr(0, l);
    break;
  }
}

if (basePath === null)
  throw "Couldn't determine location of firebase-simple-login-local.js.  WHAT DID YOU DO!?!?!";

document.write('<script type="text/javascript" src="' + basePath + '../../lib/closure/library/closure/goog/base.js"></script>');
document.write('<script type="text/javascript" src="' + basePath + '../deps.js"></script>');
document.write('<script type="text/javascript" src="' + basePath + 'firebase-simple-login-require.js"></script>');

Correct local references when loading uncompiled source


const exec = require('child_process').exec;

const AUDIT_EXCEPTIONS = ['handlebars'];

const execute = (command, callback) => {
  exec(
    command, {
      maxBuffer: 2000 * 1024
    },
    (error, stdout, stderr) => {
      callback(stdout);
    }
  );
};
execute('npm audit --json', json => {
  const advisories = JSON.parse(json).advisories;
  if (!advisories) {
    console.info('Most likely npm audit is unavailable', json);
    process.exit(0);
  }
  let auditPass = true;
  for (const id in advisories) {
    if (
      advisories[id].severity === 'high' &&
      !AUDIT_EXCEPTIONS.includes(advisories[id].module_name)
    ) {
      console.error('AUDIT Failed', advisories[id]);
      auditPass = false;
    }
  }
  if (!auditPass) process.exit(1);
});

const exec = require('child_process').exec;

const AUDIT_EXCEPTIONS = ['handlebars', 'decompress'];

const execute = (command, callback) => {
  exec(
    command, {
      maxBuffer: 2000 * 1024
    },
    (error, stdout, stderr) => {
      callback(stdout);
    }
  );
};
execute('npm audit --json', json => {
  const advisories = JSON.parse(json).advisories;
  if (!advisories) {
    console.info('Most likely npm audit is unavailable', json);
    process.exit(0);
  }
  let auditPass = true;
  for (const id in advisories) {
    if (
      advisories[id].severity === 'high' &&
      !AUDIT_EXCEPTIONS.includes(advisories[id].module_name)
    ) {
      console.error('AUDIT Failed', advisories[id]);
      auditPass = false;
    }
  }
  if (!auditPass) process.exit(1);
});

Add decompress to audit exceptions


const winston = require('winston');

module.exports = ({
  level,
  transports: transports = [
    {
      type: 'Console',
      options: {
        timestamp: true,
        colorize: process.env.NODE_ENV !== 'production',
        prettyPrint: process.env.NODE_ENV !== 'production',
        json: process.env.NODE_ENV === 'production',
        stringify: obj => JSON.stringify(obj),
        silent: process.env.NODE_ENV === 'test',
        level: 'debug',
      },
    },
  ],
}) => {
  if (level) {
    for (const transport of transports) {
      transport.options = transport.options || {};
      transport.options.level = level;
    }
  }
  return (category, options) => {
    const logger = new winston.Logger(options);
    for (const transport of transports) {
      logger.add(
        winston.transports[transport.type],
        Object.assign({}, transport.options, { label: category })
      );
    }
    return logger;
  };
};

const winston = require('winston');

module.exports = ({
  level,
  transports: transports = [
    {
      type: 'Console',
      options: {
        timestamp: true,
        colorize: process.env.NODE_ENV !== 'production',
        prettyPrint: process.env.NODE_ENV !== 'production',
        json: process.env.NODE_ENV === 'production',
        stringify: obj => JSON.stringify(obj),
        silent: process.env.NODE_ENV === 'test',
        level: 'info',
      },
    },
  ],
}) => {
  if (level) {
    for (const transport of transports) {
      transport.options = transport.options || {};
      transport.options.level = level;
    }
  }
  return (category, options) => {
    const logger = new winston.Logger(options);
    for (const transport of transports) {
      logger.add(
        winston.transports[transport.type],
        Object.assign({}, transport.options, { label: category })
      );
    }
    return logger;
  };
};

Set default log level to 'info' in winston


'use strict';

module.exports = [
  '$rootScope', '$window', '$timeout',
  function FixContainerHeight($rootScope, $window, $timeout) {
    return {
      restrict: 'A',
      link: function($scope, el) {
        function setParentHeight() {
          var pel = el[0].parentNode;
          var h = el[0].offsetHeight;
          if ( h ) {
            pel.style.minHeight = h + 'px';
          }
        }
        function updateScope() {
          // Wait for the reflow and set parent height
          for ( var i = 0; i < 5; i++ ) {
            $timeout(setParentHeight,i*200);
          }
        }
        $scope.$on('ass-page-data-applied', updateScope);
        $window.addEventListener('resize', updateScope);
      }
    };
  }
];

'use strict';

module.exports = [
  '$rootScope', '$window', '$timeout',
  function FixContainerHeight($rootScope, $window, $timeout) {
    return {
      restrict: 'A',
      link: function($scope, el) {
        function setParentHeight() {
          var pel = el[0].parentNode;
          var h = el[0].offsetHeight;
          var ph = pel.style.minHeight;
          if ( h > 0 && h + 'px' !== ph ) {
            pel.style.minHeight = h + 'px';
          }
        }
        // Wait for the content to be appended and set parent height
        function waitAndSet() {
          var i;
          var times = [0,200,400,700,1000,1500,2000];
          var len = times.length;
          for ( i = 0; i < len; i++ ) {
            $timeout(setParentHeight,times[i]);
          }
        }
        $scope.$on('ass-page-data-applied', waitAndSet);
        $window.addEventListener('resize', setParentHeight);
      }
    };
  }
];

Fix content height not being set on Safari


'use strict';

var extractCss = require('extract-css'),
    inlineCss = require('./inline-css');

module.exports = function inlineContent(src, options) {
    return new Promise(function (resolve, reject) {
        var content;

        if (!options.url) {
            reject('options.url is required');
        }

        extractCss(src, options, function (err, html, css) {
            var extraCss;

            if (err) {
                return reject(err);
            }

            extraCss = css + '\n' + options.extraCss;
            content = inlineCss(html, extraCss, options);
            resolve(content);
        });
    });

};

'use strict';

var Promise = require('bluebird'),
    extractCss = require('extract-css'),
    inlineCss = require('./inline-css');

module.exports = function inlineContent(src, options) {
    return new Promise(function (resolve, reject) {
        var content;

        if (!options.url) {
            reject('options.url is required');
        }

        extractCss(src, options, function (err, html, css) {
            var extraCss;

            if (err) {
                return reject(err);
            }

            extraCss = css + '\n' + options.extraCss;
            content = inlineCss(html, extraCss, options);
            resolve(content);
        });
    });

};

Fix error in node v0.10 missing Promises.


module.exports = {
  entry: './client/src/index.jsx',
  output: {
    path: './client/dist',
    filename: 'index.bundle.js',
  },
  module: {
    loaders: [{
      test: /\.jsx?$/,
      exclude: /node_modules/,
      loader: 'babel-loader',
      query: {
        presets: ['react', 'es2015']
      }
    }]
  },
  devtool: 'source-map'
};

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

module.exports = {
  entry: [
    'webpack-hot-middleware/client',
    './client/src/index.jsx'
  ],
  output: {
    path: path.join(__dirname, '/client/dist'),
    filename: 'index.bundle.js',
  },
  plugins: [
    new webpack.HotModuleReplacementPlugin(),
    new webpack.NoErrorsPlugin()
  ],
  module: {
    loaders: [{
      test: /\.jsx?$/,
      exclude: /node_modules/,
      loader: 'babel-loader',
      query: {
        presets: ['react', 'es2015']
      }
    }]
  },
  devtool: 'source-map'
};

Add hot module loading plugin for webpack


module.exports = {
  extends: [
    'airbnb-base',
    './common.js'
  ],

  parser: 'babel-eslint',

  plugins: [
    'babel',
    'import',
  ],

  rules: {
    'func-names': 0,
    curly: ['error', 'all']
  },
};

module.exports = {
  extends: [
    'airbnb-base'
  ],

  parser: 'babel-eslint',

  plugins: [
    'babel',
    'import',
  ],

  rules: {
    'func-names': 0,
    curly: ['error', 'all']
  },
};

Fix import file not exists in project.


module.exports = {
    "extends": "airbnb",
    "plugins": [
        "react"
    ]
};

module.exports = {
  "extends": "airbnb",
  "plugins": [
    "react"
  ],
  "env": {
    "node": true,
    "mocha": true,
  },
};

Add envs to eslint. Fix indentation


describe('Redirecter::Http', function() {
  var RedirecterHttp = require('../../../lib/redirecter/http'),
      DummyServer = require('../../support/dummy_server'),
      EasyClient = require('../../support/easy_client'),
      Http = require('http'),
      server,
      subject;

  beforeAll(function(){
    server = new DummyServer();
    server.start();
    this.memorize('client', function() {
      return new EasyClient({
        hostname: 'localhost',
        port: this.port()
      });
    });
    this.memorize('port', 3300);
    this.memorize('serverPort', 3000);
    this.memorize('config', function() {
      return {
        port: this.port(),
        proxy: {
          host: 'localhost',
          port: this.serverPort()
        }
      };
    });
  });

  beforeEach(function() {
    this.memorize('subject', function() {
      return RedirecterHttp(this.config());
    });
    this.memorized('subject').listen();
  });

  describe('when performing a get request', function() {
    it('request to server', function() {
      this.memorized('client').call(function(data) {
        console.info(data);
      });
    });
  });
});

describe('Redirecter::Http', function() {

  var RedirecterHttp = require('../../../lib/redirecter/http'),
      DummyServer = require('../../support/dummy_server'),
      EasyClient = require('../../support/easy_client'),
      nock = require('nock'),
      Http = require('http'),
      server,
      subject;

  beforeAll(function(){
    this.memorize('client', function() {
      return new EasyClient({
        hostname: 'localhost',
        port: this.port()
      });
    });
    this.memorize('port', 3300);
    this.memorize('serverPort', 3000);
    this.memorize('config', function() {
      return {
        port: this.port(),
        proxy: {
          host: 'localhost',
          port: this.serverPort()
        }
      };
    });
    nock('http://localhost:' + this.memorized('serverPort')).
      get('/').reply(200, 'data mocked');
  });

  beforeEach(function() {
    this.memorize('subject', function() {
      return RedirecterHttp(this.config());
    });
    this.memorized('subject').listen();
  });

  describe('when performing a get request', function() {
    it('request to server', function() {
      this.memorized('client').call(function(data) {
        console.info(data);
      });
    });
  });
});

Use nock for request mocking


module.exports = {
    extends: [
        "plugin:react/recommended", // Uses the recommended rules from @eslint-plugin-react
        "plugin:@typescript-eslint/recommended", // Uses the recommended rules from the @typescript-eslint/eslint-plugin
        "plugin:prettier/recommended", // Enables eslint-plugin-prettier and eslint-config-prettier. This will display prettier errors as ESLint errors.
        "prettier"
    ],
    parser: "@typescript-eslint/parser",
    parserOptions: {
        sourceType: "module",
        ecmaVersion: 2020,
        ecmaFeatures: {
            jsx: true // Allows for the parsing of JSX
        }
    },
    plugins: [
        "@typescript-eslint"
    ],
    settings: {
        react: {
            version: "detect" // Tells eslint-plugin-react to automatically detect the version of React to use
        }
    },
    // Fine tune rules
    rules: {
        "@typescript-eslint/no-var-requires": 0
    },
};

module.exports = {
    extends: [
        "plugin:react/recommended", // Uses the recommended rules from @eslint-plugin-react
        "plugin:@typescript-eslint/recommended", // Uses the recommended rules from the @typescript-eslint/eslint-plugin
        "plugin:prettier/recommended" // Enables eslint-plugin-prettier and eslint-config-prettier. This will display prettier errors as ESLint errors.
    ],
    parser: "@typescript-eslint/parser",
    parserOptions: {
        sourceType: "module",
        ecmaVersion: 2020,
        ecmaFeatures: {
            jsx: true // Allows for the parsing of JSX
        }
    },
    plugins: [
        "@typescript-eslint"
    ],
    settings: {
        react: {
            version: "detect" // Tells eslint-plugin-react to automatically detect the version of React to use
        }
    },
    // Fine tune rules
    rules: {
        "@typescript-eslint/no-var-requires": 0
    },
};

Remove obsolete entry from eslint config.


const config = {
  extends: ['standard', 'prettier', 'prettier/standard'],
  plugins: ['import', 'mocha'],
  rules: {
    'import/no-extraneous-dependencies': [
      'error',
      {
        devDependencies: [
          '**/test/**/*.js',
          '**/bootstrap-unexpected-markdown.js'
        ],
        optionalDependencies: false,
        peerDependencies: false
      }
    ],
    'mocha/no-exclusive-tests': 'error',
    'mocha/no-nested-tests': 'error',
    'mocha/no-identical-title': 'error'
  },
  parserOptions: {
    ecmaVersion: 9,
    ecmaFeatures: {
      globalReturn: true,
      jsx: true
    },
    sourceType: 'module'
  }
};

if (process.stdin.isTTY) {
  // Enable plugin-prettier when running in a terminal. Allows us to have
  // eslint verify prettier formatting, while not being bothered by it in our
  // editors.
  config.plugins.push('prettier');
  config.rules['prettier/prettier'] = 'error';
}

module.exports = config;

const config = {
  extends: ['standard', 'prettier', 'prettier/standard'],
  plugins: ['import', 'mocha'],
  env: {
    mocha: true
  },
  rules: {
    'import/no-extraneous-dependencies': [
      'error',
      {
        devDependencies: [
          '**/test/**/*.js',
          '**/bootstrap-unexpected-markdown.js'
        ],
        optionalDependencies: false,
        peerDependencies: false
      }
    ],
    'mocha/no-exclusive-tests': 'error',
    'mocha/no-nested-tests': 'error',
    'mocha/no-identical-title': 'error'
  },
  parserOptions: {
    ecmaVersion: 9,
    ecmaFeatures: {
      globalReturn: true,
      jsx: true
    },
    sourceType: 'module'
  }
};

if (process.stdin.isTTY) {
  // Enable plugin-prettier when running in a terminal. Allows us to have
  // eslint verify prettier formatting, while not being bothered by it in our
  // editors.
  config.plugins.push('prettier');
  config.rules['prettier/prettier'] = 'error';
}

module.exports = config;

Add mocha env to the eslint config


/*exported _*/
'use strict';

var _ = {};

_.find = function( array, callback ) {
  var element;
  for ( var i = 0, il = array.length; i < il; i++ ) {
    element = array[i];
    if ( callback( element, i, array ) ) {
      return element;
    }
  }
};

_.set = function( array, indices, value ) {
  if ( !Array.isArray( array ) &&
       !Array.isArray( indices ) &&
       !indices.length ) {
    return;
  }

  var index;
  for ( var i = 0, il = indices.length; i < il; i++ ) {
    index = indices[i];

    // Set if last index.
    if ( i === il - 1 ) {
      array[ index ] = value;
      return value;
    }

    if ( array[ index ] === undefined ) {
      array[ index ] = [];
    }

    array = array[ index ];
  }
};

Add setter with lazy multi-dimensional array indexing.


export default class EntitySchema {
  constructor(key, options = {}) {
    if (!key || typeof key !== 'string') {
      throw new Error('A string non-empty key is required');
    }

    this._key = key;

    const idAttribute = options.idAttribute || 'id';
    this._getId = typeof idAttribute === 'function' ? idAttribute : x => x[idAttribute];
    this._idAttribute = idAttribute;
  }

  getKey() {
    return this._key;
  }

  getId(entity) {
    return this._getId(entity);
  }

  getIdAttribute() {
    return this._idAttribute;
  }

  define(nestedSchema) {
    for (let key in nestedSchema) {
      if (nestedSchema.hasOwnProperty(key)) {
        this[key] = nestedSchema[key];
      }
    }
    return this;
  }
}

export default class EntitySchema {
  constructor(key, options = {}) {
    if (!key || typeof key !== 'string') {
      throw new Error('A string non-empty key is required');
    }

    this._key = key;

    const idAttribute = options.idAttribute || 'id';
    this._getId = typeof idAttribute === 'function' ? idAttribute : x => x[idAttribute];
    this._idAttribute = idAttribute;
  }

  getKey() {
    return this._key;
  }

  getId(entity) {
    return this._getId(entity);
  }

  getIdAttribute() {
    return this._idAttribute;
  }

  define(nestedSchema) {
    for (let key in nestedSchema) {
      if (nestedSchema.hasOwnProperty(key)) {
        this[key] = nestedSchema[key];
      }
    }
  }
}

Revert "Schema.define could return associated instance"


Mapeditor.Minimap = {};

Mapeditor.Minimap.loaded = false;

Mapeditor.Minimap.loadStatic = function(){
  jQuery('#minimap .content')
    .html('<img />');
  jQuery('#minimap img')
    .attr('src', Mapeditor.config.urls.backend + 'map/' + Mapeditor.map.meta.id + '/minimap?' + new Date().getTime())
    .css({
        'max-width': 'none',
        position: 'relative',
        left: (100)-(Mapeditor.map.meta.width/2),
        top: (100)-(Mapeditor.map.meta.height/2)
      });
  Mapeditor.Minimap.loaded = true;
}

Mapeditor.Minimap = {};

Mapeditor.Minimap.loaded = false;

Mapeditor.Minimap.loadStatic = function(){
  jQuery('#minimap .content')
    .html('<img />');
  jQuery('#minimap img')
    .attr('src', Mapeditor.config.urls.backend + 'map/' + Mapeditor.map.meta.id + '/minimap?' + new Date().getTime())
    .css({
        'max-width': 'none',
        position: 'relative',
        left: (100)-(Mapeditor.map.meta.width/2),
        top: (100)-(Mapeditor.map.meta.height/2)
      }).click(function(e){
        var pos_x = e.offsetX ? (e.offsetX) : e.pageX-this.offsetLeft;
        var pos_y = e.offsetY ? (e.offsetY) : e.pageY-this.offsetTop;
        jQuery(this).css({
          left: (100 - pos_x),
          top: (100 - pos_y)
        });
        Mapeditor.internals.infinitedrag.center(pos_x, pos_y);
      });
  Mapeditor.Minimap.loaded = true;
}

Move to coordinate when clicked


'use strict';
window.__REACT_DEVTOOLS_GLOBAL_HOOK__ = {};

const App = require('./components/app.js');
const React = require('react');
const ReactDom = require('react-dom');
const activeStore = require('./store/activeStore.js');
const alertStore = require('./store/alertStore.js');
const call = require('./call.js');
const debug = require('debug')('peer-calls:index');
const notificationsStore = require('./store/notificationsStore.js');
const play = require('./browser/video.js').play;
const streamStore = require('./store/streamStore.js');

function render() {
  debug('rendering');
  ReactDom.render(<App />, document.querySelector('#container'));
  play();
}

activeStore.addListener(render);
alertStore.addListener(render);
notificationsStore.addListener(render);
streamStore.addListener(render);

render();

call.init();

'use strict';
if (!window.__REACT_DEVTOOLS_GLOBAL_HOOK__) {
  window.__REACT_DEVTOOLS_GLOBAL_HOOK__ = {};
}

const App = require('./components/app.js');
const React = require('react');
const ReactDom = require('react-dom');
const activeStore = require('./store/activeStore.js');
const alertStore = require('./store/alertStore.js');
const call = require('./call.js');
const debug = require('debug')('peer-calls:index');
const notificationsStore = require('./store/notificationsStore.js');
const play = require('./browser/video.js').play;
const streamStore = require('./store/streamStore.js');

function render() {
  debug('rendering');
  ReactDom.render(<App />, document.querySelector('#container'));
  play();
}

activeStore.addListener(render);
alertStore.addListener(render);
notificationsStore.addListener(render);
streamStore.addListener(render);

render();

call.init();

Fix broken app for clients with react dev tools extension


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

$(function () {
  $('[data-toggle="tooltip"]').tooltip();
});

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

$(document).on('page:change', function () {
  $('[data-toggle="tooltip"]').tooltip();
});

Update tooltip on page change


/*
 *
 * YoutubePlayer
 *
 */

import React, { Component, PropTypes } from 'react';
import { connect } from 'react-redux';

import YouTube from 'react-youtube';

import mapStateToProps from './selectors';
import mapDispatchToProps from './actions';

import YoutubeOverlay from 'components/YoutubeOverlay';

class YoutubePlayer extends Component {
  constructor(props) {
    super(props);
    this.state = {
      width: window.innerWidth > 1800 ? 1280 : 853,
    };
  }

  render() {
    const {
      videoId,
      onReady,
      setRTCMute,
    } = this.props;
    const { width } = this.state;
    const height = width === 1280 ? 720 : 480;

    const containerStyle = {
      height,
      width,
      marginBottom: 10,
      position: 'relative',
    };

    const opts = {
      height,
      width,
      playerVars: {
        autoplay: 1,
      },
    };

    return (
      <div style={containerStyle}>
        <YouTube
          videoId={videoId}
          opts={opts}
          onReady={e => onReady(e.target)}
          onPlay={setRTCMute(true)}
          onPause={setRTCMute(false)}
          onEnd={setRTCMute(false)}
          onError={setRTCMute(false)}
        />
        {videoId ? null : <YoutubeOverlay />}
      </div>
    );
  }
}

YoutubePlayer.propTypes = {
  videoId: PropTypes.string.isRequired,
  onReady: PropTypes.func.isRequired,
  setRTCMute: PropTypes.func.isRequired,
};

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

/*
 *
 * YoutubePlayer
 *
 */

import React, { PropTypes } from 'react';
import { connect } from 'react-redux';

import YouTube from 'react-youtube';

import mapStateToProps from './selectors';
import mapDispatchToProps from './actions';

import YoutubeOverlay from 'components/YoutubeOverlay';

const YoutubePlayer = ({ videoId, onReady, setRTCMute }) => {
  const [width, height] = window.innerWidth > 1800 ?
    [1280, 720] : [853, 480];

  const containerStyle = {
    height,
    width,
    marginBottom: 10,
    position: 'relative',
  };

  const opts = {
    height,
    width,
    playerVars: {
      autoplay: 1,
    },
  };

  return (
    <div style={containerStyle}>
      <YouTube
        videoId={videoId}
        opts={opts}
        onReady={e => onReady(e.target)}
        onPlay={setRTCMute(true)}
        onPause={setRTCMute(false)}
        onEnd={setRTCMute(false)}
        onError={setRTCMute(false)}
      />
      {videoId ? null : <YoutubeOverlay />}
    </div>
  );
};

YoutubePlayer.propTypes = {
  videoId: PropTypes.string.isRequired,
  onReady: PropTypes.func.isRequired,
  setRTCMute: PropTypes.func.isRequired,
};

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

Use function component for youtube player container


(function() {
    CKEDITOR.plugins.add('concrete5filemanager', {
        init: function () {
            CKEDITOR.on('dialogDefinition', function(event) {
                var editor = event.editor,
                    dialogDefinition = event.data.definition,
                    tabContent = dialogDefinition.contents.length;

                for (var i = 0; i < tabContent; i++) {
                    var browseButton = dialogDefinition.contents[i].get('browse');

                    if (browseButton !== null) {
                        browseButton.hidden = false;
                        browseButton.onClick = function() {
                            editor._.filebrowserSe = this;
                            ConcreteFileManager.launchDialog(function(data) {
                                jQuery.fn.dialog.showLoader();
                                ConcreteFileManager.getFileDetails(data.fID, function(r) {
                                    jQuery.fn.dialog.hideLoader();
                                    var file = r.files[0];
                                    CKEDITOR.tools.callFunction(editor._.filebrowserFn, file.urlDownload);
                                });
                            });
                        }
                    }
                }
            });
        }
    });
})();

(function() {
    CKEDITOR.plugins.add('concrete5filemanager', {
        init: function () {
            CKEDITOR.on('dialogDefinition', function(event) {
                var editor = event.editor,
                    dialogDefinition = event.data.definition,
                    tabContent = dialogDefinition.contents.length;

                for (var i = 0; i < tabContent; i++) {
                    var browseButton = dialogDefinition.contents[i].get('browse');

                    if (browseButton !== null) {
                        browseButton.hidden = false;
                        browseButton.onClick = function() {
                            editor._.filebrowserSe = this;
                            ConcreteFileManager.launchDialog(function(data) {
                                jQuery.fn.dialog.showLoader();
                                ConcreteFileManager.getFileDetails(data.fID, function(r) {
                                    jQuery.fn.dialog.hideLoader();
                                    var file = r.files[0];
                                    CKEDITOR.tools.callFunction(editor._.filebrowserFn, file.urlInline, function() {
                                        var dialog = this.getDialog();
                                        if ( dialog.getName() == 'image' || dialog.getName() == 'image2') {
                                            dialog.dontResetSize = true;
                                            dialog.getContentElement( 'info', 'txtAlt').setValue(file.title);
                                            dialog.getContentElement( 'info', 'txtWidth').setValue('');
                                            dialog.getContentElement( 'info', 'txtHeight').setValue('');
                                            dialog.getContentElement( 'Link', 'txtUrl').setValue(file.urlDownload);
                                        }
                                    });
                                });
                            });
                        }
                    }
                }
            });
        }
    });
})();

Disable width/height resize on image browse selection and use inlnie url for responsive images.


/**
 * Activation component.
 *
 * This JavaScript loads on every admin page. Reserved for later.
 *
 * Site Kit by Google, Copyright 2020 Google LLC
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     https://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

/**
 * WordPress dependencies
 */
import domReady from '@wordpress/dom-ready';
import { render } from '@wordpress/element';
import { doAction } from '@wordpress/hooks';

/**
 * External dependencies
 */
import { loadTranslations } from 'GoogleUtil';
import 'GoogleComponents/notifications';

/**
 * Internal dependencies
 */
import { ActivationApp } from './components/activation/activation-app';

domReady( () => {
	const renderTarget = document.getElementById( 'js-googlesitekit-activation' );

	if ( renderTarget ) {
		loadTranslations();

		render( <ActivationApp />, renderTarget );

		/**
		 * Action triggered when the ActivationApp is loaded.
		 */
		doAction( 'googlesitekit.moduleLoaded', 'Activation' );
	}
} );

/**
 * Activation component.
 *
 * This JavaScript loads on every admin page. Reserved for later.
 *
 * Site Kit by Google, Copyright 2020 Google LLC
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     https://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

/**
 * WordPress dependencies
 */
import domReady from '@wordpress/dom-ready';
import { render } from '@wordpress/element';
import { doAction } from '@wordpress/hooks';

/**
 * External dependencies
 */
import { loadTranslations, sendAnalyticsTrackingEvent } from 'GoogleUtil';
import 'GoogleComponents/notifications';

/**
 * Internal dependencies
 */
import { ActivationApp } from './components/activation/activation-app';

domReady( () => {
	const renderTarget = document.getElementById( 'js-googlesitekit-activation' );

	if ( renderTarget ) {
		loadTranslations();
		sendAnalyticsTrackingEvent( 'plugin_setup', 'plugin_activated' );

		render( <ActivationApp />, renderTarget );

		/**
		 * Action triggered when the ActivationApp is loaded.
		 */
		doAction( 'googlesitekit.moduleLoaded', 'Activation' );
	}
} );

Send plugin_activated event on activation.


module.exports = {
  service: {
    name: 'melodies',
    synopsis: 'thoughts from soundtrack.io',
    icon: 'music'
  },
  database: {
    name: 'blog:soundtrack'
  }
};

module.exports = {
  service: {
    name: 'melodies',
    synopsis: 'thoughts from soundtrack.io',
    icon: 'music'
  },
  database: {
    name: 'blog:soundtrack'
  },
  services: {
    http: {
      port: 13005
    }
  }
};

Change port number for production.


const EventKit = require('event-kit')

module.exports =
function listen (element, eventName, selector, handler) {
  var innerHandler = function (event) {
    if (selector) {
      var currentTarget = event.target
      while (true) {
        if (currentTarget.matches && currentTarget.matches(selector)) {
          handler({
            type: event.type,
            currentTarget: currentTarget,
            target: event.target,
            preventDefault: function () {
              event.preventDefault()
            },
            originalEvent: event
          })
        }
        if (currentTarget === element) break
        currentTarget = currentTarget.parentNode
      }
    } else {
      handler({
        type: event.type,
        currentTarget: event.currentTarget,
        target: event.target,
        preventDefault: function () {
          event.preventDefault()
        },
        originalEvent: event
      })
    }
  }

  element.addEventListener(eventName, innerHandler)

  return new EventKit.Disposable(function () {
    element.removeEventListener(eventName, innerHandler)
  })
}

const EventKit = require('event-kit')

module.exports =
function listen (element, eventName, selector, handler) {
  var innerHandler = function (event) {
    if (selector) {
      var currentTarget = event.target
      while (currentTarget) {
        if (currentTarget.matches && currentTarget.matches(selector)) {
          handler({
            type: event.type,
            currentTarget: currentTarget,
            target: event.target,
            preventDefault: function () {
              event.preventDefault()
            },
            originalEvent: event
          })
        }
        if (currentTarget === element) break
        currentTarget = currentTarget.parentNode
      }
    } else {
      handler({
        type: event.type,
        currentTarget: event.currentTarget,
        target: event.target,
        preventDefault: function () {
          event.preventDefault()
        },
        originalEvent: event
      })
    }
  }

  element.addEventListener(eventName, innerHandler)

  return new EventKit.Disposable(function () {
    element.removeEventListener(eventName, innerHandler)
  })
}

Stop looping when currentTarget becomes undefined


import Robot from './robot-name';

describe('Robot', () => {
  let robot;

  beforeEach(() => {
    robot = new Robot();
  });

  it('has a name', () => {
    expect(robot.name).toMatch(/^[A-Z]{2}\d{3}$/);
  });

  xit('name is the same each time', () => {
    expect(robot.name).toEqual(robot.name);
  });

  xit('different robots have different names', () => {
    const NUMBER_OF_ROBOTS = 10000;
    let usedNames = {};

    for (let i = 0; i < NUMBER_OF_ROBOTS; i++) {
      let newRobot = new Robot();
      usedNames[newRobot.name] = true;
    }

    expect(Object.keys(usedNames).length).toEqual(NUMBER_OF_ROBOTS);
  });

  xit('is able to reset the name', () => {
    let originalName = robot.name;

    robot.reset();
    let newName = robot.name;

    expect(newName).toMatch(/^[A-Z]{2}\d{3}$/);
    expect(originalName).not.toEqual(newName);
  });

  xit('should set a unique name after reset', () => {
    const NUMBER_OF_ROBOTS = 10000;
    let usedNames = {};

    usedNames[robot.name] = true;
    for (let i = 0; i < NUMBER_OF_ROBOTS; i++) {
      robot.reset();
      usedNames[robot.name] = true;
    }

    expect(Object.keys(usedNames).length).toEqual(NUMBER_OF_ROBOTS + 1);
  });

});

import Robot from './robot-name';

describe('Robot', () => {
  let robot;

  beforeEach(() => {
    robot = new Robot();
  });

  it('has a name', () => {
    expect(robot.name).toMatch(/^[A-Z]{2}\d{3}$/);
  });

  xit('name is the same each time', () => {
    expect(robot.name).toEqual(robot.name);
  });

  xit('different robots have different names', () => {
    const NUMBER_OF_ROBOTS = 10000;
    const usedNames = new Set();

    for (let i = 0; i < NUMBER_OF_ROBOTS; i++) {
      const newRobot = new Robot();
      usedNames.add(newRobot.name);
    }

    expect(usedNames.size).toEqual(NUMBER_OF_ROBOTS);
  });

  xit('is able to reset the name', () => {
    const originalName = robot.name;

    robot.reset();
    const newName = robot.name;

    expect(newName).toMatch(/^[A-Z]{2}\d{3}$/);
    expect(originalName).not.toEqual(newName);
  });

  xit('should set a unique name after reset', () => {
    const NUMBER_OF_ROBOTS = 10000;
    const usedNames = new Set();

    usedNames.add(robot.name);
    for (let i = 0; i < NUMBER_OF_ROBOTS; i++) {
      robot.reset();
      usedNames.add(robot.name);
    }

    expect(usedNames.size).toEqual(NUMBER_OF_ROBOTS + 1);
  });

});

Use ES2015 feature Set() to store unique robot names


const fs = require('fs');
const path = require('path');
const env = require('env2');
env('./config.env');

const YotiClient = require('yoti-node-sdk');
const CLIENT_SDK_ID = process.env.SDK;
const PEM = fs.readFileSync(path.join(__dirname, '../../keys_pem/help-access-security.pem'));
const yotiClient = new YotiClient(CLIENT_SDK_ID, PEM);

const userTable = require('../database/tables/users');
const userHelper = require('../helpers/userHelper');
const errorHelper = require('../helpers/errorHelper.js')

module.exports = function (token, callback) {
  yotiClient
  .getActivityDetails(token)
  .then((activityDetails) => {
    let user = userHelper.getUser(activityDetails);
    userTable.queryUserId(user, function (err, userData) {
      if (err) {
        callback(errorHelper.databaseError);
      }
      if (userData) {
        user.exists = true;
        user.admin = userData.admin;
      }
      callback(null, user);
    });
  }).catch((err) => {
    callback(errorHelper.loginError);
  });
};

const fs = require('fs');
const path = require('path');
const env = require('env2');
env('./config.env');

const YotiClient = require('yoti-node-sdk');
const CLIENT_SDK_ID = process.env.SDK;
// local only
const PEM = fs.readFileSync(path.join(__dirname, '../../keys_pem/help-access-security.pem'));
const yotiClient = new YotiClient(CLIENT_SDK_ID, PEM);

const userTable = require('../database/tables/users');
const userHelper = require('../helpers/userHelper');
const errorHelper = require('../helpers/errorHelper.js')

module.exports = function (token, callback) {
  yotiClient
  .getActivityDetails(token)
  .then((activityDetails) => {
    let user = userHelper.getUser(activityDetails);
    userTable.queryUserId(user, function (err, userData) {
      if (err) {
        callback(errorHelper.databaseError);
      }
      if (userData) {
        user.exists = true;
        user.admin = userData.admin;
      }
      callback(null, user);
    });
  }).catch((err) => {
    callback(errorHelper.loginError);
  });
};

Comment on pem file use


function initScenes(Q) {
  Q.scene("mainMenu", function(stage) {
    stage.insert(new Q.UI.Button(
    {
      asset: "play_button.png",
      x: Q.width / 2 - 55,
      y: 500
    },
    function() {
      Q.stageScene('game');
    }));

    stage.insert(new Q.HoverSprite({
      asset: "title.png",
      cx: 0,
      cy: 0,
      x: Q.width / 2 - 210,
      y: 16,
    }));
  });

  Q.scene('endGame',function(stage) {
    var box = stage.insert(new Q.UI.Container({
      x: Q.width/2, y: Q.height/2, fill: "rgba(0,0,0,0.25)"
    }));

    var button = box.insert(new Q.UI.Button({ x: 0, y: 0, fill: "#CCCCCC", label: "Main Menu" }))
    var label = box.insert(new Q.UI.Text({x:10, y: -10 - button.p.h, label: stage.options.label }));
    button.on("click",function() {
      Q.clearStages();
      Q.stageScene('mainMenu');
    });
    box.fit(20);
  });

  Q.scene("game",function(stage) {
    Q.stageTMX('stage1.tmx', stage);

    var player = stage.detect(function() { return this.p.team === 'players' });
    stage.add('viewport').follow(player);
  });
}

function initScenes(Q) {
  Q.scene("mainMenu", function(stage) {
    stage.insert(new Q.UI.Button(
    {
      asset: "play_button.png",
      x: Q.width / 2 - 55,
      y: 500
    },
    function() {
      Q.stageScene('game');
    }));

    stage.insert(new Q.HoverSprite({
      asset: "title.png",
      cx: 0,
      cy: 0,
      x: Q.width / 2 - 210,
      y: 16,
    }));
  });

  Q.scene('endGame',function(stage) {
    var box = stage.insert(new Q.UI.Container({
      x: Q.width/2, y: Q.height/2, fill: "rgba(0,0,0,0.25)"
    }));

    var button = box.insert(new Q.UI.Button({ x: 0, y: 0, fill: "#CCCCCC", label: "Agree" }))
    var label = box.insert(new Q.UI.Text({x:10, y: -10 - button.p.h, label: stage.options.label }));
    button.on("click",function() {
      Q.clearStages();
      Q.stageScene('mainMenu');
    });
    box.fit(20);
  });

  Q.scene("game",function(stage) {
    Q.stageTMX('stage1.tmx', stage);

    var player = stage.detect(function() { return this.p.team === 'players' });
    stage.add('viewport').follow(player);
  });
}

Change "Main Menu" button to an "Agree" button


require.config({
    urlArgs: "bust=v"+(new Date().getTime()), // Comment out/remove to enable caching
    baseUrl: "js/",
    paths: {
        colt:  'libs/colt.0.2.7'
    }
});

define(['colt'], function (Colt) {

    // Define all of the modules
    Colt.modules = [
        'modules/static',
        'modules/modone',
        'modules/modtwo',
        'modules/modthree'
    ];

    // Initialize application
    Colt.init();

});

require.config({
    urlArgs: "bust=v"+(new Date().getTime()), // Comment out/remove to enable caching
    baseUrl: "js/",
    paths: {
        colt:  'libs/colt.0.2.9'
    }
});

define(['colt'], function (Colt) {

    // Define all of the modules
    Colt.modules = [
        'modules/static',
        'modules/modone',
        'modules/modtwo',
        'modules/modthree'
    ];

    // Initialize application
    Colt.init();

});

Fix reference version number in demo


describe('math editor', () => {
    before(waitUntil(() => $('.toolbar').length > 0 &&  $('.toolbar').is(':hidden')))

    describe('initial state', () => {
        it('answer field is contenteditable', () => {
            const $answer = $('.answer')
            expect($answer).to.have.attr('contenteditable', 'true')
            expect($answer).to.have.attr('data-js-handle', 'answer')
        })
    })

    describe('focus state', () => {
        before(() => {
            $('.answer1').focus()
            $('.answer1').focus() // TODO why two calls is necessary?
        })
        before(waitUntil(() => $('.toolbar').is(':visible')))

        it('shows tools', () => {
            expect($('.toolbar')).to.be.visible
            expect($('.answer1').prev()).to.have.class('toolbar')
        })
    })

    describe('start math', () => {
        before(() => {
        })

        it('shows tools', () => {

        })
    })
})

function waitUntil(condition) {
    return done => _waitUntil(condition, done)
}

function _waitUntil(condition, done) {
    if (condition()) done()
    else setTimeout(() => _waitUntil(condition, done), 200)
}

describe('math editor', () => {

    before(waitUntil(() => $('.toolbar').length > 0 &&  $('.toolbar').is(':hidden')))

    describe('initial state', () => {
        it('answer field is contenteditable', () => {
            const $answer = $('.answer')
            expect($answer).to.have.attr('contenteditable', 'true')
            expect($answer).to.have.attr('data-js-handle', 'answer')
        })
    })

    describe('focus state', () => {
        before(() => {
            $('.answer1').focus()
            $('.answer1').focus() // TODO why two calls is necessary?
        })
        before(waitUntil(() => $('.toolbar').is(':visible')))

        it('shows tools', () => {
            expect($('.toolbar')).to.be.visible
            expect($('.answer1').prev()).to.have.class('toolbar')
        })
    })

    describe('start math', () => {
        before(() => {
            $('.newEquation').mousedown()
        })

        it('shows tools', () => {
            expect($('.math')).to.be.visible
        })
    })
})

function waitUntil(condition) {
    return done => _waitUntil(condition, done)
}

function _waitUntil(condition, done) {
    if (condition()) done()
    else setTimeout(() => _waitUntil(condition, done), 200)
}

Add test for clicking equation


/**
 * A simple progressbar with synchronous calls to tick()
 * (i.e. no setTimeout/setInterval)
 */

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

var len = 10000000; // Adjust to your machine's speed
var bar = new ProgressBar('[:bar]', len);

for (var i = 0; i <= len; i++) {
  bar.tick();
}

/**
 * A simple progressbar with synchronous calls to tick()
 * (i.e. no setTimeout/setInterval)
 */

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

var len = 10000000; // Adjust to your machine's speed
var bar = new ProgressBar('[:bar]', {total: len, renderThrottle: 100});

for (var i = 0; i <= len; i++) {
  bar.tick();
}

Set renderThrottle in synch example to 100 ms


function loadModule (newModule) {
  let name = newModule.name ? newModule.name : 'app'
  let module = {
    name,
    controllers: [],
    views: [],
    models: [],
    data: []
  }

  let files = newModule.files

  Object.keys(files).forEach(file => {
    let path = file.match(/([a-zA-Z0-9_]+)/gi)
    let componentType = path.shift()
    path.pop()
    let componentName = path.join('/')

    if (module[componentType]) {
      let component = files[file]

      if (!component.name) {
        let path = file.match(/([a-zA-Z0-9_]+)/gi)
        path.pop()
        path.shift()
        component.name = path.join('-')
      }

      module[componentType].push(component)
    }
  })

  module.data = module.data[0]

  return module
}

export default loadModule

function loadModule (newModule) {
  let name = newModule.name ? newModule.name : 'app'
  let module = {
    name,
    controllers: [],
    views: [],
    models: [],
    data: []
  }

  let files = newModule.files

  Object.keys(files).forEach(file => {
    let path = file.match(/([a-zA-Z0-9_]+)/gi)
    let componentType = path.shift()
    path.pop()
    let componentName = path.join('/')

    if (module[componentType]) {
      let component = files[file]

      if (!component.name) {
        let path = file.match(/([a-zA-Z0-9_]+)/gi)
        path.pop()
        path.shift()
        component.name = path.join('-')
      }

      if (componentType === 'models' && !component.path) {
        component.path = '/' + component.name.replace('-', '/')
      }

      module[componentType].push(component)
    }
  })

  module.data = module.data[0]

  return module
}

export default loadModule

Add module path based on module name if path does not exist.


import {
  StackNavigator,
} from 'react-navigation';
import SearchScreen from './scenes/SearchScreen';

const GitHubProjects = StackNavigator({
  Search: { screen: SearchScreen },
});

export default GitHubProjects;

import {
  StackNavigator,
} from 'react-navigation';
import SearchScreen from './scenes/SearchScreen';

const GitHubProjects = StackNavigator({
  Search: {
    screen: SearchScreen,
    navigationOptions: {
      title: 'GitHub Search'
    }},
});

export default GitHubProjects;

Add Title to Navbar for search screen


import React, { Component } from 'react';
import { StyleSheet } from 'react-native';
import { Provider } from 'react-redux';
import { applyMiddleware, createStore } from 'redux';
import createLogger from 'redux-logger';
import thunkMiddleware from 'redux-thunk';

import App from './containers/app';
import reducers from './reducers';
import styles from './styles'

const logger = createLogger({
  colors: { action: false, error: false, nextState: false, prevState: false, title: false },
  duration: true,
  level: 'info',
  predicate: (getState, action) => __DEV__
});

const middleware = applyMiddleware(thunkMiddleware, logger);
const store = createStore(reducers, {}, middleware);

import { loadHistory } from  './statsStorage'
loadHistory(store.dispatch)

export default () => (
  <Provider store={ store }>
    <App />
  </Provider>
);

import React, { Component } from 'react';
import { StyleSheet } from 'react-native';
import { Provider } from 'react-redux';
import { applyMiddleware, createStore } from 'redux';
import { createLogger } from 'redux-logger';
import thunkMiddleware from 'redux-thunk';

import App from './containers/app';
import reducers from './reducers';
import styles from './styles'

const logger = createLogger({
  colors: { action: false, error: false, nextState: false, prevState: false, title: false },
  duration: true,
  level: 'info',
  predicate: (getState, action) => __DEV__
});

const middleware = applyMiddleware(thunkMiddleware, logger);
const store = createStore(reducers, {}, middleware);

import { loadHistory } from  './statsStorage'
loadHistory(store.dispatch)

export default () => (
  <Provider store={ store }>
    <App />
  </Provider>
);

Upgrade redux-logger 2.6.1 -> 3.0.6


const express = require('express');
const clientApp = require('../clientapp');
const config = require('./lib/config');
const nunjucks = require('nunjucks');
const path = require('path');
const middleware = require('./middleware');

var app = express();

require('express-monkey-patch')(app);

var staticDir = path.join(__dirname, '/static');
var staticRoot = '/static';

app.use(function (req, res, next) {
  res.locals.static = function static (staticPath) {
    return path.join(app.mountPoint, staticRoot, staticPath);
  };
  next();
});

app.use(express.compress());
app.use(express.bodyParser());
app.use(middleware.session());
app.use(middleware.csrf({ whitelist: [] }));

app.use(staticRoot, express.static(staticDir));

var cApp = clientApp(app, {
  developmentMode: config('DEV', false)
});
app.get('*', cApp.html());

if (!module.parent) {
  const port = config('PORT', 3000);

  app.listen(port, function(err) {
    if (err) {
      throw err;
    }

    console.log('Listening on port ' + port + '.');
  });
} else {
  module.exports = http.createServer(app);
}

const express = require('express');
const clientApp = require('../clientapp');
const config = require('./lib/config');
const nunjucks = require('nunjucks');
const path = require('path');
const middleware = require('./middleware');

const DEV_MODE = config('DEV', false);

var app = express();

require('express-monkey-patch')(app);

var staticDir = path.join(__dirname, '/static');
var staticRoot = '/static';

app.use(function (req, res, next) {
  res.locals.static = function static (staticPath) {
    return path.join(app.mountPoint, staticRoot, staticPath);
  };
  next();
});

app.use(express.compress());
app.use(express.bodyParser());
app.use(middleware.session());
app.use(middleware.csrf({ whitelist: [] }));

app.use(staticRoot, express.static(staticDir, {maxAge: DEV_MODE ? 0 : 86400000}));

var cApp = clientApp(app, {
  developmentMode: config('DEV', false)
});
app.get('*', cApp.html());

if (!module.parent) {
  const port = config('PORT', 3000);

  app.listen(port, function(err) {
    if (err) {
      throw err;
    }

    console.log('Listening on port ' + port + '.');
  });
} else {
  module.exports = http.createServer(app);
}

Add caching to static files if not in dev mode


window.addEventListener('polymer-ready', function() {
  document.body.classList.remove('loading');

  var noflo = require('noflo');
  var graphs = {};

  var registerAndStart = function (network) {
    for (var component in graphs) {
      if (component === 'main') {
        continue;
      }
      network.loader.registerComponent('local', component, graphs[component]);
    }
    network.connect(function () {
      network.start();
    });
  };

  var fbps = Array.prototype.slice.call(document.querySelectorAll('script[type="application/fbp"]'));
  fbps.forEach(function (fbp) {
    var fbpString = (fbp.innerText || fbp.textContent).trim();
    var fbpId = fbp.getAttribute('id');
    noflo.graph.loadFBP(fbpString, function (graph) {
      graphs[fbpId] = graph;
      graph.name = fbpId;
      graph.baseDir = '/noflo-ui';
      if (fbpId === 'main') {
        noflo.createNetwork(graph, function (network) {
          //network.on('data', function (data) { console.log(data); });
          if (network.loader.ready) {
            registerAndStart(network);
            return;
          }
          network.loader.once('ready', function () {
            registerAndStart(network);
          });
        }, true);
      }
    });
  });

  // Show help on demand
  var help = document.querySelector('noflo-help');
  document.getElementById("openhelp").addEventListener("click", function (event) {
    if (help) {
      help.open();
      event.preventDefault();
    }
    // Otherwise follow link to docs
  });

});

window.addEventListener('polymer-ready', function() {
  document.body.classList.remove('loading');

  var noflo = require('noflo');
  var graphs = {};

  var registerAndStart = function (network) {
    for (var component in graphs) {
      if (component === 'main') {
        continue;
      }
      network.loader.registerComponent('local', component, graphs[component]);
    }
    network.connect(function () {
      network.start();
    });
  };

  var fbps = Array.prototype.slice.call(document.querySelectorAll('script[type="application/fbp"]'));
  fbps.forEach(function (fbp) {
    var fbpString = fbp.textContent.trim();
    var fbpId = fbp.getAttribute('id');
    noflo.graph.loadFBP(fbpString, function (graph) {
      graphs[fbpId] = graph;
      graph.name = fbpId;
      graph.baseDir = '/noflo-ui';
      if (fbpId === 'main') {
        noflo.createNetwork(graph, function (network) {
          registerAndStart(network);
        }, true);
      }
    });
  });

  // Show help on demand
  var help = document.querySelector('noflo-help');
  document.getElementById("openhelp").addEventListener("click", function (event) {
    if (help) {
      help.open();
      event.preventDefault();
    }
    // Otherwise follow link to docs
  });

});

Use textContent as innerText causes issues on IE


import React, { Component } from 'react'
import { Router } from 'react-router-dom'

const LOCATION_CHANGE = '@@router/LOCATION_CHANGE'

class ConnectedRouter extends Component {
  constructor (props) {
    super(props)

    this.handleLocationChange = this.handleLocationChange.bind(this)
  }

  componentWillMount () {
    const { history } = this.props
    this.unsubscribe = history.listen(this.handleLocationChange)
    this.handleLocationChange(history.location)
  }

  componentWillUnmount () {
    this.unsubscribe()
  }

  handleLocationChange (location) {
    this.props.store.dispatch({
      type: LOCATION_CHANGE,
      payload: location
    })
  }

  render () {
    return <Router {...this.props} />
  }
}

export default ConnectedRouter

import React, { Component } from 'react'
import { Router } from 'react-router-dom'

const LOCATION_CHANGE = '@@router/LOCATION_CHANGE'

class ConnectedRouter extends Component {
  constructor (props) {
    super(props)

    this.handleLocationChange = this.handleLocationChange.bind(this)
  }

  componentWillMount () {
    const { history } = this.props
    this.unsubscribe = history.listen(this.handleLocationChange)
    this.handleLocationChange(history.location)
  }

  componentWillUnmount () {
    this.unsubscribe()
  }

  handleLocationChange (location) {
    this.props.store.dispatch({
      type: LOCATION_CHANGE,
      payload: location
    })
  }

  render () {
    return <Router
      onUpdate={() => window.scrollTo(0, 0)}
      {...this.props}
    />
  }
}

export default ConnectedRouter

Add scroll to top on router update


#!/usr/bin/env node

// Packages
const asyncToGen = require('async-to-gen/register')
const updateNotifier = require('update-notifier')
const {red} = require('chalk')
const nodeVersion = require('node-version')
const isAsyncSupported = require('is-async-supported')

// Ours
const pkg = require('../package')

// Support for keywords "async" and "await"
if (!isAsyncSupported()) {
  const pathSep = process.platform === 'win32' ? '\\\\' : '/'

  asyncToGen({
    includes: new RegExp(`.*serve?${pathSep}(lib|bin).*`),
    excludes: null,
    sourceMaps: false
  })
}

// Throw an error if node version is too low
if (nodeVersion.major < 6) {
  console.error(`${red('Error!')} Serve requires at least version 6 of Node. Please upgrade!`)
  process.exit(1)
}

// Let user know if there's an update
// This isn't important when deployed to Now
if (!process.env.NOW && pkg.dist) {
  updateNotifier({pkg}).notify()
}

// Load package core with async/await support
require('../lib')

#!/usr/bin/env node

// Native
const path = require('path')

// Packages
const asyncToGen = require('async-to-gen/register')
const updateNotifier = require('update-notifier')
const {red} = require('chalk')
const nodeVersion = require('node-version')
const isAsyncSupported = require('is-async-supported')

// Ours
const pkg = require('../package')

// Support for keywords "async" and "await"
if (!isAsyncSupported()) {
  const pathSep = process.platform === 'win32' ? '\\\\' : '/'
  const directoryName = path.parse(path.join(__dirname, '..')).base

  asyncToGen({
    includes: new RegExp(`.*${directoryName}?${pathSep}(lib|bin).*`),
    excludes: null,
    sourceMaps: false
  })
}

// Throw an error if node version is too low
if (nodeVersion.major < 6) {
  console.error(`${red('Error!')} Serve requires at least version 6 of Node. Please upgrade!`)
  process.exit(1)
}

// Let user know if there's an update
// This isn't important when deployed to Now
if (!process.env.NOW && pkg.dist) {
  updateNotifier({pkg}).notify()
}

// Load package core with async/await support
require('../lib')

Make `async-to-gen` work on Now


import assert from 'power-assert';
import rewire from '../../lib/babel-rewire-wrapper';
import greeter from './module/greeter';

/**
 * Do actual rewiring to test modules.
 */
describe('Integration: babel-rewire wrapper', () => {
  function assertGreet(expectedGreet) {
    assert.equal(greeter.greet(), expectedGreet);
  }

  it('has babel-plugin-rewire', () => {
    assertGreet('Hello');
    greeter.__Rewire__('greet', () => 'Hi');

    assertGreet('Hi');

    greeter.__ResetDependency__('greet');
    assertGreet('Hello');
  });

  it('injects mocks to registered modules');
  it('resets dependencies of registered modules');

  context('run with sync function', () => {
    it('injects mocks while running the action');
    it('resets injected mocks after the action');
  });

  context('run with async function', () => {
    it('injects mocks when running the action');
    it('resets injected mocks after the action');
  });

});

import assert from 'power-assert';
import rewire from '../../lib/babel-rewire-wrapper';
import greeter from './module/greeter';

/**
 * Do actual rewiring.
 */
describe('Integration: babel-rewire wrapper', () => {
  function assertGreet(expectedGreet) {
    assert.equal(greeter.greet(), expectedGreet);
  }

  it('has babel-plugin-rewire', () => {
    assertGreet('Hello');
    greeter.__Rewire__('greet', () => 'Hi');

    assertGreet('Hi');

    greeter.__ResetDependency__('greet');
    assertGreet('Hello');
  });

  context('run with sync function', () => {
    it('injects mocks while running the action', () => {
      assertGreet('Hello');
      rewire()
        .use(greeter, { greet: () => 'Hi' })
        .run(() => assertGreet('Hi'));
    });

    it('resets injected mocks after the action', () => {
      assertGreet('Hello');
      rewire()
        .use(greeter, { greet: () => 'Hi' })
        .run(() => {});
      assertGreet('Hello');
    });
  });

  context('run with async function', () => {
    it('injects mocks when running the action', done => {
      assertGreet('Hello');
      rewire()
        .use(greeter, { greet: () => 'Hi' })
        .run(reset => {
          setTimeout(() => {
            assertGreet('Hi');
            reset();
          }, 5);
        })
        .then(done, done);
    });

    it('resets injected mocks after the action', done => {
      assertGreet('Hello');
      rewire()
        .use(greeter, { greet: () => 'Hi' })
        .run(reset => {
          setTimeout(reset, 5);
        })
        .then(() => assertGreet('Hello'))
        .then(done, done);
    });
  });
});

Add integration tests of rewiring


/*
 * Game core for Robots Order BreakOut Trepidation!
 *
 * @canvas = reference to an HTML5 canvas element.
 */

'use strict';
import Actors from 'Actors.js';
import Collision from 'Collision.js';

export default class {
  constructor(canvas) {
    // Player settings.
    this._score = 0;
    this._lives = 3;

    // Game settings.
    this._running = false;

    this._canvas = canvas;
    this._ctx = this._canvas.getContext("2d");

    this._actors = new Actors(this._canvas.width, this._canvas.height);

    this._collision = new Collision();
  }

  _clearScene() {
    this._ctx.clearRect(0, 0, this._canvas.width, this._canvas.height);
    // TODO: Draw background.
  }

  _drawScene() {
    this.clearScene();
    // TODO: Draw scene.

    if (this._running) {
      requestAnimationFrame(()=> this.drawScene());
    }
  }

  _startGameLoop() {
    requestAnimationFrame(()=> this.drawScene());
  }

  // This is kinda out of place here, but haven't found a better
  // place for it yet.
  movePaddle(direction) {
    // TODO: Implement paddle movement.
  }

  start() {
    this._startGameLoop();
  }

  pause() {
    // TODO: Implement some kind of pause.
  }

  stop() {
    this._running = false;
  }
}

/*
 * Game core for Robots Order BreakOut Trepidation!
 *
 * @canvas = reference to an HTML5 canvas element.
 */

'use strict';
import Actors from 'Actors.js';
import Collision from 'Collision.js';

export default class {
  constructor(canvas) {
    // Player settings.
    this._score = 0;
    this._lives = 3;

    // Game settings.
    this._running = false;

    this._canvas = canvas;
    this._ctx = this._canvas.getContext("2d");

    this._actors = new Actors(this._canvas.width, this._canvas.height);

    this._collision = new Collision();
  }

  _clearScene() {
    this._ctx.clearRect(0, 0, this._canvas.width, this._canvas.height);
    // TODO: Draw background.
  }

  _drawScene() {
    this._clearScene();
    // TODO: Draw scene.

    if (this._running) {
      requestAnimationFrame(()=> this._drawScene());
    }
  }

  _startGameLoop() {
    requestAnimationFrame(()=> this._drawScene());
  }

  // This is kinda out of place here, but haven't found a better
  // place for it yet.
  movePaddle(direction) {
    // TODO: Implement paddle movement.
  }

  start() {
    this._startGameLoop();
  }

  pause() {
    // TODO: Implement some kind of pause.
  }

  stop() {
    this._running = false;
  }
}

Fix call to incorrect function name


import { entries } from '../utils/object-utils';

export const VALID_ATTRIBUTES = [
  'data-md-text-align'
];

/*
 * A "mixin" to add section attribute support
 * to markup and list sections.
 */
export function attributable(ctx) {
  ctx.attributes = {};

  ctx.setAttribute = (key, value) => {
    if (!VALID_ATTRIBUTES.includes(key)) {
      throw new Error(`Invalid attribute "${key}" was passed. Constrain attributes to the spec-compliant whitelist.`);
    }
    ctx.attributes[key] = value;
  };
  ctx.removeAttribute = key => {
    delete ctx.attributes[key];
  };
  ctx.getAttribute = key => ctx.attributes[key];
  ctx.eachAttribute = cb => {
    entries(ctx.attributes).forEach(([k,v]) => cb(k,v));
  };
}

import { entries } from '../utils/object-utils';
import { contains } from '../utils/array-utils';

export const VALID_ATTRIBUTES = [
  'data-md-text-align'
];

/*
 * A "mixin" to add section attribute support
 * to markup and list sections.
 */
export function attributable(ctx) {
  ctx.attributes = {};

  ctx.setAttribute = (key, value) => {
    if (!contains(VALID_ATTRIBUTES, key)) {
      throw new Error(`Invalid attribute "${key}" was passed. Constrain attributes to the spec-compliant whitelist.`);
    }
    ctx.attributes[key] = value;
  };
  ctx.removeAttribute = key => {
    delete ctx.attributes[key];
  };
  ctx.getAttribute = key => ctx.attributes[key];
  ctx.eachAttribute = cb => {
    entries(ctx.attributes).forEach(([k,v]) => cb(k,v));
  };
}

Replace Array.includes with utility function for IE11 compat 🐲


/*
 * broadway.js: Top-level include for the broadway module.
 *
 * (C) 2011, Nodejitsu Inc.
 * MIT LICENSE
 *
 */

var fs = require('fs');

var broadway = exports;

broadway.App      = require('./broadway/app').App;
broadway.common   = require('./broadway/common');
broadway.features = require('./broadway/features');
broadway.plugins  = {};

//
// Define each of our plugins as a lazy loaded `require` statement
//
fs.readdirSync(__dirname + '/broadway/plugins').forEach(function (plugin) {
  plugin = plugin.replace('.js', '');
  broadway.plugins.__defineGetter__(plugin, function () {
    return require('./broadway/plugins/' + plugin);
  });
});

/*
 * broadway.js: Top-level include for the broadway module.
 *
 * (C) 2011, Nodejitsu Inc.
 * MIT LICENSE
 *
 */

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

var broadway = exports;

broadway.App      = require('./broadway/app').App;
broadway.common   = require('./broadway/common');
broadway.features = require('./broadway/features');
broadway.plugins  = utile.requireDirLazy(path.join(__dirname, 'broadway', 'plugins'));

Use `utile.requireDirLazy` for loading plugins


/* global QUnit, expect, bootlint */
/* jshint browser: true */
/*eslint-env browser */

(function () {
    'use strict';

    function lintCurrentDoc() {
        var lints = [];
        var reporter = function (lint) {
            lints.push(lint.message);
        };
        bootlint.lintCurrentDocument(reporter, []);
        return lints;
    }

    QUnit.test(window.location.pathname, function (assert) {
        expect(1);
        var lints = Array.prototype.slice.call(document.querySelectorAll('#bootlint>li'));
        var expectedLintMsgs = lints.map(function (item) {
            return item.dataset.lint;
        });
        var actualLintMsgs = lintCurrentDoc();
        assert.deepEqual(actualLintMsgs, expectedLintMsgs);
    });
})();

/* global QUnit, expect, bootlint */
/* jshint browser: true */
/*eslint-env browser */

(function () {
    'use strict';

    function lintCurrentDoc() {
        var lints = [];
        var reporter = function (lint) {
            lints.push(lint.message);
        };
        bootlint.lintCurrentDocument(reporter, []);
        return lints;
    }

    QUnit.test(window.location.pathname, function (assert) {
        // Remove checkboxes QUnit dynamically adds to the DOM as part of its UI
        // because these checkboxes may not comply with some Bootlint checks.
        $('#qunit-filter-pass, #qunit-urlconfig-noglobals, #qunit-urlconfig-notrycatch').remove();

        expect(1);
        var lints = Array.prototype.slice.call(document.querySelectorAll('#bootlint>li'));
        var expectedLintMsgs = lints.map(function (item) {
            return item.dataset.lint;
        });
        var actualLintMsgs = lintCurrentDoc();
        assert.deepEqual(actualLintMsgs, expectedLintMsgs);
    });
})();

Remove QUnit UI's checkboxes from the DOM during tests to prevent false positives


import React from 'react';
import PropTypes from 'prop-types';
import { browserHistory } from 'react-router';
import { ListGroup, ListGroupItem, Alert } from 'react-bootstrap';
import { Meteor } from 'meteor/meteor';
import Documents from '../../api/documents/documents';
import container from '../../modules/container';

const handleNav = _id => browserHistory.push(`/documents/${_id}`);

const DocumentsList = ({ documents }) => (
  documents.length > 0 ? <ListGroup className="DocumentsList">
    {documents.map(({ _id, title, released, rating }) => (
      <ListGroupItem key={ _id } onClick={ () => handleNav(_id) }>
        { title } { released } [{ rating }]
      </ListGroupItem>
    ))}
  </ListGroup> :
  <Alert bsStyle="warning">No documents yet.</Alert>
);

DocumentsList.propTypes = {
  documents: PropTypes.array,
};

export default container((props, onData) => {
  const subscription = Meteor.subscribe('documents.list');
  if (subscription.ready()) {
    const documents = Documents.find().fetch();
    onData(null, { documents });
  }
}, DocumentsList);

import React from 'react';
import PropTypes from 'prop-types';
import { browserHistory } from 'react-router';
import { ListGroup, ListGroupItem, Alert } from 'react-bootstrap';
import { Meteor } from 'meteor/meteor';
import Documents from '../../api/documents/documents';
import container from '../../modules/container';

const handleNav = _id => browserHistory.push(`/documents/${_id}`);

const DocumentsList = ({ documents }) => (
  documents.length > 0 ? <ListGroup className="DocumentsList">
    {documents.map(({ _id, title, released, rating }) => (
      <ListGroupItem key={ _id } onClick={ () => handleNav(_id) }>
        { title } { released } [{ rating }]
      </ListGroupItem>
    ))}
  </ListGroup> :
  <Alert bsStyle="warning">No documents yet.</Alert>
);

DocumentsList.propTypes = {
  documents: PropTypes.array,
};

export default container((props, onData) => {
  const subscription = Meteor.subscribe('documents.list');
  if (subscription.ready()) {
    const documents = Documents.find({},{sort:{released:-1}}).fetch();
    onData(null, { documents });
  }
}, DocumentsList);

Sort movies list by released year


#!/usr/bin/env node
const Host = require('../src/host');
const logger = require('../src/logger');

// node <current script> <rest of args>
const [, , ...args] = process.argv;

try {
  const host = new Host(args);
  host.start({ proc: process });
} catch (err) {
  logger.error(err);
}

process.on('unhandledRejection', (reason, p) => {
  logger.info('Unhandled Rejection at:', p, 'reason:', reason);
});

#!/usr/bin/env node
const Host = require('../src/host');
const logger = require('../src/logger');

// node <current script> <rest of args>
const [, , ...args] = process.argv;

if (args[0] === '--version') {
  // eslint-disable-next-line global-require
  const pkg = require('../package.json');
  // eslint-disable-next-line no-console
  console.log(pkg.version);
  process.exit(0);
}

try {
  const host = new Host(args);
  host.start({ proc: process });
} catch (err) {
  logger.error(err);
}

process.on('unhandledRejection', (reason, p) => {
  logger.info('Unhandled Rejection at:', p, 'reason:', reason);
});

Change host to respond to `--version` request (for CheckHealth)