<commit_before>var TIMELINE = {};

TIMELINE.setEventPost = function($post) {
	$post.children('article').children('a').click(function() {
		var windowId = 'misskey-window-talk-' + $post.attr('data-user-id');
		var url = $post.children('article').children('a').attr('href');
		var $content = $("").attr({ src: url, seamless: true });
		openWindow(windowId, $content, '' + $post.children('article').children('header').children('h2').children('a').text(), 300, 450, true, url);
		return false;
	});

	$post.find('.reply-form').submit(function(event) {
		event.preventDefault();
		var $form = $(this);
		var $submitButton = $form.find('[type=submit]');
		$submitButton.attr('disabled', true);
		$.ajax({
			url: 'https://api.misskey.xyz/post/create',
			type: 'post',
			data: new FormData($form[0]),
			processData: false,
			contentType: false,
			dataType: 'json',
			xhrFields: {
				withCredentials: true
			}
		}).done(function() {
			$submitButton.attr('disabled', false);
			$form.text('送信しました');
		}).fail(function() {
			$submitButton.attr('disabled', false);
		});
	});

	$post.find('.image-attacher input[name=image]').change(function() {
		var $input = $(this);
		var file = $(this).prop('files')[0];
		if (!file.type.match('image.*')) return;
		var reader = new FileReader();
		reader.onload = function() {
			var $img = $('').attr('src', reader.result);
			$input.parent('.image-attacher').find('p, img').remove();
			$input.parent('.image-attacher').append($img);
		};
		reader.readAsDataURL(file);
	});

	$post.find('article > footer > .actions > .favorite > .favorite-button').click(function() {
		var $button = $(this);
		$button.attr('disabled', true);

		if ($post.attr('data-is-favorited') == 'true') {
			$post.attr('data-is-favorited', 'false')
			$.ajax('https://api.misskey.xyz/post/unfavorite', {
				type: 'delete',
				data: { 'post_id': $post.attr('data-id') },
				dataType: 'json',
				xhrFields: { withCredentials: true }
			}).done(function() {
				$button.attr('disabled', false);
			}).fail(function() {
				$button.attr('disabled', false);
				$post.attr('data-is-favorited', 'true')
			});
		} else {
			$post.attr('data-is-favorited', 'true')
			$.ajax('https://api.misskey.xyz/post/favorite', {
				type: 'post',
				data: { 'post_id': $post.attr('data-id') },
				dataType: 'json',
				xhrFields: { withCredentials: true }
			}).done(function() {
				$button.attr('disabled', false);
			}).fail(function() {
				$button.attr('disabled', false);
				$post.attr('data-is-favorited', 'false')
			});
		}
	});

	$post.find('article > footer > .actions > .repost > .repost-button').click(function() {
		var $button = $(this);
		$button.attr('disabled', true);

		if ($post.attr('data-is-reposted') == 'true') {
			$.ajax('https://api.misskey.xyz/post/unrepost', {
				type: 'delete',
				data: { 'post_id': $post.attr('data-id') },
				dataType: 'json',
				xhrFields: { withCredentials: true }
			}).done(function() {
				$button.attr('disabled', false);
				$post.attr('data-is-reposted', 'false')
			}).fail(function() {
				$button.attr('disabled', false);
			});
		} else {
			$.ajax('https://api.misskey.xyz/post/repost', {
				type: 'post',
				data: { 'post_id': $post.attr('data-id') },
				dataType: 'json',
				xhrFields: { withCredentials: true }
			}).done(function() {
				$button.attr('disabled', false);
				$post.attr('data-is-reposted', 'true')
			}).fail(function() {
				$button.attr('disabled', false);
			});
		}
	});

	$post.click(function(event) {
		if ($(event.target).is('input') ||
		$(event.target).is('textarea') ||
		$(event.target).is('button') ||
		$(event.target).is('i') ||
		$(event.target).is('time') ||
		$(event.target).is('a')) return;
		if (document.getSelection().toString() == '') {
			if ($(this).find('article > .article-main > footer').css('display') === 'none') {
				$('.timeline > .statuses > .status > .status.article > article > .talk > i').each(function() {
					$(this).show(200);
				});
				$('.timeline > .statuses > .status > .status.article > article > .talk > .statuses').each(function() {
					$(this).hide(200);
				});
				$('.timeline > .statuses > .status > .status.article > article > .article-main > footer').each(function() {
					$(this).hide(200);
				});
				$(this).find('article > .article-main > .talk > i').hide(200);
				$(this).find('article > .article-main > .talk > .statuses').show(200);
				$(this).find('article > .article-main > footer').show(200);
				var text = $(this).find('article > .article-main > footer .reply-form textarea').val();
				$(this).find('article > .article-main > footer .reply-form textarea').val('');
				$(this).find('article > .article-main > footer .reply-form textarea').focus().val(text);
			} else {
				$(this).find('article > .article-main > .talk > i').show(200);
				$(this).find('article > .article-main > .talk > .statuses').hide(200);
				$(this).find('article > .article-main > footer').hide(200);
			}
		}
	});

	/*
	$post.dblclick(function (event) {
	if ($(event.target).is('input') || $(event.target).is('textarea')) return;
	window.open('https://misskey.xyz/' + $(this).attr('data-user-screen_name') + '/post/' + $(this).attr('data-id'));
	});
	*/

}

$(function() {
	$('.timeline .status .status.article').each(function() {
		TIMELINE.setEventPost($(this));
	});
});<commit_msg>Fix bugs<commit_after>var TIMELINE = {};

TIMELINE.setEventPost = function($post) {
	$post.children('article').children('a').click(function() {
		var windowId = 'misskey-window-talk-' + $post.attr('data-user-id');
		var url = $post.children('article').children('a').attr('href');
		var $content = $("").attr({ src: url, seamless: true });
		openWindow(windowId, $content, '' + $post.children('article').children('header').children('h2').children('a').text(), 300, 450, true, url);
		return false;
	});

	$post.find('.reply-form').submit(function(event) {
		event.preventDefault();
		var $form = $(this);
		var $submitButton = $form.find('[type=submit]');
		$submitButton.attr('disabled', true);
		$.ajax({
			url: 'https://api.misskey.xyz/status/update',
			type: 'post',
			data: new FormData($form[0]),
			processData: false,
			contentType: false,
			dataType: 'json',
			xhrFields: {
				withCredentials: true
			}
		}).done(function() {
			$submitButton.attr('disabled', false);
			$form.text('送信しました');
		}).fail(function() {
			$submitButton.attr('disabled', false);
		});
	});

	$post.find('.image-attacher input[name=image]').change(function() {
		var $input = $(this);
		var file = $(this).prop('files')[0];
		if (!file.type.match('image.*')) return;
		var reader = new FileReader();
		reader.onload = function() {
			var $img = $('').attr('src', reader.result);
			$input.parent('.image-attacher').find('p, img').remove();
			$input.parent('.image-attacher').append($img);
		};
		reader.readAsDataURL(file);
	});

	$post.find('article > footer > .actions > .favorite > .favorite-button').click(function() {
		var $button = $(this);
		$button.attr('disabled', true);

		if ($post.attr('data-is-favorited') == 'true') {
			$post.attr('data-is-favorited', 'false')
			$.ajax('https://api.misskey.xyz/status/unfavorite', {
				type: 'delete',
				data: { 'status-id': $post.attr('data-id') },
				dataType: 'json',
				xhrFields: { withCredentials: true }
			}).done(function() {
				$button.attr('disabled', false);
			}).fail(function() {
				$button.attr('disabled', false);
				$post.attr('data-is-favorited', 'true')
			});
		} else {
			$post.attr('data-is-favorited', 'true')
			$.ajax('https://api.misskey.xyz/status/favorite', {
				type: 'post',
				data: { 'status-id': $post.attr('data-id') },
				dataType: 'json',
				xhrFields: { withCredentials: true }
			}).done(function() {
				$button.attr('disabled', false);
			}).fail(function() {
				$button.attr('disabled', false);
				$post.attr('data-is-favorited', 'false')
			});
		}
	});

	$post.find('article > footer > .actions > .repost > .repost-button').click(function() {
		var $button = $(this);
		$button.attr('disabled', true);

		if ($post.attr('data-is-reposted') == 'true') {
			$.ajax('https://api.misskey.xyz/status/unrepost', {
				type: 'delete',
				data: { 'status-id': $post.attr('data-id') },
				dataType: 'json',
				xhrFields: { withCredentials: true }
			}).done(function() {
				$button.attr('disabled', false);
				$post.attr('data-is-reposted', 'false')
			}).fail(function() {
				$button.attr('disabled', false);
			});
		} else {
			$.ajax('https://api.misskey.xyz/status/repost', {
				type: 'post',
				data: { 'status-id': $post.attr('data-id') },
				dataType: 'json',
				xhrFields: { withCredentials: true }
			}).done(function() {
				$button.attr('disabled', false);
				$post.attr('data-is-reposted', 'true')
			}).fail(function() {
				$button.attr('disabled', false);
			});
		}
	});

	$post.click(function(event) {
		if ($(event.target).is('input') ||
		$(event.target).is('textarea') ||
		$(event.target).is('button') ||
		$(event.target).is('i') ||
		$(event.target).is('time') ||
		$(event.target).is('a')) return;
		if (document.getSelection().toString() == '') {
			if ($(this).find('article > .article-main > footer').css('display') === 'none') {
				$('.timeline > .statuses > .status > .status.article > article > .talk > i').each(function() {
					$(this).show(200);
				});
				$('.timeline > .statuses > .status > .status.article > article > .talk > .statuses').each(function() {
					$(this).hide(200);
				});
				$('.timeline > .statuses > .status > .status.article > article > .article-main > footer').each(function() {
					$(this).hide(200);
				});
				$(this).find('article > .article-main > .talk > i').hide(200);
				$(this).find('article > .article-main > .talk > .statuses').show(200);
				$(this).find('article > .article-main > footer').show(200);
				var text = $(this).find('article > .article-main > footer .reply-form textarea').val();
				$(this).find('article > .article-main > footer .reply-form textarea').val('');
				$(this).find('article > .article-main > footer .reply-form textarea').focus().val(text);
			} else {
				$(this).find('article > .article-main > .talk > i').show(200);
				$(this).find('article > .article-main > .talk > .statuses').hide(200);
				$(this).find('article > .article-main > footer').hide(200);
			}
		}
	});

	/*
	$post.dblclick(function (event) {
	if ($(event.target).is('input') || $(event.target).is('textarea')) return;
	window.open('https://misskey.xyz/' + $(this).attr('data-user-screen_name') + '/post/' + $(this).attr('data-id'));
	});
	*/

}

$(function() {
	$('.timeline .status .status.article').each(function() {
		TIMELINE.setEventPost($(this));
	});
});<|endoftext|>


<commit_before>var url = Npm.require("url");

// There is one connect handler used by all auth redirects.  Keep track of all
// handlers which are currently being processed by shopify so that the connect
// handler knows where to "return".
//
// uuid => { authenticatorConfig, callback }
var handlers = { };

// Install the connect handler the first time it's needed.
var installWebAppHandler = _.once(function() {
    WebApp.connectHandlers.use("/__shopify-auth", function ShopifyAuthRedirect(req, res) {
        var uri = url.parse(req.url, true);

        var code = uri.query.code;
        var signature = uri.query.signature;
        var timestamp = uri.query.timestamp;

        var pathname = uri.pathname;
        var uuid = pathname.replace("/__shopify-auth/", "");

        console.log("%s => %s", uuid, code);

        var handler = handlers[counter];
        if (!handler) {
            console.error("No handler");
            res.writeHeader(500, { });
            res.end("ErrorShopify Auth failed because no handler");
            return;
        }

        var authenticatorConfig = handler.authenticatorConfig;
        var callback = handler.callback;
        delete handlers[uuid];

        if (authenticatorConfig.post_auth_uri === "close") {
            res.writeHead(200, { });
            res.end("");
        } else {
            res.writeHead(302, {
                "Location": authenticatorConfig.post_auth_uri
            });
            res.end();
        }

        Shopify.getPermanentAccessToken(authenticatorConfig, code, callback);
    });
});

