<commit_before>/**
 * # ServerChannel
 *
 * Copyright(c) 2012 Stefano Balietti
 * MIT Licensed
 *
 * Wrapper class for PlayerServer, AdminServer
 *
 * ---
 *
 */

// ## Global scope

module.exports = ServerChannel;

var AdminServer = require('./servers/AdminServer'),
	PlayerServer = require('./servers/PlayerServer'),
	GameServer = require('./GameServer'),
	Logger = require('./Logger');

var J = require('nodegame-client').JSUS;

/**
 * ## ServerChannel Constructor
 *
 * Creates an instance of ServerChannel
 *
 * @param {object} options Configuration object
 * @param {object} sio The socket.io server
 *
 */
function ServerChannel (sio, options) {
	this.sio = sio;
	this.options = J.clone(options);

	this.name = options.name;

	this.admin = null;
	this.player = null;

	this.sysLogger = Logger.get('channel', {name: this.name});

	this.createServers();
}

// ServerChannel methods

/**
 * ### ServerChannel.createServers
 *
 * Creates the AdminServer and the PlayerServer
 *
 * Mixes in default options and user-defined options specified in
 * the constructor
 *
 */
ServerChannel.prototype.createServers = function() {

// AdminServer
	var adminOptions = {
	 	sio: this.sio,
	 	endpoint: this.options.admin,
	 	channel: this.name,
	 	name: 'admin',
	 	user_options: this.options
	};

	this.admin = new AdminServer(adminOptions);

// PlayerServer
	var playerOptions = {
	   sio: this.sio,
	   endpoint: this.options.player,
	   channel: this.name,
	   name: 'player',
	   user_options: this.options
	};

	this.player = new PlayerServer(playerOptions);

// The two servers are aware of each other
	this.admin.setPartner(this.player);
	this.player.setPartner(this.admin);
};

/**
 * ### ServerChannel.listen
 *
 * Puts the AdminServer and PlayerServer on listen mode
 *
 * @return {Boolean} TRUE, if execution is successful
 *
 */
ServerChannel.prototype.listen = function() {
	try {
		this.admin.listen();
		this.player.listen();
		return true;
	}
	catch(e) {
		this.sysLogger.log(e, 'error');
		return false;
	}
};

/**
 * ### ServerChannel.startGame
 *
 * Initializes and start a new game logic on the admin server
 *
 * @param game {string} The path to the game logic
 * @param options {object} Optional. A configuration object to pass to node.setup
 *
 * @see node.Game
 * @see node.setup
 */
ServerChannel.prototype.startGame = function(pathToGame, options) {
	options = options || {};

	var node = require('nodegame-client');

//	try {
		node.setup('nodegame', options);

		node.socket.setSocketType('SocketDirect', {
			socket: this.admin.socket_manager.sockets.direct
		});

		node.connect();

		var game = require(pathToGame)(node);

		node.play(new game());

		return node;
//	}
//	catch(e) {
		this.sysLogger.log(e, 'error');
		return false;
//	}
};

<commit_msg>ServerChannel pass a self reference to game that it starts<commit_after>/**
 * # ServerChannel
 *
 * Copyright(c) 2012 Stefano Balietti
 * MIT Licensed
 *
 * Wrapper class for PlayerServer, AdminServer
 *
 * ---
 *
 */

// ## Global scope

module.exports = ServerChannel;

var AdminServer = require('./servers/AdminServer'),
	PlayerServer = require('./servers/PlayerServer'),
	GameServer = require('./GameServer'),
	Logger = require('./Logger');

var J = require('nodegame-client').JSUS;

/**
 * ## ServerChannel Constructor
 *
 * Creates an instance of ServerChannel
 *
 * @param {object} options Configuration object
 * @param {object} sio The socket.io server
 *
 */
function ServerChannel (sio, options) {
	this.sio = sio;
	this.options = J.clone(options);

	this.name = options.name;

	this.admin = null;
	this.player = null;

	this.subChannels = {};
	this.maxSubChannels = ('undefined' === typeof options.maxSubChannels) ? -1 : options.maxSubChannels;

	this.sysLogger = Logger.get('channel', {name: this.name});

	this.createServers();
}

// ServerChannel methods

/**
 * ### ServerChannel.createServers
 *
 * Creates the AdminServer and the PlayerServer
 *
 * Mixes in default options and user-defined options specified in
 * the constructor
 *
 */
ServerChannel.prototype.createServers = function() {

// AdminServer
	var adminOptions = {
	 	sio: this.sio,
	 	endpoint: this.options.admin,
	 	channel: this.name,
	 	name: 'admin',
	 	user_options: this.options
	};

	this.admin = new AdminServer(adminOptions);

// PlayerServer
	var playerOptions = {
	   sio: this.sio,
	   endpoint: this.options.player,
	   channel: this.name,
	   name: 'player',
	   user_options: this.options
	};

	this.player = new PlayerServer(playerOptions);

// The two servers are aware of each other
	this.admin.setPartner(this.player);
	this.player.setPartner(this.admin);
};

/**
 * ### ServerChannel.listen
 *
 * Puts the AdminServer and PlayerServer on listen mode
 *
 * @return {Boolean} TRUE, if execution is successful
 *
 */
ServerChannel.prototype.listen = function() {
	try {
		this.admin.listen();
		this.player.listen();
		return true;
	}
	catch(e) {
		this.sysLogger.log(e, 'error');
		return false;
	}
};

/**
 * ### ServerChannel.startGame
 *
 * Initializes and start a new game logic on the admin server
 *
 * @param game {string} The path to the game logic
 * @param options {object} Optional. A configuration object to pass to node.setup
 *
 * @see node.Game
 * @see node.setup
 */
ServerChannel.prototype.startGame = function(pathToGame, options) {
	options = options || {};

	var node = require('nodegame-client');

//	try {
		node.setup('nodegame', options);

		node.socket.setSocketType('SocketDirect', {
			socket: this.admin.socket_manager.sockets.direct
		});

		node.connect();

		var game = require(pathToGame)(node, this);

		node.play(new game());

		return node;
//	}
//	catch(e) {
		this.sysLogger.log(e, 'error');
		return false;
//	}
};

ServerChannel.prototype.createSubChannel = function(options) {

	if (this.maxSubChannels !== -1 && J.size(this.subChannels) >= this.maxSubChannels) {
		var maxSubChannelsErr = 'Max number of subchannel for ' + this.name + ' reached: ' + this.maxSubChannels;
			maxSubChannelsErr += ' . Cannot create another subchannel.';
		this.sysLogger.log(maxSubChannelsErr, 'error');
		return false;
	}

	options = options || {};

	var name = options.name || '' + Math.floor(Math.random()*10000000000);
	var duplicateCounter = 1;
	while (this.subChannels[name]) {
		name = name + '' + duplicateCounter;
		duplicateCounter++;
	}

	options.name = name;
	options.admin = options.admin || options.name + '/admin';
	options.player = options.player || options.name + '/player';

	this.subChannels[name] = new ServerChannel(this.sio, options);
	return this.subChannels[name];
};

ServerChannel.prototype.destroySubChannel = function (name, options) {
	if (!name) return;

	if (!this.subChannels[name]) {
		var destroyChannelErr = 'Unexisting subchannel in channel ' + this.name + '. Cannot destroy: ' + name;
		this.sysLogger.log(destroyChannelErr, 'error');
		return false;
	}

	options = options || {};
	// TODO: how to propertly delete a subchannel? redirect, notify players?
	delete this.subChannels[name];
	return true;
};

<|endoftext|>


<commit_before>var
    esPhinx,
    window;

