/*
 * Provide a search widget with a popup to enter and clear search terms.
 * Test suite can get a handle on this for clean slate testing.
 */
tsbar.initSearchWidget = function(exports, $) {

    function SearchWidget() {

        this.$compiledTemplate = $(exports.tswidgets.templates['search.hbs'](Handlebars));
        //TODO: this is a hack as the pre-compiled templates have carriage returns which confuses jquery
        this.$el = $(this.$compiledTemplate[0]);
        this.$popup = $(this.$compiledTemplate[2]);

        this._widget = tsbar.Widget({
            el: this.$el,
            popup: this.$popup
        });
    }

    SearchWidget.prototype.getWidget = function() {
        return this._widget;
    };

    SearchWidget.prototype._registerListeners = function() {
        this.$popup.find('#tsbar-search-button').click(this._doSearch);
        this.$popup.find('#tsbar-clear-button').click(this._doClear);
    };

    SearchWidget.prototype._doSearch = function() {
        var query = $('#tsbar-query-text').val();
        $('#tsbar-search-results').load('/hsearch?q=' + query + ' #container');
    };

    SearchWidget.prototype._doClear = function() {
        $('#tsbar-search-results').html('');
        $('#tsbar-query-text').val('');
    };

    function main() {
        var searchWidget = new SearchWidget();
        searchWidget._registerListeners();

        exports.tsbar.searchWidget = searchWidget.getWidget();
    }

    main();

};

(function(exports, $) {

    tsbar.initSearchWidget(exports, $);

}(window, jQuery));

/*
 * Provide a search widget with a popup to enter and clear search terms.
 * Test suite can get a handle on this for clean slate testing.
 */
tsbar.initSearchWidget = function(exports, $) {

    function SearchWidget() {

        this.$compiledTemplate = $(exports.tswidgets.templates['search.hbs'](Handlebars));
        this.$el = this.$compiledTemplate.first();
        this.$popup = this.$compiledTemplate.last();

        this._widget = tsbar.Widget({
            el: this.$el,
            popup: this.$popup
        });
    }

    SearchWidget.prototype.getWidget = function() {
        return this._widget;
    };

    SearchWidget.prototype._registerListeners = function() {
        this.$popup.find('#tsbar-search-button').click(this._doSearch);
        this.$popup.find('#tsbar-clear-button').click(this._doClear);
    };

    SearchWidget.prototype._doSearch = function() {
        var query = $('#tsbar-query-text').val();
        $('#tsbar-search-results').load('/hsearch?q=' + query + ' #container');
    };

    SearchWidget.prototype._doClear = function() {
        $('#tsbar-search-results').html('');
        $('#tsbar-query-text').val('');
    };

    function main() {
        var searchWidget = new SearchWidget();
        searchWidget._registerListeners();

        exports.tsbar.searchWidget = searchWidget.getWidget();
    }

    main();

};

(function(exports, $) {

    tsbar.initSearchWidget(exports, $);

}(window, jQuery));

Refactor - slightly more elegant way of splitting the template


var sse4_crc32 = require("bindings")("sse4_crc32");

/**
 * Defines a progressive 32-bit CRC calculator
 *
 * @param input The input string for which the CRC is to be calculated
 * @param initial_crc The initial CRC passed in [optional]
 *
 * @constructor
 */
function CRC32(input, initial_crc) {
    this.crc32 = initial_crc || 0;

    if (input) {
        this.update(input);
    }
}

/**
 * Progressively calculates the 32-bit CRC
 *
 * @param input Additional input to calculate the CRC for
 */
CRC32.prototype.update = function (input) {
    this.crc32 = sse4_crc32.calculate(input, this.crc32);
    return this.crc32;
};

/**
 * Returns the 32-bit CRC
 *
 * @returns {Integer}
 */
CRC32.prototype.crc = function () {
    return this.crc32;
};

/**
 * Used to calculate 32-bit CRC for single instances of strings and/or buffers
 *
 * @param input The input string for which the CRC is to be calculated
 *
 * @returns {Integer}
 */
function calculate(input) {
    return sse4_crc32.calculate(input, 0);
}

module.exports = {
    CRC32    : CRC32,
    calculate: calculate
};

var sse4_crc32 = require("bindings")("sse4_crc32");

/**
 * Defines a progressive 32-bit CRC calculator
 *
 * @param input The input string for which the CRC is to be calculated
 * @param initial_crc The initial CRC passed in [optional]
 *
 * @constructor
 */
function CRC32(input, initial_crc) {
    this.crc32 = initial_crc || 0;

    if (input) {
        this.update(input);
    }
}

/**
 * Progressively calculates the 32-bit CRC
 *
 * @param input Additional input to calculate the CRC for
 */
CRC32.prototype.update = function (input) {
    this.crc32 = sse4_crc32.calculate(input, this.crc32);
    return this.crc32;
};

/**
 * Returns the 32-bit CRC
 *
 * @returns {Integer}
 */
CRC32.prototype.crc = function () {
    return this.crc32;
};

/**
 * Used to calculate 32-bit CRC for single instances of strings and/or buffers
 *
 * @param input The input string for which the CRC is to be calculated
 * @param initial_crc The initial CRC passed in [optional]
 *
 * @returns {Integer}
 */
function calculate(input, initial_crc) {
    return sse4_crc32.calculate(input, initial_crc || 0);
}

module.exports = {
    CRC32    : CRC32,
    calculate: calculate
};

Add optional initial_crc to calculate()


define(['require', 'app', 'backbone.marionette', 'backbone', 'marked', 'underscore'],
  function (require, App, Marionette, Backbone, marked, _) {
    'use strict';

    var controller = {
      showHelp: function () {
        require(['./views/HelpView'],
          function (HelpView) {
            $.getJSON('/scripts/help/keyboardShortcuts.json', function (data) {
              _.each(data, function (category) {
                _.each(category.shortcuts, function (shortcut) {
                  shortcut.description = marked(shortcut.description);
                })
              });

              var helpModel = new Backbone.Model({keyboardShortcuts: data});
              App.mainRegion.show(new HelpView({model: helpModel}));
            });
          });
      }
    };

    App.addInitializer(function () {
      new Marionette.AppRouter({
        controller: controller,
        appRoutes: {
          'help': 'showHelp'
        }
      });
    });
  });

define(['require', 'app', 'backbone.marionette', 'backbone', 'marked',
    'underscore', 'json!./keyboardShortcuts.json'],
  function (require, App, Marionette, Backbone, marked, _, keyboardShortcuts) {
    'use strict';

    var controller = {
      showHelp: function () {
        require(['./views/HelpView'],
          function (HelpView) {
            _.each(keyboardShortcuts, function (category) {
              _.each(category.shortcuts, function (shortcut) {
                shortcut.description = marked(shortcut.description);
              });
            });

            var helpModel = new Backbone.Model({keyboardShortcuts: keyboardShortcuts});
            App.mainRegion.show(new HelpView({model: helpModel}));
          });
      }
    };

    App.addInitializer(function () {
      new Marionette.AppRouter({
        controller: controller,
        appRoutes: {
          'help': 'showHelp'
        }
      });
    });
  });

Load keyboard shortcuts json using json requirejs plugin so that it can be compiled into build


var makeNode = require("../node/make_node"),
	minify = require("../graph/minify"),
	prettier = require("prettier");

module.exports = function(bundle, options){
	var source = prettier.format(
		`
			if(typeof process === "undefined") {
				process = { env: { NODE_ENV: "development" } };
			}
		`,
		{ useTabs: true }
	);

	var node = makeNode("[process-shim]", source);

	if(options.minify){
		minify([node]);
	}

	bundle.nodes.unshift(node);
};

var makeNode = require("../node/make_node"),
	minify = require("../graph/minify"),
	prettier = require("prettier");

module.exports = function(bundle, options){
	var source = prettier.format(
		`
			if(typeof process === "undefined") {
				(function(global){
					global.process = { env: { NODE_ENV: "development" } };
				})(typeof self !== "undefined" : self : global);
			}
		`,
		{ useTabs: true }
	);

	var node = makeNode("[process-shim]", source);

	if(options.minify){
		minify([node]);
	}

	bundle.nodes.unshift(node);
};

Set the global object explicitly


module.exports = function () {
	return function (irc) {
		irc.topic = function (channel, topic, fn) {
			if (typeof topic === 'function') {
				fn = topic;
				topic = '';
			}

			this.write('TOPIC ' + channel + (topic && ' :' + topic || ''), fn);
		};

		irc.on('RPL_NOTOPIC', function (msg, channel) {
			var e = {};
			e.target = channel;
			e.message = null;
			e.raw = msg.string;
			irc.emit('topic', e);
		});

		irc.on('RPL_TOPIC', function (msg, channel, topic) {
			var e = {};
			e.target = channel;
			e.message = topic;
			e.raw = msg.string;
			irc.emit('topic', e);
		});

		irc.on('RPL_TOPIC_WHO_TIME', function (msg, channel, nick, time) {
			irc.emit('topic:time', {
				target: channel,
				nick: nick,
				time: new Date(time),
				raw: msg.string
			});
		});

		irc.on('TOPIC', function (msg) {
			var e = {};
			e.nick = msg.nick;
			e.target = msg.params.split(' ')[0];
			e.message = msg.trailing;
			e.raw = msg.string;
			irc.emit('topic', e);
		});
	};
};

module.exports = function () {
	return function (irc) {
		irc.topic = function (channel, topic, fn) {
			if (typeof topic === 'function') {
				fn = topic;
				topic = '';
			}

			this.write('TOPIC ' + channel + (topic && ' :' + topic || ''), fn);
		};

		irc.on('RPL_NOTOPIC', function (msg, channel) {
			var e = {};
			e.target = channel;
			e.message = null;
			e.raw = msg.string;
			irc.emit('topic', e);
		});

		irc.on('RPL_TOPIC', function (msg, channel, topic) {
			var e = {};
			e.target = channel;
			e.message = topic;
			e.raw = msg.string;
			irc.emit('topic', e);
		});

		irc.on('RPL_TOPIC_WHO_TIME', function (msg, channel, nick, time) {
			irc.emit('topic:time', {
				target: channel,
				nick: nick,
				time: new Date(parseFloat(time)*1000),
				raw: msg.string
			});
		});

		irc.on('TOPIC', function (msg) {
			var e = {};
			e.nick = msg.nick;
			e.target = msg.params.split(' ')[0];
			e.message = msg.trailing;
			e.raw = msg.string;
			irc.emit('topic', e);
			irc.emit('topic:changed', e);
		});
	};
};

Fix date processing on RPL_TOPIC_WHO_TIME


var assert = require('assert');
var ghir = require('../dist');

assert.ok(true);

assert(ghir.hasVote('I agree, +1!'));
assert(! ghir.hasVote('I agree, 2+1!'));
assert(! ghir.hasVote('I agree!'));

// assert(ghir.hasVote(' \uD83D\uDC4Db '));
// assert(ghir.hasVote('a\uD83D\uDC4Db'));
// assert(ghir.hasVote('a👍'));
// assert(ghir.hasVote('👍b'));

var assert = require('assert');
var ghir = require('../dist');

assert.ok(true);

assert(ghir.hasVote('I agree, +1!'));
assert(! ghir.hasVote('I agree, 2+1!'));
assert(! ghir.hasVote('I agree!'));

