<commit_before>const Discord = require("discord.js");

const client = new Discord.Client();
const config = require("./config.json");
const characterSheets = require("./characters.json");
const fs = require("fs");
const token = require("./token.json");

var dice = require("./dice.js");
var c = require("./characters.js");

client.login(token.token);

client.on("ready", () => {
	client.user.setGame("Pathfinder");
	console.log("Bot ready.");
});

client.on("message", (message) => {
	if (message.author.bot || !message.content.startsWith(config.prefix)) {
		return;
	}

	if (message.content.toLowerCase().includes("bleep")) {
		console.log("Bleeping detected.");
		commandBleep(message);
	} else if (message.content.startsWith(config.prefix + 'r')) {
		var diceRoll = new RegExp(config.diceRoll, 'i');

		if (diceRoll.test(message.content)) {
			commandRoll(message);
		} else {
			message.channel.send("I didn't understand that dice roll, sorry.");
		}
	} else if (message.content.startsWith(config.prefix + "attack")) {
		commandAttack(message);
	} else if (message.content.startsWith(config.prefix + "getAC")) {
		commandAC(message);
	} else if (message.content.startsWith(config.prefix + "bind ")) {
		commandBind(message);
	} else if (message.content.startsWith(config.prefix + "unbind ")) {
		commandUnbind(message);
	}
});

function commandBleep(message) {
	var bleeps = config.bleeps + 1;
	if (bleeps == 1) {
		message.channel.send("That's 1 time you've bleeped now.");
	} else {
		message.channel.send("That's " + bleeps + " times you've bleeped now.");
	}
	config.bleeps = bleeps;
	fs.writeFile("./config.json", JSON.stringify(config), (err) => console.error);
}

function commandRoll(message) {
	var diceRegex = new RegExp(config.dice, 'g');
	var negateRegex = new RegExp(config.negate);

	var response = message.author + " rolled: " + dice.formatRoll(dice.parseRoll(message.content));

	message.channel.send(response).catch(function () {
		message.channel.send("Why would you need to roll that many dice? If you absolutely must, break the roll up into several messages.");
	});
}

function commandBind(message) {
	var characterName = message.content.substring("!bind ".length, message.content.length);
	var response = "";

	for (var i = 0; i < characterSheets.length; i++) {
		if (characterName.toLowerCase() === characterSheets[i].name.toLowerCase()) {
			if (characterSheets[i].player === "") {
				characterSheets[i].player = message.author.id;
				response = characterSheets[i].name + " is now bound to " + message.author + ".";
			} else {
				response = "That character is already bound to " + client.users.get(characterSheets[i].player) + ".";
			}
		}
	}

	if (response === "") {
		response = "No available character with that name was found. Please try again.";
	}

	message.channel.send(response);
}

function commandUnbind(message) {
	var characterName = message.content.substring("!unbind ".length, message.content.length);
	var response = "";
	var gmRole = message.guild.roles.find("name", "dungeon master");

	if (!message.member.roles.has(gmRole.id)) {
		message.channel.send("Only a " + gmRole + " can unbind characters.");
		return;
	}

	for (var i = 0; i < characterSheets.length; i++) {
		if (characterName.toLowerCase() === characterSheets[i].name.toLowerCase()) {
			characterSheets[i].player = "";
			response = characterSheets[i].name + " has been unbound.";
		}
	}

	if (response === "") {
		response = "No character with that name was found. Please try again.";
	}

	message.channel.send(response);
}

function commandAttack(message) {
	var index = -1;

	for (var i = 0; i < characterSheets.length; i++) {
		if (characterSheets[i].player === message.author.id) {
			index = i;
		}
	}

	if (index === -1) {
		var response = "You do not have a character bound to you.";

		response += checkAvailable();
		message.channel.send(response);
		return;
	}

	message.channel.send(c.attack(characterSheets[index]));
}

function commandAC(message) {
	var pronoun = "";
	var index = -1;
	var availableCharacters = "";

	for (var i = 0; i < characterSheets.length; i++) {
		if (characterSheets[i].player === message.author.id) {
			index = i;
		}
	}

	console.log(availableCharacters);

	if (index === -1) {
		var response = "You do not have a character bound to you.";

		response += checkAvailable();
		message.channel.send(response);
		return;
	}

	if (characterSheets[index].name.endsWith("s")) {
		pronoun = "'";
	} else {
		pronoun = "'s";
	}

	var response = characterSheets[index].name + pronoun + " Armour Class is currently " + c.getAC(characterSheets[index]) + ".";
	message.channel.send(response);
}

function checkAvailable() {
	var availableCharacters = "";

	for (var i = 0; i < characterSheets.length; i++) {
		if (characterSheets[i].player === "") {
			availableCharacters += characterSheets[i].name + "; ";
		}
	}

	var response = "";

	if (availableCharacters.length > 0) {
		response +=  " The following characters are currently available (semicolon separated): \n";
		response += availableCharacters.substring(0, availableCharacters.length - 2);
		response += "\nUse the command !bind <character name> to take ownership of a character.";
	} else {
		response += " No characters are currently available. Please consult your GM.\n";
	}

	return response;
}
<commit_msg>Fixed bleeping so that it no longer overrides other commands.<commit_after>const Discord = require("discord.js");

const client = new Discord.Client();
const config = require("./config.json");
const characterSheets = require("./characters.json");
const fs = require("fs");
const token = require("./token.json");

var dice = require("./dice.js");
var c = require("./characters.js");

client.login(token.token);

client.on("ready", () => {
	client.user.setGame("Pathfinder");
	console.log("Bot ready.");
});

client.on("message", (message) => {
	if (message.author.bot || !message.content.startsWith(config.prefix)) {
		return;
	}

	if (message.content.toLowerCase().includes("bleep")) {
		console.log("Bleeping detected.");
		commandBleep(message);
	}

	if (message.content.startsWith(config.prefix + 'r')) {
		var diceRoll = new RegExp(config.diceRoll, 'i');

		if (diceRoll.test(message.content)) {
			commandRoll(message);
		} else {
			message.channel.send("I didn't understand that dice roll, sorry.");
		}
	} else if (message.content.startsWith(config.prefix + "attack")) {
		commandAttack(message);
	} else if (message.content.startsWith(config.prefix + "getAC")) {
		commandAC(message);
	} else if (message.content.startsWith(config.prefix + "bind ")) {
		commandBind(message);
	} else if (message.content.startsWith(config.prefix + "unbind ")) {
		commandUnbind(message);
	}
});

function commandBleep(message) {
	var bleeps = config.bleeps + 1;
	if (bleeps == 1) {
		message.channel.send("That's 1 time you've bleeped now.");
	} else {
		message.channel.send("That's " + bleeps + " times you've bleeped now.");
	}
	config.bleeps = bleeps;
	fs.writeFile("./config.json", JSON.stringify(config), (err) => console.error);
}

function commandRoll(message) {
	var diceRegex = new RegExp(config.dice, 'g');
	var negateRegex = new RegExp(config.negate);

	var response = message.author + " rolled: " + dice.formatRoll(dice.parseRoll(message.content));

	message.channel.send(response).catch(function () {
		message.channel.send("Why would you need to roll that many dice? If you absolutely must, break the roll up into several messages.");
	});
}

function commandBind(message) {
	var characterName = message.content.substring("!bind ".length, message.content.length);
	var response = "";

	for (var i = 0; i < characterSheets.length; i++) {
		if (characterName.toLowerCase() === characterSheets[i].name.toLowerCase()) {
			if (characterSheets[i].player === "") {
				characterSheets[i].player = message.author.id;
				response = characterSheets[i].name + " is now bound to " + message.author + ".";
			} else {
				response = "That character is already bound to " + client.users.get(characterSheets[i].player) + ".";
			}
		}
	}

	if (response === "") {
		response = "No available character with that name was found. Please try again.";
	}

	message.channel.send(response);
}

function commandUnbind(message) {
	var characterName = message.content.substring("!unbind ".length, message.content.length);
	var response = "";
	var gmRole = message.guild.roles.find("name", "dungeon master");

	if (!message.member.roles.has(gmRole.id)) {
		message.channel.send("Only a " + gmRole + " can unbind characters.");
		return;
	}

	for (var i = 0; i < characterSheets.length; i++) {
		if (characterName.toLowerCase() === characterSheets[i].name.toLowerCase()) {
			characterSheets[i].player = "";
			response = characterSheets[i].name + " has been unbound.";
		}
	}

	if (response === "") {
		response = "No character with that name was found. Please try again.";
	}

	message.channel.send(response);
}

function commandAttack(message) {
	var index = -1;

	for (var i = 0; i < characterSheets.length; i++) {
		if (characterSheets[i].player === message.author.id) {
			index = i;
		}
	}

	if (index === -1) {
		var response = "You do not have a character bound to you.";

		response += checkAvailable();
		message.channel.send(response);
		return;
	}

	message.channel.send(c.attack(characterSheets[index]));
}

function commandAC(message) {
	var pronoun = "";
	var index = -1;
	var availableCharacters = "";

	for (var i = 0; i < characterSheets.length; i++) {
		if (characterSheets[i].player === message.author.id) {
			index = i;
		}
	}

	console.log(availableCharacters);

	if (index === -1) {
		var response = "You do not have a character bound to you.";

		response += checkAvailable();
		message.channel.send(response);
		return;
	}

	if (characterSheets[index].name.endsWith("s")) {
		pronoun = "'";
	} else {
		pronoun = "'s";
	}

	var response = characterSheets[index].name + pronoun + " Armour Class is currently " + c.getAC(characterSheets[index]) + ".";
	message.channel.send(response);
}

function checkAvailable() {
	var availableCharacters = "";

	for (var i = 0; i < characterSheets.length; i++) {
		if (characterSheets[i].player === "") {
			availableCharacters += characterSheets[i].name + "; ";
		}
	}

	var response = "";

	if (availableCharacters.length > 0) {
		response +=  " The following characters are currently available (semicolon separated): \n";
		response += availableCharacters.substring(0, availableCharacters.length - 2);
		response += "\nUse the command !bind <character name> to take ownership of a character.";
	} else {
		response += " No characters are currently available. Please consult your GM.\n";
	}

	return response;
}
<|endoftext|>


<commit_before>/* jshint unused:false */
'use strict';

var syncedVariables = require('../synced_variables');
var io = {};
var server = require('../server');
var log = require('../logger')('nodecg/lib/extension_api');
var filteredConfig = require('../config').getFilteredConfig();
var utils = require('../util');

/**
 * Creates a new NodeCG extension API instance.
 * @constructor
 * @param {object} bundle The bundle object to build an API instance from.
 */