(function ($module) {
    "use strict";

    const
        CSS_CLASS = "esphinx ui",
        CSS_CLASS_QUERY = "." + CSS_CLASS.replace(/ +/g, ".");

    var
        $ = esPhinx;

    $module.extend(true, {

        panels: {

            autocomplete: {

                new: function (searchTextBox, originalList, options,
                    callback) {
                // new: function (searchTextBox, originalList, options = {},
                    // callback) {
                    var
                        ConstructorReference = $module.panels.autocomplete.new,
                        self = this,
                        nodeListFound,
                        referenceNode,
                        created = false,

                        spanize = function (mapped) {
                            var
                                spanizedSliceFound;

                            Object.keys(mapped).forEach(function (i) {
                                spanizedSliceFound = $(mapped[i].span());
                                spanizedSliceFound
                                    .addClass(CSS_CLASS + " slice-found");

                                mapped[i] = spanizedSliceFound;
                            });

                            return mapped;
                        },

                        resolveAnswer = function (found, maps) {
                            var
                                resolved = $(),
                                composed,
                                nodeText,
                                nodeTextAsArr,
                                mapped;

                            found.select(function (node, i) {
                                nodeText = $(node).text().toLowerCase().trim();
                                nodeTextAsArr = nodeText.split(" ");
                                mapped = spanize(maps[i]);
                                composed = composeMatch(mapped, nodeText,
                                    nodeTextAsArr);
                                resolved.concat(true, composeName(composed,
                                    node));
                            });

                            return resolved;
                        },

                        composeMatch = function (mapped, nodeText,
                            nodeTextAsArr) {
                            var
                                composedArr = [],
                                leftUnitName,
                                rightUnitName,
                                searchIndex,
                                sliceFound,
                                itemAsLower,
                                span;

                            nodeTextAsArr.spaceOut().capitalize()
                            .forEach(function (value, i) {
                                if (mapped.hasOwnProperty(i)) {
                                    span = mapped[i];
                                    itemAsLower = value.toLowerCase();
                                    sliceFound = span.text();
                                    searchIndex = itemAsLower
                                        .search(sliceFound);
                                    leftUnitName = itemAsLower
                                        .slice(0, searchIndex);
                                    rightUnitName = itemAsLower
                                        .slice(searchIndex + sliceFound.length,
                                        itemAsLower.length);

                                    if (!leftUnitName.empty()) {
                                        composedArr.push(window.document
                                            .createTextNode(capitalize(
                                            leftUnitName,nodeText)));
                                    } else {
                                        span.text(capitalize(span
                                            .text(), nodeText));
                                    }

                                    composedArr.push(span.asNode());

                                    if (!rightUnitName.empty()) {
                                        composedArr.push(window.document
                                        .createTextNode(rightUnitName));
                                    }
                                } else {
                                    composedArr.push(window.document
                                        .createTextNode(value));
                                }

                            });

                            return composedArr;
                        },

                        capitalize = function (word, nodeText) {
                            var
                                searchIndex;

                            searchIndex = nodeText.toLowerCase().trim()
                                .search(word);

                            if (searchIndex >= 0) {
                                return word.capitalize();
                            } else {
                                return word;
                            }
                        },

                        composeName = function (composedArr, node) {
                            var
                                copy = $(node).clone();

                            composedArr.forEach(function (v) {
                                copy.append(v);
                            });

                            return copy;
                        },

                        create = function (referenceNode) {
                            var
                                nodeListFound = $("

")
                                    .addClass(CSS_CLASS + " list-built-found"),
                                width,
                                borderBottom,
                                border,
                                marginBottom,

                                hitTarget = function (target) {
                                    if (target
                                        .is(nodeListFound.childElements()) ||
                                        target.is(referenceNode)) {
                                        return true;
                                    }
                                    return false;
                                };

                            if (referenceNode.instanceOf(window.HTMLInputElement)) {

                                referenceNode.on("blur", function (e) {
                                    var
                                        target;

                                    if (e.relatedTarget) {
                                        target = $(e.relatedTarget);
                                        if (!target.is(nodeListFound)) {
                                            self.hide();
                                        }
                                    }

                                 });

                                $(window.document).on("mouseup", function (e) {
                                    var
                                        target = $(e.target);

                                    if (!target.instanceOf(window.HTMLInputElement) &&
                                        !target.constructorIs(window.HTMLLIElement)) {
                                        target = target
                                            .parent("ol" + CSS_CLASS_QUERY +
                                                    ".list-built-found li");
                                    }

                                    if (!hitTarget(target)) {
                                        self.hide();
                                    } else if (target
                                        .constructorIs(window.HTMLLIElement)) {
                                        if (target.hasClass("selected")) {
                                            target.removeClass("selected");
                                        } else {
                                            target.addClass("selected");
                                        }
                                    }

                                });

                                referenceNode.on("focus", function () {
                                    if (nodeListFound.childElements().some()) {
                                        nodeListFound.show();
                                    }
                                });

                                marginBottom = referenceNode
                                    .css("margin-bottom");
                                width = referenceNode.width();
                                borderBottom = referenceNode
                                    .css("border-bottom");
                                border = referenceNode.css("border");

                                nodeListFound
                                    .css("margin-top", -marginBottom + "px");

                                // only after set width, otherwise doesn't can get your border
                                nodeListFound.insertAfter(referenceNode);
                                if (width) {
                                    nodeListFound.css("position", "absolute");
                                    // nodeListFound.width(width);
                                    nodeListFound.css("min-width", width);
                                }

                                nodeListFound.show();
                            } else {
                                nodeListFound.insertAfter(referenceNode);
                            }

                            return nodeListFound;
                        },

                        show = function (autocompletedList) {
                            nodeListFound.clean();

                            if (searchTextBox.value()) {
                                if (originalList instanceof $) {
                                    originalList.hide();
                                }

                                autocompletedList.select(function (li) {
                                    nodeListFound.append(li);
                                });

                                if (nodeListFound.childElements().some()) {
                                    nodeListFound.show();
                                } else {
                                    nodeListFound.hide();
                                }
                            } else {
                                if (originalList instanceof $) {
                                    originalList.show();
                                }
                                self.hide();
                            }

                            return nodeListFound.childElements();
                        };

                    if (!(this instanceof ConstructorReference)) {
                        return new ConstructorReference(searchTextBox,
                            originalList, options, callback);
                    }

                    if (Object.getPrototypeOf(originalList) ==
                        Object.getPrototypeOf({})) {
                        referenceNode = searchTextBox;
                        callback = options;
                        options = undefined;
                    } else {
                        referenceNode = originalList;
                    }

                    searchTextBox.autocomplete(originalList, options,
                    function (found, mapped) {
                        var
                            resolvedAnswer;

                        if (!created) {
                            created = true;

                            nodeListFound = create(referenceNode);
                        }

                        if (!callback || !(callback instanceof Function)) {
                            callback = function () {};
                        }

                        resolvedAnswer = resolveAnswer(found, mapped);
                        callback.call(self, show(resolvedAnswer));
                    });

                    this.hide = function () {
                        nodeListFound.hide();
                    };

                }

            }

            // ,test: "bla"

        }

    });

})(esPhinx.ui);
<commit_msg>refactor<commit_after>var
    esPhinx,
    window;

(function ($module) {
    "use strict";

    const
        CSS_CLASS = "esphinx ui",
        CSS_CLASS_QUERY = "." + CSS_CLASS.replace(/ +/g, ".");

    var
        $ = esPhinx;

    $module.extend(true, {

        panels: {

            autocomplete: {

                new: function (searchTextBox, originalList, options,
                    callback) {
                // new: function (searchTextBox, originalList, options = {},
                    // callback) {
                    var
                        ConstructorReference = $module.panels.autocomplete.new,
                        self = this,
                        nodeListFound,
                        referenceNode,
                        created = false,

                        spanize = function (mapped) {
                            var
                                spanizedSliceFound;

                            Object.keys(mapped).forEach(function (i) {
                                spanizedSliceFound = $(mapped[i].span());
                                spanizedSliceFound
                                    .addClass(CSS_CLASS + " slice-found");

                                mapped[i] = spanizedSliceFound;
                            });

                            return mapped;
                        },

                        resolveAnswer = function (found, maps) {
                            var
                                resolved = $(),
                                composed,
                                nodeText,
                                nodeTextAsArr,
                                mapped;

                            found.select(function (node, i) {
                                nodeText = $(node).text().toLowerCase().trim();
                                nodeTextAsArr = nodeText.split(" ");
                                mapped = spanize(maps[i]);
                                composed = composeMatch(mapped, nodeText,
                                    nodeTextAsArr);
                                resolved.concat(true, composeName(composed,
                                    node));
                            });

                            return resolved;
                        },

                        composeMatch = function (mapped, nodeText,
                            nodeTextAsArr) {
                            var
                                composedArr = [],
                                leftUnitName,
                                rightUnitName,
                                searchIndex,
                                sliceFound,
                                itemAsLower,
                                span;

                            nodeTextAsArr.spaceOut().capitalize()
                            .forEach(function (value, i) {
                                if (mapped.hasOwnProperty(i)) {
                                    span = mapped[i];
                                    itemAsLower = value.toLowerCase();
                                    sliceFound = span.text();
                                    searchIndex = itemAsLower
                                        .search(sliceFound);
                                    leftUnitName = itemAsLower
                                        .slice(0, searchIndex);
                                    rightUnitName = itemAsLower
                                        .slice(searchIndex + sliceFound.length,
                                        itemAsLower.length);

                                    if (!leftUnitName.empty()) {
                                        composedArr.push(window.document
                                            .createTextNode(capitalize(
                                            leftUnitName,nodeText)));
                                    } else {
                                        span.text(capitalize(span
                                            .text(), nodeText));
                                    }

                                    composedArr.push(span.asNode());

                                    if (!rightUnitName.empty()) {
                                        composedArr.push(window.document
                                        .createTextNode(rightUnitName));
                                    }
                                } else {
                                    composedArr.push(window.document
                                        .createTextNode(value));
                                }

                            });

                            return composedArr;
                        },

                        capitalize = function (word, nodeText) {
                            var
                                searchIndex;

                            searchIndex = nodeText.toLowerCase().trim()
                                .search(word);

                            if (searchIndex >= 0) {
                                return word.capitalize();
                            } else {
                                return word;
                            }
                        },

                        composeName = function (composedArr, node) {
                            var
                                copy = $(node).clone();

                            composedArr.forEach(function (v) {
                                copy.append(v);
                            });

                            return copy;
                        },

                        create = function (referenceNode) {
                            var
                                nodeListFound = $("

")
                                    .addClass(CSS_CLASS + " list-built-found"),
                                width,
                                borderBottom,
                                border,
                                marginBottom,

                                hitTarget = function (target) {
                                    if (target
                                        .is(nodeListFound.childElements()) ||
                                        target.is(referenceNode)) {
                                        return true;
                                    }
                                    return false;
                                };

                            if (referenceNode.instanceOf(window.HTMLInputElement)) {

                                referenceNode.on("focus", function () {
                                    if (nodeListFound.childElements().some()) {
                                        nodeListFound.show();
                                    }
                                });

                                referenceNode.on("blur", function (e) {
                                    if (e.relatedTarget) {
                                        if (!$(e.relatedTarget)
                                            .is(nodeListFound)) {
                                            self.hide();
                                        }
                                    }

                                 });

                                // clicking outside the panel
                                $(window.document).on("mouseup", function (e) {
                                    var
                                        target = $(e.target);

                                    if (!target.instanceOf(window.HTMLInputElement) &&
                                        !target.constructorIs(window.HTMLLIElement)) {
                                        target = target
                                            .parent("ol" + CSS_CLASS_QUERY +
                                                    ".list-built-found li");
                                    }

                                    if (!hitTarget(target)) {
                                        self.hide();
                                    } else if (target
                                        .constructorIs(window.HTMLLIElement)) {
                                        if (target.hasClass("selected")) {
                                            target.removeClass("selected");
                                        } else {
                                            target.addClass("selected");
                                        }
                                    }

                                });

                                marginBottom = referenceNode
                                    .css("margin-bottom");
                                width = referenceNode.width();
                                borderBottom = referenceNode
                                    .css("border-bottom");
                                border = referenceNode.css("border");

                                nodeListFound
                                    .css("margin-top", -marginBottom + "px");

                                // only after set width, otherwise doesn't can get your border
                                nodeListFound.insertAfter(referenceNode);
                                if (width) {
                                    nodeListFound.css("position", "absolute");
                                    // nodeListFound.width(width);
                                    nodeListFound.css("min-width", width);
                                }

                                nodeListFound.show();
                            } else {
                                nodeListFound.insertAfter(referenceNode);
                            }

                            return nodeListFound;
                        },

                        show = function (autocompletedList) {
                            nodeListFound.clean();

                            if (searchTextBox.value()) {
                                if (originalList instanceof $) {
                                    originalList.hide();
                                }

                                autocompletedList.select(function (li) {
                                    nodeListFound.append(li);
                                });

                                if (nodeListFound.childElements().some()) {
                                    nodeListFound.show();
                                } else {
                                    nodeListFound.hide();
                                }
                            } else {
                                if (originalList instanceof $) {
                                    originalList.show();
                                }
                                self.hide();
                            }

                            return nodeListFound.childElements();
                        };

                    if (!(this instanceof ConstructorReference)) {
                        return new ConstructorReference(searchTextBox,
                            originalList, options, callback);
                    }

                    if (Object.getPrototypeOf(originalList) ==
                        Object.getPrototypeOf({})) {
                        referenceNode = searchTextBox;
                        callback = options;
                        options = undefined;
                    } else {
                        referenceNode = originalList;
                    }

                    searchTextBox.autocomplete(originalList, options,
                    function (found, mapped) {
                        var
                            resolvedAnswer;

                        if (!created) {
                            created = true;

                            nodeListFound = create(referenceNode);
                        }

                        if (!callback || !(callback instanceof Function)) {
                            callback = function () {};
                        }

                        resolvedAnswer = resolveAnswer(found, mapped);
                        callback.call(self, show(resolvedAnswer));
                    });

                    this.hide = function () {
                        nodeListFound.hide();
                    };

                }

            }

            // ,test: "bla"

        }

    });

})(esPhinx.ui);
<|endoftext|>


<commit_before>/*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
           ______     ______     ______   __  __     __     ______
          /\  == \   /\  __ \   /\__  _\ /\ \/ /    /\ \   /\__  _\
          \ \  __<   \ \ \/\ \  \/_/\ \/ \ \  _"-.  \ \ \  \/_/\ \/
           \ \_____\  \ \_____\    \ \_\  \ \_\ \_\  \ \_\    \ \_\
            \/_____/   \/_____/     \/_/   \/_/\/_/   \/_/     \/_/

This is a sample Slack bot built with Botkit.

This bot demonstrates many of the core features of Botkit:

* Connect to Slack using the real time API
* Receive messages based on "spoken" patterns
* Reply to messages
* Use the conversation system to ask questions
* Use the built in storage system to store and retrieve information
  for a user.

# RUN THE BOT:

  Get a Bot token from Slack:

    -> http://my.slack.com/services/new/bot

  Run your bot from the command line:

    token=<MY TOKEN> node slack_bot.js

# USE THE BOT:

  Find your bot inside Slack to send it a direct message.

  Say: "Hello"

  The bot will reply "Hello!"

  Say: "who are you?"

  The bot will tell you its name, where it is running, and for how long.

  Say: "Call me <nickname>"

  Tell the bot your nickname. Now you are friends.

  Say: "who am I?"

  The bot will tell you your nickname, if it knows one for you.

  Say: "shutdown"

  The bot will ask if you are sure, and then shut itself down.

  Make sure to invite your bot into other channels using /invite @<my bot>!

# EXTEND THE BOT:

  Botkit has many features for building cool and useful bots!

  Read all about it here:

    -> http://howdy.ai/botkit

~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/

if (!process.env.token) {
    console.log('Error: Specify token in environment');
    process.exit(1);
}

var Botkit = require('./lib/Botkit.js');
var os = require('os');

var controller = Botkit.slackbot({
    debug: true
});

var bot = controller.spawn({
    token: process.env.token
}).startRTM();

controller.hears(['残業', '疲れた', '炎上', 'つらい', '死', '殺', 'しんどい', 'だるい'], 'direct_message,direct_mention,mention,ambient', function(bot, message) {
    bot.reply(message,'~(=^･ω･^)_:tea:　お茶でもどうぞ');
});

controller.hears(['かわいい','好き','ちゅ','愛し','i love you','i love u','なで','ぺろ','ぽんぽん'], 'direct_message,direct_mention,mention,ambient', function(bot, message) {
    bot.reply(message,'えへへ〜');
});

controller.hears(['こんにちは', 'やあ', 'どうも', 'Hello', 'hi'], 'direct_message,direct_mention,mention,ambient', function(bot, message) {

/*    bot.api.reactions.add({
        timestamp: message.ts,
        channel: message.channel,
        name: 'cat',
    }, function(err, res) {
        if (err) {
            bot.botkit.log('Failed to add emoji reaction :(', err);
        }
    });
*/

    controller.storage.users.get(message.user, function(err, user) {
        if (user && user.name) {
            bot.reply(message, 'こんにちは ' + user.name + 'さん!!');
        } else {
            bot.reply(message, 'こんにちは！');
        }
    });
});

