module.exports = {
  1: {
    plateOutlines: false,
    plateMovement: false,
    earthquakes: true,
    volcanos: false,
    exclusiveLayers: true
  },
  2: {
    plateOutlines: true,
    plateMovement: false,
    earthquakes: true,
    volcanos: false,
    exclusiveLayers: true
  },
  3: {
    plateOutlines: true,
    plateMovement: false,
    earthquakes: true,
    volcanos: true,
    exclusiveLayers: true
  },
  4: {
    plateOutlines: true,
    plateMovement: true,
    earthquakes: true,
    volcanos: true,
    exclusiveLayers: true
  },
  5: {
    plateOutlines: true,
    plateMovement: true,
    earthquakes: true,
    volcanos: true,
    exclusiveLayers: false
  },
  6: {
    plateOutlines: false,
    plateMovement: false,
    earthquakes: false,
    volcanos: true,
    exclusiveLayers: true
  }
}

module.exports = {
  1: {
    plateOutlines: false,
    plateMovement: false,
    earthquakes: true,
    volcanos: false,
    exclusiveLayers: true
  },
  2: {
    plateOutlines: true,
    plateMovement: false,
    earthquakes: true,
    volcanos: false,
    exclusiveLayers: true
  },
  3: {
    plateOutlines: true,
    plateMovement: false,
    earthquakes: true,
    volcanos: true,
    exclusiveLayers: true
  },
  4: {
    plateOutlines: true,
    plateMovement: true,
    earthquakes: true,
    volcanos: true,
    exclusiveLayers: true
  },
  5: {
    plateOutlines: true,
    plateMovement: true,
    earthquakes: true,
    volcanos: true,
    exclusiveLayers: false
  },
  6: {
    plateOutlines: false,
    plateMovement: false,
    earthquakes: false,
    volcanos: true,
    exclusiveLayers: true
  },
  7: {
    plateOutlines: false,
    plateMovement: false,
    earthquakes: true,
    volcanos: true,
    exclusiveLayers: false
  },
}

Add configuration to show volcanoes and earthquakes simultaneously


var config = require('../config'),
    request = require('request');

var vanEndpoint = 'https://api.securevan.com/v4';

function echo(apiKey, message, success, failedAuth, catchErr) {
  var url = vanEndpoint + '/echoes/' + message;

  request(url, function(error, response, body) {
    if (error) {
      return catchErr(error);
    }

    if (response.statusCode == 401) {
      return failedAuth();
    }

    if (response.statusCode == 200) {
      return success(body.message);
    }
  });
}

function getActivistCode(apiKey, dbMode, id, unauthorized, badRequest, success) {
  var options = {
    method: 'GET',
    url: config.get('vanEndpoint') + 'activistCodes/' + id,
    headers: { 'Content-Type': 'application/json' },
    auth: { user: 'api', pass: apiKey + '|' + dbMode, sendImmediately: false }
  };

  request(options, function(error, response, body) {
    if (error) {
      return badRequest(error);
    }
    else if (response.statusCode == 200) {
      return success(body);
    }
    else if (response.statusCode == 404) {
      return success({});
    }
    else if (response.statusCode == 401) {
      return unauthorized();
    }
  });
}

module.exports = {
  getActivistCode: getActivistCode
};

var config = require('../config'),
    request = require('request');

function getActivistCode(apiKey, dbMode, id, unauthorized, badRequest, success) {
  var options = {
    method: 'GET',
    url: config.get('vanEndpoint') + 'activistCodes/' + id,
    headers: { 'Content-Type': 'application/json' },
    auth: { user: 'api', pass: apiKey + '|' + dbMode, sendImmediately: false }
  };

  request(options, function(error, response, body) {
    if (error) {
      return badRequest(error);
    }
    else if (response.statusCode == 200) {
      return success(body);
    }
    else if (response.statusCode == 404) {
      return success({});
    }
    else if (response.statusCode == 401) {
      return unauthorized();
    }
  });
}

module.exports = {
  getActivistCode: getActivistCode
};

Clean up old echoes stuff and correct tags/:id 404 code.


import Ember from 'ember';
import AuthenticatedRouteMixin from 'ember-simple-auth/mixins/authenticated-route-mixin';

export default Ember.Route.extend(AuthenticatedRouteMixin, {
  currentUser: Ember.inject.service('current-user'),
  model: function() {
    return this.get('store').query('member', {
      'person__user': this.get('currentUser.user.id'),
      'is_admin': true,
      'status__gte': '0',
      'page_size': 100,
    });
  },
});

import Ember from 'ember';
import AuthenticatedRouteMixin from 'ember-simple-auth/mixins/authenticated-route-mixin';

export default Ember.Route.extend(AuthenticatedRouteMixin, {
  currentUser: Ember.inject.service('current-user'),
  model: function() {
    let user = this.get('currentUser.user.id').then((data) => data);
    return this.get('store').query('member', {
      'person__user': user,
      'is_admin': true,
      'status__gte': '0',
      'page_size': 100,
    });
  },
});

Make user on Group Manager thennable


(() => {
    let log = console.debug; // Can change to console.log if needed
    let init = () => {
        // The Socket.io script tag has loaded
        window.ioconn = window.io.connect;

        window.io.connect = function() { // The moomoo.io game client uses io.connect to establish a socket.io connection
            if (!arguments[1] || !arguments[1]["query"]) {
                // The client always makes a connection with a query string, and this connection doesn't supply one.
                return ioconn.apply(this, arguments);
            } else {
                // The game client is attempting to connect to the Socket.io server
                var s = ioconn.apply(this, arguments);
                var socketEmit = s.emit;

                // Capture outgoing traffic
                s.emit = function() {
                    var packetType = arguments[0];
                    var mainData = arguments[1];
                    // There are also other arguments that are supplied based on the packet being sent...

                    switch (packetType) {
                        case "1":
                            // Spawn packet
                            log("Player spawn detected. name:", mainData.name);
                            break;
                        default:
                            log("Unknown outgoing packet with args", arguments);
                            break;
                    }
                    return socketEmit.apply(this, arguments); // Call the real emit
                }

                // Capture incoming traffic
                s.on('ch', (sid, msg) => {
                    // Chat message
                    log('Player with sid', sid, 'send message:', msg);
                });
                // TODO: capture more incoming traffic
            }
        }
    }
    window.addEventListener('load', init);
})();

(() => {
    let log = console.debug; // Can change to console.log if needed
    console.clear();
    let init = () => {
        // The Socket.io script tag has loaded
        window.ioconn = window.io.connect;

        window.io.connect = function() { // The moomoo.io game client uses io.connect to establish a socket.io connection
            if (!arguments[1] || !arguments[1]["query"]) {
                // The client always makes a connection with a query string, and this connection doesn't supply one.
                return ioconn.apply(this, arguments);
            } else {
                // The game client is attempting to connect to the Socket.io server
                var s = ioconn.apply(this, arguments);
                var socketEmit = s.emit;

                // Capture outgoing traffic
                s.emit = function() {
                    var packetType = arguments[0];
                    var mainData = arguments[1];
                    // There are also other arguments that are supplied based on the packet being sent...

                    switch (packetType) {
                        case "1":
                            // Spawn packet
                            log("Player spawn detected. name:", mainData.name);
                            break;
                        default:
                            log("Unknown outgoing packet with args", arguments);
                            break;
                    }
                    return socketEmit.apply(this, arguments); // Call the real emit
                }

                // Capture incoming traffic
                s.on('ch', (sid, msg) => {
                    // Chat message
                    log('Player with sid', sid, 'send message:', msg);
                });
                // TODO: capture more incoming traffic
            }
        }
    }
    window.addEventListener('load', init);
})();

Clear the console when script loads


/*
 * TableConstants
 */

var keyMirror = require('keymirror');

module.exports = keyMirror({
  ADD_TABLE: null,
  REMOVE_TABLE: null,
  SELECT_TABLE: null
});

/*
 * TableConstants
 */

var keyMirror = require('keymirror');

module.exports = keyMirror({
  ADD_TABLE: null,
  REMOVE_TABLE: null,
  SELECT_TABLE: null,
  RECEIVED_TABLE_DATA: null
});

Fix bug in running queries due to missing constant


'use strict';

var debug = require('debug')('frontend:mqtt');
var express = require('express');
var router = express.Router();
var auth = require('../../passport');

var restUtils = require('../utils');

var os = require('os');

router.post('/mqtt', auth.requireAPIAuth(), function(req, res) {
  var userId = req.user.id;
  var topic = req.query.topic;
  var payload = req.body;

  if (typeof payload === 'object') {
  	payload = JSON.stringify(payload);
  }

  var routingKey = userId + '.' + topic.replace('/', '.');

  req.app.get('service').facet('amqp').then(function(amqp) {
  	amqp.publish('amq.topic', routingKey, new Buffer(payload));

  	debug('Publishing to routing key', routingKey, 'payload', payload);

  	restUtils.standardResponse(res, { "success": true }, {type: 'object'});
  });
});

module.exports = function(parent) {
  parent.use(router);
};

'use strict';

var debug = require('debug')('frontend:mqtt');
var express = require('express');
var router = express.Router();
var auth = require('../../passport');

var restUtils = require('../utils');

var os = require('os');

