<commit_before>'use strict';
var _ = require('lodash'),
  dom = require('../services/dom');

/**
 * Find the first child that is a text input.
 * @param {object} el
 * @returns {object|undefined}
 */
function findFirstTextInput(el) {

  var inputs = el.querySelectorAll('input'),
    l = inputs.length,
    i = 0,
    type,
    textInput;

  while (i < l) {
    type = inputs[i].getAttribute('type');
    if (!type || type === 'text') {
      textInput = inputs[i];
      break;
    }
    i += 1;
  }

  return textInput;
}

/**
 * Temporary, until the endpoint is hooked up.
 * @param q
 * @returns {*}
 */
function mockEndpoint(q) {
  if (q.toLowerCase().indexOf('m') === 0) {
    // Names with M
    return JSON.stringify({results:['Morgan', 'Mindy', 'Miguel']});
  } else {
    // Random names
    return JSON.stringify({results:['Amy Koran', 'Zena Strother', 'Karole Herdt', 'Sheila Cowell', 'Josiah Hagaman', 'Beatris Doetsch', 'Zachary Brunell', 'Manuel Grassi', 'Amie Ridgell', 'Lupe Harrill', 'Adriana Bakke', 'Francoise Lashley', 'Gwenn Sampley', 'Randall Coller', 'Terence Villegas', 'Matthew Mcconnaughey', 'Rosella Conroy', 'Gemma Osburn', 'Shanel Holt']});
  }
}

module.exports = function (result, args) {

  var existingInput = findFirstTextInput(result.el);

  if (existingInput) {

    // Add element.
    var datalist = document.createElement('datalist');

    // Set attributes.
    var listName = args.listName || 'list-needs-an-id';
    existingInput.setAttribute('list', listName);
    datalist.id = listName;

    // Add options.
    var options = dom.create(`
      <label>
        or select from the list:
        <select name="${ existingInput.getAttribute('name') }">
          <option value="">
          <option>Bip
          <option>Bop
          <option>Boop
        </select>
      </label>
    `);
    datalist.appendChild(options);

    var optionsParent = options.querySelector('select');

    existingInput.addEventListener('input', (function(optionsParent) {
      return function(e) {

        var options = JSON.parse(mockEndpoint(e.target.value)).results.reduce(function(prev, curr) {
          return prev + '<option>' + curr;
        }, '<option value="">');

        options = dom.create(`${ options }`);

        dom.clearChildren(optionsParent);
        optionsParent.appendChild(options);
      };
    })(optionsParent));

    // Add it back to the result element.
    result.el.appendChild(datalist);
  }

  return result;

};<commit_msg>Basic autocomplete working with API.<commit_after>'use strict';
var _ = require('lodash'),
  dom = require('../services/dom'),
  db = require('../services/db'),
  lists = {};

/**
 * Find the first child that is a text input.
 * @param {object} el
 * @returns {object|undefined}
 */
function findFirstTextInput(el) {

  var inputs, l, i, type, textInput;

  inputs = el.querySelectorAll('input');

  for (i = 0, l = inputs.length; i < l; i++) {
    type = inputs[i].getAttribute('type');
    if (!type || type === 'text') {
      textInput = inputs[i];
      break;
    }
  }

  return textInput;
}

/**
 * Get the list values from the API and store in memory.
 * @param {string} apiUrl   The endpoint for the list, e.g. '/lists/authors'
 * @returns {promise}
 */
function getListValues(apiUrl) {
  if (lists[apiUrl]) {
    return Promise.resolve(lists[apiUrl]);
  } else {
    return db.getComponentJSONFromReference(apiUrl);
  }
}

/**
 * Create a unique name for the list. Does not need to be too unique because specific to one form.
 * @returns {string}      Name to be used for the list
 */
function createListName() {
  var somewhatUnique = '' + Math.floor(Math.random() * 100) + (new Date()).getTime();
  return 'autocomplete-' + somewhatUnique;
}

module.exports = function (result, args) {

  var api = args.api,
    existingInput = findFirstTextInput(result.el),
    listName = createListName(),
    optionsParent;

  // Requirements.
  if (!api) {
    console.warn('Autocomplete requires an API.');
    return result;
  }
  if (!existingInput) {
    console.warn('Autocomplete requires a text input.');
    return result;
  }

  getListValues(api);

  // Add elements.
  var datalist = document.createElement('datalist');
  var options = dom.create(`
    <label>
      <select>
        <option value="">
      </select>
    </label>
  `);
  datalist.appendChild(options);
  optionsParent = options.querySelector('select');

  // Set attributes.
  existingInput.setAttribute('list', listName);
  datalist.id = listName;

  // Listen.
  existingInput.addEventListener('input', (function(optionsParent) {
    return function(e) {

      getListValues(api).then(function (results) {
        var options = results.reduce(function (prev, curr) {
            return prev + '<option>' + curr;
          }, '<option value="">');

        options = dom.create(`${ options }`);
        dom.clearChildren(optionsParent);
        optionsParent.appendChild(options);

        existingInput.focus();
      });

    };
  })(optionsParent));

  // Add it back to the result element.
  result.el.appendChild(datalist);

  return result;

};<|endoftext|>


<commit_before>// ==UserScript==
// @name        Better Status Bar
// @namespace   josh.lubaway.com
// @description Chrome's status bar blows. It doesn't show you the full uri.
// @include     *
// ==/UserScript==

var StatusBar = {

  initialize: function() {
    StatusBar.createStatusBar("I'm a status bar!");
    StatusBar.hide();

    var links = document.getElementsByTagName('a');
    for (var i=0; i<links.length; i++) {
      links[i].addEventListener("mouseover", StatusBar.show);
      links[i].addEventListener("mouseout", StatusBar.hide);
    }
  },

  createStatusBar: function(text) {
    var element  = document.createElement('div');
    var textNode = document.createElement('p');

    element.style.position = 'fixed';
    element.style.top = '0px';
    element.style.left = '0px';
    element.style.background = '#f6f6f6';
    element.style.margin = '0px';
    element.style['border-bottom'] = '1px solid #cdcdcd';
    element.style['border-right'] = '1px solid #cdcdcd';
    element.style['-webkit-border-bottom-right-radius'] = '5px';
    element.style.zIndex = '20000';

    textNode.innerHTML = text;
    textNode.style.color = '#161616';
    textNode.style.margin = '2px 4px';
    textNode.style.fontFamily = 'Arial';
    textNode.style.fontSize = '11px';
    textNode.style.textShadow = '1px 1px #fefefe';
    textNode.style['letter-spacing'] = '1px';

    element.appendChild(textNode);
    document.body.appendChild(element);

    StatusBar.textNode = textNode;
    StatusBar.bar      = element;
  },

  text: function(value) {
    if (value == undefined) {
      value = window.location.href;
    }

    StatusBar.textNode.innerHTML = value;
  },

  hide: function() {
    StatusBar.bar.style.display = 'none';
  },

  show: function(event) {
    StatusBar.text(event.target.href);
    StatusBar.bar.style.display = 'block';
  }

}

StatusBar.initialize();
<commit_msg>Adding padding and text-align rules.<commit_after>// ==UserScript==
// @name        Better Status Bar
// @namespace   josh.lubaway.com
// @description Chrome's status bar blows. It doesn't show you the full uri.
// @include     *
// ==/UserScript==

var StatusBar = {

  initialize: function() {
    StatusBar.createStatusBar("I'm a status bar!");
    StatusBar.hide();

    var links = document.getElementsByTagName('a');
    for (var i=0; i<links.length; i++) {
      links[i].addEventListener("mouseover", StatusBar.show);
      links[i].addEventListener("mouseout", StatusBar.hide);
    }
  },

  createStatusBar: function(text) {
    var element  = document.createElement('div');
    var textNode = document.createElement('p');

    element.style.position = 'fixed';
    element.style.top = '0px';
    element.style.left = '0px';
    element.style.background = '#f6f6f6';
    element.style.margin = '0px';
    element.style['border-bottom'] = '1px solid #cdcdcd';
    element.style['border-right'] = '1px solid #cdcdcd';
    element.style['-webkit-border-bottom-right-radius'] = '5px';
    element.style.zIndex = '20000';

    textNode.innerHTML = text;
    textNode.style.color = '#161616';
    textNode.style.margin = '2px 4px';
    textNode.style.fontFamily = 'Arial';
    textNode.style.fontSize = '11px';
    textNode.style.padding = '0px';
    textNode.style.textAlign = 'left';
    textNode.style.textShadow = '1px 1px #fefefe';
    textNode.style['letter-spacing'] = '1px';

    element.appendChild(textNode);
    document.body.appendChild(element);

    StatusBar.textNode = textNode;
    StatusBar.bar      = element;
  },

  text: function(value) {
    if (value == undefined) {
      value = window.location.href;
    }

    StatusBar.textNode.innerHTML = value;
  },

  hide: function() {
    StatusBar.bar.style.display = 'none';
  },

  show: function(event) {
    StatusBar.text(event.target.href);
    StatusBar.bar.style.display = 'block';
  }

}

StatusBar.initialize();
<|endoftext|>


<commit_before>var e = module.exports = {};

var t;

e.init = () => {
    e.category = bu.CommandType.GENERAL;
};
e.requireCtx = require;

e.isCommand = true;
e.hidden = false;
e.usage = 'feedback <feedback>';
e.alias = ['suggest', 'report'];
e.info = `This command has three different functions for varying purposes. Please do not abuse it.

**__feedback__** - give me feedback about the bot
**__suggest__** - tell me something you want to be added or changed
**__report__** - let me know about a bug you found

Thank you for your support. It means a lot to me!`;
e.longinfo = `
This command has three different functions for varying purposes. Please do not abuse it.

feedback - give me feedback about the bot

suggest - tell me something you want added or changed

report - let me know about a bug you found

Thank you for your support. It means a lot to me!
`;

