<commit_before>/* eslint-disable jsx-a11y/href-no-hash */
import React from 'react'
import PropTypes from 'prop-types'
import MoneyRaised from '../MoneyRaised/MoneyRaisedContainer'
import UserBackgroundImage from '../User/UserBackgroundImageContainer'
import UserMenu from '../User/UserMenuContainer'
import WidgetsContainer from '../Widget/WidgetsContainer'
import CampaignBaseContainer from '../Campaign/CampaignBaseContainer'
import Ad from '../Ad/Ad'
import LogTab from './LogTabContainer'
import LogRevenue from './LogRevenueContainer'
import LogAccountCreation from './LogAccountCreationContainer'
import CircleIcon from 'material-ui/svg-icons/image/lens'
import {
  dashboardIconInactiveColor,
  dashboardIconActiveColor
} from 'theme/default'
import FadeInDashboardAnimation from 'general/FadeInDashboardAnimation'
import ErrorMessage from 'general/ErrorMessage'
import Fireworks from 'lib/fireworks-react'
import RaisedButton from 'material-ui/RaisedButton'

class Dashboard extends React.Component {
  constructor (props) {
    super(props)

    this.state = {
      errorMessage: null,
      showFireworks: false
    }
  }

  showError (msg) {
    this.setState({
      errorMessage: msg
    })
  }

  clearError () {
    this.showError(null)
  }

  launchFireworks (show) {
    this.setState({
      showFireworks: show
    })
  }

  render () {
    // Props will be null on first render.
    const { user, app } = this.props
    const errorMessage = this.state.errorMessage

    const menuStyle = {
      position: 'absolute',
      zIndex: 1,
      top: 14,
      right: 16,
      display: 'flex',
      justifyItems: 'flex-end',
      alignItems: 'center'
    }
    const menuTextStyle = {
      fontSize: 24
    }
    const moneyRaisedStyle = Object.assign({}, menuTextStyle)
    const bulletPointStyle = {
      alignSelf: 'center',
      width: 5,
      height: 5,
      marginTop: 2,
      marginLeft: 12,
      marginRight: 12
    }
    const userMenuStyle = Object.assign({}, menuTextStyle)

    // Whether or not a campaign should show on the dashboard
    // TODO: also make sure the user hasn't dismissed the campaign (`isCampaignShown` var)
    const isGlobalCampaignLive = !!((app && app.isGlobalCampaignLive))

    return (

        <UserBackgroundImage user={user} showError={this.showError.bind(this)} />
        { user
          ? (
            <FadeInDashboardAnimation>

                <MoneyRaised
                  app={app}
                  style={moneyRaisedStyle}
                  launchFireworks={this.launchFireworks.bind(this)}
                  />
                <CircleIcon
                  color={dashboardIconInactiveColor}
                  hoverColor={dashboardIconActiveColor}
                  style={bulletPointStyle}
                />
                <UserMenu app={app} user={user} style={userMenuStyle} />

            </FadeInDashboardAnimation>
            )
          : null
        }
        <WidgetsContainer
          user={user}
          isCampaignLive={isGlobalCampaignLive}
          showError={this.showError.bind(this)}
          />
        { user
          ? (
            <FadeInDashboardAnimation>
              <CampaignBaseContainer
                user={user}
                isCampaignLive={isGlobalCampaignLive}
                />
            </FadeInDashboardAnimation>
            )
          : null
        }
        {
          this.state.showFireworks ? (

              <FadeInDashboardAnimation>

                    Tabbers, together we've raised over half a million dollars for charity. Congrats, and thank you!

                    <RaisedButton
                      label='Share This Milestone'
                      style={{
                        marginTop: 8
                      }}
                      primary
                    />

              </FadeInDashboardAnimation>
              <Fireworks
                width={window.innerWidth}
                height={window.innerHeight}
                background={'rgba(0, 0, 0, 0.01)'}
                style={{
                  position: 'absolute',
                  top: 0,
                  left: 0,
                  zIndex: 20
                }}
              />

          )
          : null
        }
        <Ad
          adId='div-gpt-ad-1464385742501-0'
          adSlotId='/43865596/HBTR'
          width={300}
          height={250}
          style={{
            position: 'absolute',
            bottom: 10,
            right: 10,
            display: 'block'
          }} />
        <Ad
          adId='div-gpt-ad-1464385677836-0'
          adSlotId='/43865596/HBTL'
          width={728}
          height={90}
          style={{
            position: 'absolute',
            bottom: 10,
            right: 320,
            display: 'block'
          }} />
        { user ? <LogTab user={user} /> : null }
        { user ? <LogRevenue user={user} /> : null }
        { user ? <LogAccountCreation user={user} /> : null }
        { errorMessage
          ? <ErrorMessage message={errorMessage}
            onRequestClose={this.clearError.bind(this)} />
          : null }

    )
  }
}

Dashboard.propTypes = {
  user: PropTypes.shape({
    id: PropTypes.string.isRequired
  }),
  app: PropTypes.shape({
    isGlobalCampaignLive: PropTypes.bool
  })
}

Dashboard.defaultProps = {
  app: {
    isGlobalCampaignLive: false
  }
}

export default Dashboard
<commit_msg>Add a close button<commit_after>/* eslint-disable jsx-a11y/href-no-hash */
import React from 'react'
import PropTypes from 'prop-types'
import MoneyRaised from '../MoneyRaised/MoneyRaisedContainer'
import UserBackgroundImage from '../User/UserBackgroundImageContainer'
import UserMenu from '../User/UserMenuContainer'
import WidgetsContainer from '../Widget/WidgetsContainer'
import CampaignBaseContainer from '../Campaign/CampaignBaseContainer'
import Ad from '../Ad/Ad'
import LogTab from './LogTabContainer'
import LogRevenue from './LogRevenueContainer'
import LogAccountCreation from './LogAccountCreationContainer'
import CircleIcon from 'material-ui/svg-icons/image/lens'
import {
  dashboardIconInactiveColor,
  dashboardIconActiveColor
} from 'theme/default'
import FadeInDashboardAnimation from 'general/FadeInDashboardAnimation'
import ErrorMessage from 'general/ErrorMessage'
import Fireworks from 'lib/fireworks-react'
import RaisedButton from 'material-ui/RaisedButton'
import CloseIcon from 'material-ui/svg-icons/navigation/close'

class Dashboard extends React.Component {
  constructor (props) {
    super(props)

    this.state = {
      errorMessage: null,
      showFireworks: false
    }
  }

  showError (msg) {
    this.setState({
      errorMessage: msg
    })
  }

  clearError () {
    this.showError(null)
  }

  launchFireworks (show) {
    this.setState({
      showFireworks: show
    })
  }

  render () {
    // Props will be null on first render.
    const { user, app } = this.props
    const errorMessage = this.state.errorMessage

    const menuStyle = {
      position: 'absolute',
      zIndex: 1,
      top: 14,
      right: 16,
      display: 'flex',
      justifyItems: 'flex-end',
      alignItems: 'center'
    }
    const menuTextStyle = {
      fontSize: 24
    }
    const moneyRaisedStyle = Object.assign({}, menuTextStyle)
    const bulletPointStyle = {
      alignSelf: 'center',
      width: 5,
      height: 5,
      marginTop: 2,
      marginLeft: 12,
      marginRight: 12
    }
    const userMenuStyle = Object.assign({}, menuTextStyle)

    // Whether or not a campaign should show on the dashboard
    // TODO: also make sure the user hasn't dismissed the campaign (`isCampaignShown` var)
    const isGlobalCampaignLive = !!((app && app.isGlobalCampaignLive))

    return (

        <UserBackgroundImage user={user} showError={this.showError.bind(this)} />
        { user
          ? (
            <FadeInDashboardAnimation>

                <MoneyRaised
                  app={app}
                  style={moneyRaisedStyle}
                  launchFireworks={this.launchFireworks.bind(this)}
                  />
                <CircleIcon
                  color={dashboardIconInactiveColor}
                  hoverColor={dashboardIconActiveColor}
                  style={bulletPointStyle}
                />
                <UserMenu app={app} user={user} style={userMenuStyle} />

            </FadeInDashboardAnimation>
            )
          : null
        }
        <WidgetsContainer
          user={user}
          isCampaignLive={isGlobalCampaignLive}
          showError={this.showError.bind(this)}
          />
        { user
          ? (
            <FadeInDashboardAnimation>
              <CampaignBaseContainer
                user={user}
                isCampaignLive={isGlobalCampaignLive}
                />
            </FadeInDashboardAnimation>
            )
          : null
        }
        {
          this.state.showFireworks ? (

              <FadeInDashboardAnimation>
                <CloseIcon
                  style={{
                    position: 'absolute',
                    top: 14,
                    right: 14,
                    zIndex: 22,
                    height: 32,
                    width: 32,
                    color: '#FFF',
                    cursor: 'pointer'
                  }}
                  onClick={() => { this.launchFireworks(false) }}
                />
              </FadeInDashboardAnimation>
              <FadeInDashboardAnimation>

                    Tabbers, together we've raised over half a million dollars for charity. Congrats, and thank you!

                    <RaisedButton
                      label='Share This Milestone'
                      style={{
                        marginTop: 8
                      }}
                      primary
                    />

              </FadeInDashboardAnimation>
              <Fireworks
                width={window.innerWidth}
                height={window.innerHeight}
                background={'rgba(0, 0, 0, 0.01)'}
                style={{
                  position: 'absolute',
                  top: 0,
                  left: 0,
                  zIndex: 20
                }}
              />

          )
          : null
        }
        <Ad
          adId='div-gpt-ad-1464385742501-0'
          adSlotId='/43865596/HBTR'
          width={300}
          height={250}
          style={{
            position: 'absolute',
            bottom: 10,
            right: 10,
            display: 'block'
          }} />
        <Ad
          adId='div-gpt-ad-1464385677836-0'
          adSlotId='/43865596/HBTL'
          width={728}
          height={90}
          style={{
            position: 'absolute',
            bottom: 10,
            right: 320,
            display: 'block'
          }} />
        { user ? <LogTab user={user} /> : null }
        { user ? <LogRevenue user={user} /> : null }
        { user ? <LogAccountCreation user={user} /> : null }
        { errorMessage
          ? <ErrorMessage message={errorMessage}
            onRequestClose={this.clearError.bind(this)} />
          : null }

    )
  }
}

