<commit_before>// @flow

import huffman from "n-ary-huffman";

import { bind, unreachable } from "../utils/main";
import type {
  ElementWithHint,
  ExtendedElementReport,
  FromBackground,
  FromPopup,
  FromRenderer,
  FromWorker,
  ToBackground,
  ToRenderer,
  ToWorker,
} from "../data/Messages";
import type {
  KeyboardAction,
  KeyboardMapping,
} from "../data/KeyboardShortcuts";

type PendingElements = {|
  elements: Array<ExtendedElementReport>,
  pendingFrames: number,
  startTime: number,
|};

type MessageInfo = {|
  tabId: number,
  frameId: number,
|};

type TabState = {|
  rendererFrameId: number,
  perf: Array<number>,
  hintsState: HintsState,
|};

type HintsState =
  | {|
      type: "Idle",
    |}
  | {|
      type: "Collecting",
      pendingElements: PendingElements,
    |}
  | {|
      type: "Hinting",
      elementsWithHints: Array<ElementWithHint>,
      startTime: number,
    |};

export default class BackgroundProgram {
  normalKeyboardShortcuts: Array<KeyboardMapping>;
  hintsKeyboardShortcuts: Array<KeyboardMapping>;
  hintChars: string;
  tabState: Map<number, TabState>;

  constructor({
    normalKeyboardShortcuts,
    hintsKeyboardShortcuts,
    hintChars,
  }: {|
    normalKeyboardShortcuts: Array<KeyboardMapping>,
    hintsKeyboardShortcuts: Array<KeyboardMapping>,
    hintChars: string,
  |}) {
    this.normalKeyboardShortcuts = normalKeyboardShortcuts;
    this.hintsKeyboardShortcuts = hintsKeyboardShortcuts;
    this.hintChars = hintChars;
    this.tabState = new Map();

    bind(this, ["onMessage", "onTabRemoved"]);
  }

  start() {
    browser.runtime.onMessage.addListener(this.onMessage);
    browser.tabs.onRemoved.addListener(this.onTabRemoved);
  }

  stop() {
    browser.runtime.onMessage.removeListener(this.onMessage);
  }

  async sendWorkerMessage(
    message: ToWorker,
    { tabId, frameId }: {| tabId?: number, frameId?: number |} = {}
  ): Promise<any> {
    return this.sendMessage({ type: "ToWorker", message }, { tabId, frameId });
  }

  async sendRendererMessage(message: ToRenderer): Promise<any> {
    return this.sendMessage({ type: "ToRenderer", message });
  }

  async sendMessage(
    message: FromBackground,
    { tabId: passedTabId, frameId }: {| tabId?: number, frameId?: number |} = {}
  ): Promise<any> {
    try {
      const tabId =
        passedTabId == null
          ? (await browser.tabs.query({ active: true }))[0].id
          : passedTabId;
      return frameId == null
        ? await browser.tabs.sendMessage(tabId, message)
        : await browser.tabs.sendMessage(tabId, message, { frameId });
    } catch (error) {
      console.error("BackgroundProgram#sendMessage failed", message, error);
      throw error;
    }
  }

  async onMessage(message: ToBackground, sender: MessageSender): Promise<any> {
    const info =
      sender.tab != null && sender.frameId != null
        ? { tabId: sender.tab.id, frameId: sender.frameId }
        : undefined;

    const tabStateRaw =
      info == null ? undefined : this.tabState.get(info.tabId);
    const tabState = tabStateRaw == null ? makeEmptyTabState() : tabStateRaw;

    if (info != null && tabStateRaw == null) {
      this.tabState.set(info.tabId, tabState);
    }

    switch (message.type) {
      case "FromWorker":
        if (info != null) {
          return this.onWorkerMessage(message.message, info, tabState);
        }
        console.error(
          "BackgroundProgram#onMessage: Missing info",
          info,
          message.type,
          message,
          sender
        );
        break;

      case "FromRenderer":
        if (info != null) {
          return this.onRendererMessage(message.message, info, tabState);
        }
        console.error(
          "BackgroundProgram#onMessage: Missing info",
          info,
          message.type,
          message,
          sender
        );
        break;

      case "FromPopup":
        return this.onPopupMessage(message.message);

      default:
        unreachable(message.type, message);
    }
    return undefined;
  }

  async onWorkerMessage(
    message: FromWorker,
    info: MessageInfo,
    tabState: TabState
  ): Promise<any> {
    switch (message.type) {
      case "WorkerScriptAdded":
        this.sendWorkerMessage(
          {
            type: "StateSync",
            keyboardShortcuts: this.normalKeyboardShortcuts,
            suppressByDefault: false,
            oneTimeWindowMessageToken: makeOneTimeWindowMessage(),
          },
          { tabId: info.tabId }
        );
        break;

      case "KeyboardShortcutMatched":
        this.onKeyboardShortcut(message.action, info, message.timestamp);
        break;

      case "ReportVisibleElements": {
        const { hintsState } = tabState;
        if (hintsState.type !== "Collecting") {
          return;
        }

        const elements = message.elements.map(
          ({ type, hintMeasurements, url }) => ({
            type,
            hintMeasurements,
            url,
            frameId: info.frameId,
          })
        );
        hintsState.pendingElements.elements.push(...elements);
        hintsState.pendingElements.pendingFrames += message.pendingFrames - 1;
        if (hintsState.pendingElements.pendingFrames <= 0) {
          const elementsWithHints = hintsState.pendingElements.elements.map(
            element => ({
              type: element.type,
              hintMeasurements: element.hintMeasurements,
              url: element.url,
              frameId: element.frameId,
              weight: element.hintMeasurements.area,
              hint: "",
            })
          );
          const tree = huffman.createTree(
            elementsWithHints,
            this.hintChars.length
          );
          tree.assignCodeWords(this.hintChars, (item, codeWord) => {
            item.hint = codeWord;
          });
          tabState.hintsState = {
            type: "Hinting",
            startTime: hintsState.pendingElements.startTime,
            elementsWithHints,
          };
          this.sendWorkerMessage(
            {
              type: "StateSync",
              keyboardShortcuts: this.hintsKeyboardShortcuts,
              suppressByDefault: true,
              oneTimeWindowMessageToken: makeOneTimeWindowMessage(),
            },
            { tabId: info.tabId }
          );
          this.sendRendererMessage({
            type: "Render",
            elements: elementsWithHints,
          });
          browser.browserAction.setBadgeText({
            text: String(hintsState.pendingElements.elements.length),
            tabId: info.tabId,
          });
        }
        break;
      }

      default:
        unreachable(message.type, message);
    }
  }

  async onRendererMessage(
    message: FromRenderer,
    info: MessageInfo,
    tabState: TabState
  ): Promise<any> {
    switch (message.type) {
      case "RendererScriptAdded":
        tabState.rendererFrameId = info.frameId;
        break;

      case "Rendered": {
        const { hintsState } = tabState;
        if (hintsState.type !== "Hinting") {
          return;
        }
        const { startTime } = hintsState;
        const duration = message.timestamp - startTime;
        tabState.perf = [duration, ...tabState.perf].slice(0, 10);
        break;
      }

      default:
        unreachable(message.type, message);
    }
  }

  async onPopupMessage(message: FromPopup): Promise<any> {
    switch (message.type) {
      case "GetPerf": {
        const tabId = (await browser.tabs.query({ active: true }))[0].id;
        const tabState = this.tabState.get(tabId);
        return tabState == null ? null : tabState.perf;
      }

      default:
        unreachable(message.type, message);
    }
    return undefined;
  }

  onKeyboardShortcut(
    action: KeyboardAction,
    info: MessageInfo,
    timestamp: number
  ) {
    switch (action.type) {
      case "EnterHintsMode": {
        const tabState = this.tabState.get(info.tabId);
        if (tabState == null || tabState.hintsState.type !== "Idle") {
          return;
        }
        this.sendWorkerMessage(
          { type: "StartFindElements" },
          {
            tabId: info.tabId,
            frameId: tabState.rendererFrameId,
          }
        );
        tabState.hintsState = {
          type: "Collecting",
          pendingElements: {
            elements: [],
            pendingFrames: 1,
            startTime: timestamp,
          },
        };
        break;
      }

      case "ExitHintsMode": {
        const tabState = this.tabState.get(info.tabId);
        if (tabState == null || tabState.hintsState.type !== "Hinting") {
          return;
        }
        tabState.hintsState = { type: "Idle" };
        this.sendWorkerMessage({
          type: "StateSync",
          keyboardShortcuts: this.normalKeyboardShortcuts,
          suppressByDefault: false,
          oneTimeWindowMessageToken: makeOneTimeWindowMessage(),
        });
        this.sendRendererMessage({
          type: "Unrender",
        });
        break;
      }

      case "PressHintChar":
        console.log("PressHintChar", action.char);
        break;

      default:
        unreachable(action.type, action);
    }
  }

  onTabRemoved(tabId: number) {
    this.tabState.delete(tabId);
  }
}

function makeOneTimeWindowMessage(): string {
  const array = new Uint32Array(3);
  window.crypto.getRandomValues(array);
  return array.join("");
}

// This is a function (not a constant), because of mutation.
function makeEmptyTabState(): TabState {
  return {
    // This is a bit ugly, I know. This ID will quickly be replaced with a real
    // one.
    rendererFrameId: 0,
    perf: [],
    hintsState: { type: "Idle" },
  };
}
<commit_msg>Clear button badge when exiting hints mode<commit_after>// @flow

import huffman from "n-ary-huffman";

import { bind, unreachable } from "../utils/main";
import type {
  ElementWithHint,
  ExtendedElementReport,
  FromBackground,
  FromPopup,
  FromRenderer,
  FromWorker,
  ToBackground,
  ToRenderer,
  ToWorker,
} from "../data/Messages";
import type {
  KeyboardAction,
  KeyboardMapping,
} from "../data/KeyboardShortcuts";

type PendingElements = {|
  elements: Array<ExtendedElementReport>,
  pendingFrames: number,
  startTime: number,
|};