e.execute = async function(msg, words) {
    if (words.length > 1) {
        let i = 0;
        let lastSuggestion = await r.table('suggestion').orderBy({
            index: r.desc('id')
        }).limit(1).run();
        if (lastSuggestion.length > 0) i = lastSuggestion[0].id + 1;
        logger.debug(i, lastSuggestion);
        if (isNaN(i)) i = 0;
        let type, colour, channel, list;
        switch (words[0].toLowerCase()) {
            case 'suggest':
                type = 'Suggestion';
                colour = 0x1faf0c;
                channel = '195716879237644292';
                list = '57ef25d2ba874bf651e96fc1';
                break;
            case 'report':
                type = 'Bug Report';
                colour = 0xaf0c0c;
                channel = '229137183234064384';
                list = '57ef25d5d777b8b35192eff2';
                break;
            default:
                type = 'Feedback';
                colour = 0xaaaf0c;
                channel = '268859677326966784';
                list = '5876a95f090e7f5189cd089b';
                break;
        }
        if (words[0].toLowerCase() == 'suggest') type = 'Suggestion';
        else if (words[0].toLowerCase() == 'report') type = 'Bug Report';

        function trelloPost() {
            return new Promise((f, r) => {
                bu.trello.post('1/cards', {
                    name: words.slice(1).join(' '),
                    desc: `Automated ${type} added by ${bot.user.username} - CASE ${i}.\n\nAuthor: ${msg.author.username}#${msg.author.discriminator} (${msg.author.id})`,
                    due: null,
                    idList: list,
                    idLabels: '58025f0184e677fd36dbd756' + (config.general.isbeta ? ',5888db5bced82109fff170af' : '')
                }, (err, data) => {
                    if (err) {
                        r(err);
                        return;
                    }
                    f(data);
                });
            });
        }
        try {
            let data = await trelloPost();

            await bu.send(channel, {
                embed: {
                    title: type,
                    description: words.slice(1).join(' ') + `\n\n[Trello Card](${data.shortUrl})`,
                    color: colour,
                    author: {
                        name: bu.getFullName(msg.author),
                        icon_url: msg.author.avatarURL,
                        url: `https://blargbot.xyz/user/${msg.author.id}`
                    },
                    timestamp: dep.moment(msg.timestamp),
                    footer: {
                        text: 'Case ' + i + ' | ' + msg.id
                    },
                    fields: [{
                        name: msg.guild ? msg.guild.name : 'DM',
                        value: msg.guild ? msg.guild.id : 'DM',
                        inline: true
                    }, {
                        name: msg.channel.name || 'DM',
                        value: msg.channel.id,
                        inline: true
                    }]
                }
            });

            await r.table('suggestion').insert({
                id: i,
                author: msg.author.id,
                channel: msg.channel.id,
                message: words.slice(1).join(' '),
                messageid: msg.id,
                date: r.epochTime(dep.moment().unix()),
                cardId: data.id,
                cardUrl: data.shortUrl
            }).run();
            await bu.send(msg, `${type} has been sent, and has been assigned an ID ${i}! :ok_hand:\n\nYou can view your ${type.toLowerCase()} here: <${data.shortUrl}>`);
        } catch (err) {
            logger.error(err);
            await bu.send(msg, 'An error occured posting to trello. Please try again.');
        }
    }
};<commit_msg>feedback blacklists<commit_after>var e = module.exports = {};

var t;

e.init = () => {
    e.category = bu.CommandType.GENERAL;
};
e.requireCtx = require;

e.isCommand = true;
e.hidden = false;
e.usage = 'feedback <feedback>';
e.alias = ['suggest', 'report'];
e.info = `This command has three different functions for varying purposes. Please do not abuse it.

**__feedback__** - give me feedback about the bot
**__suggest__** - tell me something you want to be added or changed
**__report__** - let me know about a bug you found

Thank you for your support. It means a lot to me!`;
e.longinfo = `
This command has three different functions for varying purposes. Please do not abuse it.

feedback - give me feedback about the bot

suggest - tell me something you want added or changed

report - let me know about a bug you found

Thank you for your support. It means a lot to me!
`;

e.execute = async function(msg, words) {
    if (words.length > 1) {
        let blacklist = await r.table('vars').get('blacklist');
        if (blacklist.users.indexOf(msg.author.id) > -1) {
            bu.send(msg, 'Sorry, you have been blacklisted from the use of the `feedback`, `suggest`, and `report` commands. If you wish to appeal this, please join my support guild. You can find a link by doing `b!invite`.');
            return;
        } else if (blacklist.guilds.indexOf(msg.guild.id) > -1) {
            bu.send(msg, 'Sorry, your guild has been blacklisted from the use of the `feedback`, `suggest`, and `report` commands. If you wish to appeal this, please join my support guild. You can find a link by doing `b!invite`.');
            return;
        }
        if (words.length > 3 && msg.author.id == bu.CAT_ID) {
            switch (words[1].toLowerCase()) {
                case 'blacklist':
                    switch (words[2].toLowerCase()) {
                        case 'guild':
                            if (blacklist.guilds.indexOf(words[3]) == -1) blacklist.guilds.push(words[3]);
                            break;
                        case 'user':
                            if (blacklist.users.indexOf(words[3]) == -1) blacklist.users.push(words[3]);
                            break;
                    }
                    break;
                case 'unblacklist':
                    let index;
                    switch (words[2].toLowerCase()) {
                        case 'guild':
                            while (index = blacklist.guilds.indexOf(words[3]) > -1) {
                                blacklist.guilds.splice(index, 1);
                            }
                            break;
                        case 'user':
                            while (index = blacklist.users.indexOf(words[3]) > -1) {
                                blacklist.users.splice(index, 1);
                            }
                            break;
                    }
                    break;
            }
            await r.table('vars').get('blacklist').replace(blacklist);
            await bu.send(msg, 'Done');
            return;
        }
        let i = 0;
        let lastSuggestion = await r.table('suggestion').orderBy({
            index: r.desc('id')
        }).limit(1).run();
        if (lastSuggestion.length > 0) i = lastSuggestion[0].id + 1;
        logger.debug(i, lastSuggestion);
        if (isNaN(i)) i = 0;
        let type, colour, channel, list;
        switch (words[0].toLowerCase()) {
            case 'suggest':
                type = 'Suggestion';
                colour = 0x1faf0c;
                channel = '195716879237644292';
                list = '57ef25d2ba874bf651e96fc1';
                break;
            case 'report':
                type = 'Bug Report';
                colour = 0xaf0c0c;
                channel = '229137183234064384';
                list = '57ef25d5d777b8b35192eff2';
                break;
            default:
                type = 'Feedback';
                colour = 0xaaaf0c;
                channel = '268859677326966784';
                list = '5876a95f090e7f5189cd089b';
                break;
        }
        if (words[0].toLowerCase() == 'suggest') type = 'Suggestion';
        else if (words[0].toLowerCase() == 'report') type = 'Bug Report';

        function trelloPost() {
            return new Promise((f, r) => {
                bu.trello.post('1/cards', {
                    name: words.slice(1).join(' '),
                    desc: `Automated ${type} added by ${bot.user.username} - CASE ${i}.\n\nAuthor: ${msg.author.username}#${msg.author.discriminator} (${msg.author.id})`,
                    due: null,
                    idList: list,
                    idLabels: '58025f0184e677fd36dbd756' + (config.general.isbeta ? ',5888db5bced82109fff170af' : '')
                }, (err, data) => {
                    if (err) {
                        r(err);
                        return;
                    }
                    f(data);
                });
            });
        }
        try {
            let data = await trelloPost();

            await bu.send(channel, {
                embed: {
                    title: type,
                    description: words.slice(1).join(' ') + `\n\n[Trello Card](${data.shortUrl})`,
                    color: colour,
                    author: {
                        name: bu.getFullName(msg.author),
                        icon_url: msg.author.avatarURL,
                        url: `https://blargbot.xyz/user/${msg.author.id}`
                    },
                    timestamp: dep.moment(msg.timestamp),
                    footer: {
                        text: 'Case ' + i + ' | ' + msg.id
                    },
                    fields: [{
                        name: msg.guild ? msg.guild.name : 'DM',
                        value: msg.guild ? msg.guild.id : 'DM',
                        inline: true
                    }, {
                        name: msg.channel.name || 'DM',
                        value: msg.channel.id,
                        inline: true
                    }]
                }
            });

            await r.table('suggestion').insert({
                id: i,
                author: msg.author.id,
                channel: msg.channel.id,
                message: words.slice(1).join(' '),
                messageid: msg.id,
                date: r.epochTime(dep.moment().unix()),
                cardId: data.id,
                cardUrl: data.shortUrl
            }).run();
            await bu.send(msg, `${type} has been sent, and has been assigned an ID ${i}! :ok_hand:\n\nYou can view your ${type.toLowerCase()} here: <${data.shortUrl}>`);
        } catch (err) {
            logger.error(err);
            await bu.send(msg, 'An error occured posting to trello. Please try again.');
        }
    }
};<|endoftext|>


<commit_before>var e = module.exports = {};

e.init = () => {
    e.category = bu.CommandType.ADMIN;
};

e.requireCtx = require;

e.isCommand = true;
e.hidden = false;
e.usage = 'settings [keys|help|set <key>]';
e.info = 'Gets or sets the settings for the current guild. Visit https://blargbot.xyz/commands/settings for key documentation.';
e.longinfo = '
Gets or sets the settings for the current guild. For key documentation, go here.
';

e.execute = async function(msg, words) {
    if (words.length == 1) {
        //do settings shit
        let storedGuild = await bu.getGuild(msg.guild.id);
        let settings = storedGuild.settings;
        let channels = storedGuild.channels;

        var nsfw = [];
        var blacklisted = [];
        var i;
        for (let channel in channels) {
            if (channels[channel].nsfw) nsfw.push(channel);
            if (channels[channel].blacklisted) blacklisted.push(channel);
        }
        var prefix = settings.prefix ?
            settings.prefix : 'Not Set';
        var nsfwMessage = 'None Set';
        if (nsfw.length > 0) {
            nsfwMessage = '';
            for (i = 0; i < nsfw.length; i++) {
                let channel = bot.getChannel(nsfw[i]);
                if (channel)
                    nsfwMessage += `${channel.name} (${nsfw[i]})\n                - `;
            }
            nsfwMessage = nsfwMessage.substring(0, nsfwMessage.length - 19);
        }
        var blacklistMessage = 'None Set';
        if (blacklisted.length > 0) {
            blacklistMessage = '';
            for (i = 0; i < blacklisted.length; i++) {
                let channel = bot.getChannel(blacklisted[i]);
                if (channel)
                    blacklistMessage += `${channel.name} (${blacklisted[i]})\n                - `;
            }
            blacklistMessage = blacklistMessage.substring(0, blacklistMessage.length - 19);
        }
        var greeting = settings.greeting ?
            settings.greeting : 'Not Set';
        var farewell = settings.farewell ?
            settings.farewell : 'Not Set';
        var modlogChannel;
        if (settings.modlog) {
            let channel = bot.getChannel(settings.modlog);
            if (channel)
                modlogChannel = `${channel.name} (${settings.modlog})`;
            else
                modlogChannel = `Channel Not Found (${settings.modlog})`;
        } else {
            modlogChannel = 'Not Set';
        }
        var deleteNotif = settings.deletenotif != 0 ? true : false;
        var cahNsfw = settings.cahnsfw && settings.cahnsfw != 0 ? true : false;
        var mutedRole = settings.mutedrole ? settings.mutedrole : 'Not Set';
        var tableFlip = settings.tableflip && settings.tableflip != 0 ? true : false;
        var parsedAntiMention;
        if (settings.antimention) {
            parsedAntiMention = parseInt(settings.antimention);
            if (parsedAntiMention == 0 || isNaN(parsedAntiMention)) {
                parsedAntiMention = 'Disabled';
            }
        } else {
            parsedAntiMention = 'Disabled';
        }
        var antiMention = parsedAntiMention;
        let permOverride = settings.permoverride && settings.permoverride != 0 ? true : false;
        let dmHelp = settings.dmhelp && settings.dmhelp != 0 ? true : false;
        let makeLogs = settings.makelogs && settings.makelogs != 0 ? true : false;

        let staffPerms = settings.staffperms || bu.defaultStaff;
        let kickPerms = settings.kickoverride || 0;
        let banPerms = settings.banoverride || 0;
        let greetChan = settings.greetchan ? bot.getChannel(settings.greetchan) : 'Default';
        if (greetChan && greetChan != 'Default') greetChan = greetChan.name;
        else greetChan = 'Undefined';
        let farewellChan = settings.farewellchan ? bot.getChannel(settings.farewellchan) : 'Default';
        if (farewellChan && farewellChan != 'Default') farewellChan = farewellChan.name;
        else farewellChan = 'Undefined';
        let kickAt = settings.kickat || 'Disabled';
        let banAt = settings.banat || 'Disabled';
        var message = `\`\`\`prolog
Settings For ${msg.channel.guild.name}

-- General --
          Prefix : ${prefix}
     CAH is NSFW : ${cahNsfw}
         DM Help : ${dmHelp}

-- Messages --
        Greeting : ${greeting}
        Farewell : ${farewell}
      Tableflips : ${tableFlip}

-- Channels --
Farewell Channel : ${farewellChan}
Greeting Channel : ${greetChan}
   NSFW Channels : ${nsfwMessage}
  Modlog Channel : ${modlogChannel}
     Blacklisted : ${blacklistMessage}

-- Moderation --
       Chat Logs : ${makeLogs}
    Anti-Mention : ${antiMention}
      Muted Role : ${mutedRole}
   Track Deletes : ${deleteNotif}

-- Permissions --
   Perm Override : ${permOverride}
     Staff Perms : ${staffPerms}
   Kick Override : ${kickPerms}
    Ban Override : ${banPerms}

-- Warnings --
         Kick At : ${kickAt}
          Ban At : ${banAt}
\`\`\``;
        bu.send(msg, message);
    } else {
        words.shift();
        var key;
        switch (words[0].toLowerCase()) {
            case 'keys':
                let message = '\nYou can use \`settings set <key> [value]\` to set the following settings. All settings are case insensitive.\n';
                for (key in bu.settings) {
                    message += ' - **' + key.toUpperCase() + '** (' + bu.settings[key].type + ')\n';
                }
                bu.send(msg, message);
                break;
            case 'help':
                bu.send(msg, e.info);
                break;
            case 'set':
                words.shift();
            default:
                if (words.length > 0) {
                    key = words.shift();
                    let value = words.join(' ');
                    if (bu.settings[key]) {
                        let res = await bu.guildSettings.set(msg.channel.guild.id, key, value, bu.settings[key].type);
                        if (res == true)
                            bu.send(msg, ':ok_hand:');
                        else {
                            bu.send(msg, `Failed to set: ${res}`);
                        };
                    } else {
                        bu.send(msg, 'Invalid key!');
                    }
                }
                break;
        }
    }
};