router.post('/mqtt', auth.requireAPIAuth(), function(req, res) {
  var userId = req.user.id;
  var topic = req.query.topic;
  var payload = req.body;

  if (typeof payload === 'object') {
  	payload = JSON.stringify(payload);
  }

  var routingKey = userId + '.' + topic.replace(/\//g, '.');

  req.app.get('service').facet('amqp').then(function(amqp) {
  	amqp.publish('amq.topic', routingKey, new Buffer(payload));

  	debug('Publishing to routing key', routingKey, 'payload', payload);

  	restUtils.standardResponse(res, { "success": true }, {type: 'object'});
  });
});

module.exports = function(parent) {
  parent.use(router);
};

Use the correct topic->routing key replacement.


console.log("Helllo world");
	var virus = document.querySelector("div");
virus.style.top = "400px";
	var x = 1;
	var v = .1; // px per millisecond
var startTime = Date.now();
timer = setInterval(function(){
	var t = Date.now() - startTime;
	x = v * t;
    virus.style.left = x + "px";
 if ( left > 400 ) {
    clearInterval( timer );
  }
},20); // ms  | 1000/20 = 50 frames per second (50 fps)

//if (x >= 500 )
//{
//    clearTimeout();
//}

//function frame() {
//   if (pos === 600) {
//      clearInterval(id);

//var frames = 0;
//var foo = document.getElementById( 'foo' );
//foo.style.left = '0px';

console.log("Helllo world");
var virus = document.querySelector("div");
virus.style.top = "400px";
var x = 1;
var velocity = .1; // px per millisecond
var startTime = Date.now();

timer = setInterval(function() {
	var t = Date.now() - startTime;
	x = velocity * t;
	virus.style.left = x + "px";
	if ( x > screen.width) {
	startTime = Date.now();
	}
},20); // ms  | 1000/20 = 50 frames per second (50 fps)

Add loop, so that animation starts over at end of screen


/**
 * Convert Object<Boolean> to bitmap
 * @param {Object<Boolean>} o ex: { announcements: true, features: false, newsletter: false, beta: false }
 * @returns {Number} bitmap
 */
export const toBitMap = (o = {}) => Object.keys(o).reduce((acc, key, index) => acc + (o[key] << index), 0);

/**
 * Define an Object from a bitmap value
 * @param {Number} value bitmap
 * @param {Array<String>} keys ex: ['announcements', 'features', 'newsletter', 'beta']
 * @returns {Object<Boolean>} ex: { announcements: true, features: false, newsletter: false, beta: false }
 */
export const fromBitmap = (value, keys = []) =>
    keys.reduce((acc, key, index) => {
        acc[key] = !!(value & (1 << index));
        return acc;
    }, {});

/**
 * This method creates an object composed of the own and inherited enumerable property paths of object that are not omitted.
 * @param {object} model The source object.
 * @param {Array} properties The property paths to omit.
 * @retuns {Object} Returns the new object.
 */
export const omit = (model, properties = []) => {
    return Object.entries(model)
        .filter(([key]) => !properties.includes(key))
        .reduce((obj, [key, val]) => Object.assign(obj, { [key]: val }), {});
};

Add omit helper for Object


/*
	Javascript functions for Chalmers.it

-------------------------------------------- */

/**
*	Quick and dirty jQuery animation helper plugin
*
*	Usage:
*	$("#element").jb_animate("<animation name>")
*
*	The single parameter, animation name, should be defined
*	as a CSS class. The class is applied to the element. The class
*	should include a CSS animation. Example:
*
*	.shake {
*		-webkit-animation: shake 1s ease-out;
* 	}
*/
$.fn.jb_animate = function(animation) {
	return this.each(function() {
		$(this).addClass(animation)
		.on("webkitAnimationEnd animationend", function() {
			$(this).removeClass(animation);
		})
	});
};

$(function() {

	/* Front page functions */

	// Shake the login form when clicking the 'Log in' button
	$(".home #login-btn").on("click", function(evt) {
		evt.preventDefault();
		$(".login form")
			.jb_animate("shake")
			.find("input[type='text']:first")
			.focus();
	});

});

/*
	Javascript functions for Chalmers.it

-------------------------------------------- */

/**
*	Quick and dirty jQuery animation helper plugin
*
*	Usage:
*	$("#element").jb_animate("<animation name>")
*
*	The single parameter, animation name, should be defined
*	as a CSS class. The class is applied to the element. The class
*	should include a CSS animation. Example:
*
*	.shake {
*		-webkit-animation: shake 1s ease-out;
* 	}
*/
$.fn.jb_animate = function(animation) {
	return this.each(function() {
		$(this).addClass(animation)
		.on("webkitAnimationEnd animationend", function() {
			$(this).removeClass(animation);
		})
	});
};

$(function() {

	/* Front page functions */

	// Shake the login form when clicking the 'Log in' button
	$(".home #login-btn").on("click", function(evt) {
		evt.preventDefault();
		$(".user-area form")
			.jb_animate("shake")
			.find("input[type='text']:first")
			.focus();
	});

});

Fix incorrect class name in front page


enyo.kind({
	name: "enyo.sample.JsonpSample",
	kind: "FittableRows",
	classes: "enyo-fit jsonp-sample",
	components: [
		{kind: "FittableColumns", classes:"onyx-toolbar-inline", components: [
			{content: "YQL: "},
			{kind: "onyx.Input", name:"query", fit:true, value:'select * from upcoming.events where woeid in (select woeid from geo.places where text="Sunnyvale, CA")'},
			{kind: "onyx.Button", content:"Fetch", ontap:"fetch"}
		]},
		{kind: "onyx.TextArea", fit:true, classes:"jsonp-sample-source"}
	],
	fetch: function() {
		var jsonp = new enyo.JsonpRequest({
			url: "http://query.yahooapis.com/v1/public/yql?format=json",
			callbackName: "callback"
		});
		// send parameters the remote service using the 'go()' method
		jsonp.go({
			q: this.$.query.getValue()
		});
		// attach responders to the transaction object
		jsonp.response(this, "processResponse");
	},
	processResponse: function(inSender, inResponse) {
		// do something with it
		this.$.textArea.setValue(JSON.stringify(inResponse, null, 2));
	}
});

enyo.kind({
	name: "enyo.sample.JsonpSample",
	kind: "FittableRows",
	classes: "enyo-fit jsonp-sample",
	components: [
		{kind: "FittableColumns", classes:"onyx-toolbar-inline", components: [
			{content: "YQL: "},
			{kind: "onyx.Input", name:"query", fit:true, value:'select * from weather.forecast where woeid in (select woeid from geo.places where text="san francisco, ca")'},
			{kind: "onyx.Button", content:"Fetch", ontap:"fetch"}
		]},
		{kind: "onyx.TextArea", fit:true, classes:"jsonp-sample-source"}
	],
	fetch: function() {
		var jsonp = new enyo.JsonpRequest({
			url: "http://query.yahooapis.com/v1/public/yql?format=json",
			callbackName: "callback"
		});
		// send parameters the remote service using the 'go()' method
		jsonp.go({
			q: this.$.query.getValue()
		});
		// attach responders to the transaction object
		jsonp.response(this, "processResponse");
	},
	processResponse: function(inSender, inResponse) {
		// do something with it
		this.$.textArea.setValue(JSON.stringify(inResponse, null, 2));
	}
});

Apply YQL change from GF-9695 and GF-9700 to JSONP sample too


import Application from 'appkit/app';

function startApp(attrs) {
  var App;

  var attributes = Ember.merge({
    // useful Test defaults
    rootElement: '#ember-testing',
    LOG_ACTIVE_GENERATION:false,
    LOG_VIEW_LOOKUPS: false
  }, attrs); // but you can override;

  Ember.run(function(){
    App = Application.create(attributes);
    App.setupForTesting();
    App.injectTestHelpers();
  });

  App.reset(); // this shouldn't be needed, i want to be able to "start an app at a specific URL"

  return App;
}

export default startApp;

import Application from 'appkit/app';
import Router from 'appkit/router';

function startApp(attrs) {
  var App;

  var attributes = Ember.merge({
    // useful Test defaults
    rootElement: '#ember-testing',
    LOG_ACTIVE_GENERATION:false,
    LOG_VIEW_LOOKUPS: false
  }, attrs); // but you can override;

  Router.reopen({
    location: 'none'
  });

  Ember.run(function(){
    App = Application.create(attributes);
    App.setupForTesting();
    App.injectTestHelpers();
  });

  App.reset(); // this shouldn't be needed, i want to be able to "start an app at a specific URL"

  return App;
}

export default startApp;

Revert "Remove unnecessary router reopening"


/* eslint-env node */
'use strict';

module.exports = function (environment) {
    let ENV = {
        modulePrefix: 'frontend',
        environment,
        rootURL: '/',
        locationType: 'auto',
        EmberENV: {
            FEATURES: {
                // Here you can enable experimental features on an ember canary build
                // e.g. 'with-controller': true
            },
            EXTEND_PROTOTYPES: {
                // Prevent Ember Data from overriding Date.parse.
                Date: false
            }
        },

        APP: {
            // Here you can pass flags/options to your application instance
            // when it is created
        }
    };

    ENV.APP.API_HOST = '/backend/api';
    ENV.APP.API_NAMESPACE = '';

    if (environment === 'development') {
        // ENV.APP.LOG_RESOLVER = true;
        // ENV.APP.LOG_ACTIVE_GENERATION = true;
        // ENV.APP.LOG_TRANSITIONS = true;
        // ENV.APP.LOG_TRANSITIONS_INTERNAL = true;
        // ENV.APP.LOG_VIEW_LOOKUPS = true;
    }

    if (environment === 'test') {
        // Testem prefers this...
        ENV.locationType = 'none';

        // keep test console output quieter
        ENV.APP.LOG_ACTIVE_GENERATION = false;
        ENV.APP.LOG_VIEW_LOOKUPS = false;

        ENV.APP.rootElement = '#ember-testing';
    }

    if (environment === 'production') {

    }

    return ENV;
};

/* eslint-env node */
'use strict';

module.exports = function (environment) {
    let ENV = {
        modulePrefix: 'frontend',
        environment,
        rootURL: '/',
        locationType: 'auto',
        EmberENV: {
            FEATURES: {
                // Here you can enable experimental features on an ember canary build
                // e.g. 'with-controller': true
            },
            EXTEND_PROTOTYPES: {
                // Prevent Ember Data from overriding Date.parse.
                Date: false
            }
        },

        APP: {
            // Here you can pass flags/options to your application instance
            // when it is created
        }
    };

    if (environment === 'development') {
        // ENV.APP.LOG_RESOLVER = true;
        // ENV.APP.LOG_ACTIVE_GENERATION = true;
        // ENV.APP.LOG_TRANSITIONS = true;
        // ENV.APP.LOG_TRANSITIONS_INTERNAL = true;
        // ENV.APP.LOG_VIEW_LOOKUPS = true;
        ENV.APP.API_HOST = '/backend/api';
        ENV.APP.API_NAMESPACE = '';
    }

    if (environment === 'test') {
        // Testem prefers this...
        ENV.locationType = 'none';

        // keep test console output quieter
        ENV.APP.LOG_ACTIVE_GENERATION = false;
        ENV.APP.LOG_VIEW_LOOKUPS = false;

        ENV.APP.rootElement = '#ember-testing';
    }

    if (environment === 'production') {

    }

    return ENV;
};

Revert "Moved API URL out of dev settings."


;(function()
{
	// CommonJS
	typeof(require) != 'undefined' ? SyntaxHighlighter = require('shCore').SyntaxHighlighter : null;

	function Brush()
	{
		this.regexList = [
			{ regex: /^\+\+\+.*$/gm,		css: 'color2' },
			{ regex: /^\-\-\-.*$/gm,		css: 'color2' },
			{ regex: /^\s.*$/gm,			css: 'color1' },
			{ regex: /^@@.*@@.*$/gm,	css: 'variable' },	// location
			{ regex: /^\+[^\+]{1}.*$/gm,	css: 'string' },
			{ regex: /^\-[^\-]{1}.*$/gm,	css: 'comments' }
			];
	};

	Brush.prototype	= new SyntaxHighlighter.Highlighter();
	Brush.aliases	= ['diff', 'patch'];

	SyntaxHighlighter.brushes.Diff = Brush;

	// CommonJS
	typeof(exports) != 'undefined' ? exports.Brush = Brush : null;
})();

;(function()
{
	// CommonJS
	typeof(require) != 'undefined' ? SyntaxHighlighter = require('shCore').SyntaxHighlighter : null;

	function Brush()
	{
		this.regexList = [
			{ regex: /^\+\+\+ .*$/gm,	css: 'color2' },	// new file
			{ regex: /^\-\-\- .*$/gm,	css: 'color2' },	// old file
			{ regex: /^\s.*$/gm,			css: 'color1' },
			{ regex: /^@@.*@@.*$/gm,	css: 'variable' },	// location
			{ regex: /^\+[^\+]{1}.*$/gm,	css: 'string' },
			{ regex: /^\-[^\-]{1}.*$/gm,	css: 'comments' }
			];
	};

	Brush.prototype	= new SyntaxHighlighter.Highlighter();
	Brush.aliases	= ['diff', 'patch'];

	SyntaxHighlighter.brushes.Diff = Brush;

	// CommonJS
	typeof(exports) != 'undefined' ? exports.Brush = Brush : null;
})();

Use space to make file marker match more specific


var ss = require('stockscraper');

module.exports.run_stock =  function(r,p,reply) {
  if(p.length === 0) {
    reply('Usage: [<exchange>] <stock>');
    return;
  }

  var exchange, stock;
  if(p.length === 1) {
    exchange = 'NYSE';
    stock = p[0];
  }
  else {
    exchange = p[0];
    stock = p[1];
  }

  ss.stockscraper(exchange, stock, function(err,res) {
    if(err) {
      reply('Error getting stock data');
      return;
    }

    var repl = [
    exchange + ':' + stock,
    'price', '$' + res.l,
    'change', '$' + res.c, '(%' + res.cp + ')'
      ];

      reply(repl.join(' '));
      });
    }

var ss = require('stockscraper');

var bot;
var aliases = {
  exchange: {},
  stock: {}
};

module.exports.init = function(b) {
  bot = b;
  bot.fsGetData('stocks', 'aliases.json', function(err, res) {
    if(err) {
      return;
    }

    aliases = JSON.parse(res);

  });
}

module.exports.run_stock =  function(r,p,reply) {
  if(p.length === 0) {
    reply('Usage: [<exchange>] <stock> | add <exchange|stock> <alias> <data>');
    return;
  }

  if(p[0] === 'add') {
    addAlias(p, reply);
  }
  else {
    getStock(p, reply);
  }
}

function addAlias(p, reply) {
  aliases[p[1]][p[2]] = p[3];
  bot.fsStoreData('stocks', 'aliases.json', JSON.stringify(aliases), function () {
    reply('Added alias ' + p[2] + ' for ' + p[3]);
  });
}

function getStock(p, reply) {
  var exchange, stock;
  if(p.length === 1) {
    exchange = 'NYSE';
    stock = p[0];
  }
  else {
    exchange = p[0];
    stock = p[1];
  }

  exchange = aliases.exchange[exchange] || exchange;
  stock = aliases.stock[stock] || stock;

  ss.stockscraper(exchange, stock, function(err,res) {
    if(err) {
      reply('Error getting stock data');
      return;
    }

    var repl = [
      res.e + ':' + res.t,
      'price', '$' + res.l,
      'change', '$' + res.c, '(%' + res.cp + ')'
    ];

    reply(repl.join(' '));
  });
}

Add user aliases for exchanges and stocks


/*jshint evil: true */
/*global loadResources, lumbarLoader */
lumbarLoader.loadJS = function(moduleName, callback, options) {
  return loadResources(moduleName, 'js', callback, function(href, callback) {
    window.loadInContext(href.replace(/\.js$/, '-server.js'), callback);
  }).length;
};
lumbarLoader.loadCSS = function(moduleName, callback, options) {
  loadResources(moduleName, 'css', callback, function(href) {
    $('head').append('<link rel="stylesheet" href="' + href + '" data-lumbar="true">');
  });

  return 0;
};

/*jshint evil: true */
/*global loadResources, lumbarLoader */
lumbarLoader.loadJS = function(moduleName, callback, options) {
  return loadResources(moduleName, 'js', callback, function(href, callback) {
    FruitLoops.loadInContext(href.replace(/\.js$/, '-server.js'), callback);
  }).length;
};
lumbarLoader.loadCSS = function(moduleName, callback, options) {
  loadResources(moduleName, 'css', callback, function(href) {
    $('head').append('<link rel="stylesheet" href="' + href + '" data-lumbar="true">');
  });

  return 0;
};

Update for latest FruitLoops API


// @flow

import * as React from 'react';
import { Platform } from 'react-native';
import Icon from 'react-native-vector-icons/FontAwesome';

import { connect } from 'lib/utils/redux-utils';

import type { AppState } from '../redux/redux-setup';
import { styleSelector } from '../themes/colors';

type Props = {|
  // Redux state
  styles: typeof styles,
|};
function PencilIcon(props: Props) {
  return <Icon name="pencil" size={16} style={props.styles.editIcon} />;
}

const styles = {
  editIcon: {
    color: 'link',
    lineHeight: 20,
    paddingTop: Platform.select({ android: 1, default: 0 }),
    textAlign: 'right',
  },
};
const stylesSelector = styleSelector(styles);

export default connect((state: AppState) => ({
  styles: stylesSelector(state),
}))(PencilIcon);

// @flow

import * as React from 'react';
import { Platform } from 'react-native';
import Icon from 'react-native-vector-icons/FontAwesome';

import { useStyles } from '../themes/colors';

function PencilIcon() {
  const styles = useStyles(unboundStyles);
  return <Icon name="pencil" size={16} style={styles.editIcon} />;
}

const unboundStyles = {
  editIcon: {
    color: 'link',
    lineHeight: 20,
    paddingTop: Platform.select({ android: 1, default: 0 }),
    textAlign: 'right',
  },
};

export default PencilIcon;

Use hook instead of connect functions and HOC in PencilIcon


module.exports = function(config) {
    config.set({
        frameworks: ['mocha'],
        browsers: ['Firefox', 'PhantomJS'],
        files: ['build/test.js']
    });

    if (process.env.TRAVIS) {
        var customLaunchers = {
            'SauceLabs_Firefox': {
                base: 'SauceLabs',
                browserName: 'firefox'
            },
            'SauceLabs_Chrome': {
                base: 'SauceLabs',
                browserName: 'chrome'
            }
        };

        config.set({
            browsers: Object.keys(customLaunchers),
            reporters: ['progress', 'saucelabs'],
            captureTimeout: 120000,
            sauceLabs: {
                startConnect: false,
                testName: 'Loud',
                tunnelIdentifier: process.env.TRAVIS_JOB_NUMBER
            },
            customLaunchers: customLaunchers
        });
    }
};

module.exports = function(config) {
    config.set({
        frameworks: ['mocha'],
        browsers: ['Firefox', 'PhantomJS'],
        files: ['build/test.js'],
        reporters: ['dots']
    });

    if (process.env.TRAVIS) {
        var customLaunchers = {
            'SauceLabs_Firefox': {
                base: 'SauceLabs',
                browserName: 'firefox'
            },
            'SauceLabs_Chrome': {
                base: 'SauceLabs',
                browserName: 'chrome'
            }
        };

        config.set({
            browsers: Object.keys(customLaunchers),
            reporters: ['dots', 'saucelabs'],
            captureTimeout: 120000,
            sauceLabs: {
                startConnect: false,
                testName: 'Loud',
                tunnelIdentifier: process.env.TRAVIS_JOB_NUMBER
            },
            customLaunchers: customLaunchers
        });
    }
};

Change reporter for clean output


var gulp        = require('gulp');
var $           = require('gulp-load-plugins')();
var fs          = require('fs');
var extend      = require('extend');

var options = extend(true, require('./gulp-config.js'), require('./gulp-config-local.js'));

// Add tasks here.
// See task module example in the `task` directory.
fs.readdirSync('./tasks/').forEach(function(file){
    require('./tasks/' + file)(gulp, options, $);
});

var gulp        = require('gulp');
var $           = require('gulp-load-plugins')();
var fs          = require('fs');
var extend      = require('extend');

var options = extend(true, require('./gulp-config.js'), require('./gulp-config-local.js'));

// Add tasks here.
// See task module example in the `task` directory.
fs.readdirSync('./gulp/tasks/').forEach(function(file){
    require('./gulp/tasks/' + file)(gulp, options, $);
});

Fix path to tasks directory


var gulp = require("gulp"),
	webserver = require("gulp-webserver"),
	notify = require("gulp-notify"),
	babel = require("gulp-babel"),
	sourcemaps = require("gulp-sourcemaps"),
	gutil = require("gulp-util");

var htmlPath = "src/html/**/*.html",
	cssPath = "src/css/**/*.css",
	jsPath = "src/js/**/*.js";

gulp.task("js", function() {
	return gulp.src(jsPath)
		.pipe(sourcemaps.init())
		.pipe(babel({ presets: ["es2015"] }))
		.pipe(sourcemaps.write("."))
		.pipe(gulp.dest("bin/js"));
});

gulp.task("html", function() {
	return gulp.src(htmlPath)
		.pipe(gulp.dest("bin"));
});

gulp.task("css", function() {
	return gulp.src(cssPath)
		.pipe(gulp.dest("bin/css"));
});

gulp.task("default", ["html", "css", "js"]);

gulp.task("watch", ["default"], function() {
	gulp.watch(htmlPath, ["html"]);
	gulp.watch(cssPath, ["css"]);
	gulp.watch(jsPath, ["js"]);

	// Local web-server
	gulp.src("bin").pipe(webserver({ livereload: true }));
});

var gulp = require("gulp"),
	webserver = require("gulp-webserver"),
	notify = require("gulp-notify"),
	babel = require("gulp-babel"),
	sourcemaps = require("gulp-sourcemaps"),
	gutil = require("gulp-util");

var htmlPath = "src/html/**/*.html",
	jsPath = "src/js/**/*.js";

gulp.task("js", function() {
	return gulp.src(jsPath)
		.pipe(sourcemaps.init())
		.pipe(babel({ presets: ["es2015"] }))
		.pipe(sourcemaps.write("."))
		.pipe(gulp.dest("bin/js"));
});

gulp.task("html", function() {
	return gulp.src(htmlPath)
		.pipe(gulp.dest("bin"));
});

gulp.task("default", ["html", "js"]);

gulp.task("watch", ["default"], function() {
	gulp.watch(htmlPath, ["html"]);
	gulp.watch(jsPath, ["js"]);

	// Local web-server
	gulp.src("bin").pipe(webserver({ livereload: true }));
});

Remove css task from gulp


const ADD_TABLE_ROW = "ADD_TABLE_ROW"
const EDIT_TABLE_ROW = "EDIT_TABLE_ROW"
const UPDATE_TABLE_CELL = "UPDATE_TABLE_CELL"

let nextTableRowId = 0
const addTableRow = (tableRow) => {
  return {
    type: ADD_TABLE_ROW,
    tableRow,
    id: nextTableRowId++
  }
}

const editTableRow = (id) => {
  return {
    type: EDIT_TABLE_ROW,
    id
  }
}

/*

tableRow object:

{
  id: Integer,
  tableName: String,
  editing: bool,
  readOnly: bool,
  cells: [{
    tableRowId: Integer,
    index: Integer,
    value: String or Number,
    type: String
  }]
}

make Table map the tableRow state it's passed and create
TableRow components from it

*/

const ADD_TABLE_ROW = "ADD_TABLE_ROW"
const EDIT_TABLE_ROW = "EDIT_TABLE_ROW"
const SAVE_TABLE_ROW = "SAVE_TABLE_ROW"

let nextTableRowId = 0
const addTableRow = (tableRow) => {
  return {
    type: ADD_TABLE_ROW,
    tableRow,
    id: nextTableRowId++
  }
}

//Remove editing state from TableRow, use this passed down as prop
const editTableRow = (id) => {
  return {
    type: EDIT_TABLE_ROW,
    id
  }
}

const saveTableRow = (cells) => {
  return {
    type: SAVE_TABLE_ROW,
    cells
  }
}

/*

tableRow object:

{
  id: Integer,
  tableName: String,
  editing: bool,
  readOnly: bool,
  cells: [{
    tableRowId: Integer,
    index: Integer,
    value: String or Number,
    type: String
  }]
}

make Table map the tableRow state it's passed and create
TableRow components from it

*/

Add save table row action


import React, { Component, PropTypes } from 'react';
import FormControl from 'react-bootstrap/lib/FormControl';

class Filter extends Component {

    constructor(props){
        super(props);
        this.state = {filterValue : this.props.query};
        this.inputChanged = this.inputChanged.bind(this);
    }

    componentWillUnmount(){
        if(this.timeout){
            window.clearTimeout(this.timeout);
            this.timeout = null;
        }
    }

    inputChanged(event){
        this.setState({filterValue: event.target.value});
        if (this.timeout){
            window.clearTimeout(this.timeout);
            this.timeout = null;
        }
        this.timeout = window.setTimeout(()=>{
            this.props.config.eventHandler(
                {
                    type:'filter-change',
                    id: this.props.config.id,
                    column: this.props.column,
                    query: this.state.filterValue
                }
            );
        }, 300);
    }

    render(){
        return(
            <FormControl id={'filter_for_'+this.props.column} type="search" key={this.props.column} value={this.state.filterValue} onChange={this.inputChanged} placeholder={"Filter..."} />
        );
    }
}

Filter.propTypes = {
    query: PropTypes.string,
    config: PropTypes.object,
    column: PropTypes.string.isRequired
};

export default Filter;

import React, { Component, PropTypes } from 'react';
import FormControl from 'react-bootstrap/lib/FormControl';

class Filter extends Component {

    constructor(props){
        super(props);
        this.state = {filterValue : this.props.query};
        this.inputChanged = this.inputChanged.bind(this);
    }

    componentWillUnmount(){
        if(this.timeout){
            window.clearTimeout(this.timeout);
            this.timeout = null;
        }
    }

    inputChanged(event){
        this.setState({filterValue: event.target.value});
        if (this.timeout){
            window.clearTimeout(this.timeout);
            this.timeout = null;
        }
        this.timeout = window.setTimeout(()=>{
            this.props.config.eventHandler(
                {
                    type:'filter-change',
                    id: this.props.config.id,
                    column: this.props.column,
                    query: this.state.filterValue
                }
            );
        }, 300);
    }

    render(){
        return(
            <FormControl id={'filter_for_'+this.props.column} type='search' key={this.props.column} value={this.state.filterValue} onChange={this.inputChanged} placeholder={"Filter..."} />
        );
    }
}

Filter.propTypes = {
    query: PropTypes.string,
    config: PropTypes.object,
    column: PropTypes.string.isRequired
};

export default Filter;

Use single quotes for strings


(function () {
  "use strict";
  var Fiber = require('fibers');

  module.exports = Future;

  function Future () {
    this.fiber = Fiber.current;
    this.resolved = false;
    this.yielded = false;
  }

  Future.prototype.resolve = function (err, res) {
    var self = this;
    if (this.resolved) return;
    this.resolved = true;
    if (err && ! (err instanceof Error)) err = new Error(err);
    if (this.yielded) {
      process.nextTick(function () {
        if (err) {
          self.fiber.run(err);
        } else {
          self.fiber.run(res);
        }
        self.fiber = null;
      });
    } else {
      this.error = err;
      this.result = res;
      this.fiber = null;
    }
  };

  Future.prototype.wait = function () {
    if (! this.resolved) {
      try {
        this.result =  this.produce();
      }
      catch (err) {
        this.error = err;
      }
    }
    this.fiber = null;
    var result = this.result, error = this.error;
    this.result = null;
    this.error = null;
    if (error)
      throw error;
    else
      return result;
  };

  Future.prototype.produce = function () {
    this.yielded = true;
    var res = Fiber.yield();
    if (res instanceof Error) throw res;
    else return res;
  };

})();

(function () {
  "use strict";
  var Fiber = require('fibers'),
      extend = require('util')._extend;

  module.exports = Future;

  function Future () {
    this.fiber = Fiber.current;
    this.resolved = false;
    this.yielded = false;
  }

  Future.prototype.resolve = function (err, res) {
    var self = this;
    if (this.resolved) return;
    this.resolved = true;
    if (err && ! (err instanceof Error)) {
      if (typeof err === 'string')
        err = new Error(err);
      else if (typeof err === 'object')
        err = extend(new Error, err);
      if (! err.message) err.message = '[syncho] Unknown error captured';
    }
    if (this.yielded) {
      process.nextTick(function () {
        if (err) {
          self.fiber.run(err);
        } else {
          self.fiber.run(res);
        }
        self.fiber = null;
      });
    } else {
      this.error = err;
      this.result = res;
      this.fiber = null;
    }
  };

  Future.prototype.wait = function () {
    if (! this.resolved) {
      try {
        this.result =  this.produce();
      }
      catch (err) {
        this.error = err;
      }
    }
    this.fiber = null;
    var result = this.result, error = this.error;
    this.result = null;
    this.error = null;
    if (error)
      throw error;
    else
      return result;
  };

  Future.prototype.produce = function () {
    this.yielded = true;
    var res = Fiber.yield();
    if (res instanceof Error) throw res;
    else return res;
  };

})();

Improve handling of non standard errors returned


import PropTypes from 'prop-types';
import React from 'react';
import { connect } from 'react-redux';
import moment from 'moment';
import { fetchRecentNews } from '../../../actions/systemActions';
import withAsyncData from '../hocs/AsyncDataContainer';
import RecentNewsMenu from './RecentNewsMenu';

const MAX_ITEMS = 8;

const RecentNewsMenuContainer = ({ recentNews }) => (
  <React.Fragment>
    {recentNews && (
      <RecentNewsMenu
        newsItems={recentNews[0].notes.slice(0, MAX_ITEMS)}
        subTitle={moment(recentNews[0].notes.date, 'YYYY-MM-DD').fromNow()}
      />
    )}
  </React.Fragment>
);

RecentNewsMenuContainer.propTypes = {
  // from parent
  // from state
  recentNews: PropTypes.array,
  // from compositional chain
};

const mapStateToProps = state => ({
  fetchStatus: state.system.recentNews.fetchStatus,
  recentNews: state.system.recentNews.releases,
});

const fetchAsyncData = dispatch => dispatch(fetchRecentNews());

export default
connect(mapStateToProps)(
  withAsyncData(fetchAsyncData)(
    RecentNewsMenuContainer
  )
);

import PropTypes from 'prop-types';
import React from 'react';
import { connect } from 'react-redux';
import moment from 'moment';
import { fetchRecentNews } from '../../../actions/systemActions';
import withAsyncData from '../hocs/AsyncDataContainer';
import RecentNewsMenu from './RecentNewsMenu';

const MAX_ITEMS = 8;

const RecentNewsMenuContainer = ({ recentNews }) => (
  <React.Fragment>
    {recentNews && (
      <RecentNewsMenu
        newsItems={recentNews[0].notes.slice(0, MAX_ITEMS)}
        subTitle={moment(recentNews[0].date, 'YYYY-M-DD').fromNow()}
      />
    )}
  </React.Fragment>
);

RecentNewsMenuContainer.propTypes = {
  // from parent
  // from state
  recentNews: PropTypes.array,
  // from compositional chain
};

const mapStateToProps = state => ({
  fetchStatus: state.system.recentNews.fetchStatus,
  recentNews: state.system.recentNews.releases,
});

const fetchAsyncData = dispatch => dispatch(fetchRecentNews());

export default
connect(mapStateToProps)(
  withAsyncData(fetchAsyncData)(
    RecentNewsMenuContainer
  )
);

Fix error parsing dates in release notes menu


var elixir = require('laravel-elixir');

/*
 |--------------------------------------------------------------------------
 | Elixir Asset Management
 |--------------------------------------------------------------------------
 |
 | Elixir provides a clean, fluent API for defining some basic Gulp tasks
 | for your Laravel application. By default, we are compiling the Sass
 | file for our application, as well as publishing vendor resources.
 |
 */

// Place all Sass dependencies here
var appSass = [
    'main.scss'
]

// Place all Node JavaScript dependencies here
var nodeScripts = [
    'jquery/dist/jquery.min.js',
    'bootstrap-sass/assets/javascripts/bootstrap.min.js',
    'picturefill/dist/picturefill.min.js'
];

// Place all app JavaScript dependencies here
var appScripts = [
    'includes/**/*.js',
    'main.js'
];

// Compile all the things
elixir(function(mix) {
    mix.sass(appSass, 'public/css/main.css')
        .scripts(nodeScripts, 'public/js/nodeFrameworks.js', 'node_modules')
        .scripts(appScripts, 'public/js/main.js');
});

var elixir = require('laravel-elixir');

/*
 |--------------------------------------------------------------------------
 | Elixir Asset Management
 |--------------------------------------------------------------------------
 |
 | Elixir provides a clean, fluent API for defining some basic Gulp tasks
 | for your Laravel application. By default, we are compiling the Sass
 | file for our application, as well as publishing vendor resources.
 |
 */

// Place all Sass dependencies here
var appSass = [
    'main.scss'
]

// Place all Node JavaScript dependencies here
var nodeScripts = [
    'jquery/dist/jquery.min.js',
    'bootstrap-sass/assets/javascripts/bootstrap.min.js',
    'picturefill/dist/picturefill.min.js'
];

// Place all app JavaScript dependencies here
var appScripts = [
    'includes/**.js',
    'main.js'
];

// Compile all the things
elixir(function(mix) {
    mix.sass(appSass, 'public/css/main.css')
        .scripts(nodeScripts, 'public/js/nodeFrameworks.js', 'node_modules')
        .scripts(appScripts, 'public/js/main.js');
});

Fix include issue with gulp config


var gulp = require('gulp')
var shell = require('./')

var paths = {
  js: ['*.js', 'test/*.js']
}

gulp.task('test', shell.task('mocha -R spec'))

gulp.task('coverage', ['test'], shell.task('istanbul cover _mocha -- -R spec'))

gulp.task('coveralls', ['coverage'], shell.task('cat coverage/lcov.info | coveralls'))

gulp.task('lint', shell.task('eslint ' + paths.js.join(' ')))

gulp.task('default', ['coverage', 'lint'])

gulp.task('watch', function () {
  gulp.watch(paths.js, ['default'])
})

var gulp = require('gulp')
var shell = require('./')

var paths = {
  js: ['*.js', 'test/*.js']
}

gulp.task('test', shell.task('mocha'))

gulp.task('coverage', ['test'], shell.task('istanbul cover _mocha'))

gulp.task('coveralls', ['coverage'], shell.task('cat coverage/lcov.info | coveralls'))

gulp.task('lint', shell.task('eslint ' + paths.js.join(' ')))

gulp.task('default', ['coverage', 'lint'])

gulp.task('watch', function () {
  gulp.watch(paths.js, ['default'])
})

Use the default report of mocha


import React, { Component, PropTypes } from 'react'
import { Spring } from 'react-motion'
import { COLS } from '../constants/Board'

const TILE_SIZE = 60
const basicStyles = {
    position: 'absolute',
    border: '1px solid',
    width: TILE_SIZE,
    height: TILE_SIZE
}

export default class Tile extends Component {

    static propTypes = {
        type: PropTypes.number.isRequired,
        position: PropTypes.number.isRequired,
        onClickCb: PropTypes.func
    }

    onClick(position) {
        if (this.props.onClickCb) {
            this.props.onClickCb(position)
        }
    }

    getLeftValue(position) {
        return (position % COLS) * TILE_SIZE
    }

    getTopValue(position) {
        return (Math.ceil((position + 1) / COLS) - 1) * TILE_SIZE
    }

    render() {
        const {type, position} = this.props
        return (
            <Spring
                endValue={{val: {left: this.getLeftValue(position), top: this.getTopValue(position)}}}
            >
                {interpolated =>
                    <div
                        style={{
                            ...basicStyles,
                            opacity: type <= 0 ? 0.1 : 1,
                            top: interpolated.val.top,
                            left: interpolated.val.left
                        }}
                        onClick={this.onClick.bind(this, position)}
                    >{type}</div>
                }
            </Spring>
        )
    }
}

import React, { Component, PropTypes } from 'react'
import { Spring } from 'react-motion'
import { COLS } from '../constants/Board'

const TILE_SIZE = 60
const basicStyles = {
    position: 'absolute',
    border: '1px solid',
    width: TILE_SIZE,
    height: TILE_SIZE
}

export default class Tile extends Component {

    static propTypes = {
        type: PropTypes.number.isRequired,
        position: PropTypes.number.isRequired,
        onClickCb: PropTypes.func
    }

    onClick(position) {
        if (this.props.onClickCb) {
            this.props.onClickCb(position)
        }
    }

    getLeftValue(position) {
        return (position % COLS) * TILE_SIZE
    }

    getTopValue(position) {
        return (Math.ceil((position + 1) / COLS) - 1) * TILE_SIZE
    }

    render() {
        const {type, position} = this.props
        return (
            <Spring
                endValue={{val: {left: this.getLeftValue(position), top: this.getTopValue(position)}}}
            >
                {interpolated =>
                    <div
                        style={{
                            ...basicStyles,
                            opacity: type <= 0 ? 0.1 : 1,
                            transform: `translate(${interpolated.val.left}px, ${interpolated.val.top}px)`
                        }}
                        onClick={this.onClick.bind(this, position)}
                    >{type}</div>
                }
            </Spring>
        )
    }
}

Use transform instead of left/top CSS properties


'use strict';

jest.dontMock('../lib/search.jsx');

var React = require('react/addons');
var TestUtils = React.addons.TestUtils;

var Search = require('../lib/search.jsx');

describe('Search', function() {

});

'use strict';

jest.dontMock('../lib/search.jsx');

var React = require('react/addons');
var TestUtils = React.addons.TestUtils;

var Search = require('../lib/search.jsx');

describe('Search', function() {
    it('should have a dropdown with default `all` option', function() {
        var search = TestUtils.renderIntoDocument(
            <Search />
        );

        var options = TestUtils.scryRenderedDOMComponentsWithTag(
            search, 'option');

        expect(options.length).toEqual(1);
        expect(options[0].getDOMNode().value).toEqual('all');
    });

    it('should have a dropdown that contain columns that have both property and header', function() {
        var columns = [
            {
                property: 'first',
                header: 'First'
            },
            {
                property: 'second'
            },
            {
                header: 'Third'
            }
        ];

        var search = TestUtils.renderIntoDocument(
            <Search columns={columns} />
        );

        var options = TestUtils.scryRenderedDOMComponentsWithTag(
            search, 'option');

        expect(options.length).toEqual(2);
        expect(options[0].getDOMNode().value).toEqual('all');
        expect(options[1].getDOMNode().value).toEqual(columns[0].property);
        expect(options[1].getDOMNode().textContent).toEqual(columns[0].header);
    });
});

Add initial tests for search


import React from "react";
import styles from "./Home.less";

import { Stickers } from "./Stickers";
import { CTM } from "./CTM";

export const Home = () => (
  <>

Hello, World! It's my laboratory.

    <Stickers />

      Igor Golopolosov.
      Senior Software Enginner at <CTM />

Find out more details about me on:

  </>
);

Add space between name and role


// json-schema for params on usergroup `create` and `update` methods.

'use strict';

module.exports = {
  short_name: {
    type: 'string',
    required: true,
    minLength: 1
  },
  parent_group: {
    type: [ 'string', 'null' ],
    required: true
  },
  settings: {
    type: 'object',
    required: true,
    patternProperties: {
      '.*': {
        type: [ 'object', 'null' ],
        required: true,
        additionalProperties: false,
        properties: {
          value: {
            required: true
          },
          force: {
            type: 'boolean',
            required: true
          }
        }
      }
    }
  }
};

// json-schema for params on usergroup `create` and `update` methods.

'use strict';

module.exports = {
  short_name: {
    type: 'string',
    required: true,
    minLength: 1
  },
  parent_group: {
    type: [ 'string', 'null' ],
    required: true
  },
  settings: {
    type: 'object',
    required: true,
    patternProperties: {
      '.*': {
        anyOf: [
          { type: 'null' },
          {
            type: 'object',
            additionalProperties: false,
            properties: {
              value: {
                required: true
              },
              force: {
                type: 'boolean',
                required: true
              }
            }
          }
        ]
      }
    }
  }
};

Fix json schema in admin controllers


Meteor.publish("misureBySito2", function (sitoId) {
    check(sitoId, String);
    var sito = Siti.findOne({
        _id: sitoId
    });
    if (!sito) {
        return null;
    }
    var query = MonthReadings.find({
        podId: sito.pod
    });
    return query;
});

Meteor.publish("misureBySitoAndMonth", function (sitoId, dateMonth) {
    check(sitoId, String);
    var sito = Siti.findOne({
        _id: sitoId
    });
    if (!sito) {
        return null;
    }
    var query = MonthReadings.find({
        podId: sito.pod,
        month: dateMonth
    });
    return query;
});

Add month in documents filter


import { StyleSheet, Dimensions } from 'react-native';

export default StyleSheet.create({
  image: {
    height: 150,
    width: 150
  }
})

import { StyleSheet, Dimensions } from 'react-native';

export default StyleSheet.create({
  mainContainer: {
    width: Dimensions.get('window').width,
    height: Dimensions.get('window').height
  },
  image: {
    height: 80,
    width: 80,
    resizeMode: 'contain'
  },
  ActorView: {
    flex: 1,
    flexDirection: 'column',
    justifyContent: 'center',
    alignItems: 'center'
  },
  endingPaths: {
    marginTop: Dimensions.get('window').height / 14
  }
})

Add styles for views and images


import update from 'immutability-helper';

const initialSearchParams = {
  project: null,
  search: '',
  type: null,
  limit: 5,
  offset: 0,
};

export default function searchParams(state = initialSearchParams, action = null) {
  switch (action.type) {
    case 'updateSearchParams': {
      const updateSpec = Object.keys(action.payload).reduce(
        (spec, key) => {
          spec[key] = {$set: action.payload[key]}; // eslint-disable-line no-param-reassign
          return spec;
        }, {},
      );
      return update(state, updateSpec);
    }
    default:
      return state;
  }
};

import update from 'immutability-helper';

const initialSearchParams = {
  project: null,
  search: '',
  type: null,
  limit: 30,
  offset: 0,
};

export default function searchParams(state = initialSearchParams, action = null) {
  switch (action.type) {
    case 'updateSearchParams': {
      const updateSpec = Object.keys(action.payload).reduce(
        (spec, key) => {
          spec[key] = {$set: action.payload[key]}; // eslint-disable-line no-param-reassign
          return spec;
        }, {},
      );
      return update(state, updateSpec);
    }
    default:
      return state;
  }
};

Update default search limit back to something more reasonable than 5


import React from 'react';
import { Link } from 'react-router';
import { FormattedMessage } from 'react-intl';
import steemconnect from 'steemconnect';
import Loading from '../../widgets/Loading';
import Icon from '../../widgets/Icon';
import Avatar from '../../widgets/Avatar';
import './SidebarHeader.scss';

const SidebarHeader = ({
  auth,
  user,
  hideSidebar,
  onClickMenu,
}) =>

     hideSidebar()}>
      <Icon name="arrow_back" className="Icon--menu" />

      {auth.isFetching &&
        <Loading color="white" />
      }
      {!auth.isAuthenticated && !auth.isFetching &&

          <Icon name="lock_outline" />
          {' '}<FormattedMessage id="login" />

      }
      {auth.isAuthenticated &&

          <Link to={`/@${user.name}`} className="my-1">
            <Avatar sm username={user.name} reputation={user.reputation} />
          </Link>

            {`${user.name} `}
             onClickMenu({ menu: 'settings' })}>
              <Icon name="settings" xs />

      }

