<commit_before>/*
 * Copyright 2020 Google LLC
 *
 * 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
 *
 *     https://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.
 */

/**
 * Internal dependencies
 */
import { Fixture } from '../../../../karma';
import useInsertElement from '../../../canvas/useInsertElement';
import createSolidFromString from '../../../../utils/createSolidFromString';
import { TEXT_ELEMENT_DEFAULT_FONT } from '../../../../app/font/defaultFonts';
import { MULTIPLE_VALUE } from '../../../form';

describe('Link Panel', () => {
  let fixture;
  let linkPanel;
  let safezone;

  beforeEach(async () => {
    fixture = new Fixture();
    await fixture.render();
  });

  afterEach(() => {
    fixture.restore();
  });

  const moveElementToBottom = async (frame) => {
    const safezoneHeight = safezone.getBoundingClientRect().height;
    const frameHeight = frame.getBoundingClientRect().height;
    await fixture.events.mouse.seq(({ moveRel, moveBy, down, up }) => [
      moveRel(frame, 10, 10),
      down(),
      moveBy(0, safezoneHeight - frameHeight, { steps: 10 }),
      up(),
    ]);
  };

  const setPageAttachmentLink = async (link) => {
    const input = fixture.screen.getByLabelText('Edit: Page Attachment link');
    await fixture.events.click(input, { clickCount: 3 });
    await fixture.events.keyboard.type(link);
    await input.dispatchEvent(new window.Event('blur'));
  };

  async function clickOnTarget(target, key = false) {
    const { x, y, width, height } = target.getBoundingClientRect();
    if (key) {
      await fixture.events.keyboard.down(key);
    }
    await fixture.events.mouse.click(x + width / 2, y + height / 2);
    if (key) {
      await fixture.events.keyboard.up(key);
    }
  }

  describe('CUJ: Creator Can Add A Link: Apply a link to any element', () => {
    beforeEach(async () => {
      await fixture.events.click(fixture.editor.library.textAdd);
      linkPanel = fixture.editor.inspector.designPanel.link;
    });

    it('should correctly show focus border both when using keyboard and mouse', async () => {
      // Click input
      await fixture.events.click(linkPanel.address);

      // Verify input has focus
      expect(linkPanel.address).toHaveFocus();

      // Screenshot it
      await fixture.snapshot('Link input has focus from mouse');

      // shift-tab, then tab to re-focus
      await fixture.events.keyboard.shortcut('shift+tab');
      await fixture.events.keyboard.press('tab');

      // Verify input still has focus
      expect(linkPanel.address).toHaveFocus();

      // Screenshot it
      await fixture.snapshot('Link input has focus from keyboard');
    });

    it('should add protocol automatically on blurring', async () => {
      await fixture.events.click(linkPanel.address);
      await fixture.events.keyboard.type('example.com');

      await linkPanel.address.dispatchEvent(new window.Event('blur'));
      expect(linkPanel.address.value).toBe('http://example.com');
    });

    it('should not add additional protocol if already present', async () => {
      await fixture.events.click(linkPanel.address);
      await fixture.events.keyboard.type('https://example.com');

      await linkPanel.address.dispatchEvent(new window.Event('blur'));
      expect(linkPanel.address.value).toBe('https://example.com');
    });

    // Disable reason: tests not implemented yet
    // eslint-disable-next-line jasmine/no-disabled-tests
    xit('should invoke API when looking up link');

    // Disable reason: tests not implemented yet
    // eslint-disable-next-line jasmine/no-disabled-tests
    xit('should display error when API errors');

    // Disable reason: tests not implemented yet
    // eslint-disable-next-line jasmine/no-disabled-tests
    xit('should display link details when API succeeds');

    // Disable reason: tests not implemented yet
    // eslint-disable-next-line jasmine/no-disabled-tests
    xit('should be able to apply a link to a text element');

    // Disable reason: tests not implemented yet
    // eslint-disable-next-line jasmine/no-disabled-tests
    xit('should be able to apply a link to a shape element');

    // Disable reason: tests not implemented yet
    // eslint-disable-next-line jasmine/no-disabled-tests
    xit('should be able to apply a link to a image element');

    // Disable reason: tests not implemented yet
    // eslint-disable-next-line jasmine/no-disabled-tests
    xit('should be able to apply a link to a video element');

    // Disable reason: tests not implemented yet
    // eslint-disable-next-line jasmine/no-disabled-tests
    xit('should not be able to apply a link to a background shape element');

    // Disable reason: tests not implemented yet
    // eslint-disable-next-line jasmine/no-disabled-tests
    xit('should not be able to apply a link to a background media element');
  });

  describe('CUJ: Creator Can Add A Link: Link with Page Attachment', () => {
    beforeEach(async () => {
      // Select Page.
      safezone = fixture.querySelector('[data-testid="safezone"]');
      await clickOnTarget(safezone);

      // Add Page Attachment
      await setPageAttachmentLink('http://pageattachment.com');
    });

    it('should not allow adding link in Page Attachment area', async () => {
      const insertElement = await fixture.renderHook(() => useInsertElement());
      const element = await fixture.act(() =>
        insertElement('shape', {
          backgroundColor: createSolidFromString('#ff00ff'),
          mask: { type: 'rectangle' },
          x: 10,
          y: 10,
          width: 50,
          height: 50,
        })
      );
      const frame = fixture.editor.canvas.framesLayer.frame(element.id).node;
      await moveElementToBottom(frame);

      linkPanel = fixture.editor.inspector.designPanel.link;
      await fixture.events.click(linkPanel.address);

      await fixture.snapshot('Page Attachment warning & dashed line visible');

      const warning = fixture.screen.getByText(
        'Link can not reside below the dashed line when a page attachment is present'
      );
      expect(warning).toBeDefined();
    });

    it('should not allow adding link to multi-selection in Page Attachment area', async () => {
      safezone = fixture.querySelector('[data-testid="safezone"]');
      // Add two elements.
      const insertElement = await fixture.renderHook(() => useInsertElement());
      // First one with link.
      const element1 = await fixture.act(() =>
        insertElement('text', {
          font: TEXT_ELEMENT_DEFAULT_FONT,
          content: 'Hello World!',
          x: 40,
          y: 100,
          width: 250,
        })
      );
      const frame = fixture.editor.canvas.framesLayer.frame(element1.id).node;
      await fixture.act(() =>
        insertElement('shape', {
          backgroundColor: createSolidFromString('#ff00ff'),
          mask: { type: 'rectangle' },
          x: 40,
          y: 0,
          width: 50,
          height: 50,
        })
      );
      // Select both elements.
      await clickOnTarget(frame, 'Shift');

      await moveElementToBottom(frame);

      linkPanel = fixture.editor.inspector.designPanel.link;
      await fixture.events.click(linkPanel.address);

      await fixture.snapshot(
        'Warning & dashed line visible with multi-selection'
      );

      const warning = fixture.screen.getByText(
        'Link can not reside below the dashed line when a page attachment is present'
      );
      expect(warning).toBeDefined();
    });
  });

  describe('CUJ: Creator Can Add A Link: Apply a link to multi-selection', () => {
    let frame1;
    let frame2;

    beforeEach(async () => {
      safezone = fixture.querySelector('[data-testid="safezone"]');
      // Add two elements.
      const insertElement = await fixture.renderHook(() => useInsertElement());
      // First one with link.
      const element1 = await fixture.act(() =>
        insertElement('text', {
          font: TEXT_ELEMENT_DEFAULT_FONT,
          content: 'Hello World!',
          x: 40,
          y: 0,
          width: 250,
          link: {
            url: 'https://example.com',
          },
        })
      );
      frame1 = fixture.editor.canvas.framesLayer.frame(element1.id).node;
      const element2 = await fixture.act(() =>
        insertElement('text', {
          font: TEXT_ELEMENT_DEFAULT_FONT,
          content: 'Hello again!',
          x: 40,
          y: 100,
          width: 250,
        })
      );
      frame2 = fixture.editor.canvas.framesLayer.frame(element2.id).node;

      // Select both elements.
      await clickOnTarget(frame1, 'Shift');
    });

    it('should allow changing link for two elements at the same time', async () => {
      linkPanel = fixture.editor.inspector.designPanel.link;
      await fixture.events.click(linkPanel.address);

      expect(linkPanel.address.value).toBe(MULTIPLE_VALUE);

      await fixture.events.keyboard.type('http://google.com');

      await linkPanel.address.dispatchEvent(new window.Event('blur'));

      // Click the elements separately to verify having the new link set.
      await clickOnTarget(frame1);
      expect(linkPanel.address.value).toBe('http://google.com');

      await clickOnTarget(frame2);
      expect(linkPanel.address.value).toBe('http://google.com');
    });
  });

  describe('CUJ: Creator Can Add A Link: Remove applied link', () => {
    beforeEach(async () => {
      await fixture.events.click(fixture.editor.library.textAdd);
      linkPanel = fixture.editor.inspector.designPanel.link;
      await fixture.events.click(linkPanel.address);
      await fixture.events.keyboard.type('http://google.com');
    });

    it('should return focus to input after clearing', async () => {
      // Verify remove button exists
      expect(linkPanel.addressClear).toBeTruthy();

      // Force focus to input - has to move out and back for weird reasons
      linkPanel.address.focus();
      linkPanel.addressClear.focus();

      // Click on remove button
      await fixture.events.click(linkPanel.addressClear);

      // Verify remove button doesn't exist
      expect(linkPanel.addressClear).not.toBeTruthy();

      // And verify that focus has been returned to input
      expect(linkPanel.address).toHaveFocus();
    });

    it('should show focus border on remove button when using keyboard', async () => {
      // Verify remove button exists
      expect(linkPanel.addressClear).toBeTruthy();

      // Force focus to input - has to move out and back for weird reasons
      linkPanel.address.focus();
      linkPanel.addressClear.focus();

      // "Tab" to move focus to remove button
      await fixture.events.keyboard.shortcut('tab');

      // Verify remove button has focus
      expect(linkPanel.addressClear).toHaveFocus();

      // Click the button by pressing enter
      await fixture.events.keyboard.press('Enter');

      // Verify remove button doesn't exist
      expect(linkPanel.addressClear).not.toBeTruthy();

      // And verify that focus has been returned to input
      expect(linkPanel.address).toHaveFocus();

      // Screenshot it
      await fixture.snapshot('Link remove button has focus from keyboard');
    });

    // Disable reason: tests not implemented yet
    // eslint-disable-next-line jasmine/no-disabled-tests
    xit('should be able to removed an applied a link');
  });

  // Disable reason: tests not implemented yet
  // eslint-disable-next-line jasmine/no-disabled-tests
  xdescribe('CUJ: Creator Can Add A Link: Edit brand icon', () => {
    it('should be able to edit brand icon');

    it('should be able to remove brand icon');
  });

  // Disable reason: tests not implemented yet
  // eslint-disable-next-line jasmine/no-disabled-tests
  xdescribe('CUJ: Creator Can Add A Link: Edit description', () => {
    it('should be able to edit descrption');
  });
});
<commit_msg>Nits.<commit_after>/*
 * Copyright 2020 Google LLC
 *
 * 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
 *
 *     https://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.
 */

/**
 * Internal dependencies
 */
import { Fixture } from '../../../../karma';
import useInsertElement from '../../../canvas/useInsertElement';
import createSolidFromString from '../../../../utils/createSolidFromString';
import { TEXT_ELEMENT_DEFAULT_FONT } from '../../../../app/font/defaultFonts';
import { MULTIPLE_VALUE } from '../../../form';