type MessageInfo = {|
  tabId: number,
  frameId: number,
|};

type TabState = {|
  rendererFrameId: number,
  perf: Array<number>,
  hintsState: HintsState,
|};

type HintsState =
  | {|
      type: "Idle",
    |}
  | {|
      type: "Collecting",
      pendingElements: PendingElements,
    |}
  | {|
      type: "Hinting",
      elementsWithHints: Array<ElementWithHint>,
      startTime: number,
    |};

export default class BackgroundProgram {
  normalKeyboardShortcuts: Array<KeyboardMapping>;
  hintsKeyboardShortcuts: Array<KeyboardMapping>;
  hintChars: string;
  tabState: Map<number, TabState>;

  constructor({
    normalKeyboardShortcuts,
    hintsKeyboardShortcuts,
    hintChars,
  }: {|
    normalKeyboardShortcuts: Array<KeyboardMapping>,
    hintsKeyboardShortcuts: Array<KeyboardMapping>,
    hintChars: string,
  |}) {
    this.normalKeyboardShortcuts = normalKeyboardShortcuts;
    this.hintsKeyboardShortcuts = hintsKeyboardShortcuts;
    this.hintChars = hintChars;
    this.tabState = new Map();

    bind(this, ["onMessage", "onTabRemoved"]);
  }

  start() {
    browser.runtime.onMessage.addListener(this.onMessage);
    browser.tabs.onRemoved.addListener(this.onTabRemoved);
  }

  stop() {
    browser.runtime.onMessage.removeListener(this.onMessage);
  }

  async sendWorkerMessage(
    message: ToWorker,
    { tabId, frameId }: {| tabId?: number, frameId?: number |} = {}
  ): Promise<any> {
    return this.sendMessage({ type: "ToWorker", message }, { tabId, frameId });
  }

  async sendRendererMessage(message: ToRenderer): Promise<any> {
    return this.sendMessage({ type: "ToRenderer", message });
  }

  async sendMessage(
    message: FromBackground,
    { tabId: passedTabId, frameId }: {| tabId?: number, frameId?: number |} = {}
  ): Promise<any> {
    try {
      const tabId =
        passedTabId == null
          ? (await browser.tabs.query({ active: true }))[0].id
          : passedTabId;
      return frameId == null
        ? await browser.tabs.sendMessage(tabId, message)
        : await browser.tabs.sendMessage(tabId, message, { frameId });
    } catch (error) {
      console.error("BackgroundProgram#sendMessage failed", message, error);
      throw error;
    }
  }

  async onMessage(message: ToBackground, sender: MessageSender): Promise<any> {
    const info =
      sender.tab != null && sender.frameId != null
        ? { tabId: sender.tab.id, frameId: sender.frameId }
        : undefined;

    const tabStateRaw =
      info == null ? undefined : this.tabState.get(info.tabId);
    const tabState = tabStateRaw == null ? makeEmptyTabState() : tabStateRaw;

    if (info != null && tabStateRaw == null) {
      this.tabState.set(info.tabId, tabState);
    }

    switch (message.type) {
      case "FromWorker":
        if (info != null) {
          return this.onWorkerMessage(message.message, info, tabState);
        }
        console.error(
          "BackgroundProgram#onMessage: Missing info",
          info,
          message.type,
          message,
          sender
        );
        break;

      case "FromRenderer":
        if (info != null) {
          return this.onRendererMessage(message.message, info, tabState);
        }
        console.error(
          "BackgroundProgram#onMessage: Missing info",
          info,
          message.type,
          message,
          sender
        );
        break;

      case "FromPopup":
        return this.onPopupMessage(message.message);

      default:
        unreachable(message.type, message);
    }
    return undefined;
  }

  async onWorkerMessage(
    message: FromWorker,
    info: MessageInfo,
    tabState: TabState
  ): Promise<any> {
    switch (message.type) {
      case "WorkerScriptAdded":
        this.sendWorkerMessage(
          {
            type: "StateSync",
            keyboardShortcuts: this.normalKeyboardShortcuts,
            suppressByDefault: false,
            oneTimeWindowMessageToken: makeOneTimeWindowMessage(),
          },
          { tabId: info.tabId }
        );
        break;

      case "KeyboardShortcutMatched":
        this.onKeyboardShortcut(message.action, info, message.timestamp);
        break;

      case "ReportVisibleElements": {
        const { hintsState } = tabState;
        if (hintsState.type !== "Collecting") {
          return;
        }

        const elements = message.elements.map(
          ({ type, hintMeasurements, url }) => ({
            type,
            hintMeasurements,
            url,
            frameId: info.frameId,
          })
        );
        hintsState.pendingElements.elements.push(...elements);
        hintsState.pendingElements.pendingFrames += message.pendingFrames - 1;
        if (hintsState.pendingElements.pendingFrames <= 0) {
          const elementsWithHints = hintsState.pendingElements.elements.map(
            element => ({
              type: element.type,
              hintMeasurements: element.hintMeasurements,
              url: element.url,
              frameId: element.frameId,
              weight: element.hintMeasurements.area,
              hint: "",
            })
          );
          const tree = huffman.createTree(
            elementsWithHints,
            this.hintChars.length
          );
          tree.assignCodeWords(this.hintChars, (item, codeWord) => {
            item.hint = codeWord;
          });
          tabState.hintsState = {
            type: "Hinting",
            startTime: hintsState.pendingElements.startTime,
            elementsWithHints,
          };
          this.sendWorkerMessage(
            {
              type: "StateSync",
              keyboardShortcuts: this.hintsKeyboardShortcuts,
              suppressByDefault: true,
              oneTimeWindowMessageToken: makeOneTimeWindowMessage(),
            },
            { tabId: info.tabId }
          );
          this.sendRendererMessage({
            type: "Render",
            elements: elementsWithHints,
          });
          browser.browserAction.setBadgeText({
            text: String(hintsState.pendingElements.elements.length),
            tabId: info.tabId,
          });
        }
        break;
      }

      default:
        unreachable(message.type, message);
    }
  }

  async onRendererMessage(
    message: FromRenderer,
    info: MessageInfo,
    tabState: TabState
  ): Promise<any> {
    switch (message.type) {
      case "RendererScriptAdded":
        tabState.rendererFrameId = info.frameId;
        break;

      case "Rendered": {
        const { hintsState } = tabState;
        if (hintsState.type !== "Hinting") {
          return;
        }
        const { startTime } = hintsState;
        const duration = message.timestamp - startTime;
        tabState.perf = [duration, ...tabState.perf].slice(0, 10);
        break;
      }

      default:
        unreachable(message.type, message);
    }
  }

  async onPopupMessage(message: FromPopup): Promise<any> {
    switch (message.type) {
      case "GetPerf": {
        const tabId = (await browser.tabs.query({ active: true }))[0].id;
        const tabState = this.tabState.get(tabId);
        return tabState == null ? null : tabState.perf;
      }

      default:
        unreachable(message.type, message);
    }
    return undefined;
  }

  onKeyboardShortcut(
    action: KeyboardAction,
    info: MessageInfo,
    timestamp: number
  ) {
    switch (action.type) {
      case "EnterHintsMode": {
        const tabState = this.tabState.get(info.tabId);
        if (tabState == null || tabState.hintsState.type !== "Idle") {
          return;
        }
        this.sendWorkerMessage(
          { type: "StartFindElements" },
          {
            tabId: info.tabId,
            frameId: tabState.rendererFrameId,
          }
        );
        tabState.hintsState = {
          type: "Collecting",
          pendingElements: {
            elements: [],
            pendingFrames: 1,
            startTime: timestamp,
          },
        };
        break;
      }

      case "ExitHintsMode": {
        const tabState = this.tabState.get(info.tabId);
        if (tabState == null || tabState.hintsState.type !== "Hinting") {
          return;
        }
        tabState.hintsState = { type: "Idle" };
        this.sendWorkerMessage({
          type: "StateSync",
          keyboardShortcuts: this.normalKeyboardShortcuts,
          suppressByDefault: false,
          oneTimeWindowMessageToken: makeOneTimeWindowMessage(),
        });
        this.sendRendererMessage({
          type: "Unrender",
        });
        browser.browserAction.setBadgeText({
          text: "",
          tabId: info.tabId,
        });
        break;
      }

      case "PressHintChar":
        console.log("PressHintChar", action.char);
        break;

      default:
        unreachable(action.type, action);
    }
  }

  onTabRemoved(tabId: number) {
    this.tabState.delete(tabId);
  }
}

function makeOneTimeWindowMessage(): string {
  const array = new Uint32Array(3);
  window.crypto.getRandomValues(array);
  return array.join("");
}

// This is a function (not a constant), because of mutation.
function makeEmptyTabState(): TabState {
  return {
    // This is a bit ugly, I know. This ID will quickly be replaced with a real
    // one.
    rendererFrameId: 0,
    perf: [],
    hintsState: { type: "Idle" },
  };
}
<|endoftext|>


<commit_before>export default ({type, name, placeholder, value = '', error, onInput, required, className, children, icon}) => (

            <input
                type={ type }
                name={ name }
                key={ name }
                placeholder={ placeholder }
                value={ value }
                onInput={ (e) => onInput(e, name) }
                required={ required }
                className={[
                    (value ? "gm-input-filled" : ""),
                    (error ? "gm-error" : "")
                ].join(' ')}
            />
            { icon }
            { children }

);
<commit_msg>Added support for disabled form elements<commit_after>export default ({type, name, placeholder, value = '', error, onInput, required, disabled, className, children, icon}) => (

            <input
                type={ type }
                name={ name }
                key={ name }
                placeholder={ placeholder }
                value={ value }
                onInput={ (e) => onInput(e, name) }
                required={ required }
                disabled={ disabled }
                className={[
                    (value ? "gm-input-filled" : ""),
                    (error ? "gm-error" : "")
                ].join(' ')}
            />
            { icon }
            { children }

);
<|endoftext|>


<commit_before>mf.include("a_star.js");
mf.include("arrays.js");