controller.hears(['call me (.*)', 'my name is (.*)'], 'direct_message,direct_mention,mention,ambient', function(bot, message) {
    var name = message.match[1];
    controller.storage.users.get(message.user, function(err, user) {
        if (!user) {
            user = {
                id: message.user,
            };
        }
        user.name = name;
        controller.storage.users.save(user, function(err, id) {
            bot.reply(message, 'わかったー！ あなたのことは ' + user.name + ' さんって呼ぶね！');
        });
    });
});

controller.hears(['what is my name', 'who am i'], 'direct_message,direct_mention,mention,ambient', function(bot, message) {

    controller.storage.users.get(message.user, function(err, user) {
        if (user && user.name) {
            bot.reply(message, 'あなたは ' + user.name + 'さんだよね！');
        } else {
            bot.startConversation(message, function(err, convo) {
                if (!err) {
                    convo.say('まだあなたの名前をしらないよ！');
                    convo.ask('なんて呼んでほしい？', function(response, convo) {
                        convo.ask('あなたは `' + response.text + '`って呼んでほしいの？（y/n）', [
                            {
                                pattern: 'y',
                                callback: function(response, convo) {
                                    // since no further messages are queued after this,
                                    // the conversation will end naturally with status == 'completed'
                                    convo.next();
                                }
                            },
                            {
                                pattern: 'n',
                                callback: function(response, convo) {
                                    // stop the conversation. this will cause it to end with status == 'stopped'
                                    convo.stop();
                                }
                            },
                            {
                                default: true,
                                callback: function(response, convo) {
                                    convo.repeat();
                                    convo.next();
                                }
                            }
                        ]);

                        convo.next();

                    }, {'key': 'nickname'}); // store the results in a field called nickname

                    convo.on('end', function(convo) {
                        if (convo.status == 'completed') {
                            bot.reply(message, 'OK! I will update my dossier...');

                            controller.storage.users.get(message.user, function(err, user) {
                                if (!user) {
                                    user = {
                                        id: message.user,
                                    };
                                }
                                user.name = convo.extractResponse('nickname');
                                controller.storage.users.save(user, function(err, id) {
                                    bot.reply(message, 'Got it. I will call you ' + user.name + ' from now on.');
                                });
                            });

                        } else {
                            // this happens if the conversation ended prematurely for some reason
                            bot.reply(message, 'はーい！おぼえたよ！');
                        }
                    });
                }
            });
        }
    });
});

controller.hears(['おみくじ'], 'direct_message,direct_mention,mention', function(bot, message) {
  var rand = Math.floor(Math.random()*5);
  if (rand == 0) msg = "やったあ！大吉";
  if (rand == 1) msg = "まあまあかな。。中吉";
  if (rand == 2) msg = "にゃー！吉";
  if (rand == 3) msg = "むむ。。凶";
  if (rand == 4) msg = "にゃー。。。大凶";
  bot.reply(message,'あなたの運勢は……'+ msg +'だよ〜！');
});

controller.hears(['食べよう？','食べる？','what my lunch'], 'direct_message,direct_mention,mention', function(bot, message) {
  var rand = Math.floor(Math.random()*5);
  if (rand == 0) msg = "和食";
  if (rand == 1) msg = "中華";
  if (rand == 2) msg = "イタリアン";
  if (rand == 3) msg = "アジア料理";
  if (rand == 4) msg = "喫茶店";
  bot.reply(message,'うーんごはん・・・'+ msg +'なんてどうですか？');
});

controller.hears(['にゃ', 'meow'], 'direct_message,direct_mention,mention,ambient', function(bot, message) {
    bot.reply(message,'にゃーん！');
});

/*controller.hears(['shutdown'], 'direct_message,direct_mention,mention,ambient', function(bot, message) {

    bot.startConversation(message, function(err, convo) {

        convo.ask('Are you sure you want me to shutdown?', [
            {
                pattern: bot.utterances.yes,
                callback: function(response, convo) {
                    convo.say('Bye!');
                    convo.next();
                    setTimeout(function() {
                        process.exit();
                    }, 3000);
                }
            },
        {
            pattern: bot.utterances.no,
            default: true,
            callback: function(response, convo) {
                convo.say('*Phew!*');
                convo.next();
            }
        }
        ]);
    });
});*/

controller.hears(['uptime', 'identify yourself', 'who are you', 'what is your name'],
    'direct_message,direct_mention,mention,ambient', function(bot, message) {

        var hostname = os.hostname();
        var uptime = formatUptime(process.uptime());

        bot.reply(message,
            ':heart_eyes_cat: わたしの名前は <@' + bot.identity.name +
             '>です！ 稼働時間は ' + uptime + ' で、 ' + hostname + 'で動いていますよ！');

    });

function formatUptime(uptime) {
    var unit = 'second';
    if (uptime > 60) {
        uptime = uptime / 60;
        unit = 'minute';
    }
    if (uptime > 60) {
        uptime = uptime / 60;
        unit = 'hour';
    }
    if (uptime != 1) {
        unit = unit + 's';
    }

    uptime = uptime + ' ' + unit;
    return uptime;
}
<commit_msg>[Change]文体<commit_after>/*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
           ______     ______     ______   __  __     __     ______
          /\  == \   /\  __ \   /\__  _\ /\ \/ /    /\ \   /\__  _\
          \ \  __<   \ \ \/\ \  \/_/\ \/ \ \  _"-.  \ \ \  \/_/\ \/
           \ \_____\  \ \_____\    \ \_\  \ \_\ \_\  \ \_\    \ \_\
            \/_____/   \/_____/     \/_/   \/_/\/_/   \/_/     \/_/

This is a sample Slack bot built with Botkit.

This bot demonstrates many of the core features of Botkit:

* Connect to Slack using the real time API
* Receive messages based on "spoken" patterns
* Reply to messages
* Use the conversation system to ask questions
* Use the built in storage system to store and retrieve information
  for a user.

# RUN THE BOT:

  Get a Bot token from Slack:

    -> http://my.slack.com/services/new/bot

  Run your bot from the command line:

    token=<MY TOKEN> node slack_bot.js

# USE THE BOT:

  Find your bot inside Slack to send it a direct message.

  Say: "Hello"

  The bot will reply "Hello!"

  Say: "who are you?"

  The bot will tell you its name, where it is running, and for how long.

  Say: "Call me <nickname>"

  Tell the bot your nickname. Now you are friends.

  Say: "who am I?"

  The bot will tell you your nickname, if it knows one for you.

  Say: "shutdown"

  The bot will ask if you are sure, and then shut itself down.

  Make sure to invite your bot into other channels using /invite @<my bot>!

# EXTEND THE BOT:

  Botkit has many features for building cool and useful bots!

  Read all about it here:

    -> http://howdy.ai/botkit

~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/

if (!process.env.token) {
    console.log('Error: Specify token in environment');
    process.exit(1);
}

var Botkit = require('./lib/Botkit.js');
var os = require('os');

var controller = Botkit.slackbot({
    debug: true
});

var bot = controller.spawn({
    token: process.env.token
}).startRTM();

controller.hears(['残業', '疲れた', '炎上', 'つらい', '死', '殺', 'しんどい', 'だるい'], 'direct_message,direct_mention,mention,ambient', function(bot, message) {
    bot.reply(message,'~(=^･ω･^)_:tea:　お茶でもどうぞ');
});

controller.hears(['かわいい','好き','ちゅ','愛し','i love you','i love u','なで','ぺろ','ぽんぽん'], 'direct_message,direct_mention,mention,ambient', function(bot, message) {
    bot.reply(message,'えへへ〜');
});

controller.hears(['こんにちは', 'やあ', 'どうも', 'Hello', 'hi'], 'direct_message,direct_mention,mention,ambient', function(bot, message) {

/*    bot.api.reactions.add({
        timestamp: message.ts,
        channel: message.channel,
        name: 'cat',
    }, function(err, res) {
        if (err) {
            bot.botkit.log('Failed to add emoji reaction :(', err);
        }
    });
*/

    controller.storage.users.get(message.user, function(err, user) {
        if (user && user.name) {
            bot.reply(message, 'こんにちは ' + user.name + 'さん!!');
        } else {
            bot.reply(message, 'こんにちは！');
        }
    });
});

