<commit_before>let Discord = require('discord.js'),
    config = require('./config/config'),
    roles = require('./config/roles'),
    Twitter = require('./modules/twitter').Twitter,
    fs = require('fs'),
    unirest = require('unirest'),
    token = config.TOKEN,
    bot = new Discord.Client(),
    exec = require('child_process').exec,
    moment = require('moment'),
    twitterTimer = null,
    cooldowns = {};

moment.locale('de');

bot.r = require('rethinkdbdash')({host: 'localhost', db: 'bronies_DSB'});

bot.radio = config.RADIO_START;

bot.commands = new Discord.Collection();
bot.aliases = new Discord.Collection();

bot.config = config;

bot.log = (msg) => {
    console.log(`[${moment().format("YYYY-MM-DD HH:mm:ss")}] ${msg}`);
};

/* COMMAND LOADER */
let commandLoader = function (currentPath) {
    bot.log("Searching for Commands... " + currentPath);
    let files = fs.readdirSync(currentPath);
    for (let i in files) {
        let currentFile = currentPath + '/' + files[i];
        let stats = fs.statSync(currentFile);
        if (stats.isFile()) {
            let loader = require(`${currentFile}`);
            bot.commands.set(loader.help.name.toLowerCase(), loader);
            if ('aliases' in loader.config) {
                loader.config.aliases.forEach(alias => {
                    bot.aliases.set(alias, loader.help.name);
                });
            }
        } else if (stats.isDirectory()) {
            commandLoader(currentFile);
        }
    }
};
commandLoader('./commands');

/* VERSION */
function getVersion(callback) {
    let info = {};

    exec('git rev-parse --short=4 HEAD', function (error, version) {
        if (error) {
            bot.log('Error getting version', error);
            info.version = 'unknown';
        } else {
            info.version = version.trim();
        }

        exec('git log -1 --pretty=%B', function (error, message) {
            if (error) {
                bot.log('Error getting commit message', error);
            } else {
                info.message = message.trim();
            }

            exec('git log -1 --date=short --pretty=format:%ci', function (error, timestamp) {
                if (error) {
                    console.log('Error getting creation time', error);
                } else {
                    info.timestamp = timestamp;
                }

                callback(info);
            });
        });
    });
}

/* BOT EVENTS */
bot.on('ready', () => {
    online();
    bot.log('I am ready!');
    getVersion((info) => {
        bot.versionInfo = info;
        bot.user.setGame('version ' + bot.versionInfo.version);

        if (config.DEBUG) bot.channels.get(config.BOT_CH).sendMessage('I am ready, running version `' + bot.versionInfo.version + '`! 👌');
    });

    if (!bot.guilds.has(config.SERVER_ID)) {
        bot.log('Bot is not connected to the selected server!');
        process.exit();
    }

    bot.server = bot.guilds.get(config.SERVER_ID);

    if(bot.server.members.has(config.BOT_ADMIN)) {
        bot.admin = bot.server.members.get(config.BOT_ADMIN);
    }

    const twitter = new Twitter(config.TWITTER_API, bot);

    let interval = config.DEBUG ? 20000 : 60000;

    if (twitterTimer != null) {
        clearInterval(twitterTimer);
    }

    twitterTimer = setInterval(() => {
        twitter.postNewTweets();
    }, interval);
});

bot.on('guildMemberAdd', (member) => {
    let embed = new Discord.RichEmbed({
        title: 'Ein neues Mitglied ist zu uns gestoßen!',
        description: `Hey **${member.user.username}**, willkommen auf dem offiziellen Discord Server von [Bronies.de](http://bronies.de/). Wirf doch zunächst einen Blick in **#info** für alle wichtigen Informationen und Bot-Befehle.`,
        thumbnail: {
            url: 'https://deratrox.de/dev/Bronies.de-DSB/_join.png'
        },
        color: 0x5FBB4E
    }).setFooter('Viel Spaß auf dem Server!');

    bot.channels.get(config.DEFAULT_CH).sendEmbed(embed);
});

bot.on('guildMemberRemove', (member) => {
    let embed = new Discord.RichEmbed({
        title: 'Ein Mitglied hat uns verlassen.',
        description: `**${member.user.username}** hat den Server verlassen. Bye bye **${member.user.username}**...`,
        thumbnail: {
            url: 'https://deratrox.de/dev/Bronies.de-DSB/_leave.png'
        },
        color: 0xEC4141
    }).setFooter('DERPY WANTS MUFFINS!');

    bot.channels.get(config.DEFAULT_CH).sendEmbed(embed);
});

bot.on('message', (message) => {
    onMessage(message, false);
});

bot.on('messageUpdate', (oldMessage, newMessage) => {
    if (typeof newMessage.author === 'undefined')
        return;

    onMessage(newMessage, true);
});

function onMessage(message, isUpdate) {
    if (message.author.id == bot.user.id) {
        return;
    }

    if (message.channel.type == 'group') {
        return;
    }

    function handleCommand() {
        let match = /^!([a-zA-Z]+).*$/.exec(message.content);

        if (message.channel.type == 'dm') {
            match = /^!?([a-zA-Z]+).*$/.exec(message.content);
        }

        if (match) {

            const args = message.content.split(' ').splice(1);
            let cmd = match[1].toLowerCase();

            let cmdObj = null;

            if (bot.commands.has(cmd)) {
                cmdObj = bot.commands.get(cmd);
            } else if (bot.aliases.has(cmd)) {
                cmdObj = bot.commands.get(bot.aliases.get(cmd));
            }

            if (cmdObj == null) {
                return;
            }

            addStats(true);

            if ('handled' in cmdObj.config) {
                if (cmdObj.config.handled == false) {
                    return;
                }
            }

            if ('server' in cmdObj.config) {
                if (cmdObj.config.server == true) {
                    if (message.guild != bot.server) {
                        return bot.respondPm(message, 'Dieser Befehl kann nur auf dem Bronies.de Discord Server ausgeführt werden!');
                    }
                }
            }

            if ('role' in cmdObj.config) {
                if (!bot.checkPermissions(cmdObj.config.role, message.author)) {
                    bot.respondPm(message, 'Du besitzt nicht genügend Rechte um diesen Befehl auszuführen!');
                    if (message.guild == bot.server) {
                        message.delete();
                    }
                    return;
                }
            }

            if ('cooldown' in cmdObj.config) {
                let check = true;

                if ('skip' in cmdObj.config) {
                    if (bot.checkPermissions(cmdObj.config.skip, message.author)) {
                        check = false;
                    }
                }

                if (check) {
                    let cooldown = false;

                    if (cmdObj.config.name in cooldowns) {
                        cooldown = cooldowns[cmdObj.config.name];
                    }

                    if (cooldown) {
                        bot.respondPm(message, 'Dieser Befehl wurde erst vor kurzem ausgeführt. Bitte versuche es später erneut.');
                        if (message.guild == bot.server) {
                            message.delete();
                        }

                        return;
                    }

                    cooldowns[cmdObj.config.name] = true;

                    setTimeout(() => {
                        cooldowns[cmdObj.config.name] = false;
                    }, cmdObj.config.cooldown * 1000);
                }
            }

            cmdObj.run(bot, message, args);
        } else {
            match = /^\/([a-zA-Z]+).*$/.exec(message.content);

            if(match) {
                bot.respondPm(message, 'Befehle können nur noch mit `!` vorangestellt ausgeführt werden. Beispiel: `' + message.content.replace('/', '!')+ '`');
                if (message.channel.type != 'dm') {
                    message.delete();
                }
            }

            addStats(false);
        }
    }

    function addStats(isCommand) {
        if (isUpdate || (!bot.server.channels.has(message.channel.id) && !isCommand)) return;

        const table = 'stats';
        const addCommand = isCommand ? 1 : 0;
        const addMessage = isCommand ? 0 : 1;
        const date = bot.momentToReDate(moment());

        bot.r.table(table).get(date).run().then(result => {
            if (result == null) {
                const data = {id: date, commands: addCommand, messages: addMessage};

                bot.r.table(table).insert(data).run().then(result => {
                    if (result.errors > 0) {
                        bot.log('Could not insert new date into db: ' + result.first_error);
                    }
                }).error(error => {
                    bot.log('Could not insert new date into db: ' + error);
                });
            } else {
                bot.r.table(table).get(date).update({
                    commands: bot.r.row('commands').add(addCommand),
                    messages: bot.r.row('messages').add(addMessage)
                }).run().then(result => {
                    if (result.errors > 0) {
                        bot.log('Could not update date in db: ' + result.first_error);
                    }
                }).error(error => {
                    bot.log('Could not update date in db: ' + error);
                });
            }
        }).error(error => {
            bot.log('Could not check if current date in db: ' + error);
        });
    }

    if (bot.server.channels.has(message.channel.id)) {
        handleCommand();
    } else {
        if (bot.server.members.has(message.author.id)) {
            handleCommand();
        } else {
            return message.channel.sendMessage('You have to be member of ' + bot.server.name + '!');
        }
    }
}

/* BOT METHODS */

bot.checkPermissions = (role, user) => {
    const member = bot.server.members.get(user.id);

    if (bot.server.owner == member && !config.DEBUG) {
        return true;
    }

    if (!bot.server.roles.has(role)) {
        return false;
    }

    return member.highestRole.comparePositionTo(bot.server.roles.get(role)) >= 0;
};

bot.getGuildMember = (user) => {
    return bot.server.members.get(user.id);
};

bot.respond = (message, response, mention) => {
    if (typeof mention === 'undefined') {
        mention = false;
    }

    if (mention) {
        message.reply(response);
    } else {
        message.channel.sendMessage(response);
    }
};

bot.respondPm = (message, response) => {
    message.author.sendMessage(response);
};

bot.getEmoji = (name) => {
    if (bot.server.emojis.exists('name', name)) {
        return bot.server.emojis.find('name', name).toString();
    } else {
        return ':robot:';
    }
};

bot.momentToReDate = (momentObj) => {
    return bot.r.time(momentObj.year(), momentObj.month() + 1, momentObj.date(), 'Z');
};

/* GENERAL APPLICATION STUFF */
process.on('exit', idle);

process.on('SIGINT', () => {
    idle();
    process.exit();

});

function idle() {
    bot.user.setStatus('idle');
}

function online() {
    bot.user.setStatus('online');
}

/* LOGIN */
bot.login(token);
<commit_msg>bugfix<commit_after>let Discord = require('discord.js'),
    config = require('./config/config'),
    roles = require('./config/roles'),
    Twitter = require('./modules/twitter').Twitter,
    fs = require('fs'),
    unirest = require('unirest'),
    token = config.TOKEN,
    bot = new Discord.Client(),
    exec = require('child_process').exec,
    moment = require('moment'),
    twitterTimer = null,
    cooldowns = {};

moment.locale('de');

bot.r = require('rethinkdbdash')({host: 'localhost', db: 'bronies_DSB'});

bot.radio = config.RADIO_START;

bot.commands = new Discord.Collection();
bot.aliases = new Discord.Collection();

bot.config = config;

bot.log = (msg) => {
    console.log(`[${moment().format("YYYY-MM-DD HH:mm:ss")}] ${msg}`);
};