assert(ghir.hasVote(' \uD83D\uDC4D '));
assert(ghir.hasVote(' \uD83D\uDC4D b '));
assert(ghir.hasVote('a \uD83D\uDC4D b'));
assert(ghir.hasVote('a 👍'));
assert(ghir.hasVote('👍 b'));

Add tests for unicode thumbs up.


// noinspection JSUnresolvedVariable
module.exports = {
  cache: false,
  debug: false,
  module: {
    loaders: [
      {
        // This is a custom property.
        name: 'jsx',
        test: /\.jsx?$/,
        loader: 'babel',
        query: {
          presets: ['react', 'es2015', 'stage-0'],
        },
        exclude: /node_modules/,
      },
      {test: /\.json$/, loader: 'json-loader'},
      {test: /\.png$/, loader: 'url-loader?prefix=img/&limit=5000'},
      {test: /\.jpg$/, loader: 'url-loader?prefix=img/&limit=5000'},
      {test: /\.gif$/, loader: 'url-loader?prefix=img/&limit=5000'},
      {test: /\.woff2?$/, loader: 'url-loader?prefix=font/&limit=5000'},
      {test: /\.eot$/, loader: 'file-loader?prefix=font/'},
      {test: /\.ttf$/, loader: 'file-loader?prefix=font/'},
      {test: /\.svg$/, loader: 'file-loader?prefix=font/'},
    ],
    noParse: /\.min\.js/,
  },
  eslint: {
    eslint: {
      configFile: '.eslintrc',
    },
  },
  resolve: {
    extensions: ['', '.js', '.json', '.jsx'],
    modulesDirectories: [
      'node_modules',
      'app/components',
    ],
  },
  node: {
    __dirname: true,
    fs: 'empty',
  },
};

// noinspection JSUnresolvedVariable
module.exports = {
  cache: false,
  debug: false,
  module: {
    loaders: [
      {
        // This is a custom property.
        name: 'jsx',
        test: /\.jsx?$/,
        loader: 'babel',
        query: {
          presets: ['react', 'es2015', 'stage-0'],
        },
        exclude: /node_modules/,
      },
      {test: /\.json$/, loader: 'json-loader'},
      {test: /\.png$/, loader: 'url-loader?prefix=img/&limit=5000'},
      {test: /\.jpg$/, loader: 'url-loader?prefix=img/&limit=5000'},
      {test: /\.gif$/, loader: 'url-loader?prefix=img/&limit=5000'},
      { test: /\.woff2?(\?.*)?$/, loader: 'url-loader?prefix=font/&limit=5000' },
      { test: /\.(eot|ttf|svg)(\?.*)?$/, loader: 'file-loader?prefix=font/' },
    ],
    noParse: /\.min\.js/,
  },
  eslint: {
    eslint: {
      configFile: '.eslintrc',
    },
  },
  resolve: {
    extensions: ['', '.js', '.json', '.jsx'],
    modulesDirectories: [
      'node_modules',
      'app/components',
    ],
  },
  node: {
    __dirname: true,
    fs: 'empty',
  },
};

Allow query parameters in font source urls


'use strict';

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

describe('mongoose-password-bcrypt-nodejs', function() {
});

Add use strict to tests


jsdom    = require('jsdom')
window   = jsdom.jsdom().createWindow();
document = window.document;
global.document.implementation.createHTMLDocument = function (html, url) {
    return jsdom.html(html);
};

$ = jQuery = require('jquery');