controller.hears(['call me (.*)', 'my name is (.*)'], 'direct_message,direct_mention,mention,ambient', function(bot, message) {
    var name = message.match[1];
    controller.storage.users.get(message.user, function(err, user) {
        if (!user) {
            user = {
                id: message.user,
            };
        }
        user.name = name;
        controller.storage.users.save(user, function(err, id) {
            bot.reply(message, 'わかったー！ あなたのことは ' + user.name + ' さんって呼ぶね！');
        });
    });
});

controller.hears(['what is my name', 'who am i'], 'direct_message,direct_mention,mention,ambient', function(bot, message) {

    controller.storage.users.get(message.user, function(err, user) {
        if (user && user.name) {
            bot.reply(message, 'あなたは ' + user.name + 'さんだよね！');
        } else {
            bot.startConversation(message, function(err, convo) {
                if (!err) {
                    convo.say('まだあなたの名前をしらないよ！');
                    convo.ask('なんて呼んでほしい？', function(response, convo) {
                        convo.ask('あなたは `' + response.text + '`って呼んでほしいの？（y/n）', [
                            {
                                pattern: 'y',
                                callback: function(response, convo) {
                                    // since no further messages are queued after this,
                                    // the conversation will end naturally with status == 'completed'
                                    convo.next();
                                }
                            },
                            {
                                pattern: 'n',
                                callback: function(response, convo) {
                                    // stop the conversation. this will cause it to end with status == 'stopped'
                                    convo.stop();
                                }
                            },
                            {
                                default: true,
                                callback: function(response, convo) {
                                    convo.repeat();
                                    convo.next();
                                }
                            }
                        ]);

                        convo.next();

                    }, {'key': 'nickname'}); // store the results in a field called nickname

                    convo.on('end', function(convo) {
                        if (convo.status == 'completed') {
                            bot.reply(message, 'OK! I will update my dossier...');

                            controller.storage.users.get(message.user, function(err, user) {
                                if (!user) {
                                    user = {
                                        id: message.user,
                                    };
                                }
                                user.name = convo.extractResponse('nickname');
                                controller.storage.users.save(user, function(err, id) {
                                    bot.reply(message, 'Got it. I will call you ' + user.name + ' from now on.');
                                });
                            });

                        } else {
                            // this happens if the conversation ended prematurely for some reason
                            bot.reply(message, 'はーい！おぼえたよ！');
                        }
                    });
                }
            });
        }
    });
});

controller.hears(['おみくじ'], 'direct_message,direct_mention,mention', function(bot, message) {
  var rand = Math.floor(Math.random()*5);
  if (rand == 0) msg = "やったあ！大吉";
  if (rand == 1) msg = "まあまあかな。。中吉";
  if (rand == 2) msg = "にゃー！吉";
  if (rand == 3) msg = "むむ。。凶";
  if (rand == 4) msg = "にゃー。。。大凶";
  bot.reply(message,'あなたの運勢は……'+ msg +'だよ〜！');
});

controller.hears(['食べよう？','食べる？','what my lunch'], 'direct_message,direct_mention,mention', function(bot, message) {
  var rand = Math.floor(Math.random()*5);
  if (rand == 0) msg = "和食";
  if (rand == 1) msg = "中華";
  if (rand == 2) msg = "イタリアン";
  if (rand == 3) msg = "アジア料理";
  if (rand == 4) msg = "喫茶店";
  bot.reply(message,'きょうのごはん:heart:！'+ msg +'なんてどうですか？');
});

controller.hears(['にゃ', 'meow'], 'direct_message,direct_mention,mention,ambient', function(bot, message) {
    bot.reply(message,'にゃーん！');
});

/*controller.hears(['shutdown'], 'direct_message,direct_mention,mention,ambient', function(bot, message) {

    bot.startConversation(message, function(err, convo) {

        convo.ask('Are you sure you want me to shutdown?', [
            {
                pattern: bot.utterances.yes,
                callback: function(response, convo) {
                    convo.say('Bye!');
                    convo.next();
                    setTimeout(function() {
                        process.exit();
                    }, 3000);
                }
            },
        {
            pattern: bot.utterances.no,
            default: true,
            callback: function(response, convo) {
                convo.say('*Phew!*');
                convo.next();
            }
        }
        ]);
    });
});*/

controller.hears(['uptime', 'identify yourself', 'who are you', 'what is your name'],
    'direct_message,direct_mention,mention,ambient', function(bot, message) {

        var hostname = os.hostname();
        var uptime = formatUptime(process.uptime());

        bot.reply(message,
            ':heart_eyes_cat: わたしの名前は <@' + bot.identity.name +
             '>です！ 稼働時間は ' + uptime + ' で、 ' + hostname + 'で動いていますよ！');

    });

function formatUptime(uptime) {
    var unit = 'second';
    if (uptime > 60) {
        uptime = uptime / 60;
        unit = 'minute';
    }
    if (uptime > 60) {
        uptime = uptime / 60;
        unit = 'hour';
    }
    if (uptime != 1) {
        unit = unit + 's';
    }

    uptime = uptime + ' ' + unit;
    return uptime;
}
<|endoftext|>


<commit_before>(function() {
    'use strict';

    /**
     * Escape all regexp characters except * wildcard, and adapt * wildcard to regexp (* --> .*)
     * @param str
     * @returns {*}
     */
    function escapeRegExpExceptStar(str) {
        return str.replace(/[\-\[\]\/\{\}\(\)\+\?\.\\\^\$\|]/g, '\\$&').replace(/\*/g, '.*');
    }

    /**
     * Filter infos object
     * @param type - the filter type
     * @param colId - the column id
     * @param args - the filter arguments
     * @param filterFn - the filter function
     * @constructor
     */
    function Filter(type, colId, args, filterFn) {
        var self = this;

        self.type = type;
        self.colId = colId;
        self.args = args;
        self.filterFn = filterFn;
        /*self.value = function() {
            switch(type) {
                case 'contains':
                    return self.args.phrase;
            }
        };*/
        self.__defineGetter__("value", function(){
            switch(self.type) {
                case 'contains':
                    return self.args.phrase;
            }
        });
    }

    function FilterService(DatasetGridService) {
        var self = this;
        self.filters = [];

        /**
         * Return the column with a cell that can match the phrase
         * @param phrase - to match
         * @returns {Array}
         */
        self.getColumnsContaining = function(phrase) {
            if (!phrase) {
                return [];
            }

            var regexp = new RegExp(escapeRegExpExceptStar(phrase));
            var canBeNumeric = !isNaN(phrase.replace(/\*/g, ''));
            var canBeBoolean = 'true'.match(regexp) || 'false'.match(regexp);

            return DatasetGridService.getColumnsContaining(regexp, canBeNumeric, canBeBoolean);
        };

        /**
         * Remove all the filters and update datagrid filters
         */
        self.removeAllFilters = function() {
            DatasetGridService.resetFilters();
            self.filters = [];
        };

        /**
         * Create 'contains' filter function
         * @param colId - the column id
         * @param phrase - the phrase that the item must contain
         * @returns {Function}
         */
        var createContainFilter = function(colId, phrase) {
            var lowerCasePhrase = phrase.toLowerCase();
            var regexp = new RegExp(escapeRegExpExceptStar(lowerCasePhrase));
            return function(item) {
                return item[colId].toLowerCase().match(regexp);
            };
        };

        /**
         * Add a filter and update datagrid filters
         * @param type - the filter type (ex : contains)
         * @param colId - the column id
         * @param args - the filter arguments (ex for 'contains' type : {phrase: 'toto'})
         */
        self.addFilter = function(type, colId, args) {
            var filterFn;
            switch(type) {
                case 'contains':
                    filterFn = createContainFilter(colId, args.phrase);
                    break;
            }

            var filterInfos = new Filter(type, colId, args, filterFn);
            DatasetGridService.addFilter(filterFn);
            self.filters.push(filterInfos);
        };

        /**
         * Remove a filter and update datagrid filters
         * @param filter
         */
        self.removeFilter = function(filter) {
            var filterIndex = self.filters.indexOf(filter);
            if(filterIndex > -1) {
                DatasetGridService.removeFilter(filter.filterFn);
                self.filters.splice(filterIndex, 1);
            }
        };

        /**
         * Update existing filter and update datagrid filters
         * @param oldFilter - the old filter to update
         * @param newValue - the new filter value
         */
        self.updateFilter = function(oldFilter, newValue) {
            var index = self.filters.indexOf(oldFilter);
            var oldFn = oldFilter.filterFn;

            var newArgs;
            var newFilterFn;
            switch(oldFilter.type) {
                case 'contains':
                    newArgs = { phrase: newValue};
                    newFilterFn = createContainFilter(oldFilter.colId, newValue);
                    break;
            }
            var newFilter = new Filter(oldFilter.type, oldFilter.colId, newArgs, newFilterFn);

            DatasetGridService.updateFilter(oldFn, newFilter.filterFn);
            self.filters.splice(index, 1, newFilter);
        };
    }

    angular.module('data-prep.services.filter')
        .service('FilterService', FilterService);
})();<commit_msg>TDP-54 : clean code (jshint and comment)<commit_after>(function() {
    'use strict';

    /**
     * Escape all regexp characters except * wildcard, and adapt * wildcard to regexp (* --> .*)
     * @param str
     * @returns {*}
     */
    function escapeRegExpExceptStar(str) {
        return str.replace(/[\-\[\]\/\{\}\(\)\+\?\.\\\^\$\|]/g, '\\$&').replace(/\*/g, '.*');
    }

    /**
     * Filter infos object
     * @param type - the filter type
     * @param colId - the column id
     * @param args - the filter arguments
     * @param filterFn - the filter function
     * @constructor
     */
    function Filter(type, colId, args, filterFn) {
        var self = this;

        self.type = type;
        self.colId = colId;
        self.args = args;
        self.filterFn = filterFn;
        self.__defineGetter__('value', function(){
            switch(self.type) {
                case 'contains':
                    return self.args.phrase;
            }
        });
    }

    function FilterService(DatasetGridService) {
        var self = this;
        self.filters = [];

        /**
         * Return the column with a cell that can match the phrase
         * @param phrase - to match
         * @returns {Array}
         */
        self.getColumnsContaining = function(phrase) {
            if (!phrase) {
                return [];
            }

            var regexp = new RegExp(escapeRegExpExceptStar(phrase));
            var canBeNumeric = !isNaN(phrase.replace(/\*/g, ''));
            var canBeBoolean = 'true'.match(regexp) || 'false'.match(regexp);

            return DatasetGridService.getColumnsContaining(regexp, canBeNumeric, canBeBoolean);
        };

        /**
         * Remove all the filters and update datagrid filters
         */
        self.removeAllFilters = function() {
            DatasetGridService.resetFilters();
            self.filters = [];
        };

        /**
         * Create 'contains' filter function
         * @param colId - the column id
         * @param phrase - the phrase that the item must contain
         * @returns {Function}
         */
        var createContainFilter = function(colId, phrase) {
            var lowerCasePhrase = phrase.toLowerCase();
            var regexp = new RegExp(escapeRegExpExceptStar(lowerCasePhrase));
            return function(item) {
                return item[colId].toLowerCase().match(regexp);
            };
        };

        /**
         * Add a filter and update datagrid filters
         * @param type - the filter type (ex : contains)
         * @param colId - the column id
         * @param args - the filter arguments (ex for 'contains' type : {phrase: 'toto'})
         */
        self.addFilter = function(type, colId, args) {
            var filterFn;
            switch(type) {
                case 'contains':
                    filterFn = createContainFilter(colId, args.phrase);
                    break;
            }

            var filterInfos = new Filter(type, colId, args, filterFn);
            DatasetGridService.addFilter(filterFn);
            self.filters.push(filterInfos);
        };

        /**
         * Remove a filter and update datagrid filters
         * @param filter
         */
        self.removeFilter = function(filter) {
            var filterIndex = self.filters.indexOf(filter);
            if(filterIndex > -1) {
                DatasetGridService.removeFilter(filter.filterFn);
                self.filters.splice(filterIndex, 1);
            }
        };

        /**
         * Update existing filter and update datagrid filters
         * @param oldFilter - the old filter to update
         * @param newValue - the new filter value
         */
        self.updateFilter = function(oldFilter, newValue) {
            var index = self.filters.indexOf(oldFilter);
            var oldFn = oldFilter.filterFn;

            var newArgs;
            var newFilterFn;
            switch(oldFilter.type) {
                case 'contains':
                    newArgs = { phrase: newValue};
                    newFilterFn = createContainFilter(oldFilter.colId, newValue);
                    break;
            }
            var newFilter = new Filter(oldFilter.type, oldFilter.colId, newArgs, newFilterFn);

            DatasetGridService.updateFilter(oldFn, newFilter.filterFn);
            self.filters.splice(index, 1, newFilter);
        };
    }

    angular.module('data-prep.services.filter')
        .service('FilterService', FilterService);
})();<|endoftext|>


