<commit_before>const Discord       = require('discord.js')
const request       = require('request-promise')
const config        = require('./data/client.json')
const DiscordServer = require('./DiscordServer')

module.exports =

class DiscordBot {
    constructor() {
        this.initialize();
        this.servers = {};
    }

    initialize() {
        this.bot = new Discord.Client();

        this.bot.on("ready", this.ready.bind(this));
        this.bot.on("message", this.message.bind(this));
        this.bot.on("guildMemberAdd", this.guildMemberAdd.bind(this));

        this.bot.login(config.token);
    }

    ready() {
        console.log("RoVer is ready.");
        this.bot.user.setGame("http://eryn.io/RoVer");
    }

    async message(msg) {
        if (msg.content.toLowerCase() === "!verify" && msg.guild) {
            let action = await this.getServer(msg.guild.id).verifyMember(msg.author.id);

            if (!action.status) {
                msg.reply(action.error);
            } else {
                msg.reply(`Welcome to the server, ${action.robloxUsername}!`);
            }
        }else if (msg.guild && msg.member.hasPermission('ADMINISTRATOR')) {
            let command = msg.cleanContent.toLowerCase().split(' ')[0];

            let args = msg.cleanContent.split(' ');
            args.shift();
            let argument = args.join(' ');

            let server = this.getServer(msg.guild.id);

            if (command === "!rover") {
                msg.reply("**RoVer Admin Commands:**\n\n`!RoVerVerifiedRole <exact role name>`\n`!RoVerNickname <true|false>`\n`!RoVerAnnounceChannel <exact channel name>`");
            } else if (command === "!roververifiedrole") {
                if (argument.length > 0) {
                    let role = msg.guild.roles.find('name', argument);
                    if (role) {
                        server.setSetting('verifiedRole', role.id);
                        msg.reply(`Set verified role to ${argument}`);
                    } else {
                        msg.reply(`Couldn't find role \`${argument}\`. Make sure you type it exactly (including capitalization).`);
                    }
                } else {
                    server.setSetting('verifiedRole', null);
                    msg.reply("Cleared verified role, users will no longer receive a role when they verify.");
                }
            } else if (command === "!roverannouncechannel") {
                if (argument.length > 0) {
                    let channel = msg.guild.channels.find('name', argument);
                    if (channel) {
                        server.setSetting('announceChannel', channel.id);
                        msg.reply(`Set verify announcement channel to ${argument}`);
                    } else {
                        msg.reply(`Couldn't find channel \`${argument}\`. Make sure you type it exactly (including capitalization).`);
                    }
                } else {
                    server.setSetting('announceChannel', null);
                    msg.reply("Verified users will no longer be announced.");
                }
            } else if (command === "!rovernickname") {
                if (argument.length > 0) {
                    server.setSetting('nicknameUsers', argument === 'true');
                    if (argument === 'true') {
                        msg.reply("The bot will now nickname users to their Roblox username.");
                    } else {
                        msg.reply("The bot will no longer nickname users to their Roblox name.");
                    }
                } else {
                    msg.reply("Requires argument (true|false)");
                }
            }
        }
    }

    async guildMemberAdd(member) {
        let action = await this.getServer(member.guild.id).verifyMember(member.id);

        if (action.status) {
            member.send(`Welcome to the server, ${action.robloxUsername}!`);
        }
    }

    getServer(id) {
        if (!this.servers[id]) {
            this.servers[id] = new DiscordServer(this, id);
        }
        return this.servers[id];
    }

    async globallyUpdateMember(id) {
        for (let guild of this.bot.guilds.array()) {
            let server = this.getServer(guild.id);

            let action = await server.verifyMember(id);

            if (!action.status) {
                break;
            }
        }
    }
}<commit_msg>Instruct new members on where to go<commit_after>const Discord       = require('discord.js')
const request       = require('request-promise')
const config        = require('./data/client.json')
const DiscordServer = require('./DiscordServer')

module.exports =

class DiscordBot {
    constructor() {
        this.initialize();
        this.servers = {};
    }

    initialize() {
        this.bot = new Discord.Client();

        this.bot.on("ready", this.ready.bind(this));
        this.bot.on("message", this.message.bind(this));
        this.bot.on("guildMemberAdd", this.guildMemberAdd.bind(this));

        this.bot.login(config.token);
    }

    ready() {
        console.log("RoVer is ready.");
        this.bot.user.setGame("http://eryn.io/RoVer");
    }

    async message(msg) {
        if (msg.content.toLowerCase() === "!verify" && msg.guild) {
            let action = await this.getServer(msg.guild.id).verifyMember(msg.author.id);

            if (!action.status) {
                msg.reply(action.error);
            } else {
                msg.reply(`Welcome to the server, ${action.robloxUsername}!`);
            }
        }else if (msg.guild && msg.member.hasPermission('ADMINISTRATOR')) {
            let command = msg.cleanContent.toLowerCase().split(' ')[0];

            let args = msg.cleanContent.split(' ');
            args.shift();
            let argument = args.join(' ');

            let server = this.getServer(msg.guild.id);

            if (command === "!rover") {
                msg.reply("**RoVer Admin Commands:**\n\n`!RoVerVerifiedRole <exact role name>`\n`!RoVerNickname <true|false>`\n`!RoVerAnnounceChannel <exact channel name>`");
            } else if (command === "!roververifiedrole") {
                if (argument.length > 0) {
                    let role = msg.guild.roles.find('name', argument);
                    if (role) {
                        server.setSetting('verifiedRole', role.id);
                        msg.reply(`Set verified role to ${argument}`);
                    } else {
                        msg.reply(`Couldn't find role \`${argument}\`. Make sure you type it exactly (including capitalization).`);
                    }
                } else {
                    server.setSetting('verifiedRole', null);
                    msg.reply("Cleared verified role, users will no longer receive a role when they verify.");
                }
            } else if (command === "!roverannouncechannel") {
                if (argument.length > 0) {
                    let channel = msg.guild.channels.find('name', argument);
                    if (channel) {
                        server.setSetting('announceChannel', channel.id);
                        msg.reply(`Set verify announcement channel to ${argument}`);
                    } else {
                        msg.reply(`Couldn't find channel \`${argument}\`. Make sure you type it exactly (including capitalization).`);
                    }
                } else {
                    server.setSetting('announceChannel', null);
                    msg.reply("Verified users will no longer be announced.");
                }
            } else if (command === "!rovernickname") {
                if (argument.length > 0) {
                    server.setSetting('nicknameUsers', argument === 'true');
                    if (argument === 'true') {
                        msg.reply("The bot will now nickname users to their Roblox username.");
                    } else {
                        msg.reply("The bot will no longer nickname users to their Roblox name.");
                    }
                } else {
                    msg.reply("Requires argument (true|false)");
                }
            }
        }
    }

    async guildMemberAdd(member) {
        let action = await this.getServer(member.guild.id).verifyMember(member.id);

        if (action.status) {
            member.send(`Welcome to the server, ${action.robloxUsername}!`);
        } else {
            member.send("Welcome to the server! Visit the following link to verify your Roblox account: https://verify.eryn.io");
        }
    }

    getServer(id) {
        if (!this.servers[id]) {
            this.servers[id] = new DiscordServer(this, id);
        }
        return this.servers[id];
    }

    async globallyUpdateMember(id) {
        for (let guild of this.bot.guilds.array()) {
            let server = this.getServer(guild.id);

            let action = await server.verifyMember(id);

            if (!action.status) {
                break;
            }
        }
    }
}<|endoftext|>


<commit_before>const Discord       = require('discord.js')
const request       = require('request-promise')
const config        = require('./data/client.json')
const DiscordServer = require('./DiscordServer')

let startTime = new Date();

// The main Discord bot class, only one per shard.
class DiscordBot {
    constructor() {
        this.initialize();
        this.servers = {};
    }

    // Initialize the bot, hook up events, and log in.
    initialize() {
        this.bot = new Discord.Client({
            shardId: parseInt(process.env.SHARD_ID, 10),
            shardCount: parseInt(process.env.SHARD_COUNT, 10),
            apiRequestMethod: config.apiRequestMethod || 'sequential',
            fetchAllMembers: true
        });

        // Events

        // We use .bind(this) so that the context remains within
        // the class and not the event.
        this.bot.on("ready", this.ready.bind(this));
        this.bot.on("message", this.message.bind(this));
        this.bot.on("guildMemberAdd", this.guildMemberAdd.bind(this));

        // Only hook up typingStart if lockNicknames mode is enabled.
        if (config.lockNicknames) {
            this.bot.on("typingStart", this.typingStart.bind(this));
        }

        // Login.
        this.bot.login(process.env.CLIENT_TOKEN);
    }

    // Called when the bot is ready and has logged in.
    ready() {
        console.log(`Shard ${process.env.SHARD_ID} is ready, serving ${this.bot.guilds.array().length} guilds.`);
        this.bot.user.setGame("http://eryn.io/RoVer");
    }

    // This method is called when a user starts typing, but it's used
    // for setting their nickname back to what it should be if they've
    // changed it. Only active if lockNicknames is true in config.
    async typingStart(channel, user) {
        // Don't want to do anything if this is a DM.
        if (channel.type !== "text") {
            return;
        }

        // We call verifyMember but we want to retain the cache and we
        // don't want it to post any announcements.
        this.getServer(channel.guild.id).verifyMember(user.id, {
            announce: false,
            clearBindingsCache: false
        });
    }