describe('Link Panel', () => {
  let fixture;
  let linkPanel;
  let safezone;

  beforeEach(async () => {
    fixture = new Fixture();
    await fixture.render();
  });

  afterEach(() => {
    fixture.restore();
  });

  const moveElementToBottom = async (frame) => {
    const safezoneHeight = safezone.getBoundingClientRect().height;
    const frameHeight = frame.getBoundingClientRect().height;
    await fixture.events.mouse.seq(({ moveRel, moveBy, down, up }) => [
      moveRel(frame, 10, 10),
      down(),
      moveBy(0, safezoneHeight - frameHeight, { steps: 10 }),
      up(),
    ]);
  };

  const setPageAttachmentLink = async (link) => {
    const input = fixture.screen.getByLabelText('Edit: Page Attachment link');
    await fixture.events.click(input, { clickCount: 3 });
    await fixture.events.keyboard.type(link);
    await input.dispatchEvent(new window.Event('blur'));
  };

  async function clickOnTarget(target, key = false) {
    const { x, y, width, height } = target.getBoundingClientRect();
    if (key) {
      await fixture.events.keyboard.down(key);
    }
    await fixture.events.mouse.click(x + width / 2, y + height / 2);
    if (key) {
      await fixture.events.keyboard.up(key);
    }
  }

  describe('CUJ: Creator Can Add A Link: Apply a link to any element', () => {
    beforeEach(async () => {
      await fixture.events.click(fixture.editor.library.textAdd);
      linkPanel = fixture.editor.inspector.designPanel.link;
    });

    it('should correctly show focus border both when using keyboard and mouse', async () => {
      // Click input
      await fixture.events.click(linkPanel.address);

      // Verify input has focus
      expect(linkPanel.address).toHaveFocus();

      // Screenshot it
      await fixture.snapshot('Link input has focus from mouse');

      // shift-tab, then tab to re-focus
      await fixture.events.keyboard.shortcut('shift+tab');
      await fixture.events.keyboard.press('tab');

      // Verify input still has focus
      expect(linkPanel.address).toHaveFocus();

      // Screenshot it
      await fixture.snapshot('Link input has focus from keyboard');
    });

    it('should add protocol automatically on blurring', async () => {
      await fixture.events.click(linkPanel.address);
      await fixture.events.keyboard.type('example.com');

      await linkPanel.address.dispatchEvent(new window.Event('blur'));
      expect(linkPanel.address.value).toBe('http://example.com');
    });

    it('should not add additional protocol if already present', async () => {
      await fixture.events.click(linkPanel.address);
      await fixture.events.keyboard.type('https://example.com');

      await linkPanel.address.dispatchEvent(new window.Event('blur'));
      expect(linkPanel.address.value).toBe('https://example.com');
    });

    // Disable reason: tests not implemented yet
    // eslint-disable-next-line jasmine/no-disabled-tests
    xit('should invoke API when looking up link');

    // Disable reason: tests not implemented yet
    // eslint-disable-next-line jasmine/no-disabled-tests
    xit('should display error when API errors');

    // Disable reason: tests not implemented yet
    // eslint-disable-next-line jasmine/no-disabled-tests
    xit('should display link details when API succeeds');

    // Disable reason: tests not implemented yet
    // eslint-disable-next-line jasmine/no-disabled-tests
    xit('should be able to apply a link to a text element');

    // Disable reason: tests not implemented yet
    // eslint-disable-next-line jasmine/no-disabled-tests
    xit('should be able to apply a link to a shape element');

    // Disable reason: tests not implemented yet
    // eslint-disable-next-line jasmine/no-disabled-tests
    xit('should be able to apply a link to a image element');

    // Disable reason: tests not implemented yet
    // eslint-disable-next-line jasmine/no-disabled-tests
    xit('should be able to apply a link to a video element');

    // Disable reason: tests not implemented yet
    // eslint-disable-next-line jasmine/no-disabled-tests
    xit('should not be able to apply a link to a background shape element');

    // Disable reason: tests not implemented yet
    // eslint-disable-next-line jasmine/no-disabled-tests
    xit('should not be able to apply a link to a background media element');
  });

  describe('CUJ: Creator Can Add A Link: Link with Page Attachment', () => {
    beforeEach(async () => {
      // Select Page.
      safezone = fixture.querySelector('[data-testid="safezone"]');
      await clickOnTarget(safezone);

      // Add Page Attachment
      await setPageAttachmentLink('http://pageattachment.com');
    });

    it('should not allow adding link in Page Attachment area', async () => {
      const insertElement = await fixture.renderHook(() => useInsertElement());
      const element = await fixture.act(() =>
        insertElement('shape', {
          backgroundColor: createSolidFromString('#ff00ff'),
          mask: { type: 'rectangle' },
          x: 10,
          y: 10,
          width: 50,
          height: 50,
        })
      );
      const frame = fixture.editor.canvas.framesLayer.frame(element.id).node;
      await moveElementToBottom(frame);

      linkPanel = fixture.editor.inspector.designPanel.link;
      await fixture.events.click(linkPanel.address);

      await fixture.snapshot('Page Attachment warning & dashed line visible');

      const warning = fixture.screen.getByText(
        'Link can not reside below the dashed line when a page attachment is present'
      );
      expect(warning).toBeDefined();
    });

    it('should not allow adding link to multi-selection in Page Attachment area', async () => {
      const insertElement = await fixture.renderHook(() => useInsertElement());
      const element1 = await fixture.act(() =>
        insertElement('text', {
          font: TEXT_ELEMENT_DEFAULT_FONT,
          content: 'Hello World!',
          x: 40,
          y: 100,
          width: 250,
        })
      );
      const frame = fixture.editor.canvas.framesLayer.frame(element1.id).node;
      await fixture.act(() =>
        insertElement('shape', {
          backgroundColor: createSolidFromString('#ff00ff'),
          mask: { type: 'rectangle' },
          x: 40,
          y: 0,
          width: 50,
          height: 50,
        })
      );
      // Select the first element as well.
      await clickOnTarget(frame, 'Shift');

      await moveElementToBottom(frame);

      linkPanel = fixture.editor.inspector.designPanel.link;
      await fixture.events.click(linkPanel.address);

      await fixture.snapshot(
        'Warning & dashed line visible with multi-selection'
      );

      const warning = fixture.screen.getByText(
        'Link can not reside below the dashed line when a page attachment is present'
      );
      expect(warning).toBeDefined();
    });
  });

  describe('CUJ: Creator Can Add A Link: Apply a link to multi-selection', () => {
    let frame1;
    let frame2;

    beforeEach(async () => {
      safezone = fixture.querySelector('[data-testid="safezone"]');
      // Add two elements.
      const insertElement = await fixture.renderHook(() => useInsertElement());
      // First one with link.
      const element1 = await fixture.act(() =>
        insertElement('text', {
          font: TEXT_ELEMENT_DEFAULT_FONT,
          content: 'Hello World!',
          x: 40,
          y: 0,
          width: 250,
          link: {
            url: 'https://example.com',
          },
        })
      );
      frame1 = fixture.editor.canvas.framesLayer.frame(element1.id).node;
      const element2 = await fixture.act(() =>
        insertElement('text', {
          font: TEXT_ELEMENT_DEFAULT_FONT,
          content: 'Hello again!',
          x: 40,
          y: 100,
          width: 250,
        })
      );
      frame2 = fixture.editor.canvas.framesLayer.frame(element2.id).node;

      // Select both elements.
      await clickOnTarget(frame1, 'Shift');
    });

    it('should allow changing link for two elements at the same time', async () => {
      linkPanel = fixture.editor.inspector.designPanel.link;
      await fixture.events.click(linkPanel.address);

      expect(linkPanel.address.value).toBe(MULTIPLE_VALUE);

      await fixture.events.keyboard.type('http://google.com');

      await linkPanel.address.dispatchEvent(new window.Event('blur'));

      // Click the elements separately to verify having the new link set.
      await clickOnTarget(frame1);
      expect(linkPanel.address.value).toBe('http://google.com');

      await clickOnTarget(frame2);
      expect(linkPanel.address.value).toBe('http://google.com');
    });
  });

  describe('CUJ: Creator Can Add A Link: Remove applied link', () => {
    beforeEach(async () => {
      await fixture.events.click(fixture.editor.library.textAdd);
      linkPanel = fixture.editor.inspector.designPanel.link;
      await fixture.events.click(linkPanel.address);
      await fixture.events.keyboard.type('http://google.com');
    });

    it('should return focus to input after clearing', async () => {
      // Verify remove button exists
      expect(linkPanel.addressClear).toBeTruthy();

      // Force focus to input - has to move out and back for weird reasons
      linkPanel.address.focus();
      linkPanel.addressClear.focus();

      // Click on remove button
      await fixture.events.click(linkPanel.addressClear);

      // Verify remove button doesn't exist
      expect(linkPanel.addressClear).not.toBeTruthy();

      // And verify that focus has been returned to input
      expect(linkPanel.address).toHaveFocus();
    });

    it('should show focus border on remove button when using keyboard', async () => {
      // Verify remove button exists
      expect(linkPanel.addressClear).toBeTruthy();

      // Force focus to input - has to move out and back for weird reasons
      linkPanel.address.focus();
      linkPanel.addressClear.focus();

      // "Tab" to move focus to remove button
      await fixture.events.keyboard.shortcut('tab');

      // Verify remove button has focus
      expect(linkPanel.addressClear).toHaveFocus();

      // Click the button by pressing enter
      await fixture.events.keyboard.press('Enter');

      // Verify remove button doesn't exist
      expect(linkPanel.addressClear).not.toBeTruthy();

      // And verify that focus has been returned to input
      expect(linkPanel.address).toHaveFocus();

      // Screenshot it
      await fixture.snapshot('Link remove button has focus from keyboard');
    });

    // Disable reason: tests not implemented yet
    // eslint-disable-next-line jasmine/no-disabled-tests
    xit('should be able to removed an applied a link');
  });

  // Disable reason: tests not implemented yet
  // eslint-disable-next-line jasmine/no-disabled-tests
  xdescribe('CUJ: Creator Can Add A Link: Edit brand icon', () => {
    it('should be able to edit brand icon');

    it('should be able to remove brand icon');
  });

  // Disable reason: tests not implemented yet
  // eslint-disable-next-line jasmine/no-disabled-tests
  xdescribe('CUJ: Creator Can Add A Link: Edit description', () => {
    it('should be able to edit descrption');
  });
});
<|endoftext|>


<commit_before>/**
 * @file wow command
 * @author James Naylor (a.k.a euronay)
 * @license GPL-3.0
 */

const request = xrequire('request-promise-native');

const wowConstants = {
  faction: [
    'Alliance',
    'Horde'
  ],
  gender: [
    'None',
    'Male',
    'Female'
  ],
  race: [
    'None',
    'Human',
    'Orc',
    'Dwarf',
    'Night Elf',
    'Undead',
    'Tauren',
    'Gnome',
    'Troll',
    'Goblin',
    'Blood Elf',
    'Draenei',
    'Fel Orc',
    'Naga',
    'Broken',
    'Skeleton',
    'Vrykul',
    'Tuskarr',
    'Forest Troll',
    'Taunka',
    'Northrend Skeleton',
    'Ice Troll',
    'Worgen',
    'Gilnean',
    'Pandaren',
    'Pandaren',
    'Pandaren',
    'Nightborn',
    'Highmountain Tauren',
    'Void Elf',
    'Lightforged Draenei',
    'Zandalari Troll',
    'Kul Tiran',
    'Human',
    'Dark Iron Dwarf',
    'Vulpera',
    'Mag\'har Orc'
  ],
  class: [
    'None',
    'Warrior',
    'Paladin',
    'Hunter',
    'Rogue',
    'Priest',
    'Death Knight',
    'Shaman',
    'Mage',
    'Warlock',
    'Druid',
    'Demon Hunter'
  ]
};

exports.exec = async (Bastion, message, args) => {
  try {
    if (!args.character || !args.realm) {
      /**
       * The command was ran with invalid parameters.
       * @fires commandUsage
       */
      return Bastion.emit('commandUsage', message, this.help);
    }

    args.region = args.region.toLowerCase();
    let locale = args.region === 'us' ? 'en-us' : 'en-gb';

    let options = {
      url: `https://${args.region}.api.battle.net/wow/character/${args.realm}/${args.character}`,
      qs: {
        fields: 'guild,items',
        locale: locale,
        apikey: Bastion.credentials.battleNetApiKey
      },
      json: true
    };

    let response = await request(options);

    let factionIcon = `https://worldofwarcraft.akamaized.net/static/components/Logo/Logo-${response.faction === 0 ? 'alliance' : 'horde'}.png`;
    let avatar = `https://render-${args.region}.worldofwarcraft.com/character/${response.thumbnail}`;

    let stats = [
      {
        name: 'Character',
        value: `${response.level} - ${wowConstants.race[response.race]} - ${wowConstants.class[response.class]}`
      },
      {
        name: 'Achievement Points',
        value: response.achievementPoints,
        inline: true
      },
      {
        name: 'Item Level',
        value: response.items.averageItemLevel,
        inline: true
      }
    ];

    if (response.guild) {
      stats.push(
        {
          name: 'Guild',
          value: response.guild.name,
          inline: true
        }
      );
    }

    message.channel.send({
      embed: {
        color: Bastion.colors.BLUE,
        author: {
          name: response.name,
          url: `https://worldofwarcraft.com/${locale}/character/${args.realm}/${args.character}`,
          icon_url: factionIcon
        },
        fields: stats,
        thumbnail: {
          url: avatar
        },
        footer: {
          text: 'Powered by Blizzard Battle.net'
        }
      }
    }).catch(e => {
      Bastion.log.error(e);
    });

  }
  catch (e) {
    if (e.name === 'StatusCodeError') {
      if (e.statusCode === '404') {
        return Bastion.emit('error', '', Bastion.i18n.error(message.guild.language, 'notFound', 'player'), message.channel);
      }
      return Bastion.emit('error', e.statusCode, e.error.message, message.channel);
    }
    Bastion.log.error(e);
  }
};

exports.config = {
  aliases: [],
  enabled: true,
  argsDefinitions: [
    { name: 'character', type: String, defaultOption: true },
    { name: 'realm', type: String, alias: 'r' },
    { name: 'region', type: String, defaultValue: 'us' }
  ]
};

exports.help = {
  name: 'wow',
  description: 'Get stats of any World of Warcraft character from the Armory.',
  botPermission: '',
  userTextPermission: '',
  userVoicePermission: '',
  usage: 'wow <CHARACTER> <--realm REALM> [-region <REGION>]',
  example: [
    'wow Fallken --realm Burning-Blade --region EU'
  ]
};
<commit_msg>commands: fix wow command's usage syntax<commit_after>/**
 * @file wow command
 * @author James Naylor (a.k.a euronay)
 * @license GPL-3.0
 */