;

export default SidebarHeader;

import React from 'react';
import { Link } from 'react-router';
import { FormattedMessage } from 'react-intl';
import steemconnect from 'steemconnect';
import Loading from '../../widgets/Loading';
import Icon from '../../widgets/Icon';
import Avatar from '../../widgets/Avatar';
import './SidebarHeader.scss';

const SidebarHeader = ({
  auth,
  user,
  hideSidebar,
  onClickMenu,
}) =>

     hideSidebar()}>
      <Icon name="arrow_back" className="p-2" />

      {auth.isFetching &&
        <Loading color="white" />
      }
      {!auth.isAuthenticated && !auth.isFetching &&

          <Icon name="lock_outline" />
          {' '}<FormattedMessage id="login" />

      }
      {auth.isAuthenticated &&

          <Link to={`/@${user.name}`} className="my-1">
            <Avatar sm username={user.name} reputation={user.reputation} />
          </Link>

            {`${user.name} `}
             onClickMenu({ menu: 'settings' })}>
              <Icon name="settings" xs />

      }

;

export default SidebarHeader;

Fix padding on hide sidebar button


'use strict';

// Connect to port and listen for messages.

chrome.runtime.onConnect.addListener(function (port) {
  // Listen for message from devtools.js
  port.onMessage.addListener(function (message) {
    if (message.sender == 'drupal-template-helper-devtools' && message.message == 'current-tab-url') {
      // Get the current active tab.
      chrome.tabs.query({ 'active': true }, function (tabs) {

        // Send the url of the active tab to devtools.js
        if (tabs.length) {
          port.postMessage({
            'sender': 'drupal-template-helper-background',
            'url': tabs[0].url
          });
        }
      });
    }
  });
});