/* COMMAND LOADER */
let commandLoader = function (currentPath) {
    bot.log("Searching for Commands... " + currentPath);
    let files = fs.readdirSync(currentPath);
    for (let i in files) {
        let currentFile = currentPath + '/' + files[i];
        let stats = fs.statSync(currentFile);
        if (stats.isFile()) {
            let loader = require(`${currentFile}`);
            bot.commands.set(loader.help.name.toLowerCase(), loader);
            if ('aliases' in loader.config) {
                loader.config.aliases.forEach(alias => {
                    bot.aliases.set(alias, loader.help.name);
                });
            }
        } else if (stats.isDirectory()) {
            commandLoader(currentFile);
        }
    }
};
commandLoader('./commands');

/* VERSION */
function getVersion(callback) {
    let info = {};

    exec('git rev-parse --short=4 HEAD', function (error, version) {
        if (error) {
            bot.log('Error getting version', error);
            info.version = 'unknown';
        } else {
            info.version = version.trim();
        }

        exec('git log -1 --pretty=%B', function (error, message) {
            if (error) {
                bot.log('Error getting commit message', error);
            } else {
                info.message = message.trim();
            }

            exec('git log -1 --date=short --pretty=format:%ci', function (error, timestamp) {
                if (error) {
                    console.log('Error getting creation time', error);
                } else {
                    info.timestamp = timestamp;
                }

                callback(info);
            });
        });
    });
}

/* BOT EVENTS */
bot.on('ready', () => {
    online();
    bot.log('I am ready!');
    getVersion((info) => {
        bot.versionInfo = info;
        bot.user.setGame('version ' + bot.versionInfo.version);

        if (config.DEBUG) bot.channels.get(config.BOT_CH).sendMessage('I am ready, running version `' + bot.versionInfo.version + '`! 👌');
    });

    if (!bot.guilds.has(config.SERVER_ID)) {
        bot.log('Bot is not connected to the selected server!');
        process.exit();
    }

    bot.server = bot.guilds.get(config.SERVER_ID);

    if (bot.server.members.has(config.BOT_ADMIN)) {
        bot.admin = bot.server.members.get(config.BOT_ADMIN);
    }

    const twitter = new Twitter(config.TWITTER_API, bot);

    let interval = config.DEBUG ? 20000 : 60000;

    if (twitterTimer != null) {
        clearInterval(twitterTimer);
    }

    twitterTimer = setInterval(() => {
        twitter.postNewTweets();
    }, interval);
});

bot.on('guildMemberAdd', (member) => {
    let embed = new Discord.RichEmbed({
        title: 'Ein neues Mitglied ist zu uns gestoßen!',
        description: `Hey **${member.user.username}**, willkommen auf dem offiziellen Discord Server von [Bronies.de](http://bronies.de/). Wirf doch zunächst einen Blick in **#info** für alle wichtigen Informationen und Bot-Befehle.`,
        thumbnail: {
            url: 'https://deratrox.de/dev/Bronies.de-DSB/_join.png'
        },
        color: 0x5FBB4E
    }).setFooter('Viel Spaß auf dem Server!');

    bot.channels.get(config.DEFAULT_CH).sendEmbed(embed);
});

bot.on('guildMemberRemove', (member) => {
    let embed = new Discord.RichEmbed({
        title: 'Ein Mitglied hat uns verlassen.',
        description: `**${member.user.username}** hat den Server verlassen. Bye bye **${member.user.username}**...`,
        thumbnail: {
            url: 'https://deratrox.de/dev/Bronies.de-DSB/_leave.png'
        },
        color: 0xEC4141
    }).setFooter('DERPY WANTS MUFFINS!');

    bot.channels.get(config.DEFAULT_CH).sendEmbed(embed);
});

bot.on('message', (message) => {
    onMessage(message, false);
});

bot.on('messageUpdate', (oldMessage, newMessage) => {
    if (typeof newMessage.author === 'undefined')
        return;

    onMessage(newMessage, true);
});

function onMessage(message, isUpdate) {
    if (message.author.id == bot.user.id) {
        return;
    }

    if (message.channel.type == 'group') {
        return;
    }

    function handleCommand() {
        let match = /^!([a-zA-Z]+).*$/.exec(message.content);

        if (message.channel.type == 'dm') {
            match = /^!?([a-zA-Z]+).*$/.exec(message.content);
        }

        if (match) {

            const args = message.content.split(' ').splice(1);
            let cmd = match[1].toLowerCase();

            let cmdObj = null;

            if (bot.commands.has(cmd)) {
                cmdObj = bot.commands.get(cmd);
            } else if (bot.aliases.has(cmd)) {
                cmdObj = bot.commands.get(bot.aliases.get(cmd));
            }

            if (cmdObj == null) {
                return;
            }

            addStats(true);

            if ('handled' in cmdObj.config) {
                if (cmdObj.config.handled == false) {
                    return;
                }
            }

            if ('server' in cmdObj.config) {
                if (cmdObj.config.server == true) {
                    if (message.guild != bot.server) {
                        return bot.respondPm(message, 'Dieser Befehl kann nur auf dem Bronies.de Discord Server ausgeführt werden!');
                    }
                }
            }

            if ('role' in cmdObj.config) {
                if (!bot.checkPermissions(cmdObj.config.role, message.author)) {
                    bot.respondPm(message, 'Du besitzt nicht genügend Rechte um diesen Befehl auszuführen!');
                    if (message.guild == bot.server) {
                        message.delete();
                    }
                    return;
                }
            }

            if ('cooldown' in cmdObj.config) {
                let check = true;

                if ('skip' in cmdObj.config) {
                    if (bot.checkPermissions(cmdObj.config.skip, message.author)) {
                        check = false;
                    }
                }

                if (check) {
                    let cooldown = false;

                    if (cmdObj.config.name in cooldowns) {
                        cooldown = cooldowns[cmdObj.config.name];
                    }

                    if (cooldown) {
                        bot.respondPm(message, 'Dieser Befehl wurde erst vor kurzem ausgeführt. Bitte versuche es später erneut.');
                        if (message.guild == bot.server) {
                            message.delete();
                        }

                        return;
                    }

                    cooldowns[cmdObj.config.name] = true;

                    setTimeout(() => {
                        cooldowns[cmdObj.config.name] = false;
                    }, cmdObj.config.cooldown * 1000);
                }
            }

            cmdObj.run(bot, message, args);
        } else {
            match = /^\/([a-zA-Z]+).*$/.exec(message.content);

            if (match) {
                bot.respondPm(message, 'Befehle können nur noch mit `!` vorangestellt ausgeführt werden. Beispiel: `' + message.content.replace('/', '!') + '`');
                if (message.channel.type != 'dm') {
                    message.delete();
                }
            } else {
                addStats(false);
            }
        }
    }

    function addStats(isCommand) {
        if (isUpdate || (!bot.server.channels.has(message.channel.id) && !isCommand)) return;

        const table = 'stats';
        const addCommand = isCommand ? 1 : 0;
        const addMessage = isCommand ? 0 : 1;
        const date = bot.momentToReDate(moment());

        bot.r.table(table).get(date).run().then(result => {
            if (result == null) {
                const data = {id: date, commands: addCommand, messages: addMessage};

                bot.r.table(table).insert(data).run().then(result => {
                    if (result.errors > 0) {
                        bot.log('Could not insert new date into db: ' + result.first_error);
                    }
                }).error(error => {
                    bot.log('Could not insert new date into db: ' + error);
                });
            } else {
                bot.r.table(table).get(date).update({
                    commands: bot.r.row('commands').add(addCommand),
                    messages: bot.r.row('messages').add(addMessage)
                }).run().then(result => {
                    if (result.errors > 0) {
                        bot.log('Could not update date in db: ' + result.first_error);
                    }
                }).error(error => {
                    bot.log('Could not update date in db: ' + error);
                });
            }
        }).error(error => {
            bot.log('Could not check if current date in db: ' + error);
        });
    }

    if (bot.server.channels.has(message.channel.id)) {
        handleCommand();
    } else {
        if (bot.server.members.has(message.author.id)) {
            handleCommand();
        } else {
            return message.channel.sendMessage('You have to be member of ' + bot.server.name + '!');
        }
    }
}

/* BOT METHODS */

bot.checkPermissions = (role, user) => {
    const member = bot.server.members.get(user.id);

    if (bot.server.owner == member && !config.DEBUG) {
        return true;
    }

    if (!bot.server.roles.has(role)) {
        return false;
    }

    return member.highestRole.comparePositionTo(bot.server.roles.get(role)) >= 0;
};

bot.getGuildMember = (user) => {
    return bot.server.members.get(user.id);
};

bot.respond = (message, response, mention) => {
    if (typeof mention === 'undefined') {
        mention = false;
    }

    if (mention) {
        message.reply(response);
    } else {
        message.channel.sendMessage(response);
    }
};

bot.respondPm = (message, response) => {
    message.author.sendMessage(response);
};

bot.getEmoji = (name) => {
    if (bot.server.emojis.exists('name', name)) {
        return bot.server.emojis.find('name', name).toString();
    } else {
        return ':robot:';
    }
};

bot.momentToReDate = (momentObj) => {
    return bot.r.time(momentObj.year(), momentObj.month() + 1, momentObj.date(), 'Z');
};

/* GENERAL APPLICATION STUFF */
process.on('exit', idle);

process.on('SIGINT', () => {
    idle();
    process.exit();

});

function idle() {
    bot.user.setStatus('idle');
}

function online() {
    bot.user.setStatus('online');
}

/* LOGIN */
bot.login(token);
<|endoftext|>


