<commit_before>import React, { Component } from 'react';
import PropTypes from 'prop-types';
import { isEqual, throttle } from 'lodash';
import { FormattedMessage } from 'react-intl';
import Headline from '@folio/stripes-components/lib/Headline';
import { Dropdown } from '@folio/stripes-components/lib/Dropdown'; // eslint-disable-line
import { withRouter } from 'react-router';
import localforage from 'localforage';

import { withModules } from '../Modules';
import { clearOkapiToken, clearCurrentUser } from '../../okapiActions';
import { resetStore } from '../../mainActions';
import { updateQueryResource, updateLocation } from '../../locationService';

import css from './MainNav.css';
import NavButton from './NavButton';
import NavDivider from './NavDivider';
import NavGroup from './NavGroup';
import Breadcrumbs from './Breadcrumbs';
import CurrentApp from './CurrentApp';
import MyProfile from './MyProfile';
import NotificationsDropdown from './Notifications/NotificationsDropdown';
import settingsIcon from './settings.svg';

class MainNav extends Component {
  static propTypes = {
    stripes: PropTypes.shape({
      config: PropTypes.shape({
        showPerms: PropTypes.bool,
      }),
      store: PropTypes.shape({
        dispatch: PropTypes.func.isRequired,
      }),
      hasPerm: PropTypes.func.isRequired,
      withOkapi: PropTypes.bool,
    }),
    history: PropTypes.shape({
      listen: PropTypes.func.isRequired,
      replace: PropTypes.func.isRequired,
      push: PropTypes.func.isRequired,
    }).isRequired,
    location: PropTypes.shape({
      pathname: PropTypes.string,
    }).isRequired,
    modules: PropTypes.shape({
      app: PropTypes.array,
    })
  };

  static contextTypes = {
    router: PropTypes.object.isRequired,
  }

  static childContextTypes = {
    // It seems wrong that we have to tell this generic component what specific properties to put in the context
    stripes: PropTypes.object,
  };