    // This method is called when the bot can read a message in any
    // channel it's in, even DMs.
    async message(msg) {
        if (msg.content.toLowerCase() === "!verify" && msg.guild) {
            // The user ran `!verify`, we are checking their status now.

            // Clear the request cache so we get fresh information.
            DiscordServer.clearMemberCache(msg.author.id);

            let server = this.getServer(msg.guild.id)
            let action = await server.verifyMember(msg.author.id);

            // We reply with the status of the verification in the
            // channel the command was sent.
            if (!action.status) {
                msg.reply(action.error);
            } else {
                msg.reply(server.getWelcomeMessage(action));
            }
        } else if (msg.cleanContent === "!eshard") {
            let currentTime = new Date();
            let difference = Math.abs(currentTime - startTime);
            difference = Math.floor((diff / 1000) / 60);
            msg.reply(`Shard #${process.env.SHARD_ID}. Uptime: ${difference} minutes.`);
        } else if (msg.guild && msg.member && msg.member.hasPermission('ADMINISTRATOR')) {
            // These are admin commands, and only work if the user has
            // the Administrator permission.

            // Get the first word in the message.
            let command = msg.cleanContent.toLowerCase().split(' ')[0];

            // Get the command argument, which is just a string excluding
            // the first word.
            let args = msg.cleanContent.split(' ');
            args.shift();
            let argument = args.join(' ');

            let server = this.getServer(msg.guild.id);

            switch (command) {
                case "!rover":
                    msg.reply("**RoVer Admin Commands:**\n\n`!RoVerVerifiedRole <exact role name>` - Set the role that users get when they are verified.\n`!RoVerNickname <true|false>` - Choose whether or not the bot changes nicknames.\n`!RoVerAnnounceChannel <exact channel name>` - A channel where the bot will announce new verifications, useful for admins.\n`!RoVerNicknameFormat <format>` - sets the nickname format. Available replacements are `%USERNAME%`, `%USERID%`, `%DISCORDNAME%`, and `%DISCORDID%`\n`!RoVerWelcomeMessage <welcome message>` - Set the message the user gets when they verify. Same format as above.\n`!RoVerBindGroupRank <groupid:rank number:role name|groupid:role name>` - Bind a rank in a group to a role in discord.\n`!RoVerUnbindGroupRank <role name>` - Unbind a group rank binding associated with this role.\n`!RoVerUnbindAllGroupRanks` - Unbind all group ranks in this server.\n`!RoVerUpdate <@targetUser>` - Update a user, same as them running !verify. Make sure you @mention them.");
                    break;
                case "!roververifiedrole":
                    if (argument.length > 0) {
                        if (argument.startsWith("@")) {
                            // Support for role mentions. Since we use cleanContent,
                            // it will just be the role's display name.
                            argument = argument.substring(1);
                        }

                        let role = msg.guild.roles.find('name', argument);
                        if (role) {
                            server.setSetting('verifiedRole', role.id);
                            msg.reply(`Set verified role to ${argument}`);
                        } else {
                            msg.reply(`Couldn't find role \`${argument}\`. Make sure you type it exactly (including capitalization).`);
                        }
                    } else {
                        server.setSetting('verifiedRole', null);
                        msg.reply("Cleared verified role, users will no longer receive a role when they verify.");
                    }
                    break;
                case "!roverannouncechannel":
                     if (argument.length > 0) {
                        if (argument.startsWith("#")) {
                            // Support for channel mentions. Since we use cleanContent,
                            // it will just be the channel's display name.
                            argument = argument.substring(1);
                        }

                        let channel = msg.guild.channels.find('name', argument);

                        if (channel) {
                            server.setSetting('announceChannel', channel.id);
                            msg.reply(`Set verify announcement channel to ${argument}`);
                        } else {
                            msg.reply(`Couldn't find channel \`${argument}\`. Make sure you type it exactly (including capitalization).`);
                        }
                    } else {
                        server.setSetting('announceChannel', null);
                        msg.reply("Verified users will no longer be announced.");
                    }
                    break;
                case "!rovernickname":
                     if (argument.length > 0) {
                        server.setSetting('nicknameUsers', argument === 'true');
                        if (argument === 'true') {
                            msg.reply("The bot will now nickname users to their Roblox username.");
                        } else {
                            msg.reply("The bot will no longer nickname users to their Roblox name.");
                        }
                    } else {
                        msg.reply("Requires argument (true|false)");
                    }
                    break;
                case "!rovernicknameformat":
                     if (argument.length > 0) {
                        server.setSetting('nicknameFormat', argument);
                        msg.reply(`Set nickname format to \`${argument}\``);
                    } else {
                        server.setSetting('nicknameFormat', undefined);
                        msg.reply("Nickname format set back to default");
                    }
                    break;
                case "!roverwelcomemessage":
                    if (argument.length > 0) {
                        server.setSetting('welcomeMessage', argument);
                        msg.reply(`Set welcome message to \`${argument}\``);
                    } else {
                        server.setSetting('welcomeMessage', undefined);
                        msg.reply("Set welcome message back to default");
                    }
                    break;
                case "!roverbindgrouprank":
                    if (argument.length > 0) {
                        let bindArgs = argument.split(':');
                        let binding = {};

                        if (bindArgs.length === 2) {
                            // The user excluded the rank argument.
                            binding.group = bindArgs[0];
                            binding.role = bindArgs[1];
                        } else if (bindArgs.length === 3) {
                            // Rank argument included

                            // Support for operators, so we parse them out before
                            // saving the rank number.
                            let rankUnparsed = bindArgs[1];

                            if (rankUnparsed.startsWith('>')) {
                                binding.operator = 'gt';
                                rankUnparsed = rankUnparsed.substring(1);
                            } else if (rankUnparsed.startsWith('<')) {
                                binding.operator = 'lt';
                                rankUnparsed = rankUnparsed.substring(1);
                            }

                            binding.group = bindArgs[0];
                            binding.rank = parseInt(rankUnparsed, 10);
                            binding.role = bindArgs[2];
                        } else {
                            return msg.reply("Wrong number of arguments: needs 2 or 3");
                        }

                        // Verify that the role the user provided is real.
                        let role = msg.guild.roles.find('name', binding.role);
                        if (role) {
                            binding.role = role.id;
                        } else {
                            return msg(`Couldn't find role: \`${binding.role}\``);
                        }

                        // Delete any previous binding with that role.
                        server.deleteGroupRankBinding(binding.role);

                        // Add the new binding.
                        let serverBindings = server.getSetting('groupRankBindings');
                        serverBindings.push(binding);
                        server.setSetting('groupRankBindings', serverBindings);

                        msg.reply(`Added rank binding: Group: ${binding.group}, Rank: ${binding.rank || 'none'}, Role: ${role.name}, Comparison: ${binding.operator || 'eq'}`);
                    }
                    break;
                case "!roverunbindgrouprank":
                    if (argument.length > 0) {
                        let role = msg.guild.roles.find('name', argument);
                        if (role) {
                            server.deleteGroupRankBinding(role.id);
                            msg.reply(`Cleared all bindings associated to \`${role.name}\``);
                        } else {
                            msg.reply(`Role not found: \`${argument}\``);
                        }
                    }
                    break;
                case "!roverunbindallgroupranks":
                    server.deleteGroupRankBinding('all');
                    msg.reply("Deleted all group rank bindings.");
                    break;
                case "!roverupdate":
                    if (msg.mentions.users.array().length > 0) {
                        let user = msg.mentions.users.first();
                        DiscordServer.clearMemberCache(user.id);

                        let server = this.getServer(msg.guild.id)
                        let action = await server.verifyMember(user.id);

                        if (!action.status) {
                            msg.reply(action.error);
                        } else {
                            msg.reply(`${action.robloxUsername} verified.`);
                        }
                    }
                    break;
            }
        }
    }

    // This is called when a user joins any Discord server.
    async guildMemberAdd(member) {
        let server = this.getServer(member.guild.id);
        let action = await server.verifyMember(member.id);

        if (action.status) {
            member.send(server.getWelcomeMessage(action));
        } else {
            member.send("Welcome! Visit the following link to verify your Roblox account: https://verify.eryn.io");
        }
    }

    // This is used to get the DiscordServer instance associated
    // with the specific guild id.
    getServer(id) {
        if (!this.servers[id]) {
            this.servers[id] = new DiscordServer(this, id);
        }
        return this.servers[id];
    }

    // This is called by the update server when a user verifies
    // online. It updates the member in every DiscordServer they
    // are in.
    async globallyUpdateMember(id) {
        // Start off by clearing their global cache.
        DiscordServer.clearMemberCache(id);

        let firstRun = true;

        // Iterate through all of the guilds the bot is in.
        for (let guild of this.bot.guilds.array()) {
            let server = this.getServer(guild.id);

            if (firstRun) {
                // This only runs on the first iteration. We do this so that
                // we have time to cache the user information, so it only
                // sends out the request once.

                let action = await server.verifyMember(id, {
                    // We want to clear the group rank bindings cache because
                    // this is the first iteration.
                    clearBindingsCache: true,
                    announce: false
                });

                if (!action.status && !action.nonFatal) {
                    // If there's a fatal error, don't continue with the rest.
                    break;
                } else if (server.hasCustomWelcomeMessage()) {
                    // It worked, checking if there's a custom welcome message.
                    await this.bot.fetchUser(id);

                    let member = await this.bot.guilds.get(guild.id).fetchMember(id);
                    member.send(server.getWelcomeMessage(action));
                }
            } else {
                // This is for all bit the first iteration.

                // We define an inline function and call it with the current
                // context so that we can run these commands synchronously
                // but still execute the requests all at the same time.
                (async function(){
                    let action = await server.verifyMember(id, {
                        clearBindingsCache: false,
                        announce: false
                    });

                    if (action.status && server.hasCustomWelcomeMessage()) {
                        await this.bot.fetchUser(id);

                        let member = await this.bot.guilds.get(guild.id).fetchMember(id);
                        member.send(server.getWelcomeMessage(action));
                    }
                }).apply(this);
            }
            firstRun = false;
        }
    }
}

// Instantiate the bot.
let discordBot = new DiscordBot();

// Listen for when we need to globally update a member
// from the Update Server.
process.on('message', msg => {
    if (msg.action === 'globallyUpdateMember') {
        discordBot.globallyUpdateMember(msg.argument);
    }
});

// Max shard life time (2 hours)

setTimeout( () => {
    process.exit();
}, 2 * 60 * 60 * 1000);<commit_msg>Fix eshard command<commit_after>const Discord       = require('discord.js')
const request       = require('request-promise')
const config        = require('./data/client.json')
const DiscordServer = require('./DiscordServer')