// Open Options page on browserAction click.
chrome.browserAction.onClicked.addListener(function (tab) {
  if (chrome.runtime.openOptionsPage) {
    chrome.runtime.openOptionsPage();
  }
});

'use strict';

// Connect to port and listen for messages.

chrome.runtime.onConnect.addListener(function (port) {
  // Listen for message from devtools.js
  port.onMessage.addListener(function (message) {
    if (message.sender == 'drupal-template-helper-devtools' && message.message == 'current-tab-url') {
      // Get the current window.
      chrome.windows.getCurrent(function (window) {
        if (window) {
          // Get the current active tab.
          chrome.tabs.query({ 'active': true, 'windowId': window.id }, function (tabs) {

            // Send the url of the active tab to devtools.js
            if (tabs.length) {
              port.postMessage({
                'sender': 'drupal-template-helper-background',
                'url': tabs[0].url
              });
            }
          });
        }
      });
    }
  });
});

// Open Options page on browserAction click.
chrome.browserAction.onClicked.addListener(function (tab) {
  if (chrome.runtime.openOptionsPage) {
    chrome.runtime.openOptionsPage();
  }
});

Check for current window when getting active tab


$(document).ready(function() {
 $('.details').on('click', function(event) {
    event.preventDefault();
    event.stopPropagation();
    var $details = $(event.target.parentElement);
    $.ajax({
      url: $details.context.action,
      type: 'GET',
      }).done(function (response) {
          $('#sectionA').children().hide();
          $('#sectionA').append(response);
      }).fail(function (response) {
        alert("Can Not Render Your Parcels Due to Error")
      })
    });

   $('.parcels_tab').on('click', function(event) {
      $('#sectionA').children().show();
      $('.parcel_show').hide();
    });

   $('.trips_tab').on('click', function(event) {
      $('.nav-tabs:eq(1)').removeClass('active')
      $('.nav-tabs:eq(2)').addClass('active')
    });

  });