function NodeCG(bundle) {
    // Make bundle name and config publicly accessible
    this.bundleName = bundle.name;
    this.bundleConfig = bundle.config;

    // Make logger publicly accessible
    this.log = require('../logger')('extension/' + bundle.name);

    // Initialize internal data structures and make them accessible to public methods
    this.variables = {};
    this._handlers = [];
    this._varHandlers = [];

    io = server.getIO();
    var self = this;

    io.on('connection', function onConnection(socket) {
        socket.setMaxListeners(64); // Prevent console warnings when many extensions are installed
        socket.on('message', function onMessage(data, cb) {
            log.trace('[%s] Socket %s sent a message (callback: %s):', self.bundleName, socket.id, data, Boolean(cb));
            var len = self._handlers.length;
            for (var i = 0; i < len; i++) {
                var handler = self._handlers[i];
                if (data.messageName === handler.messageName &&
                    data.bundleName === handler.bundleName) {
                    handler.func(data.content, cb);
                }
            }
        });
    });

    // Create read-only config property, which contains the current filtered NodeCG config
    Object.defineProperty(this, 'config', {
        value: filteredConfig,
        writable: false,
        enumerable: true
    });
    Object.freeze(this.config);

    // ###syncedVariables events
    syncedVariables

        // This event is currently unused.
        .on('variableDeclared', function variableDeclared(data) {})

        // When the value of a syncedVar is changed, invoke 'setters' for it with the new value
        .on('variableAssigned', function variableAssigned(data) {
            log.trace('[%s] Variable assigned:', self.bundleName, data);
            var len =  self._varHandlers.length;
            for (var i = 0; i < len; i++) {
                var handler = self._varHandlers[i];
                if (data.variableName === handler.variableName &&
                    data.bundleName === handler.bundleName) {
                    handler.func(data.value);
                }
            }
        })

        // When a syncedVar is destroyed, remove its handlers and the property from `nodecg.variables`
        .on('variableDestroyed', function variableDestroyed(data) {
            log.trace('[%s] Variable destroyed:', self.bundleName, data);
            var len =  self._varHandlers.length;
            for (var i = 0; i < len; i++) {
                var handler = self._varHandlers[i];
                if (data.variableName === handler.variableName &&
                    data.bundleName === handler.bundleName) {
                    self._varHandlers.splice(i, 1);
                }
            }

            if (self.variables.hasOwnProperty(data.variableName)) {
                delete self.variables[data.variableName];
            }
        })

        // Completely drops all trace of all syncedVariables
        // Only used in automated tests.
        .on('variablesReset', function variablesReset() {
            self.variables = {};
            self._handlers = [];
            self._varHandlers = [];
            self.variables = {};
        });
}

// ###NodeCG prototype

/**
 * Sends a message within the current bundle.
 * @param {string}   messageName The name of the message.
 * @param {mixed}    [data]      The data to send.
 */
NodeCG.prototype.sendMessage = function (messageName, data) {
    this.sendMessageToBundle(messageName, this.bundleName, data);
};

/**
 * Sends a message to a specific bundle.
 * @param {string}   messageName The name of the message.
 * @param {string}   bundleName  The name of the target bundle.
 * @param {mixed}    [data]      The data to send
 */
NodeCG.prototype.sendMessageToBundle = function (messageName, bundleName, data) {
    log.trace('[%s] Sending message %s to bundle %s with data:',
        this.bundleName, messageName, bundleName, data);
    io.emit('message', {
        bundleName: bundleName,
        messageName: messageName,
        content: data
    });
};

/**
 * Listens for a message.
 * @param {string}   messageName                     The name of the message.
 * @param {string}   [bundleName=CURRENT_BUNDLE_NAME] The bundle namespace to in which to listen for this message
 * @param {function} handler                          The callback fired when this message is received.
 */
NodeCG.prototype.listenFor = function (messageName, bundleName, handler) {
    if (typeof handler === 'undefined') {
        handler = bundleName;
        bundleName = this.bundleName;
    }

    log.trace('[%s] Listening for %s from bundle %s', this.bundleName, messageName, bundleName);

    // Check if a handler already exists for this message
    var len =  this._handlers.length;
    for (var i = 0; i < len; i++) {
        var existingHandler = this._handlers[i];
        if (messageName === existingHandler.messageName && bundleName === existingHandler.bundleName) {
            log.error('%s attempted to declare a duplicate "listenFor" handler:',
                this.bundleName, bundleName, messageName);
            return;
        }
    }

    this._handlers.push({
        messageName: messageName,
        bundleName: bundleName,
        func: handler
    });
};

/**
 * Declares a synchronized variable. If the given variable name/bundle pair already exists, uses that.
 * @param {object} args                                  The arguments object.
 * @param {string} args.name                             The name of the synced variable.
 * @param {string} [args.bundleName=CURRENT_BUNDLE_NAME] The bundle namespace in which to create this variable.
 * @param {string} [args.setter]                         The callback to be fired when this variable changes.
 * @param {string} [args.value]                          The value that this variable should be initialized to.
 */
NodeCG.prototype.declareSyncedVar = function (args) {
    var setter = args.setter || function(){};
    var name = args.name || args.variableName;
    var bundle = args.bundleName || this.bundleName;
    var value = args.initialValue || args.initialVal;

    log.trace('[%s] Variable %s declared for bundle %s with value %s', this.bundleName, name, bundle, value);

    if (!name) {
        var msg = 'Attempted to declare an unnamed variable for bundle ' + bundle;
        var e = new Error(msg);
        log.error(msg);
        throw e;
    }

    // Check if a handler already exists for this variable
    var len =  this._varHandlers.length;
    for (var i = 0; i < len; i++) {
        var existingHandler = this._varHandlers[i];
        if (name === existingHandler.variableName && bundle === existingHandler.bundleName) {
            log.error('%s attempted to declare a duplicate synced var:', this.bundleName, bundle, name);
            return;
        }
    }

    // Add the 'setter' callback to the array of handlers that will be invoked
    // when the value of this variable changes
    this._varHandlers.push({
        bundleName: bundle,
        variableName: name,
        func: setter
    });

    Object.defineProperty(this.variables, name, {
        get: function() {
            return syncedVariables.find(bundle, name);
        },
        set: function(newValue) {
            syncedVariables.assign(bundle, name, newValue);
        },
        enumerable: true
    });

    // Run the setter with the initial value, or the found value if the var already existed
    setter(syncedVariables.findOrDeclare(bundle, name, value));
};

/**
 * Reads the value of a synchronized variable without creating a subscription to it.
 * @param {string}   name                         The name of the variable.
 * @param {string}   [bundle=CURRENT_BUNDLE_NAME] The bundle namespace to in which to look for this variable
 */
NodeCG.prototype.readSyncedVar = function(name, bundle) {
    bundle = bundle || this.bundleName;
    return syncedVariables.find(bundle, name);
};

/**
 * Destroys a synchronized variable, and causes it to be removed from all API instances.
 * @param {object} args            The arguments object.
 * @param {string} args.name       The name of the synced variable to destroy.
 * @param {string} args.bundleName The bundle namespace in which to destroy this variable.
 */
NodeCG.prototype.destroySyncedVar = function (args) {
    var name = args.variableName;
    var bundle = args.bundleName || this.bundleName;

    if (!name) {
        var msg = 'Attempted to destroy an unnamed variable for bundle ' + bundle;
        var e = new Error(msg);
        log.error(msg);
        throw e;
    }

    // Automatically invokes the 'variableDestroyed' listener in the constructor of NodeCG
    // which deleted the property from NodeCG.variables
    syncedVariables.destroy(bundle, name);
};

/**
 * Mounts express middleware to the main server express app.
 * See http://expressjs.com/api.html#app.use for usage.
 */
NodeCG.prototype.mount = server.mount;

NodeCG.prototype.util = {};

/**
 * Checks if a session is authorized. Intended to be used in express routes.
 * @param {object}   req   A HTTP request.
 * @param {object}   res   A HTTP response.
 * @param {function} next  The next middleware in the control flow.
 */
NodeCG.prototype.util.authCheck = utils.authCheck;

/**
 * Finds and returns a session that matches the given parameters.
 * @param {object} params A NeDB search query [https://github.com/louischatriot/nedb#finding-documents].
 * @returns {object}
 */
NodeCG.prototype.util.findSession = utils.findSession;

// Make all extensions accessible via 'nodecg.extensions'
Object.defineProperty(NodeCG.prototype, 'extensions', {
    get: function() {
        return server.getExtensions();
    },
    enumerable: true
});

module.exports = NodeCG;
<commit_msg>Add function to return the server IO object.<commit_after>/* jshint unused:false */
'use strict';

var syncedVariables = require('../synced_variables');
var io = {};
var server = require('../server');
var log = require('../logger')('nodecg/lib/extension_api');
var filteredConfig = require('../config').getFilteredConfig();
var utils = require('../util');

/**
 * Creates a new NodeCG extension API instance.
 * @constructor
 * @param {object} bundle The bundle object to build an API instance from.
 */
function NodeCG(bundle) {
    // Make bundle name and config publicly accessible
    this.bundleName = bundle.name;
    this.bundleConfig = bundle.config;

    // Make logger publicly accessible
    this.log = require('../logger')('extension/' + bundle.name);

    // Initialize internal data structures and make them accessible to public methods
    this.variables = {};
    this._handlers = [];
    this._varHandlers = [];

    io = server.getIO();
    var self = this;

    io.on('connection', function onConnection(socket) {
        socket.setMaxListeners(64); // Prevent console warnings when many extensions are installed
        socket.on('message', function onMessage(data, cb) {
            log.trace('[%s] Socket %s sent a message (callback: %s):', self.bundleName, socket.id, data, Boolean(cb));
            var len = self._handlers.length;
            for (var i = 0; i < len; i++) {
                var handler = self._handlers[i];
                if (data.messageName === handler.messageName &&
                    data.bundleName === handler.bundleName) {
                    handler.func(data.content, cb);
                }
            }
        });
    });

    // Create read-only config property, which contains the current filtered NodeCG config
    Object.defineProperty(this, 'config', {
        value: filteredConfig,
        writable: false,
        enumerable: true
    });
    Object.freeze(this.config);

    // ###syncedVariables events
    syncedVariables

        // This event is currently unused.
        .on('variableDeclared', function variableDeclared(data) {})

        // When the value of a syncedVar is changed, invoke 'setters' for it with the new value
        .on('variableAssigned', function variableAssigned(data) {
            log.trace('[%s] Variable assigned:', self.bundleName, data);
            var len =  self._varHandlers.length;
            for (var i = 0; i < len; i++) {
                var handler = self._varHandlers[i];
                if (data.variableName === handler.variableName &&
                    data.bundleName === handler.bundleName) {
                    handler.func(data.value);
                }
            }
        })

        // When a syncedVar is destroyed, remove its handlers and the property from `nodecg.variables`
        .on('variableDestroyed', function variableDestroyed(data) {
            log.trace('[%s] Variable destroyed:', self.bundleName, data);
            var len =  self._varHandlers.length;
            for (var i = 0; i < len; i++) {
                var handler = self._varHandlers[i];
                if (data.variableName === handler.variableName &&
                    data.bundleName === handler.bundleName) {
                    self._varHandlers.splice(i, 1);
                }
            }

            if (self.variables.hasOwnProperty(data.variableName)) {
                delete self.variables[data.variableName];
            }
        })

        // Completely drops all trace of all syncedVariables
        // Only used in automated tests.
        .on('variablesReset', function variablesReset() {
            self.variables = {};
            self._handlers = [];
            self._varHandlers = [];
            self.variables = {};
        });
}

// ###NodeCG prototype

/**
 * Sends a message within the current bundle.
 * @param {string}   messageName The name of the message.
 * @param {mixed}    [data]      The data to send.
 */
NodeCG.prototype.sendMessage = function (messageName, data) {
    this.sendMessageToBundle(messageName, this.bundleName, data);
};