bu.settings = {
    makelogs: {
        name: 'Make Chatlogs',
        desc: `Whether to record chat logs or not.`,
        type: 'bool'
    },
    cahnsfw: {
        name: 'Is CAH NSFW',
        desc: `Whether 'cah' can only be done in nsfw channels or not.`,
        type: 'bool'
    },
    deletenotif: {
        name: 'Delete Notifications',
        desc: `If enabled, notifies you if a user deleted their command.`,
        type: 'bool'
    },
    greeting: {
        name: 'Greeting Message',
        desc: `What to say to new users when they join. You can also use the \`greet\` command`,
        type: 'string'
    },
    farewell: {
        name: 'Farewell Message',
        desc: `What to say when a user leaves. You can also use the \`farewell\` command`,
        type: 'string'
    },
    prefix: {
        name: 'Custom Prefix',
        desc: `The custom command prefix. You can also use the \`setprefix\` command`,
        type: 'string'
    },
    modlog: {
        name: 'Modlog Channel',
        desc: `The id of the modlog channel. You can also use the \`modlog\` command`,
        type: 'string'
    },
    mutedrole: {
        name: 'Muted Role',
        desc: `The id of the muted role.`,
        type: 'string'
    },
    tableflip: {
        name: 'Tableflips',
        desc: `Whether the bot should respond to tableflips/unflips.`,
        type: 'bool'
    },
    antimention: {
        name: 'Anti-Mention',
        desc: `The number of unique mentions required to warrant a ban (for anti-mention spam). Set to '0' to disable. Recommended: 25`,
        type: 'int'
    },
    dmhelp: {
        name: 'DM Help',
        desc: `Whether or not to dm help messages or output them in channels`,
        type: 'bool'
    },
    permoverride: {
        name: 'Permission Override',
        desc: `Whether or not specific permissions override role requirement`,
        type: 'bool'
    },
    staffperms: {
        name: 'Staff Permissions',
        desc: `The numeric value of permissions that designate a staff member. If a user has any of the permissions and permoverride is enabled, allows them to execute any command regardless of role. See <https://discordapi.com/permissions.html> for a permission calculator.`,
        type: 'int'
    },
    kickoverride: {
        name: 'Kick Override',
        desc: `Same as staffperms, but allows users to use the kick command regardless of permissions`,
        type: 'int'
    },
    banoverride: {
        name: 'Ban Override',
        desc: `Same as staffperms, but allows users to use the ban/hackban/unban commands regardless of permissions`,
        type: 'int'
    },
    banat: {
        name: 'Ban At',
        desc: 'The number of warnings before a ban. Set to 0 or below to disable.',
        type: 'int'
    },
    kickat: {
        name: 'Kick At',
        desc: 'The number of warnings before a kick. Set to 0 or below to disable.',
        type: 'int'
    }
};<commit_msg>changed markdown exclusive formatting to html<commit_after>var e = module.exports = {};

e.init = () => {
    e.category = bu.CommandType.ADMIN;
};

e.requireCtx = require;

e.isCommand = true;
e.hidden = false;
e.usage = 'settings [keys|help|set <key>]';
e.info = 'Gets or sets the settings for the current guild. Visit https://blargbot.xyz/commands/settings for key documentation.';
e.longinfo = '
Gets or sets the settings for the current guild. For key documentation, go here.
';

e.execute = async function(msg, words) {
    if (words.length == 1) {
        //do settings shit
        let storedGuild = await bu.getGuild(msg.guild.id);
        let settings = storedGuild.settings;
        let channels = storedGuild.channels;

        var nsfw = [];
        var blacklisted = [];
        var i;
        for (let channel in channels) {
            if (channels[channel].nsfw) nsfw.push(channel);
            if (channels[channel].blacklisted) blacklisted.push(channel);
        }
        var prefix = settings.prefix ?
            settings.prefix : 'Not Set';
        var nsfwMessage = 'None Set';
        if (nsfw.length > 0) {
            nsfwMessage = '';
            for (i = 0; i < nsfw.length; i++) {
                let channel = bot.getChannel(nsfw[i]);
                if (channel)
                    nsfwMessage += `${channel.name} (${nsfw[i]})\n                - `;
            }
            nsfwMessage = nsfwMessage.substring(0, nsfwMessage.length - 19);
        }
        var blacklistMessage = 'None Set';
        if (blacklisted.length > 0) {
            blacklistMessage = '';
            for (i = 0; i < blacklisted.length; i++) {
                let channel = bot.getChannel(blacklisted[i]);
                if (channel)
                    blacklistMessage += `${channel.name} (${blacklisted[i]})\n                - `;
            }
            blacklistMessage = blacklistMessage.substring(0, blacklistMessage.length - 19);
        }
        var greeting = settings.greeting ?
            settings.greeting : 'Not Set';
        var farewell = settings.farewell ?
            settings.farewell : 'Not Set';
        var modlogChannel;
        if (settings.modlog) {
            let channel = bot.getChannel(settings.modlog);
            if (channel)
                modlogChannel = `${channel.name} (${settings.modlog})`;
            else
                modlogChannel = `Channel Not Found (${settings.modlog})`;
        } else {
            modlogChannel = 'Not Set';
        }
        var deleteNotif = settings.deletenotif != 0 ? true : false;
        var cahNsfw = settings.cahnsfw && settings.cahnsfw != 0 ? true : false;
        var mutedRole = settings.mutedrole ? settings.mutedrole : 'Not Set';
        var tableFlip = settings.tableflip && settings.tableflip != 0 ? true : false;
        var parsedAntiMention;
        if (settings.antimention) {
            parsedAntiMention = parseInt(settings.antimention);
            if (parsedAntiMention == 0 || isNaN(parsedAntiMention)) {
                parsedAntiMention = 'Disabled';
            }
        } else {
            parsedAntiMention = 'Disabled';
        }
        var antiMention = parsedAntiMention;
        let permOverride = settings.permoverride && settings.permoverride != 0 ? true : false;
        let dmHelp = settings.dmhelp && settings.dmhelp != 0 ? true : false;
        let makeLogs = settings.makelogs && settings.makelogs != 0 ? true : false;

        let staffPerms = settings.staffperms || bu.defaultStaff;
        let kickPerms = settings.kickoverride || 0;
        let banPerms = settings.banoverride || 0;
        let greetChan = settings.greetchan ? bot.getChannel(settings.greetchan) : 'Default';
        if (greetChan && greetChan != 'Default') greetChan = greetChan.name;
        else greetChan = 'Undefined';
        let farewellChan = settings.farewellchan ? bot.getChannel(settings.farewellchan) : 'Default';
        if (farewellChan && farewellChan != 'Default') farewellChan = farewellChan.name;
        else farewellChan = 'Undefined';
        let kickAt = settings.kickat || 'Disabled';
        let banAt = settings.banat || 'Disabled';
        var message = `\`\`\`prolog
Settings For ${msg.channel.guild.name}

-- General --
          Prefix : ${prefix}
     CAH is NSFW : ${cahNsfw}
         DM Help : ${dmHelp}

-- Messages --
        Greeting : ${greeting}
        Farewell : ${farewell}
      Tableflips : ${tableFlip}

-- Channels --
Farewell Channel : ${farewellChan}
Greeting Channel : ${greetChan}
   NSFW Channels : ${nsfwMessage}
  Modlog Channel : ${modlogChannel}
     Blacklisted : ${blacklistMessage}

-- Moderation --
       Chat Logs : ${makeLogs}
    Anti-Mention : ${antiMention}
      Muted Role : ${mutedRole}
   Track Deletes : ${deleteNotif}

-- Permissions --
   Perm Override : ${permOverride}
     Staff Perms : ${staffPerms}
   Kick Override : ${kickPerms}
    Ban Override : ${banPerms}

-- Warnings --
         Kick At : ${kickAt}
          Ban At : ${banAt}
\`\`\``;
        bu.send(msg, message);
    } else {
        words.shift();
        var key;
        switch (words[0].toLowerCase()) {
            case 'keys':
                let message = '\nYou can use \`settings set <key> [value]\` to set the following settings. All settings are case insensitive.\n';
                for (key in bu.settings) {
                    message += ' - **' + key.toUpperCase() + '** (' + bu.settings[key].type + ')\n';
                }
                bu.send(msg, message);
                break;
            case 'help':
                bu.send(msg, e.info);
                break;
            case 'set':
                words.shift();
            default:
                if (words.length > 0) {
                    key = words.shift();
                    let value = words.join(' ');
                    if (bu.settings[key]) {
                        let res = await bu.guildSettings.set(msg.channel.guild.id, key, value, bu.settings[key].type);
                        if (res == true)
                            bu.send(msg, ':ok_hand:');
                        else {
                            bu.send(msg, `Failed to set: ${res}`);
                        };
                    } else {
                        bu.send(msg, 'Invalid key!');
                    }
                }
                break;
        }
    }
};

