<commit_before>/**
 * @author       Joachim Grill <joachim@codeandweb.com>
 * @copyright    2018 CodeAndWeb GmbH
 * @license      {@link https://github.com/photonstorm/phaser/blob/master/license.txt|MIT License}
 */

var Bodies = require('./lib/factory/Bodies');
var Body = require('./lib/body/Body');
var Bounds = require('./lib/geometry/Bounds');
var Common = require('./lib/core/Common');
var GetFastValue = require('../../utils/object/GetFastValue');
var Vector = require('./lib/geometry/Vector');
var Vertices = require('./lib/geometry/Vertices');

/**
 * Use PhysicsEditorParser.parseBody() to build a Matter body object, based on a physics data file
 * created and exported with PhysicsEditor (https://www.codeandweb.com/physicseditor).
 *
 * @name Phaser.Physics.Matter.PhysicsEditorParser
 * @since 3.10.0
 */
var PhysicsEditorParser = {

    /**
     * Parses a body element exported by PhysicsEditor.
     *
     * @function Phaser.Physics.Matter.PhysicsEditorParser.parseBody
     * @since 3.10.0
     *
     * @param {number} x - x position.
     * @param {number} y - y position.
     * @param {number} w - width.
     * @param {number} h - height.
     * @param {object} config - body configuration and fixture (child body) definitions.
     *
     * @return {object} A matter body, consisting of several parts (child bodies)
     */
    parseBody: function (x, y, w, h, config)
    {
        var fixtureConfigs = GetFastValue(config, 'fixtures', []);
        var fixtures = [];

        for (var fc = 0; fc < fixtureConfigs.length; fc++)
        {
            var fixtureParts = this.parseFixture(fixtureConfigs[fc]);

            for (var i = 0; i < fixtureParts.length; i++)
            {
                fixtures.push(fixtureParts[i]);
            }
        }

        var matterConfig = Common.extend({}, false, config);

        delete matterConfig.fixtures;
        delete matterConfig.type;

        var body = Body.create(matterConfig);

        Body.setParts(body, fixtures);
        body.render.sprite.xOffset = body.position.x / w;
        body.render.sprite.yOffset = body.position.y / h;
        Body.setPosition(body, { x: x, y: y });

        return body;
    },

    /**
     * Parses an element of the "fixtures" list exported by PhysicsEditor
     *
     * @function Phaser.Physics.Matter.PhysicsEditorParser.parseFixture
     * @since 3.10.0
     *
     * @param {object} fixtureConfig - the fixture object to parse
     *
     * @return {object[]} - A list of matter bodies
     */
    parseFixture: function (fixtureConfig)
    {
        var matterConfig = Common.extend({}, false, fixtureConfig);

        delete matterConfig.circle;
        delete matterConfig.vertices;

        var fixtures;

        if (fixtureConfig.circle)
        {
            var x = GetFastValue(fixtureConfig.circle, 'x');
            var y = GetFastValue(fixtureConfig.circle, 'y');
            var r = GetFastValue(fixtureConfig.circle, 'radius');
            fixtures = [ Bodies.circle(x, y, r, matterConfig) ];
        }
        else if (fixtureConfig.vertices)
        {
            fixtures = this.parseVertices(fixtureConfig.vertices, matterConfig);
        }

        return fixtures;
    },

    /**
     * Parses the "vertices" lists exported by PhysicsEditor.
     *
     * @function Phaser.Physics.Matter.PhysicsEditorParser.parseVertices
     * @since 3.10.0
     *
     * @param {object} vertexSets - The vertex lists to parse.
     * @param {object} options - Matter body options.
     *
     * @return {object[]} - A list of matter bodies.
     */
    parseVertices: function (vertexSets, options)
    {
        var i, j, k, v, z;
        var parts = [];

        options = options || {};

        for (v = 0; v < vertexSets.length; v += 1)
        {
            parts.push(Body.create(Common.extend({
                position: Vertices.centre(vertexSets[v]),
                vertices: vertexSets[v]
            }, options)));
        }

        // flag coincident part edges
        var coincidentMaxDist = 5;

        for (i = 0; i < parts.length; i++)
        {
            var partA = parts[i];

            for (j = i + 1; j < parts.length; j++)
            {
                var partB = parts[j];

                if (Bounds.overlaps(partA.bounds, partB.bounds))
                {
                    var pav = partA.vertices,
                        pbv = partB.vertices;

                    // iterate vertices of both parts
                    for (k = 0; k < partA.vertices.length; k++)
                    {
                        for (z = 0; z < partB.vertices.length; z++)
                        {
                            // find distances between the vertices
                            var da = Vector.magnitudeSquared(Vector.sub(pav[(k + 1) % pav.length], pbv[z])),
                                db = Vector.magnitudeSquared(Vector.sub(pav[k], pbv[(z + 1) % pbv.length]));

                            // if both vertices are very close, consider the edge concident (internal)
                            if (da < coincidentMaxDist && db < coincidentMaxDist)
                            {
                                pav[k].isInternal = true;
                                pbv[z].isInternal = true;
                            }
                        }
                    }

                }
            }
        }

        return parts;
    }
};

module.exports = PhysicsEditorParser;
<commit_msg>Fixed namespace<commit_after>/**
 * @author       Joachim Grill <joachim@codeandweb.com>
 * @copyright    2018 CodeAndWeb GmbH
 * @license      {@link https://github.com/photonstorm/phaser/blob/master/license.txt|MIT License}
 */

var Bodies = require('./lib/factory/Bodies');
var Body = require('./lib/body/Body');
var Bounds = require('./lib/geometry/Bounds');
var Common = require('./lib/core/Common');
var GetFastValue = require('../../utils/object/GetFastValue');
var Vector = require('./lib/geometry/Vector');
var Vertices = require('./lib/geometry/Vertices');

/**
 * Use PhysicsEditorParser.parseBody() to build a Matter body object, based on a physics data file
 * created and exported with PhysicsEditor (https://www.codeandweb.com/physicseditor).
 *
 * @namespace Phaser.Physics.Matter.PhysicsEditorParser
 * @since 3.10.0
 */
var PhysicsEditorParser = {

    /**
     * Parses a body element exported by PhysicsEditor.
     *
     * @function Phaser.Physics.Matter.PhysicsEditorParser.parseBody
     * @since 3.10.0
     *
     * @param {number} x - x position.
     * @param {number} y - y position.
     * @param {number} w - width.
     * @param {number} h - height.
     * @param {object} config - body configuration and fixture (child body) definitions.
     *
     * @return {object} A matter body, consisting of several parts (child bodies)
     */
    parseBody: function (x, y, w, h, config)
    {
        var fixtureConfigs = GetFastValue(config, 'fixtures', []);
        var fixtures = [];

        for (var fc = 0; fc < fixtureConfigs.length; fc++)
        {
            var fixtureParts = this.parseFixture(fixtureConfigs[fc]);

            for (var i = 0; i < fixtureParts.length; i++)
            {
                fixtures.push(fixtureParts[i]);
            }
        }

        var matterConfig = Common.extend({}, false, config);

        delete matterConfig.fixtures;
        delete matterConfig.type;

        var body = Body.create(matterConfig);

        Body.setParts(body, fixtures);
        body.render.sprite.xOffset = body.position.x / w;
        body.render.sprite.yOffset = body.position.y / h;
        Body.setPosition(body, { x: x, y: y });

        return body;
    },

    /**
     * Parses an element of the "fixtures" list exported by PhysicsEditor
     *
     * @function Phaser.Physics.Matter.PhysicsEditorParser.parseFixture
     * @since 3.10.0
     *
     * @param {object} fixtureConfig - the fixture object to parse
     *
     * @return {object[]} - A list of matter bodies
     */
    parseFixture: function (fixtureConfig)
    {
        var matterConfig = Common.extend({}, false, fixtureConfig);

        delete matterConfig.circle;
        delete matterConfig.vertices;

        var fixtures;

        if (fixtureConfig.circle)
        {
            var x = GetFastValue(fixtureConfig.circle, 'x');
            var y = GetFastValue(fixtureConfig.circle, 'y');
            var r = GetFastValue(fixtureConfig.circle, 'radius');
            fixtures = [ Bodies.circle(x, y, r, matterConfig) ];
        }
        else if (fixtureConfig.vertices)
        {
            fixtures = this.parseVertices(fixtureConfig.vertices, matterConfig);
        }

        return fixtures;
    },

    /**
     * Parses the "vertices" lists exported by PhysicsEditor.
     *
     * @function Phaser.Physics.Matter.PhysicsEditorParser.parseVertices
     * @since 3.10.0
     *
     * @param {object} vertexSets - The vertex lists to parse.
     * @param {object} options - Matter body options.
     *
     * @return {object[]} - A list of matter bodies.
     */
    parseVertices: function (vertexSets, options)
    {
        var i, j, k, v, z;
        var parts = [];

        options = options || {};

        for (v = 0; v < vertexSets.length; v += 1)
        {
            parts.push(Body.create(Common.extend({
                position: Vertices.centre(vertexSets[v]),
                vertices: vertexSets[v]
            }, options)));
        }

        // flag coincident part edges
        var coincidentMaxDist = 5;

        for (i = 0; i < parts.length; i++)
        {
            var partA = parts[i];

            for (j = i + 1; j < parts.length; j++)
            {
                var partB = parts[j];

                if (Bounds.overlaps(partA.bounds, partB.bounds))
                {
                    var pav = partA.vertices,
                        pbv = partB.vertices;

                    // iterate vertices of both parts
                    for (k = 0; k < partA.vertices.length; k++)
                    {
                        for (z = 0; z < partB.vertices.length; z++)
                        {
                            // find distances between the vertices
                            var da = Vector.magnitudeSquared(Vector.sub(pav[(k + 1) % pav.length], pbv[z])),
                                db = Vector.magnitudeSquared(Vector.sub(pav[k], pbv[(z + 1) % pbv.length]));

                            // if both vertices are very close, consider the edge concident (internal)
                            if (da < coincidentMaxDist && db < coincidentMaxDist)
                            {
                                pav[k].isInternal = true;
                                pbv[z].isInternal = true;
                            }
                        }
                    }

                }
            }
        }

        return parts;
    }
};

module.exports = PhysicsEditorParser;
<|endoftext|>


<commit_before>// Emote data
import EmoteSet from "./emoteset";

import { WebpackModules } from "../internals";

// Shim for handling builtin Discord emoji (including custom ones)
// Does not need to be handled for updating or rendering
//
// Though we could just grab the full list and use our searching functionality,
// the set of actually usable emoji varies depending on the current channel,
// permissions, and whether the user has Nitro. Plus, any of these may change
// at any moment. Instead, we can make use of Discord's internal search method,
// which takes all of this into account already.
export const discordEmoji = new EmoteSet({
    label: "Discord emoji",
    template: "{0}",
    emoteStyle: EmoteSet.emoteStyle.STANDARD,
    loader() {
        return new Promise((resolve, reject) => {
            // FIXME: handle lazy loading of webpack modules properly
            window.setTimeout(() => {
                // Collect needed modules from webpack
                const Emoji = WebpackModules.findByUniqueProperties(["getGuildEmoji", "search"]);
                const Channels = WebpackModules.findByUniqueProperties(["getChannel", "getChannels"]);
                const Selected = WebpackModules.findByUniqueProperties(["getChannelId", "getVoiceChannelId"]);

                // Bail if any of these are unavailable
                if (!(Emoji && Channels && Selected)) {
                    reject(new Error("unable to find webpack modules"));
                    return;
                }

                // Set up some helper functions
                const getCurrentChannel = () => Channels.getChannel(Selected.getChannelId());
                const emojiSearch = q => Emoji.search(getCurrentChannel(), q);

                const emoteMap = new Map();

                // Methods to override default EmoteSet behavior
                const search = query => {
                    const score = name => {
                        const d = name.length - query.length;
                        const i = name.indexOf(query);
                        if (i < 0) {
                            return Infinity;
                        }
                        return 1 + (i+1)*(d-i+2);
                    };
                    const emojiScore = emoji => {
                        if (emoji.names) {
                            return Math.min(...emoji.names.map(score));
                        }
                        return score(emoji.name);
                    };
                    const res = emojiSearch(query);
                    res.forEach(emoji => {
                        emoteMap.set(emoji.name, new EmoteSet.Emote({
                            name: emoji.name,
                            path: emoji.url,
                            emoteSet: this,
                        }));
                    });

                    return res.map(e => [e.name, emojiScore(e)]);
                };

                resolve({emoteMap, search});
            }, 1000);
        });
    },
});
<commit_msg>Add missing fields<commit_after>// Emote data
import EmoteSet from "./emoteset";