let startTime = new Date();

// The main Discord bot class, only one per shard.
class DiscordBot {
    constructor() {
        this.initialize();
        this.servers = {};
    }

    // Initialize the bot, hook up events, and log in.
    initialize() {
        this.bot = new Discord.Client({
            shardId: parseInt(process.env.SHARD_ID, 10),
            shardCount: parseInt(process.env.SHARD_COUNT, 10),
            apiRequestMethod: config.apiRequestMethod || 'sequential',
            fetchAllMembers: true
        });

        // Events

        // We use .bind(this) so that the context remains within
        // the class and not the event.
        this.bot.on("ready", this.ready.bind(this));
        this.bot.on("message", this.message.bind(this));
        this.bot.on("guildMemberAdd", this.guildMemberAdd.bind(this));

        // Only hook up typingStart if lockNicknames mode is enabled.
        if (config.lockNicknames) {
            this.bot.on("typingStart", this.typingStart.bind(this));
        }

        // Login.
        this.bot.login(process.env.CLIENT_TOKEN);
    }

    // Called when the bot is ready and has logged in.
    ready() {
        console.log(`Shard ${process.env.SHARD_ID} is ready, serving ${this.bot.guilds.array().length} guilds.`);
        this.bot.user.setGame("http://eryn.io/RoVer");
    }

    // This method is called when a user starts typing, but it's used
    // for setting their nickname back to what it should be if they've
    // changed it. Only active if lockNicknames is true in config.
    async typingStart(channel, user) {
        // Don't want to do anything if this is a DM.
        if (channel.type !== "text") {
            return;
        }

        // We call verifyMember but we want to retain the cache and we
        // don't want it to post any announcements.
        this.getServer(channel.guild.id).verifyMember(user.id, {
            announce: false,
            clearBindingsCache: false
        });
    }

    // This method is called when the bot can read a message in any
    // channel it's in, even DMs.
    async message(msg) {
        if (msg.content.toLowerCase() === "!verify" && msg.guild) {
            // The user ran `!verify`, we are checking their status now.

            // Clear the request cache so we get fresh information.
            DiscordServer.clearMemberCache(msg.author.id);

            let server = this.getServer(msg.guild.id)
            let action = await server.verifyMember(msg.author.id);

            // We reply with the status of the verification in the
            // channel the command was sent.
            if (!action.status) {
                msg.reply(action.error);
            } else {
                msg.reply(server.getWelcomeMessage(action));
            }
        } else if (msg.cleanContent === "!eshard") {
            let currentTime = new Date();
            let difference = Math.abs(currentTime - startTime);
            difference = Math.floor((difference / 1000) / 60);
            msg.reply(`Shard #${process.env.SHARD_ID}. Uptime: ${difference} minutes.`);
        } else if (msg.guild && msg.member && msg.member.hasPermission('ADMINISTRATOR')) {
            // These are admin commands, and only work if the user has
            // the Administrator permission.

            // Get the first word in the message.
            let command = msg.cleanContent.toLowerCase().split(' ')[0];

            // Get the command argument, which is just a string excluding
            // the first word.
            let args = msg.cleanContent.split(' ');
            args.shift();
            let argument = args.join(' ');

            let server = this.getServer(msg.guild.id);

            switch (command) {
                case "!rover":
                    msg.reply("**RoVer Admin Commands:**\n\n`!RoVerVerifiedRole <exact role name>` - Set the role that users get when they are verified.\n`!RoVerNickname <true|false>` - Choose whether or not the bot changes nicknames.\n`!RoVerAnnounceChannel <exact channel name>` - A channel where the bot will announce new verifications, useful for admins.\n`!RoVerNicknameFormat <format>` - sets the nickname format. Available replacements are `%USERNAME%`, `%USERID%`, `%DISCORDNAME%`, and `%DISCORDID%`\n`!RoVerWelcomeMessage <welcome message>` - Set the message the user gets when they verify. Same format as above.\n`!RoVerBindGroupRank <groupid:rank number:role name|groupid:role name>` - Bind a rank in a group to a role in discord.\n`!RoVerUnbindGroupRank <role name>` - Unbind a group rank binding associated with this role.\n`!RoVerUnbindAllGroupRanks` - Unbind all group ranks in this server.\n`!RoVerUpdate <@targetUser>` - Update a user, same as them running !verify. Make sure you @mention them.");
                    break;
                case "!roververifiedrole":
                    if (argument.length > 0) {
                        if (argument.startsWith("@")) {
                            // Support for role mentions. Since we use cleanContent,
                            // it will just be the role's display name.
                            argument = argument.substring(1);
                        }

                        let role = msg.guild.roles.find('name', argument);
                        if (role) {
                            server.setSetting('verifiedRole', role.id);
                            msg.reply(`Set verified role to ${argument}`);
                        } else {
                            msg.reply(`Couldn't find role \`${argument}\`. Make sure you type it exactly (including capitalization).`);
                        }
                    } else {
                        server.setSetting('verifiedRole', null);
                        msg.reply("Cleared verified role, users will no longer receive a role when they verify.");
                    }
                    break;
                case "!roverannouncechannel":
                     if (argument.length > 0) {
                        if (argument.startsWith("#")) {
                            // Support for channel mentions. Since we use cleanContent,
                            // it will just be the channel's display name.
                            argument = argument.substring(1);
                        }

                        let channel = msg.guild.channels.find('name', argument);

                        if (channel) {
                            server.setSetting('announceChannel', channel.id);
                            msg.reply(`Set verify announcement channel to ${argument}`);
                        } else {
                            msg.reply(`Couldn't find channel \`${argument}\`. Make sure you type it exactly (including capitalization).`);
                        }
                    } else {
                        server.setSetting('announceChannel', null);
                        msg.reply("Verified users will no longer be announced.");
                    }
                    break;
                case "!rovernickname":
                     if (argument.length > 0) {
                        server.setSetting('nicknameUsers', argument === 'true');
                        if (argument === 'true') {
                            msg.reply("The bot will now nickname users to their Roblox username.");
                        } else {
                            msg.reply("The bot will no longer nickname users to their Roblox name.");
                        }
                    } else {
                        msg.reply("Requires argument (true|false)");
                    }
                    break;
                case "!rovernicknameformat":
                     if (argument.length > 0) {
                        server.setSetting('nicknameFormat', argument);
                        msg.reply(`Set nickname format to \`${argument}\``);
                    } else {
                        server.setSetting('nicknameFormat', undefined);
                        msg.reply("Nickname format set back to default");
                    }
                    break;
                case "!roverwelcomemessage":
                    if (argument.length > 0) {
                        server.setSetting('welcomeMessage', argument);
                        msg.reply(`Set welcome message to \`${argument}\``);
                    } else {
                        server.setSetting('welcomeMessage', undefined);
                        msg.reply("Set welcome message back to default");
                    }
                    break;
                case "!roverbindgrouprank":
                    if (argument.length > 0) {
                        let bindArgs = argument.split(':');
                        let binding = {};

                        if (bindArgs.length === 2) {
                            // The user excluded the rank argument.
                            binding.group = bindArgs[0];
                            binding.role = bindArgs[1];
                        } else if (bindArgs.length === 3) {
                            // Rank argument included

                            // Support for operators, so we parse them out before
                            // saving the rank number.
                            let rankUnparsed = bindArgs[1];

                            if (rankUnparsed.startsWith('>')) {
                                binding.operator = 'gt';
                                rankUnparsed = rankUnparsed.substring(1);
                            } else if (rankUnparsed.startsWith('<')) {
                                binding.operator = 'lt';
                                rankUnparsed = rankUnparsed.substring(1);
                            }

                            binding.group = bindArgs[0];
                            binding.rank = parseInt(rankUnparsed, 10);
                            binding.role = bindArgs[2];
                        } else {
                            return msg.reply("Wrong number of arguments: needs 2 or 3");
                        }

                        // Verify that the role the user provided is real.
                        let role = msg.guild.roles.find('name', binding.role);
                        if (role) {
                            binding.role = role.id;
                        } else {
                            return msg(`Couldn't find role: \`${binding.role}\``);
                        }

                        // Delete any previous binding with that role.
                        server.deleteGroupRankBinding(binding.role);

                        // Add the new binding.
                        let serverBindings = server.getSetting('groupRankBindings');
                        serverBindings.push(binding);
                        server.setSetting('groupRankBindings', serverBindings);

                        msg.reply(`Added rank binding: Group: ${binding.group}, Rank: ${binding.rank || 'none'}, Role: ${role.name}, Comparison: ${binding.operator || 'eq'}`);
                    }
                    break;
                case "!roverunbindgrouprank":
                    if (argument.length > 0) {
                        let role = msg.guild.roles.find('name', argument);
                        if (role) {
                            server.deleteGroupRankBinding(role.id);
                            msg.reply(`Cleared all bindings associated to \`${role.name}\``);
                        } else {
                            msg.reply(`Role not found: \`${argument}\``);
                        }
                    }
                    break;
                case "!roverunbindallgroupranks":
                    server.deleteGroupRankBinding('all');
                    msg.reply("Deleted all group rank bindings.");
                    break;
                case "!roverupdate":
                    if (msg.mentions.users.array().length > 0) {
                        let user = msg.mentions.users.first();
                        DiscordServer.clearMemberCache(user.id);

                        let server = this.getServer(msg.guild.id)
                        let action = await server.verifyMember(user.id);

                        if (!action.status) {
                            msg.reply(action.error);
                        } else {
                            msg.reply(`${action.robloxUsername} verified.`);
                        }
                    }
                    break;
            }
        }
    }

    // This is called when a user joins any Discord server.
    async guildMemberAdd(member) {
        let server = this.getServer(member.guild.id);
        let action = await server.verifyMember(member.id);

        if (action.status) {
            member.send(server.getWelcomeMessage(action));
        } else {
            member.send("Welcome! Visit the following link to verify your Roblox account: https://verify.eryn.io");
        }
    }

    // This is used to get the DiscordServer instance associated
    // with the specific guild id.
    getServer(id) {
        if (!this.servers[id]) {
            this.servers[id] = new DiscordServer(this, id);
        }
        return this.servers[id];
    }