$('body').html('

');

jsdom    = require('jsdom')
window   = jsdom.jsdom().createWindow();
document = window.document;
global.document.implementation.createHTMLDocument = function (html, url) {
    return jsdom.html(html);
};

global.location = { href: '' };

$ = jQuery = require('jquery');

$('body').html('

');

Add location hack to node.js test runtime


var navbar = function($interval, $q, User) {
    return {
        restrict: 'E', // only activate on element attribute
        templateUrl: 'navbar.html',
        link: function(scope, element) {
            var formats = [formatPercentResolvedBugs, formatNumberProjects];
            var currentFormat;
            var $statisticLabel = $('.label-statistic', element);

            function getStatistics() {
                var deferred = $q.defer();
                User.statistics(function(successData) {
                    deferred.resolve(successData);
                }, function(errorData) {
                    deferred.reject(errorData);
                });
                return deferred.promise;
            }

            function formatPercentResolvedBugs(data) {
                var percent = Math.round((data.number_resolved_bugs / data.number_bugs) * 100);
                if (_.isNaN(percent)) {
                    percent = 100;
                }

                return percent + "% of the bugs across all your projects are resolved.";
            }

            function formatNumberProjects(data) {
                return "You are currently working on " + data.number_projects + " projects.";
            }

            getStatistics().then(function(data) {
                scope.statistics = [
                    formatPercentResolvedBugs(data),
                    formatNumberProjects(data)
                ];
            });

            $statisticLabel.textillate({
                minDisplayTime: 15000,
                initialDelay: 0,
                loop: true,
                autoStart: true,
                in: {
                    effect: 'fadeInUp',
                    sync: true
                },
                out: {
                    effect: 'fadeOutUp',
                    sync: true
                }
            });
        }
    };
};

bugtracker.directive('navbar', ['$interval', '$q', 'User', navbar]);

var navbar = function($interval, $q, User) {
    return {
        restrict: 'E', // only activate on element attribute
        templateUrl: 'navbar.html',
        link: function(scope, element) {
            var formats = [formatPercentResolvedBugs, formatNumberProjects];
            var currentFormat;
            var $statisticLabel = $('.label-statistic', element);

            function getStatistics() {
                var deferred = $q.defer();
                User.statistics(function(successData) {
                    deferred.resolve(successData);
                }, function(errorData) {
                    deferred.reject(errorData);
                });
                return deferred.promise;
            }

            function formatPercentResolvedBugs(data) {
                var percent = Math.round((data.number_resolved_bugs / data.number_bugs) * 100);
                if (_.isNaN(percent)) {
                    percent = 100;
                }

                return percent + "% of the bugs across all your projects are resolved.";
            }

            function formatNumberProjects(data) {
                return "You are currently working on " + data.number_projects + " projects.";
            }

            getStatistics().then(function(data) {
                scope.statistics = [
                    formatPercentResolvedBugs(data),
                    formatNumberProjects(data)
                ];

                $statisticLabel.textillate({
                    minDisplayTime: 15000,
                    initialDelay: 0,
                    loop: true,
                    autoStart: true,
                    in: {
                        effect: 'fadeInUp',
                        sync: true
                    },
                    out: {
                        effect: 'fadeOutUp',
                        sync: true
                    }
                });
            });
        }
    };
};

bugtracker.directive('navbar', ['$interval', '$q', 'User', navbar]);

Fix statistics not showing in navbar during the first iteration of textillate


global.useBluebird = false;
global.useQ = false;
global.useWhen = false;

global.useCreed = true;
global.parallelQueries = 25;

var creed = require('../../dist/creed');

require('../lib/fakesP');

module.exports = function upload(stream, idOrPath, tag, done) {
    var queries = new Array(global.parallelQueries);
    var tx = db.begin();

    for( var i = 0, len = queries.length; i < len; ++i ) {
        queries[i] = FileVersion.insert({index: i}).execWithin(tx);
    }

    creed.all(queries).then(function() {
        tx.commit();
        done();
    }, function(err) {
        tx.rollback();
        done(err);
    });
};

global.useBluebird = false;
global.useQ = false;
global.useWhen = false;

global.useCreed = true;

var creed = require('../../dist/creed');

require('../lib/fakesP');

module.exports = function upload(stream, idOrPath, tag, done) {
    var queries = new Array(global.parallelQueries);
    var tx = db.begin();

    for( var i = 0, len = queries.length; i < len; ++i ) {
        queries[i] = FileVersion.insert({index: i}).execWithin(tx);
    }

    creed.all(queries).then(function() {
        tx.commit();
        done();
    }, function(err) {
        tx.rollback();
        done(err);
    });
};

Remove unnecessary perf test config


var virus = document.querySelector("div");
var x = 1;
var y = 0;
var vy = 0;
var ay = 0;
var vx = .1; // px per millisecond
var startTime = Date.now();
var clickTime;
timer = setInterval(function animate() {
	var t = Date.now() - startTime;
	x = vx * t;
	y = vy * t + 400;

	virus.style.left = x + "px";
	virus.style.top = y + "px";

	if ( x > document.body.clientWidth) {
		startTime = Date.now();
	}
	if (ay >= .001) {
		var t = Date.now() - clickTime;
		vy = ay * t;
	}
	//if ( y > document.body.clientHeight && x > document.body.clientWidth) {
	//	console.log("hello");
	//	startTime = Date.now();
	//}
	if (y > screen.height){
		console.log("hello");
		startTime = Date.now();
		vy = 0;
		ay = 0;
	}
	//if ( y > document.body.clientHeight && x > document.body.clientWidth) {
	//	console.log("second if");
	//	vy = 0;
	//	ay = 0;
//}
},20); // ms  | 1000/20 = 50 frames per second (50 fps)

virus.addEventListener("click", function onclick(event) {
	ay = .001;
	clickTime = Date.now();

});

var virus = document.querySelector("div");
var x = 1;
var y = 0;
var vy = 0;
var ay = 0;
var vx = 0.1; // px per millisecond
var startTime = Date.now();
var clickTime;
timer = setInterval(function animate() {
	var t = Date.now() - startTime;
	x = vx * t;
	y = vy * t + 400;

	virus.style.left = x + "px";
	virus.style.top = y + "px";

	if ( x > document.body.clientWidth) {
		startTime = Date.now();
	}
	if (ay >= 0.001) {
		t = Date.now() - clickTime;
		vy = ay * t;
	}
	if (y > screen.height){
		console.log("hello");
		startTime = setTimeout(function() {
			vy = 0;
			ay = 0;
		}, 10000);
	}
},20); // ms  | 1000/20 = 50 frames per second (50 fps)

virus.addEventListener("click", function onclick(event) {
	ay = 0.001;
	clickTime = Date.now();
});

Add setTimeout property to delay ufo before starting over again


// Fake service (makes a tether)
/* jshint node: true */

(function () {
    "use strict";

    var runtime = require("quark_node_runtime.js");
    var ipify = require("./ipify");

    function Consumer() {};
    Consumer.prototype.consume = function (ip) {
        console.log("IP is " + ip);
        process.exit();
    };

    var m = new ipify.MyExternalIP(runtime, new Consumer());
    runtime.launch();
})();

// Fake service (makes a tether)
/* jshint node: true */

(function () {
    "use strict";

    var runtime = require("quark_node_runtime.js");
    var ipify = require("./ipify");

    function Consumer() {}
    Consumer.prototype.consume = function (ip) {
        console.log("IP is " + ip);
        process.exit();
    };

    var m = new ipify.MyExternalIP(runtime, new Consumer());
    runtime.launch();
})();

Fix typo in JS client example


'use strict';

var async = require('async');

/**
 * Create a new user and move it to a new subcompany.
 * The new user will be the first admin of the subcompany.
 * @param {Object} subcompany Informations of the subcompany
 * @param {Object} newAdmin User info of the admin to create.
 * @param {Function} cb(err, subcompany, admin)
 */
module.exports = function createSubcompanyWithAdmin(subcompany, newAdmin, finalCb) {
  newAdmin.is_admin = true;

  var self = this;
  async.waterfall([
    // Create the admin user who will be named admin of the new subcompany
    function createAdmin(cb) {
      self.postUser(newAdmin, cb);
    },
    function extractId(res, cb) {
      cb(null, res.body);
    },
    function createSubcompany(admin, cb) {
      subcompany.user = admin.id;
      self.postSubcompany(subcompany, function(err, res) {
        cb(err, res.body, admin);
      });
    },
  ], finalCb);
};

'use strict';

var async = require('async');

/**
 * Create a new user and move it to a new subcompany.
 * The new user will be the first admin of the subcompany.
 * @param {Object} subcompany Informations of the subcompany
 * @param {Object} newAdmin User info of the admin to create.
 * @param {Function} cb(err, subcompany, admin)
 */
module.exports = function createSubcompanyWithAdmin(subcompany, newAdmin, finalCb) {
  // Force set is_admin to false, for security reason: if the call to createSubcompany were to fail for any reasons,
  // we won't be left with an admin user in our parent company.
  newAdmin.is_admin = false;

  var self = this;
  async.waterfall([
    // Create the admin user who will be named admin of the new subcompany
    function createAdmin(cb) {
      self.postUser(newAdmin, cb);
    },
    function extractId(res, cb) {
      cb(null, res.body);
    },
    function createSubcompany(admin, cb) {
      subcompany.user = admin.id;
      self.postSubcompany(subcompany, function(err, res) {
        cb(err, res.body, admin);
      });
    },
  ], finalCb);
};

Fix potential security flaw in createSubcompany()


$(function() {
  $("#modal-1").on("change", function() {
    if ($(this).is(":checked")) {
      $("body").addClass("modal-open");
    } else {
      $("body").removeClass("modal-open");
    }
  });

  $(".modal-fade-screen, .modal-close").on("click", function() {
    $(".modal-state:checked").prop("checked", false).change();
  });

  $(".modal-inner").on("click", function(e) {
    e.stopPropagation();
  });
});

function modal () {
  $("#modal-1").on("change", function() {
    if ($(this).is(":checked")) {
      $("body").addClass("modal-open");
    } else {
      $("body").removeClass("modal-open");
    }
  });

  $(".modal-fade-screen, .modal-close").on("click", function() {
    $(".modal-state:checked").prop("checked", false).change();
  });

  $(".modal-inner").on("click", function(e) {
    e.stopPropagation();
  });
});

Rewrite js as stand alone function


Reduce delay to 1 second; 2 is too long.


/*
 * Copyright (C) 2014 - present Instructure, Inc.
 *
 * This file is part of Canvas.
 *
 * Canvas is free software: you can redistribute it and/or modify it under
 * the terms of the GNU Affero General Public License as published by the Free
 * Software Foundation, version 3 of the License.
 *
 * Canvas is distributed in the hope that it will be useful, but WITHOUT ANY
 * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
 * A PARTICULAR PURPOSE. See the GNU Affero General Public License for more
 * details.
 *
 * You should have received a copy of the GNU Affero General Public License along
 * with this program. If not, see <http://www.gnu.org/licenses/>.
 */

import router from './router'
import {monitorLtiMessages} from '@canvas/lti/jquery/messages'

router.start()
monitorLtiMessages()

/*
 * Copyright (C) 2014 - present Instructure, Inc.
 *
 * This file is part of Canvas.
 *
 * Canvas is free software: you can redistribute it and/or modify it under
 * the terms of the GNU Affero General Public License as published by the Free
 * Software Foundation, version 3 of the License.
 *
 * Canvas is distributed in the hope that it will be useful, but WITHOUT ANY
 * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
 * A PARTICULAR PURPOSE. See the GNU Affero General Public License for more
 * details.
 *
 * You should have received a copy of the GNU Affero General Public License along
 * with this program. If not, see <http://www.gnu.org/licenses/>.
 */

import router from './router'
import {monitorLtiMessages} from '@canvas/lti/jquery/messages'
import ready from '@instructure/ready'

ready(() => {
  router.start()
  monitorLtiMessages()
})

Fix flakey selenium specs for files


// nth-child pseudo selector
Modernizr.addTest('nthchild', function(){

  return Modernizr.testStyles("#modernizr div {width:1px} #modernizr div:nth-child(2n) {width:2px;}", function (elem) {
    var elems = elem.getElementsByTagName("div"),
    test = true;
    for (i=0; i<5; i++) {
      test = test && elems[i].offsetWidth == i%2+1;
    }
    return test;
  }, 5);

});

// nth-child pseudo selector
Modernizr.addTest('nthchild', function(){

    return Modernizr.testStyles('#modernizr div {width:1px} #modernizr div:nth-child(2n) {width:2px;}', function (elem) {
        var elems = elem.getElementsByTagName('div'),
            test = true;

        for (var i=0; i<5; i++) {
            test = test && elems[i].offsetWidth == i%2+1;
        }

        return test;
    }, 5);

});

Fix indent, single-quotes and leaking i


var Chat = function() {
    //this.members = {};
    //this.messages = [];
    this.state = this.buildState();
    this.colors = [
        'red',
        'blue',
        'green'
    ]
}

Chat.prototype.buildState = function() {
    var newState = {members: {}, messages: [], member_count: 0};
    return newState;
}

Chat.prototype.addMessage = function(conn, message) {
    var message = {
        user_id: conn.id,
        message: message,
        color: this.getMemberColor( conn.id )
    };

    this.state.messages.push( message );

    return message;
}

Chat.prototype.addMember = function(conn, username) {
    this.state.member_count += 1;

    var member = {
        id: conn.id,
        username: username,
        status: 'connected',
        color: this.colors.shift()
    };
    this.state.members[conn.id] = member;

    return member;
}

Chat.prototype.getMemberColor = function(id) {
    return this.state.members[id].color;
}

Chat.prototype.getState = function(){
    return this.state;
}

exports.Chat = Chat;

var Chat = function() {
    //this.members = {};
    //this.messages = [];
    this.state = this.buildState();
    this.colors = [
        'red',
        'blue',
        'green'
    ]
}

Chat.prototype.buildState = function() {
    var newState = {members: {}, messages: [], member_count: 0};
    return newState;
}

Chat.prototype.addMessage = function(conn, message) {
    var message = {
        user_id: conn.id,
        message: this.replaceUrls(message),
        color: this.getMemberColor( conn.id )
    };

    this.state.messages.push( message );

    return message;
}

Chat.prototype.addMember = function(conn, username) {
    this.state.member_count += 1;

    var member = {
        id: conn.id,
        username: username,
        status: 'connected',
        color: this.colors.shift()
    };
    this.state.members[conn.id] = member;

    return member;
}

Chat.prototype.getMemberColor = function(id) {
    return this.state.members[id].color;
}

Chat.prototype.getState = function(){
    return this.state;
}

Chat.prototype.replaceUrls = function(message) {
  var exp = /(\b(https?|ftp|file):\/\/[-A-Z0-9+&@#\/%?=~_|!:,.;]*[-A-Z0-9+&@#\/%=~_|])/ig;
  return message.replace(exp,"$1");
}

exports.Chat = Chat;

Add replace urls text to replace urls with proper links


define([
    'aux/has-property'
], function (hasProperty) {
    /**
     * Using the browsers console logging options, allowing to use console.error if accessible otherwise fallsback to
     * console.log (again if accessible).
     *
     * @exports console-logger
     *
     * @requires module:has-property
     *
     * @param {*} message Message to log out
     * @param {String} type What logging system to use error, log (defaults to log).
     */
    function logger (message, type) {
        type = type || 'log';

        if (hasProperty(window, 'console.' + type)) {
            console[type](message);
        } else if (hasProperty(window, 'console.log')) {
            console.log(message);
        }
    }

    return logger;
});

define([
    'aux/is-defined'
], function (isDefined) {
    /**
     * Using the browsers console logging options, allowing to use console.error if accessible otherwise fallsback to
     * console.log (again if accessible).
     *
     * @exports console-logger
     *
     * @requires module:is-defined
     *
     * @param {*} message Message to log out
     * @param {String} type What logging system to use error, log (defaults to log).
     */
    function logger (message, type) {
        var console = window.console;
        type = type || 'log';

        if (isDefined(console[type], 'function')) {
            console[type](message);
        } else if (isDefined(console.log, 'function')) {
            console.log(message);
        }
    }

    return logger;
});

Use is defined to check is a function not has property


import pluck from './pluck';
import {
    noop,
    mixin,
    observable,
    insertCss,
    emptyTemplate,
    computedAll,
    pureComputedAll
} from '../util/';

const modulePolyfill = {
    constructor: noop,
    defaults: {},
    template: emptyTemplate
};

// Transform transiton component module to native component module
//
// @param {Object} module Transiton component module
// @return {Object} Native component module
function transform(module) {
    const {
        name,
        constructor,
        defaults,
        props,
        mixins,
        methods,
        computed,
        pureComputed,
        style,
        template
    } = Object.assign({}, modulePolyfill, module);

    insertCss(module.style);
    Object.assign(constructor.prototype, methods);

    return {
        viewModel: {
            createViewModel(params, componentInfo) {
                componentInfo.name = name;

                const opts = Object.assign(
                    {},
                    defaults,
                    ko.toJS(params),
                    pluck(componentInfo.element)
                );
                const vm = new constructor(opts, componentInfo);

                props && Object.assign(vm, observable(opts, props));
                mixins && mixin(vm, opts, mixins);
                computed && computedAll(vm, computed);
                pureComputed && pureComputedAll(vm, pureComputed);

                vm.$opts = opts;
                vm.$defaults = defaults;
                vm.$info = vm.componentInfo = componentInfo;

                delete vm.$opts['$raw'];
                delete vm.$defaults['$raw'];

                return vm;
            }
        },
        synchronous: true,
        template
    };
}

export default transform;

import pluck from './pluck';
import {
    noop,
    mixin,
    observable,
    insertCss,
    emptyTemplate,
    computedAll,
    pureComputedAll
} from '../util/';

const modulePolyfill = {
    defaults: {},
    template: emptyTemplate
};

// Transform transiton component module to native component module
//
// @param {Object} module Transiton component module
// @return {Object} Native component module
function transform(module) {
    const {
        name,
        constructor,
        defaults,
        props,
        mixins,
        methods,
        computed,
        pureComputed,
        style,
        template
    } = Object.assign({
        constructor: function() {}
    }, modulePolyfill, module);

    insertCss(module.style);
    Object.assign(constructor.prototype, methods);

    return {
        viewModel: {
            createViewModel(params, componentInfo) {
                componentInfo.name = name;

                const opts = Object.assign(
                    {},
                    defaults,
                    ko.toJS(params),
                    pluck(componentInfo.element)
                );
                const vm = new constructor(opts, componentInfo);

                props && Object.assign(vm, observable(opts, props));
                mixins && mixin(vm, opts, mixins);
                computed && computedAll(vm, computed);
                pureComputed && pureComputedAll(vm, pureComputed);

                vm.$opts = opts;
                vm.$defaults = defaults;
                vm.$info = vm.componentInfo = componentInfo;

                delete vm.$opts['$raw'];
                delete vm.$defaults['$raw'];

                return vm;
            }
        },
        synchronous: true,
        template
    };
}

export default transform;

Fix constructor shared between components


const path = require("path");
const webpack = require("webpack");

var MINIFY = new webpack.optimize.UglifyJsPlugin({minimize: true});

module.exports = {
	name: "sunwell-card-renderer",
	target: "node",
	entry: {
		sunwell: path.join(__dirname, "sunwell-card-renderer.js"),
	},
	output: {
		path: path.resolve(__dirname, "dist"),
		filename: "sunwell-card-renderer.js",
	},
	resolve: {
		extensions: [".webpack.js", ".web.js", ".ts", ".js", ".node"],
	},
	node: {
		"fs": "empty",
	},
	module: {
		loaders: [
			{
				test: /\.ts$/,
				loaders: [
					"ts-loader",
				],
			}, {
				test: /\.node$/,
				loaders: ["node-loader"],
			}
		],
	},
	plugins: [/*MINIFY*/], // ES6 unsupported by uglifyjs
};

const path = require("path");
const webpack = require("webpack");

var MINIFY = new webpack.optimize.UglifyJsPlugin({minimize: true});

module.exports = {
	name: "sunwell-card-renderer",
	target: "node",
	entry: {
		sunwell: path.join(__dirname, "sunwell-card-renderer.js"),
	},
	output: {
		path: path.resolve(__dirname, "dist"),
		filename: "sunwell-card-renderer.js",
	},
	resolve: {
		extensions: [".webpack.js", ".web.js", ".ts", ".js", ".node"],
	},
	node: {
		"__dirname": false,
		"__filename": false,
		"fs": "empty",
	},
	module: {
		loaders: [
			{
				test: /\.ts$/,
				loaders: [
					"ts-loader",
				],
			}, {
				test: /\.node$/,
				loaders: ["node-loader"],
			}
		],
	},
	plugins: [/*MINIFY*/], // ES6 unsupported by uglifyjs
};

Fix __dirname and __filename in sunwell-card-renderer


import Ember from 'ember';
import { getContext } from 'ember-test-helpers';

export default function testWrapper(testName, callback, qunit) {
  function wrapper(assert) {
    var context = getContext();

    var result = callback.call(context, assert);

    function failTestOnPromiseRejection(reason) {
      var message;
      if (reason instanceof Error) {
        message = reason.stack;
        if (reason.message && message.indexOf(reason.message) < 0) {
          // PhantomJS has a `stack` that does not contain the actual
          // exception message.
          message = Ember.inspect(reason) + "\n" + message;
        }
      } else {
        message = Ember.inspect(reason);
      }
      ok(false, message);
    }

    Ember.run(function(){
      QUnit.stop();
      Ember.RSVP.Promise.resolve(result)['catch'](failTestOnPromiseRejection)['finally'](QUnit.start);
    });
  }

  qunit(testName, wrapper);
}

import Ember from 'ember';
import { getContext } from 'ember-test-helpers';

export default function testWrapper(testName, callback, qunit) {
  function wrapper() {
    var context = getContext();

    var result = callback.apply(context, arguments);

    function failTestOnPromiseRejection(reason) {
      var message;
      if (reason instanceof Error) {
        message = reason.stack;
        if (reason.message && message.indexOf(reason.message) < 0) {
          // PhantomJS has a `stack` that does not contain the actual
          // exception message.
          message = Ember.inspect(reason) + "\n" + message;
        }
      } else {
        message = Ember.inspect(reason);
      }
      ok(false, message);
    }

    Ember.run(function(){
      QUnit.stop();
      Ember.RSVP.Promise.resolve(result)['catch'](failTestOnPromiseRejection)['finally'](QUnit.start);
    });
  }

  qunit(testName, wrapper);
}

Change test wrapper callback from `call` to `apply`


import React from 'react'
import PropTypes from 'prop-types'

import { useI18n } from 'components/TranslationsProvider'

const maxAltitude = 3353
const minAltitude = 3200

const ExitAltitude = ({ altitude }) => {
  const { t } = useI18n()
  if (!altitude) return null

  const showWarning = altitude < minAltitude || altitude > maxAltitude

  return (
    <span>
      Exit: {altitude}m
      {showWarning && (
        <>

          <i className="fas fa-exclamation-triangle text-warning" />
        </>
      )} {t('units.m')}
    </span>
  )
}

ExitAltitude.propTypes = {
  altitude: PropTypes.number
}

ExitAltitude.defaultProps = {
  altitude: undefined
}

export default ExitAltitude

import React from 'react'
import PropTypes from 'prop-types'

import { useI18n } from 'components/TranslationsProvider'

const maxAltitude = 3353
const minAltitude = 3200

const ExitAltitude = ({ altitude }) => {
  const { t } = useI18n()
  if (!altitude) return null

  const showWarning = altitude < minAltitude || altitude > maxAltitude

  return (
    <span>
      Exit: {altitude} {t('units.m')}
      {showWarning && (
        <>

          <i className="fas fa-exclamation-triangle text-warning" />
        </>
      )}
    </span>
  )
}

ExitAltitude.propTypes = {
  altitude: PropTypes.number
}

ExitAltitude.defaultProps = {
  altitude: undefined
}

export default ExitAltitude

Fix exit altitude units placement


// server.js

/**IMPORT**/
var express = require('express');
var url = require('url');
var inject_pipe = require('./inject-pipe.js');

/** SET **/
var app = express();

/**FUNCTION**/
function sendViewMiddleware(req, res, next) { //send .html
    res.sendHtml = function(html) {
        return res.sendFile(__dirname + "/" + html);
    }
    next();
}
app.use(sendViewMiddleware);

/**GET**/
app.get('/', function (req, res) { //主页
    console.log("[SERVER][GET] /");
    res.sendHtml('index.html');
});

app.get('/pipe', function (req, res) { //pipe
    console.log("[SERVER][GET] /pipe");
    var params = url.parse(req.url,true).query;
    console.log(params);
    var href = params['href']
    inject_pipe.pipe(href,function(html){
        res.send(html);
    });
});

/**启动服务器**/
var server = app.listen(10000, function () {
    var host = server.address().address
    var port = server.address().port
    console.log("Sever Start @ http://%s:%s", host, port)
}); //于指定端口启动服务器

// server.js

/**IMPORT**/
var express = require('express');
var url = require('url');
var inject_piper = require('./inject-piper.js');

/** SET **/
var app = express();

/**FUNCTION**/
function sendViewMiddleware(req, res, next) { //send .html
    res.sendHtml = function(html) {
        return res.sendFile(__dirname + "/" + html);
    }
    next();
}
app.use(sendViewMiddleware);

/**GET**/
app.get('/', function (req, res) { //主页
    console.log("[SERVER][GET] /");
    res.sendHtml('index.html');
});

app.get('/pipe', function (req, res) { //pipe
    console.log("[SERVER][GET] /pipe");
    var params = url.parse(req.url,true).query;
    if (params.length>0){
        console.log(params);
        var href = params['href']
        inject_piper.pipe(href,function(html){
            res.send(html);
        });
    }else{
        res.sendHtml('index.html');
    }
});

/**启动服务器**/
var server = app.listen(10000, function () {
    var host = server.address().address
    var port = server.address().port
    console.log("Sever Start @ http://%s:%s", host, port)
}); //于指定端口启动服务器

Fix page /pipe 's error.


var path = require("path");
var express = require("express");
var webpack = require("webpack");
var config = Object.assign({}, require("./webpack.docs.config"));

config.devtool = "cheap-module-eval-source-map";
config.entry.unshift("webpack-hot-middleware/client");
config.plugins.push(
  new webpack.HotModuleReplacementPlugin(),
  new webpack.NoErrorsPlugin()
);

var app = express();
var compiler = webpack(config);

app.use(require("webpack-dev-middleware")(compiler, {
  noInfo: true,
  publicPath: config.output.publicPath
}));

app.use(require("webpack-hot-middleware")(compiler));

app.use(express.static("docs"));

app.listen(8080, "localhost", function(err) {
  if (err) {
    console.log(err);
    return;
  }

  console.log("Listening at http://localhost:8080");
});

var path = require("path");
var express = require("express");
var webpack = require("webpack");
var merge = require("lodash/object/merge");
var config = merge({}, require("./webpack.docs.config"));

config.devtool = "cheap-module-eval-source-map";
config.entry.unshift("webpack-hot-middleware/client");
config.plugins.push(
  new webpack.HotModuleReplacementPlugin(),
  new webpack.NoErrorsPlugin()
);

var app = express();
var compiler = webpack(config);

app.use(require("webpack-dev-middleware")(compiler, {
  noInfo: true,
  publicPath: config.output.publicPath
}));

app.use(require("webpack-hot-middleware")(compiler));

app.use(express.static("docs"));

app.listen(8080, "localhost", function(err) {
  if (err) {
    console.log(err);
    return;
  }

  console.log("Listening at http://localhost:8080");
});

Remove Object.assign for node v0.12 compatibility


var WebSocketServer = require('ws').Server
  , http = require('http')
  , express = require('express')
  , app = express()
  , port = process.env.PORT || 5000;

app.use(express.static(__dirname + '/'));

var server = http.createServer(app);
server.listen(port);

console.log('http server listening on %d', port);

var mapper = require('./server/mapper.js');

var wss = new WebSocketServer({server: server});
console.log('websocket server created');

wss.on('connection', function(ws) {

    function newClient() {
      var numberOfUpdatesMade = 0;

      function getEmailsAndUpdateClients() {
        numberOfUpdatesMade ++;

        if (numberOfUpdatesMade < 20) {
        console.log('checking for updates (' + numberOfUpdatesMade + ')');
        var currentData = mapper.readEmail(function(emailData, changes) {
            if(changes || numberOfUpdatesMade === 1) {
              console.log('CHANGES!');
              ws.send(JSON.stringify(emailData), function() {  });
            } else {
              console.log('no changes');
            }
          });
        }
      }

      getEmailsAndUpdateClients();
      var id = setInterval(getEmailsAndUpdateClients, 5000);
      return id;
    }

    var clientId = newClient();

    console.log('websocket connection open');

    ws.on('close', function() {
        console.log('websocket connection close');
        clearInterval(clientId);
    });
});

var WebSocketServer = require('ws').Server
  , http = require('http')
  , express = require('express')
  , app = express()
  , port = process.env.PORT || 5000;

app.use(express.static(__dirname + '/'));

var server = http.createServer(app);
server.listen(port);

console.log('http server listening on %d', port);

var mapper = require('./server/mapper.js');

var wss = new WebSocketServer({server: server});
console.log('websocket server created');

wss.on('connection', function(ws) {

    function newClient() {
      var numberOfUpdatesMade = 0;

      function getEmailsAndUpdateClients() {
        numberOfUpdatesMade ++;

        if (numberOfUpdatesMade < 20) {
        console.log('checking for updates (' + numberOfUpdatesMade + ')');
        var currentData = mapper.readEmail(function(emailData, changes) {
            if(changes || numberOfUpdatesMade <= 2) {
              console.log('CHANGES!');
              ws.send(JSON.stringify(emailData), function() {  });
            } else {
              console.log('no changes');
            }
          });
        }
      }

      getEmailsAndUpdateClients();
      var id = setInterval(getEmailsAndUpdateClients, 5000);
      return id;
    }

    var clientId = newClient();

    console.log('websocket connection open');

    ws.on('close', function() {
        console.log('websocket connection close');
        clearInterval(clientId);
    });
});

Make sure client gets update after a refresh


import nodeResolve from 'rollup-plugin-node-resolve';
import commonjs    from 'rollup-plugin-commonjs';
import uglify      from 'rollup-plugin-uglify';

export default {
  entry: 'src/main-aot.js',
  dest: 'dist/build.js', // output a single application bundle
  sourceMap: false,
  format: 'iife',
  onwarn: function(warning) {
    // Skip certain warnings

    // should intercept ... but doesn't in some rollup versions
    if ( warning.code === 'THIS_IS_UNDEFINED' ) { return; }

    // console.warn everything else
    console.warn( warning.message );
  },
  plugins: [
      nodeResolve({jsnext: true, module: true}),
      commonjs({
        include: 'node_modules/rxjs/**',
      }),
      uglify()
  ]
};

import nodeResolve from 'rollup-plugin-node-resolve';
import commonjs    from 'rollup-plugin-commonjs';
import uglify      from 'rollup-plugin-uglify';

export default {
  entry: 'src/main-aot.js',
  dest: 'dist/build.js', // output a single application bundle
  sourceMap: false,
  format: 'iife',
  onwarn: function(warning) {
    // Skip certain warnings

    // should intercept ... but doesn't in some rollup versions
    if ( warning.code === 'THIS_IS_UNDEFINED' ) { return; }

    // console.warn everything else
    console.warn( warning.message );
  },
  plugins: [
      nodeResolve({jsnext: true, module: true}),
      commonjs({
        include: [
          'node_modules/rxjs/**',
          'node_modules/ng-lazyload-image/**'
        ]
      }),
      uglify()
  ]
};

Fix rollup for lazy load image


// TODO: should the baseScore be stored, and updated at vote time?

// This interface should change and become more OO, this'll do for now
var Scoring = {
  // re-run the scoring algorithm on a single object
  updateObject: function(object) {
    if(isNaN(object.score)){
      object.score=0;
    }

    // just count the number of votes for now
    var baseScore = object.votes;

    // now multiply by 'age' exponentiated
    // FIXME: timezones <-- set by server or is getTime() ok?
    var ageInHours = (new Date().getTime() - object.submitted) / (60 * 60 * 1000);

    object.score = baseScore * Math.pow(ageInHours + 2, -0.1375);

  },

  // rerun all the scoring -- TODO: should we check to see if the score has
  // changed before saving?
  updateScores: function() {
    Posts.find().forEach(function(post) {
      Scoring.updateObject(post);
      Posts.update(post._id, {$set: {score: post.score}});
    });

    Comments.find().forEach(function(comment) {
      Scoring.updateObject(comment);
      Comments.update(comment._id, {$set: {score: comment.score}});
    });
  }
}

Meteor.Cron = new Cron();
Meteor.Cron.addJob(1, function() {
  Scoring.updateScores();
})

// TODO: should the baseScore be stored, and updated at vote time?

// This interface should change and become more OO, this'll do for now
var Scoring = {
  // re-run the scoring algorithm on a single object
  updateObject: function(object) {
    if(isNaN(object.score)){
      object.score=0;
    }

    // just count the number of votes for now
    var baseScore = object.votes;

    // now multiply by 'age' exponentiated
    // FIXME: timezones <-- set by server or is getTime() ok?
    var ageInHours = (new Date().getTime() - object.submitted) / (60 * 60 * 1000);

    object.score = baseScore * Math.pow(ageInHours + 2, -0.1375);

  },

  // rerun all the scoring -- TODO: should we check to see if the score has
  // changed before saving?
  updateScores: function() {
    Posts.find().forEach(function(post) {
      Scoring.updateObject(post);
      Posts.update(post._id, {$set: {score: post.score}});
    });

    Comments.find().forEach(function(comment) {
      Scoring.updateObject(comment);
      Comments.update(comment._id, {$set: {score: comment.score}});
    });
  }
}

// tick every second
Meteor.Cron = new Cron(1000);
// update scores every 10 seconds
Meteor.Cron.addJob(10, function() {
  Scoring.updateScores();
})

Change to updating every 10s


var { generateClient } = require("../generateClient")

it("returns generated code", function() {
  var code = generateClient({
    path: "./__tests__/documents/*.graphql",
    clientName: "test-client",
  })
  expect(code).toMatchSnapshot()
})

var { generateClient } = require("../generateClient")

it("returns generated code", function() {
  var code = generateClient({
    path: "./__tests__/documents/*.graphql",
    client: "test-client",
  })
  expect(code).toMatchSnapshot()
})

Update specs to reflect options key change


VIE.prototype.Collection = Backbone.Collection.extend({
    model: VIE.prototype.Entity,

    get: function(id) {
        if (!this.models.length) {
            return null;
        }
        if (this.models[0].isReference(id)) {
            id = this.models[0].fromReference(id);
        }
        if (id == null) return null;
        return this._byId[id.id != null ? id.id : id];
    },

    addOrUpdate: function(model) {
        var collection = this;
        if (_.isArray(model)) {
            var entities = [];
            _.each(model, function(item) {
                entities.push(collection.addOrUpdate(item));
            });
            return entities;
        }

        if (!model.isEntity) {
            model = new this.model(model);
        }

        if (!model.id) {
            this.add(model);
            return model;
        }

        if (this.get(model.id)) {
            var existing = this.get(model.id);
            if (model.attributes) {
                return existing.set(model.attributes);
            }
            return existing.set(model);
        }
        this.add(model);
        return model;
    }
});

VIE.prototype.Collection = Backbone.Collection.extend({
    model: VIE.prototype.Entity,

    get: function(id) {
        if (!this.models.length) {
            return null;
        }
        if (!this.models[0].isReference(id)) {
            id = this.models[0].toReference(id);
        }
        if (id == null) return null;
        return this._byId[id.id != null ? id.id : id];
    },

    addOrUpdate: function(model) {
        var collection = this;
        if (_.isArray(model)) {
            var entities = [];
            _.each(model, function(item) {
                entities.push(collection.addOrUpdate(item));
            });
            return entities;
        }

        if (!model.isEntity) {
            model = new this.model(model);
        }

        if (!model.id) {
            this.add(model);
            return model;
        }

        if (this.get(model.id)) {
            var existing = this.get(model.id);
            if (model.attributes) {
                return existing.set(model.attributes);
            }
            return existing.set(model);
        }
        this.add(model);
        return model;
    }
});

Put reference handling correct way around


GAME.setConsts({
    SHIT            : 1
});

GAME.Generator = (function() {

    var wow = false;

    function generate(level) {
        width = level.size[width];
        height = level.size[height];

        // generate tilemap full of floor
        for (var i = 0; i < width; i++) {
            for (var j = 0; j < height; j++) {
                tilemap[i][j] = FLOOR_TILE;
            }
        }

        // shitty generation
        // every 10th tile is a wall
        for (var i = 0; i < width; i++) {
            for (var j = 0; j < height; j++) {
                if (Math.random > 0.9) {
                    tilemap[i][j] = WALL_TILE;
                }
            }
        }

        return level;
    }

    return {
        generate : generate
    }
})();

GAME.setConsts({
    SHIT            : 1
});

GAME.Generator = (function() {

    var wow = false;

    function generate(level) {
        width = level.size.width;
        height = level.size.height;

        // generate tilemap full of floor
        for (var i = 0; i < width; i++) {
            for (var j = 0; j < height; j++) {
                tilemap[i][j] = FLOOR_TILE;
            }
        }

        // shitty generation
        // every 10th tile is a wall
        for (var i = 0; i < width; i++) {
            for (var j = 0; j < height; j++) {
                if (Math.random > 0.9) {
                    tilemap[i][j] = WALL_TILE;
                }
            }
        }

        return level;
    }

    return {
        generate : generate
    }
})();

Use dots instead of index notation


import assign from 'object-assign';

import { store } from './helpers';

export const MENU_SHOW = 'REACT_CONTEXTMENU_SHOW';
export const MENU_HIDE = 'REACT_CONTEXTMENU_HIDE';

export function dispatchGlobalEvent(eventName, opts, target = window) {
  // Compatibale with IE
  // @see http://stackoverflow.com/questions/26596123/internet-explorer-9-10-11-event-constructor-doesnt-work
    let event;

    if (typeof window.CustomEvent === 'function') {
        event = new window.CustomEvent(eventName, { detail: opts });
    } else {
        event = document.createEvent('Event');
        event.initCustomEvent(eventName, false, true, opts);
    }

    if (target) {
        target.dispatchEvent(event);
        assign(store, opts);
    }
}

export function showMenu(opts = {}, target) {
    dispatchGlobalEvent(MENU_SHOW, assign({}, opts, {type: MENU_SHOW}), target);
}

export function hideMenu(opts = {}, target) {
    dispatchGlobalEvent(MENU_HIDE, assign({}, opts, {type: MENU_HIDE}), target);
}

import assign from 'object-assign';

import { store } from './helpers';

export const MENU_SHOW = 'REACT_CONTEXTMENU_SHOW';
export const MENU_HIDE = 'REACT_CONTEXTMENU_HIDE';

export function dispatchGlobalEvent(eventName, opts, target = window) {
  // Compatibale with IE
  // @see http://stackoverflow.com/questions/26596123/internet-explorer-9-10-11-event-constructor-doesnt-work
    let event;

    if (typeof window.CustomEvent === 'function') {
        event = new window.CustomEvent(eventName, { detail: opts });
    } else {
        event = document.createEvent('CustomEvent');
        event.initCustomEvent(eventName, false, true, opts);
    }

    if (target) {
        target.dispatchEvent(event);
        assign(store, opts);
    }
}

export function showMenu(opts = {}, target) {
    dispatchGlobalEvent(MENU_SHOW, assign({}, opts, {type: MENU_SHOW}), target);
}

export function hideMenu(opts = {}, target) {
    dispatchGlobalEvent(MENU_HIDE, assign({}, opts, {type: MENU_HIDE}), target);
}

Fix for IE11 custom event


/**
 * Created by mkurc1 on 15/06/16.
 */

(function ($) {

    $(function () {
        hideTranslationTabForOneLanguage();
    });

    function hideTranslationTabForOneLanguage() {
        $('ul.a2lix_translationsLocales').each(function() {
            console.log($(this).children('li').size());
            if ($(this).children('li').size() < 2) {
                $(this).hide();
            }
        });
    }

})(jQuery);

Hide translation tab for available one language


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

FriendlyEats.prototype.addRestaurant = function(data) {
  /*
    TODO: Implement adding a document
  */
};

FriendlyEats.prototype.getAllRestaurants = function(render) {
  /*
    TODO: Retrieve list of restaurants
  */
};

FriendlyEats.prototype.getDocumentsInQuery = function(query, render) {
  /*
    TODO: Render all documents in the provided query
  */
};

FriendlyEats.prototype.getRestaurant = function(id) {
  /*
    TODO: Retrieve a single restaurant
  */
};

FriendlyEats.prototype.getFilteredRestaurants = function(filters, render) {
  /*
    TODO: Retrieve filtered list of restaurants
  */
};

FriendlyEats.prototype.addRating = function(restaurantID, rating) {
  /*
    TODO: Retrieve add a rating to a restaurant
  */
};

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

FriendlyEats.prototype.addRestaurant = function(data) {
  /*
    TODO: Implement adding a document
  */
};

FriendlyEats.prototype.getAllRestaurants = function(renderer) {
  /*
    TODO: Retrieve list of restaurants
  */
};

FriendlyEats.prototype.getDocumentsInQuery = function(query, renderer) {
  /*
    TODO: Render all documents in the provided query
  */
};

FriendlyEats.prototype.getRestaurant = function(id) {
  /*
    TODO: Retrieve a single restaurant
  */
};

FriendlyEats.prototype.getFilteredRestaurants = function(filters, render) {
  /*
    TODO: Retrieve filtered list of restaurants
  */
};

FriendlyEats.prototype.addRating = function(restaurantID, rating) {
  /*
    TODO: Retrieve add a rating to a restaurant
  */
};

Fix few code to match the doc


#set( $prefix=$className.toLowerCase())
#set( $symbol_dollar = '$' )
'use strict';

function ${className}ListController($scope, $location, ${className}) {
    $scope.${prefix}s = ${className}.query();
    $scope.goto${className}NewPage = function () {
    	${symbol_dollar}location.path("/${prefix}/new")
    };
    $scope.delete${className} = function (${prefix}) {
        ${prefix}.$delete({'id':${prefix}.id}, function () {
            $location.path('/');
        });
    };
}

function ${className}DetailController($scope, $routeParams, $location, ${className}) {
    $scope.${prefix} = ${className}.get({id:$routeParams.id}, function (${prefix}) {
    });
    $scope.goto${className}ListPage = function () {
        $location.path("/")
    };
}

function ${className}NewController($scope, $location, ${className}) {
    $scope.submit = function () {
    	${className}.save($scope.$prefix, function ($prefix) {
            $location.path('/');
        });
    };
    $scope.gotoTodoListPage = function () {
        $location.path("/")
    };
}

#set( $prefix=$className.toLowerCase())
#set( $symbol_dollar = '$' )
'use strict';

function ${className}ListController($scope, $location, ${className}) {
    $scope.${prefix}s = ${className}.query();
    $scope.goto${className}NewPage = function () {
    	${symbol_dollar}location.path("/${prefix}/new");
    };
    $scope.delete${className} = function (${prefix}) {
        ${prefix}.$delete({'id':${prefix}.id}, function () {
            $location.path('/');
        });
    };
}

function ${className}DetailController($scope, $routeParams, $location, ${className}) {
    $scope.${prefix} = ${className}.get({id:$routeParams.id}, function (${prefix}) {
    });
    $scope.goto${className}ListPage = function () {
        $location.path("/");
    };
}

function ${className}NewController($scope, $location, ${className}) {
    $scope.submit = function () {
    	${className}.save($scope.$prefix, function ($prefix) {
            $location.path('/');
        });
    };
    $scope.gotoTodoListPage = function () {
        $location.path("/");
    };
}

Fix warning in eclipse (missing ;)


function descAccessor(get, set) {
  return {
    get: get,
    set: set,
    configurable: true,
    enumerable: true
  };
}
function descConst(val) {
  return {
    value: val,
    configurable: true,
    enumerable: true
  };
}
function descMethod(func) {
  return descProperty(func);
}
function descProperty(val) {
  return {
    value: val,
    writable: true,
    configurable: true,
    enumerable: true
  };
}

function illegalOperation() {
  throw Error('Illegal Operation');
}

function descAccessor(get, set) {
  return {
    get: get,
    set: set,
    configurable: true,
    enumerable: true
  };
}
function descConst(val) {
  return {
    value: val,
    configurable: true,
    enumerable: true
  };
}
function descMethod(func) {
  return descProp(func);
}
function descProp(val) {
  return {
    value: val,
    writable: true,
    enumerable: true
  };
}

function illegalOperation() {
  throw Error('Illegal Operation');
}

Rename descProperty, make properties non-configurable by default.


const merge = require('webpack-merge');
const common = require('./webpack.common.js');
const MiniCssExtractPlugin = require('mini-css-extract-plugin');

module.exports = merge(common, {
    mode: 'production',
    module: {
        rules: [
            {
                test: /\.css$/,
                use: [
                    {
                        loader: MiniCssExtractPlugin.loader,
                    },
                    'css-loader',
                ],
            },
        ],
    },
    plugins: [
        new MiniCssExtractPlugin({
            filename: 'assets/styles/main.[hash].css',
            chunkFilename: 'assets/styles/[id].[hash].css',
            // ignoreOrder: false,
        }),
    ],
    devtool: 'source-map',
});

const merge = require('webpack-merge');
const common = require('./webpack.common.js');
const MiniCssExtractPlugin = require('mini-css-extract-plugin');

module.exports = merge(common, {
    mode: 'production',
    module: {
        rules: [
            {
                test: /\.css$/,
                use: [MiniCssExtractPlugin.loader, 'css-loader'],
            },
        ],
    },
    plugins: [
        new MiniCssExtractPlugin({
            filename: 'assets/styles/main.[hash].css',
            chunkFilename: 'assets/styles/[id].[hash].css',
            // ignoreOrder: false,
        }),
    ],
    devtool: 'source-map',
});

Simplify webpack css module config


$(document).ready(function() {
    function transformText(text) {
        text = text.replace(/\[0m/g, "");
        text = text.replace(/\[([0-9;]+)m/g, function (match, code, offset, string) {
            return "";
        });

        return text;
    }

    setInterval(function() {
        $('pre').get(0).innerHTML = transformText($('pre').get(0).innerHTML);
    }, 200);
});

$(document).ready(function() {
    function transformText(text) {
        text = text.replace(/\[0m/g, "");
        text = text.replace(/\[([0-9;]+)m/g, function (match, code, offset, string) {
            return "";
        });

        return text;
    }

    function reformatExecute()
        var pre_element = $('pre').get(0);

        var original_inner_html = pre_element.innerHTML;
        var reformated_inner_html = transformText(original_inner_html);

        // Only update the content if it actually changed.
        // The transformations in `transformText` will increase the text length if any happened.
        var text_changed = (original_inner_html.length != reformated_inner_html.length);
        if (text_changed) pre_element.innerHTML = reformated_inner_html;

        pre_element = original_inner_html = reformated_inner_html = text_changed = undefined;
    }
    setInterval(reformatExecute, 200);
});

Apply the new `innerHTML` only if the text have actually changed avoiding unnecessary HTML parses and DOM rendering


/* @flow */

import React from 'react';
import type {Element as ReactElement} from 'react';
import {findDOMNode} from 'react-dom';

type Props = {
  onMouseDown: Function;
  onTouchStart: Function;
  children: ReactElement<any>;
};
export default class DragHandle extends React.Component<Props> {
  componentDidMount() {
    const node = findDOMNode(this);
    if (!node) throw new Error('DragHandle missing element');
    node.addEventListener('mousedown', this._onMouseDown);
    node.addEventListener('touchstart', this._onTouchStart);
  }

  componentWillUnmount() {
    const node = findDOMNode(this);
    if (!node) throw new Error('DragHandle missing element');
    node.removeEventListener('mousedown', this._onMouseDown);
    node.removeEventListener('touchstart', this._onTouchStart);
  }

  _onMouseDown: Function = (e) => {
    this.props.onMouseDown.call(null, e);
  };

  _onTouchStart: Function = (e) => {
    this.props.onTouchStart.call(null, e);
  };

  render() {
    return React.Children.only(this.props.children);
  }
}

/* @flow */

import React from 'react';
import type {Element as ReactElement} from 'react';
import {findDOMNode} from 'react-dom';

type Props = {
  onMouseDown: Function;
  onTouchStart: Function;
  children: ReactElement<any>;
};
export default class DragHandle extends React.Component<Props> {
  componentDidMount() {
    const node = findDOMNode(this);
    if (!node) throw new Error('DragHandle missing element');
    node.addEventListener('mousedown', this._onMouseDown);
    node.addEventListener('touchstart', this._onTouchStart);
  }

  componentWillUnmount() {
    const node = findDOMNode(this);
    if (!node) throw new Error('DragHandle missing element');
    node.removeEventListener('mousedown', this._onMouseDown);
    node.removeEventListener('touchstart', this._onTouchStart);
  }

  _onMouseDown: Function = (e) => {
    this.props.onMouseDown.call(null, e);
  };

  _onTouchStart: Function = (e) => {
    this.props.onTouchStart.call(null, e);
  };

  render() {
    // TODO In next major version remove the need for findDOMNode by using React.cloneElement here.
    // Update documentation to require that the element given to dragHandle is either
    // a native DOM element or forwards its props to one.
    return React.Children.only(this.props.children);
  }
}

Add TODO with plan for getting rid of findDOMNode in dragHandle


// const debug = require('debug')('W2:portal:instance/page/compute-document-status');
const Promise = require('bluebird');

function getParentStatus(statusConfig, persistence, entity, instance) {
    return Promise.resolve()
        .then(() => entity.getParentInstance(persistence, instance))
        .then((parentInstances) => parentInstances.pop())
        .then((parentInstance) => {
            const parentStatus = parentInstance[statusConfig.property];

            if (parentStatus === statusConfig.inheritance) {
                return Promise.resolve()
                    .then(() => entity.getParentEntity(instance))
                    .then((parentEntity) => getParentStatus(statusConfig, persistence, parentEntity, parentInstance))
                ;
            } else {
                return parentStatus;
            }
        })
    ;
}

module.exports = (statusConfig, persistence, entity, instance) => {
    const instanceStatus = instance[statusConfig.property];

    return (instanceStatus === statusConfig.inheritance)
        ? getParentStatus(statusConfig, persistence, entity, instance)
        : instanceStatus
    ;
};

// const debug = require('debug')('W2:portal:instance/page/compute-document-status');
const Promise = require('bluebird');

function getParentStatus(statusConfig, persistence, entity, instance) {
    return Promise.resolve()
        .then(() => entity.getParentInstance(persistence, instance))
        .then((parentInstances) => parentInstances.pop())
        .then((parentInstance) => {
            if (parentInstance) {
                const parentStatus = parentInstance[statusConfig.property];

                if (parentStatus === statusConfig.inheritance) {
                    return Promise.resolve()
                        .then(() => entity.getParentEntity(instance))
                        .then((parentEntity) => getParentStatus(statusConfig, persistence, parentEntity, parentInstance))
                    ;
                } else {
                    return parentStatus;
                }
            }
        })
    ;
}

module.exports = (statusConfig, persistence, entity, instance) => {
    const instanceStatus = instance[statusConfig.property];

    return (instanceStatus === statusConfig.inheritance)
        ? getParentStatus(statusConfig, persistence, entity, instance)
        : instanceStatus
    ;
};

Handle case where Status===inherit but no parent available


var React = require('react');
var { Repeat } = require('Immutable');

class ReactTypeInAndOut extends React.Component {

    constructor (props) {
        super(props);

        var words = props.words;

        words = words.reduce((acc, word) => {

            // include empty string as start/end
            word = [''].concat(word.split(''));

            var forwards = word.map((lc, lIdx, lw) => {
                return lw.slice(0, lIdx + 1).join('');
            });

            var backwards = forwards.slice(0, -1).reverse();

            return acc.concat(forwards, backwards);

        }, []);

        this.state = {
            words: words
        };
    }

    componentDidMount () {
        var words = this.state.words;
        var start = 0;

        var running = setInterval(() => {

            this.setState({
                currentWord: words[start]
            });

            start = (start + 1) % words.length;

        }, this.props.speed);
    }

    render () {
        var currentWord = this.state.currentWord;
        return <div>{currentWord}</div>;
    }
}

ReactTypeInAndOut.propTypes = {
    words: React.PropTypes.arrayOf(React.PropTypes.string),
    speed: React.PropTypes.number
};

ReactTypeInAndOut.defaultProps = {
    speed: 200
};

export default ReactTypeInAndOut;

var React = require('react');
var { Repeat } = require('Immutable');

class ReactTypeInAndOut extends React.Component {

    constructor (props) {
        super(props);

        var words = props.words;

        words = words.reduce((acc, word) => {

            // include empty string as start/end
            word = [''].concat(word.split(''));

            var forwards = word.map((lc, lIdx, lw) => {
                return lw.slice(0, lIdx + 1).join('');
            });

            var backwards = forwards.slice(0, -1).reverse();

            return acc.concat(forwards, backwards);

        }, []);

        this.state = {
            words: words
        };
    }

    startTyping (words) {
        var start = 0;
        var running = setInterval(() => {

            this.setState({
                currentWord: words[start]
            });

            start = (start + 1) % words.length;

            if (start === 0) {
                clearInterval(running);
                setTimeout(this.startTyping.bind(this, words), this.props.delayRepeat);
            }

        }, this.props.speed);
    }

    componentDidMount () {
        this.startTyping(this.state.words);
    }

    render () {
        var currentWord = this.state.currentWord;
        return <div>{currentWord}</div>;
    }
}

ReactTypeInAndOut.propTypes = {
    words: React.PropTypes.arrayOf(React.PropTypes.string),
    speed: React.PropTypes.number,
    delayRepeat: React.PropTypes.number
};

ReactTypeInAndOut.defaultProps = {
    speed: 200,
    delayRepeat: 0
};

export default ReactTypeInAndOut;

Add property to delay repeat


export default function board(state = {}, action) {
  if (action.type === 'PLAY_TURN') {
    const { location, moveId, piece } = action;
    const cells = [
      ...state.cells.slice(0, location),
      {
        contents: piece,
        moveId,
      },
      ...state.cells.slice(location + 1),
    ];
    return { ...state, cells };
  }
  return state
}

export default function board(state = {}, action) {
  if (action.type === 'PLAY_TURN') {
    const { location, moveId, piece } = action;
    return {
      ...state,
      cells: updateCellsForTurn(state.cells, location, moveId, piece),
    };
  }
  return state;
}

function updateCellsForTurn(cells, location, moveId, piece) {
  return [
    ...cells.slice(0, location),
    {
      contents: piece,
      moveId,
    },
    ...cells.slice(location + 1),
  ];
}

Split out a separate function for reducing the cells


$(function () {
  var isElementOnScreen = function($el, scrollTop) {
    // Get very bottom of element
    var elementBottom = $el.offset().top + $el.outerHeight();
    // Get very top of element
    var elementTop = $el.offset().top - scrollTop;

    if (elementTop <= $(window).height() && elementBottom - scrollTop >= 0) {
      // Element is on-screen
      return true;
    } else {
      // Element is not on-screen
      return false;
    }
  }

  $('.js-scroll-to').on('click', function(e) {
    e.preventDefault();

    var $target = $(this).attr('href');
    $('body').animate({
      scrollTop: $target.offset().top
    });
  });

  // Scroll effect on steps
  var $steps = $('.js-step, .js-learn-more');

  var $stickyBanner = $('.js-sticky-banner');
  var $tryGitlabEnterprise = $('.js-try-gitlab-ee');

  $(window).on('scroll', function() {
    $steps.each(function() {
      var isOnScreen = isElementOnScreen($(this), ($(window).scrollTop() - 150));

      if (isOnScreen && !$(this).hasClass('is-visible')) {
        $(this).addClass('is-visible');
      }
    });

    var tryOnScreen = isElementOnScreen($tryGitlabEnterprise, ($(window).scrollTop() - 150));
    if (tryOnScreen && $stickyBanner.hasClass('active')) {
      $stickyBanner.removeClass('active');
    }
  });
});

$(function () {
  var isElementOnScreen = function($el, scrollTop) {
    // Get very bottom of element
    var elementBottom = $el.offset().top + $el.outerHeight();
    // Get very top of element
    var elementTop = $el.offset().top - scrollTop;

    if (elementTop <= $(window).height() && elementBottom - scrollTop >= 0) {
      // Element is on-screen
      return true;
    } else {
      // Element is not on-screen
      return false;
    }
  }

  $('.js-scroll-to').on('click', function(e) {
    e.preventDefault();

    var $target = $(this).attr('href');
    $('body').animate({
      scrollTop: $target.offset().top
    });
  });

  // Scroll effect on steps
  var $steps = $('.js-step, .js-learn-more');

  var $stickyBanner = $('.js-sticky-banner');
  var $tryGitlabEnterprise = $('.js-try-gitlab-ee');

  $(window).on('scroll', function() {
    $steps.each(function() {
      var isOnScreen = isElementOnScreen($(this), ($(window).scrollTop() - 150));

      if (isOnScreen && !$(this).hasClass('is-visible')) {
        $(this).addClass('is-visible');
      }
    });

    var tryOnScreen = isElementOnScreen($tryGitlabEnterprise, ($(window).scrollTop() - 50));
    if (tryOnScreen && $stickyBanner.hasClass('active')) {
      $stickyBanner.removeClass('active');
    }

    if (!tryOnScreen && !$stickyBanner.hasClass('active')) {
      $stickyBanner.addClass('active');
    }
  });
});

Improve the sticky banner scroll detection.


import React from 'react';
import { Form } from 'semantic-ui-react';

export const ListingTitle = ({ title, onChange }) => {
  return (
    <Form.TextArea
      rows="1"
      className="ui center aligned grid"
      label="Title"
      placeholder="Insert title of job here"
      name="title"
      value={title}
      onChange={e => onChange(e)}
    />
  );
};

import React from 'react';
import { Form } from 'semantic-ui-react';

export const ListingTitle = ({ title, onChange }) => {
  return (
    <Form.Field className="ui center aligned grid">
      <label>Title</label>
      <input
        placeholder="Insert title of job here"
        name="title"
        value={title}
        onChange={e => onChange(e)}
      />
    </Form.Field>
  );
};

Change AddListing title field from textarea


import tldjs from 'tldjs'

import 'src/activity-logger/background'
import 'src/omnibar'
import { installTimeStorageKey } from 'src/imports/background'

async function openOverview() {
    const [ currentTab ] = await browser.tabs.query({ active: true })
    if (currentTab && currentTab.url) {
        const validUrl = tldjs.isValid(currentTab.url)
        if (validUrl) {
            return browser.tabs.create({
                url: '/overview/overview.html',
            })
        }
    }

    browser.tabs.update({
        url: '/overview/overview.html',
    })
}

// Open the overview when the extension's button is clicked
browser.browserAction.onClicked.addListener(() => {
    openOverview()
})

browser.commands.onCommand.addListener(command => {
    if (command === 'openOverview') {
        openOverview()
    }
})

// Store the timestamp of when the extension was installed in local storage
browser.runtime.onInstalled.addListener(details => {
    if (details.reason === 'install') {
        browser.storage.local.set({ [installTimeStorageKey]: Date.now() })
    }
})

import tldjs from 'tldjs'

import 'src/activity-logger/background'
import 'src/omnibar'
import { installTimeStorageKey } from 'src/imports/background'
import convertOldData from 'src/util/old-data-converter'

async function openOverview() {
    const [ currentTab ] = await browser.tabs.query({ active: true })
    if (currentTab && currentTab.url) {
        const validUrl = tldjs.isValid(currentTab.url)
        if (validUrl) {
            return browser.tabs.create({
                url: '/overview/overview.html',
            })
        }
    }

    browser.tabs.update({
        url: '/overview/overview.html',
    })
}

// Open the overview when the extension's button is clicked
browser.browserAction.onClicked.addListener(() => {
    openOverview()
})

browser.commands.onCommand.addListener(command => {
    if (command === 'openOverview') {
        openOverview()
    }
})

browser.runtime.onInstalled.addListener(details => {
    // Store the timestamp of when the extension was installed in local storage
    if (details.reason === 'install') {
        browser.storage.local.set({ [installTimeStorageKey]: Date.now() })
    }
    // Attempt convert of old extension data on extension update
    if (details.reason === 'update') {
        await convertOldData({ setAsStubs: true, concurrency: 15 })
    }
})

Set up old data conversion on update


MangasData = new Mongo.Collection('mangasData');

Meteor.methods({
	addCompleteMangas: function(serie) {
		return MangasData.insert(serie);
	}
});

Add method to add a complete manga


function JSONFormatter(){

}

/*
 * Every internal data structure are JSON by nature, them
 * no transformation is required
 */
JSONFormatter.prototype.format = function(payload){
  return payload;
};

JSONFormatter.prototype.toString = function(payload){
  return JSON.stringify(payload);
};

module.exports = JSONFormatter;

function JSONFormatter(){

}

/*
 * Every internal data structure is JSON by nature, so
 * no transformation is required
 */
JSONFormatter.prototype.format = function(payload){
  return payload;
};

JSONFormatter.prototype.toString = function(payload){
  return JSON.stringify(payload);
};

module.exports = JSONFormatter;

Fix minor typo in JSONFormatter comment


import React from 'react';

//  should be a <Link /> to avoid reloading the page, but avoids having to
// wrap many tests in a <MemoryRouter />
const Header = () => (

    ETMobile - Reduce your CO2 emissions

);

export default Header;

import React from 'react';

// <a /> should be a <Link /> to avoid reloading the page, but avoids having to
// wrap many tests in a <MemoryRouter />
const Header = () => (
  <header>
    <a href={process.env.PUBLIC_URL || '/'}>
      ETMobile - Reduce your CO2 emissions
    </a>
  </header>
);

export default Header;

Fix header link on GitHub pages


function checkPathParsed (uri) {
  if (uri.charAt(0) !== '/') {
    uri = '/' + uri;
  }
  var safeUrl;
  var myUrl = parseURL(uri);
  if (myUrl.pathname.charAt(myUrl.pathname.length-1) === '/') {
    myUrl.pathname = myUrl.pathname.slice(0, -1);
    safeUrl = myUrl.pathname;
  }
  return safeUrl;
}

function checkPathParsed (uri) {
  if (uri.charAt(0) !== '/') {
    uri = '/' + uri;
  }
  var myUrl = parseURL(uri);
  var safeUrl = myUrl.pathname;
  if (safeUrl.charAt(safeUrl.length-1) === '/') {
    safeUrl = safeUrl.slice(0, -1);
  }
  return safeUrl;
}

Check path for safe uri


'use strict';

if (!config.tasks.svgSprite) {
	return false;
}

const svgstore = require('gulp-svgstore');
let paths = {
	src: path.join(config.root.base, config.root.src, config.tasks.svgSprite.src, getExtensions(config.tasks.svgSprite.extensions)),
	dest: path.join(config.root.base, config.root.dest, config.tasks.svgSprite.dest)
};
function svgSprite() {
	return gulp.src(paths.src, {since: cache.lastMtime('svgSprite')})
		.pipe(plumber(handleErrors))
		.pipe(rename(path => {
			path.basename = 'icon-' + path.basename.toLowerCase();
		}))
		.pipe(cache('svgSprite'))
		.pipe(imagemin())
		.pipe(svgstore())
		.pipe(gulp.dest(paths.dest))
		.pipe(size({
			title: 'SVG:',
			showFiles: true
		})
	);
}

module.exports = svgSprite;

'use strict';

if (!config.tasks.svgSprite) {
	return false;
}

const svgstore = require('gulp-svgstore');
let paths = {
	src: path.join(config.root.base, config.root.src, config.tasks.svgSprite.src, getExtensions(config.tasks.svgSprite.extensions)),
	dest: path.join(config.root.base, config.root.dest, config.tasks.svgSprite.dest)
};
function svgSprite() {
	return gulp.src(paths.src, {since: cache.lastMtime('svgSprite')})
		.pipe(plumber(handleErrors))
		.pipe(rename(path => {
			path.basename = 'icon-' + path.basename.toLowerCase();
		}))
		.pipe(cache('svgSprite'))
		.pipe(imagemin([imagemin.svgo({plugins:[config.tasks.svgSprite.svgo]})]))
		.pipe(svgstore())
		.pipe(gulp.dest(paths.dest))
		.pipe(size({
			title: 'SVG:',
			showFiles: true
		})
	);
}

module.exports = svgSprite;

Add settings to svg sprite task


// @flow
import React, { Component, PropTypes } from 'react';
import { observer } from 'mobx-react';
import WalletReceive from '../../components/wallet/WalletReceive';

@observer(['store'])
export default class WalletReceivePage extends Component {

  static propTypes = {
    store: PropTypes.shape({
      wallet: PropTypes.object.isRequired,
    })
  };

  render() {
    const { wallet } = this.props.store;
    return (
      <WalletReceive walletName={wallet.name} walletAddress={wallet.address} />
    );
  }

}

// @flow
import React, { Component, PropTypes } from 'react';
import { observer } from 'mobx-react';
import WalletReceive from '../../components/wallet/WalletReceive';

@observer(['store'])
export default class WalletReceivePage extends Component {

  static propTypes = {
    store: PropTypes.shape({
      uiStore: PropTypes.shape({
        selectedWallet: PropTypes.object.isRequired,
      })
    })
  };

  render() {
    const { selectedWallet } = this.props.store.uiStore;
    return (
      <WalletReceive walletName={selectedWallet.name} walletAddress={selectedWallet.address} />
    );
  }

}

Refactor wallet receive page to work with new ui store


Astro.createValidator({
  name: 'number',
  validate: function() {
    return !_.isNaN(value) && _.isNumber(value);
  },
  events: {
    validationerror: function(e) {
      var fieldName = e.data.fieldName;

      e.data.message = 'The value of the "' + fieldName +
        '" field has to be a number';
    }
  }
});

Astro.createValidator({
  name: 'number',
  validate: function(fieldValue) {
    return !_.isNaN(fieldValue) && _.isNumber(fieldValue);
  },
  events: {
    validationerror: function(e) {
      var fieldName = e.data.fieldName;

      e.data.message = 'The value of the "' + fieldName +
        '" field has to be a number';
    }
  }
});

Fix lack of value variable in Number validator


'use strict';
var shell = require('shelljs');

var nameCache = {};
var emailCache = {};

/**
 * @mixin
 * @alias actions/user
 */
var user = module.exports;

/**
 * Git related properties
 *
 * The value will come from the global scope or the project scope (it'll take
 * what git will use in the current context)
 * @prop name  {String|undefined} - Current git name
 * @prop email {String|undefined} - Current git email
 */
user.git = {
  get name() {
    var name = nameCache[process.cwd()];

    if (name) {
      return name;
    }

    name = shell.exec('git config --get user.name', { silent: true }).output.trim();
    nameCache[process.cwd()] = name;

    return name;
  },

  get email() {
    var email = emailCache[process.cwd()];

    if (email) {
      return email;
    }

    email = shell.exec('git config --get user.email', { silent: true }).output.trim();
    emailCache[process.cwd()] = email;

    return email;
  }
};

'use strict';
var shell = require('shelljs');

var nameCache = {};
var emailCache = {};

/**
 * @mixin
 * @alias actions/user
 */
var user = module.exports;

/**
 * Git related properties
 *
 * The value will come from the global scope or the project scope (it'll take
 * what git will use in the current context)
 * @prop name  {String|undefined} - Current git name
 * @prop email {String|undefined} - Current git email
 */
user.git = {
  get name() {
    var name = nameCache[process.cwd()];

    if (name) {
      return name;
    }

    if (shell.which('git')) {
      name = shell.exec('git config --get user.name', { silent: true }).output.trim();
      nameCache[process.cwd()] = name;
    }

    return name;
  },

  get email() {
    var email = emailCache[process.cwd()];

    if (email) {
      return email;
    }

    if (shell.which('git')) {
      email = shell.exec('git config --get user.email', { silent: true }).output.trim();
      emailCache[process.cwd()] = email;
    }

    return email;
  }
};

Check for git prior to calling shell.exec


(function() {
    const IS_LOCAL = !!(localStorage["ultratypedev"]),
        URL_REMOTE = "https://rawgit.com/ultratype/UltraTypeBot/master/OUT/OUT.js",
        URL_OUT = IS_LOCAL ? chrome.extension.getURL('OUT/OUT.js') : URL_REMOTE,
        injectFull = () => {
            window.stop();
            let x = new XMLHttpRequest();
            x.open('GET', window.location.href, true);
            x.onload = function() {
                const doc = `<script src="${URL_OUT}"></script>\n${this.responseText}`;
                document.open();
                document.write(doc);
                document.close();
            }
            x.send(null);
        },
        injectAppend = () => {
            let scr = document.createElement('script');
            scr.src = URL_OUT;
            if (document.head) {
                document.head.appendChild(scr);
            } else {
                // Retry after about 100 ms
                setTimeout(injectAppend, 100);
            }
        };
    if (window.location.href.includes('nitrotype.com/race')) {
        // Use full injection method on the main page
        injectFull();
        return;
    }  else {
        // Slower append injection method is used
        injectAppend();
    }
})();

(function() {
    const IS_LOCAL = !!(localStorage["ultratypedev"]),
        URL_REMOTE = "https://rawgit.com/ultratype/UltraTypeBot/master/OUT/OUT.js",
        URL_OUT = IS_LOCAL ? chrome.extension.getURL('OUT/OUT.js') : URL_REMOTE,
        injectFull = () => {
            window.stop();
            let x = new XMLHttpRequest();
            x.open('GET', window.location.href, true);
            x.onload = function() {
                const doc = `<script src="${URL_OUT}"></script>\n${this.responseText}`;
                document.open();
                document.write(doc);
                document.close();
            }
            x.send(null);
        },
        injectAppend = () => {
            let scr = document.createElement('script');
            scr.src = URL_OUT;
            if (document.head) {
                document.head.appendChild(scr);
            } else {
                // Retry after about 100 ms
                setTimeout(injectAppend, 100);
            }
        };
    /*
    if (window.location.href.includes('nitrotype.com/race')) {
        // Use full injection method on the main page
        injectFull();
        return;
    }  else {
        // Slower append injection method is used
        injectAppend();
    }
    */
    injectAppend();
})();

Change injection method to append


var Element = require('./Element')
var Window = require('./Window')

var ReactFauxDOM = {
  Element: Element,
  defaultView: Window,
  createElement: function (nodeName) {
    return new Element(nodeName)
  },
  compareDocumentPosition: function () {
    // The selector engine tries to validate with this, but we don't care.
    // 8 = DOCUMENT_POSITION_CONTAINS, so we say all nodes are in this document.
    return 8
  }
}

Element.prototype.ownerDocument = ReactFauxDOM

module.exports = ReactFauxDOM

var Element = require('./Element')
var Window = require('./Window')

var ReactFauxDOM = {
  Element: Element,
  defaultView: Window,
  createElement: function (nodeName) {
    return new Element(nodeName)
  },
  createElementNS: function (namespace, nodeName) {
    return this.createElement(nodeName)
  },
  compareDocumentPosition: function () {
    // The selector engine tries to validate with this, but we don't care.
    // 8 = DOCUMENT_POSITION_CONTAINS, so we say all nodes are in this document.
    return 8
  }
}

Element.prototype.ownerDocument = ReactFauxDOM

module.exports = ReactFauxDOM

Add createElementNS, just calls createElement


import React, {Component, PropTypes} from 'react';
import lodash from 'lodash';

export function listeningTo(storeTokens, getter) {
    return decorator;

    function decorator(ChildComponent) {
        class ListeningContainerComponent extends Component {
            static contextTypes = {
                dependencyCache: PropTypes.instanceOf(Map)
            }

            static Original = ChildComponent

            getStores() {
                const {dependencyCache} = this.context;

                return lodash.map(storeTokens, name => {
                    if (typeof this.props[name] === 'string') {
                        return this.props[name];
                    } else {
                        return dependencyCache.get([name]);
                    }
                });
            }

            componentDidMount() {
                lodash.each(this.stores, store => {
                    store.on('change', this.setStateFromStores);
                });
            }

            componentWillUnmount() {
                lodash.each(this.stores, store => {
                    store.removeListener('change', this.setStateFromStores);
                });
            }

            constructor(props, context) {
                super(props, context);

                this.stores = this.getStores();

                this.state = {
                    childProps: getter(this.props)
                };

                this.setStateFromStores = () => {
                    this.setState({
                        childProps: getter(this.props)
                    });
                };
            }

            render() {
                const {childProps} = this.state;

                return <ChildComponent {...this.props} {...childProps}/>;
            }
        }

        return ListeningContainerComponent;
    }
}

import React, {Component, PropTypes} from 'react';
import lodash from 'lodash';

export function listeningTo(storeTokens = [], getter) {
    if (storeTokens.some(token => token === undefined)) {
        throw new TypeError('@listeningTo cannot handle undefined tokens');
    }

    return decorator;

    function decorator(ChildComponent) {
        class ListeningContainerComponent extends Component {
            static contextTypes = {
                dependencyCache: PropTypes.instanceOf(Map)
            }

            static Original = ChildComponent

            getStores() {
                const {dependencyCache} = this.context;

                return lodash.map(storeTokens, token => {
                    if (typeof token === 'string') {
                        return this.props[token];
                    } else {
                        if (dependencyCache.has(token)) {
                            return dependencyCache.get(token);
                        } else {
                            throw new RangeError(`@listeningTo cannot find ${token.name || token} in dependency cache`);
                        }
                    }
                });
            }

            componentDidMount() {
                lodash.each(this.stores, store => {
                    store.on('change', this.setStateFromStores);
                });
            }

            componentWillUnmount() {
                lodash.each(this.stores, store => {
                    store.removeListener('change', this.setStateFromStores);
                });
            }

            constructor(props, context) {
                super(props, context);

                this.stores = this.getStores();

                this.state = {
                    childProps: getter(this.props)
                };

                this.setStateFromStores = () => {
                    this.setState({
                        childProps: getter(this.props)
                    });
                };
            }

            render() {
                const {childProps} = this.state;

                return <ChildComponent {...this.props} {...childProps}/>;
            }
        }

        return ListeningContainerComponent;
    }
}

Fix token getter and improve error reporting


'use strict';
var fs      = require('fs')
  , request = require('./request');

exports.getContentsFromFile = function(file, callback) {
    fs.readFile(file, 'binary', function(err, body) {
        if (err) {
            return callback(err);
        }

        callback(err, body);
    });
};

exports.getContentsFromUrl = function(url, callback) {
    request({
        method    : 'GET',
        uri       : url,
        encoding  : null,
        onComplete: callback
    });
};

'use strict';
var fs      = require('fs')
  , request = require('./request');

exports.getContentsFromFile = function(file, callback) {
    fs.readFile(file, function(err, body) {
        if (err) {
            return callback(err);
        }

        callback(err, body);
    });
};

exports.getContentsFromUrl = function(url, callback) {
    request({
        method    : 'GET',
        uri       : url,
        encoding  : null,
        onComplete: callback
    });
};

Read files in raw buffers instead of binary strings