var navigator = {};
(function() {
    var monitor_interval = 40;
    var water_threshold = 20;
    /**
     * Finds a path to the specified location and goes there.
     * @param {Point} end Where you want to go.
     * @param {Object} params Optional parameters:
     *      is_end_func(node) - passed on to the a_star library. node.point is a mf.Point.
     *      end_radius - used for default is_end_func. effectively defaults to 0.
     *      timeout_milliseconds - passed on to the a_star library.
     *      path_part_found_func(path) - called when a partial path is found. path is an array of nodes.
     *      path_found_func(path) - called when a complete path is found. path is an array of nodes.
     *      cant_find_func() - called when a path can't be found.
     *      arrived_func() - called when the destination is reached.
     */
    navigator.navigateTo = function(end, params) {
        var start = mf.self().position.floored();
        end = end.floored();
        if (params.timeout_milliseconds === undefined) {
            params.timeout_milliseconds = 10 * 1000;
        }
        if (start.distanceTo(end) > 150) {
            // Too far to calculate reliably. Go in the right general direction for now.
            var old_is_end_func = params.is_end_func;
            var old_path_found_func = params.path_found_func;
            var old_arrived_func = params.arrived_func;
            params.is_end_func = function(node) {
                // let's just go 100 meters (not end in water)
                return node.water === 0 && start.distanceTo(node.point) >= 100;
            };
            params.path_found_func = params.path_part_found_func;
            params.arrived_func = function() {
                // try to go the next bit
                params.is_end_func = old_is_end_func;
                params.path_found_func = old_path_found_func;
                params.arrived_func = old_arrived_func;
                navigator.navigateTo(end, params);
            };
        }
        if (params.is_end_func === undefined) {
            if (params.end_radius !== undefined) {
                params.is_end_func = function(node) {
                    return node.point.distanceTo(end) <= params.end_radius;
                };
            } else {
                params.is_end_func = function(node) {
                    return node.point.equals(end);
                };
            }
        }
        var path = a_star.findPath({
            "start": new Node(start, 0),
            "is_end_func": params.is_end_func,
            "neighbor_func": getNeighbors,
            "distance_func": function(node_a, node_b) { return node_a.point.distanceTo(node_b.point); },
            "heuristic_func": function(node) { return node.point.distanceTo(end) + 5 * node.water; },
            "timeout_milliseconds": params.timeout_milliseconds,
        });
        if (path === undefined) {
            if (params.cant_find_func !== undefined) {
                params.cant_find_func();
            }
            return;
        }
        if (params.path_found_func !== undefined) {
            params.path_found_func(path);
        }

        // start
        navigator.stop();
        // go to the centers of blocks
        current_completed_callback = params.arrived_func;
        current_course = path.mapped(function(node) { return node.point.offset(0.5, 0.5, 0); });
        previous_position = mf.self().position;
        current_callback_id = mf.setInterval(monitor_movement, monitor_interval);
    };
    navigator.stop = function() {
        if (current_callback_id === undefined) {
            return; // already stopped
        }
        mf.clearInterval(current_callback_id);
        mf.clearControlStates();
    }

    function Node(point, water) {
        this.point = point;
        this.water = water;
    }
    Node.prototype.toString = function() {
        // must declare a toString so that a_star works.
        return this.point.toString() + ":" + this.water;
    };
    var current_callback_id;
    var current_completed_callback;
    var current_course = [];
    var previous_position;
    function monitor_movement() {
        var next_point = current_course[0];
        var current_position = mf.self().position;
        if (current_position.distanceTo(next_point) <= 0.2) {
            // arrived at next point
            current_course.shift();
            if (current_course.length === 0) {
                // done
                navigator.stop();
                if (current_completed_callback !== undefined) {
                    current_completed_callback();
                }
                return;
            }
            // not done yet
            next_point = current_course[0];
        }
        var delta = next_point.minus(current_position);
        var gotta_jump;
        var horizontal_delta = Math.abs(delta.x + delta.y);
        if (delta.z > 0.1) {
            // gotta jump up when we're close enough
            gotta_jump = horizontal_delta < 1.75;
        } else if (delta.z > -0.1) {
            // possibly jump over a hole
            gotta_jump = 1.5 < horizontal_delta && horizontal_delta < 2.5;
        } else {
            gotta_jump = 2.4 < horizontal_delta && horizontal_delta < 2.7;
        }
        mf.setControlState(mf.Control.Jump, gotta_jump);

        // run toward next point
        var look_at_point = new mf.Point(next_point.x, next_point.y, current_position.z);
        mf.lookAt(look_at_point);
        mf.setControlState(mf.Control.Forward, true);

        previous_position = current_position;
    }
    var cardinal_direction_vectors = [
        new mf.Point( 0,  1, 0), // north
        new mf.Point( 0, -1, 0), // south
        new mf.Point( 1,  0, 0), // east
        new mf.Point(-1,  0, 0), // west
    ];
    function getNeighbors(node) {
        // for each cardinal direction:
        // "." is head. "+" is feet and current location.
        // "#" is initial floor which is always solid. "a"-"u" are blocks to check
        //
        //   --0123-- horizontal_offset
        //  |
        // +2  aho
        // +1  .bip
        //  0  +cjq
        // -1  #dkr
        // -2   els
        // -3   fmt
        // -4   gn
        //  |
        //  dz
        //
        var point = node.point;
        var is_safe_a = mf.isSafe(mf.blockAt(point.offset(0, 0, 2)).type);
        var result = [];
        for (var i = 0; i < cardinal_direction_vectors.length; i++) {
            var direction_vector = cardinal_direction_vectors[i];
            function pointAt(horizontal_offset, dz) {
                return point.offset(direction_vector.x * horizontal_offset, direction_vector.y * horizontal_offset, dz);
            }
            function properties(point) {
                var type = mf.blockAt(point).type;
                return {
                    "safe": mf.isSafe(type),
                    "physical": mf.isPhysical(type),
                };
            }
            var point_b = pointAt(1, 1);
            var block_b = properties(point_b);
            if (!block_b.safe) {
                // we can do nothing in this direction
                continue;
            }
            var point_c = pointAt(1, 0);
            var block_c = properties(point_c);
            if (!block_c.safe) {
                // can't walk forward
                if (!block_c.physical) {
                    // too dangerous
                    continue;
                }
                if (!is_safe_a) {
                    // can't jump
                    continue;
                }
                var block_h = properties(pointAt(1, 2));
                if (!block_h.safe) {
                    // no head room to stand on c
                    continue;
                }
                // can jump up onto c
                result.push(point_b);
                continue;
            }
            // c is open
            var point_d = pointAt(1, -1);
            var block_d = properties(point_d);
            if (block_d.physical) {
                // can walk onto d. this is the case of flat ground.
                result.push(point_c);
                continue;
            }
            if (block_d.safe) {
                // safe to drop through d
                var point_e = pointAt(1, -2);
                var block_e = properties(point_e);
                if (block_e.physical) {
                    // can drop onto e
                    result.push(point_d);
                } else if (block_e.safe) {
                    // can drop through e
                    var point_f = pointAt(1, -3);
                    var block_f = properties(point_f);
                    if (block_f.physical) {
                        // can drop onto f
                        result.push(point_e);
                    } else if (block_f.safe) {
                        // can drop through f
                        var block_g = properties(pointAt(1, -4));
                        if (block_g.physical) {
                            result.push(point_f);
                        }
                    }
                }
            }
            // might be able to jump over the d hole.
            block_h = properties(pointAt(1, 2));
            var block_o = properties(pointAt(2, 2));
            var can_jump_forward = is_safe_a && block_h.safe && block_o.safe;

            var point_i = pointAt(2, 1);
            var block_i = properties(point_i);
            var point_j = pointAt(2, 0);
            var block_j = properties(point_j);
            if (can_jump_forward && block_i.safe && block_j.physical) {
                // can jump over and up onto j
                result.push(point_i);
            }
            var point_k = pointAt(2, -1);
            var block_k = properties(point_k);
            var can_jump_past_j = can_jump_forward;
            if (can_jump_forward && block_i.safe && block_j.safe && block_k.physical) {
                // can jump over onto k
                result.push(point_j);
                can_jump_past_j = false;
            }

            // might be able to walk and drop forward
            var point_l = pointAt(2, -2);
            var block_l = properties(point_l);
            var can_land_on_l = false;
            if (block_i.safe && block_j.safe && block_k.safe && block_l.physical) {
                // can walk and drop onto l
                can_land_on_l = true;
                result.push(point_k);
            }

            if (block_e === undefined) {
                block_e = properties(pointAt(1, -2));
            }
            var can_land_on_m = false;
            if (block_e.safe) {
                // can drop through e
                var point_m = pointAt(2, -3);
                var block_m = properties(point_m);
                if (block_j.safe && block_k.safe && block_l.safe && block_m.physical) {
                    // can walk and drop onto m
                    can_land_on_m = true;
                    result.push(point_l);
                }
                var block_n = properties(pointAt(2, -4));
                if (block_k.safe && block_l.safe && block_m.safe && block_n.physical) {
                    // can walk and drop onto n
                    result.push(point_m);
                }
            }
            if (!can_jump_past_j) {
                continue;
            }
            // 3rd column
            var block_p = properties(pointAt(3, 1));
            var point_q = pointAt(3, 0);
            var block_q = properties(point_q);
            var point_r = pointAt(3, -1);
            var block_r = properties(point_r);
            if (block_p.safe && block_q.safe && block_r.physical) {
                // can jump way over onto r
                result.push(point_q);
                continue;
            }
            var point_s = pointAt(3, -2);
            var block_s = properties(point_s);
            if (!can_land_on_l && block_q.safe && block_r.safe && block_s.physical) {
                // can jump way over and down onto s
                result.push(point_r);
                continue;
            }
            var block_t = properties(pointAt(3, -3));
            if (!can_land_on_m && block_r.safe && block_s.safe && block_t.physical) {
                // can jump way over and down onto t
                result.push(point_s);
                continue;
            }
        }
        return result.mapped(function(point) {
            var face_block = mf.blockAt(point.offset(0, 0, 1));
            var water = 0;
            if (face_block.type === mf.ItemType.Water || face_block.type === mf.ItemType.StationaryWater) {
                water = node.water + 1;
            }
            return new Node(point, water);
        }).filtered(function(node) {
            return node.water <= water_threshold;
        });
    }
})();