    // This is called by the update server when a user verifies
    // online. It updates the member in every DiscordServer they
    // are in.
    async globallyUpdateMember(id) {
        // Start off by clearing their global cache.
        DiscordServer.clearMemberCache(id);

        let firstRun = true;

        // Iterate through all of the guilds the bot is in.
        for (let guild of this.bot.guilds.array()) {
            let server = this.getServer(guild.id);

            if (firstRun) {
                // This only runs on the first iteration. We do this so that
                // we have time to cache the user information, so it only
                // sends out the request once.

                let action = await server.verifyMember(id, {
                    // We want to clear the group rank bindings cache because
                    // this is the first iteration.
                    clearBindingsCache: true,
                    announce: false
                });

                if (!action.status && !action.nonFatal) {
                    // If there's a fatal error, don't continue with the rest.
                    break;
                } else if (server.hasCustomWelcomeMessage()) {
                    // It worked, checking if there's a custom welcome message.
                    await this.bot.fetchUser(id);

                    let member = await this.bot.guilds.get(guild.id).fetchMember(id);
                    member.send(server.getWelcomeMessage(action));
                }
            } else {
                // This is for all bit the first iteration.

                // We define an inline function and call it with the current
                // context so that we can run these commands synchronously
                // but still execute the requests all at the same time.
                (async function(){
                    let action = await server.verifyMember(id, {
                        clearBindingsCache: false,
                        announce: false
                    });

                    if (action.status && server.hasCustomWelcomeMessage()) {
                        await this.bot.fetchUser(id);

                        let member = await this.bot.guilds.get(guild.id).fetchMember(id);
                        member.send(server.getWelcomeMessage(action));
                    }
                }).apply(this);
            }
            firstRun = false;
        }
    }
}

// Instantiate the bot.
let discordBot = new DiscordBot();

// Listen for when we need to globally update a member
// from the Update Server.
process.on('message', msg => {
    if (msg.action === 'globallyUpdateMember') {
        discordBot.globallyUpdateMember(msg.argument);
    }
});

// Max shard life time (2 hours)

setTimeout( () => {
    process.exit();
}, 2 * 60 * 60 * 1000);<|endoftext|>


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

    angular.module('app.core').controller('BookController', BookController);

    BookController.$inject = ['$scope', '$location', 'BookService', 'AuthorService', 'BookStatusService'];

    function BookController($scope, $location, BookService, AuthorService, BookStatusService) {
        $scope.bookStatus = BookStatusService.query();
        $scope.authors = AuthorService.query();
        $scope.books = BookService.query();
        $scope.book = {};

        $scope.submitBook = function submitBook() {
            var bookDAO = new BookService($scope.book);
            bookDAO.$save();
            $location.path('/library/public/ui/#/books');
        };

        $scope.addAuthor = function () {
            $scope.author = $scope.bookData.author;
            $scope.book.authors.push($scope.author);
        };

        $scope.removeAuthor = function (author) {
            var index = $scope.book.authors.indexOf(author);
            if (index !== -1) {
                $scope.book.authors.splice(index, 1);
            }
        };

        /* set default sort order */
        $scope.orderByField = 'title';
        $scope.reverseSort = false;
    }
}());
<commit_msg>add comment explaining a bug<commit_after>(function () {
    'use strict';

    angular.module('app.core').controller('BookController', BookController);

    BookController.$inject = ['$scope', '$location', 'BookService', 'AuthorService', 'BookStatusService'];

    function BookController($scope, $location, BookService, AuthorService, BookStatusService) {
        $scope.bookStatus = BookStatusService.query();
        $scope.authors = AuthorService.query();
        $scope.books = BookService.query();
        $scope.book = {};

        $scope.submitBook = function submitBook() {
            var bookDAO = new BookService($scope.book);
            bookDAO.$save();
            $location.path('/library/public/ui/#/books');
        };

        /*
         * addAuthor and removeAuthor only work on the book update page since
         * $scope.book is only an empty object when creating a book in this
         * controller.  See line 12 above.  Due to this
         * $scrop.book.authors.push() does not exist when it is called in the
         * function below.  Same goes for $scope.book.authros.splice().
         *
         * Compare this to how these are setup in the book-detail-controller to
         * see the differences.
         */
        $scope.addAuthor = function () {
            $scope.author = $scope.bookData.author;
            $scope.book.authors.push($scope.author);
        };

        $scope.removeAuthor = function (author) {
            var index = $scope.book.authors.indexOf(author);
            if (index !== -1) {
                $scope.book.authors.splice(index, 1);
            }
        };

        /* set default sort order */
        $scope.orderByField = 'title';
        $scope.reverseSort = false;
    }
}());
<|endoftext|>


<commit_before>(function(angular){

	angular
		.module('app')
			.controller(
        'MainController',
        ['$scope',
         function($scope){
          /*******local variables!!*******/
          var self = $scope;

					/*******web variables*******/

					/********Personal DATA*****/
					self.userName = null
					self.userEmail = null;
					self.userBornDate = null;

					/********END Personal DATA*****/

					/**********REMUNERATION VARIABLES**********/
					//self.dateRetirement = null;
					self.typeRemuneration = 2;
					/********type 1 Remuneracion constante*****/
					self.mothEarnIt = 0;
					self.cantPaymentPerYear = null;
					/******** END type 1 Remuneracion constante*****/

					/******* type 2 remuneracion intermitente *****/
					self.dateAtInitWork = null;
					self.dateAtFinishtWork = null;
					self.arrRemunerations = [];
					self.objRemuneration = {
						remunerationCant: 1500,
						dateBegin: null,
						dateEnd: null
					};

					self.validDateInitWork = function(){
						if(self.dateAtInitWork && self.userBornDate){
							if(moment(new Date(self.dateAtInitWork)).isSameOrBefore(moment(new Date(self.userBornDate)))){
								console.log("fecha laboral debe ser mayor a la de nacimiento");
								self.dateAtInitWork = null;
							}
						}else{
							console.log("Es necesario la fecha de nacimiento");
							self.dateAtInitWork = null;
						}
						$('#remu-end').html(moment(new Date(self.dateAtInitWork)).add(+$('#display-3')[0].innerHTML, 'y').format('D MMM YYYY'));
					};

					self.agregateNewRemuneration = function(){
						if(!objRemunerationIsValid(self.objRemuneration)){
							return;
						}
					};

					var objRemunerationIsValid = function(objParam){
						if(!objParam){
							return false;
						}
						if(!objParam.remunerationCant ||
						!objParam.dateBegin ||
						!objParam.dateEnd){
							console.log("invalid params!");
							return false;
						}
						if(moment(new Date(objParam.dateBegin)).isAfter(moment(new Date(objParam.dateEnd)))){
							console.log("fecha de inicio debe ser anterior a la de fin");
							return false;
						}
						if(moment(new Date(objParam.dateBegin)).isBefore(moment(new Date(self.dateAtInitWork)))){
							console.log("fecha de inicio debe ser posterior a la fecha inicio trabajo");
							return false;
						}
						if(moment(new Date(objParam.dateEnd)).isAfter(moment(new Date($('#remu-end')[0].innerHTML)))){
							console.log("fecha de inicio debe ser posterior a la fecha inicio trabajo");
							return false;
						}
						return true;
					};

					var checkObjRemunerationDateConflictInArray = function(){

					};

					/******* END type 2 remuneracion intermitente *****/
					/********** END REMUNERATION VARIABLES**********/

			}]);
})(window.angular);
<commit_msg>Agregaciopn de comentarios<commit_after>(function(angular){

	angular
		.module('app')
			.controller(
        'MainController',
        ['$scope',
         function($scope){
          /*******local variables!!*******/
          var self = $scope;

					/*******web variables*******/

					/*****************************************************************Personal DATA*****/
					self.userName = null
					self.userEmail = null;
					self.userBornDate = null;
					/***************************************************************END Personal DATA*****/

					/*************************************************************REMUNERATION VARIABLES**********/
					//self.dateRetirement = null;
					self.typeRemuneration = 2;
					/********type 1 Remuneracion constante*****/
					self.mothEarnIt = 0;
					self.cantPaymentPerYear = null;
					/******** END type 1 Remuneracion constante*****/

					/******* type 2 remuneracion intermitente *****/
					self.dateAtInitWork = null;
					self.dateAtFinishtWork = null; //--not in use!!
					self.arrRemunerations = [];  //--not in use!!
					self.objRemuneration = {
						remunerationCant: 1500,
						dateBegin: null,
						dateEnd: null
					};

					self.validDateInitWork = function(){
						//Se activa al cambiar "Inicio de vida laboral"
						//Verifica que la fecha de inicio de vida laboral sea mayor a la de nacimiento
						if(self.dateAtInitWork && self.userBornDate){
							if(moment(new Date(self.dateAtInitWork)).isSameOrBefore(moment(new Date(self.userBornDate)))){
								console.log("fecha laboral debe ser mayor a la de nacimiento");
								self.dateAtInitWork = null;
							}
						}else{
							console.log("Es necesario la fecha de nacimiento");
							self.dateAtInitWork = null;
						}
						$('#remu-end').html(moment(new Date(self.dateAtInitWork)).add(+$('#display-3')[0].innerHTML, 'y').format('D MMM YYYY'));
					};

					self.agregateNewRemuneration = function(){

						//Check if objRemuneration is valid
						if(!objRemunerationIsValid(self.objRemuneration)){
							return;
						}
					};

					var objRemunerationIsValid = function(objParam){
						if(!objParam){
							return false;
						}
						if(!objParam.remunerationCant ||
						!objParam.dateBegin ||
						!objParam.dateEnd){
							console.log("invalid params!");
							return false;
						}
						if(moment(new Date(objParam.dateBegin)).isAfter(moment(new Date(objParam.dateEnd)))){
							console.log("fecha de inicio debe ser anterior a la de fin");
							return false;
						}
						if(moment(new Date(objParam.dateBegin)).isBefore(moment(new Date(self.dateAtInitWork)))){
							console.log("fecha de inicio debe ser posterior a la fecha inicio trabajo");
							return false;
						}
						if(moment(new Date(objParam.dateEnd)).isAfter(moment(new Date($('#remu-end')[0].innerHTML)))){
							console.log("fecha de inicio debe ser posterior a la fecha inicio trabajo");
							return false;
						}
						return true;
					};

					var checkObjRemunerationDateConflictInArray = function(){

					};

					/******* END type 2 remuneracion intermitente *****/
					/************************************************************************* END REMUNERATION VARIABLES**********/

			}]);
})(window.angular);
<|endoftext|>