<commit_before>'use strict';
function solve() {
    function* IdGenerator() {
        let lastId = 0;
        while (true) {
            yield lastId += 1;
        }
    }

    const validator = (() => {
        const ISBN_PATTERN = /^[0-9]+$/;

        return {
            validateIsStringWithLength(value, min = 1, max = Number.MAX_VALUE) {
                if (value === undefined) {
                    throw new Error();
                }

                if (typeof value !== 'string') {
                    throw new Error();
                }

                if (!(min <= value.length && value.length <= max)) {
                    throw new Error();
                }
            },
            validateIsNumberInRange(value, min = 0, max = Number.MAX_VALUE) {
                value = Number(value);
                if (isNaN(value)) {
                    throw new Error();
                }

                if (!(min <= value && value <= max)) {
                    throw new Error();
                }
            },
            validateIsbn(value) {
                validator.validateIsStringWithLength(value);
                if (!(value.length === 10 || value.length === 13)) {
                    throw new Error();
                }

                if (!ISBN_PATTERN.test(value)) {
                    throw new Error();
                }
            },
            validateIsItem(item) {
                const hasId = item.id || false;
                const hasName = item.name || false;
                const hasDescription = item.description || false;

                const isValid = hasId && hasName && hasDescription;
                return isValid;
            }
        };
    })();

    const itemIdGenerator = IdGenerator();
    class Item {
        constructor(name, description) {
            this.id = itemIdGenerator.next().value;
            this.name = name;
            this.description = description;
        }

        get name() {
            return this._name;
        }

        set name(value) {
            validator.validateIsStringWithLength(value, 2, 40);
            this._name = value;
        }

        get description() {
            return this._description;
        }

        set description(value) {
            validator.validateIsStringWithLength(value);
            this._description = value;
        }
    }

    class Book extends Item {
        constructor(name, isbn, genre, description) {
            super(name, description);

            this.genre = genre;
            this.isbn = isbn;
        }

        get genre() {
            return this._genre;
        }

        set genre(value) {
            validator.validateIsStringWithLength(value, 2, 20);
            this._genre = value;
        }

        get isbn() {
            return this._isbn;
        }

        set isbn(value) {
            validator.validateIsbn(value);
            this._isbn = value;
        }
    }

    class Media extends Item {
        constructor(name, rating, duration, description) {
            super(name, description);

            this.rating = rating;
            this.duration = duration;
        }

        get duration() {
            return this._duration;
        }

        set duration(value) {
            validator.validateIsNumberInRange(value, 1);
            this._duration = value;
        }

        get rating() {
            return this._rating;
        }

        set rating(value) {
            validator.validateIsNumberInRange(value, 1, 5);
            this._rating = value;
        }
    }

    const catalogIdGenerator = IdGenerator();
    class Catalog {
        constructor(name, items) {
            this.id = catalogIdGenerator.next().value;

            this.name = name;
            this.items = items || [];
        }

        get name() {
            return this._name;
        }

        set name(value) {
            validator.validateIsStringWithLength(value, 2, 40);
            this._name = value;
        }

        add(...items) {
            if (items.length === 0) {
                throw new Error();
            }

            if (items.length === 1) {
                items = items[0];
            }

            items.forEach(item => {
                if (!validator.validateIsItem(item)) {
                    throw new Error();
                }
            });

            this.items.push(...items);
            return this;
        }

        find(options) {
            if (options === undefined) {
                throw new Error();
            }

            let result;
            if (typeof options === 'object') {
                const matches = this.items.filter(item => {
                    let isMatch = true;
                    if (options.id && options.id !== item.id) {
                        return false;
                    }

                    if (options.name && options.name !== item.name) {
                        return false;
                    }

                    return isMatch;
                });

                result = matches;
            } else {
                const id = Number(options);
                if (isNaN(id)) {
                    throw new Error();
                }

                const matches = this.items.filter(item => {
                    const isMatch = item.id === id;
                    return isMatch;
                });

                result = matches[0] || null;
            }

            return result;
        }

        search(pattern) {
            validator.validateIsStringWithLength(pattern, 1);
            pattern = pattern.toLowerCase();

            const matches = this.items.filter(item => {
                const isMatchingName = item.name.toLowerCase().indexOf(pattern) >= 0;
                const isMatchingDescription = item.description.toLowerCase().indexOf(pattern) >= 0;

                return isMatchingName || isMatchingDescription;
            });

            return matches;
        }
    }

    class BookCatalog extends Catalog {
        constructor(name) {
            super(name);
        }

        add(...books) {
            if (books.length === 0) {
                throw new Error();
            }

            if (books.length === 1 && Array.isArray(books[0])) {
                books = books[0];
            }

            books.forEach(book => {
                if (!book.isbn) {
                    throw new Error();
                }

                if (!book.genre) {
                    throw new Error();
                }
            });

            super.add(books);
            return this;
        }

        getGenres() {
            const genres = [];
            this.items.forEach(book => {
                const genre = book.genre.toLowerCase();
                if (genres.indexOf(genre) < 0) {
                    genres.push(genre);
                }
            });

            return genres;
        }

        find(options) {
            let filtered = super.find(options);

            if (options.genre) {
                filtered = filtered.filter(item => {
                    const isMatch = item.genre.toLowerCase() === options.genre.toLowerCase();
                    return isMatch;
                });
            }

            return filtered;
        }
    }

    class MediaCatalog extends Catalog {
        constructor(name) {
            super(name);
        }

        add(...media) {
            if (media.length === 0) {
                throw new Error();
            }

            if (media.length === 1 && Array.isArray(media[0])) {
                media = media[0];
            }

            media.forEach(medium => {
                if (!medium.rating) {
                    throw new Error();
                }

                if (!medium.duration) {
                    throw new Error();
                }
            });

            super.add(media);
            return this;
        }

        getTop(count) {

        }

        getSortedByDuration() {

        }
    }

    const module = {
        getBook: function (name, isbn, genre, description) {
            return new Book(name, isbn, genre, description);
        },
        getMedia: function (name, rating, duration, description) {
            return new Media(name, rating, duration, description);
        },
        getBookCatalog: function (name) {
            return new BookCatalog(name);
        },
        getMediaCatalog: function (name) {
            return new MediaCatalog(name);
        }
    };

    return module;
}

module.exports = solve;<commit_msg>update<commit_after>'use strict';
function solve() {
    function* IdGenerator() {
        let lastId = 0;
        while (true) {
            yield lastId += 1;
        }
    }

    const validator = (() => {
        const ISBN_PATTERN = /^[0-9]+$/;

        return {
            validateIsStringWithLength(value, min = 1, max = Number.MAX_VALUE) {
                if (value === undefined) {
                    throw new Error();
                }

                if (typeof value !== 'string') {
                    throw new Error();
                }

                if (!(min <= value.length && value.length <= max)) {
                    throw new Error();
                }
            },
            validateIsNumberInRange(value, min = 0, max = Number.MAX_VALUE) {
                value = Number(value);
                if (isNaN(value)) {
                    throw new Error();
                }

                if (!(min <= value && value <= max)) {
                    throw new Error();
                }
            },
            validateIsbn(value) {
                validator.validateIsStringWithLength(value);
                if (!(value.length === 10 || value.length === 13)) {
                    throw new Error();
                }

                if (!ISBN_PATTERN.test(value)) {
                    throw new Error();
                }
            },
            validateIsItem(item) {
                const hasId = item.id || false;
                const hasName = item.name || false;
                const hasDescription = item.description || false;

                const isValid = hasId && hasName && hasDescription;
                return isValid;
            }
        };
    })();

    const itemIdGenerator = IdGenerator();
    class Item {
        constructor(name, description) {
            this.id = itemIdGenerator.next().value;
            this.name = name;
            this.description = description;
        }

        get name() {
            return this._name;
        }

        set name(value) {
            validator.validateIsStringWithLength(value, 2, 40);
            this._name = value;
        }

        get description() {
            return this._description;
        }

        set description(value) {
            validator.validateIsStringWithLength(value);
            this._description = value;
        }
    }

    class Book extends Item {
        constructor(name, isbn, genre, description) {
            super(name, description);

            this.genre = genre;
            this.isbn = isbn;
        }

        get genre() {
            return this._genre;
        }

        set genre(value) {
            validator.validateIsStringWithLength(value, 2, 20);
            this._genre = value;
        }

        get isbn() {
            return this._isbn;
        }

        set isbn(value) {
            validator.validateIsbn(value);
            this._isbn = value;
        }
    }

    class Media extends Item {
        constructor(name, rating, duration, description) {
            super(name, description);

            this.rating = rating;
            this.duration = duration;
        }

        get duration() {
            return this._duration;
        }

        set duration(value) {
            validator.validateIsNumberInRange(value, 1);
            this._duration = value;
        }

        get rating() {
            return this._rating;
        }

        set rating(value) {
            validator.validateIsNumberInRange(value, 1, 5);
            this._rating = value;
        }
    }

    const catalogIdGenerator = IdGenerator();
    class Catalog {
        constructor(name, items) {
            this.id = catalogIdGenerator.next().value;

            this.name = name;
            this.items = items || [];
        }

        get name() {
            return this._name;
        }

        set name(value) {
            validator.validateIsStringWithLength(value, 2, 40);
            this._name = value;
        }

        add(...items) {
            if (items.length === 0) {
                throw new Error();
            }

            if (items.length === 1) {
                items = items[0];
            }

            items.forEach(item => {
                if (!validator.validateIsItem(item)) {
                    throw new Error();
                }
            });

            this.items.push(...items);
            return this;
        }

        find(options) {
            if (options === undefined) {
                throw new Error();
            }

            let result;
            if (typeof options === 'object') {
                const matches = this.items.filter(item => {
                    let isMatch = true;
                    if (options.id && options.id !== item.id) {
                        return false;
                    }

                    if (options.name && options.name !== item.name) {
                        return false;
                    }

                    return isMatch;
                });

                result = matches;
            } else {
                const id = Number(options);
                if (isNaN(id)) {
                    throw new Error();
                }

                const matches = this.items.filter(item => {
                    const isMatch = item.id === id;
                    return isMatch;
                });

                result = matches[0] || null;
            }

            return result;
        }

        search(pattern) {
            validator.validateIsStringWithLength(pattern, 1);
            pattern = pattern.toLowerCase();

            const matches = this.items.filter(item => {
                const isMatchingName = item.name.toLowerCase().indexOf(pattern) >= 0;
                const isMatchingDescription = item.description.toLowerCase().indexOf(pattern) >= 0;

                return isMatchingName || isMatchingDescription;
            });

            return matches;
        }
    }

    class BookCatalog extends Catalog {
        constructor(name) {
            super(name);
        }

        add(...books) {
            if (books.length === 0) {
                throw new Error();
            }

            if (books.length === 1 && Array.isArray(books[0])) {
                books = books[0];
            }

            books.forEach(book => {
                if (!book.isbn) {
                    throw new Error();
                }

                if (!book.genre) {
                    throw new Error();
                }
            });

            super.add(books);
            return this;
        }

        getGenres() {
            const genres = [];
            this.items.forEach(book => {
                const genre = book.genre.toLowerCase();
                if (genres.indexOf(genre) < 0) {
                    genres.push(genre);
                }
            });

            return genres;
        }

        find(options) {
            let filtered = super.find(options);

            if (options.genre) {
                filtered = filtered.filter(item => {
                    const isMatch = item.genre.toLowerCase() === options.genre.toLowerCase();
                    return isMatch;
                });
            }

            return filtered;
        }
    }

    class MediaCatalog extends Catalog {
        constructor(name) {
            super(name);
        }

        add(...media) {
            if (media.length === 0) {
                throw new Error();
            }

            if (media.length === 1 && Array.isArray(media[0])) {
                media = media[0];
            }

            media.forEach(medium => {
                if (!medium.rating) {
                    throw new Error();
                }

                if (!medium.duration) {
                    throw new Error();
                }
            });

            super.add(media);
            return this;
        }

        find(options) {
            let filtered = super.find(options);

            if (options.rating) {
                filtered = filtered.filter(item => {
                    const isMatch = item.rating === options.rating;
                    return isMatch;
                });
            }

            return filtered;
        }

        getTop(count) {
            validator.validateIsNumberInRange(count, 1);
            const sorted = this.items.sort((a, b) => {
                const comparison = b.rating - a.rating;
            });

            let result = [];
            for (let i = 0; i < count; i += 1) {
                if (sorted[i]) {
                    result.push({
                        id: sorted[i].id,
                        name: sorted[i].name
                    });
                } else {
                    break;
                }
            }

            return result;
        }

        getSortedByDuration() {
            let sorted = this.items.sort((a, b) => {
                const compareDuration = b.duration - a.duration;
                if (compareDuration !== 0) {
                    return compareDuration;
                } else {
                    const compareId = a.id - b.id;
                    return compareId;
                }
            });

            return sorted;
        }
    }

    const module = {
        getBook: function (name, isbn, genre, description) {
            return new Book(name, isbn, genre, description);
        },
        getMedia: function (name, rating, duration, description) {
            return new Media(name, rating, duration, description);
        },
        getBookCatalog: function (name) {
            return new BookCatalog(name);
        },
        getMediaCatalog: function (name) {
            return new MediaCatalog(name);
        }
    };

    return module;
}

module.exports = solve;<|endoftext|>