<commit_msg>fixing the 'trying to jump through a wall' navigator bug<commit_after>mf.include("a_star.js");
mf.include("arrays.js");

var navigator = {};
(function() {
    var monitor_interval = 40;
    var water_threshold = 20;
    /**
     * Finds a path to the specified location and goes there.
     * @param {Point} end Where you want to go.
     * @param {Object} params Optional parameters:
     *      is_end_func(node) - passed on to the a_star library. node.point is a mf.Point.
     *      end_radius - used for default is_end_func. effectively defaults to 0.
     *      timeout_milliseconds - passed on to the a_star library.
     *      path_part_found_func(path) - called when a partial path is found. path is an array of nodes.
     *      path_found_func(path) - called when a complete path is found. path is an array of nodes.
     *      cant_find_func() - called when a path can't be found.
     *      arrived_func() - called when the destination is reached.
     */
    navigator.navigateTo = function(end, params) {
        var start = mf.self().position.floored();
        end = end.floored();
        if (params.timeout_milliseconds === undefined) {
            params.timeout_milliseconds = 10 * 1000;
        }
        if (start.distanceTo(end) > 150) {
            // Too far to calculate reliably. Go in the right general direction for now.
            var old_is_end_func = params.is_end_func;
            var old_path_found_func = params.path_found_func;
            var old_arrived_func = params.arrived_func;
            params.is_end_func = function(node) {
                // let's just go 100 meters (not end in water)
                return node.water === 0 && start.distanceTo(node.point) >= 100;
            };
            params.path_found_func = params.path_part_found_func;
            params.arrived_func = function() {
                // try to go the next bit
                params.is_end_func = old_is_end_func;
                params.path_found_func = old_path_found_func;
                params.arrived_func = old_arrived_func;
                navigator.navigateTo(end, params);
            };
        }
        if (params.is_end_func === undefined) {
            if (params.end_radius !== undefined) {
                params.is_end_func = function(node) {
                    return node.point.distanceTo(end) <= params.end_radius;
                };
            } else {
                params.is_end_func = function(node) {
                    return node.point.equals(end);
                };
            }
        }
        var path = a_star.findPath({
            "start": new Node(start, 0),
            "is_end_func": params.is_end_func,
            "neighbor_func": getNeighbors,
            "distance_func": function(node_a, node_b) { return node_a.point.distanceTo(node_b.point); },
            "heuristic_func": function(node) { return node.point.distanceTo(end) + 5 * node.water; },
            "timeout_milliseconds": params.timeout_milliseconds,
        });
        if (path === undefined) {
            if (params.cant_find_func !== undefined) {
                params.cant_find_func();
            }
            return;
        }
        if (params.path_found_func !== undefined) {
            params.path_found_func(path);
        }

        // start
        navigator.stop();
        // go to the centers of blocks
        current_completed_callback = params.arrived_func;
        current_course = path.mapped(function(node) { return node.point.offset(0.5, 0.5, 0); });
        previous_position = mf.self().position;
        current_callback_id = mf.setInterval(monitor_movement, monitor_interval);
    };
    navigator.stop = function() {
        if (current_callback_id === undefined) {
            return; // already stopped
        }
        mf.clearInterval(current_callback_id);
        mf.clearControlStates();
    }

    function Node(point, water) {
        this.point = point;
        this.water = water;
    }
    Node.prototype.toString = function() {
        // must declare a toString so that a_star works.
        return this.point.toString() + ":" + this.water;
    };
    var current_callback_id;
    var current_completed_callback;
    var current_course = [];
    var previous_position;
    function monitor_movement() {
        var next_point = current_course[0];
        var current_position = mf.self().position;
        if (current_position.distanceTo(next_point) <= 0.2) {
            // arrived at next point
            current_course.shift();
            if (current_course.length === 0) {
                // done
                navigator.stop();
                if (current_completed_callback !== undefined) {
                    current_completed_callback();
                }
                return;
            }
            // not done yet
            next_point = current_course[0];
        }
        var delta = next_point.minus(current_position);
        var gotta_jump;
        var horizontal_delta = Math.abs(delta.x + delta.y);
        if (delta.z > 0.1) {
            // gotta jump up when we're close enough
            gotta_jump = horizontal_delta < 1.75;
        } else if (delta.z > -0.1) {
            // possibly jump over a hole
            gotta_jump = 1.5 < horizontal_delta && horizontal_delta < 2.5;
        } else {
            gotta_jump = 2.4 < horizontal_delta && horizontal_delta < 2.7;
        }
        mf.setControlState(mf.Control.Jump, gotta_jump);

        // run toward next point
        var look_at_point = new mf.Point(next_point.x, next_point.y, current_position.z);
        mf.lookAt(look_at_point);
        mf.setControlState(mf.Control.Forward, true);

        previous_position = current_position;
    }
    var cardinal_direction_vectors = [
        new mf.Point( 0,  1, 0), // north
        new mf.Point( 0, -1, 0), // south
        new mf.Point( 1,  0, 0), // east
        new mf.Point(-1,  0, 0), // west
    ];
    function getNeighbors(node) {
        // for each cardinal direction:
        // "." is head. "+" is feet and current location.
        // "#" is initial floor which is always solid. "a"-"u" are blocks to check
        //
        //   --0123-- horizontal_offset
        //  |
        // +2  aho
        // +1  .bip
        //  0  +cjq
        // -1  #dkr
        // -2   els
        // -3   fmt
        // -4   gn
        //  |
        //  dz
        //
        var point = node.point;
        var is_safe_a = mf.isSafe(mf.blockAt(point.offset(0, 0, 2)).type);
        var result = [];
        for (var i = 0; i < cardinal_direction_vectors.length; i++) {
            var direction_vector = cardinal_direction_vectors[i];
            function pointAt(horizontal_offset, dz) {
                return point.offset(direction_vector.x * horizontal_offset, direction_vector.y * horizontal_offset, dz);
            }
            function properties(point) {
                var type = mf.blockAt(point).type;
                return {
                    "safe": mf.isSafe(type),
                    "physical": mf.isPhysical(type),
                };
            }
            var point_b = pointAt(1, 1);
            var block_b = properties(point_b);
            if (!block_b.safe) {
                // we can do nothing in this direction
                continue;
            }
            var point_c = pointAt(1, 0);
            var block_c = properties(point_c);
            if (!block_c.safe) {
                // can't walk forward
                if (!block_c.physical) {
                    // too dangerous
                    continue;
                }
                if (!is_safe_a) {
                    // can't jump
                    continue;
                }
                var block_h = properties(pointAt(1, 2));
                if (!block_h.safe) {
                    // no head room to stand on c
                    continue;
                }
                // can jump up onto c
                result.push(point_b);
                continue;
            }
            // c is open
            var point_d = pointAt(1, -1);
            var block_d = properties(point_d);
            if (block_d.physical) {
                // can walk onto d. this is the case of flat ground.
                result.push(point_c);
                continue;
            }
            if (block_d.safe) {
                // safe to drop through d
                var point_e = pointAt(1, -2);
                var block_e = properties(point_e);
                if (block_e.physical) {
                    // can drop onto e
                    result.push(point_d);
                } else if (block_e.safe) {
                    // can drop through e
                    var point_f = pointAt(1, -3);
                    var block_f = properties(point_f);
                    if (block_f.physical) {
                        // can drop onto f
                        result.push(point_e);
                    } else if (block_f.safe) {
                        // can drop through f
                        var block_g = properties(pointAt(1, -4));
                        if (block_g.physical) {
                            result.push(point_f);
                        }
                    }
                }
            }
            // might be able to jump over the d hole.
            block_h = properties(pointAt(1, 2));
            var block_o = properties(pointAt(2, 2));
            var can_jump_forward = is_safe_a && block_h.safe && block_o.safe;

            var point_i = pointAt(2, 1);
            var block_i = properties(point_i);
            var point_j = pointAt(2, 0);
            var block_j = properties(point_j);
            if (can_jump_forward && block_i.safe && block_j.physical) {
                // can jump over and up onto j
                result.push(point_i);
            }
            var point_k = pointAt(2, -1);
            var block_k = properties(point_k);
            var can_jump_past_j = can_jump_forward && block_j.safe && block_i.safe;
            if (can_jump_past_j && block_k.physical) {
                // can jump over onto k
                result.push(point_j);
                can_jump_past_j = false;
            }

            // might be able to walk and drop forward
            var point_l = pointAt(2, -2);
            var block_l = properties(point_l);
            var can_land_on_l = false;
            if (block_i.safe && block_j.safe && block_k.safe && block_l.physical) {
                // can walk and drop onto l
                can_land_on_l = true;
                result.push(point_k);
            }

            if (block_e === undefined) {
                block_e = properties(pointAt(1, -2));
            }
            var can_land_on_m = false;
            if (block_e.safe) {
                // can drop through e
                var point_m = pointAt(2, -3);
                var block_m = properties(point_m);
                if (block_j.safe && block_k.safe && block_l.safe && block_m.physical) {
                    // can walk and drop onto m
                    can_land_on_m = true;
                    result.push(point_l);
                }
                var block_n = properties(pointAt(2, -4));
                if (block_k.safe && block_l.safe && block_m.safe && block_n.physical) {
                    // can walk and drop onto n
                    result.push(point_m);
                }
            }
            if (!can_jump_past_j) {
                continue;
            }
            // 3rd column
            var block_p = properties(pointAt(3, 1));
            var point_q = pointAt(3, 0);
            var block_q = properties(point_q);
            var point_r = pointAt(3, -1);
            var block_r = properties(point_r);
            if (block_p.safe && block_q.safe && block_r.physical) {
                // can jump way over onto r
                result.push(point_q);
                continue;
            }
            var point_s = pointAt(3, -2);
            var block_s = properties(point_s);
            if (!can_land_on_l && block_q.safe && block_r.safe && block_s.physical) {
                // can jump way over and down onto s
                result.push(point_r);
                continue;
            }
            var block_t = properties(pointAt(3, -3));
            if (!can_land_on_m && block_r.safe && block_s.safe && block_t.physical) {
                // can jump way over and down onto t
                result.push(point_s);
                continue;
            }
        }
        return result.mapped(function(point) {
            var face_block = mf.blockAt(point.offset(0, 0, 1));
            var water = 0;
            if (face_block.type === mf.ItemType.Water || face_block.type === mf.ItemType.StationaryWater) {
                water = node.water + 1;
            }
            return new Node(point, water);
        }).filtered(function(node) {
            return node.water <= water_threshold;
        });
    }
})();