<commit_before>define(["zepto", "q", "react", "modules/channels/chatChannel", "modules/data-types/utf8string"], function ($, Q, React, ChatChannel, Utf8String) {
    "use strict";

    return React.createClass({
        displayName: "ChannelTestInfo",

        send: function () {
            var node = this.refs.messageText.getDOMNode();
            var data = {type: "t", data: node.value};
            node.value = "";
            var messageData = new Utf8String(JSON.stringify(data));
            this.props.send(messageData);
            return false;
        },

        render: function () {
            var channel = this.props.channel;
            var messages = {"s": React.DOM.li(null, "test")}, i = 0;
            channel.messages.map(function (messageData) {
                var data = JSON.parse(messageData.as(Utf8String).value);
                messages["m_" + i] = React.DOM.li(null, data.data);
            });
            return React.DOM.div({style: {float: "left", width: 250, "margin-left": 10 }},
                React.DOM.h1({className: "title"}, channel.name + ": "),
                React.DOM.ul({style: {"list-style-type": "none"}}, messages),
                React.DOM.div(null, React.DOM.form({onSubmit: this.send},
                    React.DOM.input({type: "text", ref: "messageText"}, React.DOM.input({type: "submit", value: "send"}))
                    )));
        }
    });
});<commit_msg>quick fix<commit_after>define(["zepto", "q", "react", "modules/channels/chatChannel", "modules/data-types/utf8string"], function ($, Q, React, ChatChannel, Utf8String) {
    "use strict";

    return React.createClass({
        displayName: "ChannelTestInfo",

        send: function () {
            var node = this.refs.messageText.getDOMNode();
            var data = {type: "t", data: node.value};
            node.value = "";
            var messageData = new Utf8String(JSON.stringify(data));
            this.props.send(messageData);
            return false;
        },

        render: function () {
            var channel = this.props.channel;
            var messages = {}, i = 0;
            channel.messages.forEach(function (messageData) {
                var data = JSON.parse(messageData.as(Utf8String).value);
                messages["m_" + i] = React.DOM.li(null, data.data);
                i += 1;
            });
            return React.DOM.div({style: {float: "left", width: 250, "margin-left": 10 }},
                React.DOM.h1({className: "title"}, channel.name + ": "),
                React.DOM.ul({style: {"list-style-type": "none"}}, messages),
                React.DOM.div(null, React.DOM.form({onSubmit: this.send},
                    React.DOM.input({type: "text", ref: "messageText"}, React.DOM.input({type: "submit", value: "send"}))
                    )));
        }
    });
});<|endoftext|>


<commit_before>import MarkdownArea from 'mdarea';
import BasicEditorDriver from 'flarum/utils/BasicEditorDriver';

export default class MarkdownAreaEditorDriver extends BasicEditorDriver {
  build(dom, params) {
    super.build(dom, params);
    this.el.id = params.textareaId;

    // We can't bind shortcutHandler directly in case `build`
    // runs before MarkdownToolbar's `oninit`.
    this.el.addEventListener('keydown', function (e) {
      return params.shortcutHandler(...arguments);
    });

    this.mdarea = new MarkdownArea(this.el, {
      keyMap: {
        indent: ['Ctrl+m'],
        outdent: ['Ctrl+M'],
        inline: []
      }
    });
  }

  destroy() {
    this.mdarea.destroy();
    super.destroy();
  }
}
<commit_msg>Attach oninput, inputListeners, onsubmit via mdarea extension<commit_after>import MarkdownArea from 'mdarea';
import BasicEditorDriver from 'flarum/utils/BasicEditorDriver';

export class MarkdownEditorFlarumExtension {
  constructor(oninput, callInputListeners, onsubmit) {
    this.oninput = oninput;
    this.callInputListeners = callInputListeners;
    this.onsubmit = onsubmit;
  }

  handleKey(
    prefix,
    selection,
    postfix,
    evt
  ) {
    // setTimeout without a time executes after the call stack has cleared,
    // so any DOM changes originating from mdarea (e.g. executing an undo)
    // will be finished by then. At that time, `e.target.value` will represent
    // the updated value of the textarea in response to the keypress.
    setTimeout(() => {
      this.oninput(evt.target.value);

      if ((evt.metaKey || evt.ctrlKey) && evt.key === 'Enter') {
        return this.onsubmit();
      }

      this.callInputListeners(evt);
    });
  }
}

export default class MarkdownAreaEditorDriver extends BasicEditorDriver {
  build(dom, params) {
    this.el.className = params.classNames.join(' ');
    this.el.disabled = params.disabled;
    this.el.placeholder = params.placeholder;
    this.el.value = params.value;
    this.el.id = params.textareaId;

    dom.append(this.el);

    // We can't bind shortcutHandler directly in case `build`
    // runs before MarkdownToolbar's `oninit`.
    this.el.addEventListener('keydown', function (e) {
      return params.shortcutHandler(...arguments);
    });

    const callInputListeners = (e) => {
      params.inputListeners.forEach((listener) => {
        listener();
      });

      e.redraw = false;
    };

    // This one can't be run through mdarea, but that doesn't matter
    // because mdarea doesn't change value in response to clicks.
    this.el.addEventListener('click', callInputListeners);

    this.mdarea = new MarkdownArea(this.el, {
      keyMap: {
        indent: ['Ctrl+m'],
        outdent: ['Ctrl+M'],
        inline: []
      },
      extensions: [
        new MarkdownEditorFlarumExtension(params.oninput, callInputListeners, params.onsubmit)
      ]
    });
  }

  destroy() {
    this.mdarea.destroy();
    super.destroy();
  }
}
<|endoftext|>


<commit_before>/**********************************************************\
|                                                          |
|                          hprose                          |
|                                                          |
| Official WebSite: http://www.hprose.com/                 |
|                   http://www.hprose.org/                 |
|                                                          |
\**********************************************************/

/**********************************************************\
 *                                                        *
 * JSONRPCClientFilter.js                                 *
 *                                                        *
 * jsonrpc client filter for JavaScript.                  *
 *                                                        *
 * LastModified: May 15, 2015                             *
 * Author: Ma Bingyao <andot@hprose.com>                  *
 *                                                        *
\**********************************************************/

(function (global) {
    'use strict';

    var Tags = global.hprose.Tags;
    var BytesIO = global.hprose.BytesIO;
    var Writer = global.hprose.Writer;
    var Reader = global.hprose.Reader;

    var s_id = 1;

    global.hprose.JSONRPCClientFilter = {
        version: "2.0",
        inputFilter: function(value, context) {
            var response = JSON.parse(BytesIO.toString(value));
            var stream = new BytesIO();
            var writer = new Writer(stream, true);
            if (response.error) {
                stream.writeByte(Tags.TagError);
                writer.writeString(response.error.message);
            }
            else {
                stream.writeByte(Tags.TagResult);
                writer.serialize(response.result);
            }
            stream.writeByte(Tags.TagEnd);
            return stream.bytes;
        },
        outputFilter: function(value, context) {
            var request = {};
            if (this.version === "1.1") {
                request.version = "1.1";
            }
            else if (this.version === "2.0") {
                request.jsonrpc = "2.0";
            }
            var stream = new BytesIO(value);
            var reader = new Reader(stream, false, false);
            var tag = stream.readByte();
            if (tag === Tags.TagCall) {
                request.method = reader.readString();
                tag = stream.readByte();
                if (tag === Tags.TagList) {
                    request.params = reader.readListWithoutTag();
                }
            }
            request.id = s_id++;
            return JSON.stringify(request);
        }
    };

})(this);<commit_msg>Added batch support for jsonrpc client.<commit_after>/**********************************************************\
|                                                          |
|                          hprose                          |
|                                                          |
| Official WebSite: http://www.hprose.com/                 |
|                   http://www.hprose.org/                 |
|                                                          |
\**********************************************************/

/**********************************************************\
 *                                                        *
 * JSONRPCClientFilter.js                                 *
 *                                                        *
 * jsonrpc client filter for JavaScript.                  *
 *                                                        *
 * LastModified: May 21, 2015                             *
 * Author: Ma Bingyao <andot@hprose.com>                  *
 *                                                        *
\**********************************************************/

(function (global) {
    'use strict';

    var Tags = global.hprose.Tags;
    var BytesIO = global.hprose.BytesIO;
    var Writer = global.hprose.Writer;
    var Reader = global.hprose.Reader;

    var s_id = 1;

    function JSONRPCClientFilter(version) {
        this.version = version || "2.0";
    }

    JSONRPCClientFilter.prototype.inputFilter = function inputFilter(data, context) {
        var json = BytesIO.toString(data);
        if (json.charAt(0) === '{') {
            json = '[' + json + ']';
        }
        var responses = JSON.parse(json);
        var stream = new BytesIO();
        var writer = new Writer(stream, true);
        for (var i = 0, n = responses.length; i < n; ++i) {
            var response = responses[i];
            if (response.error) {
                stream.writeByte(Tags.TagError);
                writer.writeString(response.error.message);
            }
            else {
                stream.writeByte(Tags.TagResult);
                writer.serialize(response.result);
            }
        }
        stream.writeByte(Tags.TagEnd);
        return stream.bytes;
    };

    JSONRPCClientFilter.prototype.outputFilter = function outputFilter(data, context) {
        var requests = [];
        var stream = new BytesIO(data);
        var reader = new Reader(stream, false, false);
        var tag = stream.readByte();
        do {
            var request = {};
            if (tag === Tags.TagCall) {
                request.method = reader.readString();
                tag = stream.readByte();
                if (tag === Tags.TagList) {
                    request.params = reader.readListWithoutTag();
                    tag = stream.readByte();
                }
                if (tag === Tags.TagTrue) {
                    tag = stream.readByte();
                }
            }
            if (this.version === "1.1") {
                request.version = "1.1";
            }
            else if (this.version === "2.0") {
                request.jsonrpc = "2.0";
            }
            request.id = s_id++;
            requests.push(request);
        } while (tag === Tags.TagCall);
        if (requests.length > 1) {
            return JSON.stringify(requests);
        }
        return JSON.stringify(requests[0]);
    };

    global.hprose.JSONRPCClientFilter = JSONRPCClientFilter;

})(this);<|endoftext|>