<commit_before>/**
 * Copyright reelyActive 2016
 * We believe in an open Internet of Things
 */

/**
 * Parse Estimote Nearable manufacturer specific data.
 * @param {Object} advertiserData The object containing all parsed data.
 */
function process(advertiserData) {
  var nearable = {};
  var data = advertiserData.manufacturerSpecificData.data;

  nearable.id = data.substr(2,16);
  nearable.temperature = toTemperature(data.substr(24,2), data.substr(22,2));
  if(parseInt(data.substr(26,2),16) & 0x40) {
    nearable.currentState = 'motion';
  }
  else {
    nearable.currentState = 'still';
  }
  nearable.accelerationX = toAcceleration(data.substr(28,2));
  nearable.accelerationY = toAcceleration(data.substr(30,2));
  nearable.accelerationZ = toAcceleration(data.substr(32,2));
  nearable.currentStateSeconds = toDuration(data.substr(34,2));
  nearable.previousStateSeconds = toDuration(data.substr(36,2));
  nearable.statusBytes = [ data.substr(0,2), data.substr(18,2),
                           data.substr(20,2), data.substr(24,2),
                           data.substr(26,2), data.substr(38,2) ];

  advertiserData.manufacturerSpecificData.nearable = nearable;
}

/**
 * Convert the given bytes to temperature.
 * @param {String} upper4 The most-significant 4 bits of the temperature.
 * @param {String} lower8 The least-significant 8 bits of the temperature.
 */
function toTemperature(upper4, lower8) {
  var data = ((parseInt(upper4,16) % 16) * 256) + parseInt(lower8,16);
  if(data > 2047) {
    return (data - 4096) / 16;
  }
  return data / 16;
}

/**
 * Convert the given twos complement hexadecimal string to acceleration.
 * @param {String} byte The acceleration data byte as a string.
 */
function toAcceleration(byte) {
  var data = parseInt(byte,16);
  if(data > 127) {
    return data - 256;
  }
  return data;
}

/**
 * Convert the given twos complement hexadecimal string to duration.
 * @param {String} byte The duration data byte as a string.
 */
function toDuration(byte) {
  var data = parseInt(byte,16);
  if(data < 64) { // Less than 0x40 is seconds
    return data;
  }
  if(data >= 128) { // Above 0x80 is hours
    return (data % 128) * 3600;
  }
  return (data % 64) * 60; // Between the two is minutes
}

module.exports.process = process;
<commit_msg>Expressing acceleration in g<commit_after>/**
 * Copyright reelyActive 2016
 * We believe in an open Internet of Things
 */

/**
 * Parse Estimote Nearable manufacturer specific data.
 * @param {Object} advertiserData The object containing all parsed data.
 */
function process(advertiserData) {
  var nearable = {};
  var data = advertiserData.manufacturerSpecificData.data;

  nearable.id = data.substr(2,16);
  nearable.temperature = toTemperature(data.substr(24,2), data.substr(22,2));
  if(parseInt(data.substr(26,2),16) & 0x40) {
    nearable.currentState = 'motion';
  }
  else {
    nearable.currentState = 'still';
  }
  nearable.accelerationX = toAcceleration(data.substr(28,2));
  nearable.accelerationY = toAcceleration(data.substr(30,2));
  nearable.accelerationZ = toAcceleration(data.substr(32,2));
  nearable.currentStateSeconds = toDuration(data.substr(34,2));
  nearable.previousStateSeconds = toDuration(data.substr(36,2));
  nearable.statusBytes = [ data.substr(0,2), data.substr(18,2),
                           data.substr(20,2), data.substr(24,2),
                           data.substr(26,2), data.substr(38,2) ];

  advertiserData.manufacturerSpecificData.nearable = nearable;
}

/**
 * Convert the given bytes to temperature.
 * @param {String} upper4 The most-significant 4 bits of the temperature.
 * @param {String} lower8 The least-significant 8 bits of the temperature.
 */
function toTemperature(upper4, lower8) {
  var data = ((parseInt(upper4,16) % 16) * 256) + parseInt(lower8,16);
  if(data > 2047) {
    return (data - 4096) / 16;
  }
  return data / 16;
}

/**
 * Convert the given twos complement hexadecimal string to acceleration in g.
 * @param {String} byte The acceleration data byte as a string.
 */
function toAcceleration(byte) {
  var data = parseInt(byte,16);
  if(data > 127) {
    return (data - 256) / 64;
  }
  return data / 64;
}

/**
 * Convert the given twos complement hexadecimal string to duration.
 * @param {String} byte The duration data byte as a string.
 */
function toDuration(byte) {
  var data = parseInt(byte,16);
  if(data < 64) { // Less than 0x40 is seconds
    return data;
  }
  if(data >= 128) { // Above 0x80 is hours
    return (data % 128) * 3600;
  }
  return (data % 64) * 60; // Between the two is minutes
}

module.exports.process = process;
<|endoftext|>


<commit_before>var rempl = require('rempl');

module.exports = rempl.createPublisher('Component Inspector', function(settings, callback) {
  // jscs:disable
  /** @cut */ return callback(null, 'url', 'http://localhost:8000/src/inspector-rempl/subscriber/index.html');
  // jscs:enable

  callback(null, 'script', asset('../../../dist/subscriber.js', true));
});

<commit_msg>connect to ws only set up explicit<commit_after>var rempl = require('rempl');

module.exports = rempl.createPublisher('Component Inspector', function(settings, callback) {
  // jscs:disable
  /** @cut */ return callback(null, 'url', 'http://localhost:8000/src/inspector-rempl/subscriber/index.html');
  // jscs:enable

  callback(null, 'script', asset('../../../dist/subscriber.js', true));
}, {
  ws: window.REMPL_HOST || false
});
<|endoftext|>