$(document).ready(function() {
 $('.parcel_details').on('click', function(event) {
    event.preventDefault();
    event.stopPropagation();
    var $details = $(event.target.parentElement);
    $.ajax({
      url: $details.context.action,
      type: 'GET',
      }).done(function (response) {
          $('#sectionA').children().hide();
          $('#sectionA').append(response);
      }).fail(function (response) {
        alert("Can Not Render Your Parcels Due to Error")
      })
    });

   $('.parcels_tab').on('click', function(event) {
      $('#sectionA').children().show();
      $('.parcel_show').hide();
    });

   $('.trips_tab').on('click', function(event) {
      $('.nav-tabs:eq(1)').removeClass('active')
      $('.nav-tabs:eq(2)').addClass('active')
    });

  });

Change parcel details button it from details to parcel details


/* eslint no-console: 0 */
'use strict';

const phantom = require('phantom');

function* blockResourceLoading(page) {
    yield page.property('onResourceRequested', function(requestData, request) {
        var BLOCKED_RESOURCES = [
            /\.gif/gi,
            /\.png/gi,
            /\.css/gi,
            /^((?!(feuseradmin\.js|tinymce|jquery-)).)*\.js.*/gi
        ];
        var is_blacklisted_resource = BLOCKED_RESOURCES.some(function(r) {
            return r.test(requestData['url']);
        });

        if (is_blacklisted_resource) {
            request.abort();
        }
    });
}

function* fetchPageContent(url, instance, block_assets=true) {
    let is_local_instance = false;
    if (!instance) {
        instance = yield * initPhantomInstance();
        is_local_instance = true;
    }

    const page = yield instance.createPage();
    if (block_assets) {
        yield * blockResourceLoading(page);
    }

    yield page.open(url);
    let html = yield page.property('content');
    yield page.close();
    if (is_local_instance) {
        instance.exit();
    }
    return html;
}

function* initPhantomInstance() {
    return yield phantom.create();
}

module.exports = {
    blockResourceLoading: blockResourceLoading,
    fetchPageContent: fetchPageContent,
    initPhantomInstance: initPhantomInstance
};

/* eslint no-console: 0 */
'use strict';

const phantom = require('phantom');

function* blockResourceLoading(page) {
    yield page.property('onResourceRequested', function(requestData, request) {
        var BLOCKED_RESOURCES = [
            /\.gif/gi,
            /\.png/gi,
            /\.css/gi
        ];
        var is_blacklisted_resource = BLOCKED_RESOURCES.some(function(r) {
            return r.test(requestData['url']);
        });

        if (is_blacklisted_resource) {
            request.abort();
        }
    });
}

function* fetchPageContent(url, instance, block_assets=true) {
    let is_local_instance = false;
    if (!instance) {
        instance = yield * initPhantomInstance();
        is_local_instance = true;
    }

    const page = yield instance.createPage();
    if (block_assets) {
        yield * blockResourceLoading(page);
    }

    yield page.open(url);
    let html = yield page.property('content');
    yield page.close();
    if (is_local_instance) {
        instance.exit();
    }
    return html;
}