Dashboard.propTypes = {
  user: PropTypes.shape({
    id: PropTypes.string.isRequired
  }),
  app: PropTypes.shape({
    isGlobalCampaignLive: PropTypes.bool
  })
}

Dashboard.defaultProps = {
  app: {
    isGlobalCampaignLive: false
  }
}

export default Dashboard
<|endoftext|>


<commit_before>
// Function to check if location is hacker news.
function checkLocation() {
	var hackerNewsHost;

	hackerNewsHost = "news.ycombinator.com";

	// return true or false.
	if ( location.host == hackerNewsHost ) {
		return true;
	} else {
		return false;
	}
}

function nodelistToArray(nodelist) {
	var array = [];

	for (var i = 0; i < nodelist.length; i++ )
		array.push( nodelist[i] );

	return array;
}

// Main function for when on hacker news.
function onHackerNews() {
	var linkList,
		linkArray;

	// grab all links including "more" link
	linkList = document.querySelectorAll("td.title a");

	// remove the last element ("more" link)
	linkArray = nodelistToArray(linkList);
	console.log(linkArray);
}

// check if the page is hacker news and run onHackerNews if so.
var locationIsHackerNews;

locationIsHackerNews = checkLocation();

if ( locationIsHackerNews ) onHackerNews();<commit_msg>remove last element from array.<commit_after>
// Function to check if location is hacker news.
function checkLocation() {
	var hackerNewsHost;

	hackerNewsHost = "news.ycombinator.com";

	// return true or false.
	if ( location.host == hackerNewsHost ) {
		return true;
	} else {
		return false;
	}
}

function nodelistToArray(nodelist) {
	var array = [];

	for (var i = 0; i < nodelist.length; i++ )
		array.push( nodelist[i] );

	return array;
}

// Main function for when on hacker news.
function onHackerNews() {
	var linkList,
		linkArray;

	// grab all links including "more" link and conver to array.
	linkList = document.querySelectorAll("td.title a");
	linkArray = nodelistToArray(linkList);

	// remove the last element ("more" link)
	linkArray.splice(31, 0);

	console.log(linkArray);
}

// check if the page is hacker news and run onHackerNews if so.
var locationIsHackerNews;

locationIsHackerNews = checkLocation();

if ( locationIsHackerNews ) onHackerNews();<|endoftext|>


<commit_before>Iago.ThemesController = Ember.ArrayController.extend({
  needs: 'use_case',
  actions: {
    create: function() {
      var name = this.get('newThemeName'),
          controller = this;
      if (!name.trim()) { return; }

      var theme = Iago.Theme.create({
        org: 'iago',
        name: name,
        version: 'master'
      });

      if (!Iago.ThemeModel.find(name)) {
        Ember.$.post('/themes', { name: name }, function(data) {
          console.log(controller.content);
        });
      }

      controller.content.addObject(theme);

      Iago.UseCaseModel.addTheme(this.get('controllers.use_case.name'), name);
      Iago.UseCaseModel.save(this.get('controllers.use_case'));
    }
  }
});
<commit_msg>Add theme to ThemeModel if it does not exist<commit_after>Iago.ThemesController = Ember.ArrayController.extend({
  needs: 'use_case',
  actions: {
    create: function() {
      var name = this.get('newThemeName'),
          controller = this;
      if (!name.trim()) { return; }

      var theme = Iago.Theme.create({
        org: 'iago',
        name: name,
        version: 'master'
      });

      if (!Iago.ThemeModel.find(name)) {
        Ember.$.post('/themes', { name: name }, function(data) {
          console.log(controller.content);
        });
        Iago.ThemeModel.push(theme);
      }

      controller.content.addObject(theme);

      Iago.UseCaseModel.addTheme(this.get('controllers.use_case.name'), name);
      Iago.UseCaseModel.save(this.get('controllers.use_case'));
    }
  }
});
<|endoftext|>


<commit_before>/*
 This file is a part of libertysoil.org website
 Copyright (C) 2015  Loki Education (Social Enterprise)

 This program is free software: you can redistribute it and/or modify
 it under the terms of the GNU Affero General Public License as published by
 the Free Software Foundation, either version 3 of the License, or
 (at your option) any later version.

 This program is distributed in the hope that it will be useful,
 but WITHOUT ANY WARRANTY; without even the implied warranty of
 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 GNU Affero General Public License for more details.

 You should have received a copy of the GNU Affero General Public License
 along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */

/**
 * Matches the menu item against the path.
 */
function matchItem(item, path) {
  return (item.regexp && path.match(item.regexp) !== null) ||
          item.path === path;
}

/**
 * Recursive depth-first search on items of MenuTree.
 */
function find(currentPath, items) {
  for (const item of items) {
    if (matchItem(item, currentPath)) {
      return item;
    }

    if (item.children) {
      const result = find(currentPath, item.children);

      if (result) {
        return result;
      }
    }
  }

  return null;
}

export class MenuTree {
  /**
   * Menu item format:
   * `
   *   {
   *     name: 'Name', // Any property
   *     path: '/path', // String representing a path.
   *     regexp: /\/path/, // Optional regexp
   *     children: [] // Optional array of menu items
   *   }
   * `
   * When `regexp` is specified it will be used for matching instead of `path`.
   * @param {Object[]} items An array of menu items.
   */
  constructor(items) {
    this.items = Object.freeze(items);
  }

  /**
   * Finds the root menu item for the current path.
   * @param {String} currentPath A path without a query.
   */
  getCurrentRoot(currentPath) {
    // Match children first
    for (const item of this.items) {
      if (item.children) {
        const current = find(currentPath, item.children);

        if (current) {
          return item;
        }
      }
    }

    // then root items
    for (const item of this.items) {
      if (matchItem(item, currentPath)) {
        return item;
      }
    }

    return null;
  }

  /**
   * Finds the matching menu item for the current path.
   * @param {String} currentPath A path without a query.
   */
  getCurrent(currentPath) {
    return find(currentPath, this.items);
  }
}

/**
 * Menu for tool pages (/tools/*).
 */
export const toolsMenu = new MenuTree([
  {
    name: 'Account',
    path: '/tools/account',
    children: [
      {
        name: 'Welcome',
        path: '/tools/account'
      },
      {
        name: 'Your security',
        path: '/tools/account/security',
        children: [
          { name: 'Password change', path: '/tools/account/password-change' }
        ]
      }
    ]
  },
  {
    name: 'Tags',
    path: '/tools/tags',
    children: [
      { name: 'Schools', path: '/tools/schools', regexp: /\/tools\/schools/ }
    ]
  },
  {
    name: 'My',
    path: '/tools/my',
    children: [
      { name: 'Posts', path: '/tools/my/posts' }
    ]
  },
  {
    name: 'People',
    path: '/tools/people',
    children: [
      { name: 'Following', path: '/tools/people/following' }
    ]
  },
  {
    name: 'Account',
    path: '/tools/account',
    children: [
      { name: 'Password', path: '/tools/account/password' }
    ]
  }
]);
<commit_msg>Remove unused items from tools menu<commit_after>/*
 This file is a part of libertysoil.org website
 Copyright (C) 2015  Loki Education (Social Enterprise)

 This program is free software: you can redistribute it and/or modify
 it under the terms of the GNU Affero General Public License as published by
 the Free Software Foundation, either version 3 of the License, or
 (at your option) any later version.

 This program is distributed in the hope that it will be useful,
 but WITHOUT ANY WARRANTY; without even the implied warranty of
 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 GNU Affero General Public License for more details.

 You should have received a copy of the GNU Affero General Public License
 along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */

/**
 * Matches the menu item against the path.
 */
function matchItem(item, path) {
  return (item.regexp && path.match(item.regexp) !== null) ||
          item.path === path;
}

/**
 * Recursive depth-first search on items of MenuTree.
 */
function find(currentPath, items) {
  for (const item of items) {
    if (matchItem(item, currentPath)) {
      return item;
    }

    if (item.children) {
      const result = find(currentPath, item.children);

      if (result) {
        return result;
      }
    }
  }

  return null;
}

export class MenuTree {
  /**
   * Menu item format:
   * `
   *   {
   *     name: 'Name', // Any property
   *     path: '/path', // String representing a path.
   *     regexp: /\/path/, // Optional regexp
   *     children: [] // Optional array of menu items
   *   }
   * `
   * When `regexp` is specified it will be used for matching instead of `path`.
   * @param {Object[]} items An array of menu items.
   */
  constructor(items) {
    this.items = Object.freeze(items);
  }

  /**
   * Finds the root menu item for the current path.
   * @param {String} currentPath A path without a query.
   */
  getCurrentRoot(currentPath) {
    // Match children first
    for (const item of this.items) {
      if (item.children) {
        const current = find(currentPath, item.children);

        if (current) {
          return item;
        }
      }
    }

    // then root items
    for (const item of this.items) {
      if (matchItem(item, currentPath)) {
        return item;
      }
    }

    return null;
  }

  /**
   * Finds the matching menu item for the current path.
   * @param {String} currentPath A path without a query.
   */
  getCurrent(currentPath) {
    return find(currentPath, this.items);
  }
}

/**
 * Menu for tool pages (/tools/*).
 */
export const toolsMenu = new MenuTree([
  {
    name: 'Account',
    path: '/tools/account',
    children: [
      {
        name: 'Your security',
        children: [
          { name: 'Password', path: '/tools/account/password' }
        ]
      }
    ]
  },
  {
    name: 'Tags',
    path: '/tools/tags',
    children: [
      { name: 'Schools', path: '/tools/schools', regexp: /\/tools\/schools/ }
    ]
  },
  {
    name: 'My',
    path: '/tools/my',
    children: [
      { name: 'Posts', path: '/tools/my/posts' }
    ]
  },
  {
    name: 'People',
    path: '/tools/people',
    children: [
      { name: 'Following', path: '/tools/people/following' }
    ]
  }
]);
<|endoftext|>