<commit_before>/*jshint browser:true */
/*globals Base64Binary */
(function($) {
	"use strict";

	var XmlRpcFault = function() {
		Error.apply(this, arguments);
	};
	XmlRpcFault.prototype = new Error();
	XmlRpcFault.prototype.type = 'XML-RPC fault';

	var xmlrpc = $.xmlrpc = function(url, settings) {

		if (arguments.length == 2) {
			settings.url = url;
		} else {
			settings = url;
			url = settings.url;
		}

		settings.dataType = 'xml json';
		settings.type = 'POST';
		settings.contentType = 'text/xml';
		settings.converters = {'xml json': xmlrpc.parseDocument};

		var xmlDoc = xmlrpc.document(settings.methodName, settings.params);
		var serializer = new XMLSerializer();

		settings.data = serializer.serializeToString(xmlDoc);

		return $.ajax(settings);
	};

	/**
	* Make an XML-RPC document from a method name and a set of parameters
	*/
	xmlrpc.document = function(name, params) {
		var doc = document.implementation.createDocument(null, null, null);
		var $xml = function(name) {
			return $(doc.createElement(name));
		};

		var $methodName = $xml('methodName').text(name);
		var $params = $xml('params').append($.map(params, function(param) {
			var $value = $xml('value').append(xmlrpc.toXmlRpc(param, $xml));
			return $xml('param').append($value);
		}));
		var $methodCall = $xml('methodCall').append($methodName, $params);
		doc.appendChild($methodCall.get(0));
		return doc;
	};

	var _isInt = function(x) {
		return (x === parseInt(x, 10)) && !isNaN(x);
	};

	/**
	* Take a JavaScript value, and return an XML node representing the value
	* in XML-RPC style. If the value is one of the `XmlRpcType`s, that type is
	* used. Otherwise, a best guess is made as to its type. The best guess is
	* good enough in the vast majority of cases.
	*/
	xmlrpc.toXmlRpc = function(item, $xml) {

		if (item instanceof XmlRpcType) return item.toXmlRpc($xml);

		var types = $.xmlrpc.types;
		var type = $.type(item);

		switch (type) {
			case "undefined":
			case "null":
				return types.nil.encode(item, $xml);

			case "date":
				return types['date.iso8601'].encode(item, $xml);

			case "object":
				if (item instanceof ArrayBuffer) {
					return types.base64.encode(item, $xml);
				} else {
					return types.struct.encode(item, $xml);
				}
				break;

			case "number":
				// Ints and Floats encode differently
				if (_isInt(item)) {
					return types.int.encode(item, $xml);
				} else {
					return types.double.encode(item, $xml);
				}
				break;

			case "array":
			case "boolean":
			case "string":
				return types[type].encode(item, $xml);

			default:
				throw new Error("Unknown type", item);
		}
	};

	/**
	* Take an XML-RPC document and decode it to an equivalent JavaScript
	* representation.
	*
	* If the XML-RPC document represents a fault, then an equivalent
	* XmlRpcFault will be thrown instead
	*/
	xmlrpc.parseDocument = function(doc) {
		var $doc = $(doc);
		var $response = $doc.children('methodresponse');
		var data;

		var $fault = $response.find('> fault');
		if ($fault.length === 0) {
			var $params = $response.find('> params > param > value > *');
			var json = $params.toArray().map(xmlrpc.parseNode);
			return json;
		} else {
			var fault = xmlrpc.parseNode($fault.find('> value > *').get(0));
			var err = new XmlRpcFault(fault.faultString);
			err.msg = err.message = fault.faultString;
			err.type = err.code = fault.faultCode;
			throw err;
		}
	};

	/*
	* Take an XML-RPC node, and return the JavaScript equivalent
	*/
	xmlrpc.parseNode = function(node) {
		var nodename = node.nodeName.toLowerCase();
		if (nodename in xmlrpc.types) {
			return xmlrpc.types[nodename].decode(node);
		} else {
			throw new Error('Unknown type ' + nodename);
		}
	};

	var XmlRpcType = function() { };

	$.xmlrpc.types = {};

	/**
	* Make a XML-RPC type. We use these to encode and decode values. You can
	* also force a values type using this. See `$.xmlrpc.force()`
	*/
	xmlrpc.makeType = function(tagName, simple, encode, decode) {
		var Type;

		Type = function(value) {
			this.value = value;
		};
		Type.prototype = new XmlRpcType();
		Type.prototype.tagName = tagName;

		if (simple) {
			var simpleEncode = encode, simpleDecode = decode;
			encode = function(value, $xml) {
				var text = simpleEncode(value);
				return $xml(Type.tagName).text(text);
			};
			decode = function(node) {
				return simpleDecode($(node).text(), node);
			};
		}
		Type.prototype.toXmlRpc = function($xml) {
			return Type.encode(this.value, $xml);
		};

		Type.tagName = tagName;
		Type.encode = encode;
		Type.decode = decode;

		xmlrpc.types[tagName] = Type;
	};

	// Number types
	var _fromInt = function(value) { return '' + Math.floor(value); };
	var _toInt = function(text, node) { return parseInt(text, 10); };

	xmlrpc.makeType('int', true, _fromInt, _toInt),
	xmlrpc.makeType('i4', true, _fromInt, _toInt),
	xmlrpc.makeType('i8', true, _fromInt, _toInt),
	xmlrpc.makeType('i16', true, _fromInt, _toInt),
	xmlrpc.makeType('i32', true, _fromInt, _toInt),

	xmlrpc.makeType('double', true, String, function(text) {
		return parseFloat(text, 10);
	});

	// String type. Fairly simple
	xmlrpc.makeType('string', true, String, String);

	// Boolean type. True == '1', False == '0'
	xmlrpc.makeType('boolean', true, function(value) {
		return value ? '1' : '0';
	}, function(text) {
		return text == '1';
	});

	// Dates are a little trickier
	var _pad = function(n) { return n<10 ? '0'+n : n; };

	xmlrpc.makeType('date.iso8601', true, function(d) {
		return [
			d.getUTCFullYear(), '-', _pad(d.getUTCMonth()+1), '-',
			_pad(d.getUTCDate()), 'T', _pad(d.getUTCHours()), ':',
			_pad(d.getUTCMinutes()), ':', _pad(d.getUTCSeconds()), 'Z'
		].join('');
	}, function(text) {
		return new Date(text);
	});

	// Go between a base64 string and an ArrayBuffer
	xmlrpc.binary = (function() {
		var pad = '=';
		var toChars = ('ABCDEFGHIJKLMNOPQRSTUVWXYZ' +
			'abcdefghijklmnopqrstuvwxyz0123456789+/').split("");
		var fromChars = toChars.reduce(function(acc, chr, i) {
			acc[chr] = i;
			return acc;
		}, {});

		/*
		* In the following, three bytes are added together into a 24-bit
		* number, which is then split up in to 4 6-bit numbers - or vice versa.
		* That is why there is lots of shifting by multiples of 6 and 8, and
		* the magic numbers 3 and 4.
		*
		* The modulo 64 is for converting to base 64, and the modulo 256 is for
		* converting to 8-bit numbers.
		*/
		return {
			toBase64: function(ab) {
				var acc = [];

				var int8View = new Uint8Array(ab);
				var int8Index = 0, int24;
				for (; int8Index < int8View.length; int8Index += 3) {

					// Grab three bytes
					int24 =
						(int8View[int8Index + 0] << 16) +
						(int8View[int8Index + 1] << 8) +
						(int8View[int8Index + 2] << 0);

					// Push four chars
					acc.push(toChars[(int24 >> 18) % 64]);
					acc.push(toChars[(int24 >> 12) % 64]);
					acc.push(toChars[(int24 >> 6) % 64]);
					acc.push(toChars[(int24 >> 0)% 64]);
				}

				// Set the last few characters to the padding character
				var padChars = 3 - ((ab.byteLength % 3) || 3);
				while (padChars--) {
					acc[acc.length - padChars - 1] = pad;
				}

				return acc.join('');
			},

			fromBase64: function(base64) {
				var base64Len = base64.length;

				// Work out the length of the data, accommodating for padding
				var abLen = (base64Len / 4) * 3;
				if (base64.charAt(base64Len - 1) == pad) abLen--;
				if (base64.charAt(base64Len - 2) == pad) abLen--;

				// Make the ArrayBuffer, and an Int8Array to work with it
				var ab = new ArrayBuffer(abLen);
				var int8View = new Uint8Array(ab);

				var base64Index = 0, int8Index = 0, int24;
				for (; base64Index < base64Len; base64Index += 4, int8Index += 3) {

					// Grab four chars
					int24 =
						(fromChars[base64[base64Index + 0]] << 18) +
						(fromChars[base64[base64Index + 1]] << 12) +
						(fromChars[base64[base64Index + 2]] << 6) +
						(fromChars[base64[base64Index + 3]] << 0);

					// Push three bytes
					int8View[int8Index + 0] = (int24 >> 16) % 256;
					int8View[int8Index + 1] = (int24 >> 8) % 256;
					int8View[int8Index + 2] = (int24 >> 0) % 256;

				}

				return ab;
			}
		};
	})();

	xmlrpc.makeType('base64', true, function(ab) {
		return xmlrpc.binary.toBase64(ab);
	}, function(text) {
		return xmlrpc.binary.fromBase64(text);
	});

	// Nil/null
	xmlrpc.makeType('nil', false,
		function(val, $xml) { return $xml('nil'); },
		function(node) { return null; }
	);

	// Structs/Objects
	xmlrpc.makeType('struct', false, function(value, $xml) {
		var $struct = $xml('struct');

		$.each(value, function(name, value) {
			var $name = $xml('name').text(name);
			var $value = $xml('value').append(xmlrpc.toXmlRpc(value, $xml));
			$struct.append($xml('member').append($name, $value));
		});

		return $struct;

	}, function(node) {
		return $(node)
			.find('> member')
			.toArray()
			.reduce(function(struct, el) {
				var $el = $(el);
				var key = $el.find('> name').text();

				// If no child elements of <value> exist, skip calling .parseNode
				var elementEmpty = $el.find('> value > *').length == 0;
				var value = elementEmpty ? '' : xmlrpc.parseNode($el.find('> value > *').get(0));
				struct[key] = value;
				return struct;
			}, {});

	});

	// Arrays
	xmlrpc.makeType('array', false, function(value, $xml) {
		var $array = $xml('array');
		var $data = $xml('data');
		$.each(value, function(i, val) {
			$data.append($xml('value').append(xmlrpc.toXmlRpc(val, $xml)));
		});
		$array.append($data);
		return $array;
	}, function(node) {
		var $values = $(node).find('> data > value > *');
		var decoded = $values.toArray().map(xmlrpc.parseNode);
		return decoded;
	});

	/**
	* Force a value to an XML-RPC type. All the usual XML-RPC types are
	* supported
	*/
	xmlrpc.force = function(type, value) {
		return new xmlrpc.types[type](value);
	};

})(jQuery);
<commit_msg>Handle empty <values> in `parseNode`, also arrays<commit_after>/*jshint browser:true */
/*globals Base64Binary */
(function($) {
	"use strict";

	var XmlRpcFault = function() {
		Error.apply(this, arguments);
	};
	XmlRpcFault.prototype = new Error();
	XmlRpcFault.prototype.type = 'XML-RPC fault';

	var xmlrpc = $.xmlrpc = function(url, settings) {

		if (arguments.length == 2) {
			settings.url = url;
		} else {
			settings = url;
			url = settings.url;
		}

		settings.dataType = 'xml json';
		settings.type = 'POST';
		settings.contentType = 'text/xml';
		settings.converters = {'xml json': xmlrpc.parseDocument};

		var xmlDoc = xmlrpc.document(settings.methodName, settings.params);
		var serializer = new XMLSerializer();

		settings.data = serializer.serializeToString(xmlDoc);

		return $.ajax(settings);
	};

	/**
	* Make an XML-RPC document from a method name and a set of parameters
	*/
	xmlrpc.document = function(name, params) {
		var doc = document.implementation.createDocument(null, null, null);
		var $xml = function(name) {
			return $(doc.createElement(name));
		};

		var $methodName = $xml('methodName').text(name);
		var $params = $xml('params').append($.map(params, function(param) {
			var $value = $xml('value').append(xmlrpc.toXmlRpc(param, $xml));
			return $xml('param').append($value);
		}));
		var $methodCall = $xml('methodCall').append($methodName, $params);
		doc.appendChild($methodCall.get(0));
		return doc;
	};

	var _isInt = function(x) {
		return (x === parseInt(x, 10)) && !isNaN(x);
	};

	/**
	* Take a JavaScript value, and return an XML node representing the value
	* in XML-RPC style. If the value is one of the `XmlRpcType`s, that type is
	* used. Otherwise, a best guess is made as to its type. The best guess is
	* good enough in the vast majority of cases.
	*/
	xmlrpc.toXmlRpc = function(item, $xml) {

		if (item instanceof XmlRpcType) return item.toXmlRpc($xml);

		var types = $.xmlrpc.types;
		var type = $.type(item);

		switch (type) {
			case "undefined":
			case "null":
				return types.nil.encode(item, $xml);

			case "date":
				return types['date.iso8601'].encode(item, $xml);

			case "object":
				if (item instanceof ArrayBuffer) {
					return types.base64.encode(item, $xml);
				} else {
					return types.struct.encode(item, $xml);
				}
				break;

			case "number":
				// Ints and Floats encode differently
				if (_isInt(item)) {
					return types.int.encode(item, $xml);
				} else {
					return types.double.encode(item, $xml);
				}
				break;

			case "array":
			case "boolean":
			case "string":
				return types[type].encode(item, $xml);

			default:
				throw new Error("Unknown type", item);
		}
	};

	/**
	* Take an XML-RPC document and decode it to an equivalent JavaScript
	* representation.
	*
	* If the XML-RPC document represents a fault, then an equivalent
	* XmlRpcFault will be thrown instead
	*/
	xmlrpc.parseDocument = function(doc) {
		var $doc = $(doc);
		var $response = $doc.children('methodresponse');
		var data;

		var $fault = $response.find('> fault');
		if ($fault.length === 0) {
			var $params = $response.find('> params > param > value > *');
			var json = $params.toArray().map(xmlrpc.parseNode);
			return json;
		} else {
			var fault = xmlrpc.parseNode($fault.find('> value > *').get(0));
			var err = new XmlRpcFault(fault.faultString);
			err.msg = err.message = fault.faultString;
			err.type = err.code = fault.faultCode;
			throw err;
		}
	};

	/*
	* Take an XML-RPC node, and return the JavaScript equivalent
	*/
	xmlrpc.parseNode = function(node) {

		// Some XML-RPC services return empty <value /> elements. This is not
		// legal XML-RPC, but we may as well handle it.
		if (node === undefined) {
			return null;
		}
		var nodename = node.nodeName.toLowerCase();
		if (nodename in xmlrpc.types) {
			return xmlrpc.types[nodename].decode(node);
		} else {
			throw new Error('Unknown type ' + nodename);
		}
	};

	var XmlRpcType = function() { };

	$.xmlrpc.types = {};

	/**
	* Make a XML-RPC type. We use these to encode and decode values. You can
	* also force a values type using this. See `$.xmlrpc.force()`
	*/
	xmlrpc.makeType = function(tagName, simple, encode, decode) {
		var Type;

		Type = function(value) {
			this.value = value;
		};
		Type.prototype = new XmlRpcType();
		Type.prototype.tagName = tagName;

		if (simple) {
			var simpleEncode = encode, simpleDecode = decode;
			encode = function(value, $xml) {
				var text = simpleEncode(value);
				return $xml(Type.tagName).text(text);
			};
			decode = function(node) {
				return simpleDecode($(node).text(), node);
			};
		}
		Type.prototype.toXmlRpc = function($xml) {
			return Type.encode(this.value, $xml);
		};

		Type.tagName = tagName;
		Type.encode = encode;
		Type.decode = decode;

		xmlrpc.types[tagName] = Type;
	};

	// Number types
	var _fromInt = function(value) { return '' + Math.floor(value); };
	var _toInt = function(text, node) { return parseInt(text, 10); };

	xmlrpc.makeType('int', true, _fromInt, _toInt),
	xmlrpc.makeType('i4', true, _fromInt, _toInt),
	xmlrpc.makeType('i8', true, _fromInt, _toInt),
	xmlrpc.makeType('i16', true, _fromInt, _toInt),
	xmlrpc.makeType('i32', true, _fromInt, _toInt),

	xmlrpc.makeType('double', true, String, function(text) {
		return parseFloat(text, 10);
	});

	// String type. Fairly simple
	xmlrpc.makeType('string', true, String, String);

	// Boolean type. True == '1', False == '0'
	xmlrpc.makeType('boolean', true, function(value) {
		return value ? '1' : '0';
	}, function(text) {
		return text == '1';
	});

	// Dates are a little trickier
	var _pad = function(n) { return n<10 ? '0'+n : n; };

	xmlrpc.makeType('date.iso8601', true, function(d) {
		return [
			d.getUTCFullYear(), '-', _pad(d.getUTCMonth()+1), '-',
			_pad(d.getUTCDate()), 'T', _pad(d.getUTCHours()), ':',
			_pad(d.getUTCMinutes()), ':', _pad(d.getUTCSeconds()), 'Z'
		].join('');
	}, function(text) {
		return new Date(text);
	});

	// Go between a base64 string and an ArrayBuffer
	xmlrpc.binary = (function() {
		var pad = '=';
		var toChars = ('ABCDEFGHIJKLMNOPQRSTUVWXYZ' +
			'abcdefghijklmnopqrstuvwxyz0123456789+/').split("");
		var fromChars = toChars.reduce(function(acc, chr, i) {
			acc[chr] = i;
			return acc;
		}, {});

		/*
		* In the following, three bytes are added together into a 24-bit
		* number, which is then split up in to 4 6-bit numbers - or vice versa.
		* That is why there is lots of shifting by multiples of 6 and 8, and
		* the magic numbers 3 and 4.
		*
		* The modulo 64 is for converting to base 64, and the modulo 256 is for
		* converting to 8-bit numbers.
		*/
		return {
			toBase64: function(ab) {
				var acc = [];

				var int8View = new Uint8Array(ab);
				var int8Index = 0, int24;
				for (; int8Index < int8View.length; int8Index += 3) {

					// Grab three bytes
					int24 =
						(int8View[int8Index + 0] << 16) +
						(int8View[int8Index + 1] << 8) +
						(int8View[int8Index + 2] << 0);

					// Push four chars
					acc.push(toChars[(int24 >> 18) % 64]);
					acc.push(toChars[(int24 >> 12) % 64]);
					acc.push(toChars[(int24 >> 6) % 64]);
					acc.push(toChars[(int24 >> 0)% 64]);
				}

				// Set the last few characters to the padding character
				var padChars = 3 - ((ab.byteLength % 3) || 3);
				while (padChars--) {
					acc[acc.length - padChars - 1] = pad;
				}

				return acc.join('');
			},

			fromBase64: function(base64) {
				var base64Len = base64.length;

				// Work out the length of the data, accommodating for padding
				var abLen = (base64Len / 4) * 3;
				if (base64.charAt(base64Len - 1) == pad) abLen--;
				if (base64.charAt(base64Len - 2) == pad) abLen--;

				// Make the ArrayBuffer, and an Int8Array to work with it
				var ab = new ArrayBuffer(abLen);
				var int8View = new Uint8Array(ab);

				var base64Index = 0, int8Index = 0, int24;
				for (; base64Index < base64Len; base64Index += 4, int8Index += 3) {

					// Grab four chars
					int24 =
						(fromChars[base64[base64Index + 0]] << 18) +
						(fromChars[base64[base64Index + 1]] << 12) +
						(fromChars[base64[base64Index + 2]] << 6) +
						(fromChars[base64[base64Index + 3]] << 0);

					// Push three bytes
					int8View[int8Index + 0] = (int24 >> 16) % 256;
					int8View[int8Index + 1] = (int24 >> 8) % 256;
					int8View[int8Index + 2] = (int24 >> 0) % 256;

				}

				return ab;
			}
		};
	})();

	xmlrpc.makeType('base64', true, function(ab) {
		return xmlrpc.binary.toBase64(ab);
	}, function(text) {
		return xmlrpc.binary.fromBase64(text);
	});

	// Nil/null
	xmlrpc.makeType('nil', false,
		function(val, $xml) { return $xml('nil'); },
		function(node) { return null; }
	);

	// Structs/Objects
	xmlrpc.makeType('struct', false, function(value, $xml) {
		var $struct = $xml('struct');

		$.each(value, function(name, value) {
			var $name = $xml('name').text(name);
			var $value = $xml('value').append(xmlrpc.toXmlRpc(value, $xml));
			$struct.append($xml('member').append($name, $value));
		});

		return $struct;

	}, function(node) {
		return $(node)
			.find('> member')
			.toArray()
			.reduce(function(struct, el) {
				var $el = $(el);
				var key = $el.find('> name').text();
				var valueNode = $el.find('> value').children()[0];
				var value = xmlrpc.parseNode(valueNode);

				struct[key] = value;
				return struct;
			}, {});

	});

	// Arrays
	xmlrpc.makeType('array', false, function(value, $xml) {
		var $array = $xml('array');
		var $data = $xml('data');
		$.each(value, function(i, val) {
			$data.append($xml('value').append(xmlrpc.toXmlRpc(val, $xml)));
		});
		$array.append($data);
		return $array;
	}, function(node) {
		return $(node).find('> data > value').toArray()
			.map(function(el) { return $(el).children()[0]; })
			.map(xmlrpc.parseNode);
	});

	/**
	* Force a value to an XML-RPC type. All the usual XML-RPC types are
	* supported
	*/
	xmlrpc.force = function(type, value) {
		return new xmlrpc.types[type](value);
	};

})(jQuery);
<|endoftext|>