import { WebpackModules } from "../internals";

// Shim for handling builtin Discord emoji (including custom ones)
// Does not need to be handled for updating or rendering
//
// Though we could just grab the full list and use our searching functionality,
// the set of actually usable emoji varies depending on the current channel,
// permissions, and whether the user has Nitro. Plus, any of these may change
// at any moment. Instead, we can make use of Discord's internal search method,
// which takes all of this into account already.
export const discordEmoji = new EmoteSet({
    label: "Discord emoji",
    template: "{0}",
    emoteStyle: EmoteSet.emoteStyle.STANDARD,
    loader() {
        return new Promise((resolve, reject) => {
            // FIXME: handle lazy loading of webpack modules properly
            window.setTimeout(() => {
                // Collect needed modules from webpack
                const Emoji = WebpackModules.findByUniqueProperties(["getGuildEmoji", "search"]);
                const Channels = WebpackModules.findByUniqueProperties(["getChannel", "getChannels"]);
                const Selected = WebpackModules.findByUniqueProperties(["getChannelId", "getVoiceChannelId"]);

                // Bail if any of these are unavailable
                if (!(Emoji && Channels && Selected)) {
                    reject(new Error("unable to find webpack modules"));
                    return;
                }

                // Set up some helper functions
                const getCurrentChannel = () => Channels.getChannel(Selected.getChannelId());
                const emojiSearch = q => Emoji.search(getCurrentChannel(), q);

                const emoteMap = new Map();

                // Methods to override default EmoteSet behavior
                const search = query => {
                    const score = name => {
                        const d = name.length - query.length;
                        const i = name.indexOf(query);
                        if (i < 0) {
                            return Infinity;
                        }
                        return 1 + (i+1)*(d-i+2);
                    };
                    const emojiScore = emoji => {
                        if (emoji.names) {
                            return Math.min(...emoji.names.map(score));
                        }
                        return score(emoji.name);
                    };
                    const res = emojiSearch(query);
                    res.forEach(emoji => {
                        emoteMap.set(emoji.name, new EmoteSet.Emote({
                            name: emoji.name,
                            path: emoji.url,
                            emoteSet: this,
                        }));
                    });

                    return res.map(e => [e.name, emojiScore(e)]);
                };

                resolve({emoteMap, search, loaded: 0, skipped: 0});
            }, 1000);
        });
    },
});
<|endoftext|>


<commit_before>test('Dragster',

  [
    'ephox.dragster.api.Dragster'
  ],

  function(Dragster) {

  }
);
<commit_msg>removed atomic tests.<commit_after><|endoftext|>