const request = xrequire('request-promise-native');

const wowConstants = {
  faction: [
    'Alliance',
    'Horde'
  ],
  gender: [
    'None',
    'Male',
    'Female'
  ],
  race: [
    'None',
    'Human',
    'Orc',
    'Dwarf',
    'Night Elf',
    'Undead',
    'Tauren',
    'Gnome',
    'Troll',
    'Goblin',
    'Blood Elf',
    'Draenei',
    'Fel Orc',
    'Naga',
    'Broken',
    'Skeleton',
    'Vrykul',
    'Tuskarr',
    'Forest Troll',
    'Taunka',
    'Northrend Skeleton',
    'Ice Troll',
    'Worgen',
    'Gilnean',
    'Pandaren',
    'Pandaren',
    'Pandaren',
    'Nightborn',
    'Highmountain Tauren',
    'Void Elf',
    'Lightforged Draenei',
    'Zandalari Troll',
    'Kul Tiran',
    'Human',
    'Dark Iron Dwarf',
    'Vulpera',
    'Mag\'har Orc'
  ],
  class: [
    'None',
    'Warrior',
    'Paladin',
    'Hunter',
    'Rogue',
    'Priest',
    'Death Knight',
    'Shaman',
    'Mage',
    'Warlock',
    'Druid',
    'Demon Hunter'
  ]
};

exports.exec = async (Bastion, message, args) => {
  try {
    if (!args.character || !args.realm) {
      /**
       * The command was ran with invalid parameters.
       * @fires commandUsage
       */
      return Bastion.emit('commandUsage', message, this.help);
    }

    args.region = args.region.toLowerCase();
    let locale = args.region === 'us' ? 'en-us' : 'en-gb';

    let options = {
      url: `https://${args.region}.api.battle.net/wow/character/${args.realm}/${args.character}`,
      qs: {
        fields: 'guild,items',
        locale: locale,
        apikey: Bastion.credentials.battleNetApiKey
      },
      json: true
    };

    let response = await request(options);

    let factionIcon = `https://worldofwarcraft.akamaized.net/static/components/Logo/Logo-${response.faction === 0 ? 'alliance' : 'horde'}.png`;
    let avatar = `https://render-${args.region}.worldofwarcraft.com/character/${response.thumbnail}`;

    let stats = [
      {
        name: 'Character',
        value: `${response.level} - ${wowConstants.race[response.race]} - ${wowConstants.class[response.class]}`
      },
      {
        name: 'Achievement Points',
        value: response.achievementPoints,
        inline: true
      },
      {
        name: 'Item Level',
        value: response.items.averageItemLevel,
        inline: true
      }
    ];

    if (response.guild) {
      stats.push(
        {
          name: 'Guild',
          value: response.guild.name,
          inline: true
        }
      );
    }

    message.channel.send({
      embed: {
        color: Bastion.colors.BLUE,
        author: {
          name: response.name,
          url: `https://worldofwarcraft.com/${locale}/character/${args.realm}/${args.character}`,
          icon_url: factionIcon
        },
        fields: stats,
        thumbnail: {
          url: avatar
        },
        footer: {
          text: 'Powered by Blizzard Battle.net'
        }
      }
    }).catch(e => {
      Bastion.log.error(e);
    });

  }
  catch (e) {
    if (e.name === 'StatusCodeError') {
      if (e.statusCode === '404') {
        return Bastion.emit('error', '', Bastion.i18n.error(message.guild.language, 'notFound', 'player'), message.channel);
      }
      return Bastion.emit('error', e.statusCode, e.error.message, message.channel);
    }
    Bastion.log.error(e);
  }
};

exports.config = {
  aliases: [],
  enabled: true,
  argsDefinitions: [
    { name: 'character', type: String, defaultOption: true },
    { name: 'realm', type: String, alias: 'r' },
    { name: 'region', type: String, defaultValue: 'us' }
  ]
};

exports.help = {
  name: 'wow',
  description: 'Get stats of any World of Warcraft character from the Armory.',
  botPermission: '',
  userTextPermission: '',
  userVoicePermission: '',
  usage: 'wow <CHARACTER> <--realm REALM> [--region <REGION>]',
  example: [
    'wow Fallken --realm Burning-Blade --region EU'
  ]
};
<|endoftext|>


<commit_before>// Constants
DAYS_PER_WEEK = 7
HOURS_PER_DAY = 24
MINUTES_PER_HOUR = 60

// Reverse map from the time to the timeblock
timeblocks = {};
// Dict of sleeps by pk
sleeps = {};
// Global object for tentative sleep
tentativeSleep = null;
// A sleep looks like:
// 'pk' -- the pk of the sleep ("tentative" for the tentative sleep)
// 'start' -- a Date object representing the start time/date
// 'end' -- a Date object representing the end time/date
// 'comment' -- a String; the user's comment on this sleep
// 'date' -- a Date object, in which we only care about the
//           actual day; which day to associate the sleep with

// Track the global mouse state
// 0 = No click
// 1 = Click, but no drag
// 2 = Click and drag
mouseState = 0;

// Utility function used by the mouseX functions to choose
// a reasonable end time, if the one you are hovering over
// would cause a negative time sleep
function getValidEndTime(startTime, endTime)
{
    // Duplicate endTime, since we don't ever want to return
    // the original (because it's likely we'll be mutating
    // what we return)
    var newEndTime = new Date(endTime);
    if (newEndTime > startTime)
    {
	return newEndTime;
    }

    // Try bumping the end time to the current date
    newEndTime.setDate(startTime.getDate());
    if (newEndTime > startTime)
    {
	return newEndTime;
    }

    // Bump up by one more day, if that doesn't put us
    // into the future. Else, just return the same time as
    // the startTime.
    var dateToday = (new Date()).getDate();
    if (newEndTime.getDate() < dateToday)
    {
	newEndTime.setDate(newEndTime.getDate()+1);
	return newEndTime;
    }
    else
    {
	return new Date(startTime);
    }
}
function mouseDown(startTime, endTime)
{
    // If we aren't in the middle of creating a sleep,
    // initiate a new one.
    if (mouseState == 0 || mouseState == 1)
    {
	// Update mouseState
	mouseState = 1;

	// Create a tentative sleep
	tentativeSleep = {
	    'pk': "tentative",
	    'start': startTime,
	    'end': null,
	    'comment': null,
	    'date': null
	};
    }
    // If we were in the middle of creating a sleep,
    // use this to terminate the sleep
    else
    {
	// Update mouseState
	mouseState = 0;
	// Complete the tentative sleep
	tentativeSleep.end = getValidEndTime(tentativeSleep.start, endTime);
	// Spawn a popup to make final edits, save the tentative
	// sleep
	spawnPopup();
    }
}
function mouseUp(startTime, endTime)
{
    // Only if we were previously dragging, complete this sleep
    if (mouseState == 2)
    {
	// Update mouseState
	mouseState = 0;

	// Update the tentative sleep
	tentativeSleep.end = getValidEndTime(tentativeSleep.start, endTime);
	// Spawn a popup to make final edits, save the tentative
	// sleep
	spawnPopup();
    }
}
function mouseMove(startTime, endTime)
{
    // Only repond to click-and-drag
    if (mouseState == 1 || mouseState == 2)
    {
	// Update mouseState
	mouseState = 2;
	if (getValidEndTime(tentativeSleep.start, endTime) != tentativeSleep.end)
	{
	    tentativeSleep.end = getValidEndTime(tentativeSleep.start, endTime);
	    drawSleep(tentativeSleep);
	}
    }
}