function* initPhantomInstance() {
    return yield phantom.create();
}

module.exports = {
    blockResourceLoading: blockResourceLoading,
    fetchPageContent: fetchPageContent,
    initPhantomInstance: initPhantomInstance
};

Update the list of regexps for BLOCKING resource loading on page.


(function(window, $) {
  $(window.document).ready(function() {
    window.MaSha.instance = new window.MaSha({
      selectable: 'bodyContent'
    });
  });
})(window, window.jQuery);

(function(window, $) {
  $(window.document).ready(function() {
    window.MaSha.instance = $('#bodyContent').masha({
    });
  });
})(window, window.jQuery);

Use jQuery style of MaSha constructor


/**
 * Adds support for count blueprint and binds :model/count route for each RESTful model.
 */

var _ = require('lodash');
var actionUtil = require('./actionUtil');
var pluralize = require('pluralize');

const defaultCountBlueprint = function(req, res) {
  var Model = actionUtil.parseModel(req);

  var countQuery = Model.count(actionUtil.parseCriteria(req));

  countQuery
    .then(function(count) {
      return res.ok({count : count})
    });
};

module.exports = function (sails) {
  return {
      initialize: function(cb) {
        var config = sails.config.blueprints;
        var countFn = _.get(sails.middleware, 'blueprints.count') || defaultCountBlueprint;

        sails.on('router:before', function() {
          _.forEach(sails.models, function(model) {
            var controller = sails.middleware.controllers[model.identity];

            if (!controller) return;

            var baseRoute = [config.prefix, model.identity].join('/');

            if (config.pluralize && _.get(controller, '_config.pluralize', true)) {
              baseRoute = pluralize(baseRoute);
            }

            var route = baseRoute + '/count';

            sails.router.bind(route, countFn, null, {controller: model.identity});
          });
        });

        cb();
      }
  }
};

/**
 * Adds support for count blueprint and binds :model/count route for each RESTful model.
 */

var _ = require('lodash');
var actionUtil = require('./actionUtil');
var pluralize = require('pluralize');

const defaultCountBlueprint = function(req, res) {
  var Model = actionUtil.parseModel(req);

  var countQuery = Model.count(actionUtil.parseCriteria(req));

  countQuery
    .then(function(count) {
      return res.ok({count : count})
    });
};

module.exports = function (sails) {
  return {
      initialize: function(cb) {
        var config = sails.config.blueprints;
        var countFn = _.get(sails.middleware, 'blueprints.count') || defaultCountBlueprint;

        sails.on('router:before', function() {
          _.forEach(sails.models, function(model) {
            var controller = sails.middleware.controllers[model.identity];

            if (!controller) return;

            var prefix = config.restPrefix || config.prefix;
            var baseRoute = [prefix, model.identity].join('/');

            if (config.pluralize && _.get(controller, '_config.pluralize', true)) {
              baseRoute = pluralize(baseRoute);
            }

            var route = baseRoute + '/count';

            sails.router.bind(route, countFn, null, {controller: model.identity});
          });
        });

        cb();
      }
  }
};

Support fallback for restPrefix blueprint configuration


'use strict';

/* global Connector */

Connector.playerSelector = '.player-controls';

Connector.artistSelector = '.track_type_player .track__artists';

Connector.trackSelector = '.track_type_player .track__title';

Connector.currentTimeSelector = '.progress__left';

Connector.durationSelector = '.progress__right';

Connector.isPlaying = function () {
	return $('.player-controls__btn_play').hasClass('player-controls__btn_pause');
};

'use strict';

/* global Connector */

Connector.playerSelector = '.player-controls';

Connector.artistSelector = '.track_type_player .track__artists';

Connector.trackSelector = '.track_type_player .track__title';

Connector.currentTimeSelector = '.progress__left';

Connector.durationSelector = '.progress__right';

Connector.getUniqueID = function() {
	var trackUrl = $('.track_type_player .track__title').attr('href');
	if (trackUrl) {
		return trackUrl.split('/').pop();
	}
	return null;
};

Connector.isPlaying = function () {
	return $('.player-controls__btn_play').hasClass('player-controls__btn_pause');
};

Implement `getUniqueID` for Yandex connector


//@flow
import React from "react";

import { Ul, Li, Nav } from "./styled-components";
import { LinkedInIcon } from "../../../../elements/linkedin-icon";
import { Highlight } from "../../../../elements/highlight";
import { Chevron } from "../../../../elements/chevron";
import theme from "../../../../global/style/mainTheme";

export const MainNav = (props: {}) => (

<Highlight {...props} url="/">Projects</Highlight>

<Highlight {...props}>About</Highlight>

        <Highlight highlightColor="transparent" {...props}>
          <Chevron {...props}>Download</Chevron>
        </Highlight>

<Highlight {...props}>Contact</Highlight>

<LinkedInIcon />

);

//@flow
import React from "react";

import { Ul, Li, Nav } from "./styled-components";
import { LinkedInIcon } from "../../../../elements/linkedin-icon";
import { OctocatIcon } from "../../../../elements/octocat-icon";
import { Highlight } from "../../../../elements/highlight";
import { Chevron } from "../../../../elements/chevron";
import theme from "../../../../global/style/mainTheme";

export const MainNav = (props: {}) => (

<Highlight {...props} url="/">Projects</Highlight>

<Highlight {...props}>About</Highlight>

        <Highlight highlightColor="transparent" {...props}>
          <Chevron {...props}>Download</Chevron>
        </Highlight>

<Highlight {...props}>Contact</Highlight>

<LinkedInIcon />

<OctocatIcon />

);

Add Octocat icon to Header


var path 		= require('path');
var rootPath 	= path.normalize(__dirname + '/../../');

module.exports 	= {
    local: {
        baseUrl: 'http://localhost:3030',
        db: 'mongodb://localhost/rp_local',
        rootPath: rootPath,
        port: process.env.PORT || 3030
    },
    staging : {
        baseUrl: 'http://dev.resourceprojects.org',
        //db: '@aws-us-east-1-portal.14.dblayer.com:10669/rp_dev?ssl=true',
        db: '@aws-us-east-1-portal.14.dblayer.com:10669,aws-us-east-1-portal.13.dblayer.com:10499/rp_dev?ssl=true',
        rootPath: rootPath,
        port: process.env.PORT || 80
    }
};

var path 		= require('path');
var rootPath 	= path.normalize(__dirname + '/../../');

module.exports 	= {
    local: {
        baseUrl: 'http://localhost:3016',
        db: 'mongodb://localhost/rp_local',
        rootPath: rootPath,
        port: process.env.PORT || 3016
    },
    staging : {
        baseUrl: 'http://dev.resourceprojects.org',
        //db: '@aws-us-east-1-portal.14.dblayer.com:10669/rp_dev?ssl=true',
        db: '@aws-us-east-1-portal.14.dblayer.com:10669,aws-us-east-1-portal.13.dblayer.com:10499/rp_dev?ssl=true',
        rootPath: rootPath,
        port: process.env.PORT || 80
    }
};

Add to download links ng-csv


'use strict';

angular.module('confRegistrationWebApp')
  .factory('currentRegistrationInterceptor', function ($q, $injector) {
    return {
      'responseError': function (rejection) {
        var regExp = /conferences\/[-a-zA-Z0-9]+\/registrations\/current\/?$/;
        if (rejection.status === 404 && regExp.test(rejection.config.url)) {
          var url = rejection.config.url.split('/');
          if (_.isEmpty(_.last(url))) {
            url.pop();
          }
          url.pop();
          return $injector.get('$http').post(url.join('/'));
        }
        return $q.reject(rejection);
      }
    };
  });

'use strict';

angular.module('confRegistrationWebApp')
  .factory('currentRegistrationInterceptor', function ($q, $injector) {
    return {
      'responseError': function (rejection) {
        var regExp = /conferences\/[-a-zA-Z0-9]+\/registrations\/current\/?$/;
        if (rejection.status === 404 && regExp.test(rejection.config.url)) {
          var url = rejection.config.url.split('/');
          if (_.isEmpty(_.last(url))) {
            url.pop();
          }
          url.pop();
          return $injector.get('$http').post(url.join('/'), {});
        }
        return $q.reject(rejection);
      }
    };
  });

Fix content type bug (415 error from server). - The server doesn't recognize the POST as type application/json, but adding an empty JSON object {} solves that.


import React from 'react';
import {BrowserRouter as Router, Route} from 'react-router-dom';

import LandingPage from '../Landing';
import SignInPage from '../SignIn'
import ViewActivitiesPage from '../ViewActivities';
import ViewTripsPage from '../ViewTrips';
import * as ROUTES from '../../constants/routes';

/**
 * The main App component that is fed to the index.js entrypoint. Defines all
 * pages that can be visited using React Router.
 */
const App = () => {
  return (
    <Router>
      <div>
        <Route exact path={ROUTES.LANDING} component={LandingPage} />
        <Route path={ROUTES.SIGN_IN} component={SignInPage} />
        <Route path={ROUTES.VIEW_TRIPS} component={ViewTripsPage} />
        <Route path={ROUTES.VIEW_ACTIVITIES + '/:trip-id'} component={ViewActivitiesPage} />
      </div>
    </Router>
  );
};

export default App;

import React from 'react';
import {BrowserRouter as Router, Route} from 'react-router-dom';

import LandingPage from '../Landing';
import SignInPage from '../SignIn'
import ViewActivitiesPage from '../ViewActivities';
import ViewTripsPage from '../ViewTrips';
import * as ROUTES from '../../constants/routes';

/**
 * The main App component that is fed to the index.js entrypoint. Defines all
 * pages that can be visited using React Router.
 */
const App = () => {
  return (
    <Router>
      <div>
        <Route exact path={ROUTES.LANDING} component={LandingPage} />
        <Route path={ROUTES.SIGN_IN} component={SignInPage} />
        <Route path={ROUTES.VIEW_TRIPS} component={ViewTripsPage} />
        <Route path={ROUTES.VIEW_ACTIVITIES + '/:tripId'} component={ViewActivitiesPage} />
      </div>
    </Router>
  );
};

export default App;

Change tripId url to work conveniently


function get_cookie(name) {
    if(!document.cookie || document.cookie === '') return;
    var bits = document.cookie.split(';');
    for(var i=0, l=bits.length; i < l; i++) {
        var m = bits[i].trim().match(/(\w+)=(.*)/);
        if(m !== undefined && m[1] == name) {
            return decodeURIComponent(m[2])
        }
    }
}

/* resolve a string to an element, if it's not already */
function element(el) {
    return (typeof el == 'string') ? document.querySelector(el) : el;
}

/* Helpers for Fetch */
function check_status(response) {
    if(response.status >= 200 && response.status < 300) { return response; }
    var err = new Error(response.statusText);
    err.response = response;
    throw err;
}
function json (response) { return response.json(); }

fetch.post = function (url, data) {
    return fetch(url, {
        method: 'post',
        body: data,
        credentials: 'same-origin',
        headers: { 'X-CSRFToken': get_cookie('csrftoken') }
    });
}

fetch.get = function (url, data) {
    return fetch(url, {
        body: data,
        credentials: 'same-origin',
    })
}

/* string helper */
String.prototype.format = function (args) {
    return this.replace(/{(\w+)}/g, function(m, key) { return args[key]; });
}

function get_cookie(name) {
    if(!document.cookie || document.cookie === '') return;
    var bits = document.cookie.split(';');
    for(var i=0, l=bits.length; i < l; i++) {
        var m = bits[i].trim().match(/(\w+)=(.*)/);
        if(m !== undefined && m[1] == name) {
            return decodeURIComponent(m[2])
        }
    }
}

/* resolve a string to an element, if it's not already */
function element(el) {
    return (typeof el == 'string') ? document.querySelector(el) : el;
}

/* Helpers for Fetch */
function check_status(response) {
    if(response.status >= 200 && response.status < 300) { return response; }
    var err = new Error(response.statusText);
    err.response = response;
    throw err;
}
function json (response) { return response.json(); }