<commit_before>import React from 'react';
import moment from 'moment';
import { DATE_TIMESTAMP } from './constants';
import { getSchemaCountryCode } from './utils/form-submit-transform';

export const directDepositWarning = (

    The Department of Treasury requires all federal benefit payments be made by
    electronic funds transfer (EFT), also called direct deposit. If you don’t
    have a bank account, you must get your payment through Direct Express Debit
    MasterCard. To request a Direct Express Debit MasterCard you must apply at{' '}

      www.usdirectexpress.com
    {' '}
    or by telephone at 1-800-333-1795. If you chose
    not to enroll, you must contact representatives handling waiver requests for
    the Department of Treasury at
    1-888-224-2950. They will address any questions
    or concerns you may have and encourage your participation in EFT.

);

export const chapter30Label = (
  <>
    Montgomery GI Bill Active Duty (Chapter 30)
    <va-additional-info trigger="Learn more">

        Our records indicate you may be eligible for this benefit because you
        served at least two years on active duty and were honorably discharged.
        If you give up this benefit, VA will pay you for any eligible kickers
        associated with it.

        Learn more about the Montgomery GI Bill Active Duty

    </va-additional-info>
  </>
);

export const chapter1606Label = (
  <>
    Montgomery GI Bill Selected Reserve (Chapter 1606)
    <va-additional-info trigger="Learn more">

        Our records indicate you may be eligible for this benefit because you
        agreed to serve six years in the Selected Reserve. If you give up this
        benefit, VA will pay you for any eligible kickers associated with it.

        Learn more about the Montgomery GI Bill Selected Reserve

    </va-additional-info>
  </>
);

export const unsureDescription = (
  <>
    Note: After you submit this applicaton, a VA representative
    will reach out to help via your preferred contact method.
  </>
);

export const post911GiBillNote = (

You’re applying for the Post-9/11 GI Bill®

      At this time, you can only apply for the Post-9/11 GI Bill (Chapter 33)
      benefits through this application. Doing so will require that you give up
      one other benefit you may be eligible for. You cannot change your decision
      after you submit this application.

);

/**
 * Converts a number to a string, preserving a minimum number of integer
 * digits.
 * Examples:
 * (5, 0) => '5'
 * (5, 1) => '5'
 * (5, 2) => '05'
 * (5, 3) => '005'
 * (2022, 2) => '2022'
 * (2022, 5) => '02022'
 * (3.14159, 1) => '3.14159'
 * (3.14159, 2) => '03.14159'
 * (3.14159, 3) => '003.14159'
 * @param {number} n The number we want to convert.
 * @param {number} minDigits The minimum number of integer digits to preserve.
 * @returns {string} The number formatted as a string.
 */
export const convertNumberToStringWithMinimumDigits = (n, minDigits) => {
  return n.toLocaleString('en-US', {
    minimumIntegerDigits: minDigits,
    useGrouping: false,
  });
};

/**
 * Formats a date in human-readable form. For example:
 * January 1, 2000.
 *
 * @param {*} rawDate A date in the form '01-01-2000'
 * @returns A human-readable date string.
 */
export const formatReadableDate = (rawDate, minimumDateDigits = 1) => {
  const months = [
    'January',
    'February',
    'March',
    'April',
    'May',
    'June',
    'July',
    'August',
    'September',
    'October',
    'November',
    'December',
  ];

  const dateParts = rawDate?.split('-');
  let date;

  if (rawDate && dateParts.length >= 3) {
    date = new Date(
      Number.parseInt(dateParts[0], 10),
      Number.parseInt(dateParts[1], 10) - 1,
      Number.parseInt(dateParts[2], 10),
    );
  }

  if (!date) {
    return '';
  }

  return `${months[date.getMonth()]} ${convertNumberToStringWithMinimumDigits(
    date.getDate(),
    minimumDateDigits,
  )}, ${date.getFullYear()}`;
};

/**
 * This function recieves the uiSchema and formData objects and returns
 * a comma separated list of the selected checkboxes in a checkbox
 * group.  The formData object contains the name of the checkbox
 * elements in the group as the keys with a boolean value as the proerty
 * (true if checked, false if unchecked). Here is an example:
 *
 * ```
 * {
 *   canEmailNotify: true,
 *   canTextNotify: false
 * }
 * ```
 *
 * The following function splits the formData object into a 2D array
 * where each array item has two properties: the key and the form value
 * (a boolean).  So, our example above would end up being:
 *
 * ```
 * [
 *   ['canEmailNotify', true],
 *   ['canTextNotify', false]
 * ]
 * ```
 *
 * It then filters the formData object, selecting only the checkboxes
 * that are checked.  Then, it retrieves the titles of the selected keys
 * from the uiSchema and joins them with a comma.
 *
 * Also, Object.entries splits the formData data object into
 *
 * @param {*} uiSchema UI Schema object
 * @param {*} formData Form Data object.
 * @returns Comma separated list of selected checkbox titles.
 */
export const getSelectedCheckboxes = (uiSchema, formData) =>
  Object.entries(formData)
    .filter(checkboxOption => checkboxOption[1]) // true/false
    .map(checkboxOption => checkboxOption[0]) // object key
    .map(selectedCheckboxKey => uiSchema[selectedCheckboxKey]['ui:title'])
    .join(', ');

function transformServiceHistory(serviceHistory) {
  return {
    dateRange: {
      from: moment(serviceHistory?.beginDate).format(DATE_TIMESTAMP),
      to: moment(serviceHistory?.endDate).format(DATE_TIMESTAMP),
    },
    exclusionPeriods: serviceHistory?.exclusionPeriods?.map(exclusionPeriod => {
      return {
        from: moment(exclusionPeriod.beginDate).format(DATE_TIMESTAMP),
        to: moment(exclusionPeriod.endDate).format(DATE_TIMESTAMP),
      };
    }),
    trainingPeriods: serviceHistory?.trainingPeriods?.map(exclusionPeriod => {
      return {
        from: moment(exclusionPeriod.beginDate).format(DATE_TIMESTAMP),
        to: moment(exclusionPeriod.endDate).format(DATE_TIMESTAMP),
      };
    }),
    serviceBranch: serviceHistory?.branchOfService,
    serviceCharacter: serviceHistory?.characterOfService,
    separationReason: serviceHistory?.reasonForSeparation,
  };
}

function mapNotificaitonMethod(notificationMethod) {
  if (notificationMethod === 'mail') {
    return 'Mail';
  }
  if (notificationMethod === 'email') {
    return 'Email';
  }
  return notificationMethod;
}

export const transformAlphaOnlyLowercase = str =>
  str.toLowerCase().replace(/[^a-z]/g, '');

export const equalsAlphaOnlyIgnoreCase = (a, b) => {
  return transformAlphaOnlyLowercase(a) === transformAlphaOnlyLowercase(b);
};