<|endoftext|>


<commit_before>import ReactiveElement from "../core/ReactiveElement.js";
import * as template from "../core/template.js";
import EffectMixin from "./EffectMixin.js";
import * as internal from "./internal.js";
import LanguageDirectionMixin from "./LanguageDirectionMixin.js";
import ResizeMixin from "./ResizeMixin.js";
import SingleSelectionMixin from "./SingleSelectionMixin.js";
import SlotItemsMixin from "./SlotItemsMixin.js";
import TapSelectionMixin from "./TapSelectionMixin.js";

const Base = EffectMixin(
  LanguageDirectionMixin(
    ResizeMixin(
      SingleSelectionMixin(SlotItemsMixin(TapSelectionMixin(ReactiveElement)))
    )
  )
);

/**
 * Horizontal strip of items with the selected item centered
 *
 * This keeps the selected item centered unless that item is at either end of
 * the list.
 *
 * @inherits ReactiveElement
 * @mixes EffectMixin
 * @mixes LanguageDirectionMixin
 * @mixes ResizeMixin
 * @mixes SingleSelectionMixin
 * @mixes SlotItemsMixin
 * @mixes TapSelectionMixin
 */
class CenteredStrip extends Base {
  get [internal.defaultState]() {
    return Object.assign(super[internal.defaultState], {
      orientation: "horizontal",
      selectionRequired: true,
    });
  }

  get orientation() {
    return this[internal.state].orientation;
  }
  set orientation(orientation) {
    this[internal.setState]({ orientation });
  }

  [internal.render](/** @type {ChangedFlags} */ changed) {
    super[internal.render](changed);
    if (
      changed.clientWidth ||
      changed.enableEffects ||
      changed.rightToLeft ||
      changed.selectedIndex ||
      changed.swipeFraction
    ) {
      const { orientation, rightToLeft, selectedIndex } = this[internal.state];
      const sign = rightToLeft ? 1 : -1;
      const swiping = this[internal.state].swipeFraction != null;
      const swipeFraction = this[internal.state].swipeFraction || 0;
      const selectionFraction = selectedIndex + sign * swipeFraction;

      const vertical = orientation === "vertical";
      const leadingEdge = vertical ? "offsetTop" : "offsetLeft";
      const dimension = vertical ? "offsetHeight" : "offsetWidth";

      // @ts-ignore
      const stripContainerDimension = this[internal.ids].stripContainer[
        dimension
      ];
      // @ts-ignore
      const stripDimension = this[internal.ids].strip[dimension];

      // It seems this method can be invoked before the strip any height/width.
      // We only render if the height/width is positive.
      if (stripDimension > 0) {
        let translation = 0; // The amount by which we'll shift content horizontally
        let justifyContent = "";
        if (stripDimension <= stripContainerDimension) {
          // Container can show all items. Center all items.
          justifyContent = "center";
        } else {
          // Items are wider than container can show.
          // Center the selected item.
          // During swipes, center a pro-rated point between the midpoints
          // of the items on either side of the fractional selection.

          const itemBeforeIndex = Math.floor(selectionFraction);
          const itemBefore = this.items && this.items[itemBeforeIndex];
          const itemBeforeCenter =
            itemBefore instanceof HTMLElement
              ? itemBefore[leadingEdge] + itemBefore[dimension] / 2
              : 0;
          const itemAfterIndex = itemBeforeIndex + 1;
          const itemAfter = this.items && this.items[itemAfterIndex];
          const itemAfterCenter =
            itemAfter instanceof HTMLElement
              ? itemAfter[leadingEdge] + itemAfter[dimension] / 2
              : 0;

          let center = 0;
          if (itemBefore && !itemAfter) {
            center = itemBeforeCenter;
          } else if (!itemBefore && itemAfter) {
            center = itemAfterCenter;
          } else if (itemBefore && itemAfter) {
            const offsetFraction = selectionFraction - itemBeforeIndex;
            // TODO: sign
            center =
              itemBeforeCenter +
              offsetFraction * (itemAfterCenter - itemBeforeCenter);
          }
          if (!vertical && rightToLeft) {
            center = stripDimension - center;
          }

          // Try to center the selected item.
          translation = center - stripContainerDimension / 2;

          // Constrain x to avoid showing space on either end.
          translation = Math.max(translation, 0);
          translation = Math.min(
            translation,
            stripDimension - stripContainerDimension
          );

          translation *= sign;
        }

        const axis = vertical ? "Y" : "X";
        const transform = `translate${axis}(${translation}px)`;
        const showTransition = this[internal.state].enableEffects && !swiping;
        Object.assign(this[internal.ids].strip.style, {
          transform,
          transition: showTransition ? "transform 0.25s" : "none",
        });

        this[internal.ids].stripContainer.style.justifyContent = justifyContent;
      }
    }
    if (changed.items || changed.selectedIndex) {
      // Apply `selected` style to the selected item only.
      const { selectedIndex, items } = this[internal.state];
      if (items) {
        items.forEach((item, index) => {
          item.toggleAttribute("selected", index === selectedIndex);
        });
      }
    }
    if (changed.orientation) {
      const flexDirection =
        this[internal.state].orientation === "horizontal" ? "" : "column";
      this[internal.ids].stripContainer.style.flexDirection = flexDirection;
      this[internal.ids].strip.style.flexDirection = flexDirection;
    }
  }

  get swipeFraction() {
    return this[internal.state].swipeFraction;
  }
  set swipeFraction(swipeFraction) {
    this[internal.setState]({ swipeFraction });
  }

  get [internal.template]() {
    return template.html`

          <slot></slot>

    `;
  }
}

export default CenteredStrip;
<commit_msg>Default to centering items to workaround a bug in Chrome, in which the stripContainer never gets a non-zero width during rendering. Safari/Firefox do not show this bug. Centering seems like a better default in any event.<commit_after>import ReactiveElement from "../core/ReactiveElement.js";
import * as template from "../core/template.js";
import EffectMixin from "./EffectMixin.js";
import * as internal from "./internal.js";
import LanguageDirectionMixin from "./LanguageDirectionMixin.js";
import ResizeMixin from "./ResizeMixin.js";
import SingleSelectionMixin from "./SingleSelectionMixin.js";
import SlotItemsMixin from "./SlotItemsMixin.js";
import TapSelectionMixin from "./TapSelectionMixin.js";

const Base = EffectMixin(
  LanguageDirectionMixin(
    ResizeMixin(
      SingleSelectionMixin(SlotItemsMixin(TapSelectionMixin(ReactiveElement)))
    )
  )
);

/**
 * Horizontal strip of items with the selected item centered
 *
 * This keeps the selected item centered unless that item is at either end of
 * the list.
 *
 * @inherits ReactiveElement
 * @mixes EffectMixin
 * @mixes LanguageDirectionMixin
 * @mixes ResizeMixin
 * @mixes SingleSelectionMixin
 * @mixes SlotItemsMixin
 * @mixes TapSelectionMixin
 */
class CenteredStrip extends Base {
  get [internal.defaultState]() {
    return Object.assign(super[internal.defaultState], {
      orientation: "horizontal",
      selectionRequired: true,
    });
  }

  get orientation() {
    return this[internal.state].orientation;
  }
  set orientation(orientation) {
    this[internal.setState]({ orientation });
  }

  [internal.render](/** @type {ChangedFlags} */ changed) {
    super[internal.render](changed);
    if (
      changed.clientWidth ||
      changed.enableEffects ||
      changed.rightToLeft ||
      changed.selectedIndex ||
      changed.swipeFraction
    ) {
      const { orientation, rightToLeft, selectedIndex } = this[internal.state];
      const sign = rightToLeft ? 1 : -1;
      const swiping = this[internal.state].swipeFraction != null;
      const swipeFraction = this[internal.state].swipeFraction || 0;
      const selectionFraction = selectedIndex + sign * swipeFraction;

      const vertical = orientation === "vertical";
      const leadingEdge = vertical ? "offsetTop" : "offsetLeft";
      const dimension = vertical ? "offsetHeight" : "offsetWidth";

      // @ts-ignore
      const stripContainerDimension = this[internal.ids].stripContainer[
        dimension
      ];
      // @ts-ignore
      const stripDimension = this[internal.ids].strip[dimension];

      // It seems this method can be invoked before the strip any height/width.
      // We only render if the height/width is positive.
      if (stripDimension > 0) {
        let translation = 0; // The amount by which we'll shift content horizontally
        let justifyContent = "";
        if (stripDimension <= stripContainerDimension) {
          // Container can show all items. Center all items.
          justifyContent = "center";
        } else {
          // Items are wider than container can show.
          // Center the selected item.
          // During swipes, center a pro-rated point between the midpoints
          // of the items on either side of the fractional selection.

          const itemBeforeIndex = Math.floor(selectionFraction);
          const itemBefore = this.items && this.items[itemBeforeIndex];
          const itemBeforeCenter =
            itemBefore instanceof HTMLElement
              ? itemBefore[leadingEdge] + itemBefore[dimension] / 2
              : 0;
          const itemAfterIndex = itemBeforeIndex + 1;
          const itemAfter = this.items && this.items[itemAfterIndex];
          const itemAfterCenter =
            itemAfter instanceof HTMLElement
              ? itemAfter[leadingEdge] + itemAfter[dimension] / 2
              : 0;

          let center = 0;
          if (itemBefore && !itemAfter) {
            center = itemBeforeCenter;
          } else if (!itemBefore && itemAfter) {
            center = itemAfterCenter;
          } else if (itemBefore && itemAfter) {
            const offsetFraction = selectionFraction - itemBeforeIndex;
            // TODO: sign
            center =
              itemBeforeCenter +
              offsetFraction * (itemAfterCenter - itemBeforeCenter);
          }
          if (!vertical && rightToLeft) {
            center = stripDimension - center;
          }

          // Try to center the selected item.
          translation = center - stripContainerDimension / 2;

          // Constrain x to avoid showing space on either end.
          translation = Math.max(translation, 0);
          translation = Math.min(
            translation,
            stripDimension - stripContainerDimension
          );

          translation *= sign;
        }

        const axis = vertical ? "Y" : "X";
        const transform = `translate${axis}(${translation}px)`;
        const showTransition = this[internal.state].enableEffects && !swiping;
        Object.assign(this[internal.ids].strip.style, {
          transform,
          transition: showTransition ? "transform 0.25s" : "none",
        });

        this[internal.ids].stripContainer.style.justifyContent = justifyContent;
      }
    }
    if (changed.items || changed.selectedIndex) {
      // Apply `selected` style to the selected item only.
      const { selectedIndex, items } = this[internal.state];
      if (items) {
        items.forEach((item, index) => {
          item.toggleAttribute("selected", index === selectedIndex);
        });
      }
    }
    if (changed.orientation) {
      const flexDirection =
        this[internal.state].orientation === "horizontal" ? "" : "column";
      this[internal.ids].stripContainer.style.flexDirection = flexDirection;
      this[internal.ids].strip.style.flexDirection = flexDirection;
    }
  }