fetch.post = function (url, data) {
    return fetch(url, {
        method: 'post',
        body: data,
        credentials: 'same-origin',
        headers: { 'X-CSRFToken': get_cookie('csrftoken') }
    });
}

fetch.get = function (url, data) {
    return fetch(url, {
        body: data,
        credentials: 'same-origin',
    })
}

/* string helper */
String.prototype.format = function (args) {
    return this.replace(/{([\-\w]+)}/g, function(m, key) { return args[key]; });
}

Support hyphens in string formatting


// Mithril component which embeds an iframe with HTML
// Doug Engelbart Unfinished Revolution II Colloquium -- UnRev-II - headers sorted By Date
// This also demostrates supplying a title to display instead of "Untitled" when the component is collapsed

const MyComponent = {
    view(controller, args) {
        return m("div.ba.ma3.pa3.bg-light-purple",
            m("iframe", {src: "//dougengelbart.org/colloquium/forum/discussion/date.html", width: 800, height: 600})
        )
    }
}

Twirlip7.show(MyComponent, { extraStyling: ".bg-light-purple", title: "Doug Engelbart Unfinished Revolution II Colloquium" })

// Mithril component which embeds an iframe with HTML
// Doug Engelbart Unfinished Revolution II Colloquium -- UnRev-II - headers sorted By Date
// This also demonstrates supplying a specific title to display instead of the triple entity and attribute when the component is collapsed

const MyComponent = {
    view(controller, args) {
        return m("div.ba.ma3.pa3.bg-light-purple",
            m("iframe", {src: "//dougengelbart.org/colloquium/forum/discussion/date.html", width: 800, height: 600})
        )
    }
}

Twirlip7.show(MyComponent, { extraStyling: ".bg-light-purple", title: "Doug Engelbart Unfinished Revolution II Colloquium" })

Improve comment in Englebart iframe example


import React from 'react'
import ReactDOM from 'react-dom'
import { AppContainer } from 'react-hot-loader'
import App from './containers/App'

ReactDOM.render(
  <AppContainer>
    <App/>
  </AppContainer>,
  document.getElementById('root')
)

if (module.hot) {
  module.hot.accept('./containers/App', () => {
    // If you use Webpack 2 in ES modules mode, you can
    // use <App /> here rather than require() a <NextApp />.
    const NextApp = require('./containers/App').default
    ReactDOM.render(
      <AppContainer>
        <NextApp/>
      </AppContainer>,
      document.getElementById('root')
    )
  })
}

Add line break at end of file


$(function() {
  $('.toggleDeadlineUnitDate').on('click', function(e) {
    e.preventDefault();
    $(this).replaceWith("Goal Deadline Date<input type='date' name='project[goal_deadline_date]' id='project_goal_deadline_date'></input>");
    $('.toggleDeadlineUnitHours').remove();
  });

  $('.toggleDeadlineUnitHours').on('click', function(e) {
    e.preventDefault();
    $(this).replaceWith("Goal Time Limit<input type='text' name='project[goal_time_limit]' id='project_goal_time_limit'></input>");
    $('.toggleDeadlineUnitDate').remove();
  });

});

$(function() {
  $('.toggleDeadlineUnitDate').on('click', function(e) {
    e.preventDefault();
    $(this).replaceWith("Goal Deadline Date<input type='date' name='project[goal_deadline_date]' id='project_goal_deadline_date'></input>");
    $('.toggleDeadlineUnitHours').remove();
  });

  $('.toggleDeadlineUnitHours').on('click', function(e) {
    e.preventDefault();
    $(this).replaceWith("Goal Time Limit <input type='text' name='project[goal_time_limit]' id='project_goal_time_limit'></input> hours");
    $('.toggleDeadlineUnitDate').remove();
  });

});

Add label for hours to goal_time_limit input field.


var user = "highfidelity/";
var repository = "hifi_tests/";
var branch = "master/";
var autoTester = Script.require("https://github.com/" + user + repository + "blob/" + branch + "tests/utils/autoTester.js?raw=true" );

autoTester.enableAuto();

Script.include("./test.js?raw=true");

if (typeof user === 'undefined') user = "highfidelity/";
if (typeof repository === 'undefined') repository = "hifi_tests/";
if (typeof branch === 'undefined') branch = "master/";

var autoTester = Script.require("https://github.com/" + user + repository + "blob/" + branch + "tests/utils/autoTester.js?raw=true" );

autoTester.enableAuto();

Script.include("./test.js?raw=true");

Use new repo parameter format (allowing easier testing).


export default {
  list() {
    this.NewLibConnection
      .Class
      .please()
      .list()
      .ordering('desc')
      .then(this.completed)
      .catch(this.failure);
  },

  get(name) {
    this.NewLibConnection
      .Class
      .please()
      .get({ name })
      .then(this.completed)
      .catch(this.failure);
  },

  create(payload) {
    this.NewLibConnection
      .Class
      .please()
      .create(payload)
      .then(this.completed)
      .catch(this.failure);
  },

  update(name, payload) {
    this.NewLibConnection
      .Class
      .please()
      .update({ name }, payload)
      .then(this.completed)
      .catch(this.failure);
  },

  remove(classes) {
    const promises = classes.map((item) => this.NewLibConnection.Class.please().delete({ name: item.name }));

    this.Promise.all(promises)
      .then(this.completed)
      .catch(this.failure);
  }
};

export default {
  list() {
    this.NewLibConnection
      .Class
      .please()
      .list()
      .ordering('desc')
      .then((classes) => {
        const classesList = classes.filter((item) => item.name !== 'user_profile');

        this.completed(classesList);
      })
      .catch(this.failure);
  },

  get(name) {
    this.NewLibConnection
      .Class
      .please()
      .get({ name })
      .then(this.completed)
      .catch(this.failure);
  },

  create(payload) {
    this.NewLibConnection
      .Class
      .please()
      .create(payload)
      .then(this.completed)
      .catch(this.failure);
  },

  update(name, payload) {
    this.NewLibConnection
      .Class
      .please()
      .update({ name }, payload)
      .then(this.completed)
      .catch(this.failure);
  },

  remove(classes) {
    const promises = classes.map((item) => this.NewLibConnection.Class.please().delete({ name: item.name }));

    this.Promise.all(promises)
      .then(this.completed)
      .catch(this.failure);
  }
};

Hide the user_profile data class


'use strict';

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

var config = require('./config');
var routes = require('./routes');

function API(backend, opts) {
  this.backend = backend;
  this.opts = opts;

  this._initApp();
}

API.prototype._initApp = function() {
  this.app = express();

  // parse POST data
  this.app.use(bodyParser.json());
  this.app.use(bodyParser.urlencoded({ extended: false }));

  // install routes
  this.app.use('/', routes);

  // catch 404 and forward to error handler
  this.app.use(function(req, res, next) {
      var err = new Error('Not Found');
      err.status = 404;
      next(err);
  });

  // production error handler
  this.app.use(function(err, req, res, next) {
      res.status(err.status || 500);
      res.send({
          message: err.message,
          error: {}
      });
  });
}

module.exports = API;

'use strict';

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

var config = require('./config');
var routes = require('./routes');

function API(backend, opts) {
  this.backend = backend;
  this.opts = opts;

  this._initApp();
}

API.prototype._initApp = function() {
  this.app = express();

  // parse POST data
  this.app.use(cors());
  this.app.use(bodyParser.json());
  this.app.use(bodyParser.urlencoded({ extended: false }));

  // install routes
  this.app.use('/', routes);

  // catch 404 and forward to error handler
  this.app.use(function(req, res, next) {
      var err = new Error('Not Found');
      err.status = 404;
      next(err);
  });

  // production error handler
  this.app.use(function(err, req, res, next) {
      res.status(err.status || 500);
      res.send({
          message: err.message,
          error: {}
      });
  });
}

module.exports = API;

Add CORS to web api


angular.module('myapp')
    .controller('HomeController', ['$scope', '$timeout', 'SnapshotServices', function ($scope, $timeout, SnapshotServices) {
        $scope.listSnapshot = [];
        $scope.currentSnapshot = "";

        $scope.getSnapshots = function () {
            SnapshotServices.getSnapshots().then(function (data) {
                $scope.listSnapshot = data['snapshots'];
            });
        };

        $scope.backupDatabase = function () {
            $scope.showLoading();
            SnapshotServices.backupDatabase(function () {
                $timeout(function () {
                    $scope.hideLoading();
                }, 500);
                $scope.getSnapshots();
            }).then(function (data) {
                if (data.status == "success") {
                } else {
                }
            });
        };

        $scope.restoreDatabase = function () {
            if ($scope.currentSnapshot) {
                $scope.showLoading()
                SnapshotServices.restoreDatabase($scope.currentSnapshot, function () {
                    $timeout(function () {
                        $scope.hideLoading();
                    }, 500);
                }).then(function (data) {
                    if (data.status == "success") {
                        $scope.currentSnapshot = "";
                    } else {
                    }
                });
            }
        };

        $scope.chooseSnapshot = function (snapshotId, $event) {
            $('.active').removeClass('active');
            $($event.currentTarget).find('span').addClass('active');
            $scope.currentSnapshot = snapshotId;
//            console.log($scope.currentSnapshot)
        };
    }]);

angular.module('myapp')
    .controller('HomeController', ['$scope', '$timeout', 'SnapshotServices', function ($scope, $timeout, SnapshotServices) {
        $scope.listSnapshot = [];
        $scope.currentSnapshot = "";

        $scope.getSnapshots = function () {
            SnapshotServices.getSnapshots().then(function (data) {
                $scope.listSnapshot = data['snapshots'];
            });
        };

        $scope.backupDatabase = function () {
            $scope.showLoading();
            SnapshotServices.backupDatabase(function () {
                $timeout(function () {
                    $scope.hideLoading();
                }, 500);
                $scope.getSnapshots();
            }).then(function (data) {
                if (data.status == "success") {
                } else {
                }
            }, function () {
                $timeout(function () {
                    $scope.hideLoading();
                }, 500);
            });
        };

        $scope.restoreDatabase = function () {
            if ($scope.currentSnapshot) {
                $scope.showLoading()
                SnapshotServices.restoreDatabase($scope.currentSnapshot, function () {
                    $timeout(function () {
                        $scope.hideLoading();
                    }, 200);
                }).then(function (data) {
                    if (data.status == "success") {
                        $scope.currentSnapshot = "";
                    } else {
                    }
                }, function () {
                    $timeout(function () {
                        $scope.hideLoading();
                    }, 500);
                });
            }
        };

        $scope.chooseSnapshot = function (snapshotId, $event) {
            $('.active').removeClass('active');
            $($event.currentTarget).find('span').addClass('active');
            $scope.currentSnapshot = snapshotId;
        };
    }]);

Handle request error: controller backupDatabase, restoreDatabase


/*
 * grunt-strip-comments
 * https://github.com/kkemple/grunt-strip-comments
 *
 * Copyright (c) 2013 Kurtis Kemple
 * Licensed under the MIT license.
 */

'use strict';

module.exports = function(grunt) {

  // Please see the Grunt documentation for more information regarding task
  // creation: http://gruntjs.com/creating-tasks

  grunt.registerMultiTask('comments', 'Remove comments from production code', function() {

    var mulitlineComment = /\/\*(.|\n|\t|\r)+\*\//gm;
    var singleLineComment = /\/\/.+/g;

    var options = this.options({
      singleline: true,
      multiline: true
    });

    this.files[0].src.forEach(function (file) {

      var contents = grunt.file.read(file);

      if ( options.multiline ) {
         contents = contents.replace(mulitlineComment, '');
      }

      if ( options.singleline ) {
         contents = contents.replace(singleLineComment, '');
      }

      grunt.file.write(file, contents);
    });
  });
};

/*
 * grunt-strip-comments
 * https://github.com/kkemple/grunt-strip-comments
 *
 * Copyright (c) 2013 Kurtis Kemple
 * Licensed under the MIT license.
 */

'use strict';