<commit_before>/*
Manager

Handles integration to Taaspace and higher level events such as tap event.

Keep all taaspace-related stuff in Manager.
Keep Recognizer compatible with all html elements.

Notes
  [1] For press event detection, we can place a threshold how far
      we allow fingers to move to still classify it as a press.
*/
var Recognizer = require('./Recognizer')
var utils = require('./utils')
var taaspace = require('taaspace')
var nudged = require('nudged')

var toRawPivot = function (mode, transformer) {
  // The pivot needs to be converted to [x, y]
  // on transformer's coordinate plane.
  //
  if (mode.pivot) {
    return mode.pivot.to(transformer).toArray()
  }

  // Use middle of transformer as a default pivot if translation not allowed.
  if (mode.translate === false) {
    return transformer.atMid().to(transformer).toArray()
  }

  // Return undefined so that when rawPivot is given as a parameter
  // to Recognizer, it looks like the parameter was not given.
}

var rebase = function (pointers, sourcePlane, targetPlane) {
  // Represent pointers on another SpacePlane
  //
  // Parameters
  //   pointers
  //     a map: id -> [x, y]
  //   sourcePlane
  //     a SpacePlane of the pointers
  //   targetPlane
  //     a SpacePlane of the returned pointers
  //
  if (sourcePlane === targetPlane) {
    return pointers
  }

  var k
  var result = {}
  var sourceToSpace = sourcePlane.getGlobalTransform().toSpace()
  var targetToSpace = targetPlane.getGlobalTransform().toSpace()
  var sourceToTarget = targetToSpace.inverse().multiplyRight(sourceToSpace)
  for (k in pointers) {
    if (pointers.hasOwnProperty(k)) {
      result[k] = sourceToTarget.transform(pointers[k])
    }
  }
  return result
}

var multiplyLeft = function (pointers, transform) {
  // Transform pointers
  var k
  var result = {}
  for (k in pointers) {
    if (pointers.hasOwnProperty(k)) {
      result[k] = transform.transform(pointers[k])
    }
  }
  return result
}