bu.settings = {
    makelogs: {
        name: 'Make Chatlogs',
        desc: `Whether to record chat logs or not.`,
        type: 'bool'
    },
    cahnsfw: {
        name: 'Is CAH NSFW',
        desc: `Whether 'cah' can only be done in nsfw channels or not.`,
        type: 'bool'
    },
    deletenotif: {
        name: 'Delete Notifications',
        desc: `If enabled, notifies you if a user deleted their command.`,
        type: 'bool'
    },
    greeting: {
        name: 'Greeting Message',
        desc: `What to say to new users when they join. You can also use the <code>greet</code> command`,
        type: 'string'
    },
    farewell: {
        name: 'Farewell Message',
        desc: `What to say when a user leaves. You can also use the <code>farewell</code> command`,
        type: 'string'
    },
    prefix: {
        name: 'Custom Prefix',
        desc: `The custom command prefix. You can also use the <code>setprefix</code> command`,
        type: 'string'
    },
    modlog: {
        name: 'Modlog Channel',
        desc: `The id of the modlog channel. You can also use the <code>modlog</code> command`,
        type: 'string'
    },
    mutedrole: {
        name: 'Muted Role',
        desc: `The id of the muted role.`,
        type: 'string'
    },
    tableflip: {
        name: 'Tableflips',
        desc: `Whether the bot should respond to tableflips/unflips.`,
        type: 'bool'
    },
    antimention: {
        name: 'Anti-Mention',
        desc: `The number of unique mentions required to warrant a ban (for anti-mention spam). Set to '0' to disable. Recommended: 25`,
        type: 'int'
    },
    dmhelp: {
        name: 'DM Help',
        desc: `Whether or not to dm help messages or output them in channels`,
        type: 'bool'
    },
    permoverride: {
        name: 'Permission Override',
        desc: `Whether or not specific permissions override role requirement`,
        type: 'bool'
    },
    staffperms: {
        name: 'Staff Permissions',
        desc: `The numeric value of permissions that designate a staff member. If a user has any of the permissions and permoverride is enabled, allows them to execute any command regardless of role. See here for a permission calculator.`,
        type: 'int'
    },
    kickoverride: {
        name: 'Kick Override',
        desc: `Same as staffperms, but allows users to use the kick command regardless of permissions`,
        type: 'int'
    },
    banoverride: {
        name: 'Ban Override',
        desc: `Same as staffperms, but allows users to use the ban/hackban/unban commands regardless of permissions`,
        type: 'int'
    },
    banat: {
        name: 'Ban At',
        desc: 'The number of warnings before a ban. Set to 0 or below to disable.',
        type: 'int'
    },
    kickat: {
        name: 'Kick At',
        desc: 'The number of warnings before a kick. Set to 0 or below to disable.',
        type: 'int'
    }
};<|endoftext|>


<commit_before>/*
* Copyright 2016 IBM Corp.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
*      http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

/**
 * Represents a row-oriented distributed Matrix with no meaningful row indices.
 *
 * @classdesc
 * @param {RDD} rows stored as an RDD[Vector]
 * @param {number} [nRows] number of rows. A non-positive value means unknown, and then the number of rows will
 *              be determined by the number of records in the RDD `rows`.
 * @param {number} [nCols] number of columns. A non-positive value means unknown, and then the number of
 *              columns will be determined by the size of the first row.
 * @class
 * @extends {DistributedMatrix}
 */
var RowMatrix = function(rows,nRows,nCols) {
	this.logger = Logger.getLogger("RowMatrix_js");
	var jvmObject;
	if (arguments[0] instanceof org.apache.spark.mllib.linalg.distributed.RowMatrix) {
		jvmObject = arguments[0];
	} else if (arguments.length === 3) {
		jvmObject = new org.apache.spark.mllib.linalg.distributed.RowMatrix(Utils.unwrapObject(rows).rdd(),nRows,nCols);
	} else {
		jvmObject = new org.apache.spark.mllib.linalg.distributed.RowMatrix(Utils.unwrapObject(rows).rdd());
	}

	 DistributedMatrix.call(this, jvmObject);

};

RowMatrix.prototype = Object.create(DistributedMatrix.prototype);

RowMatrix.prototype.constructor = RowMatrix;

/**
 * Computes the Gramian matrix `A^T A`. Note that this cannot be computed on matrices with
 * more than 65535 columns.
 * @returns {Matrix}
 */
RowMatrix.prototype.computeGramianMatrix = function() {
   var javaObject =  this.getJavaObject().computeGramianMatrix();
   return Utils.javaToJs(javaObject);
};

/**
 * Computes singular value decomposition of this matrix. Denote this matrix by A (m x n). This
 * will compute matrices U, S, V such that A ~= U * S * V', where S contains the leading k
 * singular values, U and V contain the corresponding singular vectors.
 *
 * At most k largest non-zero singular values and associated vectors are returned. If there are k
 * such values, then the dimensions of the return will be:
 *  - U is a RowMatrix of size m x k that satisfies U' * U = eye(k),
 *  - s is a Vector of size k, holding the singular values in descending order,
 *  - V is a Matrix of size n x k that satisfies V' * V = eye(k).
 *
 * We assume n is smaller than m, though this is not strictly required.
 * The singular values and the right singular vectors are derived
 * from the eigenvalues and the eigenvectors of the Gramian matrix A' * A. U, the matrix
 * storing the right singular vectors, is computed via matrix multiplication as
 * U = A * (V * S^-1^), if requested by user. The actual method to use is determined
 * automatically based on the cost:
 *  - If n is small (n < 100) or k is large compared with n (k > n / 2), we compute
 *    the Gramian matrix first and then compute its top eigenvalues and eigenvectors locally
 *    on the driver. This requires a single pass with O(n^2^) storage on each executor and
 *    on the driver, and O(n^2^ k) time on the driver.
 *  - Otherwise, we compute (A' * A) * v in a distributive way and send it to ARPACK's DSAUPD to
 *    compute (A' * A)'s top eigenvalues and eigenvectors on the driver node. This requires O(k)
 *    passes, O(n) storage on each executor, and O(n k) storage on the driver.
 *
 * Several internal parameters are set to default values. The reciprocal condition number rCond
 * is set to 1e-9. All singular values smaller than rCond * sigma(0) are treated as zeros, where
 * sigma(0) is the largest singular value. The maximum number of Arnoldi update iterations for
 * ARPACK is set to 300 or k * 3, whichever is larger. The numerical tolerance for ARPACK's
 * eigen-decomposition is set to 1e-10.
 *
 * @note The conditions that decide which method to use internally and the default parameters are
 *       subject to change.
 *
 * @param {integer} k  number of leading singular values to keep (0 < k <= n).
 *          It might return less than k if
 *          there are numerically zero singular values or there are not enough Ritz values
 *          converged before the maximum number of Arnoldi update iterations is reached (in case
 *          that matrix A is ill-conditioned).
 * @param {boolean} computeU  whether to compute U
 * @param {float} rCond  the reciprocal condition number. All singular values smaller than rCond * sigma(0)
 *              are treated as zero, where sigma(0) is the largest singular value.
 * @returns {SingularValueDecomposition}  SingularValueDecomposition(U, s, V). U = null if computeU = false.
 */
RowMatrix.prototype.computeSVD = function(k,computeU,rCond) {
   var javaObject =  this.getJavaObject().computeSVD(k,computeU,rCond);
   return new SingularValueDecomposition(javaObject);
};

/**
 * Computes the covariance matrix, treating each row as an observation. Note that this cannot
 * be computed on matrices with more than 65535 columns.
 * @returns {Matrix}  a local dense matrix of size n x n
 */
RowMatrix.prototype.computeCovariance = function() {
   var javaObject =  this.getJavaObject().computeCovariance();
   return Utils.javaToJs(javaObject);
};

/**
 * Computes the top k principal components.
 * Rows correspond to observations and columns correspond to variables.
 * The principal components are stored a local matrix of size n-by-k.
 * Each column corresponds for one principal component,
 * and the columns are in descending order of component variance.
 * The row data do not need to be "centered" first; it is not necessary for
 * the mean of each column to be 0.
 *
 * Note that this cannot be computed on matrices with more than 65535 columns.
 *
 * @param {integer} k  number of top principal components.
 * @returns {Matrix}  a matrix of size n-by-k, whose columns are principal components
 */
RowMatrix.prototype.computePrincipalComponents = function(k) {
   var javaObject =  this.getJavaObject().computePrincipalComponents(k);
   return Utils.javaToJs(javaObject);
};

/**
 * Computes column-wise summary statistics.
 * @returns {MultivariateStatisticalSummary}
 */
RowMatrix.prototype.computeColumnSummaryStatistics = function() {
throw "not implemented by ElairJS";
//   var javaObject =  this.getJavaObject().computeColumnSummaryStatistics();
//   return Utils.javaToJs(javaObject);
};

/**
 * Multiply this matrix by a local matrix on the right.
 *
 * @param {Matrix} B  a local matrix whose number of rows must match the number of columns of this matrix
 *         which preserves partitioning
 * @returns {RowMatrix}  a [[org.apache.spark.mllib.linalg.distributed.RowMatrix]] representing the product,
 */
RowMatrix.prototype.multiply = function(B) {
   var B_uw = Utils.unwrapObject(B);
   var javaObject =  this.getJavaObject().multiply(B_uw);
   return new RowMatrix(javaObject);
};

/**
 * Compute similarities between columns of this matrix using a sampling approach.
 *
 * The threshold parameter is a trade-off knob between estimate quality and computational cost.
 *
 * Setting a threshold of 0 guarantees deterministic correct results, but comes at exactly
 * the same cost as the brute-force approach. Setting the threshold to positive values
 * incurs strictly less computational cost than the brute-force approach, however the
 * similarities computed will be estimates.
 *
 * The sampling guarantees relative-error correctness for those pairs of columns that have
 * similarity greater than the given similarity threshold.
 *
 * To describe the guarantee, we set some notation:
 * Let A be the smallest in magnitude non-zero element of this matrix.
 * Let B be the largest  in magnitude non-zero element of this matrix.
 * Let L be the maximum number of non-zeros per row.
 *
 * For example, for {0,1} matrices: A=B=1.
 * Another example, for the Netflix matrix: A=1, B=5
 *
 * For those column pairs that are above the threshold,
 * the computed similarity is correct to within 20% relative error with probability
 * at least 1 - (0.981)^10/B^
 *
 * The shuffle size is bounded by the *smaller* of the following two expressions:
 *
 * O(n log(n) L / (threshold * A))
 * O(m L^2^)
 *
 * The latter is the cost of the brute-force approach, so for non-zero thresholds,
 * the cost is always cheaper than the brute-force approach.
 *
 * @param {number} [threshold]  Set to 0 for deterministic guaranteed correctness.
 *                  Similarities above this threshold are estimated
 *                  with the cost vs estimate quality trade-off described above.
 *         between columns of this matrix.
 * @returns {CoordinateMatrix}  An n x n sparse upper-triangular matrix of cosine similarities
 */