export function prefillTransformer(pages, formData, metadata, state) {
  const claimant = state.data?.formData?.data?.attributes?.claimant || {};
  const serviceData = state.data?.formData?.data?.attributes?.serviceData || [];
  const contactInfo = claimant?.contactInfo || {};

  const newData = {
    ...formData,
    formId: state.data?.formData?.data?.id,
    claimantId: claimant.claimantId,
    'view:userFullName': {
      userFullName: {
        first: claimant.firstName || undefined,
        middle: claimant.middleName || undefined,
        last: claimant.lastName || undefined,
      },
    },
    dateOfBirth: claimant.dateOfBirth,
    email: {
      email: contactInfo.emailAddress,
      confirmEmail: contactInfo.emailAddress,
    },
    'view:phoneNumbers': {
      mobilePhoneNumber: {
        phone: contactInfo?.mobilePhoneNumber?.replace(/\D/g, '') || undefined,
      },
      phoneNumber: {
        phone: contactInfo?.homePhoneNumber?.replace(/\D/g, '') || undefined,
      },
    },
    'view:contactMethod': {
      contactMethod: mapNotificaitonMethod(claimant?.notificationMethod),
    },
    'view:mailingAddress': {
      address: {
        street: contactInfo?.addressLine1,
        street2: contactInfo?.addressLine2 || undefined,
        city: contactInfo?.city,
        state: contactInfo?.stateCode,
        postalCode: contactInfo?.zipcode,
        country: getSchemaCountryCode(contactInfo?.countryCode),
      },
      livesOnMilitaryBase:
        contactInfo?.countryCode !== 'US' &&
        contactInfo?.addressType === 'MILITARY_OVERSEAS',
    },
    toursOfDuty: serviceData.map(transformServiceHistory),
  };

  if (claimant?.suffix) {
    newData['view:userFullName'].userFullName.suffix =
      state?.form?.pages?.applicantInformation?.schema?.properties[
        'view:userFullName'
      ]?.properties?.userFullName?.properties?.suffix?.enum?.find(e =>
        equalsAlphaOnlyIgnoreCase(e, claimant.suffix),
      ) || undefined;
  }

  return {
    metadata,
    formData: newData,
    pages,
    state,
  };
}
<commit_msg>fixed aria-label issue for anchor tags (#21143)<commit_after>import React from 'react';
import moment from 'moment';
import { DATE_TIMESTAMP } from './constants';
import { getSchemaCountryCode } from './utils/form-submit-transform';

export const directDepositWarning = (

    The Department of Treasury requires all federal benefit payments be made by
    electronic funds transfer (EFT), also called direct deposit. If you don’t
    have a bank account, you must get your payment through Direct Express Debit
    MasterCard. To request a Direct Express Debit MasterCard you must apply at{' '}

      www.usdirectexpress.com
    {' '}
    or by telephone at <va-telephone contact="8003331795" />. If you chose not
    to enroll, you must contact representatives handling waiver requests for the
    Department of Treasury at <va-telephone contact="8882242950" />. They will
    address any questions or concerns you may have and encourage your
    participation in EFT.

);

export const chapter30Label = (
  <>
    Montgomery GI Bill Active Duty (Chapter 30)
    <va-additional-info trigger="Learn more">

        Our records indicate you may be eligible for this benefit because you
        served at least two years on active duty and were honorably discharged.
        If you give up this benefit, VA will pay you for any eligible kickers
        associated with it.

        Learn more about the Montgomery GI Bill Active Duty

    </va-additional-info>
  </>
);

export const chapter1606Label = (
  <>
    Montgomery GI Bill Selected Reserve (Chapter 1606)
    <va-additional-info trigger="Learn more">

        Our records indicate you may be eligible for this benefit because you
        agreed to serve six years in the Selected Reserve. If you give up this
        benefit, VA will pay you for any eligible kickers associated with it.

        Learn more about the Montgomery GI Bill Selected Reserve

    </va-additional-info>
  </>
);

export const unsureDescription = (
  <>
    Note: After you submit this applicaton, a VA representative
    will reach out to help via your preferred contact method.
  </>
);

export const post911GiBillNote = (

You’re applying for the Post-9/11 GI Bill®

      At this time, you can only apply for the Post-9/11 GI Bill (Chapter 33)
      benefits through this application. Doing so will require that you give up
      one other benefit you may be eligible for. You cannot change your decision
      after you submit this application.

);

/**
 * Converts a number to a string, preserving a minimum number of integer
 * digits.
 * Examples:
 * (5, 0) => '5'
 * (5, 1) => '5'
 * (5, 2) => '05'
 * (5, 3) => '005'
 * (2022, 2) => '2022'
 * (2022, 5) => '02022'
 * (3.14159, 1) => '3.14159'
 * (3.14159, 2) => '03.14159'
 * (3.14159, 3) => '003.14159'
 * @param {number} n The number we want to convert.
 * @param {number} minDigits The minimum number of integer digits to preserve.
 * @returns {string} The number formatted as a string.
 */
export const convertNumberToStringWithMinimumDigits = (n, minDigits) => {
  return n.toLocaleString('en-US', {
    minimumIntegerDigits: minDigits,
    useGrouping: false,
  });
};

/**
 * Formats a date in human-readable form. For example:
 * January 1, 2000.
 *
 * @param {*} rawDate A date in the form '01-01-2000'
 * @returns A human-readable date string.
 */
export const formatReadableDate = (rawDate, minimumDateDigits = 1) => {
  const months = [
    'January',
    'February',
    'March',
    'April',
    'May',
    'June',
    'July',
    'August',
    'September',
    'October',
    'November',
    'December',
  ];

  const dateParts = rawDate?.split('-');
  let date;

  if (rawDate && dateParts.length >= 3) {
    date = new Date(
      Number.parseInt(dateParts[0], 10),
      Number.parseInt(dateParts[1], 10) - 1,
      Number.parseInt(dateParts[2], 10),
    );
  }

  if (!date) {
    return '';
  }

  return `${months[date.getMonth()]} ${convertNumberToStringWithMinimumDigits(
    date.getDate(),
    minimumDateDigits,
  )}, ${date.getFullYear()}`;
};

/**
 * This function recieves the uiSchema and formData objects and returns
 * a comma separated list of the selected checkboxes in a checkbox
 * group.  The formData object contains the name of the checkbox
 * elements in the group as the keys with a boolean value as the proerty
 * (true if checked, false if unchecked). Here is an example:
 *
 * ```
 * {
 *   canEmailNotify: true,
 *   canTextNotify: false
 * }
 * ```
 *
 * The following function splits the formData object into a 2D array
 * where each array item has two properties: the key and the form value
 * (a boolean).  So, our example above would end up being:
 *
 * ```
 * [
 *   ['canEmailNotify', true],
 *   ['canTextNotify', false]
 * ]
 * ```
 *
 * It then filters the formData object, selecting only the checkboxes
 * that are checked.  Then, it retrieves the titles of the selected keys
 * from the uiSchema and joins them with a comma.
 *
 * Also, Object.entries splits the formData data object into
 *
 * @param {*} uiSchema UI Schema object
 * @param {*} formData Form Data object.
 * @returns Comma separated list of selected checkbox titles.
 */
export const getSelectedCheckboxes = (uiSchema, formData) =>
  Object.entries(formData)
    .filter(checkboxOption => checkboxOption[1]) // true/false
    .map(checkboxOption => checkboxOption[0]) // object key
    .map(selectedCheckboxKey => uiSchema[selectedCheckboxKey]['ui:title'])
    .join(', ');

function transformServiceHistory(serviceHistory) {
  return {
    dateRange: {
      from: moment(serviceHistory?.beginDate).format(DATE_TIMESTAMP),
      to: moment(serviceHistory?.endDate).format(DATE_TIMESTAMP),
    },
    exclusionPeriods: serviceHistory?.exclusionPeriods?.map(exclusionPeriod => {
      return {
        from: moment(exclusionPeriod.beginDate).format(DATE_TIMESTAMP),
        to: moment(exclusionPeriod.endDate).format(DATE_TIMESTAMP),
      };
    }),
    trainingPeriods: serviceHistory?.trainingPeriods?.map(exclusionPeriod => {
      return {
        from: moment(exclusionPeriod.beginDate).format(DATE_TIMESTAMP),
        to: moment(exclusionPeriod.endDate).format(DATE_TIMESTAMP),
      };
    }),
    serviceBranch: serviceHistory?.branchOfService,
    serviceCharacter: serviceHistory?.characterOfService,
    separationReason: serviceHistory?.reasonForSeparation,
  };
}

function mapNotificaitonMethod(notificationMethod) {
  if (notificationMethod === 'mail') {
    return 'Mail';
  }
  if (notificationMethod === 'email') {
    return 'Email';
  }
  return notificationMethod;
}

export const transformAlphaOnlyLowercase = str =>
  str.toLowerCase().replace(/[^a-z]/g, '');

export const equalsAlphaOnlyIgnoreCase = (a, b) => {
  return transformAlphaOnlyLowercase(a) === transformAlphaOnlyLowercase(b);
};

export function prefillTransformer(pages, formData, metadata, state) {
  const claimant = state.data?.formData?.data?.attributes?.claimant || {};
  const serviceData = state.data?.formData?.data?.attributes?.serviceData || [];
  const contactInfo = claimant?.contactInfo || {};

  const newData = {
    ...formData,
    formId: state.data?.formData?.data?.id,
    claimantId: claimant.claimantId,
    'view:userFullName': {
      userFullName: {
        first: claimant.firstName || undefined,
        middle: claimant.middleName || undefined,
        last: claimant.lastName || undefined,
      },
    },
    dateOfBirth: claimant.dateOfBirth,
    email: {
      email: contactInfo.emailAddress,
      confirmEmail: contactInfo.emailAddress,
    },
    'view:phoneNumbers': {
      mobilePhoneNumber: {
        phone: contactInfo?.mobilePhoneNumber?.replace(/\D/g, '') || undefined,
      },
      phoneNumber: {
        phone: contactInfo?.homePhoneNumber?.replace(/\D/g, '') || undefined,
      },
    },
    'view:contactMethod': {
      contactMethod: mapNotificaitonMethod(claimant?.notificationMethod),
    },
    'view:mailingAddress': {
      address: {
        street: contactInfo?.addressLine1,
        street2: contactInfo?.addressLine2 || undefined,
        city: contactInfo?.city,
        state: contactInfo?.stateCode,
        postalCode: contactInfo?.zipcode,
        country: getSchemaCountryCode(contactInfo?.countryCode),
      },
      livesOnMilitaryBase:
        contactInfo?.countryCode !== 'US' &&
        contactInfo?.addressType === 'MILITARY_OVERSEAS',
    },
    toursOfDuty: serviceData.map(transformServiceHistory),
  };

  if (claimant?.suffix) {
    newData['view:userFullName'].userFullName.suffix =
      state?.form?.pages?.applicantInformation?.schema?.properties[
        'view:userFullName'
      ]?.properties?.userFullName?.properties?.suffix?.enum?.find(e =>
        equalsAlphaOnlyIgnoreCase(e, claimant.suffix),
      ) || undefined;
  }

  return {
    metadata,
    formData: newData,
    pages,
    state,
  };
}
<|endoftext|>


<commit_before>import React, {Component} from 'react'
import PropTypes from 'prop-types'
import RedactedInput from './RedactedInput'

class PagerDutyConfig extends Component {
  constructor(props) {
    super(props)
    this.state = {
      testEnabled: this.props.enabled,
    }
    this.refFunc = r => {
      this.serviceKey = r
    }
  }

  handleSubmit = async e => {
    e.preventDefault()

    const properties = {
      'service-key': this.serviceKey.value,
      url: this.url.value,
    }

    const success = await this.props.onSave(properties)
    if (success) {
      this.setState({testEnabled: true})
    }
  }

  disableTest = () => {
    this.setState({testEnabled: false})
  }

  render() {
    const {options} = this.props.config
    const {url} = options
    const serviceKey = options['service-key']
    return (
      <form onSubmit={this.handleSubmit}>

          Service Key
          <RedactedInput
            defaultValue={serviceKey || ''}
            id="service-key"
            refFunc={this.refFunc}
            disableTest={this.disableTest}
          />

          PagerDuty URL
          <input
            className="form-control"
            id="url"
            type="text"
            ref={r => (this.url = r)}
            defaultValue={url || ''}
            onChange={this.disableTest}
          />

          <button
            className="btn btn-primary"
            type="submit"
            disabled={this.state.testEnabled}
          >

            Save Changes
          </button>
          <button
            className="btn btn-primary"
            disabled={!this.state.testEnabled}
            onClick={this.props.onTest}
          >

            Send Test Alert
          </button>

      </form>
    )
  }
}

const {bool, func, shape, string} = PropTypes

PagerDutyConfig.propTypes = {
  config: shape({
    options: shape({
      'service-key': bool.isRequired,
      url: string.isRequired,
    }).isRequired,
  }).isRequired,
  onSave: func.isRequired,
  onTest: func.isRequired,
  enabled: bool.isRequired,
}

export default PagerDutyConfig
<commit_msg>Change PagerDutyConfig to typescript<commit_after><|endoftext|>


<commit_before>// import oSelect from './components/o-select.js';
// import cMenu from './components/c-menu.js';
// import cFoo from './components/c-foo.js';

// Oxe.router.on('route:after', function () {
//     console.log('route:after');
// });

Oxe.setup({
    loader: {
        type: 'es'
    },
    fetcher: {
        request: function () {
            console.log(arguments);
        },
        response: function () {
            console.log(arguments);
        }
    },
    component: {
        components: [
            // cFoo,
            // cMenu,
            // oSelect
            './components/c-foo.js',
            './components/c-menu.js',
            './components/o-select.js'
        ]
    },
    router: {
        // mode: 'href',
        routes: [
            'index',
            'js',
            'test',
            'select',
            'class',
            'input',
            'style',
            'each',
            '(~)'
        ]
    }
}).catch(console.error);
<commit_msg>removed comments<commit_after>
// Oxe.router.on('route:after', function () {
//     console.log('route:after');
// });

Oxe.setup({
    loader: {
        type: 'es'
    },
    fetcher: {
        request: function () {
            console.log(arguments);
        },
        response: function () {
            console.log(arguments);
        }
    },
    component: {
        components: [
            './components/c-foo.js',
            './components/c-menu.js',
            './components/o-select.js'
        ]
    },
    router: {
        // mode: 'href',
        routes: [
            'index',
            'js',
            'test',
            'select',
            'class',
            'input',
            'style',
            'each',
            '(~)'
        ]
    }
}).catch(console.error);
<|endoftext|>


<commit_before>function wxpay(app, money, orderId, redirectUrl) {
  let remark = "在线充值";
  let nextAction = {};
  if (orderId != 0) {
    remark = "支付订单 ：" + orderId;
    nextAction = { type: 0, id: orderId };
  }
  wx.request({
    url: 'https://api.it120.cc/' + app.globalData.subDomain + '/pay/wxapp/get-pay-data',
    data: {
      token: wx.getStorageSync('token'),
      money:money,
      remark: remark,
      payName:"在线支付",
      nextAction: nextAction
    },
    //method:'POST',
    success: function(res){
      if(res.data.code == 0){
        // 发起支付
        wx.requestPayment({
          timeStamp:res.data.data.timeStamp,
          nonceStr:res.data.data.nonceStr,
          package:'prepay_id=' + res.data.data.prepayId,
          signType:'MD5',
          paySign:res.data.data.sign,
          fail:function (aaa) {
            wx.showToast({title: '支付失败:' + aaa})
          },
          success:function () {
            wx.showToast({title: '支付成功'})
            wx.redirectTo({
              url: redirectUrl
            });
          }
        })
      } else {
        wx.showToast({ title: '服务器忙' + res.data.code + res.data.msg})
      }
    }
  })
}

module.exports = {
  wxpay: wxpay
}
<commit_msg>更换新版小程序支付接口<commit_after>function wxpay(app, money, orderId, redirectUrl) {
  let remark = "在线充值";
  let nextAction = {};
  if (orderId != 0) {
    remark = "支付订单 ：" + orderId;
    nextAction = { type: 0, id: orderId };
  }
  wx.request({
    url: 'https://api.it120.cc/' + app.globalData.subDomain + '/pay/wx/wxapp',
    data: {
      token: wx.getStorageSync('token'),
      money:money,
      remark: remark,
      payName:"在线支付",
      nextAction: nextAction
    },
    //method:'POST',
    success: function(res){
      if(res.data.code == 0){
        // 发起支付
        wx.requestPayment({
          timeStamp:res.data.data.timeStamp,
          nonceStr:res.data.data.nonceStr,
          package:'prepay_id=' + res.data.data.prepayId,
          signType:'MD5',
          paySign:res.data.data.sign,
          fail:function (aaa) {
            wx.showToast({title: '支付失败:' + aaa})
          },
          success:function () {
            wx.showToast({title: '支付成功'})
            wx.redirectTo({
              url: redirectUrl
            });
          }
        })
      } else {
        wx.showToast({ title: '服务器忙' + res.data.code + res.data.msg})
      }
    }
  })
}

module.exports = {
  wxpay: wxpay
}
<|endoftext|>


<commit_before>import React from 'react'
import { constants } from '../gameStore'

const DropPiece = ({ column, dropped, player, style }) => {
  let clsName = 'piece drop-piece'
  if (player === constants.PIECE_PLAYER_1) {
    clsName += ' piece-player-1'
  } else if (player === constants.PIECE_PLAYER_2) {
    clsName += ' piece-player-2'
  }
  if (dropped) {
    clsName += ' dropped'
  }
  return

}

export default DropPiece
<commit_msg>hide drop piece on small screen<commit_after>import React from 'react'
import { constants } from '../gameStore'

const DropPiece = ({ column, dropped, player, style }) => {
  let clsName = 'piece drop-piece hidden-xs-down'
  if (player === constants.PIECE_PLAYER_1) {
    clsName += ' piece-player-1'
  } else if (player === constants.PIECE_PLAYER_2) {
    clsName += ' piece-player-2'
  }
  if (dropped) {
    clsName += ' dropped'
  }
  return

}

export default DropPiece
<|endoftext|>


<commit_before>import blessed from 'blessed';
import devnull from 'dev-null';
import _ from 'lodash';

import BlessedRootComponent from './blessed/RootComponent';
//import ReactBlessedRootComponent from './react-blessed/RootComponent';
import conf from 'conf';
import EventManager from 'lib/EventManager';
import SingletonMixin from 'lib/mixins/SingletonMixin';

const COMPONENT_MODES = [
  'blessed',
  'react-blessed'
];

export default class Screen {

  constructor({ componentMode }) {

    if (!COMPONENT_MODES.some((v) => componentMode === v)) {
      throw new Error(`${componentMode} is invalid component-mode`);
    }
    this._componentMode = componentMode;

    let screen;
    switch (this._componentMode) {
      case 'blessed':
        screen = this._createBlessedScreen();
        break;
      case 'react-blessed':
        screen = this._createReactBlessedScreen();
        break;
    }
    this._screen = screen;

    Object.defineProperty(this, 'screen', { get() { return this._screen; } });
  }

  _createBlessedOptions() {
    let options = {
      debug: true,
      title: 'Escape From The Maze'
    };

    if (conf.ignoreScreenOutput) {
      options.output = devnull();
    }

    return options;
  }

  _createBlessedScreen() {
    let screen = blessed.screen(this._createBlessedOptions());
    new BlessedRootComponent({ screen, $parent: screen });
    return screen;
  }

  _createReactBlessedScreen() {
  }
}

Object.assign(Screen, SingletonMixin);
<commit_msg>Disable screen.debugLog's default key bind<commit_after>import blessed from 'blessed';
import devnull from 'dev-null';
import _ from 'lodash';
import reactBlessed from 'react-blessed';

import BlessedRootComponent from './blessed/RootComponent';
import ReactBlessedRootComponent from './react-blessed/RootComponent';
import conf from 'conf';
import EventManager from 'lib/EventManager';
import SingletonMixin from 'lib/mixins/SingletonMixin';

const COMPONENT_MODES = [
  'blessed',
  'react-blessed'
];

export default class Screen {

  constructor({ componentMode }) {

    if (!COMPONENT_MODES.some((v) => componentMode === v)) {
      throw new Error(`${componentMode} is invalid component-mode`);
    }

    let initializeScreen = {
      'blessed': this._initializeBlessedScreen.bind(this),
      'react-blessed': this._initializeReactBlessedScreen.bind(this)
    }[componentMode];

    this._screen = initializeScreen();
  }

  _createBlessedOptions() {
    let options = {
      debug: true,
      title: 'Escape From The Maze'
    };

    if (conf.ignoreScreenOutput) {
      options.output = devnull();
    }

    return options;
  }

  _initializeBlessedScreen() {
    let screen = blessed.screen(this._createBlessedOptions());
    // Disable default key bind for debug
    // Ref) blessed/lib/widgets/screen.js#L374
    screen.debugLog.unkey(['q', 'escape'], () => {});
    new BlessedRootComponent({ screen, $parent: screen });
    return screen;
  }

  _initializeReactBlessedScreen() {
    return reactBlessed.render(<RootComponent />, this._createBlessedOptions());
  }
}

Object.assign(Screen, SingletonMixin);
<|endoftext|>


<commit_before>define([
    'extensions/collection'
],
function (Collection) {
    var WeeklyApplications = Collection.extend({
        queryUrl: 'licensing',
        queryParams: function () {
          return {
            start_at: this.moment().startOf('week').subtract(8, 'weeks'),
            end_at: this.moment().startOf('week'),
            period: 'week'
          }
        },
        parse: function (response) {
            return response.data;
        }
    });

    return WeeklyApplications;
});
<commit_msg>keep applications collection sorted chronologically<commit_after>define([
    'extensions/collection'
],
function (Collection) {
    var WeeklyApplications = Collection.extend({

        queryUrl: 'licensing',

        queryParams: function () {
          return {
            start_at: this.moment().startOf('week').subtract(8, 'weeks'),
            end_at: this.moment().startOf('week'),
            period: 'week'
          }
        },

        parse: function (response) {
            return response.data;
        },

        /**
         * Keep sorted chronologically
         */
        comparator: function(model) {
          return +model.get('_start_at');
        }
    });

    return WeeklyApplications;
});
<|endoftext|>


<commit_before>/**
 * VisualEditor user interface Toolbar class.
 *
 * @copyright 2011-2012 VisualEditor Team and others; see AUTHORS.txt
 * @license The MIT License (MIT); see LICENSE.txt
 */

/**
 * Editing toolbar.
 *
 * @class
 * @constructor
 */
ve.ui.Toolbar = function ( $container, surfaceView, config ) {
	// Inheritance TODO: Do we still need it?
	ve.EventEmitter.call( this );
	if ( !surfaceView ) {
		return;
	}

	// Properties
	this.surfaceView = surfaceView;
	this.$ = $container;
	this.$groups = $( '

' ).prependTo( this.$ );
	this.tools = [];

	// Update tools on selection and all transactions.
	this.surfaceView.model.on( 'change', ve.proxy( this.updateTools, this ) );

	this.config = config || [
		{ 'name': 'history', 'items' : ['undo', 'redo'] },
		{ 'name': 'textStyle', 'items' : ['format'] },
		{ 'name': 'textStyle', 'items' : ['bold', 'italic', 'link', 'clear'] },
		{ 'name': 'list', 'items' : ['number', 'bullet', 'outdent', 'indent'] }
	];
	this.setup();
};

/* Methods */

/**
 * Triggers update events on all tools.
 *
 * @method
 */
ve.ui.Toolbar.prototype.updateTools = function () {
	var model = this.surfaceView.getModel(),
		doc = model.getDocument(),
		annotations,
		nodes = [],
		range = model.getSelection(),
		startNode,
		endNode,
		tool = this,
		i;

	if ( range !== null ) {
		if ( range.from === range.to ) {
			nodes.push( doc.getNodeFromOffset( range.from ) );
		} else {
			startNode = doc.getNodeFromOffset( range.from );
			endNode = doc.getNodeFromOffset ( range.end );

			if(startNode.type === 'document' || endNode.type === 'document') {
				// Clear state
				for ( i = 0; i < this.tools.length; i++ ) {
					this.tools[i].clearState();
				}
				return;
			}

			// These should be different, alas just in case.
			if ( startNode === endNode ) {
				nodes.push( startNode );
			} else {
				model.getDocument().getDocumentNode().traverseLeafNodes( function ( node ) {
					nodes.push( node );
					if( node === endNode ) {
						return false;
					}
				}, startNode );
			}
		}

		if ( range.getLength() > 0 ) {
			annotations = doc.getAnnotationsFromRange( range );
		} else {
			// Clear context
			tool.surfaceView.contextView.clear();
			annotations = doc.getAnnotationsFromOffset(
				doc.getNearestContentOffset( range.start - 1 )
			);
		}
		// Update state
		for ( i = 0; i < this.tools.length; i++ ) {
			this.tools[i].updateState( annotations, nodes );
		}
	} else {
		// Clear state
		for ( i = 0; i < this.tools.length; i++ ) {
			this.tools[i].clearState();
		}
	}
};

ve.ui.Toolbar.prototype.getSurfaceView = function () {
	return this.surfaceView;
};

ve.ui.Toolbar.prototype.setup = function () {
	var i, j, $group, tool, toolDefintion;
	for ( i = 0; i < this.config.length; i++ ) {
		$group = $( '
' )
			.addClass( 'es-toolbarGroup' )
			.addClass( 'es-toolbarGroup-' + this.config[i].name );
		if ( this.config[i].label ) {
			$group.append(
				$( '
' ).addClass( 'es-toolbarLabel' ).html( this.config[i].label )
			);
		}

		for ( j = 0; j < this.config[i].items.length; j++ ) {
			toolDefintion = ve.ui.Tool.tools[ this.config[i].items[j] ];
			if ( toolDefintion ) {
				tool = new toolDefintion.constructor(
					this, toolDefintion.name, toolDefintion.title, toolDefintion.data
				);
				this.tools.push( tool );
				$group.append( tool.$ );
			}
		}

		this.$groups.append( $group );
	}

};

ve.extendClass( ve.ui.Toolbar, ve.EventEmitter );
<commit_msg>Refactor ve.js utilities and improve documentation<commit_after>/**
 * VisualEditor user interface Toolbar class.
 *
 * @copyright 2011-2012 VisualEditor Team and others; see AUTHORS.txt
 * @license The MIT License (MIT); see LICENSE.txt
 */

/**
 * Editing toolbar.
 *
 * @class
 * @constructor
 */
ve.ui.Toolbar = function ( $container, surfaceView, config ) {
	// Inheritance TODO: Do we still need it?
	ve.EventEmitter.call( this );
	if ( !surfaceView ) {
		return;
	}

	// Properties
	this.surfaceView = surfaceView;
	this.$ = $container;
	this.$groups = $( '

' ).prependTo( this.$ );
	this.tools = [];

	// Update tools on selection and all transactions.
	this.surfaceView.model.on( 'change', ve.bind( this.updateTools, this ) );

	this.config = config || [
		{ 'name': 'history', 'items' : ['undo', 'redo'] },
		{ 'name': 'textStyle', 'items' : ['format'] },
		{ 'name': 'textStyle', 'items' : ['bold', 'italic', 'link', 'clear'] },
		{ 'name': 'list', 'items' : ['number', 'bullet', 'outdent', 'indent'] }
	];
	this.setup();
};

/* Methods */

/**
 * Triggers update events on all tools.
 *
 * @method
 */
ve.ui.Toolbar.prototype.updateTools = function () {
	var model = this.surfaceView.getModel(),
		doc = model.getDocument(),
		annotations,
		nodes = [],
		range = model.getSelection(),
		startNode,
		endNode,
		tool = this,
		i;

	if ( range !== null ) {
		if ( range.from === range.to ) {
			nodes.push( doc.getNodeFromOffset( range.from ) );
		} else {
			startNode = doc.getNodeFromOffset( range.from );
			endNode = doc.getNodeFromOffset ( range.end );

			if(startNode.type === 'document' || endNode.type === 'document') {
				// Clear state
				for ( i = 0; i < this.tools.length; i++ ) {
					this.tools[i].clearState();
				}
				return;
			}

			// These should be different, alas just in case.
			if ( startNode === endNode ) {
				nodes.push( startNode );
			} else {
				model.getDocument().getDocumentNode().traverseLeafNodes( function ( node ) {
					nodes.push( node );
					if( node === endNode ) {
						return false;
					}
				}, startNode );
			}
		}

		if ( range.getLength() > 0 ) {
			annotations = doc.getAnnotationsFromRange( range );
		} else {
			// Clear context
			tool.surfaceView.contextView.clear();
			annotations = doc.getAnnotationsFromOffset(
				doc.getNearestContentOffset( range.start - 1 )
			);
		}
		// Update state
		for ( i = 0; i < this.tools.length; i++ ) {
			this.tools[i].updateState( annotations, nodes );
		}
	} else {
		// Clear state
		for ( i = 0; i < this.tools.length; i++ ) {
			this.tools[i].clearState();
		}
	}
};

ve.ui.Toolbar.prototype.getSurfaceView = function () {
	return this.surfaceView;
};

ve.ui.Toolbar.prototype.setup = function () {
	var i, j, $group, tool, toolDefintion;
	for ( i = 0; i < this.config.length; i++ ) {
		$group = $( '
' )
			.addClass( 'es-toolbarGroup' )
			.addClass( 'es-toolbarGroup-' + this.config[i].name );
		if ( this.config[i].label ) {
			$group.append(
				$( '
' ).addClass( 'es-toolbarLabel' ).html( this.config[i].label )
			);
		}

		for ( j = 0; j < this.config[i].items.length; j++ ) {
			toolDefintion = ve.ui.Tool.tools[ this.config[i].items[j] ];
			if ( toolDefintion ) {
				tool = new toolDefintion.constructor(
					this, toolDefintion.name, toolDefintion.title, toolDefintion.data
				);
				this.tools.push( tool );
				$group.append( tool.$ );
			}
		}

		this.$groups.append( $group );
	}

};

ve.extendClass( ve.ui.Toolbar, ve.EventEmitter );
<|endoftext|>


<commit_before>/*global d3, utils, bib, Worker */
"use strict";
var model;

// model specification
// -------------------
// data stored internally as follows:
// tw: array of d3.map()s keyed to words as strings
// alpha: alpha values for topics
// meta: array of objects holding document citations

model = function (spec) {
    var my = spec || { }, // private members
        that = { }, // resultant object
        info, // accessors and pseudo-accessors
        n_docs,
        has_dt,
        tw,
        n,
        n_top_words,
        total_tokens,
        topic_total,
        alpha,
        meta,
        vocab,
        topic_scaled,
        topic_yearly, // time-slicing
        valid_year,
        yearly_total,
        topic_docs, // most salient _ in _
        topic_words,
        doc_topics,
        word_topics,
        year_topics,
        topic_label,
        set_dt, // methods for loading model data from strings
        set_tw,
        set_meta,
        set_topic_scaled;

    my.ready = { };
    my.worker = new Worker("js/worker.min.js");
    my.worker.fs = d3.map();
    my.worker.onmessage = function (e) {
        var f = my.worker.fs.get(e.data.what);
        if (f) {
            f(e.data.result);
        }
    };
    my.worker.callback = function (key, f) {
        my.worker.fs.set(key, f);
    };

    info = function (model_meta) {
        if (model_meta) {
            my.info = model_meta;
        }
        return my.info;
    };
    that.info = info;

    n_docs = function () {
        var result;
        if (my.n_docs !== undefined) {
            result = my.n_docs;
        } else if (my.meta) {
            result = my.meta.n_docs();
        }

        return result; // undefined if my.meta is missing
    };
    that.n_docs = n_docs;

    // has dt been loaded?
    has_dt = function () {
        return !!my.ready.dt;
    };
    that.has_dt = has_dt;

    // access top key words per topic
    tw = function (t, word) {
        if (!my.tw) {
            return undefined;
        }

        // tw() for the whole list of hashes
        if (t === undefined) {
            return my.tw;
        }

        // tw(t) for a particular topic
        if (word === undefined) {
            return my.tw[t];
        }

        // tw(t, word) for the weight of word in topic t
        return my.tw[t].get(word);
    };
    that.tw = tw;

    // number of topics
    n = function () {
        return my.n;
    };
    that.n = n;

    // number of top words per topic stored in tw
    n_top_words = function () {
        if (!this.tw()) {
            return undefined;
        }

        return my.tw[0].keys().length;
    };
    that.n_top_words = n_top_words;

    total_tokens = function (callback) {
        if (!my.total_tokens) {
            my.worker.callback("total_tokens", function (tot) {
                my.total_tokens = tot;
                callback(tot);
            });
            my.worker.postMessage({ what: "total_tokens" });
        } else {
            callback(my.total_tokens);
        }
    };
    that.total_tokens = total_tokens;

    topic_total = function (t, callback) {
        var topic = isFinite(t) ? t : "all";
        my.worker.callback("topic_total/" + topic, callback);
        my.worker.postMessage({
            what: "topic_total",
            t: topic
        });
    };
    that.topic_total = topic_total;

    // alpha hyperparameters
    alpha = function (t) {
        if (!my.alpha) {
            return undefined;
        }

        // alpha() for all of them or alpha(t) for just one
        return isFinite(t) ? my.alpha[t] : my.alpha;
    };
    that.alpha = alpha;

    // metadata table
    meta = function (d) {
        if (!my.meta) {
            return undefined;
        }

        return my.meta.doc(d);
    };
    that.meta = meta;

    // validate dates
    valid_year = function (y) {
        if (!my.years) {
            return undefined;
        }
        return my.years.has(y);
    };
    that.valid_year = valid_year;

    // aggregate vocabulary of all top words for some or all topics
    vocab = function (t) {
        var result = d3.set(),
            tws;
        if (!tw()) {
            return undefined;
        }

        if (isFinite(t)) {
            // single topic
            tw(t).keys().forEach(result.add);
        } else {
            if (t === undefined) {
                // all topics
                tws = tw();
            } else {
                // array of indices
                tws = t.map(function (t) {
                    return that.tw(t);
                });
            }
            tws.forEach(function (topic) {
                topic.keys().forEach(function (w) {
                    result.add(w);
                });
            });
        }

        return result.values().sort();
    };
    that.vocab = vocab;

    // scaled coordinates for topics
    topic_scaled = function (t) {
        if (!my.topic_scaled) {
            return undefined;
        }

        // topic_scaled() for all of them
        if (t === undefined) {
            return my.topic_scaled;
        }

        // topic_scaled(t) for coordinates for topic t
        return my.topic_scaled[t];
    };
    that.topic_scaled = topic_scaled;

    // get aggregate topic counts over years
    yearly_total = function (year, callback) {
        var y = (year === undefined) ? "all" : year,
            f = callback;
        if (y === "all") {
            f = function (yearly_total) {
                callback(d3.map(yearly_total));
            };
        }
        my.worker.callback("yearly_total/" + y, f);
        my.worker.postMessage({
            what: "yearly_total",
            y: y
        });
    };
    that.yearly_total = yearly_total;

    // yearly proportions for topic t
    topic_yearly = function (t, callback) {
        var topic = (t === undefined) ? "all" : t,
            f;
        if (topic === "all") {
            f = function (yearly) {
                callback(yearly.map(d3.map));
            };
        } else {
            f = function (yearly) {
                callback(d3.map(yearly));
            };
        }
        my.worker.callback("topic_yearly/" + topic, f);
        my.worker.postMessage({
            what: "topic_yearly",
            t: topic
        });
    };
    that.topic_yearly = topic_yearly;

    // Get n top documents for topic t. Uses a naive document ranking,
    // by the proportion of words assigned to t, which does *not*
    // necessarily give the docs where t is most salient
    topic_docs = function (t, n, year, callback) {
        var n_req = n, f = callback;
        if (year !== undefined) {
            n_req = this.n_docs();
            f = function (docs) {
                var year_docs = docs.filter(function (d) {
                    return that.meta(d.doc).date.getUTCFullYear() === +year;
                });
                return callback(utils.shorten(year_docs, n));
            };
        }

        my.worker.callback("topic_docs/" + t + "/" + n_req, f);
        my.worker.postMessage({
            what: "topic_docs",
            t: t,
            n: n_req
        });
    };
    that.topic_docs = topic_docs;

    // Get n top topics for document d. Unlike with docs, no reason to
    // go to lengths to avoid sorting n_topics entries, since
    // n_topics << n_docs. The expensive step is the row slice, which we
    // have to do anyway.
    doc_topics = function (d, n, callback) {
        my.worker.callback("doc_topics/" + d + "/" + n, callback);
        my.worker.postMessage({
            what: "doc_topics",
            d: d,
            n: n
        });
    };
    that.doc_topics = doc_topics;

    // Get n top words for topic t.
    topic_words = function (t, n) {
        var n_words = n || this.n_top_words(),
            words;
        if (t === undefined) {
            return d3.range(this.n()).map(function (topic) {
                return that.topic_words(topic, n);
            });
        }

        words = this.tw(t).entries(); // d3.map method
        words.sort(function (w1, w2) {
            return d3.descending(w1.value, w2.value) ||
                d3.ascending(w1.key, w2.key); // stabilize sort: alphabetical
        });

        return utils.shorten(words, n_words, function (ws, i) {
            return ws[i].value;
        })
            .map(function (w) {
                return {
                    word: w.key,
                    weight: w.value
                };
            });
    };
    that.topic_words = topic_words;

    // Get n top topics for a word.
    word_topics = function (word, n) {
        var t, row, word_wt,
            n_topics = n || this.n_top_words(),
            result = [],
            calc_rank = function (row) {
                // zero-based rank = (# of words strictly greater than word)
                return row.values().reduce(function (acc, cur) {
                    return cur > word_wt ? acc + 1 : acc;
                },
                    0);
            };

        for (t = 0; t < this.n(); t += 1) {
            row = this.tw(t);
            if (row.has(word)) {
                word_wt = row.get(word);
                result.push({
                    topic: t,
                    rank: calc_rank(row)
                });
            }
        }
        result.sort(function (a, b) {
            return d3.ascending(a.rank, b.rank) ||
                d3.ascending(a.topic, b.topic); // stabilize sort
        });
        return utils.shorten(result, n_topics, function (topics, i) {
            return topics[i].rank;
        });
    };
    that.word_topics = word_topics;

    year_topics = function (year, n) {
        // TODO DEPRECATED. Not used by the browser.
        var t, series, result = [];

        // *Could* calculate totals just for this year, but that still
        // requires running over all the documents to find those that
        // belong to the right year, and since we're comparing topics
        // we're cursed to traverse all of the doc-topics matrix anyway.

        for (t = 0; t < this.n(); t += 1) {
            series = this.topic_yearly(t);
            result.push({
                topic: t,
                weight: series.get(year) || 0 // TODO raw weighting or...?
            });
        }
        result.sort(function (a, b) {
            return d3.descending(a.weight, b.weight) ||
                d3.ascending(a.topic, b.topic); // stabilize sort
        });

        return utils.shorten(result, n);
    };
    that.year_topics = year_topics;

    topic_label = function (t) {
        var t_s = String(t + 1);
        // expect names keyed to 1-indexed numbers (easier to edit)
        if (my.info.topic_labels && my.info.topic_labels[t_s]) {
            return my.info.topic_labels[t_s];
        }

        // default name: use a no-break space
        return "Topic" + "\u00a0" + t_s;
    };
    that.topic_label = topic_label;

    // load tw from a string of JSON
    set_tw = function (tw_s) {
        var parsed;

        if (typeof tw_s !== 'string') {
            return;
        }

        parsed = JSON.parse(tw_s);
        my.alpha = parsed.alpha;
        my.tw = parsed.tw.map(function (topic) {
            var result = d3.map();
            topic.words.map(function (w, j) {
                result.set(w, topic.weights[j]);
            });
            return result;
        });

        if (!my.n) {
            my.n = my.alpha.length;
        }
    };
    that.set_tw = set_tw;

    // load dt from a string of JSON
    // callback should take one parameter, a Boolean indicating success
    set_dt = function (dt_s, callback) {
        if (typeof dt_s  !== 'string') {
            callback(false);
        }

        my.worker.callback("set_dt", function (result) {
            my.ready.dt = result;
            callback(result);
        });
        my.worker.postMessage({
            what: "set_dt",
            dt: JSON.parse(dt_s)
        });
    };
    that.set_dt = set_dt;

    // load meta from a string of CSV lines
    set_meta = function (meta) {
        var doc_years;

        my.meta = meta;

        // calculate and store document years
        doc_years = meta.doc().map(function (d) {
            return d.date.getUTCFullYear();
        });

        my.worker.callback("set_doc_years", function (result) {
            my.ready.doc_years = result;
        });
        my.worker.postMessage({
            what: "set_doc_years",
            doc_years: doc_years
        });
        my.years = d3.set(doc_years);
    };
    that.set_meta = set_meta;

    // load scaled topic coordinates from a string of CSV lines
    set_topic_scaled = function (ts_s) {
        var s;
        if (typeof ts_s  !== 'string') {
            return;
        }

        // strip blank "rows" at start or end
        s = ts_s.replace(/^\n*/, "")
            .replace(/\n*$/, "\n");
        my.topic_scaled = d3.csv.parseRows(s, function (row) {
            return row.map(parseFloat);
        });
    };
    that.set_topic_scaled = set_topic_scaled;

    return that;
};
<commit_msg>remove superfluous year_topics<commit_after>/*global d3, utils, bib, Worker */
"use strict";
var model;

// model specification
// -------------------
// data stored internally as follows:
// tw: array of d3.map()s keyed to words as strings
// alpha: alpha values for topics
// meta: array of objects holding document citations

model = function (spec) {
    var my = spec || { }, // private members
        that = { }, // resultant object
        info, // accessors and pseudo-accessors
        n_docs,
        has_dt,
        tw,
        n,
        n_top_words,
        total_tokens,
        topic_total,
        alpha,
        meta,
        vocab,
        topic_scaled,
        topic_yearly, // time-slicing
        valid_year,
        yearly_total,
        topic_docs, // most salient _ in _
        topic_words,
        doc_topics,
        word_topics,
        topic_label,
        set_dt, // methods for loading model data from strings
        set_tw,
        set_meta,
        set_topic_scaled;

    my.ready = { };
    my.worker = new Worker("js/worker.min.js");
    my.worker.fs = d3.map();
    my.worker.onmessage = function (e) {
        var f = my.worker.fs.get(e.data.what);
        if (f) {
            f(e.data.result);
        }
    };
    my.worker.callback = function (key, f) {
        my.worker.fs.set(key, f);
    };

    info = function (model_meta) {
        if (model_meta) {
            my.info = model_meta;
        }
        return my.info;
    };
    that.info = info;

    n_docs = function () {
        var result;
        if (my.n_docs !== undefined) {
            result = my.n_docs;
        } else if (my.meta) {
            result = my.meta.n_docs();
        }

        return result; // undefined if my.meta is missing
    };
    that.n_docs = n_docs;

    // has dt been loaded?
    has_dt = function () {
        return !!my.ready.dt;
    };
    that.has_dt = has_dt;

    // access top key words per topic
    tw = function (t, word) {
        if (!my.tw) {
            return undefined;
        }

        // tw() for the whole list of hashes
        if (t === undefined) {
            return my.tw;
        }

        // tw(t) for a particular topic
        if (word === undefined) {
            return my.tw[t];
        }

        // tw(t, word) for the weight of word in topic t
        return my.tw[t].get(word);
    };
    that.tw = tw;

    // number of topics
    n = function () {
        return my.n;
    };
    that.n = n;

    // number of top words per topic stored in tw
    n_top_words = function () {
        if (!this.tw()) {
            return undefined;
        }

        return my.tw[0].keys().length;
    };
    that.n_top_words = n_top_words;

    total_tokens = function (callback) {
        if (!my.total_tokens) {
            my.worker.callback("total_tokens", function (tot) {
                my.total_tokens = tot;
                callback(tot);
            });
            my.worker.postMessage({ what: "total_tokens" });
        } else {
            callback(my.total_tokens);
        }
    };
    that.total_tokens = total_tokens;

    topic_total = function (t, callback) {
        var topic = isFinite(t) ? t : "all";
        my.worker.callback("topic_total/" + topic, callback);
        my.worker.postMessage({
            what: "topic_total",
            t: topic
        });
    };
    that.topic_total = topic_total;

    // alpha hyperparameters
    alpha = function (t) {
        if (!my.alpha) {
            return undefined;
        }

        // alpha() for all of them or alpha(t) for just one
        return isFinite(t) ? my.alpha[t] : my.alpha;
    };
    that.alpha = alpha;

    // metadata table
    meta = function (d) {
        if (!my.meta) {
            return undefined;
        }

        return my.meta.doc(d);
    };
    that.meta = meta;

    // validate dates
    valid_year = function (y) {
        if (!my.years) {
            return undefined;
        }
        return my.years.has(y);
    };
    that.valid_year = valid_year;

    // aggregate vocabulary of all top words for some or all topics
    vocab = function (t) {
        var result = d3.set(),
            tws;
        if (!tw()) {
            return undefined;
        }

        if (isFinite(t)) {
            // single topic
            tw(t).keys().forEach(result.add);
        } else {
            if (t === undefined) {
                // all topics
                tws = tw();
            } else {
                // array of indices
                tws = t.map(function (t) {
                    return that.tw(t);
                });
            }
            tws.forEach(function (topic) {
                topic.keys().forEach(function (w) {
                    result.add(w);
                });
            });
        }

        return result.values().sort();
    };
    that.vocab = vocab;

    // scaled coordinates for topics
    topic_scaled = function (t) {
        if (!my.topic_scaled) {
            return undefined;
        }

        // topic_scaled() for all of them
        if (t === undefined) {
            return my.topic_scaled;
        }

        // topic_scaled(t) for coordinates for topic t
        return my.topic_scaled[t];
    };
    that.topic_scaled = topic_scaled;

    // get aggregate topic counts over years
    yearly_total = function (year, callback) {
        var y = (year === undefined) ? "all" : year,
            f = callback;
        if (y === "all") {
            f = function (yearly_total) {
                callback(d3.map(yearly_total));
            };
        }
        my.worker.callback("yearly_total/" + y, f);
        my.worker.postMessage({
            what: "yearly_total",
            y: y
        });
    };
    that.yearly_total = yearly_total;

    // yearly proportions for topic t
    topic_yearly = function (t, callback) {
        var topic = (t === undefined) ? "all" : t,
            f;
        if (topic === "all") {
            f = function (yearly) {
                callback(yearly.map(d3.map));
            };
        } else {
            f = function (yearly) {
                callback(d3.map(yearly));
            };
        }
        my.worker.callback("topic_yearly/" + topic, f);
        my.worker.postMessage({
            what: "topic_yearly",
            t: topic
        });
    };
    that.topic_yearly = topic_yearly;

    // Get n top documents for topic t. Uses a naive document ranking,
    // by the proportion of words assigned to t, which does *not*
    // necessarily give the docs where t is most salient
    topic_docs = function (t, n, year, callback) {
        var n_req = n, f = callback;
        if (year !== undefined) {
            n_req = this.n_docs();
            f = function (docs) {
                var year_docs = docs.filter(function (d) {
                    return that.meta(d.doc).date.getUTCFullYear() === +year;
                });
                return callback(utils.shorten(year_docs, n));
            };
        }

        my.worker.callback("topic_docs/" + t + "/" + n_req, f);
        my.worker.postMessage({
            what: "topic_docs",
            t: t,
            n: n_req
        });
    };
    that.topic_docs = topic_docs;

    // Get n top topics for document d. Unlike with docs, no reason to
    // go to lengths to avoid sorting n_topics entries, since
    // n_topics << n_docs. The expensive step is the row slice, which we
    // have to do anyway.
    doc_topics = function (d, n, callback) {
        my.worker.callback("doc_topics/" + d + "/" + n, callback);
        my.worker.postMessage({
            what: "doc_topics",
            d: d,
            n: n
        });
    };
    that.doc_topics = doc_topics;

    // Get n top words for topic t.
    topic_words = function (t, n) {
        var n_words = n || this.n_top_words(),
            words;
        if (t === undefined) {
            return d3.range(this.n()).map(function (topic) {
                return that.topic_words(topic, n);
            });
        }

        words = this.tw(t).entries(); // d3.map method
        words.sort(function (w1, w2) {
            return d3.descending(w1.value, w2.value) ||
                d3.ascending(w1.key, w2.key); // stabilize sort: alphabetical
        });

        return utils.shorten(words, n_words, function (ws, i) {
            return ws[i].value;
        })
            .map(function (w) {
                return {
                    word: w.key,
                    weight: w.value
                };
            });
    };
    that.topic_words = topic_words;

    // Get n top topics for a word.
    word_topics = function (word, n) {
        var t, row, word_wt,
            n_topics = n || this.n_top_words(),
            result = [],
            calc_rank = function (row) {
                // zero-based rank = (# of words strictly greater than word)
                return row.values().reduce(function (acc, cur) {
                    return cur > word_wt ? acc + 1 : acc;
                },
                    0);
            };

        for (t = 0; t < this.n(); t += 1) {
            row = this.tw(t);
            if (row.has(word)) {
                word_wt = row.get(word);
                result.push({
                    topic: t,
                    rank: calc_rank(row)
                });
            }
        }
        result.sort(function (a, b) {
            return d3.ascending(a.rank, b.rank) ||
                d3.ascending(a.topic, b.topic); // stabilize sort
        });
        return utils.shorten(result, n_topics, function (topics, i) {
            return topics[i].rank;
        });
    };
    that.word_topics = word_topics;

    topic_label = function (t) {
        var t_s = String(t + 1);
        // expect names keyed to 1-indexed numbers (easier to edit)
        if (my.info.topic_labels && my.info.topic_labels[t_s]) {
            return my.info.topic_labels[t_s];
        }

        // default name: use a no-break space
        return "Topic" + "\u00a0" + t_s;
    };
    that.topic_label = topic_label;

    // load tw from a string of JSON
    set_tw = function (tw_s) {
        var parsed;

        if (typeof tw_s !== 'string') {
            return;
        }

        parsed = JSON.parse(tw_s);
        my.alpha = parsed.alpha;
        my.tw = parsed.tw.map(function (topic) {
            var result = d3.map();
            topic.words.map(function (w, j) {
                result.set(w, topic.weights[j]);
            });
            return result;
        });

        if (!my.n) {
            my.n = my.alpha.length;
        }
    };
    that.set_tw = set_tw;

    // load dt from a string of JSON
    // callback should take one parameter, a Boolean indicating success
    set_dt = function (dt_s, callback) {
        if (typeof dt_s  !== 'string') {
            callback(false);
        }

        my.worker.callback("set_dt", function (result) {
            my.ready.dt = result;
            callback(result);
        });
        my.worker.postMessage({
            what: "set_dt",
            dt: JSON.parse(dt_s)
        });
    };
    that.set_dt = set_dt;

    // load meta from a string of CSV lines
    set_meta = function (meta) {
        var doc_years;

        my.meta = meta;

        // calculate and store document years
        doc_years = meta.doc().map(function (d) {
            return d.date.getUTCFullYear();
        });

        my.worker.callback("set_doc_years", function (result) {
            my.ready.doc_years = result;
        });
        my.worker.postMessage({
            what: "set_doc_years",
            doc_years: doc_years
        });
        my.years = d3.set(doc_years);
    };
    that.set_meta = set_meta;

    // load scaled topic coordinates from a string of CSV lines
    set_topic_scaled = function (ts_s) {
        var s;
        if (typeof ts_s  !== 'string') {
            return;
        }

        // strip blank "rows" at start or end
        s = ts_s.replace(/^\n*/, "")
            .replace(/\n*$/, "\n");
        my.topic_scaled = d3.csv.parseRows(s, function (row) {
            return row.map(parseFloat);
        });
    };
    that.set_topic_scaled = set_topic_scaled;

    return that;
};
<|endoftext|>