<commit_before>// Copyright (c) 2011-2012 UX Productivity. All rights reserved.

(function() {
    function SelectionFormatting()
    {
        this.style = null;
        this.multipleStyles = false;
        this.bold = false;
        this.italic = false;
        this.underline = false;
        this.linethrough = false;
        this.overline = false;
        this.typewriter = false;
        this.ul = false;
        this.ol = false;
        this.inBrackets = false;
        this.inQuote = false;
    }

    SelectionFormatting.prototype.setStyle = function(style)
    {
        if (!this.multipleStyles) {
            if ((this.style != null) && (this.style != style)) {
                this.style = null;
                this.multipleStyles = true;
            }
            else {
                this.style = style;
            }
        }
    }

    function checkBracketsAndQuotes(node,offset,formatting)
    {
        var haveClosingBracket = false;
        var haveClosingQuote = false;
        while ((node != null) && (isInlineNode(node))) {
            if (node.nodeType == Node.TEXT_NODE) {
                for (var i = offset-1; i >= 0; i--) {
                    switch (node.nodeValue.charCodeAt(i)) {
                    case 0x28: // opening (
                        if (!haveClosingBracket)
                            formatting.inBrackets = true;
                        break;
                    case 0x29: // closing )
                        haveClosingBracket = true;
                        break;
                    case 0x201C: // opening "
                        if (!haveClosingQuote)
                            formatting.inQuote = true;
                        break;
                    case 0x201D: // closing "
                        haveClosingQuote = true;
                        break;
                    }
                }
            }

            node = prevNode(node);

            if (node.nodeType == Node.TEXT_NODE)
                offset = node.nodeValue.length;
            else
                offset = 0;
        }
    }

    function wrapNode(node,elementName)
    {
        var wrapper = document.createElement(elementName);
        node.parentNode.insertBefore(wrapper,node);
        wrapper.appendChild(node);
    }

    function splitAroundSelection(range)
    {
        range.omitEmptyTextSelection();
        range.ensureRangeValidHierarchy();

        if ((range.start.node.nodeType == Node.TEXT_NODE) &&
            (range.start.offset > 0)) {
            splitTextBefore(range.start.node,range.start.offset);
            if (range.end.node == range.start.node)
                range.end.offset -= range.start.offset;
            range.start.offset = 0;
        }
        else {
            movePrecedingSiblingsToOtherNode(range.start.node,isParagraphNode);
        }

        if ((range.end.node.nodeType == Node.TEXT_NODE) &&
            (range.end.offset < range.end.node.nodeValue.length)) {
            splitTextAfter(range.end.node,range.end.offset);
        }
        else {
            moveFollowingSiblingsToOtherNode(range.end.node,isParagraphNode);
        }
    }

    function mergeRange(range)
    {
        var node = range.start.node;
        var last = nextNode(range.end.node);
        while (node != null) {
            checkMerge(range,node);

            if (node == last)
                break;

            node = nextNode(node);
        }

        function checkMerge(range,node)
        {
            if (node == null)
                return;

            if ((node.previousSibling != null) &&
                (node.nodeType == Node.TEXT_NODE) &&
                (node.previousSibling.nodeType == Node.TEXT_NODE)) {

                //            debug("Merging \""+node.previousSibling.nodeValue+"\" and \""+
                //                         node.nodeValue+"\"");

                node.nodeValue = node.previousSibling.nodeValue + node.nodeValue;

                if (range.start.node == node.previousSibling)
                    range.start.node = node;
                else if (range.start.node == node)
                    range.start.offset += node.previousSibling.nodeValue.length;

                if (range.end.node == node.previousSibling)
                    range.end.node = node;
                else if (range.end.node == node)
                    range.end.offset += node.previousSibling.nodeValue.length;

                node.parentNode.removeChild(node.previousSibling);
                checkMerge(range,node);
                var next = nextNode(node);
                if (next != null)
                    checkMerge(range,next);
            }
            else if ((node.previousSibling != null) &&
                     (node.nodeType == Node.ELEMENT_NODE) &&
                     (node.previousSibling.nodeType == Node.ELEMENT_NODE) &&
                     elementsMergable(node,node.previousSibling)) {

                //            debug("Merging \""+node.previousSibling.nodeName+"\" and \""+
                //                         node.nodeName+"\"");
                var origFirst = node.firstChild;
                while (node.previousSibling.lastChild != null)
                    node.insertBefore(node.previousSibling.lastChild,node.firstChild);
                node.parentNode.removeChild(node.previousSibling);
                checkMerge(range,origFirst);
            }

            checkMerge(range,node.parentNode);
        }

        function elementsMergable(a,b)
        {
            if (isInlineNode(a) && isInlineNode(b) &&
                (a.nodeName == b.nodeName) &&
                (a.attributes.length == b.attributes.length)) {
                for (var i = 0; i < a.attributes.length; i++) {
                    var attrName = a.attributes[i].nodeName;
                    if (a.getAttribute(attrName) != b.getAttribute(attrName))
                        return false;
                }
                return true;
            }

            return false;
        }
    }

    // public (called from cursor.js)
    function splitTextBefore(node,offset)
    {
        var before = document.createTextNode(node.nodeValue.slice(0,offset));

        node.parentNode.insertBefore(before,node);
        node.nodeValue = node.nodeValue.slice(offset);

        movePrecedingSiblingsToOtherNode(node,isParagraphNode);
    }

    // public
    function splitTextAfter(node,offset)
    {
        var after = document.createTextNode(node.nodeValue.slice(offset));

        node.parentNode.insertBefore(after,node.nextSibling);
        node.nodeValue = node.nodeValue.slice(0,offset);

        moveFollowingSiblingsToOtherNode(node,isParagraphNode);
    }

    // public (called from styles.js)
    function movePrecedingSiblingsToOtherNode(child,parentCheckFn)
    {
        var parent = child.parentNode;
        if (parentCheckFn(parent) || (parent == document.body))
            return;
        if (child.previousSibling == null)
            return;

        var parentCopy = shallowCopyElement(parent);
        parent.parentNode.insertBefore(parentCopy,parent);

        while (child.previousSibling != null)
            parentCopy.insertBefore(child.previousSibling,parentCopy.firstChild);

        movePrecedingSiblingsToOtherNode(child.parentNode,parentCheckFn)
    }

    // public (called from styles.js)
    function moveFollowingSiblingsToOtherNode(child,parentCheckFn)
    {
        var parent = child.parentNode;
        if (parentCheckFn(parent) || (parent == document.body))
            return;
        if (child.nextSibling == null)
            return;

        var parentCopy = shallowCopyElement(parent);
        parent.parentNode.insertBefore(parentCopy,parent.nextSibling);

        while (child.nextSibling != null)
            parentCopy.appendChild(child.nextSibling);

        moveFollowingSiblingsToOtherNode(child.parentNode,parentCheckFn)
    }

    // public
    function reportSelectionFormatting()
    {
        var formatting = new SelectionFormatting();
        var selectionRange = getSelectionRange();
        if (selectionRange == null) {
            debug("reportSelectionFormatting: no selection");
            return; // FIXME: report that there is no formatting info available
        }
        var nodes = selectionRange.getOutermostSelectedNodes();
        for (var i = 0; i < nodes.length; i++) {
            var node = nodes[i];
            detectFormatting(formatting,node);
        }
        if (selectionRange != null) {
            checkBracketsAndQuotes(selectionRange.start.node,
                                   selectionRange.start.offset,
                                   formatting);
        }
        editor.reportFormatting(formatting);
        return;

        function detectFormatting(formatting,node)
        {
            if (node == null)
                return;

            if (node.nodeName == "B")
                formatting.bold = true;
            if (node.nodeName == "I")
                formatting.italic = true;
            if (node.nodeName == "U")
                formatting.underline = true;
            if (node.nodeName == "TT")
                formatting.typewriter = true;
            if (node.nodeName == "UL")
                formatting.ul = true;
            if (node.nodeName == "OL")
                formatting.ol = true;

            // In the case where a heading or PRE element has the class attribute set, we ignore
            // the class attribute and just go with the element name - otherwise it would really
            // complicate things (we don't support multiple styles for a single paragraph).
            if ((node.nodeName == "H1") ||
                (node.nodeName == "H2") ||
                (node.nodeName == "H3") ||
                (node.nodeName == "H4") ||
                (node.nodeName == "H5") ||
                (node.nodeName == "H6") ||
                (node.nodeName == "PRE")) {
                formatting.setStyle(node.nodeName);
            }
            else if ((node.nodeType == Node.ELEMENT_NODE) && node.hasAttribute("class")) {
                formatting.setStyle("."+node.getAttribute("class"));
            }
            else if (node.nodeName == "P") {
                formatting.setStyle(node.nodeName);
            }

            detectFormatting(formatting,node.parentNode);
        }
    }

    // public
    // "Wraps" a selection in a given element, i.e. ensures that all inline nodes that are part of
    // the selection have an ancestor of the given element type, e.g. B or UL. If the selection
    // starts or ends part-way through a text node, the text node(s) are split and the operation
    // is applied only to the portion of the text that is actually selected.
    function selectionWrapElement(elementName)
    {
        debug("selectionWrapElement "+elementName);
        var range = selectionUnwrapElement(elementName);

        if ((range == null) ||
            ((range.start.node == range.end.node) && (range.start.offset == range.end.offset))) {
            debug("selectionWrapElement: no range");
            return null;
        }
        splitAroundSelection(range);

        var inlineNodes = range.getInlineNodes();
        for (var i = 0; i < inlineNodes.length; i++) {
            var node = inlineNodes[i];
            ensureValidHierarchy(node,true);

            // splitAroundSelection() ensured that there is a child element of the current paragraph
            // that is wholly contained within the selection. It is this element that we will wrap.
            // Note that this part of the selection might already be wrapped in the requested
            // element; so we include a check to avoid double-wrapping.
            for (var p = node; p.parentNode != null; p = p.parentNode) {
                if ((p.nodeName != elementName) && isParagraphNode(p.parentNode)) {
                    wrapNode(p,elementName);
                    break;
                }
            }
        }

        mergeRange(range);

        setSelectionRange(range);
        reportSelectionFormatting();
        return range;
    }

    // public
    // For all nodes in the selection, remove any ancestor nodes with the given name from the tree
    // (replacing them with their children)
    function selectionUnwrapElement(elementName)
    {
        debug("selectionUnwrapElement "+elementName);
        var range = getSelectionRange();
        if ((range == null) ||
            ((range.start.node == range.end.node) && (range.start.offset == range.end.offset)))
            return null;
        splitAroundSelection(range);

        var node = range.start.node;
        while (node != null) {
            var next = nextNode(node);
            if (node.nodeType == Node.TEXT_NODE)
                unwrapSingle(node,elementName);
            if (node == range.end.node)
                break;
            node = next;
        }

        mergeRange(range);

        setSelectionRange(range);
        debug("\n");

        function unwrapSingle(node,elementName)
        {
            if (node == null)
                return;

            var parent = node.parentNode;
            if (node.nodeName == elementName) {
                // We found the node we're looking for. Move all of its children to its parent
                // and then remove the node
                while (node.firstChild != null)
                    node.parentNode.insertBefore(node.firstChild,node);
                node.parentNode.removeChild(node);
            }

            unwrapSingle(parent,elementName);
        }

        reportSelectionFormatting();
        return range;
    }

    window.splitTextBefore = splitTextBefore;
    window.splitTextAfter = splitTextAfter;
    window.movePrecedingSiblingsToOtherNode = movePrecedingSiblingsToOtherNode;
    window.moveFollowingSiblingsToOtherNode = moveFollowingSiblingsToOtherNode;
    window.reportSelectionFormatting = reportSelectionFormatting;
    window.selectionWrapElement = selectionWrapElement;
    window.selectionUnwrapElement = selectionUnwrapElement;
})();
<commit_msg>Skeleton of applyFormattingChanges()<commit_after>// Copyright (c) 2011-2012 UX Productivity. All rights reserved.