RowMatrix.prototype.columnSimilarities = function(threshold) {
throw "not implemented by ElairJS";
//
//   if (arguments[0]) {
//   var javaObject =  this.getJavaObject().columnSimilarities(threshold);
//   return new CoordinateMatrix(javaObject);
//   } else {
//   var javaObject =  this.getJavaObject().columnSimilarities();
//   return new CoordinateMatrix(javaObject);
//   }
};

/**
 * Compute QR decomposition for {@link RowMatrix}. The implementation is designed to optimize the QR
 * decomposition (factorization) for the {@link RowMatrix} of a tall and skinny shape.
 * Reference:
 *  Paul G. Constantine, David F. Gleich. "Tall and skinny QR factorizations in MapReduce
 *  architectures"  ([[http://dx.doi.org/10.1145/1996092.1996103]])
 *
 * @param {boolean} computeQ  whether to computeQ
 * @returns {QRDecomposition}  QRDecomposition(Q, R), Q = null if computeQ = false.
 */
RowMatrix.prototype.tallSkinnyQR = function(computeQ) {
throw "not implemented by ElairJS";
//   var javaObject =  this.getJavaObject().tallSkinnyQR(computeQ);
//   return new QRDecomposition(javaObject);
};

/**
 * Gets RDD of Vectors
 * @returns {RDD} RDD of Vectors
 */
RowMatrix.prototype.rows = function() {
   var javaObject =  this.getJavaObject().rows();
   return Utils.javaToJs(javaObject);
};
<commit_msg>Implement RowMatrix.toJSON<commit_after>/*
* Copyright 2016 IBM Corp.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
*      http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

/**
 * Represents a row-oriented distributed Matrix with no meaningful row indices.
 *
 * @classdesc
 * @param {RDD} rows stored as an RDD[Vector]
 * @param {number} [nRows] number of rows. A non-positive value means unknown, and then the number of rows will
 *              be determined by the number of records in the RDD `rows`.
 * @param {number} [nCols] number of columns. A non-positive value means unknown, and then the number of
 *              columns will be determined by the size of the first row.
 * @class
 * @extends {DistributedMatrix}
 */
var RowMatrix = function(rows,nRows,nCols) {
	this.logger = Logger.getLogger("RowMatrix_js");
	var jvmObject;
	if (arguments[0] instanceof org.apache.spark.mllib.linalg.distributed.RowMatrix) {
		jvmObject = arguments[0];
	} else if (arguments.length === 3) {
		jvmObject = new org.apache.spark.mllib.linalg.distributed.RowMatrix(Utils.unwrapObject(rows).rdd(),nRows,nCols);
	} else {
		jvmObject = new org.apache.spark.mllib.linalg.distributed.RowMatrix(Utils.unwrapObject(rows).rdd());
	}

	 DistributedMatrix.call(this, jvmObject);

};

RowMatrix.prototype = Object.create(DistributedMatrix.prototype);

RowMatrix.prototype.constructor = RowMatrix;

/**
 * Computes the Gramian matrix `A^T A`. Note that this cannot be computed on matrices with
 * more than 65535 columns.
 * @returns {Matrix}
 */
RowMatrix.prototype.computeGramianMatrix = function() {
   var javaObject =  this.getJavaObject().computeGramianMatrix();
   return Utils.javaToJs(javaObject);
};

/**
 * Computes singular value decomposition of this matrix. Denote this matrix by A (m x n). This
 * will compute matrices U, S, V such that A ~= U * S * V', where S contains the leading k
 * singular values, U and V contain the corresponding singular vectors.
 *
 * At most k largest non-zero singular values and associated vectors are returned. If there are k
 * such values, then the dimensions of the return will be:
 *  - U is a RowMatrix of size m x k that satisfies U' * U = eye(k),
 *  - s is a Vector of size k, holding the singular values in descending order,
 *  - V is a Matrix of size n x k that satisfies V' * V = eye(k).
 *
 * We assume n is smaller than m, though this is not strictly required.
 * The singular values and the right singular vectors are derived
 * from the eigenvalues and the eigenvectors of the Gramian matrix A' * A. U, the matrix
 * storing the right singular vectors, is computed via matrix multiplication as
 * U = A * (V * S^-1^), if requested by user. The actual method to use is determined
 * automatically based on the cost:
 *  - If n is small (n < 100) or k is large compared with n (k > n / 2), we compute
 *    the Gramian matrix first and then compute its top eigenvalues and eigenvectors locally
 *    on the driver. This requires a single pass with O(n^2^) storage on each executor and
 *    on the driver, and O(n^2^ k) time on the driver.
 *  - Otherwise, we compute (A' * A) * v in a distributive way and send it to ARPACK's DSAUPD to
 *    compute (A' * A)'s top eigenvalues and eigenvectors on the driver node. This requires O(k)
 *    passes, O(n) storage on each executor, and O(n k) storage on the driver.
 *
 * Several internal parameters are set to default values. The reciprocal condition number rCond
 * is set to 1e-9. All singular values smaller than rCond * sigma(0) are treated as zeros, where
 * sigma(0) is the largest singular value. The maximum number of Arnoldi update iterations for
 * ARPACK is set to 300 or k * 3, whichever is larger. The numerical tolerance for ARPACK's
 * eigen-decomposition is set to 1e-10.
 *
 * @note The conditions that decide which method to use internally and the default parameters are
 *       subject to change.
 *
 * @param {integer} k  number of leading singular values to keep (0 < k <= n).
 *          It might return less than k if
 *          there are numerically zero singular values or there are not enough Ritz values
 *          converged before the maximum number of Arnoldi update iterations is reached (in case
 *          that matrix A is ill-conditioned).
 * @param {boolean} computeU  whether to compute U
 * @param {float} rCond  the reciprocal condition number. All singular values smaller than rCond * sigma(0)
 *              are treated as zero, where sigma(0) is the largest singular value.
 * @returns {SingularValueDecomposition}  SingularValueDecomposition(U, s, V). U = null if computeU = false.
 */
RowMatrix.prototype.computeSVD = function(k,computeU,rCond) {
   var javaObject =  this.getJavaObject().computeSVD(k,computeU,rCond);
   return new SingularValueDecomposition(javaObject);
};

/**
 * Computes the covariance matrix, treating each row as an observation. Note that this cannot
 * be computed on matrices with more than 65535 columns.
 * @returns {Matrix}  a local dense matrix of size n x n
 */
RowMatrix.prototype.computeCovariance = function() {
   var javaObject =  this.getJavaObject().computeCovariance();
   return Utils.javaToJs(javaObject);
};

/**
 * Computes the top k principal components.
 * Rows correspond to observations and columns correspond to variables.
 * The principal components are stored a local matrix of size n-by-k.
 * Each column corresponds for one principal component,
 * and the columns are in descending order of component variance.
 * The row data do not need to be "centered" first; it is not necessary for
 * the mean of each column to be 0.
 *
 * Note that this cannot be computed on matrices with more than 65535 columns.
 *
 * @param {integer} k  number of top principal components.
 * @returns {Matrix}  a matrix of size n-by-k, whose columns are principal components
 */
RowMatrix.prototype.computePrincipalComponents = function(k) {
   var javaObject =  this.getJavaObject().computePrincipalComponents(k);
   return Utils.javaToJs(javaObject);
};

/**
 * Computes column-wise summary statistics.
 * @returns {MultivariateStatisticalSummary}
 */
RowMatrix.prototype.computeColumnSummaryStatistics = function() {
throw "not implemented by ElairJS";
//   var javaObject =  this.getJavaObject().computeColumnSummaryStatistics();
//   return Utils.javaToJs(javaObject);
};

/**
 * Multiply this matrix by a local matrix on the right.
 *
 * @param {Matrix} B  a local matrix whose number of rows must match the number of columns of this matrix
 *         which preserves partitioning
 * @returns {RowMatrix}  a [[org.apache.spark.mllib.linalg.distributed.RowMatrix]] representing the product,
 */
RowMatrix.prototype.multiply = function(B) {
   var B_uw = Utils.unwrapObject(B);
   var javaObject =  this.getJavaObject().multiply(B_uw);
   return new RowMatrix(javaObject);
};

/**
 * Compute similarities between columns of this matrix using a sampling approach.
 *
 * The threshold parameter is a trade-off knob between estimate quality and computational cost.
 *
 * Setting a threshold of 0 guarantees deterministic correct results, but comes at exactly
 * the same cost as the brute-force approach. Setting the threshold to positive values
 * incurs strictly less computational cost than the brute-force approach, however the
 * similarities computed will be estimates.
 *
 * The sampling guarantees relative-error correctness for those pairs of columns that have
 * similarity greater than the given similarity threshold.
 *
 * To describe the guarantee, we set some notation:
 * Let A be the smallest in magnitude non-zero element of this matrix.
 * Let B be the largest  in magnitude non-zero element of this matrix.
 * Let L be the maximum number of non-zeros per row.
 *
 * For example, for {0,1} matrices: A=B=1.
 * Another example, for the Netflix matrix: A=1, B=5
 *
 * For those column pairs that are above the threshold,
 * the computed similarity is correct to within 20% relative error with probability
 * at least 1 - (0.981)^10/B^
 *
 * The shuffle size is bounded by the *smaller* of the following two expressions:
 *
 * O(n log(n) L / (threshold * A))
 * O(m L^2^)
 *
 * The latter is the cost of the brute-force approach, so for non-zero thresholds,
 * the cost is always cheaper than the brute-force approach.
 *
 * @param {number} [threshold]  Set to 0 for deterministic guaranteed correctness.
 *                  Similarities above this threshold are estimated
 *                  with the cost vs estimate quality trade-off described above.
 *         between columns of this matrix.
 * @returns {CoordinateMatrix}  An n x n sparse upper-triangular matrix of cosine similarities
 */
RowMatrix.prototype.columnSimilarities = function(threshold) {
throw "not implemented by ElairJS";
//
//   if (arguments[0]) {
//   var javaObject =  this.getJavaObject().columnSimilarities(threshold);
//   return new CoordinateMatrix(javaObject);
//   } else {
//   var javaObject =  this.getJavaObject().columnSimilarities();
//   return new CoordinateMatrix(javaObject);
//   }
};

/**
 * Compute QR decomposition for {@link RowMatrix}. The implementation is designed to optimize the QR
 * decomposition (factorization) for the {@link RowMatrix} of a tall and skinny shape.
 * Reference:
 *  Paul G. Constantine, David F. Gleich. "Tall and skinny QR factorizations in MapReduce
 *  architectures"  ([[http://dx.doi.org/10.1145/1996092.1996103]])
 *
 * @param {boolean} computeQ  whether to computeQ
 * @returns {QRDecomposition}  QRDecomposition(Q, R), Q = null if computeQ = false.
 */