<commit_before>test( "Popcorn Subtitle Plugin", function() {

  var popped = Popcorn( "#video" ),
      popped2 = Popcorn( "#video2" ),
      expects = 12,
      count = 0,
      subTop = 9001,
      subLeft = 9001,
      subtitlediv,
      subtitle2div;

  expect( expects );

  function plus() {
    if ( ++count === expects ) {
      start();
    }
  }

  stop( 12000 );

  ok( "subtitle" in popped, "subtitle is a method of the popped instance" );
  plus();

  popped.subtitle({
      start: 0,
      end: 2,
      text: "this is the first subtitle of 2011"
    })
  .subtitle({
      start: 2,
      end: 4,
      text: "this is the second subtitle of 2011"
    })
  .subtitle({
      start: 5,
      end: 7,
      text: "this is the third subtitle of 2011"
    } )
    .volume( 0 )
    .play();

  subtitlediv = popped.getTrackEvent( popped.getLastTrackEventId() ).container;

  popped.subtitle({
    start: 7,
    end: 9,
    text: "instance one test"
  });

  popped2.subtitle({
      start: 7,
      end: 9,
      text: "instance two test"
    })
    .volume( 0 )
    .play().pause();

  subtitle2div = popped2.getTrackEvent( popped2.getLastTrackEventId() ).container;

  popped.exec( 1, function() {

    popped.media.pause();
    equals( subtitlediv.children[ 0 ].innerHTML, "this is the first subtitle of 2011", "subtitle displaying correct information" );
    plus();

    // capturing location now, to check against later,
    // a subtitle must be displayed to get valid data
    // which is why we do this in exec
    subLeft = subtitlediv.style.left;
    subTop  = subtitlediv.style.top;

    // changing position
    popped.media.style.position = "absolute";
    popped.media.style.left = "400px";
    popped.media.style.top = "600px";
    popped.media.play();

  });

  popped.exec( 3, function() {

    popped.media.pause();

    // check position of subttile that should of moved with video,
    // a subtitle must be displayed to get valid data
    ok( subtitlediv.style.left !== subLeft, "subtitle's left position has changed" );
    plus();
    ok( subtitlediv.style.top !== subTop, "subtitle's top position has changed" );
    plus();

    // we know values have changed, but how accurate are they?
    // check values against the video's values
    // we need four checks because if we just check against video's position,
    // and video's position hasn't updated either, we'll pass when we should fail
    equals( subtitlediv.style.left, popped.position().left + "px", "subtitle left position moved" );
    plus();
    ok( Popcorn.position( subtitlediv ).top > popped.position().top, "subtitle top position moved" );
    plus();

    equals( subtitlediv.children[ 1 ].innerHTML, "this is the second subtitle of 2011", "subtitle displaying correct information" );
    plus();

    popped.media.play();

  });

  popped.exec( 4, function() {

    popped.media.pause();
    equals( subtitlediv.children[ 1 ].innerHTML, "", "subtitle is clear" );
    plus();

    popped.media.play();

  });

  popped.exec( 8, function() {
    popped.pause();
    popped2.currentTime( 8 ).play();
  });

  popped2.exec( 8, function() {
    popped2.media.pause();

    equals( subtitlediv.children[ 3 ].innerHTML, "instance one test", "subtitle displaying correct information" );
    plus();
    equals( subtitle2div.children[ 0 ].innerHTML, "instance two test", "subtitle displaying correct information" );
    plus();

    popped.media.play();
  });

  popped.exec( 10, function() {
    ok( document.getElementById( "subtitle-0" ).style.display === "none" &&
        document.getElementById( "subtitle-1" ).style.display === "none" &&
        document.getElementById( "subtitle-2" ).style.display === "none", "All subtitles are no longer visible" );
    plus();

    popped.pause().removeTrackEvent( popped.data.trackEvents.byStart[ 6 ]._id );

    ok( !document.getElementById( "subtitle-2" ), "removed subtitle div was properly destroyed"  );
    plus();
  });
});

test( "subtitle data tests", function() {

  var popped = Popcorn( "#video" ),
      expects = 1,
      count = 0,
      container = document.getElementById( "sub-content" );

  expect( expects );

  function plus() {
    if ( ++count === expects ) {
      start();
    }
  }

  stop( 12000 );

  popped.subtitle({
    start: 0,
    end: 10,
    target: "sub-content"
  });

  popped.pause( 0 );

  equals( container.children[ 0 ].innerHTML, "", "subtitle with no text defaults to an empty string" );
  plus();
});

test( "subtitle container creation tests", function() {

  var popped = Popcorn( "#video" ),
      expects = 3,
      containerAtLoad = document.getElementById("divThatDoesntExist"),
      containerAfterParse,
      count = 0;

  expect( expects );

  function plus() {
    if ( ++count === expects ) {
      start();
    }
  }

  stop( 12000 );

  popped.subtitle({
    start: 0,
    end: 10,
    text: "My Text",
    target: "divThatDoesntExist"
  });

  popped.pause( 0 );

  containerAfterParse = document.getElementById("divThatDoesntExist");

  equals( !!containerAtLoad, false, "Container doesn't exist initially" );
  plus();
  equals( !!containerAfterParse, true, "Container exists now" );
  plus();
  equals( containerAfterParse.children[ 0 ].innerHTML, "My Text", "Subtitle displayed in created container" );
  plus();

  // Cleanup
  containerAfterParse.parentNode.removeChild( containerAfterParse );
});<commit_msg>Extend Subtitle unit test timeout for Safari on Windows [#871]<commit_after>test( "Popcorn Subtitle Plugin", function() {

  var popped = Popcorn( "#video" ),
      popped2 = Popcorn( "#video2" ),
      expects = 12,
      count = 0,
      subTop = 9001,
      subLeft = 9001,
      subtitlediv,
      subtitle2div;

  expect( expects );

  function plus() {
    if ( ++count === expects ) {
      start();
    }
  }

  stop( 15000 );

  ok( "subtitle" in popped, "subtitle is a method of the popped instance" );
  plus();

  popped.subtitle({
      start: 0,
      end: 2,
      text: "this is the first subtitle of 2011"
    })
  .subtitle({
      start: 2,
      end: 4,
      text: "this is the second subtitle of 2011"
    })
  .subtitle({
      start: 5,
      end: 7,
      text: "this is the third subtitle of 2011"
    } )
    .volume( 0 )
    .play();

  subtitlediv = popped.getTrackEvent( popped.getLastTrackEventId() ).container;

  popped.subtitle({
    start: 7,
    end: 9,
    text: "instance one test"
  });

  popped2.subtitle({
      start: 7,
      end: 9,
      text: "instance two test"
    })
    .volume( 0 )
    .play().pause();

  subtitle2div = popped2.getTrackEvent( popped2.getLastTrackEventId() ).container;

  popped.exec( 1, function() {

    popped.media.pause();
    equals( subtitlediv.children[ 0 ].innerHTML, "this is the first subtitle of 2011", "subtitle displaying correct information" );
    plus();

    // capturing location now, to check against later,
    // a subtitle must be displayed to get valid data
    // which is why we do this in exec
    subLeft = subtitlediv.style.left;
    subTop  = subtitlediv.style.top;

    // changing position
    popped.media.style.position = "absolute";
    popped.media.style.left = "400px";
    popped.media.style.top = "600px";
    popped.media.play();

  });

  popped.exec( 3, function() {

    popped.media.pause();

    // check position of subttile that should of moved with video,
    // a subtitle must be displayed to get valid data
    ok( subtitlediv.style.left !== subLeft, "subtitle's left position has changed" );
    plus();
    ok( subtitlediv.style.top !== subTop, "subtitle's top position has changed" );
    plus();

    // we know values have changed, but how accurate are they?
    // check values against the video's values
    // we need four checks because if we just check against video's position,
    // and video's position hasn't updated either, we'll pass when we should fail
    equals( subtitlediv.style.left, popped.position().left + "px", "subtitle left position moved" );
    plus();
    ok( Popcorn.position( subtitlediv ).top > popped.position().top, "subtitle top position moved" );
    plus();

    equals( subtitlediv.children[ 1 ].innerHTML, "this is the second subtitle of 2011", "subtitle displaying correct information" );
    plus();

    popped.media.play();

  });

  popped.exec( 4, function() {

    popped.media.pause();
    equals( subtitlediv.children[ 1 ].innerHTML, "", "subtitle is clear" );
    plus();

    popped.media.play();

  });

  popped.exec( 8, function() {
    popped.pause();
    popped2.currentTime( 8 ).play();
  });

  popped2.exec( 8, function() {
    popped2.media.pause();

    equals( subtitlediv.children[ 3 ].innerHTML, "instance one test", "subtitle displaying correct information" );
    plus();
    equals( subtitle2div.children[ 0 ].innerHTML, "instance two test", "subtitle displaying correct information" );
    plus();

    popped.media.play();
  });

  popped.exec( 10, function() {
    ok( document.getElementById( "subtitle-0" ).style.display === "none" &&
        document.getElementById( "subtitle-1" ).style.display === "none" &&
        document.getElementById( "subtitle-2" ).style.display === "none", "All subtitles are no longer visible" );
    plus();

    popped.pause().removeTrackEvent( popped.data.trackEvents.byStart[ 6 ]._id );

    ok( !document.getElementById( "subtitle-2" ), "removed subtitle div was properly destroyed"  );
    plus();
  });
});

test( "subtitle data tests", function() {

  var popped = Popcorn( "#video" ),
      expects = 1,
      count = 0,
      container = document.getElementById( "sub-content" );

  expect( expects );

  function plus() {
    if ( ++count === expects ) {
      start();
    }
  }

  stop( 12000 );

  popped.subtitle({
    start: 0,
    end: 10,
    target: "sub-content"
  });

  popped.pause( 0 );

  equals( container.children[ 0 ].innerHTML, "", "subtitle with no text defaults to an empty string" );
  plus();
});

test( "subtitle container creation tests", function() {

  var popped = Popcorn( "#video" ),
      expects = 3,
      containerAtLoad = document.getElementById("divThatDoesntExist"),
      containerAfterParse,
      count = 0;

  expect( expects );

  function plus() {
    if ( ++count === expects ) {
      start();
    }
  }

  stop( 12000 );

  popped.subtitle({
    start: 0,
    end: 10,
    text: "My Text",
    target: "divThatDoesntExist"
  });

  popped.pause( 0 );

  containerAfterParse = document.getElementById("divThatDoesntExist");

  equals( !!containerAtLoad, false, "Container doesn't exist initially" );
  plus();
  equals( !!containerAfterParse, true, "Container exists now" );
  plus();
  equals( containerAfterParse.children[ 0 ].innerHTML, "My Text", "Subtitle displayed in created container" );
  plus();

  // Cleanup
  containerAfterParse.parentNode.removeChild( containerAfterParse );
});<|endoftext|>