/**
 * Sends a message to a specific bundle.
 * @param {string}   messageName The name of the message.
 * @param {string}   bundleName  The name of the target bundle.
 * @param {mixed}    [data]      The data to send
 */
NodeCG.prototype.sendMessageToBundle = function (messageName, bundleName, data) {
    log.trace('[%s] Sending message %s to bundle %s with data:',
        this.bundleName, messageName, bundleName, data);
    io.emit('message', {
        bundleName: bundleName,
        messageName: messageName,
        content: data
    });
};

/**
 * Listens for a message.
 * @param {string}   messageName                     The name of the message.
 * @param {string}   [bundleName=CURRENT_BUNDLE_NAME] The bundle namespace to in which to listen for this message
 * @param {function} handler                          The callback fired when this message is received.
 */
NodeCG.prototype.listenFor = function (messageName, bundleName, handler) {
    if (typeof handler === 'undefined') {
        handler = bundleName;
        bundleName = this.bundleName;
    }

    log.trace('[%s] Listening for %s from bundle %s', this.bundleName, messageName, bundleName);

    // Check if a handler already exists for this message
    var len =  this._handlers.length;
    for (var i = 0; i < len; i++) {
        var existingHandler = this._handlers[i];
        if (messageName === existingHandler.messageName && bundleName === existingHandler.bundleName) {
            log.error('%s attempted to declare a duplicate "listenFor" handler:',
                this.bundleName, bundleName, messageName);
            return;
        }
    }

    this._handlers.push({
        messageName: messageName,
        bundleName: bundleName,
        func: handler
    });
};

/**
 * Declares a synchronized variable. If the given variable name/bundle pair already exists, uses that.
 * @param {object} args                                  The arguments object.
 * @param {string} args.name                             The name of the synced variable.
 * @param {string} [args.bundleName=CURRENT_BUNDLE_NAME] The bundle namespace in which to create this variable.
 * @param {string} [args.setter]                         The callback to be fired when this variable changes.
 * @param {string} [args.value]                          The value that this variable should be initialized to.
 */
NodeCG.prototype.declareSyncedVar = function (args) {
    var setter = args.setter || function(){};
    var name = args.name || args.variableName;
    var bundle = args.bundleName || this.bundleName;
    var value = args.initialValue || args.initialVal;

    log.trace('[%s] Variable %s declared for bundle %s with value %s', this.bundleName, name, bundle, value);

    if (!name) {
        var msg = 'Attempted to declare an unnamed variable for bundle ' + bundle;
        var e = new Error(msg);
        log.error(msg);
        throw e;
    }

    // Check if a handler already exists for this variable
    var len =  this._varHandlers.length;
    for (var i = 0; i < len; i++) {
        var existingHandler = this._varHandlers[i];
        if (name === existingHandler.variableName && bundle === existingHandler.bundleName) {
            log.error('%s attempted to declare a duplicate synced var:', this.bundleName, bundle, name);
            return;
        }
    }

    // Add the 'setter' callback to the array of handlers that will be invoked
    // when the value of this variable changes
    this._varHandlers.push({
        bundleName: bundle,
        variableName: name,
        func: setter
    });

    Object.defineProperty(this.variables, name, {
        get: function() {
            return syncedVariables.find(bundle, name);
        },
        set: function(newValue) {
            syncedVariables.assign(bundle, name, newValue);
        },
        enumerable: true
    });

    // Run the setter with the initial value, or the found value if the var already existed
    setter(syncedVariables.findOrDeclare(bundle, name, value));
};

/**
 * Reads the value of a synchronized variable without creating a subscription to it.
 * @param {string}   name                         The name of the variable.
 * @param {string}   [bundle=CURRENT_BUNDLE_NAME] The bundle namespace to in which to look for this variable
 */
NodeCG.prototype.readSyncedVar = function(name, bundle) {
    bundle = bundle || this.bundleName;
    return syncedVariables.find(bundle, name);
};

/**
 * Destroys a synchronized variable, and causes it to be removed from all API instances.
 * @param {object} args            The arguments object.
 * @param {string} args.name       The name of the synced variable to destroy.
 * @param {string} args.bundleName The bundle namespace in which to destroy this variable.
 */
NodeCG.prototype.destroySyncedVar = function (args) {
    var name = args.variableName;
    var bundle = args.bundleName || this.bundleName;

    if (!name) {
        var msg = 'Attempted to destroy an unnamed variable for bundle ' + bundle;
        var e = new Error(msg);
        log.error(msg);
        throw e;
    }

    // Automatically invokes the 'variableDestroyed' listener in the constructor of NodeCG
    // which deleted the property from NodeCG.variables
    syncedVariables.destroy(bundle, name);
};

/**
 * Gets the server Socket.IO context.
 */
NodeCG.prototype.getIO = server.getIO;

/**
 * Mounts express middleware to the main server express app.
 * See http://expressjs.com/api.html#app.use for usage.
 */
NodeCG.prototype.mount = server.mount;

NodeCG.prototype.util = {};

/**
 * Checks if a session is authorized. Intended to be used in express routes.
 * @param {object}   req   A HTTP request.
 * @param {object}   res   A HTTP response.
 * @param {function} next  The next middleware in the control flow.
 */
NodeCG.prototype.util.authCheck = utils.authCheck;

/**
 * Finds and returns a session that matches the given parameters.
 * @param {object} params A NeDB search query [https://github.com/louischatriot/nedb#finding-documents].
 * @returns {object}
 */
NodeCG.prototype.util.findSession = utils.findSession;

// Make all extensions accessible via 'nodecg.extensions'
Object.defineProperty(NodeCG.prototype, 'extensions', {
    get: function() {
        return server.getExtensions();
    },
    enumerable: true
});

module.exports = NodeCG;
<|endoftext|>


<commit_before>// Generated by CoffeeScript 1.3.3
var BASE_PATH, DATA_PATH, LOG_FILE, PUBLIC_PATH, UPLOAD_PATH, app, checkPort, dir, dirs, echo, express, fs, getFiles, io, isSet, os, port, request, server, shorten, sortByDate, startListen, url, util, _i, _len;

express = require("express");

fs = require("fs");

os = require("os");

request = require("request");

util = require('util');

/* 定数
*/

BASE_PATH = "../../../../";

DATA_PATH = BASE_PATH + "data/";

UPLOAD_PATH = BASE_PATH + "uploads/";

PUBLIC_PATH = "../public/";

LOG_FILE = "log.txt";

/* 標準出力を上書き
*/

echo = console.log;

console.log = function() {
  var scr;
  scr = util.format.apply(this, arguments) + '\n';
  return fs.appendFileSync(LOG_FILE, scr);
};

/* app
*/

app = express();

app.use(require("connect").bodyParser());

/* static
*/

app.use("/", express["static"](__dirname + "/" + PUBLIC_PATH));

app.use("/uploads", express["static"](__dirname + "/" + UPLOAD_PATH));

/* データディレクトリがない場合は作成
*/

dirs = [DATA_PATH, UPLOAD_PATH];

for (_i = 0, _len = dirs.length; _i < _len; _i++) {
  dir = dirs[_i];
  try {
    fs.statSync(dir);
  } catch (e) {
    fs.mkdirSync(dir, "777");
  }
}

/* 内部で使う関数
*/

getFiles = function(dataPath) {
  var files, list;
  files = fs.readdirSync(dataPath);
  list = [];
  files.forEach(function(fileName) {
    var data, file;
    if (fileName.match(/.+\.json/)) {
      file = fs.readFileSync(dataPath + fileName) + "";
      if (file) {
        data = null;
        try {
          data = JSON.parse(file);
        } catch (e) {
          console.log(e);
        }
        if (data) {
          return list.push(data);
        }
      }
    }
  });
  return list;
};

isSet = function(arg) {
  return (arg != null) && arg !== "";
};