(function() {
    function SelectionFormatting()
    {
        this.style = null;
        this.multipleStyles = false;
        this.bold = false;
        this.italic = false;
        this.underline = false;
        this.linethrough = false;
        this.overline = false;
        this.typewriter = false;
        this.ul = false;
        this.ol = false;
        this.inBrackets = false;
        this.inQuote = false;
    }

    SelectionFormatting.prototype.setStyle = function(style)
    {
        if (!this.multipleStyles) {
            if ((this.style != null) && (this.style != style)) {
                this.style = null;
                this.multipleStyles = true;
            }
            else {
                this.style = style;
            }
        }
    }

    function checkBracketsAndQuotes(node,offset,formatting)
    {
        var haveClosingBracket = false;
        var haveClosingQuote = false;
        while ((node != null) && (isInlineNode(node))) {
            if (node.nodeType == Node.TEXT_NODE) {
                for (var i = offset-1; i >= 0; i--) {
                    switch (node.nodeValue.charCodeAt(i)) {
                    case 0x28: // opening (
                        if (!haveClosingBracket)
                            formatting.inBrackets = true;
                        break;
                    case 0x29: // closing )
                        haveClosingBracket = true;
                        break;
                    case 0x201C: // opening "
                        if (!haveClosingQuote)
                            formatting.inQuote = true;
                        break;
                    case 0x201D: // closing "
                        haveClosingQuote = true;
                        break;
                    }
                }
            }

            node = prevNode(node);

            if (node.nodeType == Node.TEXT_NODE)
                offset = node.nodeValue.length;
            else
                offset = 0;
        }
    }

    function wrapNode(node,elementName)
    {
        var wrapper = document.createElement(elementName);
        node.parentNode.insertBefore(wrapper,node);
        wrapper.appendChild(node);
    }

    function splitAroundSelection(range)
    {
        range.omitEmptyTextSelection();
        range.ensureRangeValidHierarchy();

        if ((range.start.node.nodeType == Node.TEXT_NODE) &&
            (range.start.offset > 0)) {
            splitTextBefore(range.start.node,range.start.offset);
            if (range.end.node == range.start.node)
                range.end.offset -= range.start.offset;
            range.start.offset = 0;
        }
        else {
            movePrecedingSiblingsToOtherNode(range.start.node,isParagraphNode);
        }

        if ((range.end.node.nodeType == Node.TEXT_NODE) &&
            (range.end.offset < range.end.node.nodeValue.length)) {
            splitTextAfter(range.end.node,range.end.offset);
        }
        else {
            moveFollowingSiblingsToOtherNode(range.end.node,isParagraphNode);
        }
    }

    function mergeRange(range)
    {
        var node = range.start.node;
        var last = nextNode(range.end.node);
        while (node != null) {
            checkMerge(range,node);

            if (node == last)
                break;

            node = nextNode(node);
        }

        function checkMerge(range,node)
        {
            if (node == null)
                return;

            if ((node.previousSibling != null) &&
                (node.nodeType == Node.TEXT_NODE) &&
                (node.previousSibling.nodeType == Node.TEXT_NODE)) {

                //            debug("Merging \""+node.previousSibling.nodeValue+"\" and \""+
                //                         node.nodeValue+"\"");

                node.nodeValue = node.previousSibling.nodeValue + node.nodeValue;

                if (range.start.node == node.previousSibling)
                    range.start.node = node;
                else if (range.start.node == node)
                    range.start.offset += node.previousSibling.nodeValue.length;

                if (range.end.node == node.previousSibling)
                    range.end.node = node;
                else if (range.end.node == node)
                    range.end.offset += node.previousSibling.nodeValue.length;

                node.parentNode.removeChild(node.previousSibling);
                checkMerge(range,node);
                var next = nextNode(node);
                if (next != null)
                    checkMerge(range,next);
            }
            else if ((node.previousSibling != null) &&
                     (node.nodeType == Node.ELEMENT_NODE) &&
                     (node.previousSibling.nodeType == Node.ELEMENT_NODE) &&
                     elementsMergable(node,node.previousSibling)) {

                //            debug("Merging \""+node.previousSibling.nodeName+"\" and \""+
                //                         node.nodeName+"\"");
                var origFirst = node.firstChild;
                while (node.previousSibling.lastChild != null)
                    node.insertBefore(node.previousSibling.lastChild,node.firstChild);
                node.parentNode.removeChild(node.previousSibling);
                checkMerge(range,origFirst);
            }

            checkMerge(range,node.parentNode);
        }

        function elementsMergable(a,b)
        {
            if (isInlineNode(a) && isInlineNode(b) &&
                (a.nodeName == b.nodeName) &&
                (a.attributes.length == b.attributes.length)) {
                for (var i = 0; i < a.attributes.length; i++) {
                    var attrName = a.attributes[i].nodeName;
                    if (a.getAttribute(attrName) != b.getAttribute(attrName))
                        return false;
                }
                return true;
            }

            return false;
        }
    }

    // public (called from cursor.js)
    function splitTextBefore(node,offset)
    {
        var before = document.createTextNode(node.nodeValue.slice(0,offset));

        node.parentNode.insertBefore(before,node);
        node.nodeValue = node.nodeValue.slice(offset);

        movePrecedingSiblingsToOtherNode(node,isParagraphNode);
    }

    // public
    function splitTextAfter(node,offset)
    {
        var after = document.createTextNode(node.nodeValue.slice(offset));

        node.parentNode.insertBefore(after,node.nextSibling);
        node.nodeValue = node.nodeValue.slice(0,offset);

        moveFollowingSiblingsToOtherNode(node,isParagraphNode);
    }

    // public (called from styles.js)
    function movePrecedingSiblingsToOtherNode(child,parentCheckFn)
    {
        var parent = child.parentNode;
        if (parentCheckFn(parent) || (parent == document.body))
            return;
        if (child.previousSibling == null)
            return;

        var parentCopy = shallowCopyElement(parent);
        parent.parentNode.insertBefore(parentCopy,parent);

        while (child.previousSibling != null)
            parentCopy.insertBefore(child.previousSibling,parentCopy.firstChild);

        movePrecedingSiblingsToOtherNode(child.parentNode,parentCheckFn)
    }

    // public (called from styles.js)
    function moveFollowingSiblingsToOtherNode(child,parentCheckFn)
    {
        var parent = child.parentNode;
        if (parentCheckFn(parent) || (parent == document.body))
            return;
        if (child.nextSibling == null)
            return;

        var parentCopy = shallowCopyElement(parent);
        parent.parentNode.insertBefore(parentCopy,parent.nextSibling);

        while (child.nextSibling != null)
            parentCopy.appendChild(child.nextSibling);

        moveFollowingSiblingsToOtherNode(child.parentNode,parentCheckFn)
    }

    // public
    function reportSelectionFormatting()
    {
        var formatting = new SelectionFormatting();
        var selectionRange = getSelectionRange();
        if (selectionRange == null) {
            debug("reportSelectionFormatting: no selection");
            return; // FIXME: report that there is no formatting info available
        }
        var nodes = selectionRange.getOutermostSelectedNodes();
        for (var i = 0; i < nodes.length; i++) {
            var node = nodes[i];
            detectFormatting(formatting,node);
        }
        if (selectionRange != null) {
            checkBracketsAndQuotes(selectionRange.start.node,
                                   selectionRange.start.offset,
                                   formatting);
        }
        editor.reportFormatting(formatting);
        return;

        function detectFormatting(formatting,node)
        {
            if (node == null)
                return;

            if (node.nodeName == "B")
                formatting.bold = true;
            if (node.nodeName == "I")
                formatting.italic = true;
            if (node.nodeName == "U")
                formatting.underline = true;
            if (node.nodeName == "TT")
                formatting.typewriter = true;
            if (node.nodeName == "UL")
                formatting.ul = true;
            if (node.nodeName == "OL")
                formatting.ol = true;

            // In the case where a heading or PRE element has the class attribute set, we ignore
            // the class attribute and just go with the element name - otherwise it would really
            // complicate things (we don't support multiple styles for a single paragraph).
            if ((node.nodeName == "H1") ||
                (node.nodeName == "H2") ||
                (node.nodeName == "H3") ||
                (node.nodeName == "H4") ||
                (node.nodeName == "H5") ||
                (node.nodeName == "H6") ||
                (node.nodeName == "PRE")) {
                formatting.setStyle(node.nodeName);
            }
            else if ((node.nodeType == Node.ELEMENT_NODE) && node.hasAttribute("class")) {
                formatting.setStyle("."+node.getAttribute("class"));
            }
            else if (node.nodeName == "P") {
                formatting.setStyle(node.nodeName);
            }

            detectFormatting(formatting,node.parentNode);
        }
    }

    // public
    // "Wraps" a selection in a given element, i.e. ensures that all inline nodes that are part of
    // the selection have an ancestor of the given element type, e.g. B or UL. If the selection
    // starts or ends part-way through a text node, the text node(s) are split and the operation
    // is applied only to the portion of the text that is actually selected.
    function selectionWrapElement(elementName)
    {
        debug("selectionWrapElement "+elementName);
        var range = selectionUnwrapElement(elementName);

        if ((range == null) ||
            ((range.start.node == range.end.node) && (range.start.offset == range.end.offset))) {
            debug("selectionWrapElement: no range");
            return null;
        }
        splitAroundSelection(range);

        var inlineNodes = range.getInlineNodes();
        for (var i = 0; i < inlineNodes.length; i++) {
            var node = inlineNodes[i];
            ensureValidHierarchy(node,true);

            // splitAroundSelection() ensured that there is a child element of the current paragraph
            // that is wholly contained within the selection. It is this element that we will wrap.
            // Note that this part of the selection might already be wrapped in the requested
            // element; so we include a check to avoid double-wrapping.
            for (var p = node; p.parentNode != null; p = p.parentNode) {
                if ((p.nodeName != elementName) && isParagraphNode(p.parentNode)) {
                    wrapNode(p,elementName);
                    break;
                }
            }
        }

        mergeRange(range);

        setSelectionRange(range);
        reportSelectionFormatting();
        return range;
    }

    // public
    // For all nodes in the selection, remove any ancestor nodes with the given name from the tree
    // (replacing them with their children)
    function selectionUnwrapElement(elementName)
    {
        debug("selectionUnwrapElement "+elementName);
        var range = getSelectionRange();
        if ((range == null) ||
            ((range.start.node == range.end.node) && (range.start.offset == range.end.offset)))
            return null;
        splitAroundSelection(range);

        var node = range.start.node;
        while (node != null) {
            var next = nextNode(node);
            if (node.nodeType == Node.TEXT_NODE)
                unwrapSingle(node,elementName);
            if (node == range.end.node)
                break;
            node = next;
        }

        mergeRange(range);

        setSelectionRange(range);
        debug("\n");

        function unwrapSingle(node,elementName)
        {
            if (node == null)
                return;

            var parent = node.parentNode;
            if (node.nodeName == elementName) {
                // We found the node we're looking for. Move all of its children to its parent
                // and then remove the node
                while (node.firstChild != null)
                    node.parentNode.insertBefore(node.firstChild,node);
                node.parentNode.removeChild(node);
            }

            unwrapSingle(parent,elementName);
        }

        reportSelectionFormatting();
        return range;
    }

    var PARAGRAPH_PROPERTIES = {
        "margin-left": true,
        "margin-right": true,
        "margin-top": true,
        "margin-bottom": true,

        "padding-left": true,
        "padding-right": true,
        "padding-top": true,
        "padding-bottom": true,

        "border-left-width": true,
        "border-right-width": true,
        "border-top-width": true,
        "border-bottom-width": true,

        "border-left-style": true,
        "border-right-style": true,
        "border-top-style": true,
        "border-bottom-style": true,

        "border-left-color": true,
        "border-right-color": true,
        "border-top-color": true,
        "border-bottom-color": true,

        "text-align": true,
        "line-height": true,
    };

    function getParagraphs(nodes)
    {
        var paragraphs = new Array();
        // FIXME
        return paragraphs;
    }

    function removeProperties(node,propertiesToRemove)
    {
        for (var name in propertiesToRemove)
            node.style.removeProperty(name);
    }

    function removePropertiesRecursive(node,propertiesToRemove)
    {
        if (node.nodeType == Node.ELEMENT_NODE) {
            var next;
            for (var child = node.firstChild; child != null; child = next) {
                var next = child.nextSibling;
                removePropertiesRecursive(node,propertiesToRemove);
            }
            removeproperties(node,propertiesToRemove);
        }
    }

    function setInlinePropertiesRecursive(node,inlinePropertiesToSet)
    {
        if (isInlineNode(node)) {
            for (var name in inlinePropertiesToSet)
                node.style.setProperty(name,inlinePropertiesToSet[name],null);
        }
        else {
            for (var child = node.firstChild; child != null; child = next)
                setInlinePropertiesRecursive(child,inlinePropertiesToSet);
        }
    }

    // public
    function applyFormattingChanges(style,propertiesToSet,propertiesToRemove)
    {
        var paragraphPropertiesToSet = new Object();
        var inlinePropertiesToSet = new Object();
        var paragraphPropertiesToRemove = new Object();
        var inlinePropertiesToRemove = new Object();

        for (var name in propertiesToSet) {
            if (PARAGRAPH_PROPERTIES[name])
                paragraphPropertiesToSet[name] = propertiesToSet[name];
            else
                inlinePropertiesToSet[name] = propertiesToSet[name];
        }

        for (var name in propertiesToRemove) {
            if (PARAGRAPH_PROPERTIES[name])
                paragraphPropertiesToRemove[name] = true;
            else
                inlinePropertiesToRemove[name] = true;
        }

        var selectionRange = getSelectionRange();
        if (selectionRange == null)
            return;

        var nodes = selectionRange.getOutermostSelectedNodes();
        var paragraphs = getParagraphs(nodes);

        // Set properties on inline nodes
        for (var i = 0; i < nodes.length; i++) {
            setInlinePropertiesRecursive(nodes[i],inlinePropertiesToSet);
        }

        // Remove properties from inline nodes
        for (var i = 0; i < nodes.length; i++) {
            removePropertiesRecursive(nodes[i],inlinePropertiesToRemove);
        }

        // Remove properties from paragraph nodes
        for (var i = 0; i < paragraphs.length; i++) {
            removeProperties(paragraphs[i],paragraphPropertiesToRemove);
        }

        // Set properties on paragrph nodes
        for (var i = 0; i < paragraphs.length; i++) {
            for (var name in paragraphPropertiesToSet)
                paragraphs[i].setProperty(name,paragraphPropertiesToSet[name],null);
        }

        return;
    }

    window.splitTextBefore = splitTextBefore;
    window.splitTextAfter = splitTextAfter;
    window.movePrecedingSiblingsToOtherNode = movePrecedingSiblingsToOtherNode;
    window.moveFollowingSiblingsToOtherNode = moveFollowingSiblingsToOtherNode;
    window.reportSelectionFormatting = reportSelectionFormatting;
    window.selectionWrapElement = selectionWrapElement;
    window.selectionUnwrapElement = selectionUnwrapElement;
    window.applyFormattingChanges = applyFormattingChanges;
})();
<|endoftext|>