function spawnPopup()
{
    $("#confirm-sleep-entry [name='start']").datepicker("setDate", tentativeSleep.start);
    $("#confirm-sleep-entry [name='end']").datepicker("setDate", tentativeSleep.end);
    $("#confirm-sleep-entry [name='date']").datepicker("setDate", new Date((tentativeSleep.start.valueOf() + tentativeSleep.end.valueOf()) / 2));
    $("#confirm-sleep-entry").dialog("open");
}
function clearSleep(sleep)
{
    // Clear the sleep box
    $(".sleep-id-" + sleep.pk).remove();
}
// Handle the JSON response from the server
// containing a list of our sleeps
function processSleeps(jsonData)
{
    // Clear our current dict of sleeps
    sleeps = {};
    // Build a new dict based on JSON data
    for (var i in jsonData)
    {
	var sleep = jsonData[i];
	sleeps[sleep.pk] = {
	    'pk': sleep.pk,
	    'start': new Date(sleep.fields.start_time),
	    'end': new Date(sleep.fields.end_time),
	    'comments': sleep.fields.comments,
	    'date': new Date(sleep.fields.date)
	};
    }
    // Now render all our current sleeps
    renderSleeps();
}
// Render all sleeps in the sleeps dict
function renderSleeps()
{
    for (var i in sleeps)
    {
	var sleep = sleeps[i];
	drawSleep(sleep);
    }
}
// Draw floating box from $td1 to $td2, potentially
// drawing the beginning and end as a continuing beginning/end
function drawSleepBox(sleep_pk, $td1, $td2, drawStartIcon, drawEndIcon)
{
    // Fill in default values
    if (typeof(drawStartIcon) === 'undefined')
    {
	drawStartIcon = true;
    }
    if (typeof(drawEndIcon) === 'undefined')
    {
	drawEndIcon = true;
    }

    // Compute the width and draw the box itself
    var width = $td2.position().left - $td1.position().left;
    var $sleep_box = $("

").css("padding-right", width+19).css("margin-right", -width-21)
	.css("margin-left", 1).addClass("sleep-box")
	.addClass("sleep-id-" + sleep_pk);
    // Add the tentative class if it's a tentative box (changes the color)
    if (sleep_pk == "tentative") $sleep_box.addClass("tentative");

    // Draw the start icon if needed
    if (drawStartIcon)
    {
	var $sleep_box_start = $("

");
	if (sleep_pk == "tentative") $sleep_box_start.addClass("tentative");
	$sleep_box.append($sleep_box_start);
    }
    if (drawEndIcon)
    {
	var $sleep_box_end = $("

").css("margin-right", -width-29);
	if (sleep_pk == "tentative") $sleep_box_end.addClass("tentative");
	$sleep_box.append($sleep_box_end);
    }
    $td1.append($sleep_box);
}
// Draw a sleep on the grid
function drawSleep(sleep)
{
    // Duplicate the input Date objects on the sleep
    var start = new Date(sleep.start);
    var end = new Date(sleep.end);
    // Round off the times to the nearest half-hour
    var startMinutes = start.getMinutes() + MINUTES_PER_HOUR/4;
    var endMinutes = end.getMinutes() + MINUTES_PER_HOUR/4;
    start.setMinutes(startMinutes - (startMinutes % (MINUTES_PER_HOUR/2)));
    start.setSeconds(0);
    start.setMicroseconds(0);
    end.setMinutes(endMinutes - (endMinutes % (MINUTES_PER_HOUR/2)));
    end.setSeconds(0);
    end.setMicroseconds(0);

    // First clear the current sleep box
    $(".sleep-id-" + sleep.pk).remove();
    // Now find the appropriate blocks
    var startblock = timeblocks[start.getTime()];
    if (typeof(startblock) === 'undefined') return false;
    var $starttd = startblock['start'];
    if ($starttd == null) return false;
    var endblock = timeblocks[end.getTime()];
    if (typeof(endblock) === 'undefined') return false;
    var $endtd = endblock['end'];
    if ($endtd == null) return false;
    // Fill them in with carryovers as needed
    if (start.getDate() == end.getDate())
    {
	// It's all one line -- render normally
	drawSleepBox(sleep.pk, $starttd, $endtd);
    }
    else
    {
	// Multiline display with carryovers
	var curDate = new Date(start);
	while (curDate.getDate() <= end.getDate())
	{
	    if (curDate.getDate() == start.getDate())
	    {
		var endRowTime = new Date(start);
		endRowTime.setHours(24);
		endRowTime.setMinutes(0);
		var endRowBlock = timeblocks[endRowTime.getTime()];
		if (typeof(endRowBlock) === 'undefined') return false;
		var $endRowTd = endRowBlock['end'];
		if($endRowTd == null) return false;
		drawSleepBox(sleep.pk, $starttd, $endRowTd, true, false);
	    }
	    else if (curDate.getDate() == end.getDate())
	    {
		var beginRowTime = new Date(end);
		beginRowTime.setHours(0);
		beginRowTime.setMinutes(0);
		var beginRowBlock = timeblocks[beginRowTime.getTime()];
		if (typeof(beginRowBlock) === 'undefined') return false;
		var $beginRowTd = beginRowBlock['start'];
		if($beginRowTd == null) return false;
		drawSleepBox(sleep.pk, $beginRowTd, $endtd, false, true);
	    }
	    else
	    {
		var beginRowTime = new Date(curDate);
		beginRowTime.setHours(0);
		beginRowTime.setMinutes(0);
		var beginRowBlock = timeblocks[beginRowTime.getTime()];
		if (typeof(beginRowBlock) === 'undefined') return false;
		var $beginRowTd = beginRowBlock['start'];
		if($beginRowTd == null) return false;
		var endRowTime = new Date(curDate);
		endRowTime.setHours(24);
		endRowTime.setMinutes(0);
		var endRowBlock = timeblocks[endRowTime.getTime()];
		if (typeof(endRowBlock) === 'undefined') return false;
		var $endRowTd = endRowBlock['end'];
		if($endRowTd == null) return false;
		drawSleepBox(sleep.pk, $beginRowTd, $endRowTd, false, false);
	    }
	    // Increment the date
	    curDate.setDate(curDate.getDate()+1);
	}
    }
}

$(document).ready(function()
{
    // Clear the timeblocks reverse map
    timeblocks = {};

    // Get the sleep grid div, create a table
    var $sleep_grid = $(".sleep-grid");
    var $sleep_table = $("

").addClass("sleep-table");
    $sleep_table.prop("cellspacing", 0).prop("cellpadding", 0);
    $sleep_grid.append($sleep_table);

    // Store the date without any time component
    var today = new Date();
    today.setHours(0);
    today.setMinutes(0);
    today.setSeconds(0);
    today.setMilliseconds(0);

    // Create a header for the times
    var $tr = $("

").addClass("header-row");
    $tr.append($("	").addClass("corner"));
    for (var h = 0; h < HOURS_PER_DAY; ++h)
    {
	var $th = $("	").addClass("column-header")
	    .html(h + ":00");
	$tr.append($th);
    }
    $sleep_table.append($tr);
    // Iterate backwards over the past week, starting from the current date
    for (var d = 0; d < DAYS_PER_WEEK; ++d)
    {
	var $tr = $("

");
	// Create a header for the row
	var date = new Date(today);
	date.setDate(date.getDate() - d);
	var $td = $("	").html(date.toLocaleDateString())
	    .addClass("row-header");
	$tr.append($td);
	// Create a left and right cell per hour
	for (var h = 0; h < HOURS_PER_DAY; ++h)
	{
	    // Define an anonymous function for scoping
	    (function()
	    {
		// Compute the times at the start, middle, and end of this block
		var t_left = new Date(today);
		t_left.setDate(t_left.getDate() - d);
		var t_mid = new Date(t_left);
		var t_right = new Date(t_left);
		t_left.setHours(h);
		t_mid.setHours(h);
		t_mid.setMinutes(MINUTES_PER_HOUR/2);
		t_right.setHours(h+1);

		// Split the block into the two half-hours;
		// Set the mouse triggers
		var $td_left = $("	").addClass("left");
		$td_left.mousedown(function() {mouseDown(t_left, t_mid)})
		    .mousemove(function() {mouseMove(t_left, t_mid)})
		    .mouseup(function() {mouseUp(t_left, t_mid)});
		var $td_right = $("	").addClass("right");
		$td_right.mousedown(function() {mouseDown(t_mid, t_right)})
		    .mousemove(function() {mouseMove(t_mid, t_right)})
		    .mouseup(function() {mouseUp(t_mid, t_right)});

		$tr.append($td_left);
		$tr.append($td_right);

		// Add the half-blocks to the reverse map
		var left_time = t_left.getTime();
		var mid_time = t_mid.getTime();
		var right_time = t_right.getTime();
		if (typeof(timeblocks[left_time]) == 'undefined')
		{
		    timeblocks[left_time] = {'start': $td_left, 'end': null};
		}
		else
		{
		    timeblocks[left_time]['start'] = $td_left;
		}
		if (typeof(timeblocks[right_time]) == 'undefined')
		{
		    timeblocks[right_time] = {'start': null, 'end': $td_right};
		}
		else
		{
		    timeblocks[right_time]['end'] = $td_right;
		}
		timeblocks[mid_time] = {'start': $td_right, 'end': $td_left};
	    })();
	}
	$sleep_table.append($tr);
    }

    // Get the list of current sleeps and display them in the grid
    $.post("/sleep/getSleeps/", null, processSleeps);

    // Set the fields of the confirm sleep dialog to be datetimepicker objects
    $("#confirm-sleep-entry [name='start']").datetimepicker();
    $("#confirm-sleep-entry [name='end']").datetimepicker();
    $("#confirm-sleep-entry [name='date']").datepicker();
    // Set up the confirm sleep dialog
    $("#confirm-sleep-entry").dialog({
	autoOpen: false,
	height: 350,
	width: 400,
	modal: true,
	position: {my: "center top", at: "center top", of: window},
	buttons:
	{
	    "Create sleep": function()
	    {
		// Create the sleep
		$.post("/sleep/create/", {
		    "start[]": dateToArray($("#confirm-sleep-entry [name='start']").datepicker("getDate")),
		    "end[]": dateToArray($("#confirm-sleep-entry [name='end']").datepicker("getDate")),
		    "date[]": dateToArray($("#confirm-sleep-entry [name='date']").datepicker("getDate")),
		    "comments": $("#confirm-sleep-entry [name='comments']").val()
		}, function() {
		    // TODO: Update the page dyanmically (without reloading)
		    // For now:
		    location.reload();
		});
	    },
	    Cancel: function()
	    {
		$(this).dialog("close");
	    }
	},
	close: function()
	{
	    clearSleep(tentativeSleep);
	    $(".confirm-sleep-entry input").removeClass("ui-state-error");
	}
    });
});
<commit_msg>Fixes sleep-entry UI to be based on date/month/year instead of just date<commit_after>// Constants
DAYS_PER_WEEK = 7
HOURS_PER_DAY = 24
MINUTES_PER_HOUR = 60

// Reverse map from the time to the timeblock
timeblocks = {};
// Dict of sleeps by pk
sleeps = {};
// Global object for tentative sleep
tentativeSleep = null;
// A sleep looks like:
// 'pk' -- the pk of the sleep ("tentative" for the tentative sleep)
// 'start' -- a Date object representing the start time/date
// 'end' -- a Date object representing the end time/date
// 'comment' -- a String; the user's comment on this sleep
// 'date' -- a Date object, in which we only care about the
//           actual day; which day to associate the sleep with

// Track the global mouse state
// 0 = No click
// 1 = Click, but no drag
// 2 = Click and drag
mouseState = 0;

// Utility function used by the mouseX functions to choose
// a reasonable end time, if the one you are hovering over
// would cause a negative time sleep
function getValidEndTime(startTime, endTime)
{
    // Duplicate endTime, since we don't ever want to return
    // the original (because it's likely we'll be mutating
    // what we return)
    var newEndTime = new Date(endTime);
    if (newEndTime > startTime)
    {
	return newEndTime;
    }

    // Try bumping the end time to the current date
    newEndTime.setDate(startTime.getDate());
    if (newEndTime > startTime)
    {
	return newEndTime;
    }

    // Bump up by one more day, if that doesn't put us
    // into the future. Else, just return the same time as
    // the startTime.
    var dateToday = new Date();
    dateToday.setHours(24);
    dateToday.setMinutes(0);
    dateToday.setSeconds(0);
    dateToday.setMicroseconds(0);
    newEndTime.setDate(newEndTime.getDate()+1);
    if (newEndTime < dateToday)
    {
	return newEndTime;
    }
    else
    {
	return new Date(startTime);
    }
}
function mouseDown(startTime, endTime)
{
    // If we aren't in the middle of creating a sleep,
    // initiate a new one.
    if (mouseState == 0 || mouseState == 1)
    {
	// Update mouseState
	mouseState = 1;

	// Create a tentative sleep
	tentativeSleep = {
	    'pk': "tentative",
	    'start': startTime,
	    'end': null,
	    'comment': null,
	    'date': null
	};
    }
    // If we were in the middle of creating a sleep,
    // use this to terminate the sleep
    else
    {
	// Update mouseState
	mouseState = 0;
	// Complete the tentative sleep
	tentativeSleep.end = getValidEndTime(tentativeSleep.start, endTime);
	// Spawn a popup to make final edits, save the tentative
	// sleep
	spawnPopup();
    }
}
function mouseUp(startTime, endTime)
{
    // Only if we were previously dragging, complete this sleep
    if (mouseState == 2)
    {
	// Update mouseState
	mouseState = 0;

	// Update the tentative sleep
	tentativeSleep.end = getValidEndTime(tentativeSleep.start, endTime);
	// Spawn a popup to make final edits, save the tentative
	// sleep
	spawnPopup();
    }
}
function mouseMove(startTime, endTime)
{
    // Only repond to click-and-drag
    if (mouseState == 1 || mouseState == 2)
    {
	// Update mouseState
	mouseState = 2;
	if (getValidEndTime(tentativeSleep.start, endTime) != tentativeSleep.end)
	{
	    tentativeSleep.end = getValidEndTime(tentativeSleep.start, endTime);
	    drawSleep(tentativeSleep);
	}
    }
}

function spawnPopup()
{
    $("#confirm-sleep-entry [name='start']").datepicker("setDate", tentativeSleep.start);
    $("#confirm-sleep-entry [name='end']").datepicker("setDate", tentativeSleep.end);
    $("#confirm-sleep-entry [name='date']").datepicker("setDate", new Date((tentativeSleep.start.valueOf() + tentativeSleep.end.valueOf()) / 2));
    $("#confirm-sleep-entry").dialog("open");
}
function clearSleep(sleep)
{
    // Clear the sleep box
    $(".sleep-id-" + sleep.pk).remove();
}
// Handle the JSON response from the server
// containing a list of our sleeps
function processSleeps(jsonData)
{
    // Clear our current dict of sleeps
    sleeps = {};
    // Build a new dict based on JSON data
    for (var i in jsonData)
    {
	var sleep = jsonData[i];
	sleeps[sleep.pk] = {
	    'pk': sleep.pk,
	    'start': new Date(sleep.fields.start_time),
	    'end': new Date(sleep.fields.end_time),
	    'comments': sleep.fields.comments,
	    'date': new Date(sleep.fields.date)
	};
    }
    // Now render all our current sleeps
    renderSleeps();
}
// Render all sleeps in the sleeps dict
function renderSleeps()
{
    for (var i in sleeps)
    {
	var sleep = sleeps[i];
	drawSleep(sleep);
    }
}
// Draw floating box from $td1 to $td2, potentially
// drawing the beginning and end as a continuing beginning/end
function drawSleepBox(sleep_pk, $td1, $td2, drawStartIcon, drawEndIcon)
{
    // Fill in default values
    if (typeof(drawStartIcon) === 'undefined')
    {
	drawStartIcon = true;
    }
    if (typeof(drawEndIcon) === 'undefined')
    {
	drawEndIcon = true;
    }

    // Compute the width and draw the box itself
    var width = $td2.position().left - $td1.position().left;
    var $sleep_box = $("

").css("padding-right", width+19).css("margin-right", -width-21)
	.css("margin-left", 1).addClass("sleep-box")
	.addClass("sleep-id-" + sleep_pk);
    // Add the tentative class if it's a tentative box (changes the color)
    if (sleep_pk == "tentative") $sleep_box.addClass("tentative");

    // Draw the start icon if needed
    if (drawStartIcon)
    {
	var $sleep_box_start = $("

");
	if (sleep_pk == "tentative") $sleep_box_start.addClass("tentative");
	$sleep_box.append($sleep_box_start);
    }
    if (drawEndIcon)
    {
	var $sleep_box_end = $("

").css("margin-right", -width-29);
	if (sleep_pk == "tentative") $sleep_box_end.addClass("tentative");
	$sleep_box.append($sleep_box_end);
    }
    $td1.append($sleep_box);
}
// Draw a sleep on the grid
function drawSleep(sleep)
{
    // Duplicate the input Date objects on the sleep
    var start = new Date(sleep.start);
    var end = new Date(sleep.end);
    // Round off the times to the nearest half-hour
    var startMinutes = start.getMinutes() + MINUTES_PER_HOUR/4;
    var endMinutes = end.getMinutes() + MINUTES_PER_HOUR/4;
    start.setMinutes(startMinutes - (startMinutes % (MINUTES_PER_HOUR/2)));
    start.setSeconds(0);
    start.setMicroseconds(0);
    end.setMinutes(endMinutes - (endMinutes % (MINUTES_PER_HOUR/2)));
    end.setSeconds(0);
    end.setMicroseconds(0);

    // First clear the current sleep box
    $(".sleep-id-" + sleep.pk).remove();
    // Now find the appropriate blocks
    var startblock = timeblocks[start.getTime()];
    if (typeof(startblock) === 'undefined') return false;
    var $starttd = startblock['start'];
    if ($starttd == null) return false;
    var endblock = timeblocks[end.getTime()];
    if (typeof(endblock) === 'undefined') return false;
    var $endtd = endblock['end'];
    if ($endtd == null) return false;
    // Fill them in with carryovers as needed
    if (start.getDate() == end.getDate())
    {
	// It's all one line -- render normally
	drawSleepBox(sleep.pk, $starttd, $endtd);
    }
    else
    {
	// Multiline display with carryovers
	var curDate = new Date(start);
	curDate.setHours(0);
	curDate.setMinutes(0);
	curDate.setSeconds(0);
	curDate.setMicroseconds(0);
	while (curDate <= end)
	{
	    if (curDate.getDate() == start.getDate())
	    {
		var endRowTime = new Date(start);
		endRowTime.setHours(24);
		endRowTime.setMinutes(0);
		var endRowBlock = timeblocks[endRowTime.getTime()];
		if (typeof(endRowBlock) === 'undefined') return false;
		var $endRowTd = endRowBlock['end'];
		if($endRowTd == null) return false;
		drawSleepBox(sleep.pk, $starttd, $endRowTd, true, false);
	    }
	    else if (curDate.getDate() == end.getDate())
	    {
		var beginRowTime = new Date(end);
		beginRowTime.setHours(0);
		beginRowTime.setMinutes(0);
		var beginRowBlock = timeblocks[beginRowTime.getTime()];
		if (typeof(beginRowBlock) === 'undefined') return false;
		var $beginRowTd = beginRowBlock['start'];
		if($beginRowTd == null) return false;
		drawSleepBox(sleep.pk, $beginRowTd, $endtd, false, true);
	    }
	    else
	    {
		var beginRowTime = new Date(curDate);
		beginRowTime.setHours(0);
		beginRowTime.setMinutes(0);
		var beginRowBlock = timeblocks[beginRowTime.getTime()];
		if (typeof(beginRowBlock) === 'undefined') return false;
		var $beginRowTd = beginRowBlock['start'];
		if($beginRowTd == null) return false;
		var endRowTime = new Date(curDate);
		endRowTime.setHours(24);
		endRowTime.setMinutes(0);
		var endRowBlock = timeblocks[endRowTime.getTime()];
		if (typeof(endRowBlock) === 'undefined') return false;
		var $endRowTd = endRowBlock['end'];
		if($endRowTd == null) return false;
		drawSleepBox(sleep.pk, $beginRowTd, $endRowTd, false, false);
	    }
	    // Increment the date
	    curDate.setDate(curDate.getDate()+1);
	}
    }
}

$(document).ready(function()
{
    // Clear the timeblocks reverse map
    timeblocks = {};

    // Get the sleep grid div, create a table
    var $sleep_grid = $(".sleep-grid");
    var $sleep_table = $("

").addClass("sleep-table");
    $sleep_table.prop("cellspacing", 0).prop("cellpadding", 0);
    $sleep_grid.append($sleep_table);

    // Store the date without any time component
    var today = new Date();
    today.setHours(0);
    today.setMinutes(0);
    today.setSeconds(0);
    today.setMilliseconds(0);

    // Create a header for the times
    var $tr = $("

").addClass("header-row");
    $tr.append($("	").addClass("corner"));
    for (var h = 0; h < HOURS_PER_DAY; ++h)
    {
	var $th = $("	").addClass("column-header")
	    .html(h + ":00");
	$tr.append($th);
    }
    $sleep_table.append($tr);
    // Iterate backwards over the past week, starting from the current date
    for (var d = 0; d < DAYS_PER_WEEK; ++d)
    {
	var $tr = $("

");
	// Create a header for the row
	var date = new Date(today);
	date.setDate(date.getDate() - d);
	var $td = $("	").html(date.toLocaleDateString())
	    .addClass("row-header");
	$tr.append($td);
	// Create a left and right cell per hour
	for (var h = 0; h < HOURS_PER_DAY; ++h)
	{
	    // Define an anonymous function for scoping
	    (function()
	    {
		// Compute the times at the start, middle, and end of this block
		var t_left = new Date(today);
		t_left.setDate(t_left.getDate() - d);
		var t_mid = new Date(t_left);
		var t_right = new Date(t_left);
		t_left.setHours(h);
		t_mid.setHours(h);
		t_mid.setMinutes(MINUTES_PER_HOUR/2);
		t_right.setHours(h+1);

		// Split the block into the two half-hours;
		// Set the mouse triggers
		var $td_left = $("	").addClass("left");
		$td_left.mousedown(function() {mouseDown(t_left, t_mid)})
		    .mousemove(function() {mouseMove(t_left, t_mid)})
		    .mouseup(function() {mouseUp(t_left, t_mid)});
		var $td_right = $("	").addClass("right");
		$td_right.mousedown(function() {mouseDown(t_mid, t_right)})
		    .mousemove(function() {mouseMove(t_mid, t_right)})
		    .mouseup(function() {mouseUp(t_mid, t_right)});

		$tr.append($td_left);
		$tr.append($td_right);

		// Add the half-blocks to the reverse map
		var left_time = t_left.getTime();
		var mid_time = t_mid.getTime();
		var right_time = t_right.getTime();
		if (typeof(timeblocks[left_time]) == 'undefined')
		{
		    timeblocks[left_time] = {'start': $td_left, 'end': null};
		}
		else
		{
		    timeblocks[left_time]['start'] = $td_left;
		}
		if (typeof(timeblocks[right_time]) == 'undefined')
		{
		    timeblocks[right_time] = {'start': null, 'end': $td_right};
		}
		else
		{
		    timeblocks[right_time]['end'] = $td_right;
		}
		timeblocks[mid_time] = {'start': $td_right, 'end': $td_left};
	    })();
	}
	$sleep_table.append($tr);
    }

    // Get the list of current sleeps and display them in the grid
    $.post("/sleep/getSleeps/", null, processSleeps);

    // Set the fields of the confirm sleep dialog to be datetimepicker objects
    $("#confirm-sleep-entry [name='start']").datetimepicker();
    $("#confirm-sleep-entry [name='end']").datetimepicker();
    $("#confirm-sleep-entry [name='date']").datepicker();
    // Set up the confirm sleep dialog
    $("#confirm-sleep-entry").dialog({
	autoOpen: false,
	height: 350,
	width: 400,
	modal: true,
	position: {my: "center top", at: "center top", of: window},
	buttons:
	{
	    "Create sleep": function()
	    {
		// Create the sleep
		$.post("/sleep/create/", {
		    "start[]": dateToArray($("#confirm-sleep-entry [name='start']").datepicker("getDate")),
		    "end[]": dateToArray($("#confirm-sleep-entry [name='end']").datepicker("getDate")),
		    "date[]": dateToArray($("#confirm-sleep-entry [name='date']").datepicker("getDate")),
		    "comments": $("#confirm-sleep-entry [name='comments']").val()
		}, function() {
		    // TODO: Update the page dyanmically (without reloading)
		    // For now:
		    location.reload();
		});
	    },
	    Cancel: function()
	    {
		$(this).dialog("close");
	    }
	},
	close: function()
	{
	    clearSleep(tentativeSleep);
	    $(".confirm-sleep-entry input").removeClass("ui-state-error");
	}
    });
});
<|endoftext|>


<commit_before>var _; //globals

/* This section uses a functional extension known as Underscore.js - http://documentcloud.github.com/underscore/
     "Underscore is a utility-belt library for JavaScript that provides a lot of the functional programming support
      that you would expect in Prototype.js (or Ruby), but without extending any of the built-in JavaScript objects.
      It's the tie to go along with jQuery's tux."
 */
describe("About Higher Order Functions", function () {

  it("should use filter to return array items that meet a criteria", function () {
    var numbers = [1,2,3];
    var odd = _(numbers).filter(function (x) { return x % 2 !== 0;});

    expect(odd).toEqual([1, 3]);
    expect(odd.length).toBe(2);
    expect(numbers.length).toBe(3);
  });

  it("should use 'map' to transform each element", function () {
    var numbers = [1, 2, 3];
    var numbersPlus1 = _(numbers).map(function(x) { return x + 1;});

    expect(numbersPlus1).toEqual([2, 3, 4]);
    expect(numbers).toEqual([1, 2, 3]);
  });

  it("should use 'reduce' to update the same result on each iteration", function () {
    var numbers = [1, 2, 3];
    var reduction = _(numbers).reduce(
      function(memo, x) {
        //note: memo is the result from last call, and x is the current number
        return memo + x;
      },
      /* initial */ 0
    );

    expect(reduction).toBe(6);
    expect(numbers).toEqual([1, 2, 3]);
  });

  it("should use 'forEach' for simple iteration", function () {
    var numbers = [1,2,3];
    var msg = "";
    var isEven = function (item) {
      msg += (item % 2) === 0;
    };

    _(numbers).forEach(isEven);

    expect(msg).toEqual('falsetruefalse');
    expect(numbers).toEqual([1, 2, 3]);
  });

  it("should use 'all' to test whether all items pass condition", function () {
    var onlyEven = [2,4,6];
    var mixedBag = [2,4,5,6];

    var isEven = function(x) { return x % 2 === 0;};

    expect(_(onlyEven).all(isEven)).toBe(FILL_ME_IN);
    expect(_(mixedBag).all(isEven)).toBe(FILL_ME_IN);
  });

  it("should use 'any' to test if any items passes condition" , function () {
    var onlyEven = [2,4,6];
    var mixedBag = [2,4,5,6];

    var isEven = function(x) { return x % 2 === 0;};

    expect(_(onlyEven).any(isEven)).toBe(FILL_ME_IN);
    expect(_(mixedBag).any(isEven)).toBe(FILL_ME_IN);
  });

  it("should use range to generate an array", function() {
    expect(_.range(3)).toEqual(FILL_ME_IN);
    expect(_.range(1, 4)).toEqual(FILL_ME_IN);
    expect(_.range(0, -4, -1)).toEqual(FILL_ME_IN);
  });

  it("should use flatten to make nested arrays easy to work with", function() {
    expect(_([ [1, 2], [3, 4] ]).flatten()).toEqual(FILL_ME_IN);
  });

  it("should use chain() ... .value() to use multiple higher order functions", function() {
    var result = _([ [0, 1], 2 ]).chain()
                     .flatten()
                     .map(function(x) { return x+1;} )
                     .reduce(function (sum, x) { return sum + x;})
                     .value();

    expect(result).toEqual(FILL_ME_IN);
  });

});

<commit_msg>completed "should use 'all' to test whether all items pass condition"<commit_after>var _; //globals

/* This section uses a functional extension known as Underscore.js - http://documentcloud.github.com/underscore/
     "Underscore is a utility-belt library for JavaScript that provides a lot of the functional programming support
      that you would expect in Prototype.js (or Ruby), but without extending any of the built-in JavaScript objects.
      It's the tie to go along with jQuery's tux."
 */
describe("About Higher Order Functions", function () {

  it("should use filter to return array items that meet a criteria", function () {
    var numbers = [1,2,3];
    var odd = _(numbers).filter(function (x) { return x % 2 !== 0;});

    expect(odd).toEqual([1, 3]);
    expect(odd.length).toBe(2);
    expect(numbers.length).toBe(3);
  });

  it("should use 'map' to transform each element", function () {
    var numbers = [1, 2, 3];
    var numbersPlus1 = _(numbers).map(function(x) { return x + 1;});

    expect(numbersPlus1).toEqual([2, 3, 4]);
    expect(numbers).toEqual([1, 2, 3]);
  });

  it("should use 'reduce' to update the same result on each iteration", function () {
    var numbers = [1, 2, 3];
    var reduction = _(numbers).reduce(
      function(memo, x) {
        //note: memo is the result from last call, and x is the current number
        return memo + x;
      },
      /* initial */ 0
    );

    expect(reduction).toBe(6);
    expect(numbers).toEqual([1, 2, 3]);
  });

  it("should use 'forEach' for simple iteration", function () {
    var numbers = [1,2,3];
    var msg = "";
    var isEven = function (item) {
      msg += (item % 2) === 0;
    };

    _(numbers).forEach(isEven);

    expect(msg).toEqual('falsetruefalse');
    expect(numbers).toEqual([1, 2, 3]);
  });

  it("should use 'all' to test whether all items pass condition", function () {
    var onlyEven = [2,4,6];
    var mixedBag = [2,4,5,6];

    var isEven = function(x) { return x % 2 === 0;};

    expect(_(onlyEven).all(isEven)).toBe(true);
    expect(_(mixedBag).all(isEven)).toBe(false);
  });

  it("should use 'any' to test if any items passes condition" , function () {
    var onlyEven = [2,4,6];
    var mixedBag = [2,4,5,6];

    var isEven = function(x) { return x % 2 === 0;};

    expect(_(onlyEven).any(isEven)).toBe(FILL_ME_IN);
    expect(_(mixedBag).any(isEven)).toBe(FILL_ME_IN);
  });

  it("should use range to generate an array", function() {
    expect(_.range(3)).toEqual(FILL_ME_IN);
    expect(_.range(1, 4)).toEqual(FILL_ME_IN);
    expect(_.range(0, -4, -1)).toEqual(FILL_ME_IN);
  });

  it("should use flatten to make nested arrays easy to work with", function() {
    expect(_([ [1, 2], [3, 4] ]).flatten()).toEqual(FILL_ME_IN);
  });

  it("should use chain() ... .value() to use multiple higher order functions", function() {
    var result = _([ [0, 1], 2 ]).chain()
                     .flatten()
                     .map(function(x) { return x+1;} )
                     .reduce(function (sum, x) { return sum + x;})
                     .value();

    expect(result).toEqual(FILL_ME_IN);
  });

});

<|endoftext|>


<commit_before>/*****************************************
 * Team Level Overrides - LOGO / POS
 *
 * LOGOS: Add your image to /logos with
 * the file name "{team_number}.png" and
 * then add your team number to the icons
 * array below.
 *
 * POSITION: Add your team's lattitude
 * and longitude to the updatedLocations
 * array below, following the documented
 * syntax.
 ****************************************/

// icons array - Add your team number here to pull your team's marker from /logos instead of using the default FIRST marker
var icons = [
	16,
	171,
	236,
	330,
	525,
	548,
	967,
	1058,
	1108,
	1418,
	1452,
	1540,
	1741,
	1982,
	2370,
	2403,
	2412,
	2626,
	2713,
	2827,
	2855,
	2848,
	3015,
	3223,
	3928,
	3946,
	4150,
	4646,
	4909,
	4924,
	5041,
	5113,
	5115,
	5431,
	5482,
	5587,
	5837,
	5859,
	5881,
	5938,
	6164,
	6412,
	6419,
	6467,
	6630,

    // Scraped images
    4,
    25,
    33,
    34,
    41,
    66,
    67,
    68,
    69,
    71,
    73,
    74,
    133,
    168,
    171,
    199,
    228,
    303,
    316,
    321,
    401,
    418,
    476,
    509,
    527,
    540,
    619,
    640,
    701,
    830,
    836,
    967,
    968,
    1024,
    1185,
    1222,
    1262,
    1263,
    1277,
    1288,
    1298,
    1334,
    1389,
    1422,
    1425,
    1429,
    1502,
    1597,
    1692,
    1712,
    1730,
    1766,
    1768,
    1778,
    1923,
    1939,
    1967,
    1977,
    1999,
    2096,
    2135,
    2335,
    2363,
    2374,
    2408,
    2410,
    2451,
    2456,
    2506,
    2529,
    2605,
    2626,
    2630,
    2704,
    2723,
    2805,
    2823,
    2846,
    2853,
    2883,
    2903,
    2905,
    2916,
    2945,
    2984,
    2993,
    3005,
    3023,
    3128,
    3129,
    3130,
    3158,
    3196,
    3208,
    3260,
    3322,
    3417,
    3459,
    3489,
	3572,
    3735,
    3756,
    3770,
    3824,
    3847,
    3946,
    4003,
    4085,
    4121,
    4159,
    4180,
    4207,
    4213,
    4248,
    4276,
    4308,
    4458,
    4468,
    4481,
    4499,
    4518,
    4586,
    4622,
    4646,
    4678,
    4828,
    4930,
    4961,
    4990,
    5030,
    5099,
    5232,
    5410,
    5412,
    5477,
    5494,
    5502,
    5514,
    5526,
    5528,
    5553,
    5582,
    5625,
    5667,
    5700,
    5818,
    5822,
    5877,
    5924,
    5966,
    5973,
    5990,
    6063,
    6064,
    6325,
    6331,
];

// updatedLocations Array.  Add your team's lat and lng to this array to modify the exact position of your team's marker.
/* Data Structure
    team_number: {
        lat: latitude,
        lng: longitude
    }

    All values should be numerical values, not strings.
 */
var updatedLocations = {
	330: {
		lat: 33.87253,
		lng: -118.395732
	},
	364: { // GeeII
		lat: 30.3897667,
		lng: -89.0436744
	},
	456: { // GeeII
		lat: 32.3257,
		lng: -90.9553
	},
	590: { // GeeII
		lat: 32.7809,
		lng: -89.2280
	},
	1421: { // GeeII
		lat: 30.6252185,
		lng: -89.6565752
	},
	1540: {
		lat: 45.511433,
		lng: -122.769145
	},
	1741: { // GeeII
		lat: 39.573578,
		lng: -86.191377
	},
	1818: { // GeeII
		lat: 32.4089,
		lng: -93.8193
	},
	1912: { // GeeII
		lat: 30.2715933,
		lng: -89.7455335
	},
	1920: { // GeeII
		lat: 29.9419,
		lng: -90.1140
	},
	1927: { // GeeII
		lat: 30.552632,
		lng: -89.0239767
	},
	2080: { // GeeII
		lat: 30.5172157,
		lng: -90.3809465
	},
	2183: { // GeeII
		lat: 29.8891,
		lnt: -90.4083
	},
	2190: { // GeeII
		lat: 31.3515155,
		lng: -89.2072034
	},
	2221: { // GeeII
		lat: 30.4068,
		lng: -90.0407
	},
	2992: { // GeeII
		lat: 30.3813461,
		lng: -90.1028666
	},
	2403: {
		lat: 33.434551,
		lng: -111.672356
	},
	2626: {
		lat: 45.403450,
		lng: -71.894242
	},
    2930: {
        lat: 47.868753,
        lng: -122.133521
    },
	3039: { // GeeII
		lat: 29.9657,
		lng: -90.3839029
	},
    3223: {
        lat: 47.651672,
        lng: -122.698748
    },
	3337: { // GeeII
		lat: 30.3742,
		lng: -91.0136
	},
	3468: { // GeeII
		lat: 32.5109,
		lng: -92.1421
	},
	3572: { // GeeII
		lat: 43.185072,
		lng: -86.272554
	},
	3606: { // GeeII
		lat: 30.4407,
		lng: -91.1589
	},
	3616: { // GeeII
		lat: 30.1567,
		lng: -92.0350
	},
	3666: { // GeeII
		lat: 31.2944,
		lng: -92.4582
	},
	3753: { // GeeII
		lat: 30.3994,
		lng: -91.0514
	},
	3946: { // GeeII
		lat: 30.2888866,
		lng: -89.772360
	},
	3991: { // GeeII
		lat: 30.4227,
		lng: -91.0351
	},
	4053: { // GeeII
		lat: 30.1010,
		lng: -91.0028
	},
	4087: { // GeeII
		lat: 30.0248,
		lng: -90.0677
	},
	4107: { // GeeII
		lat: 30.4349106,
		lng: -89.1967398
	},
    4309: {
        lat: 47.884388,
        lng: -121.863099
    },
	4316: { // GeeII
		lat: 29.6931,
		lng: -90.5588
	},
	4336: { // GeeII
		lat: 30.2045,
		lng: -92.0933
	},
	4353: { // GeeII
		lat: 30.2482,
		lng: -91.9921
	},
    4512: {
        lat: 47.924402,
        lng: -122.222745
    },
    4681: {
        lat: 47.880796,
        lng: -122.178467
    },
	4805: { //GeeII
		lat: 29.9255,
		lng: -90.1046
	},
    4915: {
        lat: 47.636593,
        lng: -122.524264
    },
	4978: { // GeeII
		lat: 30.3831,
		lng: -91.0875
	},
	5168: { // GeeII
		lat: 29.9685,
		lng: -90.0927
	},
	5325: { // GeeII
		lat: 29.8829956,
		lng: -90.1317362
	},
	5785: { // GeeII
		lat: 30.4369,
		lng: -91.1590
	},
	5863: { // GeeII
		lat: 30.3930,
		lng: -91.2407
	},
	5864: { // GeeII
		lat: 30.4599,
		lng: -91.2583
	},
	5881: {
		lat: 42.6918615,
		lng: -73.8357275
	},
	5938: {
		lat: 39.15819444,
		lng: -75.524405556
	},
	5965: { // GeeII
		lat: 29.902892,
		lng: -90.1547547
	},
	5971: { // GeeII
		lat: 29.9900,
		lng: -90.1615
	},
	5997: { // GeeII
		lat: 30.2605,
		lng: -91.104573
	},
	6068: { // GeeII
		lat: 33.4427,
		lng: -90.6441
	},
	6103: { // GeeII
		lat: 30.2619,
		lng: -91.2534
	}
}
<commit_msg>Update 2410 & 1982 Lat/Long<commit_after>/*****************************************
 * Team Level Overrides - LOGO / POS
 *
 * LOGOS: Add your image to /logos with
 * the file name "{team_number}.png" and
 * then add your team number to the icons
 * array below.
 *
 * POSITION: Add your team's lattitude
 * and longitude to the updatedLocations
 * array below, following the documented
 * syntax.
 ****************************************/

// icons array - Add your team number here to pull your team's marker from /logos instead of using the default FIRST marker
var icons = [
	16,
	171,
	236,
	330,
	525,
	548,
	967,
	1058,
	1108,
	1418,
	1452,
	1540,
	1741,
	1982,
	2370,
	2403,
	2412,
	2626,
	2713,
	2827,
	2855,
	2848,
	3015,
	3223,
	3928,
	3946,
	4150,
	4646,
	4909,
	4924,
	5041,
	5113,
	5115,
	5431,
	5482,
	5587,
	5837,
	5859,
	5881,
	5938,
	6164,
	6412,
	6419,
	6467,
	6630,

    // Scraped images
    4,
    25,
    33,
    34,
    41,
    66,
    67,
    68,
    69,
    71,
    73,
    74,
    133,
    168,
    171,
    199,
    228,
    303,
    316,
    321,
    401,
    418,
    476,
    509,
    527,
    540,
    619,
    640,
    701,
    830,
    836,
    967,
    968,
    1024,
    1185,
    1222,
    1262,
    1263,
    1277,
    1288,
    1298,
    1334,
    1389,
    1422,
    1425,
    1429,
    1502,
    1597,
    1692,
    1712,
    1730,
    1766,
    1768,
    1778,
    1923,
    1939,
    1967,
    1977,
    1999,
    2096,
    2135,
    2335,
    2363,
    2374,
    2408,
    2410,
    2451,
    2456,
    2506,
    2529,
    2605,
    2626,
    2630,
    2704,
    2723,
    2805,
    2823,
    2846,
    2853,
    2883,
    2903,
    2905,
    2916,
    2945,
    2984,
    2993,
    3005,
    3023,
    3128,
    3129,
    3130,
    3158,
    3196,
    3208,
    3260,
    3322,
    3417,
    3459,
    3489,
	3572,
    3735,
    3756,
    3770,
    3824,
    3847,
    3946,
    4003,
    4085,
    4121,
    4159,
    4180,
    4207,
    4213,
    4248,
    4276,
    4308,
    4458,
    4468,
    4481,
    4499,
    4518,
    4586,
    4622,
    4646,
    4678,
    4828,
    4930,
    4961,
    4990,
    5030,
    5099,
    5232,
    5410,
    5412,
    5477,
    5494,
    5502,
    5514,
    5526,
    5528,
    5553,
    5582,
    5625,
    5667,
    5700,
    5818,
    5822,
    5877,
    5924,
    5966,
    5973,
    5990,
    6063,
    6064,
    6325,
    6331,
];

// updatedLocations Array.  Add your team's lat and lng to this array to modify the exact position of your team's marker.
/* Data Structure
    team_number: {
        lat: latitude,
        lng: longitude
    }

    All values should be numerical values, not strings.
 */
var updatedLocations = {
	330: {
		lat: 33.87253,
		lng: -118.395732
	},
	364: { // GeeII
		lat: 30.3897667,
		lng: -89.0436744
	},
	456: { // GeeII
		lat: 32.3257,
		lng: -90.9553
	},
	590: { // GeeII
		lat: 32.7809,
		lng: -89.2280
	},
	1421: { // GeeII
		lat: 30.6252185,
		lng: -89.6565752
	},
	1540: {
		lat: 45.511433,
		lng: -122.769145
	},
	1741: { // GeeII
		lat: 39.573578,
		lng: -86.191377
	},
	1818: { // GeeII
		lat: 32.4089,
		lng: -93.8193
	},
	1912: { // GeeII
		lat: 30.2715933,
		lng: -89.7455335
	},
	1920: { // GeeII
		lat: 29.9419,
		lng: -90.1140
	},
	1927: { // GeeII
		lat: 30.552632,
		lng: -89.0239767
	},
	1982: { // GeeII
		lat: 39.006258,
		lng: -94.732603
	},
	2080: { // GeeII
		lat: 30.5172157,
		lng: -90.3809465
	},
	2183: { // GeeII
		lat: 29.8891,
		lnt: -90.4083
	},
	2190: { // GeeII
		lat: 31.3515155,
		lng: -89.2072034
	},
	2221: { // GeeII
		lat: 30.4068,
		lng: -90.0407
	},
	2992: { // GeeII
		lat: 30.3813461,
		lng: -90.1028666
	},
	2403: {
		lat: 33.434551,
		lng: -111.672356
	},
	2410: {
		lat: 38.856385,
		lng: -94.672073
	},
	2626: {
		lat: 45.403450,
		lng: -71.894242
	},
    2930: {
        lat: 47.868753,
        lng: -122.133521
    },
	3039: { // GeeII
		lat: 29.9657,
		lng: -90.3839029
	},
    3223: {
        lat: 47.651672,
        lng: -122.698748
    },
	3337: { // GeeII
		lat: 30.3742,
		lng: -91.0136
	},
	3468: { // GeeII
		lat: 32.5109,
		lng: -92.1421
	},
	3572: { // GeeII
		lat: 43.185072,
		lng: -86.272554
	},
	3606: { // GeeII
		lat: 30.4407,
		lng: -91.1589
	},
	3616: { // GeeII
		lat: 30.1567,
		lng: -92.0350
	},
	3666: { // GeeII
		lat: 31.2944,
		lng: -92.4582
	},
	3753: { // GeeII
		lat: 30.3994,
		lng: -91.0514
	},
	3946: { // GeeII
		lat: 30.2888866,
		lng: -89.772360
	},
	3991: { // GeeII
		lat: 30.4227,
		lng: -91.0351
	},
	4053: { // GeeII
		lat: 30.1010,
		lng: -91.0028
	},
	4087: { // GeeII
		lat: 30.0248,
		lng: -90.0677
	},
	4107: { // GeeII
		lat: 30.4349106,
		lng: -89.1967398
	},
    4309: {
        lat: 47.884388,
        lng: -121.863099
    },
	4316: { // GeeII
		lat: 29.6931,
		lng: -90.5588
	},
	4336: { // GeeII
		lat: 30.2045,
		lng: -92.0933
	},
	4353: { // GeeII
		lat: 30.2482,
		lng: -91.9921
	},
    4512: {
        lat: 47.924402,
        lng: -122.222745
    },
    4681: {
        lat: 47.880796,
        lng: -122.178467
    },
	4805: { //GeeII
		lat: 29.9255,
		lng: -90.1046
	},
    4915: {
        lat: 47.636593,
        lng: -122.524264
    },
	4978: { // GeeII
		lat: 30.3831,
		lng: -91.0875
	},
	5168: { // GeeII
		lat: 29.9685,
		lng: -90.0927
	},
	5325: { // GeeII
		lat: 29.8829956,
		lng: -90.1317362
	},
	5785: { // GeeII
		lat: 30.4369,
		lng: -91.1590
	},
	5863: { // GeeII
		lat: 30.3930,
		lng: -91.2407
	},
	5864: { // GeeII
		lat: 30.4599,
		lng: -91.2583
	},
	5881: {
		lat: 42.6918615,
		lng: -73.8357275
	},
	5938: {
		lat: 39.15819444,
		lng: -75.524405556
	},
	5965: { // GeeII
		lat: 29.902892,
		lng: -90.1547547
	},
	5971: { // GeeII
		lat: 29.9900,
		lng: -90.1615
	},
	5997: { // GeeII
		lat: 30.2605,
		lng: -91.104573
	},
	6068: { // GeeII
		lat: 33.4427,
		lng: -90.6441
	},
	6103: { // GeeII
		lat: 30.2619,
		lng: -91.2534
	}
}
<|endoftext|>


<commit_before>const EventEmitter = require('events');
const globby = require('globby');
const fs = require('fs');
const path = require('path');
const Mocha = require('mocha');
const NYC = require('nyc');
const utils = require('@after-work.js/utils');
const { deleteTransform } = require('@after-work.js/transform');
const options = require('./options');

class Runner extends EventEmitter {
  constructor(argv) {
    super();
    this.argv = argv;
    this.testFiles = [];
    this.srcFiles = [];
    this.mochaRunner = undefined;
    this.mocha = undefined;
    this.nyc = undefined;
    this.isWrapped = false;
    this.isRunning = false;
    this.debugging = false;
    this.snapshotContexts = new Map();
    this.warnings = [];
    this.startCallbacks = [];
    this.finishedCallbacks = [];
  }

  logLine(prefix, msg) {
    utils.writeLine(prefix, msg);
  }

  safeDeleteCache(f) {
    utils.safeDeleteCache(f);
    deleteTransform(f);
  }

  getMatchedSrcDependency(testFile) {
    const testName = path
      .basename(testFile)
      .split('.')
      .shift();
    this.safeDeleteCache(testFile);
    deleteTransform(testFile);
    const mod = utils.safeRequireCache(testFile);
    const found = mod.children
      .filter(m => this.srcFiles.indexOf(m.id) !== -1)
      .map(m => m.id);
    const use = utils.matchDependency(found, testName);
    return use;
  }

  getMatchedTestDependency(srcFile) {
    const srcName = path
      .basename(srcFile)
      .split('.')
      .shift();
    const found = this.testFiles.filter(f => {
      const mod = utils.safeRequireCache(f);
      return mod.children.filter(m => m.id === srcFile).length !== 0;
    });
    const use = utils.matchDependency(found, srcName);
    return use;
  }

  getSrcFilesFromTestFiles(testFiles) {
    return testFiles.reduce(
      (acc, curr) => [...acc, ...this.getMatchedSrcDependency(curr)],
      [],
    );
  }

  getTestFilesFromSrcFiles(srcFiles) {
    return srcFiles.reduce(
      (acc, curr) => [...acc, ...this.getMatchedTestDependency(curr)],
      [],
    );
  }

  findFiles(glob) {
    return utils.filter(
      this.getFilter().files,
      globby.sync(glob).map(f => path.resolve(f)),
    );
  }

  getFilter() {
    return this.argv.filter.node;
  }

  setTestFiles() {
    this.testFiles = this.findFiles(this.argv.glob).filter(f => utils.isTestFile(f, this.argv));
    if (!this.testFiles.length) {
      console.error('No files found for:', this.argv.glob);
      if (!this.argv.interactive) {
        this.exit(1);
      }
    }
    return this;
  }

  setSrcFiles() {
    this.srcFiles = this.findFiles(this.argv.src).filter(f => utils.isSrcFile(f, this.argv));
    return this;
  }

  require() {
    if (!this.argv.coverage) {
      this.register();
    }
    const importCwd = require('import-cwd');
    this.argv.require.forEach(m => importCwd(m));
    return this;
  }

  deleteCoverage() {
    delete global.__coverage__;
    return this;
  }

  onFinished(failures) {
    if (!this.isRunning) {
      return;
    }
    this.finishedCallbacks.forEach(fn => fn());
    this.isRunning = false;
    if (failures === 0 && this.argv.coverage) {
      this.nyc.writeCoverageFile();
      this.nyc.report();
    }

    if (this.argv.warnings && this.warnings.length) {
      console.error('\u001b[33mwarnings:\u001b[0m');
      this.warnings.forEach(w => {
        console.error('');
        w();
      });
      console.error('');
    }
    this.snapshotContexts.forEach(({ currentTestName, snapshotState }) => {
      const uncheckedCount = snapshotState.getUncheckedCount();
      if (uncheckedCount) {
        snapshotState.removeUncheckedKeys();
      }
      snapshotState.save();
      if (uncheckedCount && !this.argv.updateSnapshot) {
        console.error(
          `\u001b[33mObsolete snapshot:\u001b[0m \u001b[31m${currentTestName}\u001b[0m \u001b[33m\n\u001b[90mat (${snapshotState._snapshotPath})\u001b[0m`,
        );
      }
    });
    if (this.argv.watch) {
      this.emit('watchEnd');
      return;
    }
    this.exit(failures);
    if (this.argv.exit) {
      process.exit();
    }
  }

  runTests() {
    this.isRunning = true;
    this.warnings = [];
    this.mochaRunner = this.mocha.run(failures => this.onFinished(failures));
    this.mochaRunner.once('start', () => utils.clearLine());
  }

  register() {
    if (this.argv.hookRequire) {
      require('@after-work.js/register')(
        this.argv,
        this.srcFiles,
        this.testFiles,
        fn => this.warnings.push(fn),
        fn => this.startCallbacks.push(fn),
        fn => this.finishedCallbacks.push(fn),
      );
    }
  }

  setup(testFiles, srcFiles) {
    srcFiles.forEach(f => this.safeDeleteCache(f));
    if (this.argv.coverage) {
      this.nyc.reset();
      if (!this.isWrapped) {
        this.nyc.wrap();
        this.isWrapped = true;
        this.register();
      }
    }
    testFiles.forEach(f => {
      this.safeDeleteCache(f);
      this.mocha.addFile(f);
    });
    if (this.argv.coverage) {
      srcFiles.forEach(f => {
        if (!this.argv.shouldInstrument(f)) {
          return;
        }
        this.logLine('Loading', f);
        require(f);
      });
    }
    return this;
  }

  setupAndRunTests(testFiles, srcFiles) {
    this.snapshotContexts.clear();
    process.removeAllListeners();
    if (this.mochaRunner) {
      this.mochaRunner.removeAllListeners();
    }
    this.mocha = new Mocha(this.argv.mocha);
    this.mocha.suite.on('pre-require', (_, file) => {
      this.logLine('Loading', file);
    });
    this.nyc = new NYC(this.argv.nyc);
    this.argv.shouldInstrument = f => !utils.isTestFile(f, this.argv) && this.nyc.exclude.shouldInstrument(f);
    try {
      this.deleteCoverage()
        .setup(testFiles, srcFiles)
        .runTests();
    } catch (err) {
      this.isRunning = false;
      console.error(err);
      if (this.argv.watch) {
        this.emit('interactive');
        return;
      }
      this.exit(1);
    }
  }

  run() {
    this.startCallbacks.forEach(fn => fn());
    this.setupAndRunTests(this.testFiles, this.srcFiles);
  }

  autoDetectDebug() {
    const exv = process.execArgv.join();
    const debug = exv.includes('inspect') || exv.includes('debug');
    if (debug) {
      this.argv.mocha.timeout = false;
      this.debugging = true;
    }
    return this;
  }

  exit(code) {
    process.exitCode = code;
  }
}

const configure = configPath => {
  if (configPath === null) {
    return {};
  }
  if (!fs.existsSync(configPath)) {
    throw new Error(`Config ${configPath} not found`);
  }
  let config = {};
  const foundConfig = require(configPath);
  if (typeof foundConfig === 'function') {
    config = Object.assign({}, foundConfig());
  } else {
    config = Object.assign({}, foundConfig);
  }
  return config;
};

const node = {
  Runner,
  configure,
  command: ['node', '$0'],
  desc: 'Run tests in node',
  builder(yargs) {
    return yargs
      .middleware(utils.addDefaults)
      .options(options)
      .config('config', configure)
      .coerce('babel', utils.coerceBabel)
      .middleware(mwargv => {
        if (!mwargv.babel.enable && mwargv.coverage) {
          mwargv.nyc.hookRequire = true; // Enable nyc instrumenting on the fly
        }
      });
  },
  handler(argv) {
    const runner = new node.Runner(argv);
    argv.__isNodeRunner = true;
    if (argv.presetEnv) {
      require('@after-work.js/preset-plugin')(runner);
    }
    let skipInitialInteractive = false;
    if (argv.watch && !argv.interactive) {
      skipInitialInteractive = true;
      argv.interactive = true;
    }
    if (argv.interactive) {
      require('@after-work.js/interactive-plugin')(runner);
    }
    if (argv.watch) {
      require('@after-work.js/watch-plugin')(runner);
    }
    runner
      .autoDetectDebug()
      .setTestFiles()
      .setSrcFiles()
      .require();
    if (!skipInitialInteractive && argv.interactive) {
      runner.emit('interactive');
      return runner;
    }
    runner.run();
    return runner;
  },
};

module.exports = node;
<commit_msg>fix: use istanbul instrumenter (#489)<commit_after>const EventEmitter = require('events');
const globby = require('globby');
const fs = require('fs');
const path = require('path');
const Mocha = require('mocha');
const NYC = require('nyc');
const utils = require('@after-work.js/utils');
const { deleteTransform } = require('@after-work.js/transform');
const options = require('./options');

class Runner extends EventEmitter {
  constructor(argv) {
    super();
    this.argv = argv;
    this.testFiles = [];
    this.srcFiles = [];
    this.mochaRunner = undefined;
    this.mocha = undefined;
    this.nyc = undefined;
    this.isWrapped = false;
    this.isRunning = false;
    this.debugging = false;
    this.snapshotContexts = new Map();
    this.warnings = [];
    this.startCallbacks = [];
    this.finishedCallbacks = [];
  }

  logLine(prefix, msg) {
    utils.writeLine(prefix, msg);
  }

  safeDeleteCache(f) {
    utils.safeDeleteCache(f);
    deleteTransform(f);
  }

  getMatchedSrcDependency(testFile) {
    const testName = path
      .basename(testFile)
      .split('.')
      .shift();
    this.safeDeleteCache(testFile);
    deleteTransform(testFile);
    const mod = utils.safeRequireCache(testFile);
    const found = mod.children
      .filter(m => this.srcFiles.indexOf(m.id) !== -1)
      .map(m => m.id);
    const use = utils.matchDependency(found, testName);
    return use;
  }

  getMatchedTestDependency(srcFile) {
    const srcName = path
      .basename(srcFile)
      .split('.')
      .shift();
    const found = this.testFiles.filter(f => {
      const mod = utils.safeRequireCache(f);
      return mod.children.filter(m => m.id === srcFile).length !== 0;
    });
    const use = utils.matchDependency(found, srcName);
    return use;
  }

  getSrcFilesFromTestFiles(testFiles) {
    return testFiles.reduce(
      (acc, curr) => [...acc, ...this.getMatchedSrcDependency(curr)],
      [],
    );
  }

  getTestFilesFromSrcFiles(srcFiles) {
    return srcFiles.reduce(
      (acc, curr) => [...acc, ...this.getMatchedTestDependency(curr)],
      [],
    );
  }

  findFiles(glob) {
    return utils.filter(
      this.getFilter().files,
      globby.sync(glob).map(f => path.resolve(f)),
    );
  }

  getFilter() {
    return this.argv.filter.node;
  }

  setTestFiles() {
    this.testFiles = this.findFiles(this.argv.glob).filter(f => utils.isTestFile(f, this.argv));
    if (!this.testFiles.length) {
      console.error('No files found for:', this.argv.glob);
      if (!this.argv.interactive) {
        this.exit(1);
      }
    }
    return this;
  }

  setSrcFiles() {
    this.srcFiles = this.findFiles(this.argv.src).filter(f => utils.isSrcFile(f, this.argv));
    return this;
  }

  require() {
    if (!this.argv.coverage) {
      this.register();
    }
    const importCwd = require('import-cwd');
    this.argv.require.forEach(m => importCwd(m));
    return this;
  }

  deleteCoverage() {
    delete global.__coverage__;
    return this;
  }

  onFinished(failures) {
    if (!this.isRunning) {
      return;
    }
    this.finishedCallbacks.forEach(fn => fn());
    this.isRunning = false;
    if (failures === 0 && this.argv.coverage) {
      this.nyc.writeCoverageFile();
      this.nyc.report();
    }

    if (this.argv.warnings && this.warnings.length) {
      console.error('\u001b[33mwarnings:\u001b[0m');
      this.warnings.forEach(w => {
        console.error('');
        w();
      });
      console.error('');
    }
    this.snapshotContexts.forEach(({ currentTestName, snapshotState }) => {
      const uncheckedCount = snapshotState.getUncheckedCount();
      if (uncheckedCount) {
        snapshotState.removeUncheckedKeys();
      }
      snapshotState.save();
      if (uncheckedCount && !this.argv.updateSnapshot) {
        console.error(
          `\u001b[33mObsolete snapshot:\u001b[0m \u001b[31m${currentTestName}\u001b[0m \u001b[33m\n\u001b[90mat (${snapshotState._snapshotPath})\u001b[0m`,
        );
      }
    });
    if (this.argv.watch) {
      this.emit('watchEnd');
      return;
    }
    this.exit(failures);
    if (this.argv.exit) {
      process.exit();
    }
  }

  runTests() {
    this.isRunning = true;
    this.warnings = [];
    this.mochaRunner = this.mocha.run(failures => this.onFinished(failures));
    this.mochaRunner.once('start', () => utils.clearLine());
  }

  register() {
    if (this.argv.hookRequire) {
      require('@after-work.js/register')(
        this.argv,
        this.srcFiles,
        this.testFiles,
        fn => this.warnings.push(fn),
        fn => this.startCallbacks.push(fn),
        fn => this.finishedCallbacks.push(fn),
      );
    }
  }

  setup(testFiles, srcFiles) {
    srcFiles.forEach(f => this.safeDeleteCache(f));
    if (this.argv.coverage) {
      this.nyc.reset();
      if (!this.isWrapped) {
        this.nyc.wrap();
        this.isWrapped = true;
        this.register();
      }
    }
    testFiles.forEach(f => {
      this.safeDeleteCache(f);
      this.mocha.addFile(f);
    });
    if (this.argv.coverage) {
      srcFiles.forEach(f => {
        if (!this.argv.shouldInstrument(f)) {
          return;
        }
        this.logLine('Loading', f);
        require(f);
      });
    }
    return this;
  }

  setupAndRunTests(testFiles, srcFiles) {
    this.snapshotContexts.clear();
    process.removeAllListeners();
    if (this.mochaRunner) {
      this.mochaRunner.removeAllListeners();
    }
    this.mocha = new Mocha(this.argv.mocha);
    this.mocha.suite.on('pre-require', (_, file) => {
      this.logLine('Loading', file);
    });
    this.nyc = new NYC(this.argv.nyc);
    this.argv.shouldInstrument = f => !utils.isTestFile(f, this.argv) && this.nyc.exclude.shouldInstrument(f);
    try {
      this.deleteCoverage()
        .setup(testFiles, srcFiles)
        .runTests();
    } catch (err) {
      this.isRunning = false;
      console.error(err);
      if (this.argv.watch) {
        this.emit('interactive');
        return;
      }
      this.exit(1);
    }
  }

  run() {
    this.startCallbacks.forEach(fn => fn());
    this.setupAndRunTests(this.testFiles, this.srcFiles);
  }

  autoDetectDebug() {
    const exv = process.execArgv.join();
    const debug = exv.includes('inspect') || exv.includes('debug');
    if (debug) {
      this.argv.mocha.timeout = false;
      this.debugging = true;
    }
    return this;
  }

  exit(code) {
    process.exitCode = code;
  }
}

const configure = configPath => {
  if (configPath === null) {
    return {};
  }
  if (!fs.existsSync(configPath)) {
    throw new Error(`Config ${configPath} not found`);
  }
  let config = {};
  const foundConfig = require(configPath);
  if (typeof foundConfig === 'function') {
    config = Object.assign({}, foundConfig());
  } else {
    config = Object.assign({}, foundConfig);
  }
  return config;
};

const node = {
  Runner,
  configure,
  command: ['node', '$0'],
  desc: 'Run tests in node',
  builder(yargs) {
    return yargs
      .middleware(utils.addDefaults)
      .options(options)
      .config('config', configure)
      .coerce('babel', utils.coerceBabel)
      .middleware(mwargv => {
        if (!mwargv.babel.enable && mwargv.coverage) {
          mwargv.nyc.hookRequire = true; // Enable nyc instrumenting on the fly
          mwargv.nyc.instrumenter = './lib/instrumenters/istanbul';
        }
      });
  },
  handler(argv) {
    const runner = new node.Runner(argv);
    argv.__isNodeRunner = true;
    if (argv.presetEnv) {
      require('@after-work.js/preset-plugin')(runner);
    }
    let skipInitialInteractive = false;
    if (argv.watch && !argv.interactive) {
      skipInitialInteractive = true;
      argv.interactive = true;
    }
    if (argv.interactive) {
      require('@after-work.js/interactive-plugin')(runner);
    }
    if (argv.watch) {
      require('@after-work.js/watch-plugin')(runner);
    }
    runner
      .autoDetectDebug()
      .setTestFiles()
      .setSrcFiles()
      .require();
    if (!skipInitialInteractive && argv.interactive) {
      runner.emit('interactive');
      return runner;
    }
    runner.run();
    return runner;
  },
};

module.exports = node;
<|endoftext|>


<commit_before>var _; //globals

/* This section uses a functional extension known as Underscore.js - http://documentcloud.github.com/underscore/
     "Underscore is a utility-belt library for JavaScript that provides a lot of the functional programming support
      that you would expect in Prototype.js (or Ruby), but without extending any of the built-in JavaScript objects.
      It's the tie to go along with jQuery's tux."
 */
describe("About Higher Order Functions", function () {

  it("should use filter to return array items that meet a criteria", function () {
    var numbers = [1,2,3];
    var odd = _(numbers).filter(function (x) { return x % 2 !== 0 });

    expect(odd).toEqual([ 1, 3 ]);
    expect(odd.length).toBe(2);
    expect(numbers.length).toBe(3);
  });

  it("should use 'map' to transform each element", function () {
    var numbers = [1, 2, 3];
    var numbersPlus1 = _(numbers).map(function(x) { return x + 1 });

    expect(numbersPlus1).toEqual([ 2, 3, 4 ]);
    expect(numbers).toEqual([ 1, 2, 3 ]);
  });

  it("should use 'reduce' to update the same result on each iteration", function () {
    var numbers = [1, 2, 3];
    var reduction = _(numbers).reduce(
      function(memo, x) {
        //note: memo is the result from last call, and x is the current number
        return memo + x;
      },
      /* initial */ 0
    );

    expect(reduction).toBe(6);
    expect(numbers).toEqual([ 1, 2, 3 ]);
  });

  it("should use 'forEach' for simple iteration", function () {
    var numbers = [1,2,3];
    var msg = "";
    var isEven = function (item) {
      msg += (item % 2) === 0;
    };

    _(numbers).forEach(isEven);

    expect(msg).toEqual('falsetruefalse');
    expect(numbers).toEqual([ 1, 2, 3 ]);
  });

  it("should use 'all' to test whether all items pass condition", function () {
    var onlyEven = [2,4,6];
    var mixedBag = [2,4,5,6];

    var isEven = function(x) { return x % 2 === 0 };

    expect(_(onlyEven).all(isEven)).toBe(true);
    expect(_(mixedBag).all(isEven)).toBe(false);
  });

  it("should use 'any' to test if any items passes condition" , function () {
    var onlyEven = [2,4,6];
    var mixedBag = [2,4,5,6];

    var isEven = function(x) { return x % 2 === 0 };

    expect(_(onlyEven).any(isEven)).toBe(FILL_ME_IN);
    expect(_(mixedBag).any(isEven)).toBe(FILL_ME_IN);
  });

  it("should use range to generate an array", function() {
    expect(_.range(3)).toEqual(FILL_ME_IN);
    expect(_.range(1, 4)).toEqual(FILL_ME_IN);
    expect(_.range(0, -4, -1)).toEqual(FILL_ME_IN);
  });

  it("should use flatten to make nested arrays easy to work with", function() {
    expect(_([ [1, 2], [3, 4] ]).flatten()).toEqual(FILL_ME_IN);
  });

  it("should use chain() ... .value() to use multiple higher order functions", function() {
    var result = _([ [0, 1], 2 ]).chain()
                     .flatten()
                     .map(function(x) { return x+1 } )
                     .reduce(function (sum, x) { return sum + x })
                     .value();

    expect(result).toEqual(FILL_ME_IN);
  });

});

<commit_msg>Fix Higher Order Functions tests 2.<commit_after>var _; //globals

/* This section uses a functional extension known as Underscore.js - http://documentcloud.github.com/underscore/
     "Underscore is a utility-belt library for JavaScript that provides a lot of the functional programming support
      that you would expect in Prototype.js (or Ruby), but without extending any of the built-in JavaScript objects.
      It's the tie to go along with jQuery's tux."
 */
describe("About Higher Order Functions", function () {

  it("should use filter to return array items that meet a criteria", function () {
    var numbers = [1,2,3];
    var odd = _(numbers).filter(function (x) { return x % 2 !== 0 });

    expect(odd).toEqual([ 1, 3 ]);
    expect(odd.length).toBe(2);
    expect(numbers.length).toBe(3);
  });

  it("should use 'map' to transform each element", function () {
    var numbers = [1, 2, 3];
    var numbersPlus1 = _(numbers).map(function(x) { return x + 1 });

    expect(numbersPlus1).toEqual([ 2, 3, 4 ]);
    expect(numbers).toEqual([ 1, 2, 3 ]);
  });

  it("should use 'reduce' to update the same result on each iteration", function () {
    var numbers = [1, 2, 3];
    var reduction = _(numbers).reduce(
      function(memo, x) {
        //note: memo is the result from last call, and x is the current number
        return memo + x;
      },
      /* initial */ 0
    );

    expect(reduction).toBe(6);
    expect(numbers).toEqual([ 1, 2, 3 ]);
  });

  it("should use 'forEach' for simple iteration", function () {
    var numbers = [1,2,3];
    var msg = "";
    var isEven = function (item) {
      msg += (item % 2) === 0;
    };

    _(numbers).forEach(isEven);

    expect(msg).toEqual('falsetruefalse');
    expect(numbers).toEqual([ 1, 2, 3 ]);
  });

  it("should use 'all' to test whether all items pass condition", function () {
    var onlyEven = [2,4,6];
    var mixedBag = [2,4,5,6];

    var isEven = function(x) { return x % 2 === 0 };

    expect(_(onlyEven).all(isEven)).toBe(true);
    expect(_(mixedBag).all(isEven)).toBe(false);
  });

  it("should use 'any' to test if any items passes condition" , function () {
    var onlyEven = [2,4,6];
    var mixedBag = [2,4,5,6];

    var isEven = function(x) { return x % 2 === 0 };

    expect(_(onlyEven).any(isEven)).toBe(true);
    expect(_(mixedBag).any(isEven)).toBe(true);
  });

  it("should use range to generate an array", function() {
    expect(_.range(3)).toEqual([ 0, 1, 2 ]);
    expect(_.range(1, 4)).toEqual([ 1, 2, 3 ]);
    expect(_.range(0, -4, -1)).toEqual([ 0, -1, -2, -3 ]);
  });

  it("should use flatten to make nested arrays easy to work with", function() {
    expect(_([ [1, 2], [3, 4] ]).flatten()).toEqual([ 1, 2, 3, 4 ]);
  });

  it("should use chain() ... .value() to use multiple higher order functions", function() {
    var result = _([ [0, 1], 2 ]).chain()
                     .flatten()
                     .map(function(x) { return x+1 } )
                     .reduce(function (sum, x) { return sum + x })
                     .value();

    expect(result).toEqual(6);
  });

});

<|endoftext|>