Shopify._getPermanentAccessToken = function(authenticatorConfig, code, callback) {
    var accessTokenUrl = [
        "https://",
        authenticatorConfig.shop,
        ".myshopify.com/admin/oauth/access_token"
    ].join("");

    var res = HTTP.post(accessTokenUrl, {
        data: {
            client_id: authenticatorConfig.api_key,
            client_secret: authenticatorConfig.secret,
            code: code
        }
    });

    if (res.statusCode === 200) {
        callback(null, res.data.access_token);
    } else {
        callback("Shopify access_token request failed with status " + res.statusCode);
    }
};

var asyncHandleAuthRedirect = function(authenticatorConfig, callback) {
    var redirect_uri = authenticatorConfig.redirect_uri;
    console.log("asyncHandleAuthRedirect with redirect_uri=%s", redirect_uri);

    var uri = url.parse(redirect_uri, true);
    var pathname = uri.pathname;
    var uuid = pathname.replace("/__shopify-auth/", "");

    handlers[uuid] = {
        authenticatorConfig: authenticatorConfig,
        callback: callback
    };
};

var handleAuthRedirect = Meteor.wrapAsync(asyncHandleAuthRedirect);

Meteor.methods({
    "__ShopifyHandleAuthRedirect": function(authenticatorConfig) {
        var access_token = handleAuthRedirect(authenticatorConfig);
        console.log("Got access_token:", access_token);
        return access_token;
    }
});

<commit_msg>Fix AuthRedirect kinks.<commit_after>var url = Npm.require("url");

// There is one connect handler used by all auth redirects.  Keep track of all
// handlers which are currently being processed by shopify so that the connect
// handler knows where to "return".
//
// uuid => { authenticatorConfig, callback }
var handlers = { };