<commit_before>/***
|''Name''|TiddlySpaceTiddlerIconsPlugin|
|''Version''|0.21|
|''Status''|beta|
|''Description''|Provides ability to render SiteIcons and icons that correspond to the home location of given tiddlers|
|''Requires''|TiddlySpaceConfig ImageMacroPlugin|
|''Source''||
!Notes
Provides an additional SiteIcon view for use with view macro
{{{<<view modifier SiteIcon>>}}}
will show the SiteIcon located in the space with the same name as modifier.
It also works if the attribute given ends with _private or _public (so {{{<<view server.bag SiteIcon>>}}} is usable).

{{{<<tiddlerOrigin>>}}} shows the origin of the tiddler it is being run on.
In TiddlySpace terms this means it will determine whether the tiddler is external, public or private.
Where private it will analyse whether a public version exists and distinguish between the different scenarios.
If a tiddler is external, the SiteIcon of that external space will be shown

When a ViewTemplate contains an element with class concertina, clicking on the icon outputted by the tiddlerOrigin macro
will reveal more detailed information on what the icon means.
!Parameters
both take the same parameters
width / height : define a width or height of the outputted icon
label: if label parameter is set to yes, a label will accompany the icon.

!!additional view parameters
labelPrefix / labelSuffix : prefix or suffix the label with additional text. eg. labelPrefix:'modified by '
!Code
***/
//{{{
(function($) {

if(!config.macros.image) {
	throw "Missing dependency: ImageMacroPlugin";
}

var imageMacro = config.macros.image;
var tweb = config.extensions.tiddlyweb;
var tiddlyspace = config.extensions.tiddlyspace;

config.macros.view.views.SiteIcon = function(value, place, params, wikifier,
		paramString, tiddler) {
	var publicSuffix = value.indexOf("_public");
	var privateSuffix = value.indexOf("_private");
	if(publicSuffix == value.length - 7) {
		value = value.substr(0, publicSuffix);
	} else if(privateSuffix == value.length - 8) {
		value = value.substr(0, privateSuffix);
	}
	if(!store.tiddlerExists(tiddler.title) || value == "None") { // some core tiddlers lack modifier
		value = false;
	}
	var args = paramString.parseParams("name", null, true, false, true)[0];
	var labelPrefix = args.labelPrefix ? args.labelPrefix[0] : "";
	var labelSuffix = args.labelSuffix ? args.labelSuffix[0] : "";
	var imageOptions = imageMacro.getArguments(paramString, []);
	tweb.getStatus(function(status) {
		var container = $('
');
		var uri = tiddlyspace.getAvatar(status.server_host, {name: value});
		var extraArgs = params.splice(2, params.length - 2).join(" ");
		var imageOptions = imageMacro.getArguments(extraArgs, []);
		imageMacro.renderImage(container, uri, imageOptions);
		if(!value) {
			value = "tiddlyspace";
		}
		$('
').
			text("%0%1%2".format([labelPrefix, value, labelSuffix])).
			appendTo(container);
		$(container).prependTo(place);
		$(container).attr("title", value).attr("alt", value);
	});
};

var originMacro = config.macros.tiddlerOrigin = {
	locale: {
		"private": "private",
		"public": "public",
		"privateAndPublic": "public and private tiddler",
		"privateNotPublic": "new draft of public",
		"external": "from %0",
		"external_info": "This tiddler was written by %0 in the %1 space. \n It is visible to all at:\n%2",
		"private_info": "This tiddler is currently private.\n It is visible to only members of this space at:\n%2",
		"public_info": "This tiddler is currently public with no private revision.\nIt is visible to all at:\n%2",
		"privateAndPublic_info": "This tiddler is currently public without any later private revisions.\nIt is visible to all at:\n%2",
		"privateNotPublic_info": "This tiddler is currently public, with a later private revision.\nIt is visible to all at:\n%2\nbut the content will differ depending on permissions.",
		"unknownUser": "an unknown user"
	},

	handler: function(place, macroName, params,wikifier, paramString, tiddler){
		var adaptor = tiddler.getAdaptor();
		var imageOptions = imageMacro.getArguments(paramString, []);
		var locale = originMacro.locale;
		var type = "private";
		var parsedParams = paramString.parseParams("name")[0];
		var includeLabel = parsedParams.label && parsedParams.label[0] == "yes";
		if(store.tiddlerExists(tiddler.title) || store.isShadowTiddler(tiddler.title)) {
			var space = tiddlyspace.determineSpace(tiddler, true);
			type = space && space.name == tiddlyspace.currentSpace.name ? space.type : "external";

			var tidEl = story.findContainingTiddler(place);
			var concertina = $(".concertina", tidEl)[0];
			var concertinaButton = $('').
				click(function() {
					if($(tidEl).hasClass("concertinaOn")) {
						$(tidEl).removeClass("concertinaOn");
						$(concertina).hide(500);
					} else {
						$(tidEl).addClass("concertinaOn");
						$(concertina).show(500, function() {
							$(concertina).attr("style", "display:block;"); // jQuery uses the filter for opacity
						});
					}
				}).appendTo(place)[0];

			var label;
			var showLabel = function(type) {
				$(tidEl).
					removeClass("private public external privateAndPublic privateNotPublic").
					addClass(type);
				if(includeLabel) {
					$('
').text(label).appendTo(concertinaButton);
				}
				$(concertinaButton).prependTo(place).attr("title", label);
			};

			if(type != "external") {
				var showPrivacyRoundel = function(context) {
					if(context && context.status) { // there is a public tiddler as well as the current tiddler!
						// to do: not this is not enough.. we also need to check if the public tiddler is the same as..
						// .. the private tiddler to determine whether this is a draft
						// use of hashes would be useful here.
						if(context.tiddler.modified < tiddler.modified) { // public is older so therefore this is a draft
							label = locale.privateNotPublic;
							type = "privateNotPublic";
						} else {
							label = locale.privateAndPublic;
							type = "privateAndPublic";
						}
					}
					imageMacro.renderImage(concertinaButton, "%0Icon".format([type]), imageOptions);
					showLabel(type);
					originMacro.fillConcertina(concertina, type, tiddler);
				};
				if(type == "private") { //check for a public version
					var context = {
						workspace: "bags/%0_public".format([space.name])
					};
					label = locale["private"];
					adaptor.getTiddler(tiddler.title, context, null, showPrivacyRoundel);
				} else {
					label = locale["public"];
					showPrivacyRoundel();
				}
			} else {
				label = locale.external.format([space.name || "tiddlyspace"]);
				tweb.getStatus(function(status) {
					var uri = tiddlyspace.getAvatar(status.server_host, space);
					imageMacro.renderImage(concertinaButton, uri, imageOptions);
					showLabel(type);
					originMacro.fillConcertina(concertina, type, tiddler);
				});
			}
		}
	},
	fillConcertina: function(place, privacyType, tiddler) {
		if(!place) {
			return;
		} else {
			var locale = originMacro.locale;
			var space = tiddlyspace.determineSpace(tiddler);
			space = space.name ? space.name : false;
			tweb.getStatus(function(status) {
				var modifier = tiddler.modifier;
				if(modifier == "None") {
					modifier = locale.unknownUser;
				}
				var spaceLink, link;
				if(!space) {
					space = "core";
					link = "[[/%0|/%0]]".format([tiddler.title]);
				} else {
					spaceLink = tiddlyspace.getHost(status.server_host, space);
					space = "[[%0|%1]]".format([space, spaceLink]);
					link = "[[%0/%1]]".format([spaceLink, tiddler.title]);
				}

				var localeString = locale["%0_info".format([privacyType])];
				if(localeString){
					wikify(localeString.format([modifier, space, link]), place);
				}
			});
		}
	}
};

})(jQuery);
//}}}
<commit_msg>fixed various issues in TiddlySpaceTiddlerIconsPlugin<commit_after>/***
|''Name''|TiddlySpaceTiddlerIconsPlugin|
|''Version''|0.21|
|''Status''|beta|
|''Description''|Provides ability to render SiteIcons and icons that correspond to the home location of given tiddlers|
|''Requires''|TiddlySpaceConfig ImageMacroPlugin|
|''Source''||
!Notes
Provides an additional SiteIcon view for use with view macro
{{{<<view modifier SiteIcon>>}}}
will show the SiteIcon located in the space with the same name as modifier.
It also works if the attribute given ends with _private or _public (so {{{<<view server.bag SiteIcon>>}}} is usable).

{{{<<tiddlerOrigin>>}}} shows the origin of the tiddler it is being run on.
In TiddlySpace terms this means it will determine whether the tiddler is external, public or private.
Where private it will analyse whether a public version exists and distinguish between the different scenarios.
If a tiddler is external, the SiteIcon of that external space will be shown

When a ViewTemplate contains an element with class concertina, clicking on the icon outputted by the tiddlerOrigin macro
will reveal more detailed information on what the icon means.
!Parameters
both take the same parameters
width / height : define a width or height of the outputted icon
label: if label parameter is set to yes, a label will accompany the icon.

!!additional view parameters
labelPrefix / labelSuffix : prefix or suffix the label with additional text. eg. labelPrefix:'modified by '
!Code
***/
//{{{
(function($) {

if(!config.macros.image) {
	throw "Missing dependency: ImageMacroPlugin";
}

var imageMacro = config.macros.image;
var tweb = config.extensions.tiddlyweb;
var tiddlyspace = config.extensions.tiddlyspace;

config.macros.view.views.SiteIcon = function(value, place, params, wikifier,
		paramString, tiddler) {
	var pos;
	if(tweb.endsWith(value, "_public")) {
		pos = value.indexOf("_public");
		value = value.substr(0, pos);
	} else if(tweb.endsWith(value, "_private")) {
		pos = value.indexOf("_private");
		value = value.substr(0, pos);
	}
	if(!store.tiddlerExists(tiddler.title) || value == "None") { // some core tiddlers lack modifier
		value = false;
	}
	var args = paramString.parseParams("name", null, true, false, true)[0];
	var labelPrefix = args.labelPrefix ? args.labelPrefix[0] : "";
	var labelSuffix = args.labelSuffix ? args.labelSuffix[0] : "";
	var imageOptions = imageMacro.getArguments(paramString, []);
	tweb.getStatus(function(status) {
		var container = $('
');
		var uri = tiddlyspace.getAvatar(status.server_host, { name: value });
		var extraArgs = params.splice(2, params.length - 2).join(" ");
		var imageOptions = imageMacro.getArguments(extraArgs, []);
		imageMacro.renderImage(container, uri, imageOptions);
		if(!value) {
			value = "tiddlyspace";
		}
		$('
').
			text("%0%1%2".format([labelPrefix, value, labelSuffix])).
			appendTo(container);
		container.attr("title", value).attr("alt", value).prependTo(place);
	});
};

var originMacro = config.macros.tiddlerOrigin = {
	locale: {
		"private": "private",
		"public": "public",
		"privateAndPublic": "public and private tiddler",
		"privateNotPublic": "new draft of public",
		"external": "from %0",
		"external_info": "This tiddler was written by %0 in the %1 space. \n It is visible to all at:\n%2",
		"private_info": "This tiddler is currently private.\n It is visible to only members of this space at:\n%2",
		"public_info": "This tiddler is currently public with no private revision.\nIt is visible to all at:\n%2",
		"privateAndPublic_info": "This tiddler is currently public without any later private revisions.\nIt is visible to all at:\n%2",
		"privateNotPublic_info": "This tiddler is currently public, with a later private revision.\nIt is visible to all at:\n%2\nbut the content will differ depending on permissions.",
		"unknownUser": "an unknown user"
	},

	handler: function(place, macroName, params,wikifier, paramString, tiddler){
		var adaptor = tiddler.getAdaptor();
		var imageOptions = imageMacro.getArguments(paramString, []);
		var locale = originMacro.locale;
		var type = "private";
		var parsedParams = paramString.parseParams("name")[0];
		var includeLabel = parsedParams.label && parsedParams.label[0] == "yes";
		if(store.tiddlerExists(tiddler.title) || store.isShadowTiddler(tiddler.title)) {
			var space = tiddlyspace.determineSpace(tiddler, true);
			type = space && space.name == tiddlyspace.currentSpace.name ? space.type : "external";

			var tidEl = story.findContainingTiddler(place);
			tidEl = $(tidEl);
			var concertina = $(".concertina", tidEl)[0];
			var concertinaButton = $('').
				click(function() {
					if(tidEl.hasClass("concertinaOn")) {
						tidEl.removeClass("concertinaOn");
						concertina.hide(500);
					} else {
						tidEl.addClass("concertinaOn");
						concertina.show(500, function() {
							concertina.attr("style", "display: block;"); // jQuery uses the filter for opacity
						});
					}
				}).appendTo(place);

			var label;
			var showLabel = function(type) {
				tidEl.
					removeClass("private public external privateAndPublic privateNotPublic").
					addClass(type);
				if(includeLabel) {
					$('
').text(label).appendTo(concertinaButton);
				}
				concertinaButton.prependTo(place).attr("title", label);
			};

			if(type != "external") {
				var showPrivacyRoundel = function(context) {
					if(context && context.status) { // there is a public tiddler as well as the current tiddler!
						// to do: not this is not enough.. we also need to check if the public tiddler is the same as..
						// .. the private tiddler to determine whether this is a draft
						// use of hashes would be useful here.
						if(context.tiddler.modified < tiddler.modified) { // public is older so therefore this is a draft
							label = locale.privateNotPublic;
							type = "privateNotPublic";
						} else {
							label = locale.privateAndPublic;
							type = "privateAndPublic";
						}
					}
					imageMacro.renderImage(concertinaButton[0], "%0Icon".format([type]), imageOptions);
					showLabel(type);
					originMacro.fillConcertina(concertina, type, tiddler);
				};
				if(type == "private") { //check for a public version
					var context = {
						workspace: "bags/%0_public".format([space.name])
					};
					label = locale["private"];
					adaptor.getTiddler(tiddler.title, context, null, showPrivacyRoundel);
				} else {
					label = locale["public"];
					showPrivacyRoundel();
				}
			} else {
				label = locale.external.format([space.name || "tiddlyspace"]);
				tweb.getStatus(function(status) {
					var uri = tiddlyspace.getAvatar(status.server_host, space);
					imageMacro.renderImage(concertinaButton[0], uri, imageOptions);
					showLabel(type);
					originMacro.fillConcertina(concertina, type, tiddler);
				});
			}
		}
	},
	fillConcertina: function(place, privacyType, tiddler) {
		if(!place) {
			return;
		} else {
			var locale = originMacro.locale;
			var space = tiddlyspace.determineSpace(tiddler);
			space = space.name ? space.name : false;
			tweb.getStatus(function(status) {
				var modifier = tiddler.modifier;
				if(modifier == "None") {
					modifier = locale.unknownUser;
				}
				var spaceLink, link;
				if(!space) {
					space = "core";
					link = "[[/%0|/%0]]".format([tiddler.title]);
				} else {
					spaceLink = tiddlyspace.getHost(status.server_host, space);
					space = "[[%0|%1]]".format([space, spaceLink]);
					link = "[[%0/%1]]".format([spaceLink, tiddler.title]);
				}

				var localeString = locale["%0_info".format([privacyType])];
				if(localeString){
					wikify(localeString.format([modifier, space, link]), place);
				}
			});
		}
	}
};

})(jQuery);
//}}}
<|endoftext|>


<commit_before>(function (Q, $, window, document, undefined) {

/**
 * Places Tools
 * @module Places-tools
 */

var Users = Q.Users;
var Streams = Q.Streams;
var Places = Q.Places;

/**
 * Allows the logged-in user to select/add areas to locations
 * @class Places areas
 * @constructor
 * @param {Object} options used to pass options
 * @param {String} options.publisherId Location stream publisher id
 * @param {String} options.streamName Location stream name
 * @param {String} options.stream Location stream
 * @param {Object} options.location Location object
 */
Q.Tool.define("Places/areas", function (options) {
	var tool = this;
	var state = this.state;
	var $te = $(tool.element);

	Q.addStylesheet('Q/plugins/Places/css/areas.css');

	Q.Text.get('Places/content', function (err, text) {
		state.text = text;

		// get location stream and run refresh method
		tool.refresh();
	});

},

{ // default options here
	publisherId: null,
	streamName: null,
	location: null,
	stream: null
},

{ // methods go here
	/**
	 * Refresh the display
	 * @method refresh
	 */
	refresh: function () {
		var tool = this;
		var state = tool.state;
		var $te = $(tool.element);
		var qFilterTool = tool.$(".Q_tool.Q_filter_tool")[0];
		qFilterTool = qFilterTool ? Q.Tool.from(qFilterTool, "Q/filter") : null;

		// if Q/filter didn't created - create one
		if (!qFilterTool) {
			$te.tool('Q/filter', {
				placeholder: state.text.areas.filterPlaceholder
			}, 'Q_filter')
			.appendTo(tool.element)
			.activate(function(){
				qFilterTool = Q.Tool.from(this.element, "Q/filter");

				qFilterTool.state.onFilter.set(function (query, element) {
					var titles = qFilterTool.$(".Streams_related_tool .Streams_preview_tool").not(".Streams_preview_composer");

					titles.each(function(){
						var $this = $(this);

						// if title start with query string - show Streams/preview tool element, and hide otherwise
						if ($(".Streams_preview_title", $this).text().toUpperCase().startsWith(query.toUpperCase())) {
							$this.show();
						} else {
							$this.hide();
						}
					});
				}, tool);
			});
		}

		var relatedTool = Q.Tool.from(tool.$(".Q_filter_results"), "Streams/related");
		tool.getStream(function(stream){
			// if related tool already exist - set new stream and refresh
			if (relatedTool) {
				relatedTool.state.publisherId = stream.fields.publisherId;
				relatedTool.state.streamName = stream.fields.name;
				relatedTool.refresh();

				return;
			}

			// apply Streams/related tool exactly to Q/filter results element
			tool.$(".Q_filter_results").tool("Streams/related", {
				publisherId: stream.fields.publisherId,
				streamName: stream.fields.name,
				relationType: 'area',
				isCategory: true,
				editable: false,
				onRefresh: function(){
					// add Q_filter_result class to each preview tool except composer
					$(".Streams_preview_container", $(".Streams_preview_tool", this.element).not(".Streams_preview_composer")).addClass("Q_filter_result");
				},
				creatable: {
					"Places/area": {
						'title': state.text.areas.newArea,
						'preprocess': function(_proceed){
							var previewTool = this;
							var title = qFilterTool.$input.val();

							tool.prompt(title, relatedTool, _proceed);
						}
					}
				}
			}).activate(function(){
				relatedTool = this;
			});
		});
	},
	/**
	 * Get area selected
	 * @method getValue
	 * @return {string} selected value
	 */
	getValue: function() {
		return this.$(".Q_filter_input").val();
	},
	/**
	 * Show Q.prompt to add new area
	 * @method prompt
	 * @param {string} [title] default area name
	 * @param {Q.Tool} relatedTool Streams/related tool with areas related to Places/location stream
	 * @param {function} _proceed Callback from Streams/related tool to create new stream
	 */
	prompt: function(title, relatedTool, _proceed){
		var tool = this;
		var state = this.state;

		title = title || "";

		var $prompt = Q.prompt(state.text.areas.promptTitle, function (title, dialog) {
			// user click cancel button
			if (title === null) {
				return false;
			}

			// title required
			if (!title) {
				Q.alert(state.text.areas.absent, {
					title: state.text.areas.error,
					onClose: function(){
						tool.prompt(title, relatedTool, _proceed);
					}
				});
				return false;
			}

			// get array of areas exist
			var areasExist = relatedTool.$(".Streams_preview_title").map(function(){
				return $.trim($(this).text());
			}).get();

			// if title already exist
			if ($.inArray(title, areasExist) >= 0) {
				Q.alert(state.text.areas.exist, {
					title: state.text.areas.error,
					onClose: function(){
						tool.prompt(title, relatedTool, _proceed);
					}
				});
				return false;
			}

			Q.handle(_proceed, this, [{title: title}]);

			// wait when new preview tool created with this title and add class Q_filter_result
			var timerId = setInterval(function(){
				var container = relatedTool.$(".Streams_preview_container .Streams_preview_title:contains('"+title+"')");

				if(!container.length){
					return;
				}

				clearInterval(timerId);

				container.closest(".Streams_preview_container").addClass("Q_filter_result")
			}, 500);
		},
		{
			title: state.text.areas.addNewArea,
			ok: state.text.areas.add
		});

		// set default value
		$("input[type=text]", $prompt).val(title);
	},
	/**
	 * Get location stream and launch callback with this stream as argument
	 * @method getStream
	 * @param {Function} [callback] callback
	 */
	getStream: function(callback){
		var state = this.state;

		// set communityId as publisherId if last empty
		state.publisherId = state.publisherId || Q.info.appId;

		if (!state.stream && (state.publisherId && state.streamName)) {
			state.stream = {
				publisherId: state.publisherId,
				name: state.streamName,
				stripped: true
			};
		}

		var location = state.location;

		// required publisherId and streamName OR location
		if (!state.stream && !location) {
			throw new Exception("Places/areas: required publisherId and streamName OR location");
		}

		if (state.stream && state.stream.stripped) { // stripped stream means that it have only publisherId and name
			Q.Streams.get(state.stream.publisherId, state.stream.name, function () {
				Q.handle(callback, this, [this]);
			});
		} else if(state.stream) { // we have pure stream
			Q.handle(callback, state.stream, [state.stream]);
		} else if(location) { // we have just location object and need to check whether stream exist
			// check if we already have location with lat, lng and use one
			// if no - create Places/location stream
			Q.req("Places/areas", 'data', function (err, response) {
				var msg;
				if (msg = Q.firstErrorMessage(err, response && response.errors)) {
					console.warn("Places/areas: " + msg);
					return false;
				}

				var stream = response.slots.data;
				Q.Streams.get(stream.publisherId, stream.name, function () {
					Q.handle(callback, this, [this]);
				});
			}, {
				method: 'GET',
				fields: {
					location: {
						latitude: location.latitude,
						longitude: location.longitude,
						venue: location.venue,
						placeId: location.placeId
					}
				}
			});
		}
	}
});

})(Q, jQuery, window, document);<commit_msg>make Q/filter input element non-editable because choice is possible only Places/area stream, which are listed in drop down<commit_after>(function (Q, $, window, document, undefined) {

/**
 * Places Tools
 * @module Places-tools
 */

var Users = Q.Users;
var Streams = Q.Streams;
var Places = Q.Places;

/**
 * Allows the logged-in user to select/add areas to locations
 * @class Places areas
 * @constructor
 * @param {Object} options used to pass options
 * @param {String} options.publisherId Location stream publisher id
 * @param {String} options.streamName Location stream name
 * @param {String} options.stream Location stream
 * @param {Object} options.location Location object
 */
Q.Tool.define("Places/areas", function (options) {
	var tool = this;
	var state = this.state;
	var $te = $(tool.element);

	Q.addStylesheet('Q/plugins/Places/css/areas.css');

	Q.Text.get('Places/content', function (err, text) {
		state.text = text;

		// get location stream and run refresh method
		tool.refresh();
	});

},

{ // default options here
	publisherId: null,
	streamName: null,
	location: null,
	stream: null
},

{ // methods go here
	/**
	 * Refresh the display
	 * @method refresh
	 */
	refresh: function () {
		var tool = this;
		var state = tool.state;
		var $te = $(tool.element);
		var qFilterTool = tool.$(".Q_tool.Q_filter_tool")[0];
		qFilterTool = qFilterTool ? Q.Tool.from(qFilterTool, "Q/filter") : null;

		// if Q/filter didn't created - create one
		if (!qFilterTool) {
			$te.tool('Q/filter', {
				placeholder: state.text.areas.filterPlaceholder
			}, 'Q_filter')
			.appendTo(tool.element)
			.activate(function(){
				qFilterTool = Q.Tool.from(this.element, "Q/filter");

				// make input non-editable because choice is possible
				// only Places/area stream, which are listed in drop down
				qFilterTool.$(".Q_filter_input").prop("readonly", true);

				qFilterTool.state.onFilter.set(function (query, element) {
					var titles = qFilterTool.$(".Streams_related_tool .Streams_preview_tool").not(".Streams_preview_composer");

					titles.each(function(){
						var $this = $(this);

						// if title start with query string - show Streams/preview tool element, and hide otherwise
						if ($(".Streams_preview_title", $this).text().toUpperCase().startsWith(query.toUpperCase())) {
							$this.show();
						} else {
							$this.hide();
						}
					});
				}, tool);
			});
		}

		var relatedTool = Q.Tool.from(tool.$(".Q_filter_results"), "Streams/related");
		tool.getStream(function(stream){
			// if related tool already exist - set new stream and refresh
			if (relatedTool) {
				relatedTool.state.publisherId = stream.fields.publisherId;
				relatedTool.state.streamName = stream.fields.name;
				relatedTool.refresh();

				return;
			}

			// apply Streams/related tool exactly to Q/filter results element
			tool.$(".Q_filter_results").tool("Streams/related", {
				publisherId: stream.fields.publisherId,
				streamName: stream.fields.name,
				relationType: 'area',
				isCategory: true,
				editable: false,
				onRefresh: function(){
					// add Q_filter_result class to each preview tool except composer
					$(".Streams_preview_container", $(".Streams_preview_tool", this.element).not(".Streams_preview_composer")).addClass("Q_filter_result");
				},
				creatable: {
					"Places/area": {
						'title': state.text.areas.newArea,
						'preprocess': function(_proceed){
							var previewTool = this;
							var title = qFilterTool.$input.val();

							tool.prompt(title, relatedTool, _proceed);
						}
					}
				}
			}).activate(function(){
				relatedTool = this;
			});
		});
	},
	/**
	 * Get area selected
	 * @method getValue
	 * @return {string} selected value
	 */
	getValue: function() {
		return this.$(".Q_filter_input").val();
	},
	/**
	 * Show Q.prompt to add new area
	 * @method prompt
	 * @param {string} [title] default area name
	 * @param {Q.Tool} relatedTool Streams/related tool with areas related to Places/location stream
	 * @param {function} _proceed Callback from Streams/related tool to create new stream
	 */
	prompt: function(title, relatedTool, _proceed){
		var tool = this;
		var state = this.state;

		title = title || "";

		var $prompt = Q.prompt(state.text.areas.promptTitle, function (title, dialog) {
			// user click cancel button
			if (title === null) {
				return false;
			}

			// title required
			if (!title) {
				Q.alert(state.text.areas.absent, {
					title: state.text.areas.error,
					onClose: function(){
						tool.prompt(title, relatedTool, _proceed);
					}
				});
				return false;
			}

			// get array of areas exist
			var areasExist = relatedTool.$(".Streams_preview_title").map(function(){
				return $.trim($(this).text());
			}).get();

			// if title already exist
			if ($.inArray(title, areasExist) >= 0) {
				Q.alert(state.text.areas.exist, {
					title: state.text.areas.error,
					onClose: function(){
						tool.prompt(title, relatedTool, _proceed);
					}
				});
				return false;
			}

			Q.handle(_proceed, this, [{title: title}]);

			// wait when new preview tool created with this title and add class Q_filter_result
			var timerId = setInterval(function(){
				var container = relatedTool.$(".Streams_preview_container .Streams_preview_title:contains('"+title+"')");

				if(!container.length){
					return;
				}

				clearInterval(timerId);

				container.closest(".Streams_preview_container").addClass("Q_filter_result")
			}, 500);
		},
		{
			title: state.text.areas.addNewArea,
			ok: state.text.areas.add
		});

		// set default value
		$("input[type=text]", $prompt).val(title);
	},
	/**
	 * Get location stream and launch callback with this stream as argument
	 * @method getStream
	 * @param {Function} [callback] callback
	 */
	getStream: function(callback){
		var state = this.state;

		// set communityId as publisherId if last empty
		state.publisherId = state.publisherId || Q.info.appId;

		if (!state.stream && (state.publisherId && state.streamName)) {
			state.stream = {
				publisherId: state.publisherId,
				name: state.streamName,
				stripped: true
			};
		}

		var location = state.location;

		// required publisherId and streamName OR location
		if (!state.stream && !location) {
			throw new Exception("Places/areas: required publisherId and streamName OR location");
		}

		if (state.stream && state.stream.stripped) { // stripped stream means that it have only publisherId and name
			Q.Streams.get(state.stream.publisherId, state.stream.name, function () {
				Q.handle(callback, this, [this]);
			});
		} else if(state.stream) { // we have pure stream
			Q.handle(callback, state.stream, [state.stream]);
		} else if(location) { // we have just location object and need to check whether stream exist
			// check if we already have location with lat, lng and use one
			// if no - create Places/location stream
			Q.req("Places/areas", 'data', function (err, response) {
				var msg;
				if (msg = Q.firstErrorMessage(err, response && response.errors)) {
					console.warn("Places/areas: " + msg);
					return false;
				}

				var stream = response.slots.data;
				Q.Streams.get(stream.publisherId, stream.name, function () {
					Q.handle(callback, this, [this]);
				});
			}, {
				method: 'GET',
				fields: {
					location: {
						latitude: location.latitude,
						longitude: location.longitude,
						venue: location.venue,
						placeId: location.placeId
					}
				}
			});
		}
	}
});

})(Q, jQuery, window, document);<|endoftext|>


<commit_before>'use strict';

Darwinator.Enemy = function(game, target, x, y, health, strength, agility, intellect) {
  if (strength > intellect && strength > agility) {
    this.category = this.categories.STRONG;
  } else if (agility > intellect && agility > strength) {
    this.category = this.categories.AGILE;
  } else if (intellect > strength && intellect > agility) {
    this.category = this.categories.INTELLIGENT;
  } else {
    this.category = this.categories.DEFAULT;
  }
  Darwinator.Entity.call(this, game, x, y, this.category,/* [],*/ health, strength, agility, intellect);

  this.scale.setTo(0.25,0.25);
  this.target = target;
  this.path   = [];
  this.debug  = true;

  //Allow enemy to overlap objects, i.e. reduce the hitbox
  //this.body.setRectangle(20*4, 16*4, 0, 16*4);
  this.lastPathUpdate = 0;

  // score properties to measure success
  this.damageDone = 0;

  // melee cooldown
  this.lastMeleeTimestamp = 0;
  this.cooldownMs         = 250;

  // reduce fire rate for 'stupid' enemies
  this.fireProbability    = 0.01;
  this.lastFireTimeStamp  = 0;
  this.fireCooldownMs     = 10000;
};

Darwinator.Enemy.prototype = Object.create(Darwinator.Entity.prototype);

// for convenience, categories are represented by image names
Darwinator.Enemy.prototype.categories = {
  INTELLIGENT:  'enemy_intellect',
  AGILE:        'enemy_agility',
  STRONG:       'enemy_strength',
  DEFAULT:      'enemy'
};

Darwinator.Enemy.prototype.arm = function(weapon) {
  this.weapon = weapon;
};

Darwinator.Enemy.prototype.update = function() {
  if (!this.alive) {
    return;
  }
  this.body.velocity.setTo(0,0);
  var currTile    = Darwinator.Helpers.pixelsToTile(this.body.x, this.body.y);
  var targetTile  = Darwinator.Helpers.pixelsToTile(this.target.body.x, this.target.body.y);

  var pathLength = this.path.length;
  if(!(pathLength && this.path[pathLength - 1].x === targetTile.x &&
                     this.path[pathLength - 1].y === targetTile.y &&
                     this.path[0].x === currTile.x &&
                     this.path[0].y === currTile.y)) {
    if (Darwinator.Helpers.calculateDistance(targetTile, currTile) * 5 < this.lastPathUpdate) {
      this.updatePath();
    } else {
      this.lastPathUpdate++;
    }
  }

  switch(this.category) {
  case this.categories.INTELLIGENT:
    if (this.path.length && Darwinator.Helpers.calculateDistance(targetTile, currTile) > 10) {
      this.followPath();
    } else {
      this.weapon.fire(this.target.body.x, this.target.body.y);
    }
    break;
  default:
    /* If a path exists - follow it. Else, try to move in the general direction of the player, ignoring
       obsticles*/
    if (this.path.length) {
      this.followPath();
      var onCooldown = (Date.now() - this.lastFireTimeStamp) < this.fireCooldownMs;
      if (!onCooldown && Math.random() < this.fireProbability) {
        this.weapon.fire(this.target.body.x, this.target.body.y);
        this.lastFireTimeStamp = Date.now();
      }
    } else {
      this.game.physics.arcade.moveToXY(this, this.target.body.x, this.target.body.y, this.speed);
    }
    break;
  }
  // Target (ie. player) takes damage while the target and enemy overlap.
  // If they continuously overlap the target will take damage every 0.25 seconds
  this.game.physics.arcade.overlap(this, this.target, this.meleeAttack, null, this);

  if (this.health <= 0 && this.alive){
    console.log('%c Enemy killed by player! ', 'background: black; color: orange');
    this.kill();
  }

};

Darwinator.Enemy.prototype.meleeAttack = function(){ //callback for overlapping with target
  var onCooldown = (Date.now() - this.lastMeleeTimestamp) < this.cooldownMs;
  if (!onCooldown){
    var crit  = Math.random() - this.criticalStrike;
    var dmg   = this.damage;

    if (crit < 0){
      dmg *= 2;
      console.log('%c Enemy made a critical hit! ', 'background: red; color: white');
    }
    this.target.takeDamage(dmg);
    this.damageDone += dmg;
    this.lastMeleeTimestamp = Date.now();
  }
};

Darwinator.Enemy.prototype.updatePath = function() {
  var currTile    = Darwinator.Helpers.pixelsToTile(this.body.x, this.body.y);
  var targetTile  = Darwinator.Helpers.pixelsToTile(this.target.body.x, this.target.body.y);
  this.path = Darwinator.Pathfinder.findPath(currTile, targetTile);
  this.lastPathUpdate = 0;
};

Darwinator.Enemy.prototype.followPath = function() {
  var targetPos = Darwinator.Helpers.tileToPixels(this.path[1].x, this.path[1].y);
  targetPos.x   = Math.round(targetPos.x - this.body.width / 2);
  targetPos.y   = Math.round(targetPos.y - this.body.height / 2);

  var distance = Darwinator.Helpers.calculateDistance(targetPos, [this.body.x, this.body.y]);
  if (distance < 4 && this.path.length > 2) {      // Trial and error - modify if need be.
    // Remember, include (x,y,health) in reset, otherwise health will = 1.
    this.reset(targetPos.x, targetPos.y, this.health);
    this.path.splice(0,1); // Remove first tile in path.
    this.updatePath();
  } else {
    this.game.physics.arcade.moveToXY(this, targetPos.x, targetPos.y, this.speed);
  }
  if (this.path.length < 5 && this.currBreath > 1) {
    this.body.velocity.multiply(2,2);
    this.currBreath--;
  } else if (this.currBreath < this.stamina) {
    this.currBreath += 0.2;
  }
};
<commit_msg>Implement teleportation for enemies of type STRONG<commit_after>'use strict';

Darwinator.Enemy = function(game, target, x, y, health, strength, agility, intellect) {
  if (strength > intellect && strength > agility) {
    this.category = this.categories.STRONG;
  } else if (agility > intellect && agility > strength) {
    this.category = this.categories.AGILE;
  } else if (intellect > strength && intellect > agility) {
    this.category = this.categories.INTELLIGENT;
  } else {
    this.category = this.categories.DEFAULT;
  }
  Darwinator.Entity.call(this, game, x, y, this.category,/* [],*/ health, strength, agility, intellect);

  this.scale.setTo(0.25,0.25);
  this.target = target;
  this.path   = [];
  this.debug  = true;

  //Allow enemy to overlap objects, i.e. reduce the hitbox
  //this.body.setRectangle(20*4, 16*4, 0, 16*4);
  this.lastPathUpdate = 0;

  // score properties to measure success
  this.damageDone = 0;

  // melee cooldown
  this.lastMeleeTimestamp = 0;
  this.cooldownMs         = 250;

  // reduce fire rate for 'stupid' enemies
  this.fireProbability    = 0.01;
  this.lastFireTimeStamp  = 0;
  this.fireCooldownMs     = 10000;
  this.lastAbilityUse     = 0;
  this.abilityCooldownMs  = 10000;
};

Darwinator.Enemy.prototype = Object.create(Darwinator.Entity.prototype);

// for convenience, categories are represented by image names
Darwinator.Enemy.prototype.categories = {
  INTELLIGENT:  'enemy_intellect',
  AGILE:        'enemy_agility',
  STRONG:       'enemy_strength',
  DEFAULT:      'enemy'
};

Darwinator.Enemy.prototype.arm = function(weapon) {
  this.weapon = weapon;
};

Darwinator.Enemy.prototype.update = function() {
  if (!this.alive) {
    return;
  }
  this.body.velocity.setTo(0,0);
  var currTile    = Darwinator.Helpers.pixelsToTile(this.body.x, this.body.y);
  var targetTile  = Darwinator.Helpers.pixelsToTile(this.target.body.x, this.target.body.y);

  var pathLength = this.path.length;
  if(!(pathLength && this.path[pathLength - 1].x === targetTile.x &&
                     this.path[pathLength - 1].y === targetTile.y &&
                     this.path[0].x === currTile.x &&
                     this.path[0].y === currTile.y)) {
    if (Darwinator.Helpers.calculateDistance(targetTile, currTile) * 5 < this.lastPathUpdate) {
      this.updatePath();
    } else {
      this.lastPathUpdate++;
    }
  }

  switch(this.category) {
  case this.categories.INTELLIGENT:
    if (this.path.length && Darwinator.Helpers.calculateDistance(targetTile, currTile) > 10) {
      this.followPath();
    } else {
      this.weapon.fire(this.target.body.x, this.target.body.y);
    }
    break;
  case this.categories.STRONG:
    if (this.path.length) {
      var onCooldown = (Date.now() - this.lastAbilityUse) < this.abilityCooldownMs;
      if(!onCooldown) {
        var telRange = Math.min((this.path.length - 1), 5);
        var targetTile = this.path[telRange];
        var target = Darwinator.Helpers.tileToPixels(targetTile.x, targetTile.y);
        this.reset(target.x, target.y, this.health);
        this.lastAbilityUse = Date.now();
      }
    }
  default:
    /* If a path exists - follow it. Else, try to move in the general direction of the player, ignoring
       obsticles*/
    if (this.path.length) {
      this.followPath();
      var onCooldown = (Date.now() - this.lastFireTimeStamp) < this.fireCooldownMs;
      if (!onCooldown && Math.random() < this.fireProbability) {
        this.weapon.fire(this.target.body.x, this.target.body.y);
        this.lastFireTimeStamp = Date.now();
      }
    } else {
      this.game.physics.arcade.moveToXY(this, this.target.body.x, this.target.body.y, this.speed);
    }
    break;
  }
  // Target (ie. player) takes damage while the target and enemy overlap.
  // If they continuously overlap the target will take damage every 0.25 seconds
  this.game.physics.arcade.overlap(this, this.target, this.meleeAttack, null, this);

  if (this.health <= 0 && this.alive){
    console.log('%c Enemy killed by player! ', 'background: black; color: orange');
    this.kill();
  }

};

Darwinator.Enemy.prototype.meleeAttack = function(){ //callback for overlapping with target
  var onCooldown = (Date.now() - this.lastMeleeTimestamp) < this.cooldownMs;
  if (!onCooldown){
    var crit  = Math.random() - this.criticalStrike;
    var dmg   = this.damage;

    if (crit < 0){
      dmg *= 2;
      console.log('%c Enemy made a critical hit! ', 'background: red; color: white');
    }
    this.target.takeDamage(dmg);
    this.damageDone += dmg;
    this.lastMeleeTimestamp = Date.now();
  }
};

Darwinator.Enemy.prototype.updatePath = function() {
  var currTile    = Darwinator.Helpers.pixelsToTile(this.body.x, this.body.y);
  var targetTile  = Darwinator.Helpers.pixelsToTile(this.target.body.x, this.target.body.y);
  this.path = Darwinator.Pathfinder.findPath(currTile, targetTile);
  this.lastPathUpdate = 0;
};

Darwinator.Enemy.prototype.followPath = function() {
  var targetPos = Darwinator.Helpers.tileToPixels(this.path[1].x, this.path[1].y);
  targetPos.x   = Math.round(targetPos.x - this.body.width / 2);
  targetPos.y   = Math.round(targetPos.y - this.body.height / 2);

  var distance = Darwinator.Helpers.calculateDistance(targetPos, [this.body.x, this.body.y]);
  if (distance < 4 && this.path.length > 2) {      // Trial and error - modify if need be.
    // Remember, include (x,y,health) in reset, otherwise health will = 1.
    this.reset(targetPos.x, targetPos.y, this.health);
    this.path.splice(0,1); // Remove first tile in path.
    this.updatePath();
  } else {
    this.game.physics.arcade.moveToXY(this, targetPos.x, targetPos.y, this.speed);
  }
  if (this.path.length < 5 && this.currBreath > 1) {
    this.body.velocity.multiply(2,2);
    this.currBreath--;
  } else if (this.currBreath < this.stamina) {
    this.currBreath += 0.2;
  }
};
<|endoftext|>


<commit_before>function createQueue(fn, options = { concurrency: 1 }) {
  let active = 0;
  let queue = [];

  const { concurrency } = options;

  async function exec(...params) {
    active++;
    const res = await fn.apply(this, params);
    active--;

    if (queue.length && active < concurrency) {
      const next = queue.shift();
      exec.apply(this, next.params)
        .then(next.resolve)
        .catch(next.reject);
    }

    return res;
  }

  function handleCall(...params) {
    if (active < concurrency) {
      return exec.apply(this, params);
    }
    return new Promise((resolve, reject) => {
      queue.push({
        resolve,
        reject,
        params
      });
    });
  };

  handleCall.stop = function (ids = null) {
    if (ids) {
      queue = queue.filter(item => ids.findIndex(id => id !== item.params[0].id) > -1);  // use the ids param for filtering the queue
    } else {
      active = 0;
      queue = [];  // remove all items in the queue
    }
  }

  return handleCall;
}

function handleDescriptor(target, name, descriptor, ...options) {
  if (typeof descriptor === 'undefined') {
    throw new SyntaxError('queueable decorator can\'t be used with parameters. Wrap your function instead.');
  }

  const fn = descriptor.value;
  if (typeof fn !== 'function') {
    throw new SyntaxError('Only functions can be made queueable');
  }

  const queue = createQueue(fn, ...options);

  return {
    ...descriptor,
    value: queue
  };
}

export default function queueable(...args) {
  if (typeof args[0] === 'function') {
    return createQueue.apply(this, args);
  }
  return handleDescriptor(...args);
}

export {
  queueable
}<commit_msg>feat(unshift): add items to beginning of queue<commit_after>function createQueue(fn, options = { concurrency: 1, unshift: false }) {
  let active = 0;
  let queue = [];

  const { concurrency, unshift } = options;

  async function exec(...params) {
    active++;
    const res = await fn.apply(this, params);
    active--;

    if (queue.length && active < concurrency) {
      const next = queue.shift();
      exec.apply(this, next.params)
        .then(next.resolve)
        .catch(next.reject);
    }

    return res;
  }

  function handleCall(...params) {
    if (active < concurrency) {
      return exec.apply(this, params);
    }
    return new Promise((resolve, reject) => {
      if (unshift) {
        queue.unshift({
          resolve,
          reject,
          params
        });
      } else {
        queue.push({
          resolve,
          reject,
          params
        });
      }
    });
  };

  handleCall.stop = function (ids = null) {
    if (ids) {
      queue = queue.filter(item => ids.findIndex(id => id !== item.params[0].id) > -1);  // use the ids param for filtering the queue
    } else {
      active = 0;
      queue = [];  // remove all items in the queue
    }
  }

  return handleCall;
}

function handleDescriptor(target, name, descriptor, ...options) {
  if (typeof descriptor === 'undefined') {
    throw new SyntaxError('queueable decorator can\'t be used with parameters. Wrap your function instead.');
  }

  const fn = descriptor.value;
  if (typeof fn !== 'function') {
    throw new SyntaxError('Only functions can be made queueable');
  }

  const queue = createQueue(fn, ...options);

  return {
    ...descriptor,
    value: queue
  };
}

export default function queueable(...args) {
  if (typeof args[0] === 'function') {
    return createQueue.apply(this, args);
  }
  return handleDescriptor(...args);
}

export {
  queueable
}<|endoftext|>


<commit_before>'use strict';

const events = require('events');
const serverPort = 36001;
const librato = require('../lib/librato.js');
const nock = require('nock');
const sinon = require('sinon');

const config = {
  debug: false,
  librato: {
    email: '-@-',
    token: '-',
    api: 'http://127.0.0.1:' + serverPort,
    writeToLegacy: false,
  },
};

module.exports.debugConfig = {
  setUp: function(callback) {
    this.emitter = new events.EventEmitter();
    global.util = this.util = require('util');
    this.apiServer = nock('http://127.0.0.1:36001')
                         .defaultReplyHeaders({'Content-Type': 'application/json'});

    this.logSpy = sinon.spy(global.util, 'log');

    callback();
  },

  tearDown: function(callback) {
    config.debug = false;
    global.util.log.restore();
    callback();
  },

  testDebugConfigTrue: function(test) {
    test.expect(1);

    config.debug = true;
    let metrics = {gauges: {my_gauge: 1}};

    librato.init(null, config, this.emitter, this.util);
    this.emitter.emit('flush', 123, metrics);

    test.ok(this.logSpy.called);
    test.done();
  },

  testDebugConfigFalse: function(test) {
    test.expect(1);

    let metrics = {gauges: {my_gauge: 1}};

    librato.init(null, config, this.emitter, this.util);
    test.ok(!this.logSpy.called);

    this.emitter.emit('flush', 123, metrics);
    test.done();
  },

  testNoLogger: function(test) {
    test.expect(1);

    let metrics = {gauges: {my_gauge: 1}};

    librato.init(null, config, this.emitter);
    test.ok(!this.logSpy.called);

    this.emitter.emit('flush', 123, metrics);
    test.done();
  },

  testLoggerNoDebugConfig: function(test) {
    test.expect(1);
    let metrics = {gauges: {my_gauge: 1}};
    config.debug = null;

    librato.init(null, config, this.emitter, this.util);
    test.ok(!this.logSpy.called);

    this.emitter.emit('flush', 123, metrics);
    test.done();
  },

};

module.exports.tags = {
  setUp: function(callback) {
    this.emitter = new events.EventEmitter();

    this.apiServer = nock('http://127.0.0.1:36001')
                         .defaultReplyHeaders({'Content-Type': 'application/json'});

    librato.init(null, config, this.emitter);
    callback();
  },

  tearDown: function(callback) {
    callback();
  },

  testValidMeasurementNoTags: function(test) {
    test.expect(4);
    let metrics = {gauges: {my_gauge: 1}};
    this.apiServer.post('/v1/measurements')
             .reply(200, (uri, requestBody) => {
                let measurement = requestBody.measurements[0];
                test.ok(requestBody);
                test.equal(measurement.name, 'my_gauge');
                test.equal(measurement.value, 1);
                test.deepEqual(measurement.tags, {});
                test.done();
             });

    this.emitter.emit('flush', 123, metrics);
  },

  testValidMeasurementSingleTag: function(test) {
    test.expect(4);
    let metrics = {gauges: {'my_gauge#foo=bar': 1}};
    this.apiServer.post('/v1/measurements')
             .reply(200, (uri, requestBody) => {
                let measurement = requestBody.measurements[0];
                test.ok(requestBody);
                test.equal(measurement.name, 'my_gauge');
                test.equal(measurement.value, 1);
                test.deepEqual(measurement.tags, {foo: 'bar'});
                test.done();
             });

    this.emitter.emit('flush', 123, metrics);
  },

  testValidMeasurementMultipleTags: function(test) {
    test.expect(4);
    let metrics = {gauges: {'my_gauge#foo=bar,biz=baz': 1}};
    this.apiServer.post('/v1/measurements')
             .reply(200, (uri, requestBody) => {
                let measurement = requestBody.measurements[0];
                test.ok(requestBody);
                test.equal(measurement.name, 'my_gauge');
                test.equal(measurement.value, 1);
                test.deepEqual(measurement.tags, {
                  foo: 'bar',
                  biz: 'baz',
                });
                test.done();
             });

    this.emitter.emit('flush', 123, metrics);
  },

  testTimers: function(test) {
    test.expect(7);
    let metrics = {
      timers: {
        'my_timer#tag=foo': [
          41,
          73.5,
        ],
      },
      timer_data: {'my_timer#tag=foo': null},
    };
    this.apiServer.post('/v1/measurements')
             .reply(200, (uri, requestBody) => {
                let measurement = requestBody.measurements[0];
                test.ok(measurement);
                test.equal(measurement.name, 'my_timer');
                test.equal(measurement.value, undefined);
                test.equal(measurement.min, 41);
                test.equal(measurement.max, 73.5);
                test.equal(measurement.sum, 114.5);
                test.deepEqual(measurement.tags, {tag: 'foo'});
                test.done();
             });

    this.emitter.emit('flush', 123, metrics);
  },

  testTimersPercentiles: function(test) {
    test.expect(14);
    let metrics = {
      timers: {
        'my_timer#tag=foo': [10],
      },
      timer_data: {'my_timer#tag=foo': null},
      pctThreshold: {99: 99},
    };
    this.apiServer.post('/v1/measurements')
             .reply(200, (uri, requestBody) => {
                let hundredth = requestBody.measurements[0];
                test.ok(hundredth);
                test.equal(hundredth.name, 'my_timer');
                test.equal(hundredth.value, undefined);
                test.equal(hundredth.min, 10);
                test.equal(hundredth.max, 10);
                test.equal(hundredth.sum, 10);
                test.deepEqual(hundredth.tags, {tag: 'foo'});

                let measurement = requestBody.measurements[1];
                test.ok(measurement);
                test.equal(measurement.name, 'my_timer.99');
                test.equal(measurement.value, undefined);
                test.equal(measurement.min, 10);
                test.equal(measurement.max, 10);
                test.equal(measurement.sum, 10);
                test.deepEqual(measurement.tags, {tag: 'foo'});
                test.done();
             });

    this.emitter.emit('flush', 123, metrics);
  },

  testIgnoreBrokenMetrics: function(test) {
    test.expect(3);
    let metrics = {
      gauges: {
        cool_gauge: 123,
        bad_counter: 321,
      },
    };
    let errors = {errors: {params: {type: ['\'bad_counter\'' + ' is a counter, but was' + ' submitted as different type']}}};

    this.apiServer.post('/v1/measurements')
                  .reply(400, JSON.stringify(errors));

    this.emitter.emit('flush', 123, metrics);

    // Similuate another flush...
    setTimeout(() => {
      this.apiServer.post('/v1/measurements')
               .reply(200, (uri, requestBody) => {
                 let measurements = requestBody.measurements;
                 let gaugeNames = measurements.map((gauge) => gauge.name);
                 test.equal(requestBody.measurements.length, 1);
                 test.equal(gaugeNames.indexOf('cool_gauge'), 0);
                 test.equal(gaugeNames.indexOf('bad_counter'), -1);
                 test.done();
               });

      this.emitter.emit('flush', 123, metrics);
    }, 500);
  },

  testMaxBatchSize: function(test) {
    test.expect(2);
    var gauges = {};
    for (var i = 0; i < 500; i++) {
      var key = 'gauge' + i;
      gauges[key] = 1;
    }
    var metrics = {gauges: gauges};
    this.apiServer.post('/v1/measurements')
                  .reply(200, (uri, requestBody) => {
                    test.ok(requestBody.measurements);
                    test.equal(requestBody.measurements.length, 500);
                    test.done();
                  });

    this.emitter.emit('flush', 123, metrics);
  },

  testValidMeasurementTopLevelTag: function(test) {
    config.librato.host = '127.0.0.1';
    config.librato.tags = {test: true};
    librato.init(null, config, this.emitter);

    test.expect(5);
    let metrics = {gauges: {'my_gauge#foo=bar': 1}};
    this.apiServer.post('/v1/measurements')
                  .reply(200, (uri, requestBody) => {
                    // Top-level tags
                    test.deepEqual(requestBody.tags, {test: true, host: '127.0.0.1'});

                    let measurement = requestBody.measurements[0];
                    test.ok(requestBody);
                    test.equal(measurement.name, 'my_gauge');
                    test.equal(measurement.value, 1);
                    test.deepEqual(measurement.tags, {foo: 'bar'});
                    test.done();
                  });

    this.emitter.emit('flush', 123, metrics);
  },
};

module.exports.legacy = {
  setUp: function(callback) {
    config.librato.writeToLegacy = true;
    config.librato.countersAsGauges = false;
    this.emitter = new events.EventEmitter();

    this.apiServer = nock('http://127.0.0.1:36001')
                         .defaultReplyHeaders({'Content-Type': 'application/json'});

    librato.init(null, config, this.emitter);
    callback();
  },

  tearDown: function(callback) {
    callback();
  },

  testGauges: function(test) {
    test.expect(7);
    let metrics = {gauges: {my_gauge: 1}};
    this.apiServer.post('/v1/measurements')
                  .reply(200, (uri, requestBody) => {
                    let measurement = requestBody.measurements[0];
                    test.ok(requestBody);
                    test.equal(measurement.name, 'my_gauge');
                    test.equal(measurement.value, 1);
                    test.deepEqual(measurement.tags, {});
                  });

    this.apiServer.post('/v1/metrics')
                  .reply(200, (uri, requestBody) => {
                    let gauge = requestBody.gauges[0];
                    test.ok(requestBody);
                    test.equal(gauge.name, 'my_gauge');
                    test.equal(gauge.value, 1);
                    test.done();
                  });

    this.emitter.emit('flush', 123, metrics);
  },

  testCounters: function(test) {
    test.expect(7);
    let metrics = {counters: {my_counter: 1}};
    this.apiServer.post('/v1/measurements')
                  .reply(200, (uri, requestBody) => {
                    let measurement = requestBody.measurements[0];
                    test.ok(requestBody);
                    test.equal(measurement.name, 'my_counter');
                    test.equal(measurement.value, 1);
                    test.deepEqual(measurement.tags, {});
                  });

    this.apiServer.post('/v1/metrics')
                  .reply(200, (uri, requestBody) => {
                    let counter = requestBody.counters[0];
                    test.ok(requestBody);
                    test.equal(counter.name, 'my_counter');
                    test.equal(counter.value, 1);
                    test.done();
                  });

    this.emitter.emit('flush', 123, metrics);
  },

  testSource: function(test) {
    config.librato.source = 'localhost';
    librato.init(null, config, this.emitter);

    test.expect(9);
    let metrics = {gauges: {my_gauge: 1}};
    this.apiServer.post('/v1/measurements')
                  .reply(200, (uri, requestBody) => {
                    // No top level source
                    test.equal(requestBody.source, undefined);

                    let measurement = requestBody.measurements[0];
                    test.ok(requestBody);
                    test.equal(measurement.name, 'my_gauge');
                    test.equal(measurement.value, 1);
                    test.deepEqual(measurement.tags, {source: 'localhost'});
                  });

    this.apiServer.post('/v1/metrics')
                  .reply(200, (uri, requestBody) => {
                    let gauge = requestBody.gauges[0];
                    test.ok(requestBody);
                    test.equal(gauge.name, 'my_gauge');
                    test.equal(gauge.value, 1);
                    test.equal(gauge.source, 'localhost');
                    test.done();
                  });

    this.emitter.emit('flush', 123, metrics);
  },

  testSourceRegex: function(test) {
    config.librato.sourceRegex = /^(.*?)--/;
    librato.init(null, config, this.emitter);

    test.expect(9);
    let metrics = {gauges: {'rails-application--my_gauge': 1}};
    this.apiServer.post('/v1/measurements')
                  .reply(200, (uri, requestBody) => {
                    let measurement = requestBody.measurements[0];
                    test.ok(requestBody);
                    // no source when using the measurements api
                    test.equal(measurement.source, undefined);
                    test.equal(measurement.name, 'my_gauge');
                    test.equal(measurement.value, 1);
                    test.deepEqual(measurement.tags, {source: 'rails-application'});
                  });

    this.apiServer.post('/v1/metrics')
                  .reply(200, (uri, requestBody) => {
                    let gauge = requestBody.gauges[0];
                    test.ok(requestBody);
                    test.equal(gauge.name, 'my_gauge');
                    test.equal(gauge.value, 1);
                    test.equal(gauge.source, 'rails-application');
                    test.done();
                  });

    this.emitter.emit('flush', 123, metrics);
  },

  testGlobalPrefix: function(test) {
    config.librato.sourceRegex = /^(.*?)--/;
    config.librato.globalPrefix = 'global.prefix';
    librato.init(null, config, this.emitter);

    test.expect(6);
    let metrics = {gauges: {'rails-application--my_gauge': 1}};
    this.apiServer.post('/v1/measurements')
                  .reply(200, (uri, requestBody) => {
                    let measurement = requestBody.measurements[0];
                    test.ok(requestBody);
                    test.equal(measurement.name.startsWith('global.prefix'), true);
                    test.deepEqual(measurement.tags, {source: 'rails-application'});
                  });

    this.apiServer.post('/v1/metrics')
                  .reply(200, (uri, requestBody) => {
                    let gauge = requestBody.gauges[0];
                    test.ok(requestBody);
                    test.equal(gauge.name.startsWith('global.prefix'), true);
                    test.equal(gauge.source, 'rails-application');
                    test.done();
                  });

    this.emitter.emit('flush', 123, metrics);
  },
};
<commit_msg>Add a test (#94)<commit_after>'use strict';

const events = require('events');
const serverPort = 36001;
const librato = require('../lib/librato.js');
const nock = require('nock');
const sinon = require('sinon');
const http = require('http');

const config = {
  debug: false,
  librato: {
    email: '-@-',
    token: '-',
    api: 'http://127.0.0.1:' + serverPort,
    writeToLegacy: false,
  },
};

module.exports.debugConfig = {
  setUp: function(callback) {
    this.emitter = new events.EventEmitter();
    global.util = this.util = require('util');
    this.apiServer = nock('http://127.0.0.1:36001')
                         .defaultReplyHeaders({'Content-Type': 'application/json'});

    this.logSpy = sinon.spy(global.util, 'log');

    callback();
  },

  tearDown: function(callback) {
    config.debug = false;
    global.util.log.restore();
    callback();
  },

  testDebugConfigTrue: function(test) {
    test.expect(1);

    config.debug = true;
    let metrics = {gauges: {my_gauge: 1}};

    librato.init(null, config, this.emitter, this.util);
    this.emitter.emit('flush', 123, metrics);

    test.ok(this.logSpy.called);
    test.done();
  },

  testDebugConfigFalse: function(test) {
    test.expect(1);

    let metrics = {gauges: {my_gauge: 1}};

    librato.init(null, config, this.emitter, this.util);
    test.ok(!this.logSpy.called);

    this.emitter.emit('flush', 123, metrics);
    test.done();
  },

  testNoLogger: function(test) {
    test.expect(1);

    let metrics = {gauges: {my_gauge: 1}};

    librato.init(null, config, this.emitter);
    test.ok(!this.logSpy.called);

    this.emitter.emit('flush', 123, metrics);
    test.done();
  },

  testLoggerNoDebugConfig: function(test) {
    test.expect(1);
    let metrics = {gauges: {my_gauge: 1}};
    config.debug = null;

    librato.init(null, config, this.emitter, this.util);
    test.ok(!this.logSpy.called);

    this.emitter.emit('flush', 123, metrics);
    test.done();
  },

};

module.exports.tags = {
  setUp: function(callback) {
    this.emitter = new events.EventEmitter();

    this.apiServer = nock('http://127.0.0.1:36001')
                         .defaultReplyHeaders({'Content-Type': 'application/json'});

    librato.init(null, config, this.emitter);
    this.httpSpy = sinon.spy(http, 'request');
    callback();
  },

  tearDown: function(callback) {
    http.request.restore();
    callback();
  },

  testValidMeasurementNoTags: function(test) {
    test.expect(4);
    let metrics = {gauges: {my_gauge: 1}};
    this.apiServer.post('/v1/measurements')
             .reply(200, (uri, requestBody) => {
                let measurement = requestBody.measurements[0];
                test.ok(requestBody);
                test.equal(measurement.name, 'my_gauge');
                test.equal(measurement.value, 1);
                test.deepEqual(measurement.tags, {});
                test.done();
             });

    this.emitter.emit('flush', 123, metrics);
  },

  testValidMeasurementSingleTag: function(test) {
    test.expect(4);
    let metrics = {gauges: {'my_gauge#foo=bar': 1}};
    this.apiServer.post('/v1/measurements')
             .reply(200, (uri, requestBody) => {
                let measurement = requestBody.measurements[0];
                test.ok(requestBody);
                test.equal(measurement.name, 'my_gauge');
                test.equal(measurement.value, 1);
                test.deepEqual(measurement.tags, {foo: 'bar'});
                test.done();
             });

    this.emitter.emit('flush', 123, metrics);
  },

  testValidMeasurementMultipleTags: function(test) {
    test.expect(4);
    let metrics = {gauges: {'my_gauge#foo=bar,biz=baz': 1}};
    this.apiServer.post('/v1/measurements')
             .reply(200, (uri, requestBody) => {
                let measurement = requestBody.measurements[0];
                test.ok(requestBody);
                test.equal(measurement.name, 'my_gauge');
                test.equal(measurement.value, 1);
                test.deepEqual(measurement.tags, {
                  foo: 'bar',
                  biz: 'baz',
                });
                test.done();
             });

    this.emitter.emit('flush', 123, metrics);
  },

  testTimers: function(test) {
    test.expect(7);
    let metrics = {
      timers: {
        'my_timer#tag=foo': [
          41,
          73.5,
        ],
      },
      timer_data: {'my_timer#tag=foo': null},
    };
    this.apiServer.post('/v1/measurements')
             .reply(200, (uri, requestBody) => {
                let measurement = requestBody.measurements[0];
                test.ok(measurement);
                test.equal(measurement.name, 'my_timer');
                test.equal(measurement.value, undefined);
                test.equal(measurement.min, 41);
                test.equal(measurement.max, 73.5);
                test.equal(measurement.sum, 114.5);
                test.deepEqual(measurement.tags, {tag: 'foo'});
                test.done();
             });

    this.emitter.emit('flush', 123, metrics);
  },

  testTimersPercentiles: function(test) {
    test.expect(14);
    let metrics = {
      timers: {
        'my_timer#tag=foo': [10],
      },
      timer_data: {'my_timer#tag=foo': null},
      pctThreshold: {99: 99},
    };
    this.apiServer.post('/v1/measurements')
             .reply(200, (uri, requestBody) => {
                let hundredth = requestBody.measurements[0];
                test.ok(hundredth);
                test.equal(hundredth.name, 'my_timer');
                test.equal(hundredth.value, undefined);
                test.equal(hundredth.min, 10);
                test.equal(hundredth.max, 10);
                test.equal(hundredth.sum, 10);
                test.deepEqual(hundredth.tags, {tag: 'foo'});

                let measurement = requestBody.measurements[1];
                test.ok(measurement);
                test.equal(measurement.name, 'my_timer.99');
                test.equal(measurement.value, undefined);
                test.equal(measurement.min, 10);
                test.equal(measurement.max, 10);
                test.equal(measurement.sum, 10);
                test.deepEqual(measurement.tags, {tag: 'foo'});
                test.done();
             });

    this.emitter.emit('flush', 123, metrics);
  },

  testIgnoreBrokenMetrics: function(test) {
    test.expect(3);
    let metrics = {
      gauges: {
        cool_gauge: 123,
        bad_counter: 321,
      },
    };
    let errors = {errors: {params: {type: ['\'bad_counter\'' + ' is a counter, but was' + ' submitted as different type']}}};

    this.apiServer.post('/v1/measurements')
                  .reply(400, JSON.stringify(errors));

    this.emitter.emit('flush', 123, metrics);

    // Similuate another flush...
    setTimeout(() => {
      this.apiServer.post('/v1/measurements')
               .reply(200, (uri, requestBody) => {
                 let measurements = requestBody.measurements;
                 let gaugeNames = measurements.map((gauge) => gauge.name);
                 test.equal(requestBody.measurements.length, 1);
                 test.equal(gaugeNames.indexOf('cool_gauge'), 0);
                 test.equal(gaugeNames.indexOf('bad_counter'), -1);
                 test.done();
               });

      this.emitter.emit('flush', 123, metrics);
    }, 500);
  },

  testMaxBatchSize: function(test) {
    test.expect(2);
    var gauges = {};
    for (var i = 0; i < 500; i++) {
      var key = 'gauge' + i;
      gauges[key] = 1;
    }
    var metrics = {gauges: gauges};
    this.apiServer.post('/v1/measurements')
                  .reply(200, (uri, requestBody) => {
                    test.ok(requestBody.measurements);
                    test.equal(requestBody.measurements.length, 500);
                    test.done();
                  });

    this.emitter.emit('flush', 123, metrics);
  },

  testBatchProperRequestCount: function(test) {
    test.expect(3);
    var gauges = {};
    for (var i = 0; i < 500; i++) {
      var key = 'gauge' + i;
      gauges[key] = 1;
    }
    var metrics = {gauges: gauges};
    this.apiServer.post('/v1/measurements')
                  .reply(200, (uri, requestBody) => {
                    test.ok(requestBody.measurements);
                  });

    this.emitter.emit('flush', 123, metrics);

    // Let some time pass...
    setTimeout(() => {
      // There should have ever been one submission because we hit our maxBatchSize
      test.ok(this.httpSpy.calledOnce);

      // Try with a bigger batch...
      for (var i = 0; i < 1500; i++) {
        var key = 'gauge' + i;
        gauges[key] = 1;
      }
      metrics = {gauges: gauges};
      // Reset the spy
      this.httpSpy.reset();

      // Flush
      this.emitter.emit('flush', 123, metrics);

      // Let some time pass...
      setTimeout(() => {
        // One request, per batch of 500 metrics....
        test.ok(this.httpSpy.calledThrice);
        test.done();
      });
    }, 500);
  },

  testValidMeasurementTopLevelTag: function(test) {
    config.librato.host = '127.0.0.1';
    config.librato.tags = {test: true};
    librato.init(null, config, this.emitter);

    test.expect(5);
    let metrics = {gauges: {'my_gauge#foo=bar': 1}};
    this.apiServer.post('/v1/measurements')
                  .reply(200, (uri, requestBody) => {
                    // Top-level tags
                    test.deepEqual(requestBody.tags, {test: true, host: '127.0.0.1'});

                    let measurement = requestBody.measurements[0];
                    test.ok(requestBody);
                    test.equal(measurement.name, 'my_gauge');
                    test.equal(measurement.value, 1);
                    test.deepEqual(measurement.tags, {foo: 'bar'});
                    test.done();
                  });

    this.emitter.emit('flush', 123, metrics);
  },
};

module.exports.legacy = {
  setUp: function(callback) {
    config.librato.writeToLegacy = true;
    config.librato.countersAsGauges = false;
    this.emitter = new events.EventEmitter();

    this.apiServer = nock('http://127.0.0.1:36001')
                         .defaultReplyHeaders({'Content-Type': 'application/json'});

    librato.init(null, config, this.emitter);
    callback();
  },

  tearDown: function(callback) {
    callback();
  },

  testGauges: function(test) {
    test.expect(7);
    let metrics = {gauges: {my_gauge: 1}};
    this.apiServer.post('/v1/measurements')
                  .reply(200, (uri, requestBody) => {
                    let measurement = requestBody.measurements[0];
                    test.ok(requestBody);
                    test.equal(measurement.name, 'my_gauge');
                    test.equal(measurement.value, 1);
                    test.deepEqual(measurement.tags, {});
                  });

    this.apiServer.post('/v1/metrics')
                  .reply(200, (uri, requestBody) => {
                    let gauge = requestBody.gauges[0];
                    test.ok(requestBody);
                    test.equal(gauge.name, 'my_gauge');
                    test.equal(gauge.value, 1);
                    test.done();
                  });

    this.emitter.emit('flush', 123, metrics);
  },

  testCounters: function(test) {
    test.expect(7);
    let metrics = {counters: {my_counter: 1}};
    this.apiServer.post('/v1/measurements')
                  .reply(200, (uri, requestBody) => {
                    let measurement = requestBody.measurements[0];
                    test.ok(requestBody);
                    test.equal(measurement.name, 'my_counter');
                    test.equal(measurement.value, 1);
                    test.deepEqual(measurement.tags, {});
                  });

    this.apiServer.post('/v1/metrics')
                  .reply(200, (uri, requestBody) => {
                    let counter = requestBody.counters[0];
                    test.ok(requestBody);
                    test.equal(counter.name, 'my_counter');
                    test.equal(counter.value, 1);
                    test.done();
                  });

    this.emitter.emit('flush', 123, metrics);
  },

  testSource: function(test) {
    config.librato.source = 'localhost';
    librato.init(null, config, this.emitter);

    test.expect(9);
    let metrics = {gauges: {my_gauge: 1}};
    this.apiServer.post('/v1/measurements')
                  .reply(200, (uri, requestBody) => {
                    // No top level source
                    test.equal(requestBody.source, undefined);

                    let measurement = requestBody.measurements[0];
                    test.ok(requestBody);
                    test.equal(measurement.name, 'my_gauge');
                    test.equal(measurement.value, 1);
                    test.deepEqual(measurement.tags, {source: 'localhost'});
                  });

    this.apiServer.post('/v1/metrics')
                  .reply(200, (uri, requestBody) => {
                    let gauge = requestBody.gauges[0];
                    test.ok(requestBody);
                    test.equal(gauge.name, 'my_gauge');
                    test.equal(gauge.value, 1);
                    test.equal(gauge.source, 'localhost');
                    test.done();
                  });

    this.emitter.emit('flush', 123, metrics);
  },

  testSourceRegex: function(test) {
    config.librato.sourceRegex = /^(.*?)--/;
    librato.init(null, config, this.emitter);

    test.expect(9);
    let metrics = {gauges: {'rails-application--my_gauge': 1}};
    this.apiServer.post('/v1/measurements')
                  .reply(200, (uri, requestBody) => {
                    let measurement = requestBody.measurements[0];
                    test.ok(requestBody);
                    // no source when using the measurements api
                    test.equal(measurement.source, undefined);
                    test.equal(measurement.name, 'my_gauge');
                    test.equal(measurement.value, 1);
                    test.deepEqual(measurement.tags, {source: 'rails-application'});
                  });

    this.apiServer.post('/v1/metrics')
                  .reply(200, (uri, requestBody) => {
                    let gauge = requestBody.gauges[0];
                    test.ok(requestBody);
                    test.equal(gauge.name, 'my_gauge');
                    test.equal(gauge.value, 1);
                    test.equal(gauge.source, 'rails-application');
                    test.done();
                  });

    this.emitter.emit('flush', 123, metrics);
  },

  testGlobalPrefix: function(test) {
    config.librato.sourceRegex = /^(.*?)--/;
    config.librato.globalPrefix = 'global.prefix';
    librato.init(null, config, this.emitter);

    test.expect(6);
    let metrics = {gauges: {'rails-application--my_gauge': 1}};
    this.apiServer.post('/v1/measurements')
                  .reply(200, (uri, requestBody) => {
                    let measurement = requestBody.measurements[0];
                    test.ok(requestBody);
                    test.equal(measurement.name.startsWith('global.prefix'), true);
                    test.deepEqual(measurement.tags, {source: 'rails-application'});
                  });

    this.apiServer.post('/v1/metrics')
                  .reply(200, (uri, requestBody) => {
                    let gauge = requestBody.gauges[0];
                    test.ok(requestBody);
                    test.equal(gauge.name.startsWith('global.prefix'), true);
                    test.equal(gauge.source, 'rails-application');
                    test.done();
                  });

    this.emitter.emit('flush', 123, metrics);
  },
};
<|endoftext|>


<commit_before>const path = require('path');
const FriendlyErrorsPlugin = require('friendly-errors-webpack-plugin');
const TerserPlugin = require('terser-webpack-plugin');
const { ProvidePlugin } = require('webpack');

const { project, paths } = require('./gulpfile.js/config');
const { getWebpackEntries } = require('./gulpfile.js/utils');

const devMode = project.mode !== 'production';

module.exports = {
  mode: project.mode,
  entry: getWebpackEntries(),
  output: {
    filename: '[name].bundle.js',
    path: path.resolve(__dirname, paths.dist),
    pathinfo: devMode,
    publicPath: paths.dist.replace(paths.app, ''),
  },
  devtool: devMode ? 'eval-cheap-source-map' : false,
  optimization: {
    minimize: !devMode,
    minimizer: [
      new TerserPlugin({
        exclude: /vendors/,
        cache: true,
        parallel: true,
        sourceMap: false,
        terserOptions: {
          compress: {
            drop_console: true,
          },
        },
      }),
    ],
    removeEmptyChunks: !devMode,
    runtimeChunk: 'single',
    splitChunks: {
      cacheGroups: {
        vendors: {
          test: /[\\/]node_modules[\\/]/,
          name: 'vendors',
          chunks: 'initial',
          minChunks: 2,
          minSize: 0,
        },
        common: {
          test: /[\\/]scripts\/(App|config)[\\/]/i,
          name: 'common',
          chunks: 'all',
          minSize: 0,
        },
      },
    },
  },
  module: {
    rules: [
      {
        test: /\.m?js$/,
        exclude: /(node_modules)/,
        use: {
          loader: 'babel-loader',
          options: {
            presets: [
              [
                '@babel/preset-env',
                {
                  bugfixes: true,
                  corejs: 3,
                  useBuiltIns: 'entry',
                },
              ],
            ],
            plugins: [
              '@babel/plugin-transform-runtime',
              '@babel/plugin-proposal-class-properties',
            ],
          },
        },
      },
    ],
  },
  plugins: [
    new FriendlyErrorsPlugin(),
    new ProvidePlugin({
      $: 'jquery',
      jQuery: 'jquery',
    }),
  ],
  watchOptions: {
    ignored: ['./node_modules/'],
  },
};
<commit_msg>no comments in bundled scripts<commit_after>const path = require('path');
const FriendlyErrorsPlugin = require('friendly-errors-webpack-plugin');
const TerserPlugin = require('terser-webpack-plugin');
const { ProvidePlugin } = require('webpack');

const { project, paths } = require('./gulpfile.js/config');
const { getWebpackEntries } = require('./gulpfile.js/utils');

const devMode = project.mode !== 'production';

module.exports = {
  mode: project.mode,
  entry: getWebpackEntries(),
  output: {
    filename: '[name].bundle.js',
    path: path.resolve(__dirname, paths.dist),
    pathinfo: devMode,
    publicPath: paths.dist.replace(paths.app, ''),
  },
  devtool: devMode ? 'eval-cheap-source-map' : false,
  optimization: {
    minimize: !devMode,
    minimizer: [
      new TerserPlugin({
        cache: true,
        exclude: /vendors/,
        extractComments: false,
        parallel: true,
        sourceMap: false,
        terserOptions: {
          compress: {
            drop_console: true,
          },
          output: {
            comments: false,
          },
        },
      }),
    ],
    removeEmptyChunks: !devMode,
    runtimeChunk: 'single',
    splitChunks: {
      cacheGroups: {
        vendors: {
          test: /[\\/]node_modules[\\/]/,
          name: 'vendors',
          chunks: 'initial',
          minChunks: 2,
          minSize: 0,
        },
        common: {
          test: /[\\/]scripts\/(App|config)[\\/]/i,
          name: 'common',
          chunks: 'all',
          minSize: 0,
        },
      },
    },
  },
  module: {
    rules: [
      {
        test: /\.m?js$/,
        exclude: /(node_modules)/,
        use: {
          loader: 'babel-loader',
          options: {
            presets: [
              [
                '@babel/preset-env',
                {
                  bugfixes: true,
                  corejs: 3,
                  useBuiltIns: 'entry',
                },
              ],
            ],
            plugins: [
              '@babel/plugin-transform-runtime',
              '@babel/plugin-proposal-class-properties',
            ],
          },
        },
      },
    ],
  },
  plugins: [
    new FriendlyErrorsPlugin(),
    new ProvidePlugin({
      $: 'jquery',
      jQuery: 'jquery',
    }),
  ],
  watchOptions: {
    ignored: ['./node_modules/'],
  },
};
<|endoftext|>