  get swipeFraction() {
    return this[internal.state].swipeFraction;
  }
  set swipeFraction(swipeFraction) {
    this[internal.setState]({ swipeFraction });
  }

  get [internal.template]() {
    return template.html`

          <slot></slot>

    `;
  }
}

export default CenteredStrip;
<|endoftext|>


<commit_before>import React from 'react';
import { Link, withRouter } from 'react-router-dom';
import { HashLink } from 'react-router-hash-link';
import styled from '@emotion/styled';

import Box from './Box';

import config from '../config';

import domestosLogo from '../images/domestos_logo3.png';
import domestosUseLoos from '../images/domestos_use_our_loos_logo.png';

const DomestosLogo = styled((props) => (

))`
  height: 2rem;
`;

const UseOurLoosLogo = styled((props) => (

))`
  height: 2rem;
`;

// Todo: Link Cookie Policy
const Footer = (props) => (
  <Box
    as="footer"
    display={['block', 'flex']}
    justifyContent="space-between"
    alignItems="center"
    px={4}
    py={2}
    bg="grey"
    color="primary"
    minHeight="60px"
  >
    {config.shouldShowSponsor() && (
      <HashLink
        to="/use-our-loos"
        title="Domestos: Use Our Loos Campaign"
        scroll={(el) => el.scrollIntoView(true)}
      >
        <Box display="flex" alignItems="center">
          Proudly sponsored by Domestos
          <Box ml={3}>
            <UseOurLoosLogo />
          </Box>
          <Box ml={2}>
            <DomestosLogo />
          </Box>
        </Box>
      </HashLink>
    )}

    <Box as="ul" display={['block', 'flex']} alignItems="center">

Cookie Policy

      <Box as="li" ml={[0, 4]}>
        <Link to="/privacy">Privacy Policy</Link>
      </Box>
    </Box>
  </Box>
);

export default withRouter(Footer);
<commit_msg>fix: make Footer responsive<commit_after>import React from 'react';
import { Link, withRouter } from 'react-router-dom';
import { HashLink } from 'react-router-hash-link';
import styled from '@emotion/styled';

import Box from './Box';
import Text from './Text';

import config from '../config';

import domestosLogo from '../images/domestos_logo3.png';
import domestosUseLoos from '../images/domestos_use_our_loos_logo.png';

const DomestosLogo = styled((props) => (

))`
  height: 2rem;
`;

const UseOurLoosLogo = styled((props) => (

))`
  height: 2rem;
`;

// Todo: Link Cookie Policy
const Footer = (props) => (
  <Box
    as="footer"
    display="flex"
    flexDirection={['column', 'row']}
    justifyContent="space-between"
    alignItems="center"
    px={[3, 4]}
    py={[0, 2]}
    bg={['transparent', 'grey']}
    color="primary"
    minHeight={60}
  >
    {config.shouldShowSponsor() && (
      <HashLink
        to="/use-our-loos"
        title="Domestos: Use Our Loos Campaign"
        scroll={(el) => el.scrollIntoView(true)}
      >
        <Box
          display="flex"
          flexDirection={['column', 'row']}
          alignItems="center"
        >
          <Text fontSize={14}>
            Proudly sponsored by Domestos
          </Text>

          <Box display="flex" ml={[0, 3]} mb={[3, 0]} order={[-1, 0]}>
            <UseOurLoosLogo />
            <Box ml={2}>
              <DomestosLogo />
            </Box>
          </Box>
        </Box>
      </HashLink>
    )}

    <Box order={[-1, 0]} mb={[4, 0]}>
      <Text fontSize={[12, 16]}>
        <Box as="ul" display={['block', 'flex']} alignItems="center">

Cookie Policy

          <Box as="li" ml={[0, 4]}>
            <Link to="/privacy">Privacy Policy</Link>
          </Box>
        </Box>
      </Text>
    </Box>
  </Box>
);

export default withRouter(Footer);
<|endoftext|>