<commit_before>'use strict';

import {INSTANCE_READ_SERVICE} from 'core/instance/instance.read.service';

const angular = require('angular');

module.exports = angular.module('spinnaker.core.instance.details.console.controller', [
  INSTANCE_READ_SERVICE,
])
  .controller('ConsoleOutputCtrl', function($scope, $uibModalInstance, instanceReader, instance) {
    const instanceId = instance.instanceId || instance.id;
    $scope.vm = {
      loading: true,
      instanceId: instanceId,
    };

    instanceReader.getConsoleOutput(instance.account, instance.region, instanceId, instance.provider).then(
      function(response) {
        $scope.vm.consoleOutput = response.output;
        $scope.vm.loading = false;
      },
      function(exception) {
        $scope.vm.exception = exception;
        $scope.vm.loading = false;
      }
    );

    $scope.close = $uibModalInstance.dismiss;

    $scope.jumpToEnd = () => {
      const console = document.getElementById("console-output");
      console.scrollTop = console.scrollHeight;
    };

  });
<commit_msg>chore(core): fix lint warnings (#4516)<commit_after>'use strict';

import {INSTANCE_READ_SERVICE} from 'core/instance/instance.read.service';

const angular = require('angular');

module.exports = angular.module('spinnaker.core.instance.details.console.controller', [
  INSTANCE_READ_SERVICE,
])
  .controller('ConsoleOutputCtrl', function($scope, $uibModalInstance, instanceReader, instance) {
    const instanceId = instance.instanceId || instance.id;
    $scope.vm = {
      loading: true,
      instanceId: instanceId,
    };

    instanceReader.getConsoleOutput(instance.account, instance.region, instanceId, instance.provider).then(
      function(response) {
        $scope.vm.consoleOutput = response.output;
        $scope.vm.loading = false;
      },
      function(exception) {
        $scope.vm.exception = exception;
        $scope.vm.loading = false;
      }
    );

    $scope.close = $uibModalInstance.dismiss;

    $scope.jumpToEnd = () => {
      const console = document.getElementById('console-output');
      console.scrollTop = console.scrollHeight;
    };

  });
<|endoftext|>