var Manager = function (element, transformer, view, emitter, mode) {
  // Create a Manager
  //   var man = new Manager(...)
  //
  // Parameters:
  //   element
  //     HTML element to listen to
  //   transformer
  //     a taaspace.SpaceTransformer, the object to move
  //   view
  //     a taaspace.SpaceViewHTML
  //   emitter
  //     public emitter the user listens to
  //   mode
  //     a transformation type
  //
  var self = this

  this.mode = mode
  this.transformer = transformer
  this.view = view

  var startTime = null
  var totalTravel = null
  var pointersOnItem = {}

  var onStart = function (firstPointers) {
    startTime = Date.now()
    totalTravel = 0

    pointersOnItem = rebase(firstPointers, view, transformer)

    emitter.emit('transformstart', {
      distance: 0,
      duration: 0
    })
  }

  var onMove = function (prevPointers, nextPointers) {
    var k, n, pivot, type, tr, itr
    var domain = []
    var range = []

    // Current location of new pointers on the transformer.
    // This approach zeroes the effects caused by
    // 1) transformations of the view
    // 2) transformations of the parents of the transformer
    var nextPointersOnItem = rebase(nextPointers, view, transformer)

    // Set intersection. Do not use removed or appeared pointers
    // in estimation.
    for (k in pointersOnItem) {
      if (pointersOnItem.hasOwnProperty(k) &&
          nextPointersOnItem.hasOwnProperty(k)) {
        domain.push(pointersOnItem[k])
        range.push(nextPointersOnItem[k])
      }
    }

    // Accumulate to travelled distance. See [1]
    // Use Manhattan distance for simpler computation.
    // Goal is to form a threshold to filter out small
    // involuntary movement of the fingers or arm.
    //
    // N. Divide distance by number of fingers. This way
    // an involuntary arm movement has same threshold regardless of
    // the number of touching fingers.
    //
    // Note that we compute travel on view instead of travel on
    // the transformer. This way travel does not depend on transformations
    // of the space, but only the screen pixels.
    n = domain.length
    for (k in prevPointers) {
      if (prevPointers.hasOwnProperty(k) && nextPointers.hasOwnProperty()) {
        totalTravel += Math.abs(prevPointers[k][0] - nextPointers[k][0]) / n
        totalTravel += Math.abs(prevPointers[k][1] - nextPointers[k][1]) / n
      }
    }

    // Compute current position of the pivot on the transformer.
    // Pivot will be undefined if mode has no pivot but enables translation.
    pivot = toRawPivot(self.mode, self.transformer)
    // Get current nudged-compatible transformation type string
    type = utils.convertToTransformationType(mode)

    // Estimate optimal transformation
    tr = nudged.estimate(type, domain, range, pivot)
    itr = new taaspace.InvariantTransform(tr, transformer)

    // Apply the transformation to transformer. We also
    // memorize the new pointers for the next onMove call.
    // We want to memorize only their relative location on the transformer.
    // This way transformations of the view and parents between
    // onMove calls become part of the resulting transformation.
    // Note that transformBy emits 'transformed' that might eventually
    // cause transformations in the space. Therefore we should do all
    // coordinate-plane conversions before transformBy to avoid weird bugs.
    if (self.view === self.transformer) {
      // Somehow we do not need the following line with views:
      // pointersOnItem = multiplyLeft(nextPointersOnItem, tr)
      pointersOnItem = nextPointersOnItem
      transformer.transformBy(itr.inverse())
    } else {
      pointersOnItem = multiplyLeft(nextPointersOnItem, tr.inverse())
      transformer.transformBy(itr)
    }

    emitter.emit('transformmove', {
      distance: totalTravel,
      duration: Date.now() - startTime
    })
  }

  var onEnd = function (lastPointers) {
    emitter.emit('transformend', {
      distance: totalTravel,
      duration: Date.now() - startTime
    })

    pointersOnItem = {}

    if (mode.tap && totalTravel < mode.tapMaxTravel) {
      emitter.emit('tap', {
        distance: totalTravel,
        duration: Date.now() - startTime
      })
    }
  }

  this.recognizer = new Recognizer(element, {
    start: onStart,
    move: onMove,
    end: onEnd
  })
}

Manager.prototype.update = function (mode) {
  this.mode = mode
}

Manager.prototype.destroy = function () {
  this.recognizer.destroy()
}

module.exports = Manager
<commit_msg>Improve comments<commit_after>/*
Manager

Handles integration to Taaspace and higher level events such as tap event.

Keep all taaspace-related stuff in Manager.
Keep Recognizer compatible with all html elements.

Notes
  [1] For press event detection, we can place a threshold how far
      we allow fingers to move to still classify it as a press.
*/
var Recognizer = require('./Recognizer')
var utils = require('./utils')
var taaspace = require('taaspace')
var nudged = require('nudged')

var toRawPivot = function (mode, transformer) {
  // The pivot needs to be converted to [x, y]
  // on transformer's coordinate plane.
  //
  if (mode.pivot) {
    return mode.pivot.to(transformer).toArray()
  }

  // Use middle of transformer as a default pivot if translation not allowed.
  if (mode.translate === false) {
    return transformer.atMid().to(transformer).toArray()
  }

  // Return undefined so that when rawPivot is given as a parameter
  // to Recognizer, it looks like the parameter was not given.
}

var rebase = function (pointers, sourcePlane, targetPlane) {
  // Represent pointers on another SpacePlane
  //
  // Parameters
  //   pointers
  //     a map: id -> [x, y]
  //   sourcePlane
  //     a SpacePlane of the pointers
  //   targetPlane
  //     a SpacePlane of the returned pointers
  //
  if (sourcePlane === targetPlane) {
    return pointers
  }

  var k
  var result = {}
  var sourceToSpace = sourcePlane.getGlobalTransform().toSpace()
  var targetToSpace = targetPlane.getGlobalTransform().toSpace()
  var sourceToTarget = targetToSpace.inverse().multiplyRight(sourceToSpace)
  for (k in pointers) {
    if (pointers.hasOwnProperty(k)) {
      result[k] = sourceToTarget.transform(pointers[k])
    }
  }
  return result
}

var multiplyLeft = function (pointers, transform) {
  // Transform pointers. Regard pointers as a row vector
  // of column vectors. Multiply each column vector from left by
  // a transformation matrix.
  var k
  var result = {}
  for (k in pointers) {
    if (pointers.hasOwnProperty(k)) {
      result[k] = transform.transform(pointers[k])
    }
  }
  return result
}

var Manager = function (element, transformer, view, emitter, mode) {
  // Create a Manager
  //   var man = new Manager(...)
  //
  // Parameters:
  //   element
  //     HTML element to listen to
  //   transformer
  //     a taaspace.SpaceTransformer, the object to move
  //   view
  //     a taaspace.SpaceViewHTML
  //   emitter
  //     public emitter the user listens to
  //   mode
  //     a transformation type
  //
  var self = this

  this.mode = mode
  this.transformer = transformer
  this.view = view

  var startTime = null
  var totalTravel = null
  var pointersOnItem = {}

  var onStart = function (firstPointers) {
    startTime = Date.now()
    totalTravel = 0

    pointersOnItem = rebase(firstPointers, view, transformer)

    emitter.emit('transformstart', {
      distance: 0,
      duration: 0
    })
  }

  var onMove = function (prevPointers, nextPointers) {
    var k, n, pivot, type, tr, itr
    var domain = []
    var range = []

    // Current location of new pointers on the transformer.
    // This approach zeroes the effects caused by
    // 1) transformations of the view
    // 2) transformations of the parents of the transformer
    var nextPointersOnItem = rebase(nextPointers, view, transformer)

    // Set intersection. Do not use removed or appeared pointers
    // in estimation.
    for (k in pointersOnItem) {
      if (pointersOnItem.hasOwnProperty(k) &&
          nextPointersOnItem.hasOwnProperty(k)) {
        domain.push(pointersOnItem[k])
        range.push(nextPointersOnItem[k])
      }
    }

    // Accumulate to travelled distance. See [1]
    // Use Manhattan distance for simpler computation.
    // Goal is to form a threshold to filter out small
    // involuntary movement of the fingers or arm.
    //
    // N. Divide distance by number of fingers. This way
    // an involuntary arm movement has same threshold regardless of
    // the number of touching fingers.
    //
    // Note that we compute travel on view instead of travel on
    // the transformer. This way travel does not depend on transformations
    // of the space, but only the screen pixels.
    n = domain.length
    for (k in prevPointers) {
      if (prevPointers.hasOwnProperty(k) && nextPointers.hasOwnProperty()) {
        totalTravel += Math.abs(prevPointers[k][0] - nextPointers[k][0]) / n
        totalTravel += Math.abs(prevPointers[k][1] - nextPointers[k][1]) / n
      }
    }

    // Compute current position of the pivot on the transformer.
    // Pivot will be undefined if mode has no pivot but enables translation.
    pivot = toRawPivot(self.mode, self.transformer)
    // Get current nudged-compatible transformation type string
    type = utils.convertToTransformationType(mode)

    // Estimate optimal transformation
    tr = nudged.estimate(type, domain, range, pivot)
    itr = new taaspace.InvariantTransform(tr, transformer)

    // Apply the transformation to transformer. We also
    // memorize the new pointers for the next onMove call.
    // We want to memorize only their relative location on the transformer.
    // This way transformations of the view and parents between
    // onMove calls become part of the resulting transformation.
    // Note that transformBy emits 'transformed' that might eventually
    // cause transformations in the space. Therefore we should do all
    // coordinate-plane conversions before transformBy to avoid weird bugs.
    if (self.view === self.transformer) {
      // Somehow we do not need the following line with views:
      // pointersOnItem = multiplyLeft(nextPointersOnItem, tr)
      pointersOnItem = nextPointersOnItem
      transformer.transformBy(itr.inverse())
    } else {
      pointersOnItem = multiplyLeft(nextPointersOnItem, tr.inverse())
      transformer.transformBy(itr)
    }

    emitter.emit('transformmove', {
      distance: totalTravel,
      duration: Date.now() - startTime
    })
  }

  var onEnd = function (lastPointers) {
    emitter.emit('transformend', {
      distance: totalTravel,
      duration: Date.now() - startTime
    })

    pointersOnItem = {}

    if (mode.tap && totalTravel < mode.tapMaxTravel) {
      emitter.emit('tap', {
        distance: totalTravel,
        duration: Date.now() - startTime
      })
    }
  }

  this.recognizer = new Recognizer(element, {
    start: onStart,
    move: onMove,
    end: onEnd
  })
}

Manager.prototype.update = function (mode) {
  this.mode = mode
}

Manager.prototype.destroy = function () {
  this.recognizer.destroy()
}

module.exports = Manager
<|endoftext|>