<commit_before>
mi2JS.addCompClass('base/ListScrollbar', 'Base', '

',

// component initializer function that defines constructor and adds methods to the prototype
function(proto, superProto, comp, superComp){

	var mi2 = mi2JS;

	proto.initChildren = function(){
		superProto.initChildren.call(this);

		this.allData = this.data = this.chunk = [];
		this.limit = 0;
		this.offset = 0;
		this.scrollAmount = 0;
		this.filter = null;
		this.sort = null;
		this.bar = this.el.firstElementChild;
		this.listen(this.el,'click');
		this.listen(this.bar,'mousedown');
		this.listen(document,'mousemove');
		this.listen(document,'mouseup');
	};

	proto.on_click = function(evt){
		if(evt.target == this.el){
			var rect = this.el.getBoundingClientRect();
			var to = ((evt.pointerY()-rect.top)/this.el.offsetHeight);
			this.fireEvent({name:'scrollTo',pos:to});
			this.setOffset( Math.floor(to*this.data.length) );
			this.updateScroll();
		}
	};

	proto.on_mousedown = function(evt){
		evt.stop();
		this.addClass('scroll-moving');
		this.mousedown = true;
	};

	proto.on_mousemove = function(evt){
		if(!this.mousedown) return;
		evt.stop();
		var rect = this.el.getBoundingClientRect();
		var to = ((evt.pointerY()-rect.top)/this.el.offsetHeight);
		this.setOffset( Math.floor(to*this.data.length) );
	};

	proto.on_mouseup = function(evt){
		if(!this.mousedown) return;
		this.removeClass('scroll-moving');
		this.mousedown = false;
	};

	proto.setValue = function(offset,range,max){
		var hor = this.attr('direction') == 'horizontal';
		var style = this.bar.style;
		var maxSize = hor ? this.el.offsetWidth : this.el.offsetHeight;
		var len = Math.floor( (range/max)*maxSize );
		if(len<10) len = 10;
		maxSize -= len;
		var pos = Math.floor( ((offset+range)/max)*(maxSize));
		if(hor){
			style.width = len+'px';
			style.left = pos+'px';
		}else{
			style.height = len+'px';
			style.top = pos+'px';
		}
	};

	proto.init = function(list, scrollAmount){
		this.list = list;
		this.scrollAmount = scrollAmount || 3;
		// FF
		this.listen(this.list.el,'DOMMouseScroll',this.on_mousewheel);
		// other
		this.listen(this.list.el,'mousewheel');
	};

	proto.on_mousewheel = function(evt){
		var dir = evt.deltaY || evt.detail;
		// this.moveOffset(this.limit * (dir > 0 ? 1:-1) );
		this.moveOffset(this.scrollAmount * (dir > 0 ? 1:-1) );
		evt.stop();

		this.updateScroll();
	};

	proto.updateScroll = function(){
		this.setTimeout(function(){
			// this.setVisible(wh < max);
			// this.setValue(this.scrollWrap.el.scrollTop,wh,max,);
			var len = this.data.length;
			this.setVisible(this.limit < len);
			this.setValue( this.offset, this.limit, len );
		},0);
	};

	function makeAsc(column){
		return function(a,b){
			if (a[column] == b[column]) return 0;
			return a[column] > b[column] ? 1:-1;
		}
	}

	function makeDesc(column){
		return function(a,b){
			if (a[column] == b[column]) return 0;
			return a[column] > b[column] ? -1:1;
		}
	}

	proto.setOrderColumn = function(order){
		if(order){
			this.setSort( order[1] == 'DESC' ?  makeDesc(order[0]) : makeAsc(order[0]));
		}else{
			this.setSort(null);
		}
	};

	proto.newData = function(allData){
	    this.allData = allData || [];
	    this.applySort();
	    this.applyFilter();
	    this.applyLimit();
	};

	proto.moveOffset = function(move){
		this.setOffset(this.offset + move);
	};

	proto.setOffset = function(offset, limit){
	  this.offset = offset;
	  if(limit !== void 0) this.limit = limit;
	  this.applyLimit();
	};

	proto.setLimit = function(limit){
	  this.limit = limit;
	  this.applyLimit();
	};

	proto.setFilter = function(filter){
	  this.filter = filter;
	  this.applyFilter();
	  this.applyLimit();
	};

	proto.setSort = function(sort){
	  this.sort = sort;
	  this.applySort();
	  this.applyFilter();
	  this.applyLimit();
	};

	proto.applySort = function(){
	  if(this.sort) this.allData.sort(this.sort);
	};

	proto.applyFilter = function(){
	    if(this.filter){
	      this.data = this.data.filter(this.filter);
	    }else{
	      this.data = this.allData;
	    }
	};

	proto.applyLimit = function(){
	    var data = this.data;
	    if(this.limit){
	      if(this.limit + this.offset > data.length) this.offset = data.length - this.limit;
	      if(this.offset < 0) this.offset = 0;
	      var to = this.offset + this.limit;
	      if(to > this.data.length) to = data.length;
	      this.chunk = this.data.slice(this.offset, to);
	    }else{
	      this.chunk = this.data;
	    }
	    this.updateScroll();
	    if(this.list) this.list.setValue(this.chunk);
	};

});<commit_msg>Update ListScrollbar.js<commit_after>
mi2JS.addCompClass('base/ListScrollbar', 'Base', '

',

// component initializer function that defines constructor and adds methods to the prototype
function(proto, superProto, comp, superComp){

	var mi2 = mi2JS;

	proto.initChildren = function(){
		superProto.initChildren.call(this);

		this.allData = this.data = this.chunk = [];
		this.limit = 0;
		this.offset = 0;
		this.scrollAmount = 0;
		this.filter = null;
		this.sort = null;
		this.bar = this.el.firstElementChild;
		this.listen(this.el,'click');
		this.listen(this.bar,'mousedown');
		this.listen(document,'mousemove');
		this.listen(document,'mouseup');
		this.minLength = 20;
	};

	proto.on_click = function(evt){
		if(evt.target == this.el){
			var rect = this.el.getBoundingClientRect();
			var to = ((evt.pointerY()-rect.top)/this.el.offsetHeight);
			this.fireEvent({name:'scrollTo',pos:to});
			this.setOffset( Math.floor(to*this.data.length) );
			this.updateScroll();
		}
	};

	proto.on_mousedown = function(evt){
		evt.stop();
		this.addClass('scroll-moving');
		this.mousedown = true;
	};

	proto.on_mousemove = function(evt){
		if(!this.mousedown) return;
		evt.stop();
		var rect = this.el.getBoundingClientRect();
		var to = ((evt.pointerY()-rect.top)/this.el.offsetHeight);
		this.setOffset( Math.floor(to*this.data.length) );
	};

	proto.on_mouseup = function(evt){
		if(!this.mousedown) return;
		this.removeClass('scroll-moving');
		this.mousedown = false;
	};

	proto.setValue = function(offset,range,max){
		var hor = this.attr('direction') == 'horizontal';
		var style = this.bar.style;
		var maxSize = hor ? this.el.offsetWidth : this.el.offsetHeight;
		var len = Math.floor( (range/max)*maxSize );
		if(len<this.minLength){
			maxSize -= this.minLength;
			range = max/(maxSize)*this.minLength;
			len = this.minLength;
		}
		var pos = Math.floor( ((offset+range)/max)*(maxSize))-len;
		if(hor){
			style.width = len+'px';
			style.left = pos+'px';
		}else{
			style.height = len+'px';
			style.top = pos+'px';
		}
	};

	proto.init = function(list, scrollAmount){
		this.list = list;
		this.scrollAmount = scrollAmount || 3;
		// FF
		this.listen(this.list.el,'DOMMouseScroll',this.on_mousewheel);
		// other
		this.listen(this.list.el,'mousewheel');
	};

	proto.on_mousewheel = function(evt){
		var dir = evt.deltaY || evt.detail;
		// this.moveOffset(this.limit * (dir > 0 ? 1:-1) );
		this.moveOffset(this.scrollAmount * (dir > 0 ? 1:-1) );
		evt.stop();

		this.updateScroll();
	};

	proto.updateScroll = function(){
		this.setTimeout(function(){
			// this.setVisible(wh < max);
			// this.setValue(this.scrollWrap.el.scrollTop,wh,max,);
			var len = this.data.length;
			this.setVisible(this.limit < len);
			this.setValue( this.offset, this.limit, len );
		},0);
	};

	function makeAsc(column){
		return function(a,b){
			if (a[column] == b[column]) return 0;
			return a[column] > b[column] ? 1:-1;
		}
	}

	function makeDesc(column){
		return function(a,b){
			if (a[column] == b[column]) return 0;
			return a[column] > b[column] ? -1:1;
		}
	}

	proto.setOrderColumn = function(order){
		if(order){
			this.setSort( order[1] == 'DESC' ?  makeDesc(order[0]) : makeAsc(order[0]));
		}else{
			this.setSort(null);
		}
	};

	proto.newData = function(allData){
	    this.allData = allData || [];
	    this.applySort();
	    this.applyFilter();
	    this.applyLimit();
	};

	proto.moveOffset = function(move){
		this.setOffset(this.offset + move);
	};

	proto.setOffset = function(offset, limit){
	  this.offset = offset;
	  if(limit !== void 0) this.limit = limit;
	  this.applyLimit();
	};

	proto.setLimit = function(limit){
	  this.limit = limit;
	  this.applyLimit();
	};

	proto.setFilter = function(filter){
	  this.filter = filter;
	  this.applyFilter();
	  this.applyLimit();
	};

	proto.setSort = function(sort){
	  this.sort = sort;
	  this.applySort();
	  this.applyFilter();
	  this.applyLimit();
	};

	proto.applySort = function(){
	  if(this.sort) this.allData.sort(this.sort);
	};

	proto.applyFilter = function(){
	    if(this.filter){
	      this.data = this.data.filter(this.filter);
	    }else{
	      this.data = this.allData;
	    }
	};

	proto.applyLimit = function(){
	    var data = this.data;
	    if(this.limit){
	      if(this.limit + this.offset > data.length) this.offset = data.length - this.limit;
	      if(this.offset < 0) this.offset = 0;
	      var to = this.offset + this.limit;
	      if(to > this.data.length) to = data.length;
	      this.chunk = this.data.slice(this.offset, to);
	    }else{
	      this.chunk = this.data;
	    }
	    this.updateScroll();
	    if(this.list) this.list.setValue(this.chunk);
	};

});<|endoftext|>


<commit_before>// cpserver.js - Waqas Bhatti (wbhatti@astro.princeton.edu) - Jan 2017
// License: MIT. See LICENSE for the full text.
//
// This contains the JS to drive the checkplotserver's interface.
//

//////////////
// JS BELOW //
//////////////

// this contains utility functions
var cputils = {

    // this encodes a string to base64
    // https://developer.mozilla.org/en-US/docs/Web/API/WindowBase64/Base64_encoding_and_decoding
    b64_encode: function (str) {
        return btoa(
            encodeURIComponent(str)
                .replace(/%([0-9A-F]{2})/g,
                         function(match, p1) {
                             return String.fromCharCode('0x' + p1);
                         }));
    },

    // this turns a base64 string into an image by updating its source
    b64_to_image: function (str, targetelem) {

        var datauri = 'data:image/png;base64,' + str;
        $(targetelem).attr('src',datauri);

    }

};

// this is the checkplot object
var cpv = {

    filelist: [],
    nfiles: 0,
    currfile: '',
    currcp:'',

    // this loads a checkplot from an image file into an HTML canvas object
    load_checkplot: function (filename) {

        console.log('loading ' + filename);

        // build the title for this current file
        var plottitle = $('#checkplot-current');
        var filelink = filename;
        var objectidelem = $('#objectid');
        var twomassidelem = $('#twomassid');

        plottitle.html(filelink);

        if (cpv.currfile.length > 0) {
            // un-highlight the previous file in side bar
            $("a[data-fname='" + cpv.currfile + "']").unwrap();
        }

        // do the AJAX call to get this checkplot
        var ajaxurl = '/cp/' + cputils.b64_encode(filename);

        $.getJSON(ajaxurl, function (data) {

            cpv.currcp = data.result;
            console.log('received cp for ' + cpv.currcp.objectid);

            /////////////////////////////////////////////////
            // update the UI with elems for this checkplot //
            /////////////////////////////////////////////////

            // update the objectid header
            objectidelem.html(cpv.currcp.objectid);
            // update the twomassid header
            twomassidelem.html('2MASS J' + cpv.currcp.objectinfo.twomassid);

            // update the finder chart
            cputils.b64_to_image(cpv.currcp.finderchart,
                                 '#finderchart');

            // update the objectinfo
            var hatinfo = '' +
                (cpv.currcp.objectinfo.stations.split(',')).join(', ') +
                '
' +
                'LC points: ' +
                cpv.currcp.objectinfo.ndet;
            $('#hatinfo').html(hatinfo);

            var coordspm =
                'RA, Dec: ' +
                math.format(cpv.currcp.objectinfo.ra,6) + ', ' +
                math.format(cpv.currcp.objectinfo.decl,6) + '
' +
                'Total PM: ' +
                math.format(cpv.currcp.objectinfo.propermotion,5) +
                ' mas/yr
' +
                'Reduced PM: ' +
                math.format(cpv.currcp.objectinfo.reducedpropermotion,4);
            $('#coordspm').html(coordspm);

            var mags = 'gri: ' +
                math.format(cpv.currcp.objectinfo.sdssg,5) + ', ' +
                math.format(cpv.currcp.objectinfo.sdssr,5) + ', ' +
                math.format(cpv.currcp.objectinfo.sdssi,5) + '
' +
                'JHK: ' +
                math.format(cpv.currcp.objectinfo.jmag,5) + ', ' +
                math.format(cpv.currcp.objectinfo.hmag,5) + ', ' +
                math.format(cpv.currcp.objectinfo.kmag,5) + '
' +
                'BV: ' +
                math.format(cpv.currcp.objectinfo.bmag,5) + ', ' +
                math.format(cpv.currcp.objectinfo.vmag,5);
            $('#mags').html(mags);

            var colors = 'B - V: ' +
                math.format(cpv.currcp.objectinfo.bvcolor,4) + '
' +
                'i - J: ' +
                math.format(cpv.currcp.objectinfo.ijcolor,4) + '
' +
                'J - K: ' +
                math.format(cpv.currcp.objectinfo.jkcolor,4);
            $('#colors').html(colors);

            // update the magseries plot
            cputils.b64_to_image(cpv.currcp.magseries,
                                '#magseriesplot');

            // update the varinfo

            // update the phased light curves

        }).done(function () {

            console.log('done with cp');

            // update the current file tracker
            cpv.currfile = filename;
            // highlight the file in the sidebar list
            $("a[data-fname='" + filename + "']").wrap('')

        }).fail (function (xhr) {

            console.log('cp loading failed from ' + ajaxurl);

        });

    },

    // this binds actions to the web-app controls
    action_setup: function () {

        // the previous checkplot link
        $('.checkplot-prev').on('click',function (evt) {

            evt.preventDefault();

            // find the current index
            var currindex = cpv.filelist.indexOf(cpv.currfile);
            var prevfile = cpv.filelist[currindex-1];
            if (prevfile != undefined) {
                cpv.load_checkplot(prevfile);
            }

        });

        // the next checkplot link
        $('.checkplot-next').on('click',function (evt) {

            evt.preventDefault();

            // find the current index
            var currindex = cpv.filelist.indexOf(cpv.currfile);
            var nextfile = cpv.filelist[currindex+1];
            if (nextfile != undefined) {
                cpv.load_checkplot(nextfile);
            }

        });

        // clicking on a checkplot file in the sidebar
        $('#pnglist').on('click', '.checkplot-load', function (evt) {

            evt.preventDefault();

            var filetoload = $(this).attr('data-fname');
            console.log('file to load: ' + filetoload);

            if (cpv.filelist.indexOf(filetoload) != -1) {
                cpv.load_checkplot(filetoload);
            }

        });

    }

};
<commit_msg>minor js update<commit_after>// cpserver.js - Waqas Bhatti (wbhatti@astro.princeton.edu) - Jan 2017
// License: MIT. See LICENSE for the full text.
//
// This contains the JS to drive the checkplotserver's interface.
//