  constructor(props) {
    super(props);
    this.state = {
      userMenuOpen: false,
    };
    this.store = props.stripes.store;
    this.logout = this.logout.bind(this);
    this.lastVisited = {};
    this.queryValues = null;
    this.updateQueryResource = throttle(updateQueryResource, 50, { 'leading': true });
    this.moduleList = props.modules.app.concat({
      route: '/settings',
      module: '@folio/x_settings',
    });

    props.history.listen((hist) => {
      for (const entry of this.moduleList) {
        if (hist.pathname === entry.route || hist.pathname.startsWith(`${entry.route}/`)) {
          const name = entry.module.replace(/^@folio\//, '');
          this.lastVisited[name] = `${hist.pathname}${hist.search}`;
        }
      }
    });
  }

  getChildContext() {
    return {
      stripes: this.props.stripes,
    };
  }

  componentDidMount() {
    let curQuery = null;
    this.store.subscribe(() => {
      const { history, location } = this.props;
      const module = this.curModule;
      if (module && location.pathname.startsWith(module.route)) {
        curQuery = updateLocation(module, curQuery, this.store, history, location);
      }
    });
  }

  componentDidUpdate(prevProps) {
    const { location } = this.props;
    this.curModule = this.moduleList.find(m => location.pathname.startsWith(m.route) && m.queryResource);
    if (this.curModule && !isEqual(location, prevProps.location)) {
      this.updateQueryResource(location, this.curModule, this.store);
    }
  }

  toggleUserMenu() {
    const isOpen = this.state.userMenuOpen;
    this.setState({
      userMenuOpen: !isOpen,
    });
  }

  logout() {
    this.store.dispatch(clearOkapiToken());
    this.store.dispatch(clearCurrentUser());
    this.store.dispatch(resetStore());
    localforage.removeItem('okapiSess');
    this.context.router.history.push('/');
  }

  render() {
    const { stripes, location: { pathname }, modules } = this.props;
    const formatMsg = stripes.intl.formatMessage;

    // Temporary until settings becomes an app
    const settingsIconData = {
      src: settingsIcon,
      alt: 'Tenant Settings',
      title: formatMsg({ id: 'stripes-core.settings' }),
    };

    // const stripesApps = modules.app;
    const selectedApp = modules.app.find(entry => pathname.startsWith(entry.route));
    const menuLinks = modules.app.map((entry) => {
      const name = entry.module.replace(/^@[a-z0-9_]+\//, '');
      const perm = `module.${name}.enabled`;

      if (!stripes.hasPerm(perm)) {
        return null;
      }

      const navId = `clickable-${name}-module`;
      const isActive = pathname.startsWith(entry.route);
      const href = !isActive ? (this.lastVisited[name] || entry.home || entry.route) : null;

      return (
        <NavButton
          label={entry.displayName}
          id={navId}
          selected={isActive}
          href={href}
          title={entry.displayName}
          key={entry.route}
          iconKey={name}
        />);
    });

    let firstNav;
    let breadcrumbArray = []; // eslint-disable-line

    // Temporary solution until Settings becomes a standalone app
    let settingsApp;
    const settingsMsg = formatMsg({ id: 'stripes-core.settings' });
    if (stripes.hasPerm('settings.enabled') && pathname.startsWith('/settings')) {
      settingsApp = { displayName: settingsMsg, description: formatMsg({ id: 'stripes-core.folioSettings' }) };
    }

    if (breadcrumbArray.length === 0) {
      firstNav = (
        <NavGroup md="hide">

            <svg xmlns="http://www.w3.org/2000/svg" width="26" height="26" viewBox="0 0 26 26">
              <polygon style={{ fill: '#999' }} points="13 16.5 1.2 5.3 3.2 3.1 13 12.4 22.8 3.1 24.8 5.3 " />
              <polygon style={{ fill: '#999' }} points="13 24.8 1.2 13.5 3.2 11.3 13 20.6 22.8 11.3 24.8 13.5 " />
            </svg>

          <CurrentApp
            id="ModuleMainHeading"
            currentApp={selectedApp || settingsApp}
            iconData={settingsApp && settingsIconData}
          />
        </NavGroup>
      );
    } else {
      firstNav = (
        <NavGroup>
          <NavButton md="hide" />
          <Breadcrumbs linkArray={breadcrumbArray} />
        </NavGroup>
      );
    }

    return (

        {firstNav}

          <Headline tag="h2" className="sr-only">
            <FormattedMessage id="stripes-core.mainNavigation" />
          </Headline>
          <NavGroup>
            <NavGroup>
              {menuLinks}
              {
                !stripes.hasPerm('settings.enabled') ? '' : (
                  <NavButton
                    label={settingsMsg}
                    id="clickable-settings"
                    title={settingsMsg}
                    iconData={settingsIconData}
                    selected={pathname.startsWith('/settings')}
                    href={pathname.startsWith('/settings') ? null : (this.lastVisited.x_settings || '/settings')}
                  />
                )
              }
            </NavGroup>
            <NavGroup className={css.smallAlignRight}>
              <NavDivider md="hide" />
              {this.props.stripes.withOkapi && this.props.stripes.hasPerm('notify.item.get,notify.item.put,notify.collection.get') && <NotificationsDropdown stripes={stripes} {...this.props} />}
              { /* temporary divider solution.. */}
              {this.props.stripes.hasPerm('notify.item.get,notify.item.put,notify.collection.get') && (<NavDivider md="hide" />)}
              <MyProfile
                onLogout={this.logout}
                stripes={stripes}
              />
            </NavGroup>
          </NavGroup>

    );
  }
}

export default withRouter(withModules(MainNav));
<commit_msg>remove throttle on updateQueryResource, refs UICHKOUT-425 (#339)<commit_after>import React, { Component } from 'react';
import PropTypes from 'prop-types';
import { isEqual } from 'lodash';
import { FormattedMessage } from 'react-intl';
import Headline from '@folio/stripes-components/lib/Headline';
import { Dropdown } from '@folio/stripes-components/lib/Dropdown'; // eslint-disable-line
import { withRouter } from 'react-router';
import localforage from 'localforage';

import { withModules } from '../Modules';
import { clearOkapiToken, clearCurrentUser } from '../../okapiActions';
import { resetStore } from '../../mainActions';
import { updateQueryResource, updateLocation } from '../../locationService';

import css from './MainNav.css';
import NavButton from './NavButton';
import NavDivider from './NavDivider';
import NavGroup from './NavGroup';
import Breadcrumbs from './Breadcrumbs';
import CurrentApp from './CurrentApp';
import MyProfile from './MyProfile';
import NotificationsDropdown from './Notifications/NotificationsDropdown';
import settingsIcon from './settings.svg';

class MainNav extends Component {
  static propTypes = {
    stripes: PropTypes.shape({
      config: PropTypes.shape({
        showPerms: PropTypes.bool,
      }),
      store: PropTypes.shape({
        dispatch: PropTypes.func.isRequired,
      }),
      hasPerm: PropTypes.func.isRequired,
      withOkapi: PropTypes.bool,
    }),
    history: PropTypes.shape({
      listen: PropTypes.func.isRequired,
      replace: PropTypes.func.isRequired,
      push: PropTypes.func.isRequired,
    }).isRequired,
    location: PropTypes.shape({
      pathname: PropTypes.string,
    }).isRequired,
    modules: PropTypes.shape({
      app: PropTypes.array,
    })
  };

  static contextTypes = {
    router: PropTypes.object.isRequired,
  }

  static childContextTypes = {
    // It seems wrong that we have to tell this generic component what specific properties to put in the context
    stripes: PropTypes.object,
  };

  constructor(props) {
    super(props);
    this.state = {
      userMenuOpen: false,
    };
    this.store = props.stripes.store;
    this.logout = this.logout.bind(this);
    this.lastVisited = {};
    this.queryValues = null;
    this.moduleList = props.modules.app.concat({
      route: '/settings',
      module: '@folio/x_settings',
    });

    props.history.listen((hist) => {
      for (const entry of this.moduleList) {
        if (hist.pathname === entry.route || hist.pathname.startsWith(`${entry.route}/`)) {
          const name = entry.module.replace(/^@folio\//, '');
          this.lastVisited[name] = `${hist.pathname}${hist.search}`;
        }
      }
    });
  }

  getChildContext() {
    return {
      stripes: this.props.stripes,
    };
  }

  componentDidMount() {
    let curQuery = null;
    this.store.subscribe(() => {
      const { history, location } = this.props;
      const module = this.curModule;
      if (module && location.pathname.startsWith(module.route)) {
        curQuery = updateLocation(module, curQuery, this.store, history, location);
      }
    });
  }

  componentDidUpdate(prevProps) {
    const { location } = this.props;
    this.curModule = this.moduleList.find(m => location.pathname.startsWith(m.route) && m.queryResource);
    if (this.curModule && !isEqual(location, prevProps.location)) {
      updateQueryResource(location, this.curModule, this.store);
    }
  }

  toggleUserMenu() {
    const isOpen = this.state.userMenuOpen;
    this.setState({
      userMenuOpen: !isOpen,
    });
  }

  logout() {
    this.store.dispatch(clearOkapiToken());
    this.store.dispatch(clearCurrentUser());
    this.store.dispatch(resetStore());
    localforage.removeItem('okapiSess');
    this.context.router.history.push('/');
  }

  render() {
    const { stripes, location: { pathname }, modules } = this.props;
    const formatMsg = stripes.intl.formatMessage;

    // Temporary until settings becomes an app
    const settingsIconData = {
      src: settingsIcon,
      alt: 'Tenant Settings',
      title: formatMsg({ id: 'stripes-core.settings' }),
    };

    // const stripesApps = modules.app;
    const selectedApp = modules.app.find(entry => pathname.startsWith(entry.route));
    const menuLinks = modules.app.map((entry) => {
      const name = entry.module.replace(/^@[a-z0-9_]+\//, '');
      const perm = `module.${name}.enabled`;

      if (!stripes.hasPerm(perm)) {
        return null;
      }

      const navId = `clickable-${name}-module`;
      const isActive = pathname.startsWith(entry.route);
      const href = !isActive ? (this.lastVisited[name] || entry.home || entry.route) : null;

      return (
        <NavButton
          label={entry.displayName}
          id={navId}
          selected={isActive}
          href={href}
          title={entry.displayName}
          key={entry.route}
          iconKey={name}
        />);
    });

    let firstNav;
    let breadcrumbArray = []; // eslint-disable-line

    // Temporary solution until Settings becomes a standalone app
    let settingsApp;
    const settingsMsg = formatMsg({ id: 'stripes-core.settings' });
    if (stripes.hasPerm('settings.enabled') && pathname.startsWith('/settings')) {
      settingsApp = { displayName: settingsMsg, description: formatMsg({ id: 'stripes-core.folioSettings' }) };
    }

    if (breadcrumbArray.length === 0) {
      firstNav = (
        <NavGroup md="hide">

            <svg xmlns="http://www.w3.org/2000/svg" width="26" height="26" viewBox="0 0 26 26">
              <polygon style={{ fill: '#999' }} points="13 16.5 1.2 5.3 3.2 3.1 13 12.4 22.8 3.1 24.8 5.3 " />
              <polygon style={{ fill: '#999' }} points="13 24.8 1.2 13.5 3.2 11.3 13 20.6 22.8 11.3 24.8 13.5 " />
            </svg>

          <CurrentApp
            id="ModuleMainHeading"
            currentApp={selectedApp || settingsApp}
            iconData={settingsApp && settingsIconData}
          />
        </NavGroup>
      );
    } else {
      firstNav = (
        <NavGroup>
          <NavButton md="hide" />
          <Breadcrumbs linkArray={breadcrumbArray} />
        </NavGroup>
      );
    }

    return (

        {firstNav}

          <Headline tag="h2" className="sr-only">
            <FormattedMessage id="stripes-core.mainNavigation" />
          </Headline>
          <NavGroup>
            <NavGroup>
              {menuLinks}
              {
                !stripes.hasPerm('settings.enabled') ? '' : (
                  <NavButton
                    label={settingsMsg}
                    id="clickable-settings"
                    title={settingsMsg}
                    iconData={settingsIconData}
                    selected={pathname.startsWith('/settings')}
                    href={pathname.startsWith('/settings') ? null : (this.lastVisited.x_settings || '/settings')}
                  />
                )
              }
            </NavGroup>
            <NavGroup className={css.smallAlignRight}>
              <NavDivider md="hide" />
              {this.props.stripes.withOkapi && this.props.stripes.hasPerm('notify.item.get,notify.item.put,notify.collection.get') && <NotificationsDropdown stripes={stripes} {...this.props} />}
              { /* temporary divider solution.. */}
              {this.props.stripes.hasPerm('notify.item.get,notify.item.put,notify.collection.get') && (<NavDivider md="hide" />)}
              <MyProfile
                onLogout={this.logout}
                stripes={stripes}
              />
            </NavGroup>
          </NavGroup>

    );
  }
}

export default withRouter(withModules(MainNav));
<|endoftext|>


<commit_before>const {inspect} = require("util");

module.exports = async (Bot, client, message) => {
    // It's good practice to ignore other bots. This also makes your bot ignore itself
    // and not get into a spam loop (we call that "botception").
    if (message?.author?.bot || message?.user?.bot) return;

    // Grab the settings for this server
    // If there is no guild, get default conf (DMs)
    let guildSettings;
    if (!message.guild) {
        guildSettings = Bot.config.defaultSettings;
    } else {
        guildSettings = await Bot.getGuildConf(message.guild.id);
    }

    // If we don't have permission to respond, don't bother
    if (message.guild && message.channel.permissionsFor(message.guild.me) && !message.channel.permissionsFor(message.guild.me).has("SEND_MESSAGES")) return;

    // For ease of use in commands and functions, we'll attach the settings
    // to the message object, so `message.guildSettings` is accessible.
    message.guildSettings = guildSettings;

    // If the message is just mentioning the bot, tell them what the prefix is
    if (message.content === client.user.toString() || (message.guild && typeof message.guild.me !== "undefined" && message.content === message.guild.me.toString())) {
        return message.channel.send(`The prefix is \`${message.guildSettings.prefix}\`.`);
    }

    // https://discordjs.guide/popular-topics/miscellaneous-examples.html#mention-prefix
    // Used to convert special characters into literal characters by escaping them, so that they don't terminate the pattern within the regex
    const escapeRegex = str => str.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
    const prefixRegex = new RegExp(`^(<@!?${client.user.id}>|${escapeRegex(message.guildSettings.prefix)})\\s*`);
    if (!prefixRegex.test(message.content)) return;
    const [, matchedPrefix] = message.content.match(prefixRegex);

    // Also good practice to ignore any message that does not start with our prefix, which is set in the configuration file.
    if (!matchedPrefix || message.content.indexOf(matchedPrefix) !== 0) return;

    // Splits on line returns, then on spaces to preserve the line returns
    const nArgs = message.content.slice(matchedPrefix.length).trim().split(/(\n+)/);
    let args = [];
    nArgs.forEach(line => {
        const ne = line.split(" ").filter(String);
        args = args.concat(ne);
    });

    // Get the command name/ remove it from the args
    const command = args && args.length ? args.shift().toLowerCase() : null;
    if (!command) return;

    // Get the user or member's permission level from the elevation
    const level = await Bot.permLevel(message);

    // Check whether the command, or alias, exist in the collections defined in swgohbot.js.
    const cmd = client.commands.get(command) || client.commands.get(client.aliases.get(command));

    if (!cmd || !cmd.conf.enabled) return;

    const user = await Bot.userReg.getUser(message.author.id);

    // Load the language file for whatever language they have set
    if (user && user.lang) {
        if (user.lang.language) {
            message.guildSettings.language = user.lang.language || Bot.config.defaultSettings.language;
        }
        if (user.lang.swgohLanguage) {
            message.guildSettings.swgohLanguage = user.lang.swgohLanguage || Bot.config.defaultSettings.swgohLanguage;
        }
    }
    message.language = Bot.languages[message.guildSettings.language] || Bot.languages[Bot.config.defaultSettings.language];
    message.swgohLanguage = message.guildSettings.swgohLanguage || Bot.config.defaultSettings.swgohLanguage;

    // Some commands may not be useable in DMs. This check prevents those commands from running
    // and return a friendly error message.
    if (cmd && !message.guild && cmd.conf.guildOnly) {
        return message.channel.send(message.language.get("BASE_COMMAND_UNAVAILABLE"));
    }

    // If the command exists, **AND** the user has permission, run it.
    if (cmd && level >= cmd.conf.permLevel) {
        if (message.guild) {
            const defPerms = ["SEND_MESSAGES", "VIEW_CHANNEL", "EMBED_LINKS"];

            // Merge the permission arrays to make sure it has at least the minimum
            const perms = [...new Set([...defPerms, ...cmd.conf.permissions])];
            if (message.guild && message.channel && !message.channel.permissionsFor(message.guild.me).has(perms)) {
                const missingPerms = message.channel.permissionsFor(message.guild.me).missing(perms);

                if (missingPerms.length > 0) {
                    // If it can't send messages, don't bother trying
                    if (missingPerms.includes("VIEW_CHANNEL") || missingPerms.includes("SEND_MESSAGES")) return;
                    // Make it more readable
                    missingPerms.forEach((p, ix) => {missingPerms[ix] = Bot.toProperCase(p.replace("_", " "));});
                    return message.channel.send(`This bot is missing the following permissions to run this command here: \`${missingPerms.join(", ")}\``);
                }
            }
        }

        let flagArgs = getFlags(cmd.conf.flags, cmd.conf.subArgs, args);

        const noFlags   = Object.keys(flagArgs.flags).every(f => !flagArgs.flags[f]);
        const noSubArgs = Object.keys(flagArgs.subArgs).every(s => !flagArgs.subArgs[s]);
        let def = null;
        if (noFlags && noSubArgs && user) {
            if (user.defaults[cmd.help.name]) {
                flagArgs = getFlags(cmd.conf.flags, cmd.conf.subArgs, args.concat(user.defaults[cmd.help.name].split(" ")));
                def = user.defaults[cmd.help.name];
            }
        }

        // Quick shortcut to any extra ally codes you have registered
        // -1 for the first in the list, -2 for the second, etc.
        const toRep = args.filter(a => a.match(/^-\d{1,2}$/));
        if (toRep.length) {
            const ix = args.indexOf(toRep[0]);
            const jx = parseInt(args[ix].replace("-", ""), 10)-1;
            if (user && user.accounts && user.accounts.length && user.accounts.length > jx && jx >= 0) {
                args[ix] = user.accounts[jx].allyCode;
            }
        }

        // If they're just looking for the help, don't bother going through the command
        try {
            if (cmd.help.name !== "test" && cmd.help.category !== "Dev") {
                await message.channel.send(`>>> Non-slash (\`${message.guildSettings.prefix}\`) commands will be disabled at the beginning of April. \n\nhttps://support-dev.discord.com/hc/en-us/articles/4404772028055\n\nPlease move over to slash commands if able.\nPlease report any issues with the slash commands and I'll try to fix it right away.`);
            }
            await cmd.run(Bot, message, args, {
                level: level,
                flags: flagArgs.flags,
                subArgs: flagArgs.subArgs,
                defaults: def
            });
        } catch (err) {
            // Ignore specific annoying errors that I can't do anything about
            if (err.stack.toString().includes("Internal Server Error")) return;
            if (cmd.help.name === "test") {
                console.log(`ERROR(msg) I broke with ${cmd.help.name}: \nContent: ${message.content} \n${inspect(err)}`, true);
            } else {
                Bot.logger.error(`ERROR(msg) I broke with ${cmd.help.name}: \nContent: ${message.content} \n${inspect(err, {depth: 5})}`, true);
            }
        }
        if (Bot.config.logs.logComs) {
            Bot.database.models.commands.create({
                id: `${cmd.help.name}-${message.author.id}-${message.id}`,
                commandText: args.join(" ")
            });
        }
    }
};

// Checks for any args that start with - or -- that match what the command is looking for
function checkForArgs(key, args) {
    if (args.includes("-"+key)) {
        return [true, "-"+key];
    } else if (args.includes("--"+key)) {
        return [true, "--"+key];
    } else {
        return [false, null];
    }
}

// Get the flags and extra args for the command
function getFlags(cFlags, cSubArgs, args) {
    const flags = {};
    const subArgs = {};

    const flagKeys = Object.keys(cFlags);
    flagKeys.forEach(key => {
        let found = checkForArgs(key, args);
        flags[key] = false;
        if (!found[0]) {
            for (let ix = 0; ix < cFlags[key].aliases.length; ix++) {
                found = checkForArgs(cFlags[key].aliases[ix], args);
                if (found[0]) break;
            }
        }
        if (found[0]) {
            args.splice(args.indexOf(found[1]), 1);
            flags[key] = true;
        }
    });

    const subKeys = Object.keys(cSubArgs);
    subKeys.forEach(key => {
        let found = checkForArgs(key, args);
        if (!found[0]) {
            for (let ix = 0; ix < cSubArgs[key].aliases.length; ix++) {
                found = checkForArgs(cSubArgs[key].aliases[ix], args);
                if (found[0]) {
                    break;
                }
            }
        }
        if (found[0]) {
            const res = args.splice(args.indexOf(found[1]), 2);
            subArgs[key] = res[1] ? res[1] : null;
        } else {
            subArgs[key] = cSubArgs[key].default ? cSubArgs[key].default : null;
        }
    });

    return {
        subArgs: subArgs,
        flags: flags
    };
}
<commit_msg>Add a command response to push people to move to the slash command versions<commit_after>const {inspect} = require("util");

module.exports = async (Bot, client, message) => {
    // It's good practice to ignore other bots. This also makes your bot ignore itself
    // and not get into a spam loop (we call that "botception").
    if (message?.author?.bot || message?.user?.bot) return;

    // Grab the settings for this server
    // If there is no guild, get default conf (DMs)
    let guildSettings;
    if (!message.guild) {
        guildSettings = Bot.config.defaultSettings;
    } else {
        guildSettings = await Bot.getGuildConf(message.guild.id);
    }

    // If we don't have permission to respond, don't bother
    if (message.guild && message.channel.permissionsFor(message.guild.me) && !message.channel.permissionsFor(message.guild.me).has("SEND_MESSAGES")) return;

    // For ease of use in commands and functions, we'll attach the settings
    // to the message object, so `message.guildSettings` is accessible.
    message.guildSettings = guildSettings;

    // If the message is just mentioning the bot, tell them what the prefix is
    if (message.content === client.user.toString() || (message.guild && typeof message.guild.me !== "undefined" && message.content === message.guild.me.toString())) {
        return message.channel.send(`The prefix is \`${message.guildSettings.prefix}\`.`);
    }

    // https://discordjs.guide/popular-topics/miscellaneous-examples.html#mention-prefix
    // Used to convert special characters into literal characters by escaping them, so that they don't terminate the pattern within the regex
    const escapeRegex = str => str.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
    const prefixRegex = new RegExp(`^(<@!?${client.user.id}>|${escapeRegex(message.guildSettings.prefix)})\\s*`);
    if (!prefixRegex.test(message.content)) return;
    const [, matchedPrefix] = message.content.match(prefixRegex);

    // Also good practice to ignore any message that does not start with our prefix, which is set in the configuration file.
    if (!matchedPrefix || message.content.indexOf(matchedPrefix) !== 0) return;

    // Splits on line returns, then on spaces to preserve the line returns
    const nArgs = message.content.slice(matchedPrefix.length).trim().split(/(\n+)/);
    let args = [];
    nArgs.forEach(line => {
        const ne = line.split(" ").filter(String);
        args = args.concat(ne);
    });

    // Get the command name/ remove it from the args
    const command = args && args.length ? args.shift().toLowerCase() : null;
    if (!command) return;

    // Get the user or member's permission level from the elevation
    const level = await Bot.permLevel(message);

    // Check whether the command, or alias, exist in the collections defined in swgohbot.js.
    const cmd = client.commands.get(command) || client.commands.get(client.aliases.get(command));

    if (!cmd || !cmd.conf.enabled) return;

    const user = await Bot.userReg.getUser(message.author.id);

    // Load the language file for whatever language they have set
    if (user && user.lang) {
        if (user.lang.language) {
            message.guildSettings.language = user.lang.language || Bot.config.defaultSettings.language;
        }
        if (user.lang.swgohLanguage) {
            message.guildSettings.swgohLanguage = user.lang.swgohLanguage || Bot.config.defaultSettings.swgohLanguage;
        }
    }
    message.language = Bot.languages[message.guildSettings.language] || Bot.languages[Bot.config.defaultSettings.language];
    message.swgohLanguage = message.guildSettings.swgohLanguage || Bot.config.defaultSettings.swgohLanguage;

    // Some commands may not be useable in DMs. This check prevents those commands from running
    // and return a friendly error message.
    if (cmd && !message.guild && cmd.conf.guildOnly) {
        return message.channel.send(message.language.get("BASE_COMMAND_UNAVAILABLE"));
    }

    // Reply with a message to use the slash command instead for certain commands that are definitely working
    const useSlash = ["acronyms", "activities", "challenges", "info", "mods", "modsets", "time", "whois"];
    if (useSlash.includes(cmd.help.name)) {
        return message.channel.send({content: `This command has been deprecated, please use the slash command \`/${cmd.help.name}\``});
    }

    // If the command exists, **AND** the user has permission, run it.
    if (cmd && level >= cmd.conf.permLevel) {
        if (message.guild) {
            const defPerms = ["SEND_MESSAGES", "VIEW_CHANNEL", "EMBED_LINKS"];

            // Merge the permission arrays to make sure it has at least the minimum
            const perms = [...new Set([...defPerms, ...cmd.conf.permissions])];
            if (message.guild && message.channel && !message.channel.permissionsFor(message.guild.me).has(perms)) {
                const missingPerms = message.channel.permissionsFor(message.guild.me).missing(perms);

                if (missingPerms.length > 0) {
                    // If it can't send messages, don't bother trying
                    if (missingPerms.includes("VIEW_CHANNEL") || missingPerms.includes("SEND_MESSAGES")) return;
                    // Make it more readable
                    missingPerms.forEach((p, ix) => {missingPerms[ix] = Bot.toProperCase(p.replace("_", " "));});
                    return message.channel.send(`This bot is missing the following permissions to run this command here: \`${missingPerms.join(", ")}\``);
                }
            }
        }

        let flagArgs = getFlags(cmd.conf.flags, cmd.conf.subArgs, args);

        const noFlags   = Object.keys(flagArgs.flags).every(f => !flagArgs.flags[f]);
        const noSubArgs = Object.keys(flagArgs.subArgs).every(s => !flagArgs.subArgs[s]);
        let def = null;
        if (noFlags && noSubArgs && user) {
            if (user.defaults[cmd.help.name]) {
                flagArgs = getFlags(cmd.conf.flags, cmd.conf.subArgs, args.concat(user.defaults[cmd.help.name].split(" ")));
                def = user.defaults[cmd.help.name];
            }
        }

        // Quick shortcut to any extra ally codes you have registered
        // -1 for the first in the list, -2 for the second, etc.
        const toRep = args.filter(a => a.match(/^-\d{1,2}$/));
        if (toRep.length) {
            const ix = args.indexOf(toRep[0]);
            const jx = parseInt(args[ix].replace("-", ""), 10)-1;
            if (user && user.accounts && user.accounts.length && user.accounts.length > jx && jx >= 0) {
                args[ix] = user.accounts[jx].allyCode;
            }
        }

        // If they're just looking for the help, don't bother going through the command
        try {
            // if (cmd.help.name !== "test" && cmd.help.category !== "Dev") {
            //     await message.channel.send(`>>> Non-slash (\`${message.guildSettings.prefix}\`) commands will be disabled at the beginning of April. \n\nhttps://support-dev.discord.com/hc/en-us/articles/4404772028055\n\nPlease move over to slash commands if able.\nPlease report any issues with the slash commands and I'll try to fix it right away.`);
            // }
            await cmd.run(Bot, message, args, {
                level: level,
                flags: flagArgs.flags,
                subArgs: flagArgs.subArgs,
                defaults: def
            });
        } catch (err) {
            // Ignore specific annoying errors that I can't do anything about
            if (err.stack.toString().includes("Internal Server Error")) return;
            if (cmd.help.name === "test") {
                console.log(`ERROR(msg) I broke with ${cmd.help.name}: \nContent: ${message.content} \n${inspect(err)}`, true);
            } else {
                Bot.logger.error(`ERROR(msg) I broke with ${cmd.help.name}: \nContent: ${message.content} \n${inspect(err, {depth: 5})}`, true);
            }
        }
        if (Bot.config.logs.logComs) {
            Bot.database.models.commands.create({
                id: `${cmd.help.name}-${message.author.id}-${message.id}`,
                commandText: args.join(" ")
            });
        }
    }
};

// Checks for any args that start with - or -- that match what the command is looking for
function checkForArgs(key, args) {
    if (args.includes("-"+key)) {
        return [true, "-"+key];
    } else if (args.includes("--"+key)) {
        return [true, "--"+key];
    } else {
        return [false, null];
    }
}

// Get the flags and extra args for the command
function getFlags(cFlags, cSubArgs, args) {
    const flags = {};
    const subArgs = {};

    const flagKeys = Object.keys(cFlags);
    flagKeys.forEach(key => {
        let found = checkForArgs(key, args);
        flags[key] = false;
        if (!found[0]) {
            for (let ix = 0; ix < cFlags[key].aliases.length; ix++) {
                found = checkForArgs(cFlags[key].aliases[ix], args);
                if (found[0]) break;
            }
        }
        if (found[0]) {
            args.splice(args.indexOf(found[1]), 1);
            flags[key] = true;
        }
    });

    const subKeys = Object.keys(cSubArgs);
    subKeys.forEach(key => {
        let found = checkForArgs(key, args);
        if (!found[0]) {
            for (let ix = 0; ix < cSubArgs[key].aliases.length; ix++) {
                found = checkForArgs(cSubArgs[key].aliases[ix], args);
                if (found[0]) {
                    break;
                }
            }
        }
        if (found[0]) {
            const res = args.splice(args.indexOf(found[1]), 2);
            subArgs[key] = res[1] ? res[1] : null;
        } else {
            subArgs[key] = cSubArgs[key].default ? cSubArgs[key].default : null;
        }
    });

    return {
        subArgs: subArgs,
        flags: flags
    };
}
<|endoftext|>


<commit_before>const React = require('react');
const h = require('react-hyperscript');
const _ = require('lodash');
const { applyExpressionData } = require('../expression-table');
const { searchNodes } = require('../cy');
// const classNames = require('classnames');
// const Table = require('react-table').default;
// const { Tab, Tabs, TabList, TabPanel } = require('react-tabs');
// const matchSorter = require('match-sorter').default;

// const cysearch = _.debounce(require('../../common/cy/match-style'), 500);

// const { ExpressionTable, applyExpressionData } = require('./expression-table');

class ExpressionColourLegend extends React.Component {
  render(){
    let { min, max } = this.props;

    return h('div.paint-legend', [
      h('p', `low ${min}`),
      h('p', `high ${max}`)
    ]);
  }
}

class ExpressionTableView extends React.Component {
  constructor(props){
    super(props);

    this.state = {
      sortBy: 'geneName',
      sortType: 'asc',
      nodeSearchValue: ''
    };
  }

  analysisFns(){
    return {
      'mean': _.mean,
      'max': _.max,
      'min': _.min
    };
  }

  handleSortChange(newSort){
    let { sortBy, sortType } = this.state;

    if( newSort == sortBy ){
      this.setState({ sortType: sortType === 'asc' ? 'desc' : 'asc'});
    } else {
      this.setState({ sortBy: newSort });
    }
  }

  handleSearchChange(newVal){
    let cy = this.props.cySrv.get();
    this.setState({nodeSearchValue: newVal}, () => searchNodes(cy, newVal));
  }

  generateFoldChangeList(){
    let { expressionTable, paintMenuCtrls } = this.props;
    let { exprClass, exprFn } = paintMenuCtrls;
    let { sortBy, sortType, nodeSearchValue } = this.state;
    let foldChangeExpressions = expressionTable.expressions().map(e => {
      return {
        geneName: e.geneName,
        foldChange: e.foldChange( exprClass, exprFn, 'N/A')
      };
    });

    let sortedFoldChanges = _.orderBy(foldChangeExpressions, [sortBy], [sortType]);

    let filteredFoldChanges = sortedFoldChanges.filter(fc => {
      let upperFc = fc.geneName.toUpperCase();
      let upperFilter = nodeSearchValue.toUpperCase();
      return upperFc.includes(upperFilter) || upperFilter.includes(upperFc);
    });

    return filteredFoldChanges;
  }

  render(){
    let { controller, expressionTable, cySrv, paintMenuCtrls} = this.props;
    let { exprClass, exprFnName } = paintMenuCtrls;

    let functionSelector = h('select.paint-select', {
        value: exprFnName,
        onChange: e => controller.handlePaintCtrlChange({
          exprFnName: e.target.value,
          exprFn: this.analysisFns()[e.target.value]
        })
      },
      Object.entries(this.analysisFns()).map(entry => h('option', {value: entry[0]}, entry[0]))
    );

    let classSelector = h('div', [
      'Compare: ',
      h('select.paint-select', {
        value: exprClass,
        onChange: e => controller.handlePaintCtrlChange({exprClass: e.target.value})
      },
      expressionTable.classes.map(cls => h('option', { value: cls}, cls))
      ),
      ` vs ${_.difference(expressionTable.classes, [exprClass])}`
    ]);

    let foldChangeExpressions = this.generateFoldChangeList();

    return h('div.expression-table-view', [
      functionSelector,
      classSelector,
      h('div.expression-table-header', [
        h('div.expression-table-header-column', {onClick: () => this.handleSortChange('geneName') }, 'Gene'),
        h('div.expression-table-header-column', {onClick: () => this.handleSortChange('foldChange') }, 'Expression Ratio'),
      ]),
      h('div.expression-search-filter', [
        h('input', { placeholder: 'Filter by gene', onChange: e => this.handleSearchChange(e.target.value) })
      ]),
      h('div.expression-list', foldChangeExpressions.map( e => {
        return h('div.expression-entry', [
          h('div.gene', e.geneName),
          h('div.fold-change', e.foldChange)
        ]);
      }))
    ]);
  }
}

class PaintMenu extends React.Component {
  constructor(props) {
    super(props);

    // this.state = {
    //   selectedFunction: this.analysisFns().mean,
    //   selectedClass: props.selectedClass,
    //   selectedSearchResult: props.selectedSearchResult,
    //   loading: false
    // };
  }

  render() {
    let { cySrv, controller, expressionTable, paintMenuCtrls, curPathway, pathways } = this.props;
    let { exprClass, exprFn } = paintMenuCtrls;
    let { min, max } = expressionTable.computeFoldChangeRange(exprClass, exprFn);

    let pathwayResults = pathways.map(result => {
      return h('div', { onClick: () => controller.loadPathway(result.pathway) }, [
        h('div', result.pathway.name())
      ]);
    });

    return h('div.paint-menu', [
      h(ExpressionColourLegend, { min, max }),
      h(ExpressionTableView, { cySrv, expressionTable, controller, paintMenuCtrls} ),
      pathwayResults
    ]);
  }
}

module.exports = PaintMenu;<commit_msg>tab switching between paint menus<commit_after>const React = require('react');
const h = require('react-hyperscript');
const _ = require('lodash');
const { Tab, Tabs, TabList, TabPanel } = require('react-tabs');
const { applyExpressionData } = require('../expression-table');
const { searchNodes } = require('../cy');

class ExpressionColourLegend extends React.Component {
  render(){
    let { min, max } = this.props;

    return h('div.paint-legend', [
      h('p', `low ${min}`),
      h('p', `high ${max}`)
    ]);
  }
}

class ExpressionTableView extends React.Component {
  constructor(props){
    super(props);

    this.state = {
      sortBy: 'geneName',
      sortType: 'asc',
      nodeSearchValue: ''
    };
  }

  analysisFns(){
    return {
      'mean': _.mean,
      'max': _.max,
      'min': _.min
    };
  }

  handleSortChange(newSort){
    let { sortBy, sortType } = this.state;

    if( newSort == sortBy ){
      this.setState({ sortType: sortType === 'asc' ? 'desc' : 'asc'});
    } else {
      this.setState({ sortBy: newSort });
    }
  }

  handleSearchChange(newVal){
    let cy = this.props.cySrv.get();
    this.setState({nodeSearchValue: newVal}, () => searchNodes(cy, newVal));
  }

  generateFoldChangeList(){
    let { expressionTable, paintMenuCtrls } = this.props;
    let { exprClass, exprFn } = paintMenuCtrls;
    let { sortBy, sortType, nodeSearchValue } = this.state;
    let foldChangeExpressions = expressionTable.expressions().map(e => {
      return {
        geneName: e.geneName,
        foldChange: e.foldChange( exprClass, exprFn, 'N/A')
      };
    });

    let sortedFoldChanges = _.orderBy(foldChangeExpressions, [sortBy], [sortType]);

    let filteredFoldChanges = sortedFoldChanges.filter(fc => {
      let upperFc = fc.geneName.toUpperCase();
      let upperFilter = nodeSearchValue.toUpperCase();
      return upperFc.includes(upperFilter) || upperFilter.includes(upperFc);
    });

    return filteredFoldChanges;
  }

  render(){
    let { controller, expressionTable, cySrv, paintMenuCtrls} = this.props;
    let { exprClass, exprFnName } = paintMenuCtrls;

    let functionSelector = h('select.paint-select', {
        value: exprFnName,
        onChange: e => controller.handlePaintCtrlChange({
          exprFnName: e.target.value,
          exprFn: this.analysisFns()[e.target.value]
        })
      },
      Object.entries(this.analysisFns()).map(entry => h('option', {value: entry[0]}, entry[0]))
    );

    let classSelector = h('div', [
      'Compare: ',
      h('select.paint-select', {
        value: exprClass,
        onChange: e => controller.handlePaintCtrlChange({exprClass: e.target.value})
      },
      expressionTable.classes.map(cls => h('option', { value: cls}, cls))
      ),
      ` vs ${_.difference(expressionTable.classes, [exprClass])}`
    ]);

    let foldChangeExpressions = this.generateFoldChangeList();

    return h('div.expression-table-view', [
      functionSelector,
      classSelector,
      h('div.expression-table-header', [
        h('div.expression-table-header-column', {onClick: () => this.handleSortChange('geneName') }, 'Gene'),
        h('div.expression-table-header-column', {onClick: () => this.handleSortChange('foldChange') }, 'Expression Ratio'),
      ]),
      h('div.expression-search-filter', [
        h('input', { placeholder: 'Filter by gene', onChange: e => this.handleSearchChange(e.target.value) })
      ]),
      h('div.expression-list', foldChangeExpressions.map( e => {
        return h('div.expression-entry', [
          h('div.gene', e.geneName),
          h('div.fold-change', e.foldChange)
        ]);
      }))
    ]);
  }
}

class PathwayResultsListView extends React.Component {
  render(){
    let { pathways, curPathway, controller } = this.props;
    let pathwayResults = pathways.map(result => {
      return h('div', { onClick: () => controller.loadPathway(result.pathway) }, [
        h('div', result.pathway.name())
      ]);
    });

    return h('div.pathways-list', [
      ...pathwayResults
    ]);
  }
}

class PaintMenu extends React.Component {

  render() {
    let { cySrv, controller, expressionTable, paintMenuCtrls, curPathway, pathways } = this.props;
    let { exprClass, exprFn } = paintMenuCtrls;
    let { min, max } = expressionTable.computeFoldChangeRange(exprClass, exprFn);

    return h(Tabs, [
      h(TabList, [
        h(Tab, 'Expression Data'),
        h(Tab, 'Select Pathway')
      ]),
      h(TabPanel, [
        h(ExpressionColourLegend, { min, max }),
        h(ExpressionTableView, { cySrv, expressionTable, controller, paintMenuCtrls} ),
      ]),
      h(TabPanel, [
        h(PathwayResultsListView, { controller, curPathway, pathways })
      ])
    ]);
  }
}

module.exports = PaintMenu;<|endoftext|>


<commit_before>window.Nuterra = (function () {
    var myNuterra = {
        templates: {},
        loadTemplate: function (name, url, onload) {
            var template = this.templates[name];
            if (template) {
                onload(template.html());
            } else {
                this.newTemplate(name, url, onload);
            }
        },
        newTemplate: function(name, url, onload) {
            var template = $("<script type='x-tmpl-mustache' id='" + name + "'>");
            $("head script[type='x-tmpl-mustache']").last().after(template);
            var templates = this.templates;
            $.ajax({
                type: 'GET',
                url: url,
                success: function (data) {
                    template.text(data);
                    templates[name] = template;
                    if (onload) {
                        onload(template.html());
                    }
                }
            });
        },

        getAccount: function (steamId, callback) {
            if (!steamId) {
                console.warn('steamId is undefined!');
                callback(null);
                return;
            }
            $.ajax({
                type: 'GET',
                url: '/api/accounts/' + steamId,
                success: function (data) {
                    var account = JSON.parse(data);
                    callback(account);
                }
            });
        },
        getCurrentAccount: function(callback) {
            var steamId = $.cookie('YinYang.SteamID');
            Nuterra.getAccount(steamId, callback);
        },
        getAccounts: function (skip, take, callback) {
            $.ajax({
                type: 'GET',
                url: '/api/accounts?skip=' + skip + '&take='+ take,
                success: function (data) {
                    var account = JSON.parse(data);
                    callback(account);
                }
            });
        },
        getTechsForAccount: function (steamId, callback) {
            $.ajax({
                type: 'GET',
                url: '/api/techs/' + steamId,
                success: function (data) {
                    var techs = JSON.parse(data);
                    callback(techs);
                }
            });
        },
        getTechInfo: function (techId, callback) {
            $.ajax({
                type: 'GET',
                url: '/api/techs/' + techId,
                success: function (data) {
                    var tech = JSON.parse(data);
                    callback(tech);
                }
            });
        },
        pages: {},
        addPage: function (name, handler) {
            this.pages[name] = {
                name: name,
                callback: handler,
                navItems: [],
            };
        },
        showPage: function (name, id, prevent_push) {
            var page = this.pages[name];
            if (!page) {
                console.warn("Unknown page: '" + name + "' tell a developer!");
                return;
            }

            var handler = page.callback;
            var newUrl = "/app/" + name + "/";
            if (id) {
                newUrl += id + "/";
            }
            handler(id);

            $('#navbar li.active').removeClass('active');
            $(page.navItems).addClass("active");

            if (!prevent_push) {
                history.pushState({ name: name, id: id }, name + ": #" + id, newUrl);
            }
        },
        getPage: function(link){
            var matches = link.match(/^app\/([^\/]*?)(\/(\w+)(\/.*)?)?$/);
            if (matches != null) {
                var pageName = matches[1];
                var pageId = matches[3] || null;
                return this.pages[pageName];
            } else {
                return null;
            }
        },
        setPageNavItem: function (name, element) {
            var page = this.getPage(name);
            if (page) {
                page.navItems.push(element);
            } else {
                console.warn('Setting nav item for unknown page ', name, element);
            }
        },
        setTitle: function (text) {
            var hash = window.location.pathname.match(/^\/app\/([^\/]*?)((\/(\w+))?(\/.*)?)?$/);
            hash += "/" + text.replace(/\s/g, '+');
            window.location.pathname = hash;
            window.document.title = text;
        },
        showPageFromUrl: function (url, prevent_push) {
            var info = this.analyzeUrl(url);
            if (info) {
                this.showPage(info.page, info.data, prevent_push);
            } else {
                this.showPage('home', null, prevent_push);
            }
        },
        analyzeUrl: function (url) {
            var matches = url.match(/^\/app\/([^\/]*?)((\/(\w+))?(\/.*)?)?$/);
            if (matches) {
                return { page: matches[1], data: matches[3] }
            } else {
                return null;
            }
        },
    };
    return myNuterra;
}());

window.onpopstate = function (event) {
    if (event.state == null) {
        Nuterra.showPageFromUrl(window.location.pathname, true);
    } else {
        Nuterra.showPage(event.state.name, event.state.id, true);
    }
};

$(function () {
    $("#navbar li > a[href]").each(function (index, elem) {
        var linkElem = $(elem);
        var pageLink = linkElem.attr("href");
        var listElem = linkElem.parent()[0];
        Nuterra.setPageNavItem(pageLink, listElem);
    });
});

$(function () { Nuterra.showPageFromUrl(window.location.pathname, true); });

$.fn.editable.defaults.mode = 'inline';

$(function () {

    $("#navbar").on("click", "a", function (event) {
        var elem = event.target;
        if (elem.host == window.location.host) {
            event.preventDefault();
            var link = elem.pathname;
            Nuterra.showPageFromUrl(link, false);
        }
    });
});<commit_msg>Fixed navbar highlighting<commit_after>window.Nuterra = (function () {
    var myNuterra = {
        templates: {},
        loadTemplate: function (name, url, onload) {
            var template = this.templates[name];
            if (template) {
                onload(template.html());
            } else {
                this.newTemplate(name, url, onload);
            }
        },
        newTemplate: function(name, url, onload) {
            var template = $("<script type='x-tmpl-mustache' id='" + name + "'>");
            $("head script[type='x-tmpl-mustache']").last().after(template);
            var templates = this.templates;
            $.ajax({
                type: 'GET',
                url: url,
                success: function (data) {
                    template.text(data);
                    templates[name] = template;
                    if (onload) {
                        onload(template.html());
                    }
                }
            });
        },

        getAccount: function (steamId, callback) {
            if (!steamId) {
                console.warn('steamId is undefined!');
                callback(null);
                return;
            }
            $.ajax({
                type: 'GET',
                url: '/api/accounts/' + steamId,
                success: function (data) {
                    var account = JSON.parse(data);
                    callback(account);
                }
            });
        },
        getCurrentAccount: function(callback) {
            var steamId = $.cookie('YinYang.SteamID');
            Nuterra.getAccount(steamId, callback);
        },
        getAccounts: function (skip, take, callback) {
            $.ajax({
                type: 'GET',
                url: '/api/accounts?skip=' + skip + '&take='+ take,
                success: function (data) {
                    var account = JSON.parse(data);
                    callback(account);
                }
            });
        },
        getTechsForAccount: function (steamId, callback) {
            $.ajax({
                type: 'GET',
                url: '/api/techs/' + steamId,
                success: function (data) {
                    var techs = JSON.parse(data);
                    callback(techs);
                }
            });
        },
        getTechInfo: function (techId, callback) {
            $.ajax({
                type: 'GET',
                url: '/api/techs/' + techId,
                success: function (data) {
                    var tech = JSON.parse(data);
                    callback(tech);
                }
            });
        },
        pages: {},
        addPage: function (name, handler) {
            this.pages[name] = {
                name: name,
                callback: handler,
                navItems: [],
            };
        },
        showPage: function (name, id, prevent_push) {
            var page = this.pages[name];
            if (!page) {
                console.warn("Unknown page: '" + name + "' tell a developer!");
                return;
            }

            var handler = page.callback;
            var newUrl = "/app/" + name + "/";
            if (id) {
                newUrl += id + "/";
            }
            handler(id);

            $('#navbar li.active').removeClass('active');
            $(page.navItems).addClass("active");

            if (!prevent_push) {
                history.pushState({ name: name, id: id }, name + ": #" + id, newUrl);
            }
        },
        getPage: function(link){
            var info = this.analyzeUrl(link);
            if (info) {
                return this.pages[info.page];
            } else {
                return null;
            }
        },
        setPageNavItem: function (name, element) {
            var page = this.getPage(name);
            if (page) {
                page.navItems.push(element);
            } else {
                console.warn('Setting nav item for unknown page ', name, element);
            }
        },
        setTitle: function (text) {
            var hash = window.location.pathname.match(/^\/app\/([^\/]*?)((\/(\w+))?(\/.*)?)?$/);
            hash += "/" + text.replace(/\s/g, '+');
            window.location.pathname = hash;
            window.document.title = text;
        },
        showPageFromUrl: function (url, prevent_push) {
            var info = this.analyzeUrl(url);
            if (info) {
                this.showPage(info.page, info.data, prevent_push);
            } else {
                this.showPage('home', null, prevent_push);
            }
        },
        analyzeUrl: function (url) {
            var matches = url.match(/^\/app\/([^\/]*?)((\/(\w+))?(\/.*)?)?$/);
            if (matches) {
                return { page: matches[1], data: matches[3] }
            } else {
                return null;
            }
        },
    };
    return myNuterra;
}());

window.onpopstate = function (event) {
    if (event.state == null) {
        Nuterra.showPageFromUrl(window.location.pathname, true);
    } else {
        Nuterra.showPage(event.state.name, event.state.id, true);
    }
};

$(function () {
    $("#navbar li > a[href]").each(function (index, elem) {
        var linkElem = $(elem);
        var pageLink = linkElem.attr("href");
        var listElem = linkElem.parent()[0];
        Nuterra.setPageNavItem(pageLink, listElem);
    });
});

$(function () { Nuterra.showPageFromUrl(window.location.pathname, true); });

$.fn.editable.defaults.mode = 'inline';

$(function () {

    $("#navbar").on("click", "a", function (event) {
        var elem = event.target;
        if (elem.host == window.location.host) {
            event.preventDefault();
            var link = elem.pathname;
            Nuterra.showPageFromUrl(link, false);
        }
    });
});<|endoftext|>


<commit_before>/*jshint browser: true */
/*jshint unused: false */
/*global Backbone, EJS, $, setTimeout, localStorage, ace, Storage, window, _ */
/*global _, arangoHelper, templateEngine, jQuery, Joi*/

(function () {
  "use strict";
  window.queryManagementView = Backbone.View.extend({
    el: '#content',

    id: '#queryManagementContent',

    templateActive: templateEngine.createTemplate("queryManagementViewActive.ejs"),
    templateSlow: templateEngine.createTemplate("queryManagementViewSlow.ejs"),
    table: templateEngine.createTemplate("arangoTable.ejs"),
    tabbar: templateEngine.createTemplate("arangoTabbar.ejs"),

    initialize: function () {
      this.activeCollection = new window.QueryManagementActive();
      this.slowCollection = new window.QueryManagementSlow();
      this.convertModelToJSON(true);
    },

    events: {
      "click #arangoQueryManagementTabbar button" : "switchTab",
      "click #deleteSlowQueryHistory" : "deleteSlowQueryHistoryModal",
      "click #arangoQueryManagementTable .fa-minus-circle" : "deleteRunningQueryModal"
    },

    tabbarElements: {
      id: "arangoQueryManagementTabbar",
      titles: [
        ["Active", "activequeries"],
        ["Slow", "slowqueries"]
      ]
    },

    tableDescription: {
      id: "arangoQueryManagementTable",
      titles: ["ID", "Query String", "Runtime", "Started", ""],
      rows: [],
      unescaped: [false, false, false, false, true]
    },

    switchTab: function(e) {
      if (e.currentTarget.id === 'activequeries') {
        this.convertModelToJSON(true);
      }
      else if (e.currentTarget.id === 'slowqueries') {
        this.convertModelToJSON(false);
      }
    },

    deleteRunningQueryModal: function(e) {
      this.killQueryId = $(e.currentTarget).attr('data-id');
      var buttons = [], tableContent = [];

      tableContent.push(
        window.modalView.createReadOnlyEntry(
          undefined,
          "Running Query",
          'Do you want to kill the running query?',
          undefined,
          undefined,
          false,
          undefined
        )
      );

      buttons.push(
        window.modalView.createDeleteButton('Kill', this.killRunningQuery.bind(this))
      );

      window.modalView.show(
        'modalTable.ejs',
        'Kill Running Query',
        buttons,
        tableContent
      );

      $('.modal-delete-confirmation strong').html('Really kill?');

    },

    killRunningQuery: function() {
      this.collection.killRunningQuery(this.killQueryId, this.killRunningQueryCallback.bind(this));
      window.modalView.hide();
    },

    killRunningQueryCallback: function() {
      this.convertModelToJSON(true);
      this.renderActive();
    },

    deleteSlowQueryHistoryModal: function() {
      var buttons = [], tableContent = [];

      tableContent.push(
        window.modalView.createReadOnlyEntry(
          undefined,
          "Slow Query Log",
          'Do you want to delete the slow query log entries?',
          undefined,
          undefined,
          false,
          undefined
        )
      );

      buttons.push(
        window.modalView.createDeleteButton('Delete', this.deleteSlowQueryHistory.bind(this))
      );

      window.modalView.show(
        'modalTable.ejs',
        'Delete Slow Query Log',
        buttons,
        tableContent
      );
    },

    deleteSlowQueryHistory: function() {
      this.collection.deleteSlowQueryHistory(this.slowQueryCallback.bind(this));
      window.modalView.hide();
    },

    slowQueryCallback: function() {
      this.convertModelToJSON(false);
      this.renderSlow();
    },

    render: function() {
      this.convertModelToJSON(true);
    },

    renderActive: function() {
      this.$el.html(this.templateActive.render({}));
      $(this.id).html(this.tabbar.render({content: this.tabbarElements}));
      $(this.id).append(this.table.render({content: this.tableDescription}));
      $('#activequeries').addClass("arango-active-tab");
    },

    renderSlow: function() {
      this.$el.html(this.templateSlow.render({}));
      $(this.id).html(this.tabbar.render({content: this.tabbarElements}));
      $(this.id).append(this.table.render({
        content: this.tableDescription,
      }));
      $('#slowqueries').addClass("arango-active-tab");
    },

    convertModelToJSON: function (active) {
      var self = this;
      var rowsArray = [];

      if (active === true) {
        this.collection = this.activeCollection;
      }
      else {
        this.collection = this.slowCollection;
      }

      this.collection.fetch({
        success: function () {
          self.collection.each(function (model) {

          var button = '';
            if (active) {
              button = '';
            }
            rowsArray.push([
              model.get('id'),
              model.get('query'),
              model.get('runTime').toFixed(2) + ' s',
              model.get('started'),
              button
            ]);
          });

          var message = "No running queries.";
          if (!active) {
            message = "No slow queries.";
          }

          if (rowsArray.length === 0) {
            rowsArray.push([
              message,
              "",
              "",
              "",
              ""
            ]);
          }

          self.tableDescription.rows = rowsArray;

          if (active) {
            self.renderActive();
          }
          else {
            self.renderSlow();
          }
        }
      });

    }

  });
}());
<commit_msg>query management view auto update<commit_after>/*jshint browser: true */
/*jshint unused: false */
/*global Backbone, EJS, $, setTimeout, localStorage, ace, Storage, window, _ */
/*global _, arangoHelper, templateEngine, jQuery, Joi*/

(function () {
  "use strict";
  window.queryManagementView = Backbone.View.extend({
    el: '#content',

    id: '#queryManagementContent',

    templateActive: templateEngine.createTemplate("queryManagementViewActive.ejs"),
    templateSlow: templateEngine.createTemplate("queryManagementViewSlow.ejs"),
    table: templateEngine.createTemplate("arangoTable.ejs"),
    tabbar: templateEngine.createTemplate("arangoTabbar.ejs"),
    active: true,
    shouldRender: true,
    timer: 0,
    refreshRate: 2000,

    initialize: function () {
      var self = this;
      this.activeCollection = new window.QueryManagementActive();
      this.slowCollection = new window.QueryManagementSlow();
      this.convertModelToJSON(true);

      window.setInterval(function() {
        if (window.location.hash === '#queryManagement' && window.VISIBLE && self.shouldRender) {
          if (self.active) {
            self.convertModelToJSON(true);
            self.renderActive();
          }
          else {
            self.convertModelToJSON(false);
            self.renderSlow();
          }
        }
      }, self.refreshRate);
    },

    events: {
      "click #arangoQueryManagementTabbar button" : "switchTab",
      "click #deleteSlowQueryHistory" : "deleteSlowQueryHistoryModal",
      "click #arangoQueryManagementTable .fa-minus-circle" : "deleteRunningQueryModal"
    },

    tabbarElements: {
      id: "arangoQueryManagementTabbar",
      titles: [
        ["Active", "activequeries"],
        ["Slow", "slowqueries"]
      ]
    },

    tableDescription: {
      id: "arangoQueryManagementTable",
      titles: ["ID", "Query String", "Runtime", "Started", ""],
      rows: [],
      unescaped: [false, false, false, false, true]
    },

    switchTab: function(e) {
      if (e.currentTarget.id === 'activequeries') {
        this.active = true;
        this.convertModelToJSON(true);
      }
      else if (e.currentTarget.id === 'slowqueries') {
        this.active = false;
        this.convertModelToJSON(false);
      }
    },

    deleteRunningQueryModal: function(e) {
      this.killQueryId = $(e.currentTarget).attr('data-id');
      var buttons = [], tableContent = [];

      tableContent.push(
        window.modalView.createReadOnlyEntry(
          undefined,
          "Running Query",
          'Do you want to kill the running query?',
          undefined,
          undefined,
          false,
          undefined
        )
      );

      buttons.push(
        window.modalView.createDeleteButton('Kill', this.killRunningQuery.bind(this))
      );

      window.modalView.show(
        'modalTable.ejs',
        'Kill Running Query',
        buttons,
        tableContent
      );

      $('.modal-delete-confirmation strong').html('Really kill?');

    },

    killRunningQuery: function() {
      this.collection.killRunningQuery(this.killQueryId, this.killRunningQueryCallback.bind(this));
      window.modalView.hide();
    },

    killRunningQueryCallback: function() {
      this.convertModelToJSON(true);
      this.renderActive();
    },

    deleteSlowQueryHistoryModal: function() {
      var buttons = [], tableContent = [];

      tableContent.push(
        window.modalView.createReadOnlyEntry(
          undefined,
          "Slow Query Log",
          'Do you want to delete the slow query log entries?',
          undefined,
          undefined,
          false,
          undefined
        )
      );

      buttons.push(
        window.modalView.createDeleteButton('Delete', this.deleteSlowQueryHistory.bind(this))
      );

      window.modalView.show(
        'modalTable.ejs',
        'Delete Slow Query Log',
        buttons,
        tableContent
      );
    },

    deleteSlowQueryHistory: function() {
      this.collection.deleteSlowQueryHistory(this.slowQueryCallback.bind(this));
      window.modalView.hide();
    },

    slowQueryCallback: function() {
      this.convertModelToJSON(false);
      this.renderSlow();
    },

    render: function() {
      this.convertModelToJSON(true);
    },

    addEvents: function() {
      var self = this;
      $('#queryManagementContent tbody').on('mousedown', function() {
        clearTimeout(self.timer);
        self.shouldRender = false;
      });
      $('#queryManagementContent tbody').on('mouseup', function() {
        self.timer = window.setTimeout(function() {
          self.shouldRender = true;
        }, 3000);
      });
    },

    renderActive: function() {
      this.$el.html(this.templateActive.render({}));
      $(this.id).html(this.tabbar.render({content: this.tabbarElements}));
      $(this.id).append(this.table.render({content: this.tableDescription}));
      $('#activequeries').addClass("arango-active-tab");
      this.addEvents();
    },

    renderSlow: function() {
      this.$el.html(this.templateSlow.render({}));
      $(this.id).html(this.tabbar.render({content: this.tabbarElements}));
      $(this.id).append(this.table.render({
        content: this.tableDescription,
      }));
      $('#slowqueries').addClass("arango-active-tab");
      this.addEvents();
    },

    convertModelToJSON: function (active) {
      var self = this;
      var rowsArray = [];

      if (active === true) {
        this.collection = this.activeCollection;
      }
      else {
        this.collection = this.slowCollection;
      }

      this.collection.fetch({
        success: function () {
          self.collection.each(function (model) {

          var button = '';
            if (active) {
              button = '';
            }
            rowsArray.push([
              model.get('id'),
              model.get('query'),
              model.get('runTime').toFixed(2) + ' s',
              model.get('started'),
              button
            ]);
          });

          var message = "No running queries.";
          if (!active) {
            message = "No slow queries.";
          }

          if (rowsArray.length === 0) {
            rowsArray.push([
              message,
              "",
              "",
              "",
              ""
            ]);
          }

          self.tableDescription.rows = rowsArray;

          if (active) {
            self.renderActive();
          }
          else {
            self.renderSlow();
          }
        }
      });

    }

  });
}());
<|endoftext|>


<commit_before>/*
  Copyright (C) 2012 Yusuke Suzuki <utatane.tea@gmail.com>

  Redistribution and use in source and binary forms, with or without
  modification, are permitted provided that the following conditions are met:

    * Redistributions of source code must retain the above copyright
      notice, this list of conditions and the following disclaimer.
    * Redistributions in binary form must reproduce the above copyright
      notice, this list of conditions and the following disclaimer in the
      documentation and/or other materials provided with the distribution.

  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
  AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
  IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
  ARE DISCLAIMED. IN NO EVENT SHALL <COPYRIGHT HOLDER> BE LIABLE FOR ANY
  DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
  (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
  LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
  ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
  (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
  THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/

/*jslint bitwise:true */
/*global esmangle:true, module:true, define:true, require:true*/
(function (factory, global) {
    'use strict';

    function namespace(str, obj) {
        var i, iz, names, name;
        names = str.split('.');
        for (i = 0, iz = names.length; i < iz; ++i) {
            name = names[i];
            if (obj.hasOwnProperty(name)) {
                obj = obj[name];
            } else {
                obj = (obj[name] = {});
            }
        }
        return obj;
    }

    // Universal Module Definition (UMD) to support AMD, CommonJS/Node.js,
    // and plain browser loading,
    if (typeof define === 'function' && define.amd) {
        define('esmangle/pass/transform-immediate-function-call', ['module', 'esmangle/common'], function(module, common) {
            module.exports = factory(common);
        });
    } else if (typeof module !== 'undefined') {
        module.exports = factory(require('../common'));
    } else {
        namespace('esmangle.pass', global).transformImmediateFunctionCall  = factory(namespace('esmangle.common', global));
    }
}(function (common) {
    'use strict';

    var Syntax, modified;

    Syntax = common.Syntax;

    function isEmptyFunctionCall(call) {
        var callee;
        if (call.type !== Syntax.CallExpression) {
            return false;
        }

        callee = call.callee;

        if (callee.type !== Syntax.FunctionExpression) {
            return false;
        }

        if (callee.body.type !== Syntax.BlockStatement) {
            return false;
        }

        return callee.body.body.length === 0;
    }

    function callToSequence(call) {
        var expressions;
        expressions = Array.prototype.slice.call(call['arguments']);

        if (expressions.length === 0) {
            return common.SpecialNode.generateUndefined(call);
        }

        expressions.push(common.SpecialNode.generateUndefined());
        return common.moveLocation(call, {
            type: Syntax.SequenceExpression,
            expressions: expressions
        });
    }

    function transformImmediateFunctionCall(tree, options) {
        var result;

        if (options == null) {
            options = { destructive: false };
        }

        result = (options.destructive) ? tree : common.deepCopy(tree);
        modified = false;

        result = common.replace(result, {
            leave: function leave(node) {
                if (isEmptyFunctionCall(node)) {
                    return callToSequence(node);
                }
            }
        });

        return {
            result: result,
            modified: modified
        };
    }

    transformImmediateFunctionCall.passName = 'transform-immediate-function-call';
    return transformImmediateFunctionCall;
}, this));
/* vim: set sw=4 ts=4 et tw=80 : */
<commit_msg>Fix modified flag in transform-immediate-function-call<commit_after>/*
  Copyright (C) 2012 Yusuke Suzuki <utatane.tea@gmail.com>

  Redistribution and use in source and binary forms, with or without
  modification, are permitted provided that the following conditions are met:

    * Redistributions of source code must retain the above copyright
      notice, this list of conditions and the following disclaimer.
    * Redistributions in binary form must reproduce the above copyright
      notice, this list of conditions and the following disclaimer in the
      documentation and/or other materials provided with the distribution.

  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
  AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
  IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
  ARE DISCLAIMED. IN NO EVENT SHALL <COPYRIGHT HOLDER> BE LIABLE FOR ANY
  DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
  (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
  LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
  ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
  (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
  THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/

/*jslint bitwise:true */
/*global esmangle:true, module:true, define:true, require:true*/
(function (factory, global) {
    'use strict';

    function namespace(str, obj) {
        var i, iz, names, name;
        names = str.split('.');
        for (i = 0, iz = names.length; i < iz; ++i) {
            name = names[i];
            if (obj.hasOwnProperty(name)) {
                obj = obj[name];
            } else {
                obj = (obj[name] = {});
            }
        }
        return obj;
    }

    // Universal Module Definition (UMD) to support AMD, CommonJS/Node.js,
    // and plain browser loading,
    if (typeof define === 'function' && define.amd) {
        define('esmangle/pass/transform-immediate-function-call', ['module', 'esmangle/common'], function(module, common) {
            module.exports = factory(common);
        });
    } else if (typeof module !== 'undefined') {
        module.exports = factory(require('../common'));
    } else {
        namespace('esmangle.pass', global).transformImmediateFunctionCall  = factory(namespace('esmangle.common', global));
    }
}(function (common) {
    'use strict';

    var Syntax, modified;

    Syntax = common.Syntax;

    function isEmptyFunctionCall(call) {
        var callee;
        if (call.type !== Syntax.CallExpression) {
            return false;
        }

        callee = call.callee;

        if (callee.type !== Syntax.FunctionExpression) {
            return false;
        }

        if (callee.body.type !== Syntax.BlockStatement) {
            return false;
        }

        return callee.body.body.length === 0;
    }

    function callToSequence(call) {
        var expressions;
        expressions = Array.prototype.slice.call(call['arguments']);

        if (expressions.length === 0) {
            return common.SpecialNode.generateUndefined(call);
        }

        expressions.push(common.SpecialNode.generateUndefined());
        return common.moveLocation(call, {
            type: Syntax.SequenceExpression,
            expressions: expressions
        });
    }

    function transformImmediateFunctionCall(tree, options) {
        var result;

        if (options == null) {
            options = { destructive: false };
        }

        result = (options.destructive) ? tree : common.deepCopy(tree);
        modified = false;

        result = common.replace(result, {
            leave: function leave(node) {
                if (isEmptyFunctionCall(node)) {
                    modified = true;
                    return callToSequence(node);
                }
            }
        });

        return {
            result: result,
            modified: modified
        };
    }

    transformImmediateFunctionCall.passName = 'transform-immediate-function-call';
    return transformImmediateFunctionCall;
}, this));
/* vim: set sw=4 ts=4 et tw=80 : */
<|endoftext|>


<commit_before>
/* Copyright 2019 The TensorFlow Authors. All Rights Reserved.

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.
==============================================================================*/
/**
 * @fileoverview ArrayBufferProvider responsible for making requests to server,
 * receive and parse response.
 */

// TODO: this class must be refactored into base DataProvider and
// subclass ArrayBufferDataProvider later.
var vz_mesh;
(function(vz_mesh) {

/**
* Types of content displayed by the plugin.
* @enum {number}
*/
const ContentType = {
  VERTEX: 1,
  FACE: 2,
  COLOR: 3
};

/**
* Types of content displayed by the plugin mapped to underlying data types.
* @enum {string}
*/
const ContentTypeToItemType = {
  VERTEX: 'float32',
  FACE: 'int32',
  COLOR: 'uint8'
};

class ArrayBufferDataProvider {

  /**
   * ArrayBufferDataProvider constructor, initializes everything needed for
   * future requests to the server.
   * @param {!Object} requestManager Request manager to communicate with the
   *  server.
   */
  constructor(requestManager) {
    this._requestManager = requestManager;
    this._canceller = new tf_backend.Canceller();
  }

  /**
   * Requests new data from the server.
   */
  reload(run, tag, sample) {
    this._canceller.cancelAll();
    return this._fetchMetadata(run, tag, sample);
  }

  /**
   * Requests new data of some particular type from the server.
   * @param {string} run Name of the run to get data for.
   * @param {string} tag Name of the tug to get data for.
   * @param {string} content_type Type of the content to retrieve.
   * @param {!array} metadata List of metadata to complete with data from the
   *  server.
   * @param {number} sample Sample index from a batch of data.
   * @return {!Object} Promise object representing server request.
   * @private
   */
  _fetchDataByType(run, tag, content_type, metadata, sample) {
    const url = tf_backend.getRouter().pluginRoute(
        'mesh', '/data', new URLSearchParams({tag, run, content_type, sample}));

    const processData = this._canceller.cancellable(response => {
      if (response.cancelled) {
        return;
      }
      let buffer = response.value;
      let data;
      switch(content_type) {
        case 'VERTEX':
          data = new Float32Array(buffer);
          break;
        case 'FACE':
          data = new Int32Array(buffer);
          break;
        case 'COLOR':
          data = new Uint8Array(buffer);
          break;
      }
      // TODO: handle empty metadata.
      // itemShape expected to be of shape BxNxK, where B stands for batch,
      // N for number of points and K is either number of items representing
      // coordinate (x,y,z) or face indices or color (rgb).
      const itemShape = metadata[0].data_shape;
      if (itemShape.length != 3) {
        throw 'Data item shape expected to have 3 dimensions BxNxK';
      }
      const itemsCount = itemShape.slice(1).reduce((a, b) => a * b);
      for (let i = 0; i < metadata.length; i++) {
        let itemsFlat = data.slice(itemsCount * i, (i + 1) * itemsCount);
        let items = [];
        for (let n = 0; n < itemShape[1]; n++) {
          items.push([]);
          for (let m = 0; m < itemShape[2]; m++) {
            items[n].push(itemsFlat[n * itemShape[2] + m]);
          }
        }
        metadata[i].data = items;
      }
    });
    return this._requestManager
        .fetch(
            url, null, 'arraybuffer',
            ContentTypeToItemType[content_type])
        .then(response => response.arrayBuffer())
        .then(processData);
  }

  /**
   * Requests new data for each type of metadata from the server.
   * @param {string} run Name of the run to get data for.
   * @param {string} tag Name of the tug to get data for.
   * @param {!array} metadata List of metadata to complete with data from the
   *  server.
   * @param {number} sample Sample index from a batch of data.
   * @private
   */
  _fetchData(run, tag, metadata, sample) {
    let promises = [];
    Object.keys(ContentType).forEach(contentType => {
      let metadataType = [];
      for (let i = 0; i < metadata.length; i++) {
        if (metadata[i].content_type == ContentType[contentType]) {
          metadataType.push(metadata[i]);
        }
      }
      if (metadataType.length) {
        promises.push(this._fetchDataByType(
            run, tag, contentType, metadataType, sample));
      }
    });
    return Promise.all(promises);
  }

  /**
   * Requests new metadata from the server
   * @param {string} run Name of the run to get data for.
   * @param {string} tag Name of the tug to get data for.
   * @param {number} sample Sample index from a batch of data.
   * completion.
   * @private
   */
  _fetchMetadata(run, tag, sample) {
    this._canceller.cancelAll();
    const url = tf_backend.getRouter().pluginRoute(
        'mesh', '/meshes', new URLSearchParams({tag, run, sample}));
    const requestData = this._canceller.cancellable(response => {
      if (response.cancelled) {
        return;
      }
      let metadata = response.value;
      return this._fetchData(run, tag, metadata, sample).then(() => {
        return this._processMetadata(metadata);
      });
    });
    return this._requestManager.fetch(url)
        .then(response => response.json())
        .then(requestData);
  }

  /**
   * Process server raw data into frontend friendly format.
   * @param {!Array} data list of raw server records.
   * @return {!Array} list of step datums.
   * @private
   */
  _processMetadata(data) {
    const timestampToData = new Map();
    for (let i = 0; i < data.length; i++) {
      let dataEntry = data[i];
      if (!timestampToData.has(dataEntry.wall_time)) {
        timestampToData.set(dataEntry.wall_time, []);
      }
      timestampToData.get(dataEntry.wall_time).push(dataEntry);
    }
    let datums = [];
    timestampToData.forEach((data) => {
      let datum;
      let vertices = data.filter(i => i.content_type == ContentType.VERTEX);
      let faces = data.filter(i => i.content_type == ContentType.FACE);
      let colors = data.filter(i => i.content_type == ContentType.COLOR);
      datum = this._createStepDatum(data[0], vertices, faces, colors);
      datums.push(datum);
    });
    return datums;
  }

  /**
   * Process single row of server-side data and puts it in more structured form.
   * @param {!Object} metadata Object describing step summary.
   * @param {!Array} vertices List of 3D coordinates of vertices.
   * @param {?Array} faces List of indices of coordinates which form mesh faces.
   * @param {?Array} colors List of colors for each vertex.
   * @private
   * @return {!Object} with wall_time, step number and data for the step.
   */
  _createStepDatum(metadata, vertices, faces, colors) {
    // TODO: add data validation to make sure frontend is
    // compatible with backend.
    return {
      // The wall time within the metadata is in seconds. The Date
      // constructor accepts a time in milliseconds, so we multiply by 1000.
      wall_time: new Date(metadata.wall_time * 1000),
      step: metadata.step,
      vertices: vertices[0] && vertices[0].data,
      faces: faces && faces[0] && faces[0].data,
      colors: colors && colors[0] && colors[0].data,
      config: metadata.config
    };
  }
}

vz_mesh.ArrayBufferDataProvider = ArrayBufferDataProvider;

})(vz_mesh || (vz_mesh = {}));  // end of vz_mesh namespace
<commit_msg>Fix data processing error: https://github.com/tensorflow/tensorboard/issues/2271<commit_after>
/* Copyright 2019 The TensorFlow Authors. All Rights Reserved.

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.
==============================================================================*/
/**
 * @fileoverview ArrayBufferProvider responsible for making requests to server,
 * receive and parse response.
 */

// TODO: this class must be refactored into base DataProvider and
// subclass ArrayBufferDataProvider later.
var vz_mesh;
(function(vz_mesh) {

/**
* Types of errors during network data roundtrip.
* @enum {number}
*/
vz_mesh.ErrorCodes = {
  CANCELLED: 1  // Happens when the request was cancelled before it finished.
};

/**
* Types of content displayed by the plugin.
* @enum {number}
*/
const ContentType = {
  VERTEX: 1,
  FACE: 2,
  COLOR: 3
};

/**
* Types of content displayed by the plugin mapped to underlying data types.
* @enum {string}
*/
const ContentTypeToItemType = {
  VERTEX: 'float32',
  FACE: 'int32',
  COLOR: 'uint8'
};

class ArrayBufferDataProvider {

  /**
   * ArrayBufferDataProvider constructor, initializes everything needed for
   * future requests to the server.
   * @param {!Object} requestManager Request manager to communicate with the
   *  server.
   */
  constructor(requestManager) {
    this._requestManager = requestManager;
    this._canceller = new tf_backend.Canceller();
  }

  /**
   * Requests new data from the server.
   */
  reload(run, tag, sample) {
    this._canceller.cancelAll();
    return this._fetchMetadata(run, tag, sample);
  }

  /**
   * Requests new data of some particular type from the server.
   * @param {string} run Name of the run to get data for.
   * @param {string} tag Name of the tug to get data for.
   * @param {string} content_type Type of the content to retrieve.
   * @param {!array} metadata List of metadata to complete with data from the
   *  server.
   * @param {number} sample Sample index from a batch of data.
   * @return {!Object} Promise object representing server request.
   * @private
   */
  _fetchDataByType(run, tag, content_type, metadata, sample) {
    const url = tf_backend.getRouter().pluginRoute(
        'mesh', '/data', new URLSearchParams({tag, run, content_type, sample}));

    const processData = this._canceller.cancellable(response => {
      if (response.cancelled) {
        return Promise.reject({
          code: vz_mesh.ErrorCodes.CANCELLED,
          message: 'Response was invalidated.'
        });
      }
      let buffer = response.value;
      let data;
      switch(content_type) {
        case 'VERTEX':
          data = new Float32Array(buffer);
          break;
        case 'FACE':
          data = new Int32Array(buffer);
          break;
        case 'COLOR':
          data = new Uint8Array(buffer);
          break;
      }
      // TODO: handle empty metadata.
      // itemShape expected to be of shape BxNxK, where B stands for batch,
      // N for number of points and K is either number of items representing
      // coordinate (x,y,z) or face indices or color (rgb).
      const itemShape = metadata[0].data_shape;
      if (itemShape.length != 3) {
        throw 'Data item shape expected to have 3 dimensions BxNxK';
      }
      const itemsCount = itemShape.slice(1).reduce((a, b) => a * b);
      for (let i = 0; i < metadata.length; i++) {
        let itemsFlat = data.slice(itemsCount * i, (i + 1) * itemsCount);
        let items = [];
        for (let n = 0; n < itemShape[1]; n++) {
          items.push([]);
          for (let m = 0; m < itemShape[2]; m++) {
            items[n].push(itemsFlat[n * itemShape[2] + m]);
          }
        }
        metadata[i].data = items;
      }
    });
    return this._requestManager
        .fetch(
            url, null, 'arraybuffer',
            ContentTypeToItemType[content_type])
        .then(response => response.arrayBuffer())
        .then(processData);
  }

  /**
   * Requests new data for each type of metadata from the server.
   * @param {string} run Name of the run to get data for.
   * @param {string} tag Name of the tug to get data for.
   * @param {!array} metadata List of metadata to complete with data from the
   *  server.
   * @param {number} sample Sample index from a batch of data.
   * @private
   */
  _fetchData(run, tag, metadata, sample) {
    let promises = [];
    Object.keys(ContentType).forEach(contentType => {
      let metadataType = [];
      for (let i = 0; i < metadata.length; i++) {
        if (metadata[i].content_type == ContentType[contentType]) {
          metadataType.push(metadata[i]);
        }
      }
      if (metadataType.length) {
        promises.push(this._fetchDataByType(
            run, tag, contentType, metadataType, sample));
      }
    });
    return Promise.all(promises);
  }

  /**
   * Requests new metadata from the server
   * @param {string} run Name of the run to get data for.
   * @param {string} tag Name of the tug to get data for.
   * @param {number} sample Sample index from a batch of data.
   * completion.
   * @private
   */
  _fetchMetadata(run, tag, sample) {
    this._canceller.cancelAll();
    const url = tf_backend.getRouter().pluginRoute(
        'mesh', '/meshes', new URLSearchParams({tag, run, sample}));
    const requestData = this._canceller.cancellable(response => {
      if (response.cancelled) {
        return;
      }
      let metadata = response.value;
      return this._fetchData(run, tag, metadata, sample).then(() => {
        return this._processMetadata(metadata);
      });
    });
    return this._requestManager.fetch(url)
        .then(response => response.json())
        .then(requestData);
  }

  /**
   * Process server raw data into frontend friendly format.
   * @param {!Array} data list of raw server records.
   * @return {!Array} list of step datums.
   * @private
   */
  _processMetadata(data) {
    const timestampToData = new Map();
    for (let i = 0; i < data.length; i++) {
      let dataEntry = data[i];
      if (!timestampToData.has(dataEntry.wall_time)) {
        timestampToData.set(dataEntry.wall_time, []);
      }
      timestampToData.get(dataEntry.wall_time).push(dataEntry);
    }
    let datums = [];
    timestampToData.forEach((data) => {
      let datum;
      let vertices = data.filter(i => i.content_type == ContentType.VERTEX);
      let faces = data.filter(i => i.content_type == ContentType.FACE);
      let colors = data.filter(i => i.content_type == ContentType.COLOR);
      datum = this._createStepDatum(data[0], vertices, faces, colors);
      datums.push(datum);
    });
    return datums;
  }

  /**
   * Process single row of server-side data and puts it in more structured form.
   * @param {!Object} metadata Object describing step summary.
   * @param {!Array} vertices List of 3D coordinates of vertices.
   * @param {?Array} faces List of indices of coordinates which form mesh faces.
   * @param {?Array} colors List of colors for each vertex.
   * @private
   * @return {!Object} with wall_time, step number and data for the step.
   */
  _createStepDatum(metadata, vertices, faces, colors) {
    // TODO: add data validation to make sure frontend is
    // compatible with backend.
    return {
      // The wall time within the metadata is in seconds. The Date
      // constructor accepts a time in milliseconds, so we multiply by 1000.
      wall_time: new Date(metadata.wall_time * 1000),
      step: metadata.step,
      vertices: vertices[0] && vertices[0].data,
      faces: faces && faces[0] && faces[0].data,
      colors: colors && colors[0] && colors[0].data,
      config: metadata.config
    };
  }
}

vz_mesh.ArrayBufferDataProvider = ArrayBufferDataProvider;

})(vz_mesh || (vz_mesh = {}));  // end of vz_mesh namespace
<|endoftext|>