<commit_before> function showBatchWork(keyword){
    	window.clearInterval(inter);
		$("#main").load("remote/batch.jsp",{"tb":"room","keyword":keyword});
    }
 function doBatchWork(frm){
	 window.clearInterval(inter);
		var data=$(frm).serialize();

		$("#main").load("remote/batch.jsp#"+Math.random(),data);
		return false;
	}

 function viewroom(roomid){
 	window.clearInterval(inter);
		$("#main").load("view.jsp",{"tb":"room","id":roomid},reformatview);
 }
	function dbid(evt){
		var data=$(evt.target).parent().parent().attr("id");
		return parseInt(data);
	}
	function view(rowid){
		window.clearTimeout(inter);
		var tbname=$("#tbname").val();
		$("#main").load("view.jsp",{"tb":tbname,"id":rowid},reformatview);

		//window.open("view.jsp?tb="+tbname+"&id="+rowid);
	}
	function edit(rowid){
		window.clearTimeout(inter);
		var tbname=$("#tbname").val();
		$("#main").load("add.jsp",{"tb":tbname,"id":rowid},reformatform);
	}

	function report_row_del(tbname,rowid){
		var ans=confirm('确定删除编号'+rowid);
		if(ans){
			$.post("del.jsp",{"tbname":tbname,"id":rowid},new function(){
				 window.setTimeout("$('#main').load('report.jsp?page=0')",1000);
			});
		}
	}
	function del(rowid){
		var ans=confirm('确定删除编号'+rowid);
		var tbname=$("#tbname").val();
		if(ans){
			$.post("del.jsp",{"tbname":tbname,"id":rowid},new function(){
				 window.setTimeout("mainLoad('"+tbname+"')",1000);
			});
		}
	}

	function addButton(main){
		//$("#main tr :last-child").css("background","#eeeeee");
		var buttons=""+
		""+
		""
								+"";

		buttons+="	";

		$("#main tr").append(buttons);
		$(".glyphicon-eye-open").bind("click",function(evt){
			view(dbid(evt));
		});
		$(".glyphicon-trash").bind("click",function(evt){
			del(dbid(evt));
		});
		$(".glyphicon-edit").bind("click",function(evt){
			edit(dbid(evt));
		});
		$(".glyphicon-refresh").bind("click",function(evt){
			goodsPriceSync(dbid(evt));
		});

	}
	function filteNext(preSelect){
		console.log("pre:",preSelect.name,preSelect.value);
		var tr=$(preSelect).parent().parent();
		var nextSelect=tr.next().children().last().children().last();
		if(nextSelect!=null&&nextSelect.length>0&&nextSelect[0].tagName=="SELECT"){
			var keyword=preSelect.name;
			var keyvalue=preSelect.value;
			console.log("reload:",keyword,keyvalue);
		    loadSelect(nextSelect,"keyword="+keyword+"&keyvalue="+keyvalue);
		}
	}
	function loadSelect(input,append){
		var input_name=input.attr("name");
		var default_value=input.val();
		if(input[0].nodeName=="SPAN")  //view use span for tag, text() for value
			 	default_value=input.text();
		var tbname=input_name.substring(0,input_name.length-3);
		if(input.attr("fr")!=undefined) tbname=input.attr("fr");;
		var td=input.parent();
		td.load("select.jsp?tbname="+tbname+"&input_name="+input_name+"&value="+default_value+"&"+append);

	}
	function loadFile(input){
		var input_name=input.attr("name");
		var default_value=input.val();
		if(input[0].nodeName=="SPAN")  //view use span for tag, text() for value
			 	default_value=input.text();
		var td=input.parent();
		input[0].id=input_name;
		input[0].type="hidden";

		var html=$(td).html()+"";

		$(td).html(html);

	}
	function reformatform(){
		$("input[name$=_file]").each(function(){
			loadFile($(this));
		});
		$("input[name$=_id]").each(function(){
				loadSelect($(this));
		});
		$("input[postLoad=1]").each(function(){
			loadSelect($(this));
		});
		$(".input_date").datepicker();
		$(".input_datetime").datetimepicker(
				{   showSecond: false,
		            timeFormat: 'hh:mm:ss',
		            stepHour: 1,
		            stepMinute: 1,
		            stepSecond: 1
		        });
		if(tableName.endsWith('config')){
			$("textarea").attr("rows",20).attr("cols",80);
		}else{
				$("textarea").ckeditor();
		}
		$("span[class='modifiable']").each(function(){
			var tbname=$(this).attr("tb");
			var fdname=$(this).attr("fd");
			var rid=$(this).attr("rid");

			if(tbname==tableName||lastLoad.indexOf("report")!=-1){
				$(this)[0].title="双击修改";
				if($(this).html()=="")$(this).html("_");
				$(this).dblclick(function(){
					var newValue=prompt("手工修改",$(this).text());
					if(newValue!=null&&newValue!=$(this).text()){
						//alert("update "+tbname+" set "+fdname+"="+newValue+" where id="+rid);

						$.post("callStatic.jsp",{"c":"com.newsclan.crud.Tools","m":"update",
							"tbname":tbname,
							"fdname":fdname,
							"value":newValue,
							"id":rid

								},new function(){
							 window.setTimeout("refresh();",500);
						});

					}
				});
			}else{
				$(this)[0].title="双击选择";
				$(this).dblclick(function(){
					var span=$(this);
					var oldValue=$(this).html();
					var fdname=tbname+"_id";
					//if(fdname.startsWith("t_")) fdname=fdname.substring(2);
					var td=$(this).parent();
					if(fdname=="pcdata_id"){ //适配某易语言系统中文字段名
						fdname="批次uid";
					}
					var selectURL="select.jsp?tbname="+tbname+"&input_name="+fdname+"&value=-1";
					$(this).load("select.jsp",{"tbname":tbname,"input_name":fdname,"value":-1},
						function(text,status,http){
							if(status=="success"){
								var sel=$("select[name="+fdname+"]");
								if(sel.length==0){
									sel=td.find("select");
								}
								var opt=$("option");
								var nowValue=opt.map(function(){if($(this).text()==oldValue) return($(this).val())});
								sel.val(nowValue);
								sel.removeAttr("onchange");
								sel.change(function(){
									var newValue=$(this).val();
									//alert(fdname);
									$.post("callStatic.jsp",{"c":"com.newsclan.crud.Tools","m":"update",
										"tbname":tableName,
										"fdname":fdname,
										"value":newValue,
										"id":rid

											},new function(){
										 window.setTimeout("refresh();",500);
									});
								});
								sel.blur(function(){
									span.html(oldValue);
								});
								sel.focus();
							}
						}

					);
					//	alert("update "+tableName+" set "+tbname+"_id="+oldValue+" where id="+rid);
				});

			}
		});
	}
	function reformatview(){
		$("span[name$=_id]").each(function(){
			loadSelect($(this));
		});
	}

	function loadImport(){
		window.clearInterval(inter);
		$("#main").load("import.jsp?"+Math.random(),{},reformatform);
	}

	function loadReport(){
		window.clearInterval(inter);
		$("#main").load("report_select.jsp?"+Math.random(),{},reformatform);
	}

	function passChange(){
		window.clearInterval(inter);
		$("#main").load("passChange.jsp?"+Math.random());
	}
	function checkIfPasswordIsSame(frm){
		if($("#password").val()!=$("#password2").val()){
			alert("两次密码不一致");
			return false;
		}
		return true;
	}
	function mainLoad(tbname,pageNum,keyword){
		tableName=tbname;
		if (typeof(pageNum)=='undefined') pageNum=thepage;
		if (typeof(keyword)=='undefined'||keyword=="") keyword=searchKeyword;
		thepage=pageNum;
		searchKeyword=keyword;
		window.clearInterval(inter);
		$("#main").load("list.jsp",{"tb":tbname,"pageNum":pageNum,"keyword":keyword},function(text,status,http){

			if(status=="success"){
				addButton($("#main"));
				reformatform();
				$("#tbname").after("");
				$("#addrow").click(function(){
					$("#main").load("add.jsp",{tb:tbname},reformatform);
				});
				if($("#auto")[0].checked){
					autoRefresh();
				}
			}
		});
		lastLoad="mainLoad('"+tbname+"',"+pageNum+",'"+keyword+"');";

	}
	function reportPage(page){
		$('#main').load('report.jsp?page='+page);
		lastLoad="reportPage("+page+");";
	}
	function pageUp(tbname,pageNum){
		mainLoad(tbname,pageNum-1,searchKeyword);
	}
	function pageDown(tbname,pageNum){
		mainLoad(tbname,pageNum+1,searchKeyword);
	}
	function showReport(frm){
		var data=$(frm).serialize();
		$("#main").load("report_select.jsp?"+Math.random(),data,reformatform);
		lastLoad="reportPage(0);";
		return false;
	}

	function submitAdd(tbname){
		var data=$("#addForm").serialize();
		$.post("add.jsp",data,new function(){
			 mainLoad(tbname,0,searchKeyword);
		});
	}
	function submitTable(){
		var data=$("#frmAddTable").serialize();
		var tbname=$("#tb_name").val();

		$.post("addTable.jsp",data,new function(){
			 window.setTimeout("mainLoad('"+tbname+"',0,'"+searchKeyword+"')",1000);
		});
	}
	function addTable(){
		$("#main").empty();
		$("#main").append("<form method=post onSubmit='submitTable()' id='frmAddTable'>

</form>");
		$("#tb_adding").append("
表名<input type=text id=tb_name name=tb_name value='tb_'>	中文<input type=text name=tb_title value='信息'>
");
		$("#tb_adding").append("
列名	类型	中文
");
		$("#frmAddTable").append("<input type=button onclick='submitTable()' value='确定'><input type=reset value='重置'>");
		addColumn();

	}
	function addColumn(){
		var row="
";
		row+="<input name=fd_name type=text>	";
		row+="<input name=fd_type type=text value='varchar(32)'>	";
		row+="<input name=fd_title type=text>	";
		row+="
";
		$("#tb_adding").append(row);
	}
	function search(keyword){
		window.clearTimeout(inter);
		if(lastLoad.indexOf("selectMenu")!=-1){
			selectMenu(menu_id,keyword);
		}else{
			searchKeyword=keyword;
			inter=window.setTimeout("mainLoad(tableName,0,'"+keyword+"');",500);
		}
	}
	function reload(){
		$("#main").empty();
		$("#main").append("");
		window.setTimeout("mainLoad('"+tableName+"',0);",500);
		alert("reload");
	}
	function refresh(){
		eval(lastLoad);
	}
	function autoRefresh(){
		window.clearTimeout(inter);
		inter=window.setTimeout('refresh()',5000);
	}
 	function keyDownSearch(e) {
        // 兼容FF和IE和Opera
        var theEvent = e || window.event;
        var code = theEvent.keyCode || theEvent.which || theEvent.charCode;
        if (code == 13) {
            if(theEvent.target==$("#keyword")[0]){
            		search($("#keyword").val());
            		$("#keyword")[0].select();
            }
            if(theEvent.target.tagName=="INPUT"){
         	   $(theEvent.target).parent().parent().next("tr").find("input").focus();
            }
            return false;
        }
        return true;
    }
 	function selectMenu(id){
 		menu_id=id;
 		if(arguments.length==1){

 			filter=$("#keyword").val();
 	 		lastLoad="selectMenu("+id+",'"+filter+"')";
 	 		$("#main").load("report_select.jsp?id="+id+"&filter="+filter);

 		}else{
 			id=arguments[0];
 			filter=arguments[1];
 			lastLoad="selectMenu("+id+",'"+filter+"')";
 			$("#main").load("report_select.jsp?id="+id+"&filter="+filter);
 		}
	}
    document.onkeydown=keyDownSearch;
	var inter=null;
	//var stid=null;
	var tableName="userdata";
	var searchKeyword="";
	var thepage=0;
	var lastLoad="";
	var menu_id=0;<commit_msg>Update main.js<commit_after> function showBatchWork(keyword){
    	window.clearInterval(inter);
		$("#main").load("remote/batch.jsp",{"tb":"room","keyword":keyword});
    }
 function doBatchWork(frm){
	 window.clearInterval(inter);
		var data=$(frm).serialize();

		$("#main").load("remote/batch.jsp#"+Math.random(),data);
		return false;
	}

 function viewroom(roomid){
 	window.clearInterval(inter);
		$("#main").load("view.jsp",{"tb":"room","id":roomid},reformatview);
 }
	function dbid(evt){
		var data=$(evt.target).parent().parent().attr("id");
		return parseInt(data);
	}
	function view(rowid){
		window.clearTimeout(inter);
		var tbname=$("#tbname").val();
		$("#main").load("view.jsp",{"tb":tbname,"id":rowid},reformatview);

		//window.open("view.jsp?tb="+tbname+"&id="+rowid);
	}
	function edit(rowid){
		window.clearTimeout(inter);
		var tbname=$("#tbname").val();
		$("#main").load("add.jsp",{"tb":tbname,"id":rowid},reformatform);
	}

	function report_row_del(tbname,rowid){
		var ans=confirm('确定删除编号'+rowid);
		if(ans){
			$.post("del.jsp",{"tbname":tbname,"id":rowid},new function(){
				 window.setTimeout("$('#main').load('report.jsp?page=0')",1000);
			});
		}
	}
	function del(rowid){
		var ans=confirm('确定删除编号'+rowid);
		var tbname=$("#tbname").val();
		if(ans){
			$.post("del.jsp",{"tbname":tbname,"id":rowid},new function(){
				 window.setTimeout("mainLoad('"+tbname+"')",1000);
			});
		}
	}

	function addButton(main){
		//$("#main tr :last-child").css("background","#eeeeee");
		var buttons=""+
		""+
		""
								+"";

		buttons+="	";

		$("#main tr").append(buttons);
		$(".glyphicon-eye-open").bind("click",function(evt){
			view(dbid(evt));
		});
		$(".glyphicon-trash").bind("click",function(evt){
			del(dbid(evt));
		});
		$(".glyphicon-edit").bind("click",function(evt){
			edit(dbid(evt));
		});
		$(".glyphicon-refresh").bind("click",function(evt){
			goodsPriceSync(dbid(evt));
		});

	}
	function filteNext(preSelect){
		console.log("pre:",preSelect.name,preSelect.value);
		var tr=$(preSelect).parent().parent();
		var nextSelect=tr.next().children().last().children().last();
		if(nextSelect!=null&&nextSelect.length>0&&nextSelect[0].tagName=="SELECT"){
			var keyword=preSelect.name;
			var keyvalue=preSelect.value;
			console.log("reload:",keyword,keyvalue);
		    loadSelect(nextSelect,"keyword="+keyword+"&keyvalue="+keyvalue);
		}
	}
	function loadSelect(input,append){
		var input_name=input.attr("name");
		var default_value=input.val();
		if(input[0].nodeName=="SPAN")  //view use span for tag, text() for value
			 	default_value=input.text();
		var tbname=input_name.substring(0,input_name.length-3);
		if(input.attr("fr")!=undefined) tbname=input.attr("fr");;
		var td=input.parent();
		td.load("select.jsp?tbname="+tbname+"&input_name="+input_name+"&value="+default_value+"&"+append);

	}
	function loadFile(input){
		var input_name=input.attr("name");
		var default_value=input.val();
		if(input[0].nodeName=="SPAN")  //view use span for tag, text() for value
			 	default_value=input.text();
		var td=input.parent();
		input[0].id=input_name;
		input[0].type="hidden";

		var html=$(td).html()+"";

		$(td).html(html);

	}
	function reformatform(){
		$("input[name$=_file]").each(function(){
			loadFile($(this));
		});
		$("input[name$=_id]").each(function(){
				loadSelect($(this));
		});
		$("input[postLoad=1]").each(function(){
			loadSelect($(this));
		});
		$(".input_date").datepicker();
		$(".input_datetime").datetimepicker(
				{   showSecond: false,
		            timeFormat: 'hh:mm:ss',
		            stepHour: 1,
		            stepMinute: 1,
		            stepSecond: 1
		        });
		if(tableName.endsWith('config')){
			$("textarea").attr("rows",20).attr("cols",80);
		}else{
				$("textarea").ckeditor();
		}
		$("span[class='modifiable']").each(function(){
			var tbname=$(this).attr("tb");
			var fdname=$(this).attr("fd");
			var rid=$(this).attr("rid");

			if(tbname==tableName){
				$(this)[0].title="双击修改";
				if($(this).html()=="")$(this).html("_");
				$(this).dblclick(function(){
					var newValue=prompt("手工修改",$(this).text());
					if(newValue!=null&&newValue!=$(this).text()){
						//alert("update "+tbname+" set "+fdname+"="+newValue+" where id="+rid);

						$.post("callStatic.jsp",{"c":"com.newsclan.crud.Tools","m":"update",
							"tbname":tbname,
							"fdname":fdname,
							"value":newValue,
							"id":rid

								},new function(){
							 window.setTimeout("refresh();",500);
						});

					}
				});
			}else{
				$(this)[0].title="双击选择";
				$(this).dblclick(function(){
					var span=$(this);
					var oldValue=$(this).html();
					var fdname=tbname+"_id";
					//if(fdname.startsWith("t_")) fdname=fdname.substring(2);
					var td=$(this).parent();
					if(fdname=="pcdata_id"){ //适配某易语言系统中文字段名
						fdname="批次uid";
					}
					var selectURL="select.jsp?tbname="+tbname+"&input_name="+fdname+"&value=-1";
					$(this).load("select.jsp",{"tbname":tbname,"input_name":fdname,"value":-1},
						function(text,status,http){
							if(status=="success"){
								var sel=$("select[name="+fdname+"]");
								if(sel.length==0){
									sel=td.find("select");
								}
								var opt=$("option");
								var nowValue=opt.map(function(){if($(this).text()==oldValue) return($(this).val())});
								sel.val(nowValue);
								sel.removeAttr("onchange");
								sel.change(function(){
									var newValue=$(this).val();
									//alert(fdname);
									$.post("callStatic.jsp",{"c":"com.newsclan.crud.Tools","m":"update",
										"tbname":tableName,
										"fdname":fdname,
										"value":newValue,
										"id":rid

											},new function(){
										 window.setTimeout("refresh();",500);
									});
								});
								sel.blur(function(){
									span.html(oldValue);
								});
								sel.focus();
							}
						}

					);
					//	alert("update "+tableName+" set "+tbname+"_id="+oldValue+" where id="+rid);
				});

			}
		});
	}
	function reformatview(){
		$("span[name$=_id]").each(function(){
			loadSelect($(this));
		});
	}

	function loadImport(){
		window.clearInterval(inter);
		$("#main").load("import.jsp?"+Math.random(),{},reformatform);
	}

	function loadReport(){
		window.clearInterval(inter);
		$("#main").load("report_select.jsp?"+Math.random(),{},reformatform);
	}

	function passChange(){
		window.clearInterval(inter);
		$("#main").load("passChange.jsp?"+Math.random());
	}
	function checkIfPasswordIsSame(frm){
		if($("#password").val()!=$("#password2").val()){
			alert("两次密码不一致");
			return false;
		}
		return true;
	}
	function mainLoad(tbname,pageNum,keyword){
		tableName=tbname;
		if (typeof(pageNum)=='undefined') pageNum=thepage;
		if (typeof(keyword)=='undefined'||keyword=="") keyword=searchKeyword;
		thepage=pageNum;
		searchKeyword=keyword;
		window.clearInterval(inter);
		$("#main").load("list.jsp",{"tb":tbname,"pageNum":pageNum,"keyword":keyword},function(text,status,http){

			if(status=="success"){
				addButton($("#main"));
				reformatform();
				$("#tbname").after("");
				$("#addrow").click(function(){
					$("#main").load("add.jsp",{tb:tbname},reformatform);
				});
				if($("#auto")[0].checked){
					autoRefresh();
				}
			}
		});
		lastLoad="mainLoad('"+tbname+"',"+pageNum+",'"+keyword+"');";

	}
	function reportPage(page){
		$('#main').load('report.jsp?page='+page);
		lastLoad="reportPage("+page+");";
	}
	function pageUp(tbname,pageNum){
		mainLoad(tbname,pageNum-1,searchKeyword);
	}
	function pageDown(tbname,pageNum){
		mainLoad(tbname,pageNum+1,searchKeyword);
	}
	function showReport(frm){
		var data=$(frm).serialize();
		$("#main").load("report_select.jsp?"+Math.random(),data,reformatform);
		lastLoad="reportPage(0);";
		return false;
	}

	function submitAdd(tbname){
		var data=$("#addForm").serialize();
		$.post("add.jsp",data,new function(){
			 mainLoad(tbname,0,searchKeyword);
		});
	}
	function submitTable(){
		var data=$("#frmAddTable").serialize();
		var tbname=$("#tb_name").val();

		$.post("addTable.jsp",data,new function(){
			 window.setTimeout("mainLoad('"+tbname+"',0,'"+searchKeyword+"')",1000);
		});
	}
	function addTable(){
		$("#main").empty();
		$("#main").append("<form method=post onSubmit='submitTable()' id='frmAddTable'>

</form>");
		$("#tb_adding").append("
表名<input type=text id=tb_name name=tb_name value='tb_'>	中文<input type=text name=tb_title value='信息'>
");
		$("#tb_adding").append("
列名	类型	中文
");
		$("#frmAddTable").append("<input type=button onclick='submitTable()' value='确定'><input type=reset value='重置'>");
		addColumn();

	}
	function addColumn(){
		var row="
";
		row+="<input name=fd_name type=text>	";
		row+="<input name=fd_type type=text value='varchar(32)'>	";
		row+="<input name=fd_title type=text>	";
		row+="
";
		$("#tb_adding").append(row);
	}
	function search(keyword){
		window.clearTimeout(inter);
		if(lastLoad.indexOf("selectMenu")!=-1){
			selectMenu(menu_id,keyword);
		}else{
			searchKeyword=keyword;
			inter=window.setTimeout("mainLoad(tableName,0,'"+keyword+"');",500);
		}
	}
	function reload(){
		$("#main").empty();
		$("#main").append("");
		window.setTimeout("mainLoad('"+tableName+"',0);",500);
		alert("reload");
	}
	function refresh(){
		eval(lastLoad);
	}
	function autoRefresh(){
		window.clearTimeout(inter);
		inter=window.setTimeout('refresh()',5000);
	}
 	function keyDownSearch(e) {
        // 兼容FF和IE和Opera
        var theEvent = e || window.event;
        var code = theEvent.keyCode || theEvent.which || theEvent.charCode;
        if (code == 13) {
            if(theEvent.target==$("#keyword")[0]){
            		search($("#keyword").val());
            		$("#keyword")[0].select();
            }
            if(theEvent.target.tagName=="INPUT"){
         	   $(theEvent.target).parent().parent().next("tr").find("input").focus();
            }
            return false;
        }
        return true;
    }
 	function selectMenu(id){
 		menu_id=id;
 		if(arguments.length==1){

 			filter=$("#keyword").val();
 	 		lastLoad="selectMenu("+id+",'"+filter+"')";
 	 		$("#main").load("report_select.jsp?id="+id+"&filter="+filter);

 		}else{
 			id=arguments[0];
 			filter=arguments[1];
 			lastLoad="selectMenu("+id+",'"+filter+"')";
 			$("#main").load("report_select.jsp?id="+id+"&filter="+filter);
 		}
	}
    document.onkeydown=keyDownSearch;
	var inter=null;
	//var stid=null;
	var tableName="userdata";
	var searchKeyword="";
	var thepage=0;
	var lastLoad="";
	var menu_id=0;
<|endoftext|>


<commit_before>'use strict';

// Rename this file to local.js for having a local configuration variables that
// will not get commited and pushed to remote repositories.
// Use it for your API keys, passwords, etc.

/* For example:

module.exports = {
  db: {
    uri: 'mongodb://localhost/local-dev',
    options: {
      user: '',
      pass: ''
    }
  },
  facebook: {
    clientID: process.env.FACEBOOK_ID || 'APP_ID',
    clientSecret: process.env.FACEBOOK_SECRET || 'APP_SECRET',
    callbackURL: '/auth/facebook/callback'
  }
};
*/
<commit_msg>now has instructions on how to make local.js file<commit_after>'use strict';

// Rename this file to local.js for having a local configuration variables that
// will not get commited and pushed to remote repositories.
// Add your Amazon MWS credentials to the fields below

/* For example:

module.exports = {
  accessKeyId: '*****',
  secretAccessKey: '*****',
  merchantId: '*****'
};

*/
<|endoftext|>


<commit_before>/*
 * grunt-builder
 * https://github.com/amkirwan/grunt-builder
 *
 * Copyright (c) 2014 Anthony Kirwan
 * Licensed under the MIT license.
 */

'use strict';

module.exports = function(grunt) {

  var shell = require('shelljs');
  var semver = require('semver');

  grunt.registerMultiTask('builder', 'Grunt plugin for versioning, building and tagging your Git project.', function(versionType) {

    // Merge task-specific and/or target-specific options with these defaults.
    var options = this.options({
      bump: true,
      file: 'package.json',
      gitAdd: true,
      gitCommit: true,
      gitPush: true,
      gitTag: true,
      gitPushTag: true,
      gitDescribeOptions: '--tags --always --dirty=-d',
      tagPrefix: 'v',
      commitMessagePrefix: 'Release: ',
      tagMessagePrefix: 'Version: ',
      readmeText: 'Current Version:',
      pushTo: 'origin',
      branch: 'master'
    });

    var newVersion;
    var versionFileRegExp = /^([\d||A-a|.|-]+)$/im;
    var versionJSFileRegExp = /([\'|\"]?version[\'|\"]?\s*[:|=]\s*[\'|\"]?)([\d||A-a|.|-]*)([\'|\"]?)/i;
    var readmeRegExp = new RegExp("(^" + options.readmeText + ".*\\[)([\\d|.|\\-|a-z]+)(\\].*\\/)([\\d|.|\\-|a-z]+)(\\).*)", "img");

    // init function setup the newVersion
    (function() {
      if (!grunt.file.exists(options.file)) {
        grunt.log.error('Version source file "' + options.file + '" not found.');
      }
      // set versionType
      options.versionType = (options.versionType || versionType || 'patch');

      // get the current version
      var version = (grunt.file.isMatch({matchBase: true}, '*.json', options.file)) ? grunt.file.readJSON(options.file).version : grunt.file.read(options.file);
      if (options.bump) {
        setNewVersion(version); // set the newVersion
      }
    })();

    var templateData = {
      data: {
        newVersion: newVersion
      }
    };

    // replace 'newVersion in the template', and update the options message
    options.tagName = grunt.template.process(options.tagName || options.tagPrefix + "<%= newVersion %>", templateData);
    options.commitMessage = grunt.template.process(options.commitMessage || options.commitMessagePrefix + "<%= newVersion %>", templateData);
    options.tagMessage = (options.tagMessage || options.tagMessagePrefix + options.tagName);

    function exec(opts) {
      shell.exec(opts.cmd);
      if (shell.error()) {
        grunt.log.ok(opts.msg);
      } else {
        grunt.log.error(opts.errMsg);
      }
    }

    // git functions
    function gitAdd(file) {
      exec({cmd: 'git add ' + file.src,
            msg: file.src + ' added to git index',
            errMsg: 'Cannot add file ' + file.src + ' to the git index'});
    }

    function gitCommit() {
      exec({cmd: 'git commit -m ' + options.commitMessage,
            msg: "Committed as " + options.commitMessage,
            errMsg: 'Cannot commit changes'});
    }

    function gitTag() {
      exec({cmd: 'git tag -a ' + options.tagName + ' -m ' + options.tagMessage,
           msg: 'Tagged as ' + options.tagName,
           errMsg: 'Cannot create tag ' + options.tagname});
    }

    function gitPushTag() {
      exec({cmd: 'git push ' + options.pushTo + ' --tags',
            msg: 'Tag pushed to ' + options.pushTo,
            errMsg: 'Cannot push tag ' + options.tagName + ' to ' + options.pushTo });
    }

    function gitPush() {
      exec({cmd: 'git push ' + options.pushTo + ' ' + options.branch,
            msg: 'Pushed changes to remote ' + options.pushTo + ' and branch ' + options.branch,
            errMsg: 'Cannot push to remote ' + options.pushTo + ' and branch ' + options.branch});
    }

    function setNewVersion(parsedVersion) {
      if (options.setVersion !== undefined && newVersion === undefined) {
        newVersion = options.setVersion;
      } else if (options.versionType === 'git' && newVersion === undefined) {
        shell.exec('git describe ' + options.gitDescribeOptions);
      } else if (newVersion === undefined) {
        newVersion = semver.inc(parsedVersion, options.versionType || 'patch');
      }
      grunt.log.ok('Version set to: ' + newVersion);
      return newVersion;
    }

    function updateContent(content) {
      var newContent;
      if (content.match(readmeRegExp)) {
        newContent = content.replace(readmeRegExp, function(match, leadText, parsedVersion, urlUpToTag, versionUrl, endText,  offset, string) {
          return  leadText + newVersion + urlUpToTag + options.tagName + endText;
        });
      } else if (content.match(versionJSFileRegExp)) {
        newContent = content.replace(versionJSFileRegExp, function(match, prefix, parsedVersion, suffix) {
          return prefix + newVersion + suffix;
        });
      } else if (content.match(versionFileRegExp)) {
        newContent = content.replace(versionFileRegExp, function(match, parsedVersion) {
          return newVersion;
        });
      }
      return newContent;
    }

    // Iterate over all specified file groups.
    this.files.forEach(function(f) {
      // Concat specified files.
      var content = f.src.filter(function(filepath) {
        // Warn on and remove invalid source files (if nonull was set).
        if (!grunt.file.exists(filepath)) {
          grunt.log.error('Source file "' + filepath + '" not found.');
          return false;
        } else {
          return true;
        }
      }).map(function(filepath) {
        // Read file source.
        return grunt.file.read(filepath);
      }).toString();

      var updatedContent = updateContent(content);

      // Write the destination file if set otherwise write back to the src file.
      if (f.dest) {
        grunt.file.write(f.dest, updatedContent);
      } else {
        grunt.file.write(f.src, updatedContent);
      }

      // Print a success message.
      grunt.log.writeln('File "' + f.dest + '" updated.');

      if (options.gitAdd) {
        gitAdd(f);
      }
    });

    // git commit and push
    if (options.gitCommit) { gitCommit(); }
    if (options.gitTag) { gitTag(); }
    if (options.gitPush) { gitPush(); }
    if (options.gitPushTag) { gitPushTag(); }

  });
};
<commit_msg>fixed error message output<commit_after>/*
 * grunt-builder
 * https://github.com/amkirwan/grunt-builder
 *
 * Copyright (c) 2014 Anthony Kirwan
 * Licensed under the MIT license.
 */

'use strict';

module.exports = function(grunt) {

  var shell = require('shelljs');
  var semver = require('semver');

  grunt.registerMultiTask('builder', 'Grunt plugin for versioning, building and tagging your Git project.', function(versionType) {

    // Merge task-specific and/or target-specific options with these defaults.
    var options = this.options({
      bump: true,
      file: 'package.json',
      gitAdd: true,
      gitCommit: true,
      gitPush: true,
      gitTag: true,
      gitPushTag: true,
      gitDescribeOptions: '--tags --always --dirty=-d',
      tagPrefix: 'v',
      commitMessagePrefix: 'Release: ',
      tagMessagePrefix: 'Version: ',
      readmeText: 'Current Version:',
      pushTo: 'origin',
      branch: 'master'
    });

    var newVersion;
    var versionFileRegExp = /^([\d||A-a|.|-]+)$/im;
    var versionJSFileRegExp = /([\'|\"]?version[\'|\"]?\s*[:|=]\s*[\'|\"]?)([\d||A-a|.|-]*)([\'|\"]?)/i;
    var readmeRegExp = new RegExp("(^" + options.readmeText + ".*\\[)([\\d|.|\\-|a-z]+)(\\].*\\/)([\\d|.|\\-|a-z]+)(\\).*)", "img");

    // init function setup the newVersion
    (function() {
      if (!grunt.file.exists(options.file)) {
        grunt.log.error('Version source file "' + options.file + '" not found.');
      }
      // set versionType
      options.versionType = (options.versionType || versionType || 'patch');

      // get the current version
      var version = (grunt.file.isMatch({matchBase: true}, '*.json', options.file)) ? grunt.file.readJSON(options.file).version : grunt.file.read(options.file);
      if (options.bump) {
        setNewVersion(version); // set the newVersion
      }
    })();

    var templateData = {
      data: {
        newVersion: newVersion
      }
    };

    // replace 'newVersion in the template', and update the options message
    options.tagName = grunt.template.process(options.tagName || options.tagPrefix + "<%= newVersion %>", templateData);
    options.commitMessage = grunt.template.process(options.commitMessage || options.commitMessagePrefix + "<%= newVersion %>", templateData);
    options.tagMessage = (options.tagMessage || options.tagMessagePrefix + options.tagName);

    function exec(opts) {
      shell.exec(opts.cmd);
      if (shell.error()) {
        grunt.log.error(opts.errMsg);
      } else {
        grunt.log.ok(opts.msg);
      }
    }

    // git functions
    function gitAdd(file) {
      exec({cmd: 'git add ' + file.src,
            msg: file.src + ' added to git index',
            errMsg: 'Cannot add file ' + file.src + ' to the git index'});
    }

    function gitCommit() {
      exec({cmd: 'git commit -m ' + options.commitMessage,
            msg: "Committed as " + options.commitMessage,
            errMsg: 'Cannot commit changes'});
    }

    function gitTag() {
      exec({cmd: 'git tag -a ' + options.tagName + ' -m ' + options.tagMessage,
           msg: 'Tagged as ' + options.tagName,
           errMsg: 'Cannot create tag ' + options.tagname});
    }

    function gitPushTag() {
      exec({cmd: 'git push ' + options.pushTo + ' --tags',
            msg: 'Tag pushed to ' + options.pushTo,
            errMsg: 'Cannot push tag ' + options.tagName + ' to ' + options.pushTo });
    }

    function gitPush() {
      exec({cmd: 'git push ' + options.pushTo + ' ' + options.branch,
            msg: 'Pushed changes to remote ' + options.pushTo + ' and branch ' + options.branch,
            errMsg: 'Cannot push to remote ' + options.pushTo + ' and branch ' + options.branch});
    }

    function setNewVersion(parsedVersion) {
      if (options.setVersion !== undefined && newVersion === undefined) {
        newVersion = options.setVersion;
      } else if (options.versionType === 'git' && newVersion === undefined) {
        shell.exec('git describe ' + options.gitDescribeOptions);
      } else if (newVersion === undefined) {
        newVersion = semver.inc(parsedVersion, options.versionType || 'patch');
      }
      grunt.log.ok('Version set to: ' + newVersion);
      return newVersion;
    }

    function updateContent(content) {
      var newContent;
      if (content.match(readmeRegExp)) {
        newContent = content.replace(readmeRegExp, function(match, leadText, parsedVersion, urlUpToTag, versionUrl, endText,  offset, string) {
          return  leadText + newVersion + urlUpToTag + options.tagName + endText;
        });
      } else if (content.match(versionJSFileRegExp)) {
        newContent = content.replace(versionJSFileRegExp, function(match, prefix, parsedVersion, suffix) {
          return prefix + newVersion + suffix;
        });
      } else if (content.match(versionFileRegExp)) {
        newContent = content.replace(versionFileRegExp, function(match, parsedVersion) {
          return newVersion;
        });
      }
      return newContent;
    }

    // Iterate over all specified file groups.
    this.files.forEach(function(f) {
      // Concat specified files.
      var content = f.src.filter(function(filepath) {
        // Warn on and remove invalid source files (if nonull was set).
        if (!grunt.file.exists(filepath)) {
          grunt.log.error('Source file "' + filepath + '" not found.');
          return false;
        } else {
          return true;
        }
      }).map(function(filepath) {
        // Read file source.
        return grunt.file.read(filepath);
      }).toString();

      var updatedContent = updateContent(content);

      // Write the destination file if set otherwise write back to the src file.
      if (f.dest) {
        grunt.file.write(f.dest, updatedContent);
      } else {
        grunt.file.write(f.src, updatedContent);
      }

      // Print a success message.
      grunt.log.writeln('File "' + f.dest + '" updated.');

      if (options.gitAdd) {
        gitAdd(f);
      }
    });

    // git commit and push
    if (options.gitCommit) { gitCommit(); }
    if (options.gitTag) { gitTag(); }
    if (options.gitPush) { gitPush(); }
    if (options.gitPushTag) { gitPushTag(); }

  });
};
<|endoftext|>


<commit_before>define([
  'common/collections/completion_numbers'
],
function (CompletionNumbersCollection) {
  describe('Completion numbers collection', function () {

    var collection;

    beforeEach(function () {
      collection = new CompletionNumbersCollection([], {
        denominatorMatcher: 'foo',
        numeratorMatcher: 'bar'
      });
    });

    describe('defaultValueAttrs', function () {

      it('should add unique events to values', function () {
        var input = {
          _end: 5
        };
        var expected = {
          uniqueEvents: 5
        };
        expect(collection.defaultValueAttrs(input)).toEqual(expected);
      });

      it('should add null if no end value', function () {
        var input = {
          _end: null
        };
        var expected = {
          uniqueEvents: null
        };
        expect(collection.defaultValueAttrs(input)).toEqual(expected);
      });

      it('should add a default value if end is null and one is provided', function () {
        collection = new CompletionNumbersCollection([], {
          defaultValue: 0,
          denominatorMatcher: 'foo',
          numeratorMatcher: 'bar'
        });
        var input = {
          _end: null
        };
        var expected = {
          uniqueEvents: 0
        };
        expect(collection.defaultValueAttrs(input)).toEqual(expected);
      });

      it('should return default collection attributes', function () {
        var input = {
          _start: 10,
          _end: 5,
          values: [ {get: function () { return 1; }}, {get: function () { return null; }} ]
        };
        var expected = {
          id: 'done',
          title: 'Done',
          mean: 5,
          periods: { total: 2, available: 1 }
        };
        expect(collection.defaultCollectionAttrs(input)).toEqual(expected);
      });

      it('should return null mean in default collection attributes no events', function () {
        var input = {
          _start: 10,
          _end: 5,
          values: [ {get: function () { return null; }}, {get: function () { return null; }} ]
        };
        var expected = {
          id: 'done',
          title: 'Done',
          mean: null,
          periods: { total: 2, available: 0 }
        };
        expect(collection.defaultCollectionAttrs(input)).toEqual(expected);
      });

    });

  });
});
<commit_msg>Fix completion numbers collection test following matrix removal<commit_after>define([
  'common/collections/completion_numbers'
],
function (CompletionNumbersCollection) {
  describe('Completion numbers collection', function () {

    var collection;

    beforeEach(function () {
      collection = new CompletionNumbersCollection([], {
        denominatorMatcher: 'foo',
        numeratorMatcher: 'bar',
        valueAttr: '_end'
      });
    });

    describe('parse', function () {

      it('maps valueAttr to "uniqueEvents"', function () {
        var input = [
          { _end: 5 }
        ];
        var output = collection.parse({ data: input });
        expect(output[0]).toEqual({ _end: 5, uniqueEvents: 5 });
      });

      it('should add null if no end value', function () {
        var input = [
          { _end: null }
        ];
        var output = collection.parse({ data: input });
        expect(output[0]).toEqual({ _end: null, uniqueEvents: null });
      });

      it('should add a default value if end is null and one is provided', function () {
        collection.options.defaultValue = 0;
        var input = [
          { _end: null }
        ];
        var output = collection.parse({ data: input });
        expect(output[0]).toEqual({ _end: null, uniqueEvents: 0 });
      });

    });

  });
});
<|endoftext|>


<commit_before>/**
## Number Display Widget

Includes: [Base Mixin](#base-mixin)

A display of a single numeric value.

Examples:

* [Test Example](http://dc-js.github.io/dc.js/examples/number.html)

#### dc.numberDisplay(parent[, chartGroup])
Create a Number Display instance and attach it to the given parent element.

Unlike other charts, you do not need to set a dimension. Instead a valid group object must be provided and valueAccessor that is expected to return a single value.

Parameters:

* parent : string - any valid d3 single selector representing typically a dom block element such as a div or span
* chartGroup : string (optional) - name of the chart group this chart instance should be placed in. Once a chart is placed
   in a certain chart group then any interaction with such instance will only trigger events and redraw within the same
   chart group.

Return:
A newly created number display instance

```js
// create a number display under #chart-container1 element using the default global chart group
var display1 = dc.numberDisplay("#chart-container1");
```

**/
dc.numberDisplay = function (parent, chartGroup) {
    var SPAN_CLASS = 'number-display';
    var _formatNumber = d3.format(".2s");
    var _chart = dc.baseMixin({});
    var _text = "";

    // dimension not required
    _chart._mandatoryAttributes(['group']);

    /**
    #### .text(string)
    Get or set the string attached to the number.
    **/
    _chart.text = function (t) {
        if (!arguments.length) return _text;
        _text = " "+t;
        return _chart;
    };

    /**
    #### .value()
    Calculate and return the underlying value of the display
    **/
    _chart.value = function () {
        return _chart.data();
    };

    _chart.data(function (group) {
        var valObj = group.value ? group.value() : group.top(1)[0];
        return _chart.valueAccessor()(valObj);
    });

    _chart.transitionDuration(250); // good default

    _chart._doRender = function () {
        var newValue = _chart.value(),
            span     = _chart.selectAll("."+SPAN_CLASS);

        if(span.empty())
            span = span.data([0])
                .enter()
                .append("span")
                .attr("class", SPAN_CLASS);

        span.transition()
            .duration(_chart.transitionDuration())
            .ease('quad-out-in')
            .tween("text", function () {
                var interp = d3.interpolateNumber(this.lastValue || 0, newValue);
                this.lastValue = newValue;
                return function (t) {
                    this.textContent = _chart.formatNumber()(interp(t))+_text;
                };
            });
    };

    _chart._doRedraw = function(){
        return _chart._doRender();
    };

    /**
    #### .formatNumber([formatter])
    Get or set a function to format the value for the display. By default `d3.format(".2s");` is used.

    **/
    _chart.formatNumber = function (_) {
        if (!arguments.length) return _formatNumber;
        _formatNumber = _;
        return _chart;
    };

    return _chart.anchor(parent, chartGroup);
};

<commit_msg>Number-display - Hide entries if the value is 0<commit_after>/**
## Number Display Widget

Includes: [Base Mixin](#base-mixin)

A display of a single numeric value.

Examples:

* [Test Example](http://dc-js.github.io/dc.js/examples/number.html)

#### dc.numberDisplay(parent[, chartGroup])
Create a Number Display instance and attach it to the given parent element.

Unlike other charts, you do not need to set a dimension. Instead a valid group object must be provided and valueAccessor that is expected to return a single value.

Parameters:

* parent : string - any valid d3 single selector representing typically a dom block element such as a div or span
* chartGroup : string (optional) - name of the chart group this chart instance should be placed in. Once a chart is placed
   in a certain chart group then any interaction with such instance will only trigger events and redraw within the same
   chart group.

Return:
A newly created number display instance

```js
// create a number display under #chart-container1 element using the default global chart group
var display1 = dc.numberDisplay("#chart-container1");
```

**/
dc.numberDisplay = function (parent, chartGroup) {
    var SPAN_CLASS = 'number-display';
    var _formatNumber = d3.format(".2s");
    var _chart = dc.baseMixin({});
    var _text = "";
    var _hideZero = false;

    // dimension not required
    _chart._mandatoryAttributes(['group']);

    /**
    #### .text(string)
    Get or set the string attached to the number.
    **/
    _chart.text = function (t) {
        if (!arguments.length) return _text;
        _text = " "+t;
        return _chart;
    };

    /**
    #### .hideZero(boolean)
    Hide the number if it equals to zero.
    **/
    _chart.hideZero = function (b) {
        if (!arguments.length) return _hideZero;
        _hideZero = b;
        return _chart;
    };

    /**
    #### .value()
    Calculate and return the underlying value of the display
    **/
    _chart.value = function () {
        return _chart.data();
    };

    _chart.data(function (group) {
        var valObj = group.value ? group.value() : group.top(1)[0];
        return _chart.valueAccessor()(valObj);
    });

    _chart.transitionDuration(250); // good default

    _chart._doRender = function () {
        var newValue = _chart.value(),
            span     = _chart.selectAll("."+SPAN_CLASS);

        if(span.empty())
            span = span.data([0])
                .enter()
                .append("span")
                .attr("class", SPAN_CLASS);

        span.transition()
            .duration(_chart.transitionDuration())
            .ease('quad-out-in')
            .tween("text", function () {
                var interp = d3.interpolateNumber(this.lastValue || 0, newValue);
                this.lastValue = newValue;
                return function (t) {
                    if((_hideZero)&&(newValue==0))
                        this.textContent = "";
                    else
                        this.textContent = _chart.formatNumber()(interp(t))+_text;
                };
            });
    };

    _chart._doRedraw = function(){
        return _chart._doRender();
    };

    /**
    #### .formatNumber([formatter])
    Get or set a function to format the value for the display. By default `d3.format(".2s");` is used.

    **/
    _chart.formatNumber = function (_) {
        if (!arguments.length) return _formatNumber;
        _formatNumber = _;
        return _chart;
    };

    return _chart.anchor(parent, chartGroup);
};

<|endoftext|>