module.exports = function(grunt) {

  // Please see the Grunt documentation for more information regarding task
  // creation: http://gruntjs.com/creating-tasks

  grunt.registerMultiTask('comments', 'Remove comments from production code', function() {

    var mulitlineComment = /\/\*[\s\S]*?\*\//g;
    var singleLineComment = /\/\/.+/g;

    var options = this.options({
      singleline: true,
      multiline: true
    });

    this.files[0].src.forEach(function (file) {

      var contents = grunt.file.read(file);

      if ( options.multiline ) {
         contents = contents.replace(mulitlineComment, '');
      }

      if ( options.singleline ) {
         contents = contents.replace(singleLineComment, '');
      }

      grunt.file.write(file, contents);
    });
  });
};

Fix pattern matching for multiline comments.


/* global hljs */

Wee.fn.make('guide', {
	init: function() {
		Wee.history.init({
			bind: true,
			extensions: ['html']
		});
	},

	/**
	 * Bind guide functionality on each page
	 */
	setup: function() {
		var priv = this.$private;

		// Setup syntax highlighting
		priv.highlightCode();

		// Bind code toggle and selection
		$('ref:code').on('dblclick.guide', function() {
			priv.selectCode(this);
		});
	}
}, {
	/**
	 * Load highlight.js assets and highlight code
	 *
	 * @private
	 */
	highlightCode: function() {
		Wee.assets.load({
			root: 'https://cdnjs.cloudflare.com/ajax/libs/highlight.js/9.2.0/',
			files: [
				'highlight.min.js',
				'styles/github.min.css'
			],
			success: function() {
				$('ref:code').find('code').each(
					hljs.highlightBlock
				);
			}
		});
	},

	/**
	 * Select all content in a code block
	 *
	 * @private
	 * @param {HTMLElement} el - target code wrapper
	 */
	selectCode: function(el) {
		var range = Wee._doc.createRange(),
			sel = Wee._win.getSelection();

		range.selectNodeContents(el);

		sel.removeAllRanges();
		sel.addRange(range);
	}
});

/* global hljs */

Wee.fn.make('guide', {
	init: function() {
		Wee.history.init({
			bind: true,
			extensions: ['html']
		});
	},

	/**
	 * Bind guide functionality on each page
	 */
	setup: function() {
		var priv = this.$private;

		// Setup syntax highlighting
		priv.highlightCode();

		// Bind code toggle and selection
		$('ref:code').on('dblclick.guide', function() {
			priv.selectCode(this);
		});
	}
}, {
	/**
	 * Load highlight.js assets and highlight code
	 *
	 * @private
	 */
	highlightCode: function() {
		Wee.assets.load({
			root: 'https://cdnjs.cloudflare.com/ajax/libs/highlight.js/9.5.0/',
			files: [
				'highlight.min.js',
				'styles/github.min.css'
			],
			success: function() {
				$('ref:code').find('code').each(
					hljs.highlightBlock
				);
			}
		});
	},

	/**
	 * Select all content in a code block
	 *
	 * @private
	 * @param {HTMLElement} el - target code wrapper
	 */
	selectCode: function(el) {
		var range = Wee._doc.createRange(),
			sel = Wee._win.getSelection();

		range.selectNodeContents(el);

		sel.removeAllRanges();
		sel.addRange(range);
	}
});

Update Highlight.js version from 9.2.0 to 9.5.0


'use strict';
import React, { Component } from 'react';
import ReactDOM from 'react-dom';

import NavBar from './NavBar';

// Top-level React (pure functional) component:
const App = () => (
  <div>
    Hello, World!
  </div>
);

// Inject into #app HTML element:
ReactDOM.render(<App />, document.getElementById('app'));

'use strict';
import React, { Component } from 'react';
import ReactDOM from 'react-dom';

import NavBar from './NavBar';

// Top-level React (pure functional) component:
const App = () => (
  <div>
    <NavBar />
    Hello, World!
  </div>
);

// Inject into #app HTML element:
ReactDOM.render(<App />, document.getElementById('app'));

Apply NavBar component inside App


'use strict';

const browsers = [
  'last 1 Chrome versions',
  'last 1 Firefox versions',
  'last 1 Safari versions',
];

// Ember's browser support policy is changing, and IE11 support will end in
// v4.0 onwards.
//
// See https://deprecations.emberjs.com/v3.x#toc_3-0-browser-support-policy
//
// If you need IE11 support on a version of Ember that still offers support
// for it, uncomment the code block below.
//
// const isCI = Boolean(process.env.CI);
// const isProduction = process.env.EMBER_ENV === 'production';
//
// if (isCI || isProduction) {
//   browsers.push('ie 11');
// }

module.exports = {
  browsers,
};

'use strict';

const browsers = [
  'last 1 Chrome versions',
  'last 1 Firefox versions',
  'last 1 Safari versions',
];

module.exports = {
  browsers,
};

Remove IE11 comments from `config/target.js` in app blueprint


/* eslint-disable no-param-reassign */
import mapValues from 'lodash/mapValues';
import isObject from 'lodash/isObject';

const MATCH_FIRST_CURLY_BRACES_REGEX = /^\{/;
const MATCH_LAST_CURLY_BRACES_REGEX = /\};$/;
const PROPS_REGEX = /\s*props: /;
const LAST_COMMA_REGEX = /,\s*$/;

// Remove all the superflous stuff we added for readability
// in the saved files
const parse = (variations) => {
  let props;
  const code =
    variations
      .replace(MATCH_FIRST_CURLY_BRACES_REGEX, '')
      .replace(MATCH_LAST_CURLY_BRACES_REGEX, '')
      .replace(PROPS_REGEX, 'props = ')
      .replace(LAST_COMMA_REGEX, ';');
  eval(code); // eslint-disable-line no-eval
  return props;
};

// Convert the data structure to the needed data structure
const convert = (props, newObject) => {
  mapValues(props, (prop, key) => {
    if (isObject(prop.value)) {
      newObject[key] = convert(prop.value, {});
      return;
    }
    newObject[key] = prop.value;
  });
  return newObject;
};

const variationsToProps = (variations) => (
  mapValues(variations, (variation) => {
    const variationAsCode = parse(variation);
    return convert(variationAsCode, {}, {});
  }
));

export default variationsToProps;

/* eslint-disable no-param-reassign */
import mapValues from 'lodash/mapValues';
import isObject from 'lodash/isObject';

const MATCH_FIRST_CURLY_BRACES_REGEX = /^\s*\{/;
const MATCH_LAST_CURLY_BRACES_REGEX = /\};\s*$/;
const PROPS_REGEX = /\s*props: /;
const LAST_COMMA_REGEX = /,\s*$/;

// Remove all the superflous stuff we added for readability
// in the saved files
const parse = (variations) => {
  let props;
  const code =
    variations
      .replace(MATCH_FIRST_CURLY_BRACES_REGEX, '')
      .replace(MATCH_LAST_CURLY_BRACES_REGEX, '')
      .replace(PROPS_REGEX, 'props = ')
      .replace(LAST_COMMA_REGEX, ';');
  eval(code); // eslint-disable-line no-eval
  return props;
};

// Convert the data structure to the needed data structure
const convert = (props, newObject) => {
  mapValues(props, (prop, key) => {
    if (isObject(prop.value)) {
      newObject[key] = convert(prop.value, {});
      return;
    }
    newObject[key] = prop.value;
  });
  return newObject;
};

const variationsToProps = (variations) => (
  mapValues(variations, (variation) => {
    const variationAsCode = parse(variation);
    return convert(variationAsCode, {}, {});
  }
));

export default variationsToProps;

Fix whitespace, empty line at end of file


/*
 * Module code goes here. Use 'module.exports' to export things:
 * module.exports = 'a thing';
 *
 * You can import it from another modules like this:
 * var mod = require('creeps'); // -> 'a thing'
 */
var roles = require('roles');

module.exports = function() {
    var name;
    for(name in Game.creeps) {
        var creep = Game.creeps[name];

        if (!creep.my) {
            continue;
        }

        if ("role" in creep.memory) {
            roles[creep.memory.role].turn(creep);
        } else {
            console.log('Warning: Ant without role');
        }
    }
}

/*
 * Module code goes here. Use 'module.exports' to export things:
 * module.exports = 'a thing';
 *
 * You can import it from another modules like this:
 * var mod = require('creeps'); // -> 'a thing'
 */
var roles = require('roles');

module.exports = function() {
    var name, role;
    for (name in Game.creeps) {
        var creep = Game.creeps[name];

        if (!creep.my) {
            continue;
        }

        if ("role" in creep.memory) {
            roles[creep.memory.role].turn(creep);
        } else {
            console.log('Warning: Ant without role');
        }
    }

    for (role in roles) {
        if (roles[role].endTurn) {
            roles[role].endTurn();
        }
    }
}

Add optional role hook `endTurn`


var session = null;

// instead of windows.onload
$(document).ready( function() {

    // hide iphone/ipad URL bar
    setTimeout(function() { window.scrollTo(0, 1) }, 100);

    // initialize zoness
    session = new Session(
	SESSION_TYPE_DRAW,
	function() {
	    console.log("page_draw/ready: session callback ok");
	    var drawing = new Drawing(session, 'session-drawing');
	   // var globalzone = new GlobalZone(session, 'session-global');
	   // var colorzone = new ColorZone(session, 'session-colors');

	    console.log("page_draw/ready: prepicker");
	    var picker = new Color.Picker({
		    callback: function(hex) {
				drawing.color_set( "#" + hex );
			      }
	    });
	    console.log("page_draw/ready: postpicker");
	    picker.el.style.top = "5px";
	    picker.el.style.left = "5px";

	    // create session with
	} );

    /*
    setInterval( function() {
	// refresh connexion to the server
    }, 10000 );
    */
} );

var session = null, picker;

// instead of windows.onload
$(document).ready( function() {
    // hide iphone/ipad URL bar
    setTimeout(function() { window.scrollTo(0, 1) }, 100);

    // initialize zoness
    session = new Session(
        SESSION_TYPE_DRAW,
        function() {
            //console.log("page_draw/ready: session callback ok");
            var drawing = new Drawing(session, 'session-drawing');
            // var globalzone = new GlobalZone(session, 'session-global');
            // var colorzone = new ColorZone(session, 'session-colors');

            //console.log("page_draw/ready: prepicker");
            $("#brush").click(function(event){
                event.preventDefault();
                if (undefined === picker) {
                    picker = new Color.Picker({
                        callback: function(hex) {
                            drawing.color_set( "#" + hex );
                        }
                    });
                    picker.el.style.top = "5px";
                    picker.el.style.left = "5px";
                }
                if (true === $(picker.el).is(":hidden")) {
                    $(picker.el).show();
                }
            });
            //console.log("page_draw/ready: postpicker");

            // create session with
        }
    );
    /*
    setInterval( function() {
    // refresh connexion to the server
    }, 10000 );
    */
});

Create an instance of the color picker in order to be able to show it.


// User may override layout width in settings, inject new value
//

'use strict';

module.exports = function (N) {

  N.wire.after('server_chain:http:*', async function inject_layout_width(env) {
    let layout_max_width = Number(await env.extras.settings.fetch('layout_max_width'));
    let setting = N.config.setting_schemas.user.layout_max_width;

    if (layout_max_width && layout_max_width !== setting.default &&
       (!setting.min || layout_max_width >= setting.min)) {

      env.inject_headers = env.inject_headers || [];
      env.inject_headers.push(
        `<style>.layout__container { max-width: ${layout_max_width}px; }</style>`
      );
    }
  });
};

// User may override layout width in settings, inject new value
//

'use strict';

module.exports = function (N) {

  N.wire.after('server_chain:http:*', async function inject_layout_width(env) {
    let layout_max_width = Number(await env.extras.settings.fetch('layout_max_width'));
    let setting = N.config.setting_schemas.user.layout_max_width;

    if (layout_max_width && layout_max_width !== setting.default &&
       (!setting.min || layout_max_width >= setting.min)) {

      env.inject_headers = env.inject_headers || [];
      env.inject_headers.push(
        `<style>:root { --page-max-width: ${layout_max_width}px; }</style>`
      );
    }
  });
};

Set user-specified width as css variable