shorten = function(str, length) {
  var postfix, s;
  s = str.replace(/\n|\\|\/|\:|\*|\?|\"|\<|\>|\|/g, "");
  postfix = "...";
  if (s.length > length) {
    if (length > postfix.length) {
      return s.slice(0, length - postfix.length) + postfix;
    } else {
      return s.slice(0, length);
    }
  } else {
    return s;
  }
};

sortByDate = function(a, b) {
  var ax, bx;
  if (!a) {
    return -1;
  } else if (!b) {
    return 1;
  }
  ax = (new Date(a.date)).getTime();
  bx = (new Date(b.date)).getTime();
  if (ax == null) {
    ax = 0;
  }
  if (bx == null) {
    bx = 0;
  }
  return ax - bx;
};

/* API
*/

app.post("/upload", function(req, res) {
  var files, saved;
  files = req.files.files;
  if (typeof files.forEach !== 'function') {
    files = [files];
  }
  saved = [];
  files.forEach(function(file) {
    var data, newPath;
    data = fs.readFileSync(file.path);
    if (data) {
      newPath = __dirname + "/" + UPLOAD_PATH + file.name;
      fs.writeFileSync(newPath, data);
      console.log("saved:" + file.name);
      return saved.push(UPLOAD_PATH + file.name);
    }
  });
  return res.send(JSON.stringify(saved));
});

app.post("/write", function(req, res) {
  var data;
  data = req.body;
  if (isSet(data.name) && isSet(data.date) && isSet(data.text)) {
    return fs.writeFile(DATA_PATH + shorten(data.name, 10) + "「" + shorten(data.text, 20) + "」" + ".json", JSON.stringify(data), function(err) {
      if (err) {
        return res.send("0");
      } else {
        return res.send("1");
      }
    });
  }
});

app.get("/page/:page/:per", function(req, res) {
  var end, files, page, per, sliced, start;
  files = getFiles(DATA_PATH);
  files.sort(sortByDate);
  page = parseInt(req.params.page);
  per = parseInt(req.params.per);
  start = Math.max(files.length - (page + 1) * per, 0);
  end = Math.max(files.length - page * per, 0);
  sliced = files.slice(start, end);
  return res.send(JSON.stringify(sliced));
});

app.get("/read", function(req, res) {
  return res.send(JSON.stringify(getFiles(DATA_PATH)));
});

app.get("/exit", function(req, res) {
  console.log("httpからサーバーが終了されました");
  return process.exit(0);
});

/**
 * ポート番号の設定
 * macとwindowsではコロン(:)がディレクトリ名に使えない文字なので
 * ドライブ文字と被らない::をセパレータとして使う
 * 実行しているディレクトリ::portの並びで保存する
*/

checkPort = function() {
  var default_port, detected, dirToPortString, exists, file, lines, port, portDirString, portfile, runtime_dir, separator;
  dirToPortString = function(port) {
    return runtime_dir + separator + port + "\n";
  };
  portfile = os.tmpDir() + "/.dropboard.port";
  runtime_dir = __dirname;
  default_port = 50000;
  port = default_port;
  separator = "::";
  detected = false;
  exists = fs.existsSync(portfile);
  if (exists) {
    file = fs.readFileSync(portfile, "utf-8");
    lines = file.split("\n");
    lines.forEach(function(line) {
      var pear;
      pear = line.split(separator);
      if (pear.length === 2 && pear[0] === runtime_dir) {
        port = Number(pear[1]);
        return detected = true;
      }
    });
    if (!detected) {
      port = default_port + lines.length;
    }
  }
  portDirString = runtime_dir + separator + port + "\n";
  if (!detected || !exists) {
    fs.appendFileSync(portfile, portDirString, "utf-8");
  }
  return port;
};

port = checkPort();

url = "http://localhost:" + port + "/";

process.on('uncaughtException', function(err) {
  if (err.errno === 'EADDRINUSE') {
    return request(url + "exit", function(error, response, body) {
      return startListen();
    });
  }
});

/* WebSocketの準備
*/

server = require('http').createServer(app);

io = require('socket.io').listen(server);

io.set('log level', 1);

io.sockets.on('connection', function(socket) {
  /**
   * クライアントからの接続時にDATA_PATHの
   * 監視を開始する.
  */

  var watcher;
  watcher = fs.watch(DATA_PATH, function(event, filename) {
    /**
     * ディレクトリに変更があった際にupdateイベントを
     * クライアントにpushする.
    */
    return socket.emit('update', {});
  });
  /**
   * クライアントから切断された際に
   * ディレクトリの監視を停止する.
  */

  return socket.on('disconnect', function() {
    return watcher.close();
  });
});

/**
 * expressのインスタンスではなく
 * httpServerのインスタンスでlistenすること！
 * そうしないとsocket.io.jsが404になる.
*/

startListen = function() {
  return server.listen(port);
};

startListen();

echo(url);
<commit_msg>localhost以外からのアクセスは不可能にした.<commit_after>// Generated by CoffeeScript 1.3.3
var BASE_PATH, DATA_PATH, LOG_FILE, PUBLIC_PATH, UPLOAD_PATH, app, checkPort, dir, dirs, echo, express, fs, getFiles, io, isSet, os, port, request, server, shorten, sortByDate, startListen, url, util, _i, _len;

express = require("express");

fs = require("fs");

os = require("os");

request = require("request");

util = require('util');

/* 定数
*/

BASE_PATH = "../../../../";

DATA_PATH = BASE_PATH + "data/";

UPLOAD_PATH = BASE_PATH + "uploads/";

PUBLIC_PATH = "../public/";

LOG_FILE = "log.txt";

/* 標準出力を上書き
*/

echo = console.log;

console.log = function() {
  var scr;
  scr = util.format.apply(this, arguments) + '\n';
  return fs.appendFileSync(LOG_FILE, scr);
};

/* app
*/

app = express();

app.use(function(req, res, next){
  var hostname = req.headers.host;
  if(hostname == null || hostname == undefined){
    /* hostヘッダーがついていないのはHTTP1.1では
     * 規約違反である.*/
    res.send(400);
    return;
  }
  /*
   * localhostへのアクセスのみ許可.
   * 127.0.0.1も許可しない
   */
  if(hostname.match(/^localhost/) != null){
    next();
  }else{
    /* localhost以外からのアクセスは400で応答 */
    res.send(400);
  }
});

app.use(require("connect").bodyParser());

/* static
*/

app.use("/", express["static"](__dirname + "/" + PUBLIC_PATH));

app.use("/uploads", express["static"](__dirname + "/" + UPLOAD_PATH));

/* データディレクトリがない場合は作成
*/

dirs = [DATA_PATH, UPLOAD_PATH];

for (_i = 0, _len = dirs.length; _i < _len; _i++) {
  dir = dirs[_i];
  try {
    fs.statSync(dir);
  } catch (e) {
    fs.mkdirSync(dir, "777");
  }
}

/* 内部で使う関数
*/

getFiles = function(dataPath) {
  var files, list;
  files = fs.readdirSync(dataPath);
  list = [];
  files.forEach(function(fileName) {
    var data, file;
    if (fileName.match(/.+\.json/)) {
      file = fs.readFileSync(dataPath + fileName) + "";
      if (file) {
        data = null;
        try {
          data = JSON.parse(file);
        } catch (e) {
          console.log(e);
        }
        if (data) {
          return list.push(data);
        }
      }
    }
  });
  return list;
};

isSet = function(arg) {
  return (arg != null) && arg !== "";
};

shorten = function(str, length) {
  var postfix, s;
  s = str.replace(/\n|\\|\/|\:|\*|\?|\"|\<|\>|\|/g, "");
  postfix = "...";
  if (s.length > length) {
    if (length > postfix.length) {
      return s.slice(0, length - postfix.length) + postfix;
    } else {
      return s.slice(0, length);
    }
  } else {
    return s;
  }
};

sortByDate = function(a, b) {
  var ax, bx;
  if (!a) {
    return -1;
  } else if (!b) {
    return 1;
  }
  ax = (new Date(a.date)).getTime();
  bx = (new Date(b.date)).getTime();
  if (ax == null) {
    ax = 0;
  }
  if (bx == null) {
    bx = 0;
  }
  return ax - bx;
};

/* API
*/

app.post("/upload", function(req, res) {
  var files, saved;
  files = req.files.files;
  if (typeof files.forEach !== 'function') {
    files = [files];
  }
  saved = [];
  files.forEach(function(file) {
    var data, newPath;
    data = fs.readFileSync(file.path);
    if (data) {
      newPath = __dirname + "/" + UPLOAD_PATH + file.name;
      fs.writeFileSync(newPath, data);
      console.log("saved:" + file.name);
      return saved.push(UPLOAD_PATH + file.name);
    }
  });
  return res.send(JSON.stringify(saved));
});

app.post("/write", function(req, res) {
  var data;
  data = req.body;
  if (isSet(data.name) && isSet(data.date) && isSet(data.text)) {
    return fs.writeFile(DATA_PATH + shorten(data.name, 10) + "「" + shorten(data.text, 20) + "」" + ".json", JSON.stringify(data), function(err) {
      if (err) {
        return res.send("0");
      } else {
        return res.send("1");
      }
    });
  }
});

app.get("/page/:page/:per", function(req, res) {
  var end, files, page, per, sliced, start;
  files = getFiles(DATA_PATH);
  files.sort(sortByDate);
  page = parseInt(req.params.page);
  per = parseInt(req.params.per);
  start = Math.max(files.length - (page + 1) * per, 0);
  end = Math.max(files.length - page * per, 0);
  sliced = files.slice(start, end);
  return res.send(JSON.stringify(sliced));
});

app.get("/read", function(req, res) {
  return res.send(JSON.stringify(getFiles(DATA_PATH)));
});

app.get("/exit", function(req, res) {
  console.log("httpからサーバーが終了されました");
  return process.exit(0);
});

/**
 * ポート番号の設定
 * macとwindowsではコロン(:)がディレクトリ名に使えない文字なので
 * ドライブ文字と被らない::をセパレータとして使う
 * 実行しているディレクトリ::portの並びで保存する
*/

checkPort = function() {
  var default_port, detected, dirToPortString, exists, file, lines, port, portDirString, portfile, runtime_dir, separator;
  dirToPortString = function(port) {
    return runtime_dir + separator + port + "\n";
  };
  portfile = os.tmpDir() + "/.dropboard.port";
  runtime_dir = __dirname;
  default_port = 50000;
  port = default_port;
  separator = "::";
  detected = false;
  exists = fs.existsSync(portfile);
  if (exists) {
    file = fs.readFileSync(portfile, "utf-8");
    lines = file.split("\n");
    lines.forEach(function(line) {
      var pear;
      pear = line.split(separator);
      if (pear.length === 2 && pear[0] === runtime_dir) {
        port = Number(pear[1]);
        return detected = true;
      }
    });
    if (!detected) {
      port = default_port + lines.length;
    }
  }
  portDirString = runtime_dir + separator + port + "\n";
  if (!detected || !exists) {
    fs.appendFileSync(portfile, portDirString, "utf-8");
  }
  return port;
};

port = checkPort();

url = "http://localhost:" + port + "/";

process.on('uncaughtException', function(err) {
  if (err.errno === 'EADDRINUSE') {
    return request(url + "exit", function(error, response, body) {
      return startListen();
    });
  }
});

/* WebSocketの準備
*/

server = require('http').createServer(app);

io = require('socket.io').listen(server);

io.set('log level', 1);

io.sockets.on('connection', function(socket) {
  /**
   * クライアントからの接続時にDATA_PATHの
   * 監視を開始する.
  */

  var watcher;
  watcher = fs.watch(DATA_PATH, function(event, filename) {
    /**
     * ディレクトリに変更があった際にupdateイベントを
     * クライアントにpushする.
    */
    return socket.emit('update', {});
  });
  /**
   * クライアントから切断された際に
   * ディレクトリの監視を停止する.
  */

  return socket.on('disconnect', function() {
    return watcher.close();
  });
});

/**
 * expressのインスタンスではなく
 * httpServerのインスタンスでlistenすること！
 * そうしないとsocket.io.jsが404になる.
*/

startListen = function() {
  return server.listen(port);
};

startListen();

echo(url);
<|endoftext|>


<commit_before>var topSearchResults = new Bloodhound({
    datumTokenizer: Bloodhound.tokenizers.obj.whitespace("DisplayText"),
    queryTokenizer: Bloodhound.tokenizers.whitespace,
    limit: 10,
    prefetch: '/api/Prefetch/GetConferences',
    remote: '/api/Query/GetConferences/%QUERY'
});
topSearchResults.initialize();
//var topPersonSearchResults = new Bloodhound({
//    datumTokenizer: Bloodhound.tokenizers.obj.whitespace("DisplayText"),
//    queryTokenizer: Bloodhound.tokenizers.whitespace,
//    limit: 10,
//    prefetch: '/api/Prefetch/GetPeople',
//    remote: '/api/Query/GetResults/query=%QUERY'

//});
//topPersonSearchResults.initialize();

//var searches = [{ "Key": "homepages/d/TorbenDohrn", "FoundBy": "none", "DisplayText": "Torben Dohrn", "SearchResultSourceType": 0 }, { "Key": "homepages/d/FabianDohrn", "FoundBy": "none", "DisplayText": "Fred Dohrn", "SearchResultSourceType": 0 }, { "Key": "conf/l/lak", "FoundBy": "none", "DisplayText": "Learning Analyics Konference", "SearchResultSourceType": 1 }];
//var lok = ['Fred', 'Bubl'];
//var states = new Bloodhound({
//    datumTokenizer: Bloodhound.tokenizers.obj.whitespace('value'),
//    queryTokenizer: Bloodhound.tokenizers.whitespace,
//    // `states` is an array of state names defined in "The Basics"
//    local: $.map(searches, function (singleSearch) { return { value: singleSearch.DisplayText }; })
//});
//states.initialize();
//var keys = new Bloodhound({
//    datumTokenizer: Bloodhound.tokenizers.obj.whitespace('klo'),
//    queryTokenizer: Bloodhound.tokenizers.whitespace,
//    // `states` is an array of state names defined in "The Basics"
//    local: $.map(lok, function (lus) { return { klo: lus }; })
//});

//keys.initialize();

$(document).ready(function () {
    $('#search .typeahead').typeahead(
    {
        hint: true,
        highlight: true,
        minLength: 1
    },
    {
        name: 'topSearchResults',
        displayKey: 'DisplayText',
        // `ttAdapter` wraps the suggestion engine in an adapter that
        // is compatible with the typeahead jQuery plugin
        source: topSearchResults.ttAdapter(),
        templates: {
            header: '
Konferenzen
',
            empty: [
                '
',
                'Es konnten keine Konferenzen gefunden werden',
                '
'
            ].join('\n'),
}
    }

    //,{
    //    name: 'topPersonSearchResults',
    //    displayKey: 'DisplayText',
    //    // `ttAdapter` wraps the suggestion engine in an adapter that
    //    // is compatible with the typeahead jQuery plugin
    //    source: topPersonSearchResults.ttAdapter(),
    //    templates: {
    //        header: '
Personen
'
    //    }
    //}
    ).on('typeahead:selected', function (obj, datum) {
        //sendAjaxRequest("GET", function() {}, datum.Key);
        getAllItems(datum.Key);
    });
});
<commit_msg>started w<commit_after>var topSearchResults = new Bloodhound({
    datumTokenizer: Bloodhound.tokenizers.obj.whitespace("DisplayText"),
    queryTokenizer: Bloodhound.tokenizers.whitespace,
    limit: 10,
    prefetch: '/api/Prefetch/GetConferences',
    remote: '/api/Query/GetConferences/%QUERY'
});
topSearchResults.initialize();

var publications = new Bloodhound({
    datumTokenizer: Bloodhound.tokenizers.obj.whitespace("DisplayText"),
    queryTokenizer: Bloodhound.tokenizers.whitespace,
    limit: 10,
    //prefetch: '/api/Prefetch/GetConferences',
    remote: {
        url: '/api/Query/GetPublications/',
        replace: function(url, encodedQuery) {
            return url + encodeURIComponent($.trim(decodeURIComponent(encodedQuery)));
        }
    }
});
//publications.initialize();

//var topPersonSearchResults = new Bloodhound({
//    datumTokenizer: Bloodhound.tokenizers.obj.whitespace("DisplayText"),
//    queryTokenizer: Bloodhound.tokenizers.whitespace,
//    limit: 10,
//    prefetch: '/api/Prefetch/GetPeople',
//    remote: '/api/Query/GetResults/query=%QUERY'

//});
//topPersonSearchResults.initialize();

//var searches = [{ "Key": "homepages/d/TorbenDohrn", "FoundBy": "none", "DisplayText": "Torben Dohrn", "SearchResultSourceType": 0 }, { "Key": "homepages/d/FabianDohrn", "FoundBy": "none", "DisplayText": "Fred Dohrn", "SearchResultSourceType": 0 }, { "Key": "conf/l/lak", "FoundBy": "none", "DisplayText": "Learning Analyics Konference", "SearchResultSourceType": 1 }];
//var lok = ['Fred', 'Bubl'];
//var states = new Bloodhound({
//    datumTokenizer: Bloodhound.tokenizers.obj.whitespace('value'),
//    queryTokenizer: Bloodhound.tokenizers.whitespace,
//    // `states` is an array of state names defined in "The Basics"
//    local: $.map(searches, function (singleSearch) { return { value: singleSearch.DisplayText }; })
//});
//states.initialize();
//var keys = new Bloodhound({
//    datumTokenizer: Bloodhound.tokenizers.obj.whitespace('klo'),
//    queryTokenizer: Bloodhound.tokenizers.whitespace,
//    // `states` is an array of state names defined in "The Basics"
//    local: $.map(lok, function (lus) { return { klo: lus }; })
//});

//keys.initialize();

$(document).ready(function () {
    $('#search .typeahead').typeahead(
    {
        hint: true,
        highlight: true,
        minLength: 1
    },
    {
        name: 'topSearchResults',
        displayKey: 'DisplayText',
        // `ttAdapter` wraps the suggestion engine in an adapter that
        // is compatible with the typeahead jQuery plugin
        source: topSearchResults.ttAdapter(),
        templates: {
            header: '
Konferenzen
',
            empty: [
                '
',
                'Es konnten keine Konferenzen gefunden werden',
                '
'
            ].join('\n'),
}
    }

    ,{
        name: 'publications',
        displayKey: 'DisplayText',
        // `ttAdapter` wraps the suggestion engine in an adapter that
        // is compatible with the typeahead jQuery plugin
        source: publications.ttAdapter(),
        templates: {
            header: '
Publikationen
',
            empty: [
                '
',
                'Es konnten keine Publikationen gefunden werden',
                '
'
            ].join('\n'),
        }
    }
    ).on('typeahead:selected', function (obj, datum) {
        //sendAjaxRequest("GET", function() {}, datum.Key);
        getAllItems(datum.Key);
    });
});
<|endoftext|>


<commit_before>ddescribe('Autocomplete-aware form submit directive', function() {

    var $rootScope, $scope, el, $compile;
    var template = "
<form name=\"loginForm\" af-submit=\"login()\">\n    <input type=\"text\" name=\"username\" id=\"username\" ng-model=\"username\" data-af-enabled/>\n    <input type=\"password\" name=\"password\" id=\"password\" ng-model=\"password\" data-af-enabled/>\n    <input type=\"text\" name=\"notAfAware\" id=\"notAfAware\" ng-model=\"notAfAware\"/>\n</form>
";

    var user = 'john';
    var pass = 'secret';

    beforeEach(module('codebrag.common.directives'));

    beforeEach(inject(function (_$rootScope_, _$compile_) {
        $compile = _$compile_;
        $rootScope = _$rootScope_;
        $scope = $rootScope.$new();
        el = angular.element(template);
        $compile(el)($scope);
        $scope.login = jasmine.createSpy();
    }));

    afterEach(function() {
        expect($scope.login).toHaveBeenCalled();
    });

    it('should submit form via form.submit', function() {
        el.find('form').submit();
    });

    it('model should be undefined when submitted with no values set', function() {
        // given
        // no values in inputs

        // when
        form(el).submit();

        // then
        expect($scope.username).toBeUndefined();
        expect($scope.password).toBeUndefined();
    });

    it('model should be set correctly when values set outside of Angular (e.g. browser-managed passwords)', function() {
        // given
        usernameInput(el).val(user);
        passwordInput(el).val(pass);

        // when
        form(el).submit();

        // then
        expect($scope.username).toBe(user);
        expect($scope.password).toBe(pass);
    });

    it('should not update bindings when field not is not ac-aware and value is set outside', function() {
        // given
        notAfAwareInput(el).val('this should have model value undefined');

        // when
        form(el).submit();

        // then
        expect($scope.notAfAware).toBeUndefined();
    });

    it('should update bindings when values set within angular lifecycle', function() {
        // given
        userTypes(user, usernameInput(el));
        userTypes('dummy value', notAfAwareInput(el));

        // when
        form(el).submit();

        // then
        expect($scope.username).toBe(user);
        expect($scope.notAfAware).toBe('dummy value');
    });

    function userTypes(value, input) {
        input.val(value);
        input.trigger('input');
    }

    function form(el) {
        return el.find('form');
    }

    function usernameInput(el) {
        return $(el[0]).find('#username');
    }

    function passwordInput(el) {
        return $(el[0]).find('#password');
    }

    function notAfAwareInput(el) {
        return $(el[0]).find('#notAfAware');
    }

});<commit_msg>Enable all tests back.<commit_after>describe('Autocomplete-aware form submit directive', function() {

    var $rootScope, $scope, el, $compile;
    var template = "
<form name=\"loginForm\" af-submit=\"login()\">\n    <input type=\"text\" name=\"username\" id=\"username\" ng-model=\"username\" data-af-enabled/>\n    <input type=\"password\" name=\"password\" id=\"password\" ng-model=\"password\" data-af-enabled/>\n    <input type=\"text\" name=\"notAfAware\" id=\"notAfAware\" ng-model=\"notAfAware\"/>\n</form>
";

    var user = 'john';
    var pass = 'secret';

    beforeEach(module('codebrag.common.directives'));

    beforeEach(inject(function (_$rootScope_, _$compile_) {
        $compile = _$compile_;
        $rootScope = _$rootScope_;
        $scope = $rootScope.$new();
        el = angular.element(template);
        $compile(el)($scope);
        $scope.login = jasmine.createSpy();
    }));

    afterEach(function() {
        expect($scope.login).toHaveBeenCalled();
    });

    it('should submit form via form.submit', function() {
        el.find('form').submit();
    });

    it('model should be undefined when submitted with no values set', function() {
        // given
        // no values in inputs

        // when
        form(el).submit();

        // then
        expect($scope.username).toBeUndefined();
        expect($scope.password).toBeUndefined();
    });

    it('model should be set correctly when values set outside of Angular (e.g. browser-managed passwords)', function() {
        // given
        usernameInput(el).val(user);
        passwordInput(el).val(pass);

        // when
        form(el).submit();

        // then
        expect($scope.username).toBe(user);
        expect($scope.password).toBe(pass);
    });

    it('should not update bindings when field not is not ac-aware and value is set outside', function() {
        // given
        notAfAwareInput(el).val('this should have model value undefined');

        // when
        form(el).submit();

        // then
        expect($scope.notAfAware).toBeUndefined();
    });

    it('should update bindings when values set within angular lifecycle', function() {
        // given
        userTypes(user, usernameInput(el));
        userTypes('dummy value', notAfAwareInput(el));

        // when
        form(el).submit();

        // then
        expect($scope.username).toBe(user);
        expect($scope.notAfAware).toBe('dummy value');
    });

    function userTypes(value, input) {
        input.val(value);
        input.trigger('input');
    }

    function form(el) {
        return el.find('form');
    }

    function usernameInput(el) {
        return $(el[0]).find('#username');
    }

    function passwordInput(el) {
        return $(el[0]).find('#password');
    }

    function notAfAwareInput(el) {
        return $(el[0]).find('#notAfAware');
    }

});<|endoftext|>


<commit_before>import * as R from 'ramda'
import * as Yup from 'yup'

import { NumberInput } from 'src/components/inputs/formik'
import Autocomplete from 'src/components/inputs/formik/Autocomplete.js'
import { disabledColor } from 'src/styling/variables'
import { CURRENCY_MAX } from 'src/utils/constants'
import { transformNumber } from 'src/utils/number'

const classes = {
  editDisabled: {
    color: disabledColor
  }
}
const filterClass = type => R.filter(it => it.class === type)
const filterCoins = ({ id }) => R.filter(it => R.contains(id)(it.cryptos))

const WalletSchema = Yup.object().shape({
  ticker: Yup.string().required(),
  wallet: Yup.string().required(),
  exchange: Yup.string().required(),
  zeroConf: Yup.string().required(),
  zeroConfLimit: Yup.number()
    .integer()
    .required('Zero Conf Limit is a required field')
    .min(0)
    .max(CURRENCY_MAX)
    .transform(transformNumber)
})

const getElements = (cryptoCurrencies, accounts, onChange, wizard = false) => {
  const widthAdjust = wizard ? 11 : 0
  const viewCryptoCurrency = it => {
    const currencyDisplay = R.compose(
      R.prop(['display']),
      R.find(R.propEq('code', it))
    )(cryptoCurrencies)
    return currencyDisplay
  }
  const filterOptions = type => filterClass(type)(accounts || [])

  const getDisplayName = type => it =>
    R.compose(
      R.prop('display'),
      R.find(R.propEq('code', it))
    )(filterOptions(type))

  const getOptions = R.curry((option, it) =>
    filterCoins(it)(filterOptions(option))
  )

  return [
    {
      name: 'id',
      header: 'Cryptocurrency',
      width: 180 - widthAdjust,
      view: viewCryptoCurrency,
      size: 'sm',
      editable: false
    },
    {
      name: 'ticker',
      size: 'sm',
      stripe: true,
      view: getDisplayName('ticker'),
      width: 190 - widthAdjust,
      input: Autocomplete,
      inputProps: {
        options: getOptions('ticker'),
        valueProp: 'code',
        labelProp: 'display',
        optionsLimit: null
      }
    },
    {
      name: 'wallet',
      size: 'sm',
      stripe: true,
      view: getDisplayName('wallet'),
      width: 190 - widthAdjust,
      input: Autocomplete,
      inputProps: {
        options: getOptions('wallet'),
        valueProp: 'code',
        labelProp: 'display',
        optionsLimit: null,
        onChange
      }
    },
    {
      name: 'exchange',
      size: 'sm',
      stripe: true,
      view: getDisplayName('exchange'),
      width: 190 - widthAdjust,
      input: Autocomplete,
      inputProps: {
        options: getOptions('exchange'),
        valueProp: 'code',
        labelProp: 'display',
        optionsLimit: null,
        onChange
      }
    },
    {
      name: 'zeroConf',
      size: 'sm',
      stripe: true,
      view: getDisplayName('zeroConf'),
      input: Autocomplete,
      width: 190 - widthAdjust,
      inputProps: {
        options: getOptions('zeroConf'),
        valueProp: 'code',
        labelProp: 'display',
        optionsLimit: null,
        onChange
      }
    },
    {
      name: 'zeroConfLimit',
      size: 'sm',
      stripe: true,
      view: (it, row) =>
        row.id === 'ETH' ? {it} : it,
      input: NumberInput,
      width: 190 - widthAdjust,
      inputProps: {
        decimalPlaces: 0
      },
      editable: row => row.id !== 'ETH'
    }
  ]
}

export { WalletSchema, getElements, filterClass }
<commit_msg>fix: renamed 'zero conf' to 'confidence'<commit_after>import * as R from 'ramda'
import * as Yup from 'yup'

import { NumberInput } from 'src/components/inputs/formik'
import Autocomplete from 'src/components/inputs/formik/Autocomplete.js'
import { disabledColor } from 'src/styling/variables'
import { CURRENCY_MAX } from 'src/utils/constants'
import { transformNumber } from 'src/utils/number'

const classes = {
  editDisabled: {
    color: disabledColor
  }
}
const filterClass = type => R.filter(it => it.class === type)
const filterCoins = ({ id }) => R.filter(it => R.contains(id)(it.cryptos))

const WalletSchema = Yup.object().shape({
  ticker: Yup.string().required(),
  wallet: Yup.string().required(),
  exchange: Yup.string().required(),
  zeroConf: Yup.string().required(),
  zeroConfLimit: Yup.number()
    .integer()
    .required('Zero Conf Limit is a required field')
    .min(0)
    .max(CURRENCY_MAX)
    .transform(transformNumber)
})

const getElements = (cryptoCurrencies, accounts, onChange, wizard = false) => {
  const widthAdjust = wizard ? 11 : 0
  const viewCryptoCurrency = it => {
    const currencyDisplay = R.compose(
      R.prop(['display']),
      R.find(R.propEq('code', it))
    )(cryptoCurrencies)
    return currencyDisplay
  }
  const filterOptions = type => filterClass(type)(accounts || [])

  const getDisplayName = type => it =>
    R.compose(
      R.prop('display'),
      R.find(R.propEq('code', it))
    )(filterOptions(type))

  const getOptions = R.curry((option, it) =>
    filterCoins(it)(filterOptions(option))
  )

  return [
    {
      name: 'id',
      header: 'Cryptocurrency',
      width: 180 - widthAdjust,
      view: viewCryptoCurrency,
      size: 'sm',
      editable: false
    },
    {
      name: 'ticker',
      size: 'sm',
      stripe: true,
      view: getDisplayName('ticker'),
      width: 190 - widthAdjust,
      input: Autocomplete,
      inputProps: {
        options: getOptions('ticker'),
        valueProp: 'code',
        labelProp: 'display',
        optionsLimit: null
      }
    },
    {
      name: 'wallet',
      size: 'sm',
      stripe: true,
      view: getDisplayName('wallet'),
      width: 190 - widthAdjust,
      input: Autocomplete,
      inputProps: {
        options: getOptions('wallet'),
        valueProp: 'code',
        labelProp: 'display',
        optionsLimit: null,
        onChange
      }
    },
    {
      name: 'exchange',
      size: 'sm',
      stripe: true,
      view: getDisplayName('exchange'),
      width: 190 - widthAdjust,
      input: Autocomplete,
      inputProps: {
        options: getOptions('exchange'),
        valueProp: 'code',
        labelProp: 'display',
        optionsLimit: null,
        onChange
      }
    },
    {
      name: 'zeroConf',
      header: 'Confidence Checking',
      size: 'sm',
      stripe: true,
      view: getDisplayName('zeroConf'),
      input: Autocomplete,
      width: 220 - widthAdjust,
      inputProps: {
        options: getOptions('zeroConf'),
        valueProp: 'code',
        labelProp: 'display',
        optionsLimit: null,
        onChange
      }
    },
    {
      name: 'zeroConfLimit',
      size: 'sm',
      stripe: true,
      view: (it, row) =>
        row.id === 'ETH' ? {it} : it,
      input: NumberInput,
      width: 190 - widthAdjust,
      inputProps: {
        decimalPlaces: 0
      },
      editable: row => row.id !== 'ETH'
    }
  ]
}

export { WalletSchema, getElements, filterClass }
<|endoftext|>


<commit_before>
describe('Unit: accelSegmentFactory testing', function() {
  "use strict";

  var accelSegmentFactory;

  beforeEach(function() {
    module('profileEditor');

    inject(function(_accelSegmentFactory_) {
      accelSegmentFactory = _accelSegmentFactory_;
    });
  });

  it('should create an accel segment (t0=0,tf=2,p0=0,v0=0,vf=10,j=0.5) and correctly evalute position and velocities', function(){

    // https://www.desmos.com/calculator/kihyp1kjux
    debugger;
    var seg = accelSegmentFactory.MakeFromVelocity(0,2,0,0,10,0.5);

    expect(seg.AllSegments().length).toBe(3);

    var seg1=seg.AllSegments()[0];
    var seg2=seg.AllSegments()[1];
    var seg3=seg.AllSegments()[2];

    expect(seg1.initialTime).toBe(0);
    expect(seg1.finalTime).toBe(0.5);
    expect(seg1.EvaluatePositionAt(0)).toBe(0);
    expect(seg1.EvaluatePositionAt(0.5)).toBeCloseTo(0.2777777,4);

    expect(seg2.initialTime).toBe(0.5);
    expect(seg2.finalTime).toBe(1.5);
    expect(seg2.EvaluatePositionAt(0.5)).toBeCloseTo(0.2777777,4);
    expect(seg2.EvaluatePositionAt(1.5)).toBeCloseTo(5.2777777777777,4);

    expect(seg3.initialTime).toBe(1.5);
    expect(seg3.finalTime).toBe(2);
    expect(seg3.EvaluatePositionAt(1.5)).toBeCloseTo(5.2777777777777,4);
    expect(seg3.EvaluatePositionAt(2)).toBe(10);

  });

  it('should create an accel segment (t0=2,tf=4,p0=0.16667,v0=10,vf=0,j=0.5) and correctly evalute position and velocities', function(){

    // https://www.desmos.com/calculator/kihyp1kjux
    var seg = accelSegmentFactory.MakeFromVelocity(2,4,1/6,10,0,0.5);

    expect(seg.AllSegments().length).toBe(3);

    var seg1=seg.AllSegments()[0];
    var seg2=seg.AllSegments()[1];
    var seg3=seg.AllSegments()[2];

    expect(seg1.initialTime).toBe(2);
    expect(seg1.finalTime).toBe(2.5);

    expect(seg2.initialTime).toBe(2.5);
    expect(seg2.finalTime).toBe(3.5);

    expect(seg3.initialTime).toBe(3.5);
    expect(seg3.finalTime).toBe(4);

  });

});

<commit_msg>accel factory NOW (for reals) generating correct basic segments<commit_after>
describe('Unit: accelSegmentFactory testing', function() {
  "use strict";

  var accelSegmentFactory;

  beforeEach(function() {
    module('profileEditor');

    inject(function(_accelSegmentFactory_) {
      accelSegmentFactory = _accelSegmentFactory_;
    });
  });

  it('should create an accel segment (t0=0,tf=2,p0=0,v0=0,vf=10,j=0.5) and correctly evalute position and velocities', function(){

    // https://www.desmos.com/calculator/kihyp1kjux

    var seg = accelSegmentFactory.MakeFromVelocity(0,2,0,0,10,0.5);

    expect(seg.AllSegments().length).toBe(3);

    var seg1=seg.AllSegments()[0];
    var seg2=seg.AllSegments()[1];
    var seg3=seg.AllSegments()[2];

    expect(seg1.initialTime).toBe(0);
    expect(seg1.finalTime).toBe(0.5);
    expect(seg1.EvaluatePositionAt(0)).toBe(0);
    expect(seg1.EvaluatePositionAt(0.5)).toBeCloseTo(0.2777777,4);

    expect(seg2.initialTime).toBe(0.5);
    expect(seg2.finalTime).toBe(1.5);
    expect(seg2.EvaluatePositionAt(0.5)).toBeCloseTo(0.2777777,4);
    expect(seg2.EvaluatePositionAt(1.5)).toBeCloseTo(5.2777777777777,4);

    expect(seg3.initialTime).toBe(1.5);
    expect(seg3.finalTime).toBe(2);
    expect(seg3.EvaluatePositionAt(1.5)).toBeCloseTo(5.2777777777777,4);
    expect(seg3.EvaluatePositionAt(2)).toBe(10);

  });

  it('should create an accel segment (t0=2,tf=4,p0=0.16667,v0=10,vf=0,j=0.5) and correctly evalute position and velocities', function(){
            debugger;

    var seg = accelSegmentFactory.MakeFromVelocity(2,4,10,10,0,0.5);

    expect(seg.AllSegments().length).toBe(3);

    var seg1=seg.AllSegments()[0];
    var seg2=seg.AllSegments()[1];
    var seg3=seg.AllSegments()[2];

    expect(seg1.initialTime).toBe(2);
    expect(seg1.finalTime).toBe(2.5);

    expect(seg1.EvaluatePositionAt(2)).toBe(10);

    expect(seg2.initialTime).toBe(2.5);
    expect(seg2.finalTime).toBe(3.5);

    expect(seg3.initialTime).toBe(3.5);
    expect(seg3.finalTime).toBe(4);
    expect(seg3.EvaluatePositionAt(4)).toBe(20);
    expect(seg3.EvaluateVelocityAt(4)).toBeCloseTo(0,5);

  });

});

<|endoftext|>


<commit_before>/**
 * These tests help to experiment with the architecture for
 * building pipelines from components.
 */

'use strict';
require('chai').should();

const h = require('highland');

/**
 * The top-level component is a Pipeline, which runs against transforms
 * by using its apply() method.
 *
 *  let p = new Pipeline()
 *  .apply(new InputCollection([1, 2, 3, 4]))
 *  .apply(Doto(element => {
 *    console.log(`Got an element: ${JSON.stringify(element)}`);
 *  }))
 *  ;
 *
 *  p.run();
 *
 * This would mean that everything has to have an apply() method, since it
 * will be invoked in a standard way from the framework.
 *
 * The advantage of this approach is that it's quite easy to make
 * extensions to the library, by implementing a bunch of classes
 * that have an apply() method. It also means that the extensions
 * could be applied to any library not just Highland.
 */

class Pipeline {
  constructor() {
    this._input = h();
  }

  apply(obj) {

    /**
     * If this is the first time through, initialise the pipeline:
     */

    this._pipeline = this._pipeline || this._input;

    /**
     * If we have a curried function then call it with the current
     * pipeline as input:
     */

    if (typeof obj === 'function') {
      this._pipeline = obj(this._pipeline);
    }

    /**
     * Otherwise, we lot the object itself do to the pipeline, whatever
     * it does:
     */

    else {
      this._pipeline = obj.apply(this._pipeline);
    }
    return this;
  }

  run(cb) {
    this._pipeline = h.done(cb, this._pipeline);
  }
};

describe('Pipeline', () => {
  describe('input from', () => {
    it('Highland', (done) => {
      let p = new Pipeline()
      .apply(h.through(h([4, 3, 2, 1])))
      .apply(h.map(element => element * 7))
      .apply(h.collect())
      .apply(h.doto(ar => {
        ar.should.eql([
          4 * 7,
          3 * 7,
          2 * 7,
          1 * 7
        ]);
      }))
      ;

      p.run(done);
    });

    it('InputCollection', (done) => {
      let p = new Pipeline()
      .apply(new InputCollection([4, 3, 2, 1]))
      .apply(h.map(element => element - 8))
      .apply(h.collect())
      .apply(h.doto(ar => {
        ar.should.eql([
          4 - 8,
          3 - 8,
          2 - 8,
          1 - 8
        ]);
      }))
      ;

      p.run(done);
    });
  });
});

/**
 * In Beam parlance a Transform is a step in a pipeline.
 */

class Map {
  constructor(fn) {
    this._fn = fn;
  }

  apply(pipeline) {
    return h.map(this._fn, pipeline);
  }
};

describe('Transform', () => {
  it('basic transforms', (done) => {
    let p = new Pipeline()
    .apply(new InputCollection([4, 3, 2, 1]))
    .apply(new Map(element => element - 8))
    .apply(h.collect())
    .apply(h.doto(ar => {
      ar.should.eql([
        4 - 8,
        3 - 8,
        2 - 8,
        1 - 8
      ]);
    }))
    ;

    p.run(done);
  });
});

/**
 * In Beam parlance the data source is an input collection, which is
 * a special kind of transform for a Pipeline:
 *
 *  new Pipeline()
 *  .apply(new InputCollection([1, 2, 3, 4]))
 *  .apply(Doto(element => {
 *    console.log(`Got an element: ${JSON.stringify(element)}`);
 *  }))
 *  ;
 */

class InputCollection {
  constructor(source) {
    this._input = h(source);
  }

  /**
   * [TODO] This doesn't feel right because it's not consistent with
   *        Pipeline.apply(), which is returning 'this'.
   */

  apply(pipeline) {
    return h.through(this._input, pipeline);
  }
};
<commit_msg>add Collect transform<commit_after>/**
 * These tests help to experiment with the architecture for
 * building pipelines from components.
 */

'use strict';
require('chai').should();

const h = require('highland');

/**
 * The top-level component is a Pipeline, which runs against transforms
 * by using its apply() method.
 *
 *  let p = new Pipeline()
 *  .apply(new InputCollection([1, 2, 3, 4]))
 *  .apply(Doto(element => {
 *    console.log(`Got an element: ${JSON.stringify(element)}`);
 *  }))
 *  ;
 *
 *  p.run();
 *
 * This would mean that everything has to have an apply() method, since it
 * will be invoked in a standard way from the framework.
 *
 * The advantage of this approach is that it's quite easy to make
 * extensions to the library, by implementing a bunch of classes
 * that have an apply() method. It also means that the extensions
 * could be applied to any library not just Highland.
 */

class Pipeline {
  constructor() {
    this._input = h();
  }

  apply(obj) {

    /**
     * If this is the first time through, initialise the pipeline:
     */

    this._pipeline = this._pipeline || this._input;

    /**
     * If we have a curried function then call it with the current
     * pipeline as input:
     */

    if (typeof obj === 'function') {
      this._pipeline = obj(this._pipeline);
    }

    /**
     * Otherwise, we lot the object itself do to the pipeline, whatever
     * it does:
     */

    else {
      this._pipeline = obj.apply(this._pipeline);
    }
    return this;
  }

  run(cb) {
    this._pipeline = h.done(cb, this._pipeline);
  }
};

describe('Pipeline', () => {
  describe('input from', () => {
    it('Highland', (done) => {
      let p = new Pipeline()
      .apply(h.through(h([4, 3, 2, 1])))
      .apply(h.map(element => element * 7))
      .apply(h.collect())
      .apply(h.doto(ar => {
        ar.should.eql([
          4 * 7,
          3 * 7,
          2 * 7,
          1 * 7
        ]);
      }))
      ;

      p.run(done);
    });

    it('InputCollection', (done) => {
      let p = new Pipeline()
      .apply(new InputCollection([4, 3, 2, 1]))
      .apply(h.map(element => element - 8))
      .apply(h.collect())
      .apply(h.doto(ar => {
        ar.should.eql([
          4 - 8,
          3 - 8,
          2 - 8,
          1 - 8
        ]);
      }))
      ;

      p.run(done);
    });
  });
});

/**
 * In Beam parlance a Transform is a step in a pipeline.
 */

class Map {
  constructor(fn) {
    this._fn = fn;
  }

  apply(pipeline) {
    return h.map(this._fn, pipeline);
  }
};

class Collect {
  apply(pipeline) {
    return h.collect(pipeline);
  }
};

describe('Transform', () => {
  it('basic transforms', (done) => {
    let p = new Pipeline()
    .apply(new InputCollection([4, 3, 2, 1]))
    .apply(new Map(element => element - 8))
    .apply(new Collect())
    .apply(h.doto(ar => {
      ar.should.eql([
        4 - 8,
        3 - 8,
        2 - 8,
        1 - 8
      ]);
    }))
    ;

    p.run(done);
  });
});

/**
 * In Beam parlance the data source is an input collection, which is
 * a special kind of transform for a Pipeline:
 *
 *  new Pipeline()
 *  .apply(new InputCollection([1, 2, 3, 4]))
 *  .apply(Doto(element => {
 *    console.log(`Got an element: ${JSON.stringify(element)}`);
 *  }))
 *  ;
 */

class InputCollection {
  constructor(source) {
    this._input = h(source);
  }

  /**
   * [TODO] This doesn't feel right because it's not consistent with
   *        Pipeline.apply(), which is returning 'this'.
   */

  apply(pipeline) {
    return h.through(this._input, pipeline);
  }
};
<|endoftext|>


<commit_before>define( 'providers/colabs',
[ 'providers', 'jquery', 'gizmo/superdesk',

  config.guiJs('livedesk', 'models/blog'),
  config.guiJs('livedesk', 'models/collaborator'),

  'providers/colabs/adaptor',
  'tmpl!livedesk>providers/colabs',
  'tmpl!livedesk>providers/colabs/items' ],

function(providers, $, giz, Blog, Collaborator)
{
    var config = { updateInterval: 10 },
        colabsList = [],
        updateInterval = 0,
        intervalRunning = false,
        updateItemCount = 0;

    // single post item view
    var PostView = giz.View.extend
    ({
        init: function(opts)
        {
            this.model.on('update', this.render, this);
			this.model.on('read', this.render, this);
        },
        render: function()
        {
            var self = this;
            $.tmpl( 'livedesk>providers/colabs/items', {Posts: this.model.feed('json')}, function(e, o)
            {
                self.setElement(o);

                // make draggable
                self.el.hasClass('draggable') && self.el.draggable
                ({
                    helper: 'clone',
                    appendTo: 'body',
                    zIndex: 2700,
                    start: function(){ $(this).data('post', providers.colabs.adaptor.universal(this)); }
                });
            });
            return self;
        }
    }),
    // main view
    ColabView = giz.View.extend
    ({
        namespace: 'livedesk',
        events:
        {
            //'.collaborators-header .feed-info .label': {'click': 'toggleHeader'},
            '.new-results': {'update': 'showNewResults'}
        },

        toggleHeader: function()
        {
            var colabId = $(this).attr('data-colab-id'),
                posts = $(this).parents('.collaborators-header')
                            .nextAll('.search-result-list').find('li[data-colab-id='+colabId+']');

            if($(this).data('is-off'))
            {
                posts.show();
                $(this).addClass('label-info').data('is-off', false);
            }
            else
            {
                posts.hide();
                $(this).removeClass('label-info').data('is-off', true);
            }
        },
        /*!
         * @param e event
         * @param count
         * @param callback
         * @param wether to do update auto. or add a click-to-show button
         */
        showNewResults: function(e, count, callback, auto)
        {
            var self = $('.new-results', this.el),
                cb = function()
                {
                    self.addClass('hide');
                    callback.apply(this);
                };
            auto ? cb() : self.removeClass('hide').text( count+" "+_('new items! Update') ).one('click.livedesk', cb);
        },

        init: function()
        {
            $('.search-result-list', this.el).html('');

            $(this.el).on('click', '.collaborators-header .feed-info .label', this.toggleHeader);

            var blog = giz.Auth(new Blog(this.blogUrl)), // autheticated blog model
                self = this;

            self.el.html('
'+_('Loading collaborators...')+'
');

            self.colabsList = [];

            blog.on('read', function()
            {
                var collaborators = this.get('Collaborator');
                collaborators.on('read', function()
                {
                    self.colabsList = this;
                    self.setupHeader.call(self, this);
                    self.setupColabStream.call(self, this);
                });
                collaborators.xfilter('Person.Id', 'Person.FullName', 'Person.EMail', 'Post').sync();
            });
            blog.sync();
        },

        update: function()
        {
            this.colabsList.each(function()
            {
                // get post list and sync it with the server
                this.get('Post').xfilter('*').sync({data: {'startEx.cId': this._latestPost}});
            });
        },

        /*!
         * display list header
         */
        setupHeader: function(colabs)
        {
            $(this.el).tmpl('livedesk>providers/colabs', {Colabs: colabs.feed('json', true)});
        },
        /*!
         * initial - initial count of collaborators
         */
        readPostsHandle: function(initial, colab)
        {
            // list of new posts to append
            var appendPosts = [],
                self = this;
            this.each(function()
            {
                if( $.inArray( this.get('Id'), colab._viewModels ) === -1 )
                {
                    appendPosts.push(this);
                    colab._viewModels.push(this.get('Id'));
                }
                colab._latestPost = Math.max(colab._latestPost, parseInt(this.get('CId')));
            });
            updateItemCount += appendPosts.length;

            appendPosts.length && $('.new-results', self.el).trigger('update.livedesk', [updateItemCount, function()
            {
                $(appendPosts).each(function()
                {
                    $('.search-result-list', self.el).prepend( (new PostView({ model: this })).render().el );
                });
                updateItemCount -= appendPosts.length;
            }, initial ? true : false]);

            initial -= 1; // decrement initial until 0 so we know when init is over and do not send
        },
        /*!
         * setup post list
         */
        setupColabStream: function(colabs)
        {
            var self = this,
                initial = colabs.count(); // used for breaking init. action, decrementing until 0
            // collaboratos list
            colabs.each(function()
            {
                var colab = this;
                colab._latestPost = 0;
                colab._viewModels = [];
                colab.on('read', function()
                {
                    // get posts for each collaborator
                    colab.get('Post').xfilter('*')
                        .on('read', function(){ self.readPostsHandle.call(this, initial, colab); })
                        .sync();

                    self.startAutoUpdate();
//                    clearInterval(updateInterval);
//                    updateInterval = setInterval(function()
//                    {
//                        if(!$('.search-result-list:visible', self.el).length)
//                        {
//                            clearInterval(updateInterval);
//                            return;
//                        }
//                        self.update();
//                    }, config.updateInterval*1000);

                }).sync();

            });
        },
        startAutoUpdate: function()
        {
            var self = this;
            clearInterval(updateInterval);
            updateInterval = setInterval(function()
            {
                if(!$('.search-result-list:visible', self.el).length)
                {
                    clearInterval(updateInterval);
                    return;
                }
                self.update();
            }, config.updateInterval*1000);

            return self;
        },
        render: function()
        {

        }
    });

    var colabView = null;
    $.extend( providers.colabs, { init: function(blogUrl)
    {
        colabView = new ColabView({ el: this.el, blogUrl: blogUrl });
        this.init = function(){ return colabView.startAutoUpdate(); }
    }});

    return providers;
});<commit_msg>NR-230 : Collaborators provider<commit_after>define( 'providers/colabs',
[ 'providers', 'jquery', 'gizmo/superdesk',

  config.guiJs('livedesk', 'models/blog'),
  config.guiJs('livedesk', 'models/collaborator'),

  'providers/colabs/adaptor',
  'tmpl!livedesk>providers/colabs',
  'tmpl!livedesk>providers/colabs/items' ],

function(providers, $, giz, Blog, Collaborator)
{
    var config = { updateInterval: 10 },
        colabsList = [],
        updateInterval = 0,
        intervalRunning = false,
        updateItemCount = 0;

    // single post item view
    var PostView = giz.View.extend
    ({
        init: function(opts)
        {
            this.model.on('update', this.render, this);
			this.model.on('read', this.render, this);
			this.model.on('delete', this.remove, this); // TODO should remove from ColabView?
        },
        render: function()
        {
            var self = this;
            $.tmpl( 'livedesk>providers/colabs/items', {Posts: this.model.feed('json')}, function(e, o)
            {
                self.setElement(o);

                // make draggable
                self.el.hasClass('draggable') && self.el.draggable
                ({
                    helper: 'clone',
                    appendTo: 'body',
                    zIndex: 2700,
                    start: function(){ $(this).data('post', providers.colabs.adaptor.universal(this)); }
                });
            });
            return self;
        },
        remove: function()
        {
            this.el.remove();
        }
    }),
    // main view
    ColabView = giz.View.extend
    ({
        namespace: 'livedesk',
        events:
        {
            //'.collaborators-header .feed-info .label': {'click': 'toggleHeader'},
            '.new-results': {'update': 'showNewResults'}
        },

        toggleHeader: function()
        {
            var colabId = $(this).attr('data-colab-id'),
                posts = $(this).parents('.collaborators-header')
                            .nextAll('.search-result-list').find('li[data-colab-id='+colabId+']');

            if($(this).data('is-off'))
            {
                posts.show();
                $(this).addClass('label-info').data('is-off', false);
            }
            else
            {
                posts.hide();
                $(this).removeClass('label-info').data('is-off', true);
            }
        },
        /*!
         * @param e event
         * @param count
         * @param callback
         * @param wether to do update auto. or add a click-to-show button
         */
        showNewResults: function(e, count, callback, auto)
        {
            var self = $('.new-results', this.el),
                cb = function()
                {
                    self.addClass('hide');
                    callback.apply(this);
                };
            auto ? cb() : self.removeClass('hide').text( count+" "+_('new items! Update') ).one('click.livedesk', cb);
        },

        init: function()
        {
            $('.search-result-list', this.el).html('');

            $(this.el).on('click', '.collaborators-header .feed-info .label', this.toggleHeader);

            var blog = giz.Auth(new Blog(this.blogUrl)), // autheticated blog model
                self = this;

            self.el.html('
'+_('Loading collaborators...')+'
');

            self.colabsList = [];

            blog.on('read', function()
            {
                var collaborators = this.get('Collaborator');
                collaborators.on('read', function()
                {
                    self.colabsList = this;
                    self.setupHeader.call(self, this);
                    self.setupColabStream.call(self, this);
                });
                collaborators.xfilter('Person.Id', 'Person.FullName', 'Person.EMail', 'Post').sync();
            });
            blog.sync();
        },

        update: function()
        {
            this.colabsList.each(function()
            {
                // get post list and sync it with the server
                this.get('Post').xfilter('*').sync({data: {'startEx.cId': this._latestPost}});
            });
        },

        /*!
         * display list header
         */
        setupHeader: function(colabs)
        {
            $(this.el).tmpl('livedesk>providers/colabs', {Colabs: colabs.feed('json', true)});
        },
        /*!
         * initial - initial count of collaborators
         */
        readPostsHandle: function(initial, colab)
        {
            // list of new posts to append
            var appendPosts = [],
                self = this;
            this.each(function()
            {
                if( $.inArray( this.get('Id'), colab._viewModels ) === -1 )
                {
                    appendPosts.push(this);
                    colab._viewModels.push(this.get('Id'));
                }
                var pCId = parseInt(this.get('CId'));
                if(!isNaN(pCId)) colab._latestPost = Math.max(colab._latestPost, pCId);
            });
            updateItemCount += appendPosts.length;

            appendPosts.length && $('.new-results', self.el).trigger('update.livedesk', [updateItemCount, function()
            {
                $(appendPosts).each(function()
                {
                    $('.search-result-list', self.el).prepend( (new PostView({ model: this })).render().el );
                });
                updateItemCount -= appendPosts.length;
            }, initial ? true : false]);

            initial -= 1; // decrement initial until 0 so we know when init is over and do not send
        },
        /*!
         * setup post list
         */
        setupColabStream: function(colabs)
        {
            var self = this,
                initial = colabs.count(); // used for breaking init. action, decrementing until 0
            // collaboratos list
            colabs.each(function()
            {
                var colab = this;
                colab._latestPost = 0;
                colab._viewModels = [];
                colab.on('read', function()
                {
                    // get posts for each collaborator
                    colab.get('Post').xfilter('*')
                        .on('read', function(){ self.readPostsHandle.call(this, initial, colab); })
                        .sync();
                    // start the auto update timers
                    self.startAutoUpdate();
                }).sync();

            });
        },
        startAutoUpdate: function()
        {
            var self = this;
            clearInterval(updateInterval);
            updateInterval = setInterval(function()
            {
                if(!$('.search-result-list:visible', self.el).length)
                {
                    clearInterval(updateInterval);
                    return;
                }
                self.update();
            }, config.updateInterval*1000);

            return self;
        },
        render: function()
        {
            console.log('render colabs')
        }
    });

    var colabView = null;
    $.extend( providers.colabs, { init: function(blogUrl)
    {
        colabView = new ColabView({ el: this.el, blogUrl: blogUrl });
        this.init = function()
        {
            return colabView.startAutoUpdate();
        };
    }});

    return providers;
});<|endoftext|>


<commit_before>/**
 * Proudly created by ohad on 14/02/2017.
 *
 * Send data to the server using http requests.
 */
const Client = require('../client'),
      _      = require('../util/wrapper'),
      Const           = require('../../../common/const'),
      Storage         = require('./storage');
/**
 * Sends a HTTP request with message.
 * @param {Object} message
 */
exports.save = function save(message) {
  message = _enrich(message);
  const url = process.env.BACKEND_HOST + '/' + message.backendUrl;
  delete message.backendUrl;
  _.http.ajax(url, message);
};

/**
 * Takes care of requirements for this storage.
 * @param {Object} [options]
 * @param {function} onReady
 */
exports.init = function (options, onReady) {
  // Waiting for google analytics, so that Client.id exists.
  // Client.init(() => {
    let userMessage = {
      client    : {
        agent         : Client.agent,
        cookiesEnabled: Client.cookiesEnabled,
        screen        : {
          height: window.innerHeight,
          width : window.innerWidth
        }
      }
    };
    if (window.screen) {
      userMessage.client.screen.availHeight = window.screen.availHeight;
      userMessage.client.screen.availWidth  = window.screen.availWidth;
    }
  //   Storage.save(userMessage);
  // // });
  // onReady();
  _.http.ajax(Const.BACKEND_URL.CLIENT, userMessage, onReady)
};

/**
 * Enriches message with additional properties.
 * @param {Object} message
 * @private
 */
function _enrich(message) {
  return _.deepExtend(message, {
    timestamp: new Date().getTime(),
    // client   : {
    //   id     : Client.id,
    //   created: Client.created
    // },
    // url      : window.location.href,
    tracker  : Client.tracker
  });
}
<commit_msg>ajax tokens<commit_after>/**
 * Proudly created by ohad on 14/02/2017.
 *
 * Send data to the server using http requests.
 */
const Client = require('../client'),
      _      = require('../util/wrapper'),
      Const           = require('../../../common/const'),
      Storage         = require('./storage');
/**
 * Sends a HTTP request with message.
 * @param {Object} message
 */
exports.save = function save(message, callback) {
  message = _enrich(message);
  const url = process.env.BACKEND_HOST + '/' + message.backendUrl;
  delete message.backendUrl;
  _.http.ajax(url, message, callback);
};

/**
 * Takes care of requirements for this storage.
 * @param {Object} [options]
 * @param {function} onReady
 */
exports.init = function (options, onReady) {
  // Waiting for google analytics, so that Client.id exists.
  // Client.init(() => {
    let userMessage = {
      backendUrl: Const.BACKEND_URL.CLIENT,
      client    : {
        agent         : Client.agent,
        cookiesEnabled: Client.cookiesEnabled,
        screen        : {
          height: window.innerHeight,
          width : window.innerWidth
        }
      }
    };
    if (window.screen) {
      userMessage.client.screen.availHeight = window.screen.availHeight;
      userMessage.client.screen.availWidth  = window.screen.availWidth;
    }
  //   Storage.save(userMessage);
  // // });
  // onReady();
  exports.save(userMessage, onReady);
  // _.http.ajax(process.env.BACKEND_HOST + '/' + Const.BACKEND_URL.CLIENT, userMessage, onReady)
};

/**
 * Enriches message with additional properties.
 * @param {Object} message
 * @private
 */
function _enrich(message) {
  return _.deepExtend(message, {
    timestamp: new Date().getTime(),
    // client   : {
    //   id     : Client.id,
    //   created: Client.created
    // },
    // url      : window.location.href,
    tracker  : Client.tracker
  });
}
<|endoftext|>