//////////////
// JS BELOW //
//////////////

// this contains utility functions
var cputils = {

    // this encodes a string to base64
    // https://developer.mozilla.org/en-US/docs/Web/API/WindowBase64/Base64_encoding_and_decoding
    b64_encode: function (str) {
        return btoa(
            encodeURIComponent(str)
                .replace(/%([0-9A-F]{2})/g,
                         function(match, p1) {
                             return String.fromCharCode('0x' + p1);
                         }));
    },

    // this turns a base64 string into an image by updating its source
    b64_to_image: function (str, targetelem) {

        var datauri = 'data:image/png;base64,' + str;
        $(targetelem).attr('src',datauri);

    }

};

// this contains updates to current checkplots
var cptracker = {
};

// this is the container for the main functions
var cpv = {

    currfile: '',
    currcp:'',

    // this loads a checkplot from an image file into an HTML canvas object
    load_checkplot: function (filename) {

        console.log('loading ' + filename);

        // build the title for this current file
        var plottitle = $('#checkplot-current');
        var filelink = filename;
        var objectidelem = $('#objectid');
        var twomassidelem = $('#twomassid');

        plottitle.html(filelink);

        if (cpv.currfile.length > 0) {
            // un-highlight the previous file in side bar
            $("a[data-fname='" + cpv.currfile + "']").unwrap();
        }

        // do the AJAX call to get this checkplot
        var ajaxurl = '/cp/' + cputils.b64_encode(filename);

        $.getJSON(ajaxurl, function (data) {

            cpv.currcp = data.result;
            console.log('received cp for ' + cpv.currcp.objectid);

            /////////////////////////////////////////////////
            // update the UI with elems for this checkplot //
            /////////////////////////////////////////////////

            // update the objectid header
            objectidelem.html(cpv.currcp.objectid);
            // update the twomassid header
            twomassidelem.html('2MASS J' + cpv.currcp.objectinfo.twomassid);

            // update the finder chart
            cputils.b64_to_image(cpv.currcp.finderchart,
                                 '#finderchart');

            // update the objectinfo
            var hatinfo = '' +
                (cpv.currcp.objectinfo.stations.split(',')).join(', ') +
                '
' +
                'LC points: ' +
                cpv.currcp.objectinfo.ndet;
            $('#hatinfo').html(hatinfo);

            var coordspm =
                'RA, Dec: ' +
                math.format(cpv.currcp.objectinfo.ra,6) + ', ' +
                math.format(cpv.currcp.objectinfo.decl,6) + '
' +
                'Total PM: ' +
                math.format(cpv.currcp.objectinfo.propermotion,5) +
                ' mas/yr
' +
                'Reduced PM: ' +
                math.format(cpv.currcp.objectinfo.reducedpropermotion,4);
            $('#coordspm').html(coordspm);

            var mags = 'gri: ' +
                math.format(cpv.currcp.objectinfo.sdssg,5) + ', ' +
                math.format(cpv.currcp.objectinfo.sdssr,5) + ', ' +
                math.format(cpv.currcp.objectinfo.sdssi,5) + '
' +
                'JHK: ' +
                math.format(cpv.currcp.objectinfo.jmag,5) + ', ' +
                math.format(cpv.currcp.objectinfo.hmag,5) + ', ' +
                math.format(cpv.currcp.objectinfo.kmag,5) + '
' +
                'BV: ' +
                math.format(cpv.currcp.objectinfo.bmag,5) + ', ' +
                math.format(cpv.currcp.objectinfo.vmag,5);
            $('#mags').html(mags);

            var colors = 'B - V: ' +
                math.format(cpv.currcp.objectinfo.bvcolor,4) + '
' +
                'i - J: ' +
                math.format(cpv.currcp.objectinfo.ijcolor,4) + '
' +
                'J - K: ' +
                math.format(cpv.currcp.objectinfo.jkcolor,4);
            $('#colors').html(colors);

            // update the magseries plot
            cputils.b64_to_image(cpv.currcp.magseries,
                                '#magseriesplot');

            // update the varinfo

            // update the phased light curves

        }).done(function () {

            console.log('done with cp');

            // update the current file tracker
            cpv.currfile = filename;
            // highlight the file in the sidebar list
            $("a[data-fname='" + filename + "']").wrap('')

        }).fail (function (xhr) {

            console.log('cp loading failed from ' + ajaxurl);

        });

    },

    // this binds actions to the web-app controls
    action_setup: function () {

        // the previous checkplot link
        $('.checkplot-prev').on('click',function (evt) {

            evt.preventDefault();

            // find the current index
            var currindex = cpv.filelist.indexOf(cpv.currfile);
            var prevfile = cpv.filelist[currindex-1];
            if (prevfile != undefined) {
                cpv.load_checkplot(prevfile);
            }

        });

        // the next checkplot link
        $('.checkplot-next').on('click',function (evt) {

            evt.preventDefault();

            // find the current index
            var currindex = cpv.filelist.indexOf(cpv.currfile);
            var nextfile = cpv.filelist[currindex+1];
            if (nextfile != undefined) {
                cpv.load_checkplot(nextfile);
            }

        });

        // clicking on a checkplot file in the sidebar
        $('#checkplotlist').on('click', '.checkplot-load', function (evt) {

            evt.preventDefault();

            var filetoload = $(this).attr('data-fname');
            console.log('file to load: ' + filetoload);

            // ask the backend for this file
            cpv.load_checkplot(filetoload);

        });

    }

};
<|endoftext|>


<commit_before>import React, { PureComponent } from 'react';
import { css } from 'glamor';
import Globals from '../utils/Globals';

import Text from './Text';

const styles = {
  container: css({
    alignSelf: 'flex-end',
    background: Globals.colors.main,
    width: '100vw',
    display: 'flex',
    paddingBottom: 30,
    '@media(max-width: 720px)': {
      alignSelf: 'auto',
    },
  }),
  link: css({
    color: Globals.colors.background,
    fontSize: 20,
    borderRadius: 20,
    padding: '10px 20px',
    backgroundColor: Globals.colors.white,
    display: 'inline-block',
    margin: '0.5em',
    textDecoration: 'none',
  }),
};

class TextMeetup extends PureComponent {
  render() {
    return (

        <Text title="Comunidade React Brasil">

            Meetup Curitiba/PR

            Meetup Maringá/PR

            Meetup Rio de Janeiro/RJ

            Meetup Porto Alegre/RS

            Meetup Blumenau/SC

            Meetup Florianópolis/SC

            Meetup Joinville/SC

            Meetup Campinas/SP

            Meetup São Paulo/SP

            Meetup Salvador/BA

        </Text>

    );
  }
}

export default TextMeetup;
<commit_msg>[Fix/Feature] Fix Meetup Salvador position and add meetup Belo Horizonte<commit_after>import React, { PureComponent } from 'react';
import { css } from 'glamor';
import Globals from '../utils/Globals';

import Text from './Text';

const styles = {
  container: css({
    alignSelf: 'flex-end',
    background: Globals.colors.main,
    width: '100vw',
    display: 'flex',
    paddingBottom: 30,
    '@media(max-width: 720px)': {
      alignSelf: 'auto',
    },
  }),
  link: css({
    color: Globals.colors.background,
    fontSize: 20,
    borderRadius: 20,
    padding: '10px 20px',
    backgroundColor: Globals.colors.white,
    display: 'inline-block',
    margin: '0.5em',
    textDecoration: 'none',
  }),
};

class TextMeetup extends PureComponent {
  render() {
    return (

        <Text title="Comunidade React Brasil">

            Meetup Salvador/BA

            Meetup Belo Horizonte/MG

            Meetup Curitiba/PR

            Meetup Maringá/PR

            Meetup Rio de Janeiro/RJ

            Meetup Porto Alegre/RS

            Meetup Blumenau/SC

            Meetup Florianópolis/SC

            Meetup Joinville/SC

            Meetup Campinas/SP

            Meetup São Paulo/SP

        </Text>

    );
  }
}

export default TextMeetup;
<|endoftext|>