// Install the connect handler the first time it's needed.
var installWebAppHandler = _.once(function() {
    WebApp.connectHandlers.use("/__shopify-auth", function ShopifyAuthRedirect(req, res) {
        var uri = url.parse(req.url, true);

        var code = uri.query.code;
        var signature = uri.query.signature;
        var timestamp = uri.query.timestamp;

        var pathname = uri.pathname;
        var uuid = pathname.replace("/__shopify-auth/", "");

        // For some reason, there's an extra slash at the start.
        uuid = uuid.replace(/^\//, "");

        var handler = handlers[uuid];
        if (!handler) {
            console.error("No handler");
            res.writeHeader(500, { });
            res.end("<title>Error</title>Shopify Auth failed because no handler");
            return;
        }

        var authenticatorConfig = handler.authenticatorConfig;
        var callback = handler.callback;
        delete handlers[uuid];

        if (authenticatorConfig.post_auth_uri === "close") {
            res.writeHead(200, { });
            res.end("");
        } else {
            res.writeHead(302, {
                "Location": authenticatorConfig.post_auth_uri
            });
            res.end();
        }

        Shopify._getPermanentAccessToken(authenticatorConfig, code, callback);
    });
});

Shopify._getPermanentAccessToken = function(authenticatorConfig, code, callback) {
    var accessTokenUrl = [
        "https://",
        authenticatorConfig.shop,
        ".myshopify.com/admin/oauth/access_token"
    ].join("");

    var res = HTTP.post(accessTokenUrl, {
        data: {
            client_id: authenticatorConfig.api_key,
            client_secret: authenticatorConfig.secret,
            code: code
        }
    });

    if (res.statusCode === 200) {
        callback(null, res.data.access_token);
    } else {
        callback("Shopify access_token request failed with status " + res.statusCode);
    }
};

var asyncHandleAuthRedirect = function(authenticatorConfig, callback) {
    var redirect_uri = authenticatorConfig.redirect_uri;

    var uri = url.parse(redirect_uri, true);
    var pathname = uri.pathname;
    var uuid = pathname.replace("/__shopify-auth/", "");

    handlers[uuid] = {
        authenticatorConfig: authenticatorConfig,
        callback: callback
    };
};

var handleAuthRedirect = Meteor.wrapAsync(asyncHandleAuthRedirect);

Meteor.methods({
    "__ShopifyHandleAuthRedirect": function(authenticatorConfig) {
        // Install the web app handler in case it hasn't already been installed
        installWebAppHandler();

        var access_token = handleAuthRedirect(authenticatorConfig);
        return access_token;
    }
});

<|endoftext|>


<commit_before>/*
 * Copyright 2015 the Mimu Authors (Dan Bornstein et alia).
 * Licensed AS IS and WITHOUT WARRANTY under the Apache License,
 * Version 2.0. Details: <http://www.apache.org/licenses/LICENSE-2.0>
 */

"use strict";

// Canvas-drawn widget shell. This class provides the basic mechanism for
// a complex widget to be built out within a specified container node
// (typically a `div`). Drawing is assumed to be done by layering a foreground
// and background canvas, where the former is more actively modified than the
// latter. Subclasses are expected to fill in rendering functions and event
// handling as desired.
//
// This class takes care of creating the foreground and background canvases,
// sizing them such that when rendered they have square pixels, and adding them
// to the DOM. It also has a few utilities (e.g. accessing the style) to help
// simplify subclasses.
class CanvasWidget {
    // Takes a DOM `node` to insert into. The node should be an empty container.
    // `cssClass` is the class that will be assigned to a `div` which will be
    // used as the source for style information.
    constructor(node, cssClass) {
        var doc = node.ownerDocument;
        var style;

        // Make a `div` to hold the two canvases. It has to be set for
        // explicit relative positioning, otherwise the child nodes "leak"
        // out.
        var div = doc.createElement("div");
        div.className = cssClass;
        style = div.style;
        style.position = "relative";
        style.width = "100%";
        style.height = "100%";
        this._styleNode = div;
        node.appendChild(div);

        // Figure out the dimensions of the canvas based on those of the `div`,
        // ensuring the aspect ratio remains the same so as to end up with
        // square pixels.
        var width = div.clientWidth * 1.5;
        var height = (width / div.clientWidth) * div.clientHeight;

        // The fixed background canvas.
        this._background = doc.createElement("canvas");
        this._background.width = width;
        this._background.height = height;
        style = this._background.style;
        style.position = "absolute";
        style.top = "0";
        style.left = "0";
        style.width = "100%";
        style.height = "100%";
        style.zIndex = "1";

        // The main display canvas.
        this._foreground = doc.createElement("canvas");
        this._foreground.width = width;
        this._foreground.height = height;
        style = this._foreground.style;
        style.position = "absolute";
        style.top = "0";
        style.left = "0";
        style.width = "100%";
        style.height = "100%";
        style.zIndex = "2";
        style.opacity = "0.9";

        div.appendChild(this.background);
        div.appendChild(this.foreground);
    }

    // The foreground canvas.
    get foreground() {
        return this._foreground;
    }

    // The background canvas.
    get background() {
        return this._background;
    }

    // Get the computed style. The result will be read-only.
    getComputedStyle() {
        return StyleUtil.getComputedStyle(this._styleNode);
    }

    // Get a modifiable clone of the computed style.
    cloneComputedStyle() {
        return StyleUtil.cloneComputedStyle(this._styleNode);
    }

    // Set up periodic auto-refresh of the background. This is mostly of use
    // as a hackish way to be able to react to style changes (including notably
    // font loading) without getting too fancy.
    autoRefreshBackground() {
        var outerThis = this;
        var view = this._styleNode.ownerDocument.defaultView;

        function refreshBackground() {
            // We add a little bit of randomness to the delay time to avoid
            // having a massive global heartbeat.
            outerThis.renderBackground();
            view.setTimeout(refreshBackground, 1000 + (Math.random() * 500));
        }
        refreshBackground();
    }

    // Render the background. Subclasses are expected to override this.
    renderBackground() {
        var canvas = this.background;
        var style = StyleUtil.cloneComputedStyle(this._styleNode);
        var ctx = canvas.getContext("2d");

        ctx.fillStyle = Color.parse(style.backgroundColor);
        ctx.fillRect(0, 0, canvas.width, canvas.height);
    }

    // Render the foreground. Subclasses are expected to override this.
    render() {
        var canvas = this.foreground;
        var ctx = canvas.getContext("2d");

        ctx.clearRect(0, 0, width, height);
    }
}
<commit_msg>Dynamically resize the canvases.<commit_after>/*
 * Copyright 2015 the Mimu Authors (Dan Bornstein et alia).
 * Licensed AS IS and WITHOUT WARRANTY under the Apache License,
 * Version 2.0. Details: <http://www.apache.org/licenses/LICENSE-2.0>
 */

"use strict";

// Canvas-drawn widget shell. This class provides the basic mechanism for
// a complex widget to be built out within a specified container node
// (typically a `div`). Drawing is assumed to be done by layering a foreground
// and background canvas, where the former is more actively modified than the
// latter. Subclasses are expected to fill in rendering functions and event
// handling as desired.
//
// This class takes care of creating the foreground and background canvases,
// sizing them such that when rendered they have square pixels, and adding them
// to the DOM. It also has a few utilities (e.g. accessing the style) to help
// simplify subclasses.
class CanvasWidget {
    // Takes a DOM `node` to insert into. The node should be an empty container.
    // `cssClass` is the class that will be assigned to a `div` which will be
    // used as the source for style information.
    constructor(node, cssClass) {
        var doc = node.ownerDocument;
        var style;

        // Make a `div` to hold the two canvases. It has to be set for
        // explicit relative positioning, otherwise the child nodes "leak"
        // out.
        var div = doc.createElement("div");
        div.className = cssClass;
        style = div.style;
        style.position = "relative";
        style.width = "100%";
        style.height = "100%";
        this._styleNode = div;
        node.appendChild(div);

        // The fixed background canvas.
        this._background = doc.createElement("canvas");
        style = this._background.style;
        style.position = "absolute";
        style.top = "0";
        style.left = "0";
        style.width = "100%";
        style.height = "100%";
        style.zIndex = "1";

        // The main display canvas.
        this._foreground = doc.createElement("canvas");
        style = this._foreground.style;
        style.position = "absolute";
        style.top = "0";
        style.left = "0";
        style.width = "100%";
        style.height = "100%";
        style.zIndex = "2";
        style.opacity = "0.9";

        this._adjustCanvasSizes();
        div.appendChild(this.background);
        div.appendChild(this.foreground);

        // Notice if the view gets resized, and resize the canvases accordingly.
        var outerThis = this;
        doc.defaultView.addEventListener("resize", function() {
            outerThis._adjustCanvasSizes();
        });
    }

    // Set up the dimensions of the canvases.
    _adjustCanvasSizes() {
        // Copy the dimensions of the canvas from the `div`, which ensures the
        // aspect ratio remains the same. Thus, the canvases will have square
        // pixels.
        var node = this._styleNode;
        var fg = this._foreground;
        var bg = this._background;
        var width = node.clientWidth;
        var height = node.clientHeight;

        if ((width != fg.width) || (height != fg.height)) {
            fg.width = width;
            fg.height = height;
            bg.width = width;
            bg.height = height;
            this.renderBackground();
        }
    }

    // The foreground canvas.
    get foreground() {
        return this._foreground;
    }

    // The background canvas.
    get background() {
        return this._background;
    }

    // Get the computed style. The result will be read-only.
    getComputedStyle() {
        return StyleUtil.getComputedStyle(this._styleNode);
    }

    // Get a modifiable clone of the computed style.
    cloneComputedStyle() {
        return StyleUtil.cloneComputedStyle(this._styleNode);
    }

    // Set up periodic auto-refresh of the background. This is mostly of use
    // as a hackish way to be able to react to style changes (including notably
    // font loading) without getting too fancy.
    autoRefreshBackground() {
        var outerThis = this;
        var view = this._styleNode.ownerDocument.defaultView;

        function refreshBackground() {
            // We add a little bit of randomness to the delay time to avoid
            // having a massive global heartbeat.
            outerThis.renderBackground();
            view.setTimeout(refreshBackground, 1000 + (Math.random() * 500));
        }
        refreshBackground();
    }

    // Render the background. Subclasses are expected to override this.
    renderBackground() {
        var canvas = this.background;
        var style = StyleUtil.cloneComputedStyle(this._styleNode);
        var ctx = canvas.getContext("2d");

        ctx.fillStyle = Color.parse(style.backgroundColor);
        ctx.fillRect(0, 0, canvas.width, canvas.height);
    }

    // Render the foreground. Subclasses are expected to override this.
    render() {
        var canvas = this.foreground;
        var ctx = canvas.getContext("2d");

        ctx.clearRect(0, 0, width, height);
    }
}
<|endoftext|>


<commit_before>/* eslint no-console:0 */
// TODO: remove eslint-disable

import { URL } from 'url'
import fetch from 'node-fetch'
import aws4 from 'aws4'

const redisAccessEndpoint = `${process.env.REDIS_SERVICE_ENDPOINT}/redis/`

// TODO:
//   - call to get/set values
//   - gracefully handle Redis downtime or missing cache value
const callRedis = async data => {
  let responseData
  try {
    // Add AWS IAM authorization.
    // https://docs.aws.amazon.com/general/latest/gr/sigv4_signing.html
    // https://docs.aws.amazon.com/general/latest/gr/signature-v4-examples.html#signature-v4-examples-javascript
    // https://github.com/mhart/aws4
    const endpointURL = new URL(redisAccessEndpoint)
    const body = JSON.stringify(data)
    const authorizationInfo = aws4.sign({
      host: endpointURL.host,
      path: endpointURL.pathname,
      method: 'POST',
      body,
    })

    console.log('===== Calling Redis service =====', redisAccessEndpoint)
    const response = await fetch(redisAccessEndpoint, {
      method: 'POST',
      headers: {
        ...authorizationInfo.headers,
      },
      body,
    })
    responseData = await response.json()
  } catch (e) {
    console.log('Error calling Redis service', e)
  }
  console.log('===== Redis service response data =====', responseData)
}

export default callRedis
<commit_msg>Remove trailing slash<commit_after>/* eslint no-console:0 */
// TODO: remove eslint-disable

import { URL } from 'url'
import fetch from 'node-fetch'
import aws4 from 'aws4'

const redisAccessEndpoint = `${process.env.REDIS_SERVICE_ENDPOINT}/redis`

// TODO:
//   - call to get/set values
//   - gracefully handle Redis downtime or missing cache value
const callRedis = async data => {
  let responseData
  try {
    // Add AWS IAM authorization.
    // https://docs.aws.amazon.com/general/latest/gr/sigv4_signing.html
    // https://docs.aws.amazon.com/general/latest/gr/signature-v4-examples.html#signature-v4-examples-javascript
    // https://github.com/mhart/aws4
    const endpointURL = new URL(redisAccessEndpoint)
    const body = JSON.stringify(data)
    const authorizationInfo = aws4.sign({
      host: endpointURL.host,
      path: endpointURL.pathname,
      method: 'POST',
      body,
    })

    console.log('===== Calling Redis service =====', redisAccessEndpoint)
    const response = await fetch(redisAccessEndpoint, {
      method: 'POST',
      headers: {
        ...authorizationInfo.headers,
      },
      body,
    })
    responseData = await response.json()
  } catch (e) {
    console.log('Error calling Redis service', e)
  }
  console.log('===== Redis service response data =====', responseData)
}

export default callRedis
<|endoftext|>


<commit_before>'use strict';

var shell = require('shelljs');
var fs = require('fs');
var zlib = require('zlib');
var util = require('../util');
var regexLastVer = /v(?=[^v]*$).+/g;
var regexDupLines = /^(.*)(\r?\n\1)+$/gm;
var regexKey = /(https?:\/\/|:)+(?=[^:]*$)[a-z0-9]+(@)/gmi;
var regexHost = /^https?\:\/\/([^\/?#]+)(?:[\/?#]|$)/i;

/**
 * When a commit message contains "release v" followed by a version number
 * (major.minor.patch) a tagged release will be issued
 *
 * @param grunt
 *            the grunt instance
 */
module.exports = function(grunt) {

	var commit = null;

	grunt.registerTask('release',
			'Release bundle using commit message (if present)', function() {
				var options = this.options({
					src : process.cwd(),
					commitMessage : '',
					destBranch : 'gh-pages',
					destDir : 'dist',
					chgLog : 'HISTORY.md',
					authors : 'AUTHORS.md',
					distAsset : true
				});
				release.call(this, options);
			});

	/**
	 * Checks for release commit message and performs release
	 *
	 * @param options
	 *            the grunt options
	 */
	function release(options) {
		var chgLogRtn = '';
		var authorsRtn = '';

		// Capture commit message
		commit = util.getCommit(grunt, options.commitMessage);
		grunt.log.writeln('Commit message: ' + commit.message);
		if (!commit.version) {
			return;
		}

		// TODO : verify release version is less than last release version
		var lastVerTag = runCmd('git describe --abbrev=0 --tags');
		grunt.log.writeln('Preparing release: ' + commit.version
				+ ' (last release: ' + lastVerTag + ')');
		var relMsg = commit.message + ' ' + util.skipRef('ci');

		// Generate change log for release using all messages since last
		// tag/release
		var chgLogPath = options.destDir + '/' + options.chgLog;
		chgLogRtn = runCmd('git --no-pager log ' + lastVerTag
				+ '..HEAD --pretty=format:"  * %s" > ' + chgLogPath, null,
				false, true, chgLogPath);

		// Generate list of authors/contributors since last tag/release
		var authorsPath = options.destDir + '/' + options.authors;
		authorsRtn = runCmd('git log --all --format="%aN <%aE>" | sort -u > '
				+ authorsPath, null, true, false, authorsPath);

		// Setup
		var link = '${GH_TOKEN}@github.com/' + commit.slug + '.git';
		runCmd('git config --global user.email "travis@travis-ci.org"');
		runCmd('git config --global user.name "travis"');
		runCmd('git remote rm origin');
		runCmd('git remote add origin https://' + commit.username + ':' + link);
		//runCmd('git checkout master');

		// Commit changes to master
		runCmd('git add --force ' + options.destDir);
		runCmd('git commit -m "' + relMsg + '"');
		// runCmd('git push -f origin master');

		// Create distribution assets
		var distAsset = commit.reponame + '-' + commit.version + '-dist.zip';
		runCmd('git archive -v -o ' + distAsset + ' --format=zip master '
				+ options.destDir);

		// Tag release
		runCmd('git tag -f -a ' + commit.versionTag + ' -m "' + chgLogRtn + '"');
		runCmd('git push -f origin ' + commit.versionTag);
		grunt.log.writeln('Released: ' + commit.version + ' in '
				+ options.destDir);

		// Distribute archive asset for tagged release
		var done = this.async();
		uploadDistAsset(distAsset, commit, options, function(json, e) {
			if (e) {
				grunt.log.error(grunt.util.error('Failed to upload asset '
						+ distAsset, e));
			} else if (cf.state != 'uploaded') {
				e = grunt.util.error('Asset upload failed with state: '
						+ cf.state);
				grunt.log.error(e);
			} else {
				grunt.log.writeln('Distributed/' + cf.state + ' ' + distAsset
						+ ' asset');
			}
			done(e);
		});

		// Publish site
		/*
		 * runCmd('cd ..'); runCmd('git clone --quiet --branch=' +
		 * options.destBranch + ' https://' + link + ' ' + options.destBranch + ' >
		 * /dev/null'); runCmd('cd ' + options.destBranch); runCmd('git ls-files |
		 * xargs rm'); // remove all tracked files runCmd('git commit -m "' +
		 * relMsg + '"');
		 *
		 * runCmd('cp -a ../' + commit.reponame + '/' + options.destBranch + '/*
		 * .'); // runCmd('git checkout master -- ' + options.destDir);
		 * runCmd('git add -A'); runCmd('git commit -m "' + relMsg + '"');
		 * runCmd('git push -f origin ' + options.destBranch + ' > /dev/null');
		 */
	}

	/**
	 * Executes a shell command
	 *
	 * @param cmd
	 *            the command string to execute
	 * @param wpath
	 *            the optional path/file to write the results to
	 * @param nofail
	 *            true to prevent throwing an error when the command fails to
	 *            execute
	 * @param shortlog
	 *            true to log only the length of the output
	 * @param dupsPath
	 *            path to the command output that will be read, duplicate entry
	 *            lines removed and re-written
	 */
	function runCmd(cmd, wpath, nofail, shortlog, dupsPath) {
		grunt.log.writeln(cmd);
		var rtn = null;
		if (typeof cmd === 'string') {
			rtn = shell.exec(cmd, {
				silent : true
			});
		} else {
			rtn = shell[cmd.shell].apply(shell, cmd.args);
		}
		if (rtn.code !== 0) {
			var e = 'Error "' + rtn.code + '" for commit number '
					+ commit.number + ' ' + rtn.output;
			if (nofail) {
				grunt.log.error(e);
				return;
			}
			throw grunt.util.error(e);
		}
		var output = rtn.output;
		if (dupsPath) {
			// remove duplicate lines
			output = grunt.file.read(dupsPath).replace(regexDupLines, '$1');
			grunt.file.write(dupsPath, output);
		}
		if (output && wpath) {
			grunt.file.write(wpath, output);
		}
		if (output) {
			grunt.log.writeln(output.replace(regexKey, '$1[SECURE]$2'));
		}
		return output;
	}

	/**
	 * Uploads the file asset and associates it with a specified tagged release
	 * see http://developer.github.com/v3/repos/releases/#upload-a-release-asset
	 *
	 * @param filePath
	 *            the path to the file that will be added
	 * @param commit
	 *            the commit the asset is for
	 * @param options
	 *            the task options
	 * @param cb
	 *            the call back function (passed parameters: JSON response,
	 *            error)
	 */
	function uploadDistAsset(filePath, commit, options, cb) {
		var https = require('https');
		var opts = {
			hostname : 'api.github.com',
			port : 443,
			path : '/repos/' + commit.slug + '/releases',
			method : 'GET'
		};
		var req = https.request(opts, function(res) {
			var sc = res.statusCode;
			res.on('data', function(d) {
				var rls = null, rl = null;
				try {
					rls = JSON.parse(d);
					for ( var i = 0; i < rls.length; i++) {
						if (rls[i].tag_name == commit.versionTag) {
							// upload asset
							rl = rls[i];
							opts.method = 'POST';
							opts.path = rl.upload_url.replace(regexHost,
									function(m, h) {
										opts.hostname = h;
										return '/';
									});
							opts.path = opts.path.replace(/{(\?.+)}/, '$1='
									+ commit.versionTag);
							opts.headers = {
								'Content-Type' : 'application/zip'
							};
							var req2 = https.request(opts, function(res2) {
								res.on('data', function(d2) {
									var cf = null;
									try {
										cf = JSON.parse(d2);
										try {
											cb(cf);
										} catch (e) {
											// consume
										}
									} catch (e) {
										cb(cf || rl, e);
									}
								});
							});
							req2.on('error', function(e) {
								cb(rl, e);
							});
							reqWrite(req2, filePath);
							break;
						}
					}
				} catch (e) {
					cb(rl || rls, e);
				}
			});
		});
		req.end();
		req.on('error', function(e) {
			cb(null, e);
		});
	}

	/**
	 * Writes file to the specified stream
	 *
	 * @param stream
	 *            the stream to write to
	 * @param filePath
	 *            the file path to read from
	 */
	function reqWrite(stream, filePath) {
		var chunkSize = 64 * 1024;
		var bufSize = 64 * 1024;
		var bufPos = 0;
		var buf = new Buffer(bufSize);
		fs.createReadStream(filePath, {
			'flags' : 'r',
			'encoding' : 'binary',
			'mode' : 438, /* 0666 */
			'bufferSize' : chunkSize
		}).addListener("data", function(chunk) {
			// Since this is binary data, we cat use String.prototype.length We
			// *WANT* the number of chars, since node uses UTF-16 for bin data
			// meaning one byte bin data = one char = two bytes.
			var bufNextPos = bufPos + chunk.length;
			if (bufNextPos == bufSize) {
				buf.write(chunk, 'binary', bufPos);
				stream.write(buf);
				bufPos = 0;
			} else {
				buf.write(chunk, 'binary', bufPos);
				bufPos = bufNextPos;
			}
		}).addListener("close", function() {
			if (bufPos != 0) {
				stream.write(buf.slice(0, bufPos));
				stream.end();
			} else {
				stream.close();
			}
		});
	}
};<commit_msg>Release v1.0.0-beta.4 [skip saucelabs-qunit]<commit_after>'use strict';

var shell = require('shelljs');
var fs = require('fs');
var zlib = require('zlib');
var util = require('../util');
var regexLastVer = /v(?=[^v]*$).+/g;
var regexLines = /\r?\n/g;
var regexDupLines = /^(.*)(\r?\n\1)+$/gm;
var regexKey = /(https?:\/\/|:)+(?=[^:]*$)[a-z0-9]+(@)/gmi;
var regexHost = /^https?\:\/\/([^\/?#]+)(?:[\/?#]|$)/i;

/**
 * When a commit message contains "release v" followed by a version number
 * (major.minor.patch) a tagged release will be issued
 *
 * @param grunt
 *            the grunt instance
 */
module.exports = function(grunt) {

	var commit = null;

	grunt.registerTask('release',
			'Release bundle using commit message (if present)', function() {
				var options = this.options({
					src : process.cwd(),
					commitMessage : '',
					destBranch : 'gh-pages',
					destDir : 'dist',
					chgLog : 'HISTORY.md',
					authors : 'AUTHORS.md',
					distAsset : true
				});
				release.call(this, options);
			});

	/**
	 * Checks for release commit message and performs release
	 *
	 * @param options
	 *            the grunt options
	 */
	function release(options) {
		var chgLogRtn = '';
		var authorsRtn = '';

		// Capture commit message
		commit = util.getCommit(grunt, options.commitMessage);
		grunt.log.writeln('Commit message: ' + commit.message);
		if (!commit.version) {
			return;
		}

		// TODO : verify release version is less than last release version
		var lastVerTag = runCmd('git describe --abbrev=0 --tags').replace(
				regexLines, '');
		grunt.log.writeln('Preparing release: ' + commit.version
				+ ' (last release: ' + lastVerTag + ')');
		var relMsg = commit.message + ' ' + util.skipRef('ci');

		// Generate change log for release using all messages since last
		// tag/release
		var chgLogPath = options.destDir + '/' + options.chgLog;
		chgLogRtn = runCmd('git --no-pager log ' + lastVerTag
				+ '..HEAD --pretty=format:"  * %s" > ' + chgLogPath, null,
				false, true, chgLogPath);

		// Generate list of authors/contributors since last tag/release
		var authorsPath = options.destDir + '/' + options.authors;
		authorsRtn = runCmd('git log --all --format="%aN <%aE>" | sort -u > '
				+ authorsPath, null, true, false, authorsPath);

		// Setup
		var link = '${GH_TOKEN}@github.com/' + commit.slug + '.git';
		runCmd('git config --global user.email "travis@travis-ci.org"');
		runCmd('git config --global user.name "travis"');
		runCmd('git remote rm origin');
		runCmd('git remote add origin https://' + commit.username + ':' + link);
		// runCmd('git checkout master');

		// Commit changes to master
		runCmd('git add --force ' + options.destDir);
		runCmd('git commit -m "' + relMsg + '"');
		// runCmd('git push -f origin master');

		// Create distribution assets
		var distAsset = commit.reponame + '-' + commit.version + '-dist.zip';
		runCmd('git archive -v -o ' + distAsset + ' --format=zip master '
				+ options.destDir);

		// Tag release
		runCmd('git tag -f -a ' + commit.versionTag + ' -m "' + chgLogRtn + '"');
		runCmd('git push -f origin ' + commit.versionTag);
		grunt.log.writeln('Released: ' + commit.version + ' in '
				+ options.destDir);

		// Distribute archive asset for tagged release
		var done = this.async();
		uploadDistAsset(distAsset, commit, options, function(json, e) {
			if (e) {
				grunt.log.error(grunt.util.error('Failed to upload asset '
						+ distAsset, e));
			} else if (cf.state != 'uploaded') {
				e = grunt.util.error('Asset upload failed with state: '
						+ cf.state);
				grunt.log.error(e);
			} else {
				grunt.log.writeln('Distributed/' + cf.state + ' ' + distAsset
						+ ' asset');
			}
			done(e);
		});

		// Publish site
		/*
		 * runCmd('cd ..'); runCmd('git clone --quiet --branch=' +
		 * options.destBranch + ' https://' + link + ' ' + options.destBranch + ' >
		 * /dev/null'); runCmd('cd ' + options.destBranch); runCmd('git ls-files |
		 * xargs rm'); // remove all tracked files runCmd('git commit -m "' +
		 * relMsg + '"');
		 *
		 * runCmd('cp -a ../' + commit.reponame + '/' + options.destBranch + '/*
		 * .'); // runCmd('git checkout master -- ' + options.destDir);
		 * runCmd('git add -A'); runCmd('git commit -m "' + relMsg + '"');
		 * runCmd('git push -f origin ' + options.destBranch + ' > /dev/null');
		 */
	}

	/**
	 * Executes a shell command
	 *
	 * @param cmd
	 *            the command string to execute
	 * @param wpath
	 *            the optional path/file to write the results to
	 * @param nofail
	 *            true to prevent throwing an error when the command fails to
	 *            execute
	 * @param shortlog
	 *            true to log only the length of the output
	 * @param dupsPath
	 *            path to the command output that will be read, duplicate entry
	 *            lines removed and re-written
	 */
	function runCmd(cmd, wpath, nofail, shortlog, dupsPath) {
		grunt.log.writeln(cmd);
		var rtn = null;
		if (typeof cmd === 'string') {
			rtn = shell.exec(cmd, {
				silent : true
			});
		} else {
			rtn = shell[cmd.shell].apply(shell, cmd.args);
		}
		if (rtn.code !== 0) {
			var e = 'Error "' + rtn.code + '" for commit number '
					+ commit.number + ' ' + rtn.output;
			if (nofail) {
				grunt.log.error(e);
				return;
			}
			throw grunt.util.error(e);
		}
		var output = rtn.output;
		if (dupsPath) {
			// remove duplicate lines
			output = grunt.file.read(dupsPath).replace(regexDupLines, '$1');
			grunt.file.write(dupsPath, output);
		}
		if (output && wpath) {
			grunt.file.write(wpath, output);
		}
		if (output) {
			grunt.log.writeln(output.replace(regexKey, '$1[SECURE]$2'));
		}
		return output || '';
	}

	/**
	 * Uploads the file asset and associates it with a specified tagged release
	 * see http://developer.github.com/v3/repos/releases/#upload-a-release-asset
	 *
	 * @param filePath
	 *            the path to the file that will be added
	 * @param commit
	 *            the commit the asset is for
	 * @param options
	 *            the task options
	 * @param cb
	 *            the call back function (passed parameters: JSON response,
	 *            error)
	 */
	function uploadDistAsset(filePath, commit, options, cb) {
		var https = require('https');
		var opts = {
			hostname : 'api.github.com',
			port : 443,
			path : '/repos/' + commit.slug + '/releases',
			method : 'GET'
		};
		var req = https.request(opts, function(res) {
			var sc = res.statusCode;
			res.on('data', function(d) {
				var rls = null, rl = null;
				try {
					rls = JSON.parse(d);
					for (var i = 0; i < rls.length; i++) {
						if (rls[i].tag_name == commit.versionTag) {
							// upload asset
							rl = rls[i];
							opts.method = 'POST';
							opts.path = rl.upload_url.replace(regexHost,
									function(m, h) {
										opts.hostname = h;
										return '/';
									});
							opts.path = opts.path.replace(/{(\?.+)}/, '$1='
									+ commit.versionTag);
							opts.headers = {
								'Content-Type' : 'application/zip'
							};
							var req2 = https.request(opts, function(res2) {
								res.on('data', function(d2) {
									var cf = null;
									try {
										cf = JSON.parse(d2);
										try {
											cb(cf);
										} catch (e) {
											// consume
										}
									} catch (e) {
										cb(cf || rl, e);
									}
								});
							});
							req2.on('error', function(e) {
								cb(rl, e);
							});
							reqWrite(req2, filePath);
							break;
						}
					}
				} catch (e) {
					cb(rl || rls, e);
				}
			});
		});
		req.end();
		req.on('error', function(e) {
			cb(null, e);
		});
	}

	/**
	 * Writes file to the specified stream
	 *
	 * @param stream
	 *            the stream to write to
	 * @param filePath
	 *            the file path to read from
	 */
	function reqWrite(stream, filePath) {
		var chunkSize = 64 * 1024;
		var bufSize = 64 * 1024;
		var bufPos = 0;
		var buf = new Buffer(bufSize);
		fs.createReadStream(filePath, {
			'flags' : 'r',
			'encoding' : 'binary',
			'mode' : 438, /* 0666 */
			'bufferSize' : chunkSize
		}).addListener("data", function(chunk) {
			// Since this is binary data, we cat use String.prototype.length We
			// *WANT* the number of chars, since node uses UTF-16 for bin data
			// meaning one byte bin data = one char = two bytes.
			var bufNextPos = bufPos + chunk.length;
			if (bufNextPos == bufSize) {
				buf.write(chunk, 'binary', bufPos);
				stream.write(buf);
				bufPos = 0;
			} else {
				buf.write(chunk, 'binary', bufPos);
				bufPos = bufNextPos;
			}
		}).addListener("close", function() {
			if (bufPos != 0) {
				stream.write(buf.slice(0, bufPos));
				stream.end();
			} else {
				stream.close();
			}
		});
	}
};<|endoftext|>


<commit_before>var pg = require('pg');
var json_handler = require('./json_handler');
var Constants = require('../constants/constants.js');
var cb_handler = require('./cb_handler.js');

var urlDB = Constants.POSTGRE_URL_DB;

pg.defaults.ssl = true;

var db_handler = {}

db_handler.atenderQuery = function (req, res, next) {
  pg.connect(urlDB,function(err,client, done) {
    if (err) {
      done();
      db_handler.sendError(err,res,500);
      return false;
    } else {
      next.set_ClientDone(client,done);
      next.launch();
    }
  });
};

db_handler.getUsers = function (req, res, param, client, done) {
  //TODO: JOIN con tabla de users_interests
  var query = client.query("SELECT * FROM users",function(err, result) {
    done(); //Devuelvo el cliente al pool xq no necesito más la conexion
    if (err) {
      db_handler.sendError(err,res,500);
    } else {
      query.on('row', function(row,result) {result.addRow(row);});
      query.on('end', function(result) {
        var respuesta = json_handler.armarJsonListaUsuarios(result);
        res.json(respuesta).end();
      })
    }
  });
}

db_handler.getInterests = function (req, res, param, client, done) {
  var query = client.query("SELECT * FROM interests",function(err, result) {
    done();
    if (err) {
      db_handler.sendError(err,res,500);
    } else {
      query.on('row', function(row,result) {result.addRow(row);});
      query.on('end', function (result) {
        var respuesta = json_handler.armarJsonListaIntereses(result);
        res.json(respuesta).end();
      })
    }
  });
}

function createInterest(res,client,done,interest,cb) {
  var query = client.query("INSERT INTO interests (category,value) values ($1,$2) RETURNING id_interest",[interest.category,interest.value],function(err,result){
    done();
    if (err) {db_handler.sendError(err,res,500);
    } else {
      cb(result.rows[0].id_interest);
    }
  });
}

db_handler.addInterest = function (req, res, param, client, done) {
  var interest = req.body.interest;
  createInterest(res,client,done,interest,function(id_interest) {
    res.sendStatus(201).end();
  });
}

function saveUser(req,res,client,done,cb_id_user) {
  var photo_profile = "no_photo";
  var name = req.body.user.name;
  var email = req.body.user.email;
  var alias = req.body.user.alias;
  var sex = req.body.user.sex;
  var latitude = req.body.user.location.latitude;
  var longitude = req.body.user.location.longitude;
  var interests = req.body.user.interests;

  client.query(Constants.QUERY_ALTA_USUARIO,[name,email,alias,sex,latitude,longitude,photo_profile],function(err, result) {
    if (err) {
      db_handler.sendError(err,res,done,500);
    } else {
      done();
      var id_user = result.rows[0].id_user;
      cb_id_user(id_user);
    }
  });
}

/*
 * Son intereses validos los que tienen una categoria existente
 * en la tabla categories.
 * Inserto en valid_interests todos los intereses que llegan el req.body
 * cuya categoria esta en la tabla categories. Y lo devuelvo.
 */
function getValidCategories (interests,client,done,valid_categories,cb) {
  var checked = 0;
  for (var i = 0; i<interests.length;++i) {
    client.query("SELECT * FROM categories WHERE category=($1)",[interests[i].category], function(err,result){
      done();
      if (err) {db_handler.sendError(err,res,done,500);}
      else {
        if (result.rowCount > 0) {
          valid_categories.push(result.rows[0]);
        }
        if(++checked == interests.length) cb(valid_categories);
      }
    });
  }
}

function saveInterests(res,client,done,id_user,valid_interests) {
  for (var i in valid_interests) {
    client.query("SELECT * FROM interests WHERE category=($1) AND value=($2)",
    [valid_interests[i].category,valid_interests[i].value], function(err,result){
      done();
      if (err) {db_handler.sendError(err,res,done,500);}
      else {
        if (result.rowCount > 0) {
          save_one_interest(res,client,done,id_user,result.rows[0].id_interest);
        } else {
          createInterest(res,client,done,valid_interests[i],function (id_interest) {
            save_one_interest(res,client,done,id_user,id_interest);
          });
        }
      }
    });
  }
}

function save_one_interest(res,client,done,id_user,id_interest) {
  client.query("INSERT INTO users_interests (id_user,id_interest) values ($1,$2)",[id_user,id_interest],function(err,result){
    done();
    if (err) {db_handler.sendError(err,res,done,500);}
  });
}

function getValidInterests(interests,valid_categories,cb) {
  var valid_interests = [];
  var checked = 0;
  var i = 0;
  var j = 0;

  while (i<valid_categories.length) {
    while (j<interests.length) {
      if(valid_categories[i].category == interests[j].category) {
        valid_interests.push(interests[j]);
        ++i;
        ++j;
      }else{
        ++j;
      }
      if (++checked == valid_categories.length) cb(valid_interests);
    }
  }
}

db_handler.checkInterests = function (req, res, client, done, cb) {
  var interests = req.body.user.interests;
  var valid_categories = [];
  var err = null;
  var has_interests = (interests && interests.length > 0);
  if (has_interests) {
    //Verifico que la categoria exista, si existe la agrego a los intereses validos, sino no
    getValidCategories(interests,client,done,valid_categories,function (categorias_validas) {
      //Una vez que tengo las categorias que estan en la tabla categories,
      //me guardo el vector de intereses que tienen esas categorias, y el resto se descarta
      getValidInterests(interests,valid_categories, function(valid_interests){
        cb(has_interests,valid_interests,err);
      });
    });
  } else {
    //Devuelvo: Has interests false, vector vacío, y error null
    var valid_interests = [];
    cb(has_interests,valid_interests,err);
  }
}

function devolverUser(req,res,id_user,done,valid_interests) {
  done();
  var usuario = json_handler.armarJsonUsuarioNuevo(req,id_user);
  usuario.user.interests = valid_interests;
  res.json(usuario).status(201).end();
}

db_handler.addUser = function (req, res, param, client, done) {
  var email = req.body.user.email;
  var check_query = client.query("SELECT * FROM users WHERE email=($1)",[email],function (err, check_result) {
    //done(); //No se si es necesario llamarlo si todavia hay que hacer querys
    //Si existe error al consultar por email
    if (err) { db_handler.sendError(err,res,done,500);
    } else {
      var hay_email = (check_result.rowCount > 0)
      if (hay_email) {
        db_handler.sendError(Constants.ERROR_EMAIL_ALREADY_EXISTS,res,done,500);
      } else { //Si el email consultado no esta en la tabla users
        //Chequeo si hay intereses
        db_handler.checkInterests(req,res,client,done, function (has_interests,valid_interests, err) {
          if (err) { db_handler.sendError(err,res,done,500);
          } else {
            saveUser(req,res,client,done,function (id_user) {
              if (has_interests) {
                saveInterests(res,client,done,id_user,valid_interests);
              }
              devolverUser(req,res,id_user,done,valid_interests);
            });
          }
        });
      }
    }
  });
}

//Pisa todos los valores que estan en la query
//Si en el json no existen los campos, pone null
//Si el campo location no existe, crashea la app
db_handler.modifyUser = function(req, res, usrID, client, done) {
  var u = req.body.user;
  var query = client.query("UPDATE users SET name=($1),email=($2),alias=($3),sex=($4),latitude=($5),longitude=($6) WHERE id_user=($7)",
    [u.name, u.email,u.alias,u.sex, u.location.latitude,u.location.longitude, usrID],
    function (err,result) {
      db_handler.queryExitosa (err, result, res, done);
    });
}

db_handler.getUser = function (req, res, usrID, client, done) {
  var query = client.query("SELECT * FROM users WHERE id_user = ($1)",[usrID],function (err,result) {
    done();
    //Si tiro un id que no existe, la query falla y entra por aca
    if (err) { db_handler.sendError(err,res,done,404);}
    if (result.rowCount) {
      var jsonObject = json_handler.armarJsonUsuarioConsultado(result);
      res.json(jsonObject).end();
    } else {
      //No se por que nunca entra aca si hago una query con un id invalido!
      res.sendStatus(404).end(); //User not found
    }
  })
}

db_handler.deleteUser = function (req, res, usrID, client, done) {
  var query = client.query("DELETE FROM users WHERE id_user=($1)",[usrID],function (err, result) {
    db_handler.queryExitosa(err,result,res, done);
  });
}

db_handler.updatePhoto = function (req, res, usrID, client, done) {
  var photo = req.body.photo;
  var query = client.query("UPDATE users SET photo_profile=($1) WHERE id_user=($2)",[photo,usrID], function (err, result) {
    db_handler.queryExitosa(err,result,res,done);
  });
}

db_handler.sendError = function (err, res, done, status) {
  if (done) {
    done();
  }
  console.log(err);
  res.json({succes: false, error: err}).status(status).end();
}

db_handler.queryExitosa = function (err, result, res, done) {
  if (err) {
    return db_handler.sendError(err,res,done,500);
  } else {
    done();
    return res.sendStatus(200).end();
  }
}

module.exports = db_handler;
<commit_msg>Close #23 - Refactor del metodo Add users, corrigiendo errores de concurrencia al iterar sobre el vector de intereses validos. Se soluciono utilizando un callback por cada elemento obtenido del ciclo for, logrando que no se pisen unos con otros los valores y permitiendo que se realicen coherentemente las querys<commit_after>var pg = require('pg');
var json_handler = require('./json_handler');
var Cons = require('../constants/constants.js');
var cb_handler = require('./cb_handler.js');

var urlDB = Cons.POSTGRE_URL_DB;

pg.defaults.ssl = true;

var db_handler = {}

db_handler.atenderQuery = function (req, res, next) {
  pg.connect(urlDB,function(err,client, done) {
    if (err) {
      db_handler.sendError(err,res,done,500);
    } else {
      next.set_ClientDone(client,done);
      next.launch();
    }
  });
};

/*
 * Son intereses validos los que tienen una categoria existente
 * en la tabla categories.
 * Inserto en valid_interests todos los intereses que llegan el req.body
 * cuya categoria esta en la tabla categories. Y lo devuelvo.
 */
function getValidCategories (interests,client,done,valid_categories,cb) {
  var checked = 0;
  for (var i = 0; i<interests.length;++i) {
    client.query("SELECT * FROM categories WHERE category=($1)",[interests[i].category], function(err,result){
      //done(); TODO: REVISAR si hay que llamar a done
      if (err) {db_handler.sendError(err,res,done,500);}
      else {
        if (result.rowCount > 0) {
          valid_categories.push(result.rows[0]);
        }
        if(++checked == interests.length) cb(valid_categories);
      }
    });
  }
}

function processInterests(valid_interests,cb) {
  for (var i in valid_interests) {
    cb(valid_interests[i].category,valid_interests[i].value);
  }
}

function createInterest(res,client,done,category,value,cb) {
  var query = client.query("INSERT INTO interests (category,value) values ($1,$2) RETURNING id_interest",[category,value],function(err,result){
    //done();
    if (err) { cb("no_id_interest","No se puede crear el interes: "+category+" - "+value);
    } else {
      cb(result.rows[0].id_interest,err);
    }
  });
}

function saveInterests(res,client,done,id_user,category,value) {
  client.query("SELECT * FROM interests WHERE category=($1) AND value=($2)",[category,value], function(err,result){
    //done();
    if (err) {db_handler.sendError(err,res,done,500);
    } else {
      if (result.rowCount > 0) {
        //Si el interes ya existe, obtengo el id_interest y lo inserto en users_interests
        save_one_interest(res,client,done,id_user,result.rows[0].id_interest);
      } else {
        //Si no existe, lo creo para obtener el id_interest e insertarlo en users_interests
        createInterest(res,client,done,category,value,function (id_interest,err) {
          if (err) {
            console.error(err);
          } else {
            save_one_interest(res,client,done,id_user,id_interest);
          }
        });
      }
    }
  });
}

function save_one_interest(res,client,done,id_user,id_interest) {
  client.query("INSERT INTO users_interests (id_user,id_interest) values ($1,$2)",[id_user,id_interest],function(err,result){
    //done(); TODO:REVISAR si corresponde llamar
    //console.log(result);
    if (err) {db_handler.sendError(err,res,done,500);}
  });
}

function getValidInterests(interests,valid_categories,cb) {
  var valid_interests = [];
  var checked = 0;
  var i = 0;
  var j = 0;

  while (i<valid_categories.length) {
    while (j<interests.length) {
      if(valid_categories[i].category == interests[j].category) {
        valid_interests.push(interests[j]);
        ++i;
        ++j;
      }else{
        ++j;
      }
      if (++checked == valid_categories.length) cb(valid_interests);
    }
  }
}

function checkInterests(req, res, client, done, cb) {
  var interests = req.body.user.interests;
  var valid_categories = [];
  var err = null;
  var has_interests = (interests && interests.length > 0);
  if (has_interests) {
    //Verifico que la categoria exista, si existe la agrego a los intereses validos, sino no
    getValidCategories(interests,client,done,valid_categories,function (categorias_validas) {
      //Una vez que tengo las categorias que estan en la tabla categories,
      //me guardo el vector de intereses que tienen esas categorias, y el resto se descarta
      getValidInterests(interests,valid_categories, function(valid_interests){
        cb(has_interests,valid_interests,err);
      });
    });
  } else {
    //Devuelvo: Has interests false, vector vacío, y error null
    var valid_interests = [];
    cb(has_interests,valid_interests,err);
  }
}

function saveUser(req,res,client,done,cb) {
  var photo_profile = "no_photo";
  var name = req.body.user.name;
  var email = req.body.user.email;
  var alias = req.body.user.alias;
  var sex = req.body.user.sex;
  var latitude = req.body.user.location.latitude;
  var longitude = req.body.user.location.longitude;
  var interests = req.body.user.interests;
  var id_user = "";

  client.query(Cons.QUERY_ALTA_USUARIO,[name,email,alias,sex,latitude,longitude,photo_profile],function(err, result) {
    if (err) {
      cb(id_user,err);
    } else {
      //done(); TODO: REVISAR si hay que llamarlo
      id_user = result.rows[0].id_user;
      cb(id_user,err);
    }
  });
}

function devolverUser(req,res,id_user,done,valid_interests) {
  done();
  var usuario = json_handler.armarJsonUsuarioNuevo(req,id_user);
  usuario.user.interests = valid_interests;
  res.json(usuario).status(201).end();
}

db_handler.getInterests = function (req, res, param, client, done) {
  var query = client.query("SELECT * FROM interests",function(err, result) {
    done();
    if (err) {
      db_handler.sendError(err,res,done,500);
    } else {
      query.on('row', function(row,result) {result.addRow(row);});
      query.on('end', function (result) {
        var respuesta = json_handler.armarJsonListaIntereses(result);
        res.json(respuesta).end();
      })
    }
  });
}

db_handler.addInterest = function (req, res, param, client, done) {
  var category = req.body.interests.category;
  var value = req.body.interests.value;
  createInterest(res,client,done,category,value,function(id_interest,err) {
    if (err) {
      //db_handler.sendError(err,res,done,500);
      console.error("Ocurrio un error");
    } else {
      res.sendStatus(201).end();
    }
  });
}

db_handler.addUser = function(req, res, param, client, done) {
  //var email = req.body.user.email;
  var email = "";
  var check_query = client.query("SELECT * FROM users WHERE email=($1)",[email],function (err, check_result) {
    //done(); //No se si es necesario llamarlo si todavia hay que hacer querys
    //Si existe error al consultar por email
    if (err) { db_handler.sendError(err,res,done,500);
    } else {
      //Si la consulta devuelve un email existente
      if (check_result.rowCount > 0) {
        db_handler.sendError(Cons.ERROR_EMAIL_ALREADY_EXISTS,res,done,500);
      } else { //Si el email consultado no esta en la tabla users
        //Chequeo si hay intereses
        checkInterests(req,res,client,done, function (has_interests,valid_interests, err) {
          if (err) { db_handler.sendError(err,res,done,500);
          } else {
            saveUser(req,res,client,done,function (id_user,err) {
              if(err) { db_handler.sendError(Cons.ERROR_SAVE_USER,res,done,500);
              } else {
                if (has_interests) {
                  processInterests(valid_interests, function(category,value) {
                    saveInterests(res,client,done,id_user,category,value);
                  });
                }
                devolverUser(req,res,id_user,done,valid_interests);
              }
            });
          }
        });
      }
    }
  });
}

db_handler.getUsers = function (req, res, param, client, done) {
  //TODO: JOIN con tabla de users_interests
  var query = client.query("SELECT * FROM users",function(err, result) {
    done(); //Devuelvo el cliente al pool xq no necesito más la conexion
    if (err) {
      db_handler.sendError(err,res,done,500);
    } else {
      query.on('row', function(row,result) {result.addRow(row);});
      query.on('end', function(result) {
        var respuesta = json_handler.armarJsonListaUsuarios(result);
        res.json(respuesta).end();
      })
    }
  });
}

//Pisa todos los valores que estan en la query
//Si en el json no existen los campos, pone null
//Si el campo location no existe, crashea la app
db_handler.modifyUser = function(req, res, usrID, client, done) {
  var u = req.body.user;
  var query = client.query("UPDATE users SET name=($1),email=($2),alias=($3),sex=($4),latitude=($5),longitude=($6) WHERE id_user=($7)",
    [u.name, u.email,u.alias,u.sex, u.location.latitude,u.location.longitude, usrID],
    function (err,result) {
      db_handler.queryExitosa (err, result, res, done);
    });
}

db_handler.getUser = function (req, res, usrID, client, done) {
  var query = client.query("SELECT * FROM users WHERE id_user = ($1)",[usrID],function (err,result) {
    done();
    //Si tiro un id que no existe, la query falla y entra por aca
    if (err) { db_handler.sendError(err,res,done,404);}
    if (result.rowCount) {
      var jsonObject = json_handler.armarJsonUsuarioConsultado(result);
      res.json(jsonObject).end();
    } else {
      //No se por que nunca entra aca si hago una query con un id invalido!
      res.sendStatus(404).end(); //User not found
    }
  })
}

db_handler.deleteUser = function (req, res, usrID, client, done) {
  var query = client.query("DELETE FROM users WHERE id_user=($1)",[usrID],function (err, result) {
    db_handler.queryExitosa(err,result,res, done);
  });
}

db_handler.updatePhoto = function (req, res, usrID, client, done) {
  var photo = req.body.photo;
  var query = client.query("UPDATE users SET photo_profile=($1) WHERE id_user=($2)",[photo,usrID], function (err, result) {
    db_handler.queryExitosa(err,result,res,done);
  });
}

db_handler.sendError = function (err, res, done, status) {
  console.log(err);
  if (done) {done();}
  res.json({succes: false, error: err}).status(status).end();
}

db_handler.queryExitosa = function (err, result, res, done) {
  if (err) {
    db_handler.sendError(err,res,done,500);
  } else {
    done();
    res.sendStatus(200).end();
  }
}

module.exports = db_handler;
<|endoftext|>


<commit_before>import Registry from "../Registry";
import SpecFromClass from "./SpecFromClass";

export default class RegistrySpec {
    constructor(configSpec, CustomRegistry) {
        this._registry = configSpec;
        this._spec = new SpecFromClass(configSpec.name, CustomRegistry || Registry);
    }
    writeTo(config) {
        this._spec.setAllArgs(this._registry.name, ...this._registry.all());
        this._spec.writeTo(config);
    }
}
<commit_msg>Depend on spak for registry object.<commit_after>import { Registry } from "spak";
import SpecFromClass from "./SpecFromClass";

export default class RegistrySpec {
    constructor(configSpec, CustomRegistry) {
        this._registry = configSpec;
        this._spec = new SpecFromClass(configSpec.name, CustomRegistry || Registry);
    }
    writeTo(config) {
        this._spec.setAllArgs(this._registry.name, ...this._registry.all());
        this._spec.writeTo(config);
    }
}
<|endoftext|>


<commit_before>/*
 * Copyright 2015 the Mimu Authors (Dan Bornstein et alia).
 * Licensed AS IS and WITHOUT WARRANTY under the Apache License,
 * Version 2.0. Details: <http://www.apache.org/licenses/LICENSE-2.0>
 */

"use strict";

define(["./CanvasWidget", "./Color"], function(CanvasWidget, Color) {

/** Label font size as a fraction of canvas height. */
var LABEL_HEIGHT_AMT = 0.6;

/** Fraction of width to use for the label. */
var LABEL_WIDTH_AMT = 0.25;

/** Fraction of width to use for the slider. */
var SLIDER_WIDTH_AMT = 0.75;

/** Fraction of width to use for the handle. */
var HANDLE_WIDTH_AMT = 0.025;

/** Fraction of heightto use for the handle. */
var HANDLE_HEIGHT_AMT = 0.95;

/**
 * Slider control widget, with settable label, range, increment, and targeting.
 */
class SliderWidget extends CanvasWidget {
    /**
     * Takes a DOM node to insert the widget into. The node should be an empty
     * container.
     */
    constructor(node) {
        super(node, "slider");

        /** Label. */
        this._label = "";

        /** Minimum value. */
        this._minValue = 0;

        /** Maximum value. */
        this._maxValue = 10;

        /** Value increment. */
        this._increment = 1;

        /** Current value. */
        this._value = 0;

        this.renderBackground();
        this.render();
    }

    /**
     * Sets the label.
     */
    set label(value) {
        this._label = value;
        this.renderBackground();
    }

    /**
     * Sets the minimum value.
     */
    set minValue(value) {
        this._minValue = value;
        this.renderBackground();
        this.value = this.value;  // Clamp if necessary.
    }

    /**
     * Sets the maximum value.
     */
    set maxValue(value) {
        this._maxValue = value;
        this.renderBackground();
        this.value = this.value;  // Clamp if necessary.
    }

    /**
     * Sets the increment.
     */
    set increment(value) {
        this._increment = value;
        this.renderBackground();
        this.value = this.value;  // Clamp if necessary.
    }

    /**
     * Sets the current value.
     */
    set value(value) {
        value = Math.round(value * this._increment) / this._increment;
        value = Math.min(value, this._maxValue);
        value = Math.max(value, this._minValue);

        // Only do anything more if the value has changed.
        if (value !== this._value) {
            this._value = value;
            this.render();
        }
    }

    /**
     * Gets the current value.
     */
    get value() {
        return this._value;
    }

    /**
     * Renders the background canvas.
     */
    renderBackground() {
        var canvas = this.background;
        var style = this.cloneComputedStyle();
        var ctx = canvas.getContext("2d");
        var width = canvas.width;
        var height = canvas.height;
        var labelWidth = width * LABEL_WIDTH_AMT;
        var sliderWidth = width * SLIDER_WIDTH_AMT;
        var handleWidth = width * HANDLE_WIDTH_AMT;
        var halfHeight = height / 2;

        var foregroundColor = Color.parse(style.color).toCss();

        ctx.clearRect(0, 0, width, height);

        ctx.font = style.font;
        style.fontSize = Math.round(height * LABEL_HEIGHT_AMT) + "px";
        var text = this._label + " ";
        var textWidth = ctx.measureText(text).width;
        ctx.fillStyle = foregroundColor;
        ctx.textBaseline = "middle";
        ctx.fillText(text, labelWidth - textWidth, halfHeight);

        ctx.strokeStyle = foregroundColor;
        ctx.lineWidth = this.thickLineWidth;
        ctx.lineCap = "round";
        ctx.lineJoin = "round";
        ctx.beginPath();
        ctx.moveTo(labelWidth + (handleWidth/2), halfHeight);
        ctx.lineTo(labelWidth + sliderWidth - (handleWidth/2), halfHeight);
        ctx.stroke();
    }

    /**
     * Renders the control position into the foreground canvas.
     */
    renderForeground() {
        var canvas = this.foreground;
        var style = this.cloneComputedStyle();
        var ctx = canvas.getContext("2d");
        var width = canvas.width;
        var height = canvas.height;
        var labelWidth = width * LABEL_WIDTH_AMT;
        var sliderWidth = width * SLIDER_WIDTH_AMT;
        var handleWidth = width * HANDLE_WIDTH_AMT;
        var handleHeight = height * HANDLE_HEIGHT_AMT;
        var halfHeight = height / 2;

        var backgroundColor = Color.parse(style.backgroundColor).toCss();
        var foregroundColor = Color.parse(style.color).toCss();

        ctx.clearRect(0, 0, width, height);

        // Actual "throw" of slider is cut off by half a slider width on either
        // end.
        var totalRange = this._maxValue - this._minValue;
        var totalSlide = sliderWidth - handleWidth;
        var slideRatio = (this._value - this._minValue) / totalRange;
        var x = labelWidth + (handleWidth / 2) + (slideRatio * totalSlide);

        SliderWidget._roundRectPath(ctx,
            x - (handleWidth / 2), halfHeight - (handleHeight / 2),
            handleWidth, handleHeight,
            handleWidth * 0.25);
        ctx.fillStyle = backgroundColor;
        ctx.fill();
        ctx.strokeStyle = foregroundColor;
        ctx.lineWidth = this.thinLineWidth;
        ctx.lineCap = "round";
        ctx.lineJoin = "round";
        ctx.stroke();

        ctx.font = style.font;
        var fontSize = Math.round(handleHeight * 0.4);
        var boxHeight = fontSize * 1.5;
        style.fontSize = fontSize + "px";
        var text = " " + this._value + " ";
        var textWidth = ctx.measureText(text).width;
        var textX = (slideRatio < 0.5)
            ? (x + handleWidth)
            : (x - handleWidth - textWidth);
        SliderWidget._roundRectPath(ctx,
            x + handleWidth, halfHeight - (boxHeight / 2),
            textWidth, boxHeight,
            handleWidth * 0.25);
        ctx.fillStyle = backgroundColor;
        ctx.fill();
        ctx.strokeStyle = foregroundColor;
        ctx.lineWidth = this.thinLineWidth;
        ctx.lineCap = "round";
        ctx.lineJoin = "round";
        ctx.stroke();

        ctx.fillStyle = foregroundColor;
        ctx.textBaseline = "middle";
        ctx.fillText(text, x + handleWidth, halfHeight);
    }

    /**
     * Draws a roundrect with given radius. This code was adapted from
     * the Mozilla "Drawing Shapes" tutorial, at
     * <https://developer.mozilla.org/en-US/docs/Web/API/Canvas_API/Tutorial/Drawing_shapes>.
     */
    static _roundRectPath(ctx, x, y, width, height, radius) {
        ctx.beginPath();
        ctx.moveTo(x, y + radius);
        ctx.lineTo(x, y + height - radius);
        ctx.quadraticCurveTo(x, y + height, x + radius, y + height);
        ctx.lineTo(x + width - radius, y + height);
        ctx.quadraticCurveTo(x + width, y + height,
            x + width, y + height-radius);
        ctx.lineTo(x + width, y + radius);
        ctx.quadraticCurveTo(x + width, y, x + width - radius, y);
        ctx.lineTo(x + radius, y);
        ctx.quadraticCurveTo(x, y, x, y + radius);
    }
}

return SliderWidget;
});
<commit_msg>Start to be interactive.<commit_after>/*
 * Copyright 2015 the Mimu Authors (Dan Bornstein et alia).
 * Licensed AS IS and WITHOUT WARRANTY under the Apache License,
 * Version 2.0. Details: <http://www.apache.org/licenses/LICENSE-2.0>
 */

"use strict";

define(["./CanvasWidget", "./Color"], function(CanvasWidget, Color) {

/** Label font size as a fraction of canvas height. */
var LABEL_HEIGHT_AMT = 0.6;

/** Fraction of width to use for the label. */
var LABEL_WIDTH_AMT = 0.25;

/** Fraction of width to use for the slider. */
var SLIDER_WIDTH_AMT = 0.75;

/** Fraction of width to use for the handle. */
var HANDLE_WIDTH_AMT = 0.025;

/** Fraction of heightto use for the handle. */
var HANDLE_HEIGHT_AMT = 0.95;

/**
 * Slider control widget, with settable label, range, increment, and targeting.
 */
class SliderWidget extends CanvasWidget {
    /**
     * Takes a DOM node to insert the widget into. The node should be an empty
     * container.
     */
    constructor(node) {
        super(node, "slider");

        /** Label. */
        this._label = "";

        /** Minimum value. */
        this._minValue = 0;

        /** Maximum value. */
        this._maxValue = 10;

        /** Value increment. */
        this._increment = 1;

        /** Current value. */
        this._value = 0;

        this.renderBackground();
        this.render();

        var outerThis = this;
        this._divNode.addEventListener("mousedown", function(event) {
            outerThis.value = outerThis._xToValue(event.offsetX);
        });
        this._divNode.addEventListener("mousemove", function(event) {
            if (event.buttons !== 0) {
                outerThis.value = outerThis._xToValue(event.offsetX);
            }
        });
    }

    /**
     * Sets the label.
     */
    set label(value) {
        this._label = value;
        this.renderBackground();
    }

    /**
     * Sets the minimum value.
     */
    set minValue(value) {
        this._minValue = value;
        this.renderBackground();
        this.value = this.value;  // Clamp if necessary.
    }

    /**
     * Sets the maximum value.
     */
    set maxValue(value) {
        this._maxValue = value;
        this.renderBackground();
        this.value = this.value;  // Clamp if necessary.
    }

    /**
     * Sets the increment.
     */
    set increment(value) {
        this._increment = value;
        this.renderBackground();
        this.value = this.value;  // Clamp if necessary.
    }

    /**
     * Clamps and quantizes the given (would-be) value.
     */
    _clampAndQuantize(value) {
        value = Math.round(value * this._increment) / this._increment;
        value = Math.min(value, this._maxValue);
        value = Math.max(value, this._minValue);
        return value;
    }

    /**
     * Sets the current value.
     */
    set value(value) {
        value = this._clampAndQuantize(value);

        // Only do anything more if the value has changed.
        if (value !== this._value) {
            this._value = value;
            this.render();
        }
    }

    /**
     * Gets the current value.
     */
    get value() {
        return this._value;
    }

    /**
     * Renders the background canvas.
     */
    renderBackground() {
        var canvas = this.background;
        var style = this.cloneComputedStyle();
        var ctx = canvas.getContext("2d");
        var width = canvas.width;
        var height = canvas.height;
        var labelWidth = this._labelWidth;
        var halfHeight = height / 2;

        var foregroundColor = Color.parse(style.color).toCss();

        ctx.clearRect(0, 0, width, height);

        ctx.font = style.font;
        style.fontSize = Math.round(height * LABEL_HEIGHT_AMT) + "px";
        var text = this._label + " ";
        var textWidth = ctx.measureText(text).width;
        ctx.fillStyle = foregroundColor;
        ctx.textBaseline = "middle";
        ctx.fillText(text, labelWidth - textWidth, halfHeight);

        ctx.strokeStyle = foregroundColor;
        ctx.lineWidth = this.thickLineWidth;
        ctx.lineCap = "round";
        ctx.lineJoin = "round";
        ctx.beginPath();
        ctx.moveTo(this._xMin, halfHeight);
        ctx.lineTo(this._xMax, halfHeight);
        ctx.stroke();
    }

    /**
     * Renders the control position into the foreground canvas.
     */
    renderForeground() {
        var canvas = this.foreground;
        var style = this.cloneComputedStyle();
        var ctx = canvas.getContext("2d");
        var width = canvas.width;
        var height = canvas.height;
        var handleWidth = this._handleWidth;
        var handleHeight = height * HANDLE_HEIGHT_AMT;
        var halfHeight = height / 2;

        var backgroundColor = Color.parse(style.backgroundColor).toCss();
        var foregroundColor = Color.parse(style.color).toCss();

        ctx.clearRect(0, 0, width, height);

        var x = this._sliderX;
        SliderWidget._roundRectPath(ctx,
            x - (handleWidth / 2), halfHeight - (handleHeight / 2),
            handleWidth, handleHeight,
            handleWidth * 0.25);
        ctx.fillStyle = backgroundColor;
        ctx.fill();
        ctx.strokeStyle = foregroundColor;
        ctx.lineWidth = this.thinLineWidth;
        ctx.lineCap = "round";
        ctx.lineJoin = "round";
        ctx.stroke();

        ctx.font = style.font;
        var fontSize = Math.round(handleHeight * 0.4);
        var boxHeight = fontSize * 1.5;
        style.fontSize = fontSize + "px";
        var text = " " + this._value + " ";
        var textWidth = ctx.measureText(text).width;
        var textX = (x < ((this._xMin + this._xMax) / 2))
            ? (x + handleWidth)
            : (x - handleWidth - textWidth);
        SliderWidget._roundRectPath(ctx,
            x + handleWidth, halfHeight - (boxHeight / 2),
            textWidth, boxHeight,
            handleWidth * 0.25);
        ctx.fillStyle = backgroundColor;
        ctx.fill();
        ctx.strokeStyle = foregroundColor;
        ctx.lineWidth = this.thinLineWidth;
        ctx.lineCap = "round";
        ctx.lineJoin = "round";
        ctx.stroke();

        ctx.fillStyle = foregroundColor;
        ctx.textBaseline = "middle";
        ctx.fillText(text, x + handleWidth, halfHeight);
    }

    /** The width of the left-hand label. */
    get _labelWidth() {
        return this._foreground.width * LABEL_WIDTH_AMT;
    }

    /** The overall width of the slider area (including padding zones). */
    get _sliderWidth() {
        return this._foreground.width * SLIDER_WIDTH_AMT;
    }

    /** The width of the slider handle. */
    get _handleWidth() {
        return this._foreground.width * HANDLE_WIDTH_AMT;
    }

    /** The element-relative x value corresponding to `minValue`. */
    get _xMin() {
        // Note: The actual "throw" of slider is cut off by half a slider width
        // on either end.
        return this._labelWidth + (this._handleWidth / 2);
    }

    /** The element-relative x value corresponding to `maxValue`. */
    get _xMax() {
        // Note: The actual "throw" of slider is cut off by half a slider width
        // on either end.
        return this._labelWidth + this._sliderWidth - (this._handleWidth / 2);
    }

    /**
     * The current slider x position as an element-relative x coordinate,
     * based on the current value.
     */
    get _sliderX() {
        var totalRange = this._maxValue - this._minValue;
        var totalSlide = this._xMax - this._xMin;
        var valueRatio = (this._value - this._minValue) / totalRange;
        return this._xMin + (valueRatio * totalSlide);
    }

    /**
     * Given an element-relative x coordinate, return the corresponding
     * scaled and quantized value.
     */
    _xToValue(x) {
        var totalRange = this._maxValue - this._minValue;
        var totalSlide = this._xMax - this._xMin;
        var valueRatio = (x - this._xMin) / totalSlide;
        var value = this._minValue + (valueRatio * totalRange);
        return this._clampAndQuantize(value);
    }

    /**
     * Draws a roundrect with given radius. This code was adapted from
     * the Mozilla "Drawing Shapes" tutorial, at
     * <https://developer.mozilla.org/en-US/docs/Web/API/Canvas_API/Tutorial/Drawing_shapes>.
     */
    static _roundRectPath(ctx, x, y, width, height, radius) {
        ctx.beginPath();
        ctx.moveTo(x, y + radius);
        ctx.lineTo(x, y + height - radius);
        ctx.quadraticCurveTo(x, y + height, x + radius, y + height);
        ctx.lineTo(x + width - radius, y + height);
        ctx.quadraticCurveTo(x + width, y + height,
            x + width, y + height-radius);
        ctx.lineTo(x + width, y + radius);
        ctx.quadraticCurveTo(x + width, y, x + width - radius, y);
        ctx.lineTo(x + radius, y);
        ctx.quadraticCurveTo(x, y, x, y + radius);
    }
}

return SliderWidget;
});
<|endoftext|>


<commit_before>'use strict';
var path = require('path');
var os = require('os');
var Botkit = require('./CoreBot');
var express = require('express');
var bodyParser = require('body-parser');
var twilio = require('twilio');

function TwilioSMS(configuration) {

    var twilioSMS = Botkit(configuration || {});

    if (!configuration) {
        throw Error('Specify your \'account_sid\', \'auth_token\', and ' +
            '\'twilio_number\' as properties of the \'configuration\' object');
    }

    if (configuration && !configuration.account_sid) {
        throw Error('Specify an \'account_sid\' in your configuration object');
    }

    if (configuration && !configuration.auth_token) {
        throw Error('Specify an \'auth_token\'');
    }

    if (configuration && !configuration.twilio_number) {
        throw Error('Specify a \'twilio_number\'');
    }

    twilioSMS.defineBot(function(botkit, config) {

        var bot = {
            botkit: botkit,
            config: config || {},
            utterances: botkit.utterances
        };

        bot.startConversation = function(message, cb) {
            botkit.startConversation(this, message, cb);
        };

        bot.send = function(message, cb) {

            var client = new twilio.RestClient(
                configuration.account_sid,
                configuration.auth_token
            );

            var sms = {
                body: message.text,
                from: configuration.twilio_number,
                to: message.channel
            };

            client.messages.create(sms, function(err, message) {

                if (err) {
                    cb(err);
                } else {
                    cb(null, message);
                }

            });

        };

        bot.reply = function(src, resp, cb) {
            var msg = {};

            if (typeof resp === 'string') {
                msg.text = resp;
            } else {
                msg = resp;
            }

            msg.channel = src.channel;

            if (typeof cb === 'function') {
                bot.say(msg, cb);
            } else {
                bot.say(msg, function() {});
            }

        };

        bot.findConversation = function(message, cb) {

            botkit.debug('CUSTOM FIND CONVO', message.user, message.channel);

            for (var t = 0; t < botkit.tasks.length; t++) {
                for (var c = 0; c < botkit.tasks[t].convos.length; c++) {

                    var convo = botkit.tasks[t].convos[c];
                    var matchesConvo = (
                        convo.source_message.channel === message.channel ||
                        convo.source_message.user === message.user
                    );

                    if (convo.isActive() && matchesConvo) {
                        botkit.debug('FOUND EXISTING CONVO!');
                        cb(botkit.tasks[t].convos[c]);
                        return;
                    }

                }
            }

            cb();
        };

        return bot;
    });

    twilioSMS.handleWebhookPayload = function(req, res, bot) {

        twilioSMS.log('=> Got a message hook');

        var message = {
            text: req.body.Body,
            from: req.body.From,
            to: req.body.To,
            user: req.body.From,
            channel: req.body.From,
            timestamp: Date.now(),
            sid: req.body.MessageSid,
            NumMedia: req.body.NumMedia,
            MediaUrl0: req.body.MediaUrl0,
            MediaUrl1: req.body.MediaUrl1,
            MediaUrl2: req.body.MediaUrl2,
            MediaUrl3: req.body.MediaUrl3,
            MediaUrl4: req.body.MediaUrl4,
            MediaUrl5: req.body.MediaUrl5,
            MediaUrl6: req.body.MediaUrl6,
            MediaUrl7: req.body.MediaUrl7,
            MediaUrl9: req.body.MediaUrl9,
            MediaUrl10: req.body.MediaUrl10,
        };

        twilioSMS.receiveMessage(bot, message);

    };

    // set up a web route for receiving outgoing webhooks
    twilioSMS.createWebhookEndpoints = function(webserver, bot, cb) {

        twilioSMS.log('** Serving webhook endpoints for Twilio Programmable SMS' +
            ' at: ' + os.hostname() + ':' + twilioSMS.config.port + '/sms/receive');

        var endpoint = twilioSMS.config.endpoint || '/sms/receive';

        webserver.post(endpoint, function(req, res) {
            twilioSMS.handleWebhookPayload(req, res, bot);

            // Send empty TwiML response to Twilio
            var twiml = new twilio.TwimlResponse();
            res.type('text/xml');
            res.send(twiml.toString());
        });

        if (cb) cb();

        return twilioSMS;
    };

    twilioSMS.setupWebserver = function(port, cb) {

        if (!port) {
            throw new Error('Cannot start webserver without a \'port\' parameter');
        }

        if (isNaN(port)) {
            throw new TypeError('Specified \'port\' parameter is not a valid number');
        }

        var static_dir = path.join(__dirname, '/public');

        var config = twilioSMS.config;

        if (config && config.webserver && config.webserver.static_dir) {
            static_dir = twilioSMS.config.webserver.static_dir;
        }

        twilioSMS.config.port = port;

        twilioSMS.webserver = express();
        twilioSMS.webserver.use(bodyParser.json());
        twilioSMS.webserver.use(bodyParser.urlencoded({extended: true}));
        twilioSMS.webserver.use(express.static(static_dir));

        twilioSMS.webserver.listen(twilioSMS.config.port, function() {

            twilioSMS.log('*> Listening on port ' + twilioSMS.config.port);
            twilioSMS.startTicking();
            if (cb) cb(null, twilioSMS.webserver);

        });

        return twilioSMS;
    };

    return twilioSMS;
}

module.exports = TwilioSMS;
<commit_msg>Remove 'use strict' pragma<commit_after>var path = require('path');
var os = require('os');
var Botkit = require('./CoreBot');
var express = require('express');
var bodyParser = require('body-parser');
var twilio = require('twilio');

function TwilioSMS(configuration) {

    var twilioSMS = Botkit(configuration || {});

    if (!configuration) {
        throw Error('Specify your \'account_sid\', \'auth_token\', and ' +
            '\'twilio_number\' as properties of the \'configuration\' object');
    }

    if (configuration && !configuration.account_sid) {
        throw Error('Specify an \'account_sid\' in your configuration object');
    }

    if (configuration && !configuration.auth_token) {
        throw Error('Specify an \'auth_token\'');
    }

    if (configuration && !configuration.twilio_number) {
        throw Error('Specify a \'twilio_number\'');
    }

    twilioSMS.defineBot(function(botkit, config) {

        var bot = {
            botkit: botkit,
            config: config || {},
            utterances: botkit.utterances
        };

        bot.startConversation = function(message, cb) {
            botkit.startConversation(this, message, cb);
        };

        bot.send = function(message, cb) {

            var client = new twilio.RestClient(
                configuration.account_sid,
                configuration.auth_token
            );

            var sms = {
                body: message.text,
                from: configuration.twilio_number,
                to: message.channel
            };

            client.messages.create(sms, function(err, message) {

                if (err) {
                    cb(err);
                } else {
                    cb(null, message);
                }

            });

        };

        bot.reply = function(src, resp, cb) {
            var msg = {};

            if (typeof resp === 'string') {
                msg.text = resp;
            } else {
                msg = resp;
            }

            msg.channel = src.channel;

            if (typeof cb === 'function') {
                bot.say(msg, cb);
            } else {
                bot.say(msg, function() {});
            }

        };

        bot.findConversation = function(message, cb) {

            botkit.debug('CUSTOM FIND CONVO', message.user, message.channel);

            for (var t = 0; t < botkit.tasks.length; t++) {
                for (var c = 0; c < botkit.tasks[t].convos.length; c++) {

                    var convo = botkit.tasks[t].convos[c];
                    var matchesConvo = (
                        convo.source_message.channel === message.channel ||
                        convo.source_message.user === message.user
                    );

                    if (convo.isActive() && matchesConvo) {
                        botkit.debug('FOUND EXISTING CONVO!');
                        cb(botkit.tasks[t].convos[c]);
                        return;
                    }

                }
            }

            cb();
        };

        return bot;
    });

    twilioSMS.handleWebhookPayload = function(req, res, bot) {

        twilioSMS.log('=> Got a message hook');

        var message = {
            text: req.body.Body,
            from: req.body.From,
            to: req.body.To,
            user: req.body.From,
            channel: req.body.From,
            timestamp: Date.now(),
            sid: req.body.MessageSid,
            NumMedia: req.body.NumMedia,
            MediaUrl0: req.body.MediaUrl0,
            MediaUrl1: req.body.MediaUrl1,
            MediaUrl2: req.body.MediaUrl2,
            MediaUrl3: req.body.MediaUrl3,
            MediaUrl4: req.body.MediaUrl4,
            MediaUrl5: req.body.MediaUrl5,
            MediaUrl6: req.body.MediaUrl6,
            MediaUrl7: req.body.MediaUrl7,
            MediaUrl9: req.body.MediaUrl9,
            MediaUrl10: req.body.MediaUrl10,
        };

        twilioSMS.receiveMessage(bot, message);

    };

    // set up a web route for receiving outgoing webhooks
    twilioSMS.createWebhookEndpoints = function(webserver, bot, cb) {

        twilioSMS.log('** Serving webhook endpoints for Twilio Programmable SMS' +
            ' at: ' + os.hostname() + ':' + twilioSMS.config.port + '/sms/receive');

        var endpoint = twilioSMS.config.endpoint || '/sms/receive';

        webserver.post(endpoint, function(req, res) {
            twilioSMS.handleWebhookPayload(req, res, bot);

            // Send empty TwiML response to Twilio
            var twiml = new twilio.TwimlResponse();
            res.type('text/xml');
            res.send(twiml.toString());
        });

        if (cb) cb();

        return twilioSMS;
    };

    twilioSMS.setupWebserver = function(port, cb) {

        if (!port) {
            throw new Error('Cannot start webserver without a \'port\' parameter');
        }

        if (isNaN(port)) {
            throw new TypeError('Specified \'port\' parameter is not a valid number');
        }

        var static_dir = path.join(__dirname, '/public');

        var config = twilioSMS.config;

        if (config && config.webserver && config.webserver.static_dir) {
            static_dir = twilioSMS.config.webserver.static_dir;
        }

        twilioSMS.config.port = port;

        twilioSMS.webserver = express();
        twilioSMS.webserver.use(bodyParser.json());
        twilioSMS.webserver.use(bodyParser.urlencoded({extended: true}));
        twilioSMS.webserver.use(express.static(static_dir));

        twilioSMS.webserver.listen(twilioSMS.config.port, function() {

            twilioSMS.log('*> Listening on port ' + twilioSMS.config.port);
            twilioSMS.startTicking();
            if (cb) cb(null, twilioSMS.webserver);

        });

        return twilioSMS;
    };

    return twilioSMS;
}

module.exports = TwilioSMS;
<|endoftext|>