RowMatrix.prototype.tallSkinnyQR = function(computeQ) {
throw "not implemented by ElairJS";
//   var javaObject =  this.getJavaObject().tallSkinnyQR(computeQ);
//   return new QRDecomposition(javaObject);
};

/**
 * Gets RDD of Vectors
 * @returns {RDD} RDD of Vectors
 */
RowMatrix.prototype.rows = function() {
   var javaObject =  this.getJavaObject().rows();
   return Utils.javaToJs(javaObject);
};

RowMatrix.prototype.toJSON = function() {
  return this.rows().collect();
};<|endoftext|>


<commit_before>(function() {
    'use strict';
    /* globals SockJS, Stomp */

    angular
        .module('artemisApp')
        .factory('JhiWebsocketService', JhiWebsocketService);

    JhiWebsocketService.$inject = ['$rootScope', '$window', '$cookies', '$http', '$q'];

    function JhiWebsocketService ($rootScope, $window, $cookies, $http, $q) {
        var stompClient = null;
        var subscriber = null;
        var listener = {};
        var connected = $q.defer();
        var alreadyConnectedOnce = false;

        var service = {
            connect: connect,
            disconnect: disconnect,
            receive: receive,
            sendActivity: sendActivity,
            send: send,
            subscribe: subscribe,
            unsubscribe: unsubscribe
        };

        return service;

        // TODO: Valentin: Implement automatic reconnection attempts when connection is lost
        // TODO: Valentin: Implement callbacks for both "connection lost" and "reconnected" events

        function connect () {
            //building absolute path so that websocket doesn't fail when deploying with a context path
            var loc = $window.location;
            var url = '//' + loc.host + loc.pathname + 'websocket/tracker';
            var socket = new SockJS(url);
            stompClient = Stomp.over(socket);
            var stateChangeStart;
            var headers = {};
            headers[$http.defaults.xsrfHeaderName] = $cookies.get($http.defaults.xsrfCookieName);
            stompClient.connect(headers, function() {
                connected.resolve('success');
                sendActivity();
                if (!alreadyConnectedOnce) {
                    stateChangeStart = $rootScope.$on('$stateChangeStart', function () {
                        sendActivity();
                    });
                    alreadyConnectedOnce = true;
                }
            });
            $rootScope.$on('$destroy', function () {
                if(angular.isDefined(stateChangeStart) && stateChangeStart !== null){
                    stateChangeStart();
                }
            });
        }

        function disconnect () {
            if (stompClient !== null) {
                stompClient.disconnect();
                stompClient = null;
            }
        }

        function receive (channel) {
            if(!listener[channel]) {
                listener[channel] = $q.defer();
            }
            return listener[channel].promise;
        }

        function sendActivity() {
            if (stompClient !== null && stompClient.connected) {
                stompClient
                    .send('/topic/activity',
                        {},
                        angular.toJson({'page': $rootScope.toState.name}));
            }
        }

        /**
         * Send data through the websocket connection
         * @param path {string} the path for the websocket connection
         * @param data {object} the date to send through the websocket connection
         */
        function send(path, data) {
            if (stompClient !== null && stompClient.connected) {
                stompClient
                    .send(path,
                        {},
                        angular.toJson(data));
            }
        }

        function subscribe (channel) {
            connected.promise.then(function() {
                if(!listener[channel]) {
                    listener[channel] = $q.defer();
                }
                subscriber = stompClient.subscribe(channel, function(data) {
                    listener[channel].notify(angular.fromJson(data.body));
                });
            }, null, null);
        }

        function unsubscribe (channel) {
            if (subscriber !== null) {
                subscriber.unsubscribe();
            }
            listener[channel] = $q.defer();
        }
    }
})();
<commit_msg>add reconnect on stomp error callback in js client<commit_after>(function() {
    'use strict';
    /* globals SockJS, Stomp */

    angular
        .module('artemisApp')
        .factory('JhiWebsocketService', JhiWebsocketService);

    JhiWebsocketService.$inject = ['$rootScope', '$window', '$cookies', '$http', '$q'];

    function JhiWebsocketService ($rootScope, $window, $cookies, $http, $q) {
        var stompClient = null;
        var subscriber = null;
        var listener = {};
        var connected = $q.defer();
        var alreadyConnectedOnce = false;

        var service = {
            connect: connect,
            disconnect: disconnect,
            receive: receive,
            sendActivity: sendActivity,
            send: send,
            subscribe: subscribe,
            unsubscribe: unsubscribe
        };

        return service;

        //adapted from https://stackoverflow.com/questions/22361917/automatic-reconnect-with-stomp-js-in-node-js-application
        function stompFailureCallback(error) {
            setTimeout(connect, 1000);
            //TODO: after 5 failed attempts in row, increase the timeout to 5 seconds, after 10 failed attempts in row, increase the timeout to 10 seconds
            console.log('Websocket: Try to reconect in 1 seconds...');
        };

        function connect () {
            //building absolute path so that websocket doesn't fail when deploying with a context path
            var loc = $window.location;
            var url = '//' + loc.host + loc.pathname + 'websocket/tracker';
            var socket = new SockJS(url);
            stompClient = Stomp.over(socket);
            var stateChangeStart;
            var headers = {};
            headers[$http.defaults.xsrfHeaderName] = $cookies.get($http.defaults.xsrfCookieName);
            stompClient.connect(headers, function() {
                connected.resolve('success');
                //(re)connect to all existing channels
                for (var channel in listener) {
                    subscriber = stompClient.subscribe(channel, function(data) {
                        listener[channel].notify(angular.fromJson(data.body));
                    });
                }
                sendActivity();
                if (!alreadyConnectedOnce) {
                    stateChangeStart = $rootScope.$on('$stateChangeStart', function () {
                        sendActivity();
                    });
                    alreadyConnectedOnce = true;
                }
            }, stompFailureCallback);
            $rootScope.$on('$destroy', function () {
                if(angular.isDefined(stateChangeStart) && stateChangeStart !== null){
                    stateChangeStart();
                }
            });
        }

        function disconnect () {
            if (stompClient !== null) {
                stompClient.disconnect();
                stompClient = null;
            }
        }

        function receive (channel) {
            if(!listener[channel]) {
                listener[channel] = $q.defer();
            }
            return listener[channel].promise;
        }

        function sendActivity() {
            if (stompClient !== null && stompClient.connected) {
                stompClient
                    .send('/topic/activity',
                        {},
                        angular.toJson({'page': $rootScope.toState.name}));
            }
        }

        /**
         * Send data through the websocket connection
         * @param path {string} the path for the websocket connection
         * @param data {object} the date to send through the websocket connection
         */
        function send(path, data) {
            if (stompClient !== null && stompClient.connected) {
                stompClient
                    .send(path,
                        {},
                        angular.toJson(data));
            }
        }

        function subscribe (channel) {
            connected.promise.then(function() {
                if(!listener[channel]) {
                    listener[channel] = $q.defer();
                }
                subscriber = stompClient.subscribe(channel, function(data) {
                    listener[channel].notify(angular.fromJson(data.body));
                });
            }, null, null);
        }

        function unsubscribe (channel) {
            if (subscriber !== null) {
                subscriber.unsubscribe();
            }
            listener[channel] = $q.defer();
        }
    }
})();
<|endoftext|>


<commit_before>import React, { Fragment } from 'react';
import actions from '../actions';
import { bindActionCreators } from 'redux';
import { connect } from 'react-redux';
import Notify from '../../../../core/client/src/components/Notify';
import Card from '../../../../core/client/src/components/Card/Card';
import { Button, Row, Col } from 'reactstrap';
import FieldForm from './FieldForm';
import withDrafterbit from '../../../../core/client/src/withDrafterbit';
import ContentTypeForm from './ContentTypeForm'
import ApiClient from '../ApiClient';

const FieldType  = require('../../../../../FieldType');

class ContentType extends React.Component {

    constructor(props) {
        super(props);
        this.state = {
            _id: '',
            name: '',
            slug: '',
            description: '',
            fields: [],
            notifyText: '',
	        fieldDialogActive: false,
	        editedFieldId: "",
            fieldTypeSelected: null,
            basicEditForm: false,
        };

        this.deleteField = this.deleteField.bind(this);
        this.doUpdate = this.doUpdate.bind(this);
        this.fetchContentType = this.fetchContentType.bind(this);
    }

    fetchContentType() {
        let client = new ApiClient(this.props.drafterbit.getAxiosInstance());
        return client.getContentType(this.props.match.params.content_type_id)
            .then(contentType => {

                this.setState({
                    _id: contentType._id,
                    name: contentType.name,
                    slug: contentType.slug,
                    description: contentType.description,
                    fields: contentType.fields
                });
            });
    }

    componentDidMount() {
        this.fetchContentType()
    }

    deleteField(f) {
    	let newFields = this.state.fields.filter((sf) => {
		    return (sf._id !== f._id);
	    });

	    this.setState({
		    fields: newFields,
	    }, this.doUpdate);
    }

    deleteContentType(deleteForm) {
        // TODO create alert
        let client = new ApiClient(this.props.drafterbit.getAxiosInstance());
        client.deleteContentType(deleteForm.id.value)
            .then(r => {
                // TODO create success notif
                this.props.history.push('/content_types');
            });
    }

    doUpdate() {
        let client = new ApiClient(this.props.drafterbit.getAxiosInstance());
        client.updateContentType(
            this.state._id,
            this.state.name,
            this.state.slug,
            this.state.description,
            this.state.fields
        ).then(r => {
            this.setState({
                notifyText: 'Content type successfully saved.'
            });
        });
    }

    render() {

        return (
            <Fragment>
	            <Row>
		            <Col md="12" className="mb-3">

{this.state.name}  {
                            e.preventDefault();
                            this.setState({
                                basicEditForm: true
                            })
                        }}>

                        {this.state.description}

		            </Col>
		            <Col md="12">
			            <Card headerText="Fields">
                            <button onClick={e => {
                                e.preventDefault();
                                this.setState({
                                    fieldDialogActive: true,
                                    editedFieldId: ""
                                });
                            }} className="btn btn-success btn-sm mb-2">Add Field</button>

						            Name
						            Label
						            Type

					            {this.state.fields.map((f,i) => {
						            return (

                                             {
                                                e.preventDefault();

                                                this.setState({
                                                    fieldDialogActive: true,
                                                    editedFieldId: f._id
                                                })
                                            }}>{f.name}
                                            {f.label}
                                            {FieldType.get(f.type_id).name}
								            <Button size="sm" onClick={e => {
								            	this.deleteField(f);
								            }}>×</Button>

						            );
					            })}

			            </Card>

                        <Card headerText={`Delete Content Type : ${this.state.name}`}>
                            <form onSubmit={e => { e.preventDefault(); this.deleteContentType(e.target); }}>
                                <input type="hidden" name="id" id="id" value={this.state._id} />
                                <button type="submit" className="btn btn-danger">Delete Content Type</button>
                            </form>
                        </Card>
		            </Col>
	            </Row>
                <FieldForm
                    isOpen={this.state.fieldDialogActive}
                    contentTypeId={this.state._id}
                    fieldId={this.state.editedFieldId}
                    onCancel={e => {
                        e.preventDefault();
                        this.setState({
                            fieldDialogActive: false,
                            editedFieldId: ""
                        });
                    }}
                    onSuccess={e => {
                        this.fetchContentType()
                            .then(() => {
                                this.setState({
                                    fieldDialogActive: false,
                                    editedFieldId: ""
                                });
                            })
                    }}
                />
                {this.state.notifyText &&
                  <Notify type="success" message={this.state.notifyText} />
                }
                <ContentTypeForm
                    isOpen={this.state.basicEditForm}
                    contentTypeId={this.state._id}
                    name={this.state.name}
                    slug={this.state.slug}
                    description={this.state.description}
                    onCancel={e => {
                        this.setState({
                            basicEditForm: false
                        })
                    }}
                    onSuccess={ct => {
                        this.setState({
                            basicEditForm: false,
                            notifyText: "Content Type Saved Successfully !"
                        })
                    }}
                    />
            </Fragment>
        );
    }
}

const mapStateToProps = (state) => {
    return {
        contentTypes: state.CONTENT.contentTypes
    };
};

const mapDispatchToProps = (dispatch) => {
    return bindActionCreators(actions, dispatch);
};

export default connect(mapStateToProps, mapDispatchToProps)(withDrafterbit(ContentType));<commit_msg>add loding on edit content type<commit_after>import React, { Fragment } from 'react';
import actions from '../actions';
import { bindActionCreators } from 'redux';
import { connect } from 'react-redux';
import Notify from '../../../../core/client/src/components/Notify';
import Card from '../../../../core/client/src/components/Card/Card';
import { Button, Row, Col } from 'reactstrap';
import FieldForm from './FieldForm';
import withDrafterbit from '../../../../core/client/src/withDrafterbit';
import ContentTypeForm from './ContentTypeForm'
import ApiClient from '../ApiClient';

const FieldType  = require('../../../../../FieldType');

class ContentType extends React.Component {

    constructor(props) {
        super(props);
        this.state = {
            _id: '',
            name: '',
            slug: '',
            description: '',
            fields: [],
            notifyText: '',
	        fieldDialogActive: false,
	        editedFieldId: "",
            fieldTypeSelected: null,
            basicEditForm: false,
            loading: true
        };

        this.deleteField = this.deleteField.bind(this);
        this.doUpdate = this.doUpdate.bind(this);
        this.fetchContentType = this.fetchContentType.bind(this);
    }

    fetchContentType() {
        let client = new ApiClient(this.props.drafterbit.getAxiosInstance());
        return client.getContentType(this.props.match.params.content_type_id)
            .then(contentType => {

                this.setState({
                    _id: contentType._id,
                    name: contentType.name,
                    slug: contentType.slug,
                    description: contentType.description,
                    fields: contentType.fields,
                    loading: false
                });
            });
    }

    componentDidMount() {
        this.fetchContentType()
    }

    deleteField(f) {
    	let newFields = this.state.fields.filter((sf) => {
		    return (sf._id !== f._id);
	    });

	    this.setState({
		    fields: newFields,
	    }, this.doUpdate);
    }

    deleteContentType(deleteForm) {
        // TODO create alert
        let client = new ApiClient(this.props.drafterbit.getAxiosInstance());
        client.deleteContentType(deleteForm.id.value)
            .then(r => {
                // TODO create success notif
                this.props.history.push('/content_types');
            });
    }

    doUpdate() {
        let client = new ApiClient(this.props.drafterbit.getAxiosInstance());
        client.updateContentType(
            this.state._id,
            this.state.name,
            this.state.slug,
            this.state.description,
            this.state.fields
        ).then(r => {
            this.setState({
                notifyText: 'Content type successfully saved.'
            });
        });
    }

    render() {

        return (
            <Fragment>
                {this.state.loading &&
Loading…
}
                {this.state.loading ||
                <Row>
                    <Col md="12" className="mb-3">

{this.state.name}  {
                            e.preventDefault();
                            this.setState({
                                basicEditForm: true
                            })
                        }}>

                        {this.state.description}

                    </Col>
                    <Col md="12">
                        <Card headerText="Fields">
                            <button onClick={e => {
                                e.preventDefault();
                                this.setState({
                                    fieldDialogActive: true,
                                    editedFieldId: ""
                                });
                            }} className="btn btn-success btn-sm mb-2">Add Field
                            </button>

                                    Name
                                    Label
                                    Type

                                {this.state.fields.map((f, i) => {
                                    return (

                                             {
                                                e.preventDefault();

                                                this.setState({
                                                    fieldDialogActive: true,
                                                    editedFieldId: f._id
                                                })
                                            }}>{f.name}
                                            {f.label}
                                            {FieldType.get(f.type_id).name}
                                            <Button size="sm" onClick={e => {
                                                this.deleteField(f);
                                            }}>×</Button>

                                    );
                                })}

                        </Card>

                        <Card headerText={`Delete Content Type : ${this.state.name}`}>
                            <form onSubmit={e => {
                                e.preventDefault();
                                this.deleteContentType(e.target);
                            }}>
                                <input type="hidden" name="id" id="id" value={this.state._id}/>
                                <button type="submit" className="btn btn-danger">Delete Content Type</button>
                            </form>
                        </Card>
                    </Col>
                </Row>
                }
                <FieldForm
                    isOpen={this.state.fieldDialogActive}
                    contentTypeId={this.state._id}
                    fieldId={this.state.editedFieldId}
                    onCancel={e => {
                        e.preventDefault();
                        this.setState({
                            fieldDialogActive: false,
                            editedFieldId: ""
                        });
                    }}
                    onSuccess={e => {
                        this.fetchContentType()
                            .then(() => {
                                this.setState({
                                    fieldDialogActive: false,
                                    editedFieldId: ""
                                });
                            })
                    }}
                />
                {this.state.notifyText &&
                  <Notify type="success" message={this.state.notifyText} />
                }
                <ContentTypeForm
                    isOpen={this.state.basicEditForm}
                    contentTypeId={this.state._id}
                    name={this.state.name}
                    slug={this.state.slug}
                    description={this.state.description}
                    onCancel={e => {
                        this.setState({
                            basicEditForm: false
                        })
                    }}
                    onSuccess={ct => {
                        this.setState({
                            basicEditForm: false,
                            notifyText: "Content Type Saved Successfully !"
                        })
                    }}
                    />
            </Fragment>
        );
    }
}

const mapStateToProps = (state) => {
    return {
        contentTypes: state.CONTENT.contentTypes
    };
};

const mapDispatchToProps = (dispatch) => {
    return bindActionCreators(actions, dispatch);
};

export default connect(mapStateToProps, mapDispatchToProps)(withDrafterbit(ContentType));<|endoftext|>


<commit_before>/*
 * ChorPro: library to parse ChorPro file and get Html or Text output
 * Version: 0.2
 * Date: 27/06/2015
 */

'use strict';

const _ = require('lodash');

function ChordPro() {
}

ChordPro.prototype.ITchordsDiesis = ["DO","DO#","RE","RE#","MI","FA","FA#","SOL","SOL#","LA","LA#","SI" ];
ChordPro.prototype.ITchordsBemolle = ["DO","REb","RE","MIb","MI","FA","SOLb","SOL","LAb","LA","SIb","SI" ];

ChordPro.prototype.ENchordsDiesis = ["C","C#","D","D#","E","F","F#","G","G#","A","A#","B" ];
ChordPro.prototype.ENchordsBemolle = ["C","Db","D","Eb","E","F","Gb","G","Ab","A","Bb","B" ];

ChordPro.prototype.parse = function(source, showChords) {
	var lineInfos = [];

	var chordRegex = /\[([a-zA-Z0-9#-/]+)\]/g;
	var directiveRegex = /^{.*}/;
	var commentRegex = /^#/;

	var chorus = false;

	source.split('\n').forEach(function (line) {

		var fragment;
		if (line.match(commentRegex)) {
			// TODO: handle comments
		}
		else if (line.match(directiveRegex)) {
			// TODO: handle directives

			// Remove curly brackets
			var testRegex = /{(.*?)}/;
			var match = line.match(testRegex);
			var content = match[1];

			if(content.indexOf('soc') === 0) {
				chorus = true;
			} else if(content.indexOf('eoc') === 0) {
				chorus = false;
			}
			else if(content.indexOf('title:') === 0 || content.indexOf('t:') === 0) {
				lineInfos.push({
					type: 'title',
					text: content.replace('title:','').replace('t:','')
				});
			} else if(content.indexOf('subtitle:') === 0 || content.indexOf('st:') === 0) {
				lineInfos.push({
					type: 'subtitle',
					text: content.replace('subtitle:','').replace('st:','')
				});
			}
		}
		else if (line.match(chordRegex)) {
			var lineInfo = {
				type: 'lyrics',
				lyrics: '',
				chords: [],
				chorus: chorus
			};

			var sourcePos = 0;
			var chordMatch;
			while ((chordMatch = chordRegex.exec(line))) {

				// append lyrics text ending at current match position
				fragment = line.substring(sourcePos, chordMatch.index);
				if (fragment) {
					lineInfo.lyrics += fragment;
				}

				// determine offset to apply if previous chord is too long and would overlap with current chord
				var offset = 0;
				if (lineInfo.chords.length > 0) {
					var previousChordInfo = lineInfo.chords[lineInfo.chords.length - 1];
					offset = Math.max(0, previousChordInfo.pos + previousChordInfo.chord.length + 1 - lineInfo.lyrics.length);
				}

				// pad lyrics field if needed
				for (var i = 0; i < offset; i++) {
					lineInfo.lyrics += ' ';
				}

				// add chord
				var sourceChord = chordMatch[0];
				if(showChords) {
					lineInfo.chords.push({
						pos: lineInfo.lyrics.length + (chorus ? 5 : 0),
						chord: sourceChord.replace(/[\[\]]/g, '')
					});
				}
				sourcePos = chordMatch.index + sourceChord.length;
			}

			// append lyrics text following last chord
			fragment = line.substring(sourcePos, line.length);
			if (fragment) {
				lineInfo.lyrics += fragment;
			}

			// trim end of lyrics text
			lineInfo.lyrics = lineInfo.lyrics.replace(/\s+$/, '');
            if(lineInfo.lyrics.length > 0 || lineInfo.chords.length > 0) {
				lineInfos.push(lineInfo);
            }
		}
		else {
          lineInfos.push({
            type: 'lyrics',
            lyrics: line,
            chorus: chorus,
            chords: []
          });
		}
	});

	return lineInfos;
};

ChordPro.prototype.addChord = function(line, chordInfo, options) {
	// pad the current line up to the chord position
	var strippedLine = line.replace(/(<([^>]+)>)/ig, '');
	var padding = chordInfo.pos - strippedLine.length;
	if (padding > 0) {
		line += new Array(padding + 1).join(' ');
	}

	var chord = chordInfo.chord;

	if(options.transpose !== 0) {

		var normalizedChord = chord.replace(/[0-9]/g, '').replace(/\+|\-/ig,'').replace('dim','');

		var chordFound = false;
		_.forEach(ChordPro.prototype.ITchordsDiesis, function(val) {
			if(normalizedChord === val) {
				var index = _.indexOf(ChordPro.prototype.ITchordsDiesis, val) + options.transpose;
				if(index < 0) {
					index = ChordPro.prototype.ITchordsDiesis.length + index;
				}
				chord = chord.replace(val,ChordPro.prototype.ITchordsDiesis[index % ChordPro.prototype.ITchordsDiesis.length]);
				chordFound = true;
				return false;
			}
		});
		if(!chordFound) {
			_.forEach(ChordPro.prototype.ITchordsBemolle, function(val) {
				if(normalizedChord === val) {

					var index = _.indexOf(ChordPro.prototype.ITchordsBemolle, val) + options.transpose;
					if(index < 0) {
						index = ChordPro.prototype.ITchordsBemolle.length - index;
					}

					chord = chord.replace(val,ChordPro.prototype.ITchordsBemolle[index % ChordPro.prototype.ITchordsBemolle.length]);
					return false;
				}
			});
		}
	}
	if (options.chordFormatter) {
		chord = options.chordFormatter(chord);
	}

	line += chord;
	return line;
};

ChordPro.prototype.formatLyricsEntry = function(entry, lineEnd, options) {

	var text = '';

	// add chord line if available
	if (entry.chords.length > 0) {
		var line = '';
		var self = this;
		entry.chords.forEach(function (chordInfo) {
			line = self.addChord(line, chordInfo, options);
		});

		text += line;
	}

	// add lyrics line if available, or empty line if no chords and no lyrics
	if (entry.lyrics.length > 0 || entry.chords.length === 0) {
		if (text.length > 0) {
			text += lineEnd;
		}

		text += entry.chorus ? '' + entry.lyrics.trim() + '' : entry.lyrics.trim();
	}

	return text;
};

ChordPro.prototype.format = function(source, newLine, options) {
	var parsed = this.parse(source, options.showChords);
	var text = '';
	var self = this;
	parsed.forEach(function (entry) {
		if (entry.type === 'lyrics') {
			if (text.length > 0) {
				text += newLine;
			}
			text += self.formatLyricsEntry(entry, newLine, options);
		} else if(entry.type === 'title') {
			text += '';
			if (options.songNumber !== null) {
				text += '' + options.songNumber + ' ';
			}
			text += entry.text.trim() + '';
		} else if(entry.type === 'subtitle') {
			text += '
' + entry.text.trim() + '';
		}
	});

	return text;
};

ChordPro.prototype.toText = function(source) {
	return this.format(source, '\n');
};

ChordPro.prototype.toHtml = function(source, options) {

	if (!options) {
		options = {};
	}
    if (!options.transpose) {
      options.transpose = 0;
    }
	if (!options.chordFormatter) {
		options.chordFormatter = function (chord) {
			return '' + chord + '';
		};
	}
	if(typeof options.showChords === 'undefined') {
		options.showChords = true;
	}
	var openingPre = '
';
	if (options.class) {
		openingPre = '
';
	}
	return openingPre + this.format(source, '
', options) + '
';
};

module.exports = ChordPro;
<commit_msg>Avoid two consecutive blank lines in chorpro parser<commit_after>/*
 * ChorPro: library to parse ChorPro file and get Html or Text output
 * Version: 0.2
 * Date: 27/06/2015
 */

'use strict';

const _ = require('lodash');

function ChordPro() {
}

ChordPro.prototype.ITchordsDiesis = ["DO","DO#","RE","RE#","MI","FA","FA#","SOL","SOL#","LA","LA#","SI" ];
ChordPro.prototype.ITchordsBemolle = ["DO","REb","RE","MIb","MI","FA","SOLb","SOL","LAb","LA","SIb","SI" ];

ChordPro.prototype.ENchordsDiesis = ["C","C#","D","D#","E","F","F#","G","G#","A","A#","B" ];
ChordPro.prototype.ENchordsBemolle = ["C","Db","D","Eb","E","F","Gb","G","Ab","A","Bb","B" ];

ChordPro.prototype.parse = function(source, showChords) {
	var lineInfos = [];

	var chordRegex = /\[([a-zA-Z0-9#-/]+)\]/g;
	var directiveRegex = /^{.*}/;
	var commentRegex = /^#/;

	var chorus = false;

	source.split('\n').forEach(function (line) {

		var fragment;
		if (line.match(commentRegex)) {
			// TODO: handle comments
		}
		else if (line.match(directiveRegex)) {
			// TODO: handle directives

			// Remove curly brackets
			var testRegex = /{(.*?)}/;
			var match = line.match(testRegex);
			var content = match[1];

			if(content.indexOf('soc') === 0) {
				chorus = true;
			} else if(content.indexOf('eoc') === 0) {
				chorus = false;
			}
			else if(content.indexOf('title:') === 0 || content.indexOf('t:') === 0) {
				lineInfos.push({
					type: 'title',
					text: content.replace('title:','').replace('t:','')
				});
			} else if(content.indexOf('subtitle:') === 0 || content.indexOf('st:') === 0) {
				lineInfos.push({
					type: 'subtitle',
					text: content.replace('subtitle:','').replace('st:','')
				});
			}
		}
		else if (line.match(chordRegex)) {
			var lineInfo = {
				type: 'lyrics',
				lyrics: '',
				chords: [],
				chorus: chorus
			};

			var sourcePos = 0;
			var chordMatch;
			while ((chordMatch = chordRegex.exec(line))) {

				// append lyrics text ending at current match position
				fragment = line.substring(sourcePos, chordMatch.index);
				if (fragment) {
					lineInfo.lyrics += fragment;
				}

				// determine offset to apply if previous chord is too long and would overlap with current chord
				var offset = 0;
				if (lineInfo.chords.length > 0) {
					var previousChordInfo = lineInfo.chords[lineInfo.chords.length - 1];
					offset = Math.max(0, previousChordInfo.pos + previousChordInfo.chord.length + 1 - lineInfo.lyrics.length);
				}

				// pad lyrics field if needed
				for (var i = 0; i < offset; i++) {
					lineInfo.lyrics += ' ';
				}

				// add chord
				var sourceChord = chordMatch[0];
				if(showChords) {
					lineInfo.chords.push({
						pos: lineInfo.lyrics.length + (chorus ? 5 : 0),
						chord: sourceChord.replace(/[\[\]]/g, '')
					});
				}
				sourcePos = chordMatch.index + sourceChord.length;
			}

			// append lyrics text following last chord
			fragment = line.substring(sourcePos, line.length);
			if (fragment) {
				lineInfo.lyrics += fragment;
			}

			// trim end of lyrics text
			lineInfo.lyrics = lineInfo.lyrics.replace(/\s+$/, '');
            if(lineInfo.lyrics.length > 0 || lineInfo.chords.length > 0) {
				lineInfos.push(lineInfo);
            }
		}
		else {
          lineInfos.push({
            type: 'lyrics',
            lyrics: line,
            chorus: chorus,
            chords: []
          });
		}
	});

	return lineInfos;
};

ChordPro.prototype.addChord = function(line, chordInfo, options) {
	// pad the current line up to the chord position
	var strippedLine = line.replace(/(<([^>]+)>)/ig, '');
	var padding = chordInfo.pos - strippedLine.length;
	if (padding > 0) {
		line += new Array(padding + 1).join(' ');
	}

	var chord = chordInfo.chord;

	if(options.transpose !== 0) {

		var normalizedChord = chord.replace(/[0-9]/g, '').replace(/\+|\-/ig,'').replace('dim','');

		var chordFound = false;
		_.forEach(ChordPro.prototype.ITchordsDiesis, function(val) {
			if(normalizedChord === val) {
				var index = _.indexOf(ChordPro.prototype.ITchordsDiesis, val) + options.transpose;
				if(index < 0) {
					index = ChordPro.prototype.ITchordsDiesis.length + index;
				}
				chord = chord.replace(val,ChordPro.prototype.ITchordsDiesis[index % ChordPro.prototype.ITchordsDiesis.length]);
				chordFound = true;
				return false;
			}
		});
		if(!chordFound) {
			_.forEach(ChordPro.prototype.ITchordsBemolle, function(val) {
				if(normalizedChord === val) {

					var index = _.indexOf(ChordPro.prototype.ITchordsBemolle, val) + options.transpose;
					if(index < 0) {
						index = ChordPro.prototype.ITchordsBemolle.length - index;
					}

					chord = chord.replace(val,ChordPro.prototype.ITchordsBemolle[index % ChordPro.prototype.ITchordsBemolle.length]);
					return false;
				}
			});
		}
	}
	if (options.chordFormatter) {
		chord = options.chordFormatter(chord);
	}

	line += chord;
	return line;
};

ChordPro.prototype.formatLyricsEntry = function(entry, lineEnd, options) {

	var text = '';

	// add chord line if available
	if (entry.chords.length > 0) {
		var line = '';
		var self = this;
		entry.chords.forEach(function (chordInfo) {
			line = self.addChord(line, chordInfo, options);
		});

		text += line;
	}

	// add lyrics line if available, or empty line if no chords and no lyrics
	if (entry.lyrics.length > 0 || entry.chords.length === 0) {
		if (text.length > 0) {
			text += lineEnd;
		}

		text += entry.chorus ? '' + entry.lyrics.trim() + '' : entry.lyrics.trim();
	}

	return text;
};

ChordPro.prototype.format = function(source, newLine, options) {
	var parsed = this.parse(source, options.showChords);
	var text = '';
	var self = this;
	parsed.forEach(function (entry) {
		if (entry.type === 'lyrics') {
			// Pay attention to not put double consecutive newLines
			if (text.length > 0 && !text.endsWith(newLine + newLine)) {
				text += newLine;
			}
			text += self.formatLyricsEntry(entry, newLine, options);
		} else if(entry.type === 'title') {
			text += '';
			if (options.songNumber !== null) {
				text += '' + options.songNumber + ' ';
			}
			text += entry.text.trim() + '';
		} else if(entry.type === 'subtitle') {
			text += '
' + entry.text.trim() + '';
		}
	});

	return text;
};

ChordPro.prototype.toText = function(source) {
	return this.format(source, '\n');
};

ChordPro.prototype.toHtml = function(source, options) {

	if (!options) {
		options = {};
	}
    if (!options.transpose) {
      options.transpose = 0;
    }
	if (!options.chordFormatter) {
		options.chordFormatter = function (chord) {
			return '' + chord + '';
		};
	}
	if(typeof options.showChords === 'undefined') {
		options.showChords = true;
	}
	var openingPre = '
';
	if (options.class) {
		openingPre = '
';
	}
	return openingPre + this.format(source, '
', options) + '
';
};

module.exports = ChordPro;
<|endoftext|>