<commit_before>/* @flow weak */

/**
 * mSupply Mobile
 * Sustainable Solutions (NZ) Ltd. 2016
 */

import React from 'react';
import { StyleSheet, View } from 'react-native';
import globalStyles, { SUSSOL_ORANGE, WARM_GREY, WARMER_GREY } from '../globalStyles';

import {
  Cell,
  CheckableCell,
  DataTable,
  EditableCell,
  Header,
  HeaderCell,
  Row,
} from '../widgets/DataTable';

import Icon from 'react-native-vector-icons/Ionicons';
import { ListView } from 'realm/react-native';
import { SearchBar } from '../widgets';

/**
 * Provides a generic implementation of a standard page in mSupply Mobile, which
 * contains a searchable table. Should always be overridden, in particular the
 * following methods and instance variables (fields):
 * @method getUpdatedData(searchTerm, sortBy, isAscending) Should return updated data
 * @method renderCell(key, record) Should define what to render in a cell with the
 *         											 given column key and database record
 * @method onRowPress(key, rowData) Should define behaviour when a row is pressed,
 *         											 don't override if row should not be pressable
 * @method onEndEditing(key, rowData, newValue) Handles user input to an editable cell
 *         											 don't override if row should not be pressable
 * @field  {array}  columns      An array of objects defining each of the columns.
 *         											 Each column must contain: key, width, title. Each
 *         											 may optionally also contain a boolean 'sortable'.
 * @state  {ListView.DataSource} dataSource    DataTable input, used to update rows
 *         																		 being rendered
 * @state  {string}              searchTerm    Current term user has entered in search bar
 * @state  {string}              sortBy        The property to sort by (is selected
 *                                             by column press).
 * @state  {boolean}             isAscending   Direction sortBy should sort
 *                                             (ascending/descending:true/false).
 * N.B. Take care to call parent method if overriding any of the react life cycle methods.
 */
export class GenericTablePage extends React.Component {
  constructor(props) {
    super(props);
    const dataSource = new ListView.DataSource({
      rowHasChanged: (row1, row2) => row1 !== row2,
    });
    this.state = {
      dataSource: dataSource,
      searchTerm: '',
      sortBy: '',
      isAscending: true,
      selection: [],
      expandedRows: [],
    };
    this.columns = null;
    this.dataTypesDisplayed = [];
    this.databaseListenerId = null;
    this.onSearchChange = this.onSearchChange.bind(this);
    this.onColumnSort = this.onColumnSort.bind(this);
    this.onDatabaseEvent = this.onDatabaseEvent.bind(this);
    this.renderHeader = this.renderHeader.bind(this);
    this.renderRow = this.renderRow.bind(this);
    this.renderCell = this.renderCell.bind(this);
    this.refreshData = this.refreshData.bind(this);
  }

  /**
   * If overridden, first line of this method should be duplicated. May need to be overridden to
   * populate selection in state if CheckableCells are used and need to
   * remember their selected state.
   */
  componentWillMount() {
    this.databaseListenerId = this.props.database.addListener(this.onDatabaseEvent);
    this.refreshData();
  }

  componentWillUnmount() {
    this.props.database.removeListener(this.databaseListenerId);
  }

  onDatabaseEvent(changeType, recordType) {
    if (this.dataTypesDisplayed.indexOf(recordType) >= 0) this.refreshData();
  }

  onSearchChange(event) {
    const term = event.nativeEvent.text;
    this.setState({ searchTerm: term }, this.refreshData);
  }

  onColumnSort(sortBy) {
    if (this.state.sortBy === sortBy) { // Changed column sort direction
      this.setState({ isAscending: !this.state.isAscending }, this.refreshData);
    } else { // Changed sorting column
      this.setState({
        sortBy: sortBy,
        isAscending: true,
      }, this.refreshData);
    }
  }

  /**
   * Adds/removes rowData.id to/from the selection array in state. Must call this within any
   * overrides. i.e. super.onCheckablePress(rowData);
   */
  onCheckablePress(rowData) {
    const newSelection = [...this.state.selection];
    if (newSelection.indexOf(rowData.id) >= 0) {
      newSelection.splice(newSelection.indexOf(rowData.id), 1);
    } else {
      newSelection.push(rowData.id);
    }
    this.setState({ selection: newSelection });
  }

  /**
   * Adds/removes rowData.id to/from the expandedRows array in state. Must call this within any
   * overrides i.e. super.onExpandablePress(rowData);
   */
  onExpandablePress(rowData) {
    const newExpandedRows = [...this.state.expandedRows];
    if (newExpandedRows.indexOf(rowData.id) >= 0) {
      newExpandedRows.splice(newExpandedRows.indexOf(rowData.id), 1);
    } else {
      newExpandedRows.push(rowData.id);
    }
    this.setState({ expandedRows: newExpandedRows });
  }

  onRowPress(rowData) {
    this.onExpandablePress(rowData);
  }

  refreshData() {
    const { dataSource, searchTerm, sortBy, isAscending } = this.state;
    const data = this.getUpdatedData(searchTerm, sortBy, isAscending);
    this.setState({ dataSource: dataSource.cloneWithRows(data) });
  }

/**
 * Accepted Cell formats:
 * 1. <Cell style={styles.cell} width={3}/> // Or any other react component. Must be styled within
 *                                          // the extending class.
 * 2. item.name;
 * 3. {
 *      type: 'text',
 *      cellContents: item.name,
 *    };
 * 4. {
 *      type: 'editable',
 *      cellContents: transactionItem.totalQuantity,
 *    };
 * 5. {
 *      type: 'checkable',
 *      isDisabled: false,
 *    };
 * 6. {
 *      type: 'checkable',
 *      icon: 'md-remove-circle', // will use for both Checked and NotChecked, only colour changes
 *      isDisabled: false,
 *    };
 * 7. {
 *      type: 'checkable',
 *      iconChecked: 'md-radio-button-on',
 *      iconNotChecked: 'md-radio-button-off',
 *      isDisabled: false,
 *    };
 */
  renderCell() {
    return 'DEFAULT CELL';
  }

  renderHeader() {
    const headerCells = [];

    this.columns.forEach((column, index, columns) => {
      const cellStyle = index !== columns.length - 1 ?
        globalStyles.dataTableHeaderCell :
        [globalStyles.dataTableHeaderCell, globalStyles.dataTableRightMostCell];
      const sortFunction = column.sortable ? () => this.onColumnSort(column.key) : null;
      headerCells.push(
        <HeaderCell
          key={column.key}
          style={cellStyle}
          textStyle={globalStyles.dataTableText}
          width={column.width}
          onPress={sortFunction}
          isAscending={this.state.isAscending}
          isSelected={this.state.sortBy === column.key}
          text={column.title}
        />
      );
    });
    return (
      <Header style={globalStyles.header}>
        {headerCells}
      </Header>
    );
  }

  renderRow(rowData, sectionId, rowId) {
    const cells = [];
    const isExpanded = this.state.expandedRows.indexOf(rowData.id) >= 0;
    // Make rows alternate background colour
    const rowStyle = rowId % 2 === 1 ?
      globalStyles.dataTableRow : [globalStyles.dataTableRow, { backgroundColor: 'white' }];

    this.columns.forEach((column, index, columns) => {
      const cellStyle = index !== columns.length - 1 ?
        globalStyles.dataTableCell :
        [globalStyles.dataTableCell, globalStyles.dataTableRightMostCell];
      const renderedCell = this.renderCell(column.key, rowData);
      let cell;
      switch (renderedCell.type) {
        case 'custom':
          cell = renderedCell.cell;
          break;
        case 'checkable': {
          // if provided, use isChecked prop, else set isChecked according to rowData.id
          // being in selection array.
          const isChecked = renderedCell.isChecked ?
            renderedCell.isChecked : this.state.selection.indexOf(rowData.id) >= 0;
          let iconChecked;
          let iconNotChecked;
          if (renderedCell.iconChecked && renderedCell.iconNotChecked) {
            iconChecked = renderedCell.iconChecked;
            iconNotChecked = renderedCell.iconNotChecked;
          } else if (renderedCell.icon) {
            iconChecked = renderedCell.icon;
            iconNotChecked = renderedCell.icon;
          } else {
            iconChecked = 'md-radio-button-on';
            iconNotChecked = 'md-radio-button-off';
          }
          cell = (
            <CheckableCell
              key={column.key}
              style={[
                cellStyle,
                globalStyles.dataTableCheckableCell,
              ]}
              width={column.width}
              onPress={() => this.onCheckablePress(rowData)}
              renderDisabled={() => <Icon name={iconNotChecked} size={15} color={WARMER_GREY} />}
              renderIsChecked={() => <Icon name={iconChecked} size={15} color={SUSSOL_ORANGE} />}
              renderIsNotChecked={() => <Icon name={iconNotChecked} size={15} color={WARM_GREY} />}
              isChecked={isChecked}
              isDisabled={renderedCell.isDisabled}
            />
          );
          break;
        }
        case 'editable':
          cell = (
            <EditableCell
              key={column.key}
              style={cellStyle}
              textStyle={globalStyles.dataTableText}
              width={column.width}
              onEndEditing={this.onEndEditing &&
                            ((target, value) => this.onEndEditing(column.key, target, value))}
              target={rowData}
              value={renderedCell.cellContents}
            />
          );
          break;
        case 'text':
        default:
          cell = (
            <Cell
              key={column.key}
              style={cellStyle}
              textStyle={globalStyles.dataTableText}
              width={column.width}
            >
              {renderedCell.hasOwnProperty('cellContents') ?
                renderedCell.cellContents :
                renderedCell}
            </Cell>
          );
      }
      cells.push(cell);
    });
    return (
      <Row
        style={rowStyle}
        renderExpansion={this.renderExpansion && (() => this.renderExpansion(rowData))}
        isExpanded={isExpanded}
        onPress={() => this.onRowPress(rowData)}
      >
        {cells}
      </Row>
    );
  }

  renderSearchBar() {
    return (
      <SearchBar
        onChange={(event) => this.onSearchChange(event)}
      />);
  }

  renderDataTable() {
    return (
      <DataTable
        style={globalStyles.dataTable}
        listViewStyle={localStyles.listView}
        dataSource={this.state.dataSource}
        renderRow={this.renderRow}
        renderHeader={this.renderHeader}
      />);
  }

  render() {
    return (
      <View style={globalStyles.pageContentContainer}>
        <View style={globalStyles.container}>
          <View style={globalStyles.pageTopSectionContainer}>
            {this.renderSearchBar()}
          </View>
          {this.renderDataTable()}
        </View>
      </View>
    );
  }
}

GenericTablePage.propTypes = {
  database: React.PropTypes.object,
};

const localStyles = StyleSheet.create({
  listView: {
    flex: 1,
  },
  rightMostCell: {
    borderRightWidth: 0,
  },
  dataTable: {
    flex: 1,
  },
});
<commit_msg>Rows either Expandable, touchable or neither.<commit_after>/* @flow weak */

/**
 * mSupply Mobile
 * Sustainable Solutions (NZ) Ltd. 2016
 */

import React from 'react';
import { StyleSheet, View } from 'react-native';
import globalStyles, { SUSSOL_ORANGE, WARM_GREY, WARMER_GREY } from '../globalStyles';

import {
  Cell,
  CheckableCell,
  DataTable,
  EditableCell,
  Header,
  HeaderCell,
  Row,
} from '../widgets/DataTable';

import Icon from 'react-native-vector-icons/Ionicons';
import { ListView } from 'realm/react-native';
import { SearchBar } from '../widgets';

/**
 * Provides a generic implementation of a standard page in mSupply Mobile, which
 * contains a searchable table. Should always be overridden, in particular the
 * following methods and instance variables (fields):
 * @method getUpdatedData(searchTerm, sortBy, isAscending) Should return updated data
 * @method renderCell(key, record) Should define what to render in a cell with the
 *         											 given column key and database record
 * @method onRowPress(key, rowData) Should define behaviour when a row is pressed,
 *         											 don't override if row should not be pressable
 * @method onEndEditing(key, rowData, newValue) Handles user input to an editable cell
 *         											 don't override if row should not be pressable
 * @field  {array}  columns      An array of objects defining each of the columns.
 *         											 Each column must contain: key, width, title. Each
 *         											 may optionally also contain a boolean 'sortable'.
 * @state  {ListView.DataSource} dataSource    DataTable input, used to update rows
 *         																		 being rendered
 * @state  {string}              searchTerm    Current term user has entered in search bar
 * @state  {string}              sortBy        The property to sort by (is selected
 *                                             by column press).
 * @state  {boolean}             isAscending   Direction sortBy should sort
 *                                             (ascending/descending:true/false).
 * N.B. Take care to call parent method if overriding any of the react life cycle methods.
 */
export class GenericTablePage extends React.Component {
  constructor(props) {
    super(props);
    const dataSource = new ListView.DataSource({
      rowHasChanged: (row1, row2) => row1 !== row2,
    });
    this.state = {
      dataSource: dataSource,
      searchTerm: '',
      sortBy: '',
      isAscending: true,
      selection: [],
      expandedRows: [],
    };
    this.columns = null;
    this.dataTypesDisplayed = [];
    this.databaseListenerId = null;
    this.onSearchChange = this.onSearchChange.bind(this);
    this.onColumnSort = this.onColumnSort.bind(this);
    this.onDatabaseEvent = this.onDatabaseEvent.bind(this);
    this.renderHeader = this.renderHeader.bind(this);
    this.renderRow = this.renderRow.bind(this);
    this.renderCell = this.renderCell.bind(this);
    this.refreshData = this.refreshData.bind(this);
  }

  /**
   * If overridden, first line of this method should be duplicated. May need to be overridden to
   * populate selection in state if CheckableCells are used and need to
   * remember their selected state.
   */
  componentWillMount() {
    this.databaseListenerId = this.props.database.addListener(this.onDatabaseEvent);
    this.refreshData();
  }

  componentWillUnmount() {
    this.props.database.removeListener(this.databaseListenerId);
  }

  onDatabaseEvent(changeType, recordType) {
    if (this.dataTypesDisplayed.indexOf(recordType) >= 0) this.refreshData();
  }

  onSearchChange(event) {
    const term = event.nativeEvent.text;
    this.setState({ searchTerm: term }, this.refreshData);
  }

  onColumnSort(sortBy) {
    if (this.state.sortBy === sortBy) { // Changed column sort direction
      this.setState({ isAscending: !this.state.isAscending }, this.refreshData);
    } else { // Changed sorting column
      this.setState({
        sortBy: sortBy,
        isAscending: true,
      }, this.refreshData);
    }
  }

  /**
   * Adds/removes rowData.id to/from the selection array in state. Must call this within any
   * overrides. i.e. super.onCheckablePress(rowData);
   */
  onCheckablePress(rowData) {
    const newSelection = [...this.state.selection];
    if (newSelection.indexOf(rowData.id) >= 0) {
      newSelection.splice(newSelection.indexOf(rowData.id), 1);
    } else {
      newSelection.push(rowData.id);
    }
    this.setState({ selection: newSelection });
  }

  /**
   * Adds/removes rowData.id to/from the expandedRows array in state. Must call this within any
   * overrides i.e. super.onExpandablePress(rowData);
   */
  onExpandablePress(rowData) {
    const newExpandedRows = [...this.state.expandedRows];
    if (newExpandedRows.indexOf(rowData.id) >= 0) {
      newExpandedRows.splice(newExpandedRows.indexOf(rowData.id), 1);
    } else {
      newExpandedRows.push(rowData.id);
    }
    this.setState({ expandedRows: newExpandedRows });
  }

  refreshData() {
    const { dataSource, searchTerm, sortBy, isAscending } = this.state;
    const data = this.getUpdatedData(searchTerm, sortBy, isAscending);
    this.setState({ dataSource: dataSource.cloneWithRows(data) });
  }

/**
 * Accepted Cell formats:
 * 1. <Cell style={styles.cell} width={3}/> // Or any other react component. Must be styled within
 *                                          // the extending class.
 * 2. item.name;
 * 3. {
 *      type: 'text',
 *      cellContents: item.name,
 *    };
 * 4. {
 *      type: 'editable',
 *      cellContents: transactionItem.totalQuantity,
 *    };
 * 5. {
 *      type: 'checkable',
 *      isDisabled: false,
 *    };
 * 6. {
 *      type: 'checkable',
 *      icon: 'md-remove-circle', // will use for both Checked and NotChecked, only colour changes
 *      isDisabled: false,
 *    };
 * 7. {
 *      type: 'checkable',
 *      iconChecked: 'md-radio-button-on',
 *      iconNotChecked: 'md-radio-button-off',
 *      isDisabled: false,
 *    };
 */
  renderCell() {
    return 'DEFAULT CELL';
  }

  renderHeader() {
    const headerCells = [];

    this.columns.forEach((column, index, columns) => {
      const cellStyle = index !== columns.length - 1 ?
        globalStyles.dataTableHeaderCell :
        [globalStyles.dataTableHeaderCell, globalStyles.dataTableRightMostCell];
      const sortFunction = column.sortable ? () => this.onColumnSort(column.key) : null;
      headerCells.push(
        <HeaderCell
          key={column.key}
          style={cellStyle}
          textStyle={globalStyles.dataTableText}
          width={column.width}
          onPress={sortFunction}
          isAscending={this.state.isAscending}
          isSelected={this.state.sortBy === column.key}
          text={column.title}
        />
      );
    });
    return (
      <Header style={globalStyles.header}>
        {headerCells}
      </Header>
    );
  }

  renderRow(rowData, sectionId, rowId) {
    const cells = [];
    const isExpanded = this.state.expandedRows.indexOf(rowData.id) >= 0;
    // Make rows alternate background colour
    const rowStyle = rowId % 2 === 1 ?
      globalStyles.dataTableRow : [globalStyles.dataTableRow, { backgroundColor: 'white' }];

    this.columns.forEach((column, index, columns) => {
      const cellStyle = index !== columns.length - 1 ?
        globalStyles.dataTableCell :
        [globalStyles.dataTableCell, globalStyles.dataTableRightMostCell];
      const renderedCell = this.renderCell(column.key, rowData);
      let cell;
      switch (renderedCell.type) {
        case 'custom':
          cell = renderedCell.cell;
          break;
        case 'checkable': {
          // if provided, use isChecked prop, else set isChecked according to rowData.id
          // being in selection array.
          const isChecked = renderedCell.isChecked ?
            renderedCell.isChecked : this.state.selection.indexOf(rowData.id) >= 0;
          let iconChecked;
          let iconNotChecked;
          if (renderedCell.iconChecked && renderedCell.iconNotChecked) {
            iconChecked = renderedCell.iconChecked;
            iconNotChecked = renderedCell.iconNotChecked;
          } else if (renderedCell.icon) {
            iconChecked = renderedCell.icon;
            iconNotChecked = renderedCell.icon;
          } else {
            iconChecked = 'md-radio-button-on';
            iconNotChecked = 'md-radio-button-off';
          }
          cell = (
            <CheckableCell
              key={column.key}
              style={[
                cellStyle,
                globalStyles.dataTableCheckableCell,
              ]}
              width={column.width}
              onPress={() => this.onCheckablePress(rowData)}
              renderDisabled={() => <Icon name={iconNotChecked} size={15} color={WARMER_GREY} />}
              renderIsChecked={() => <Icon name={iconChecked} size={15} color={SUSSOL_ORANGE} />}
              renderIsNotChecked={() => <Icon name={iconNotChecked} size={15} color={WARM_GREY} />}
              isChecked={isChecked}
              isDisabled={renderedCell.isDisabled}
            />
          );
          break;
        }
        case 'editable':
          cell = (
            <EditableCell
              key={column.key}
              style={cellStyle}
              textStyle={globalStyles.dataTableText}
              width={column.width}
              onEndEditing={this.onEndEditing &&
                            ((target, value) => this.onEndEditing(column.key, target, value))}
              target={rowData}
              value={renderedCell.cellContents}
            />
          );
          break;
        case 'text':
        default:
          cell = (
            <Cell
              key={column.key}
              style={cellStyle}
              textStyle={globalStyles.dataTableText}
              width={column.width}
            >
              {renderedCell.hasOwnProperty('cellContents') ?
                renderedCell.cellContents :
                renderedCell}
            </Cell>
          );
      }
      cells.push(cell);
    });
    return (
      <Row
        style={rowStyle}
        renderExpansion={this.renderExpansion && (() => this.renderExpansion(rowData))}
        isExpanded={isExpanded}
        onPress={
          this.renderExpansion && (() => this.onExpandablePress(rowData))
            || this.onRowPress && (() => this.onRowPress(rowData))
        }
      >
        {cells}
      </Row>
    );
  }

  renderSearchBar() {
    return (
      <SearchBar
        onChange={(event) => this.onSearchChange(event)}
      />);
  }

  renderDataTable() {
    return (
      <DataTable
        style={globalStyles.dataTable}
        listViewStyle={localStyles.listView}
        dataSource={this.state.dataSource}
        renderRow={this.renderRow}
        renderHeader={this.renderHeader}
      />);
  }

  render() {
    return (
      <View style={globalStyles.pageContentContainer}>
        <View style={globalStyles.container}>
          <View style={globalStyles.pageTopSectionContainer}>
            {this.renderSearchBar()}
          </View>
          {this.renderDataTable()}
        </View>
      </View>
    );
  }
}

GenericTablePage.propTypes = {
  database: React.PropTypes.object,
};

const localStyles = StyleSheet.create({
  listView: {
    flex: 1,
  },
  rightMostCell: {
    borderRightWidth: 0,
  },
  dataTable: {
    flex: 1,
  },
});
<|endoftext|>


<commit_before>import "./toggle";
import $ from "jquery";
import registry from "@patternslib/patternslib/src/core/registry";
import { jest } from "@jest/globals";

describe("Toggle", function () {
    beforeEach(function () {
        document.body.innerHTML = `

            Button

              Click here to go somewhere else

        `;
        this.$el = $("#body");
    });

    afterEach(function () {
        document.body.outerHTML = "";
    });

    it("1 - by default toggles on click event", async function () {
        expect($(".toggled", this.$el).length).toEqual(0);

        // scan dom for patterns
        registry.scan(this.$el);
        expect($(".toggled", this.$el).length).toEqual(0);
        $(".pat-toggle", this.$el).trigger("click");
        expect($(".toggled", this.$el).length).toEqual(1);
        $(".pat-toggle", this.$el).trigger("click");
        expect($(".toggled", this.$el).length).toEqual(0);
    });

    it("can also listen to custom event", function () {
        $(".pat-toggle", this.$el).attr(
            "data-pat-toggle",
            "target: #target; targetScope: #body; value: toggled; event: customEvent"
        );
        expect($(".toggled", this.$el).length).toEqual(0);
        registry.scan(this.$el);
        expect($(".toggled", this.$el).length).toEqual(0);
        $(".pat-toggle", this.$el).trigger("customEvent");
        expect($(".toggled", this.$el).length).toEqual(1);
    });

    it("can also toggle custom element attribute", function () {
        $(".pat-toggle", this.$el).attr(
            "data-pat-toggle",
            "target: #target; targetScope: #body; value: toggled; attribute: rel"
        );
        expect($(".toggled", this.$el).length).toEqual(0);
        expect($('[rel="toggled"]', this.$el).length).toEqual(0);
        registry.scan(this.$el);
        expect($('[rel="toggled"]', this.$el).length).toEqual(0);
        expect($(".toggled", this.$el).length).toEqual(0);
        $(".pat-toggle", this.$el).trigger("click");
        expect($(".toggled", this.$el).length).toEqual(0);
        expect($('[rel="toggled"]', this.$el).length).toEqual(1);
        $(".pat-toggle", this.$el).trigger("click");
        expect($('[rel="toggled"]', this.$el).length).toEqual(0);
    });

    it("toggle multiple targets", function () {
        var $el = $(
            "" +
                '
' +
                "
" +
                "
" +
                "
" +
                '        Button' +
                "
" +
                '

' +
                "
" +
                '

' +
                '

' +
                "
" +
                '

' +
                '

' +
                "
"
        );
        registry.scan($el);
        expect($(".toggled", $el).length).toEqual(0);
        $(".pat-toggle", $el).trigger("click");
        expect($(".toggled", $el).length).toEqual(5);
        $(".pat-toggle", $el).trigger("click");
        expect($(".toggled", $el).length).toEqual(0);
    });

    it("if some elements already marked, mark all with first click", function () {
        var $el = $(
            "" +
                '
' +
                "
" +
                "
" +
                "
" +
                '        Button' +
                "
" +
                '

' +
                "
" +
                '

' +
                '

' +
                "
" +
                '

' +
                '

' +
                "
"
        );
        registry.scan($el);
        expect($(".toggled", $el).length).toEqual(3);
        $(".pat-toggle", $el).trigger("click");
        expect($(".toggled", $el).length).toEqual(5);
        $(".pat-toggle", $el).trigger("click");
        expect($(".toggled", $el).length).toEqual(0);
    });

    it("if all elements already marked, unmark all with first click", function () {
        var $el = $(
            "" +
                '
' +
                "
" +
                "
" +
                "
" +
                '        Button' +
                "
" +
                '

' +
                "
" +
                '

' +
                '

' +
                "
" +
                '

' +
                '

' +
                "
"
        );
        registry.scan($el);
        expect($(".toggled", $el).length).toEqual(5);
        $(".pat-toggle", $el).trigger("click");
        expect($(".toggled", $el).length).toEqual(0);
    });

    it("should also be able to mark the toggle itself", function () {
        var $el = $(
            "" +
                '
' +
                "
" +
                "
" +
                "
" +
                '        Button' +
                "
" +
                '

' +
                "
" +
                '

' +
                '

' +
                "
" +
                '

' +
                '

' +
                "
"
        );
        registry.scan($el);
        expect($(".toggled", $el).length).toEqual(0);
        $(".pat-toggle", $el).trigger("click");
        expect($(".toggled", $el).length).toEqual(6);
        $(".pat-toggle", $el).trigger("click");
        expect($(".toggled", $el).length).toEqual(0);
    });

    it("should target itself when no target is specified", function () {
        var $el = $(
            "" +
                "
" +
                ' Button' +
                "
"
        );
        registry.scan($el);
        expect($(".toggled", $el).length).toEqual(0);
        $(".pat-toggle", $el).trigger("click");
        expect($(".toggled", $el).length).toEqual(1);
        $(".pat-toggle", $el).trigger("click");
        expect($(".toggled", $el).length).toEqual(0);
    });

    it("should use the targetScope option to find the target when specified", function () {
        var $el = $(
            "" +
                "
" +
                '
' +
                '    Button' +
                '
' +
                '     Click here to go somewhere else' +
                "
" +
                "
" +
                '
' +
                '    Button' +
                '
' +
                '     Click here to go somewhere else' +
                "
" +
                "
" +
                "
"
        );
        registry.scan($el);
        var $pattern1 = $el.find(".pat-toggle").first();
        expect($(".toggled", $el).length).toEqual(0);
        $pattern1.trigger("click");
        expect($(".toggled", $el).length).toEqual(1);
        expect($(".parent1 .toggled", $el).length).toEqual(1);
        expect($(".parent2 .toggled", $el).length).toEqual(0);
    });

    it("should use the target when targetScope is global", function () {
        var $el = $(
            "" +
                '
' +
                ' Button' +
                '

' +
                "
"
        ).appendTo("body");
        var $target = $("#target");
        expect($target.hasClass("toggled")).toEqual(false);
        registry.scan($el);
        expect($target.hasClass("toggled")).toEqual(false);
        $el.find(".pat-toggle").first().trigger("click");
        expect($target.hasClass("toggled")).toEqual(true);
        $el.remove();
    });

    it("the targetScope option should also work with other tags like p tag", function () {
        var $el = $(
            "" +
                "
" +
                '
' +
                '    Button' +
                '    Click here to go somewhere else' +
                "
" +
                '
' +
                '    Button' +
                '
Click here to go somewhere else
' +
                "
" +
                "
"
        );
        registry.scan($el);
        var $pattern1 = $el.find(".pat-toggle").first();
        expect($(".toggled", $el).length).toEqual(0);
        $pattern1.trigger("click");
        expect($(".toggled", $el).length).toEqual(1);
        expect($(".parent1 .toggled", $el).length).toEqual(1);
        expect($(".parent2 .toggled", $el).length).toEqual(0);
    });

    it("12 - should throw an error when it cannot find the target", function () {
        var $el = $(
            "" +
                '
' +
                ' Button' +
                '
' +
                '   Click here to go somewhere else' +
                "
" +
                "
"
        );
        const spy_log_error = jest.spyOn(console, "error");
        registry.scan($el);
        expect(spy_log_error).toHaveBeenCalledTimes(1);
        expect(spy_log_error).toHaveBeenCalledWith(
            "pat-toggle:",
            'No target found for "#notarget".'
        );
        spy_log_error.mockRestore();
    });
});
<commit_msg>maint(pat toggle): Fix tests.<commit_after>import "./toggle";
import $ from "jquery";
import logger from "@patternslib/patternslib/src/core/logging";
import registry from "@patternslib/patternslib/src/core/registry";
import { jest } from "@jest/globals";

describe("Toggle", function () {
    beforeEach(function () {
        document.body.innerHTML = `

            Button

              Click here to go somewhere else

        `;
        this.$el = $("#body");
    });

    afterEach(function () {
        document.body.outerHTML = "";
    });

    it("1 - by default toggles on click event", async function () {
        expect($(".toggled", this.$el).length).toEqual(0);

        // scan dom for patterns
        registry.scan(this.$el);
        expect($(".toggled", this.$el).length).toEqual(0);
        $(".pat-toggle", this.$el).trigger("click");
        expect($(".toggled", this.$el).length).toEqual(1);
        $(".pat-toggle", this.$el).trigger("click");
        expect($(".toggled", this.$el).length).toEqual(0);
    });

    it("can also listen to custom event", function () {
        $(".pat-toggle", this.$el).attr(
            "data-pat-toggle",
            "target: #target; targetScope: #body; value: toggled; event: customEvent"
        );
        expect($(".toggled", this.$el).length).toEqual(0);
        registry.scan(this.$el);
        expect($(".toggled", this.$el).length).toEqual(0);
        $(".pat-toggle", this.$el).trigger("customEvent");
        expect($(".toggled", this.$el).length).toEqual(1);
    });

    it("can also toggle custom element attribute", function () {
        $(".pat-toggle", this.$el).attr(
            "data-pat-toggle",
            "target: #target; targetScope: #body; value: toggled; attribute: rel"
        );
        expect($(".toggled", this.$el).length).toEqual(0);
        expect($('[rel="toggled"]', this.$el).length).toEqual(0);
        registry.scan(this.$el);
        expect($('[rel="toggled"]', this.$el).length).toEqual(0);
        expect($(".toggled", this.$el).length).toEqual(0);
        $(".pat-toggle", this.$el).trigger("click");
        expect($(".toggled", this.$el).length).toEqual(0);
        expect($('[rel="toggled"]', this.$el).length).toEqual(1);
        $(".pat-toggle", this.$el).trigger("click");
        expect($('[rel="toggled"]', this.$el).length).toEqual(0);
    });

    it("toggle multiple targets", function () {
        var $el = $(
            "" +
                '
' +
                "
" +
                "
" +
                "
" +
                '        Button' +
                "
" +
                '

' +
                "
" +
                '

' +
                '

' +
                "
" +
                '

' +
                '

' +
                "
"
        );
        registry.scan($el);
        expect($(".toggled", $el).length).toEqual(0);
        $(".pat-toggle", $el).trigger("click");
        expect($(".toggled", $el).length).toEqual(5);
        $(".pat-toggle", $el).trigger("click");
        expect($(".toggled", $el).length).toEqual(0);
    });

    it("if some elements already marked, mark all with first click", function () {
        var $el = $(
            "" +
                '
' +
                "
" +
                "
" +
                "
" +
                '        Button' +
                "
" +
                '

' +
                "
" +
                '

' +
                '

' +
                "
" +
                '

' +
                '

' +
                "
"
        );
        registry.scan($el);
        expect($(".toggled", $el).length).toEqual(3);
        $(".pat-toggle", $el).trigger("click");
        expect($(".toggled", $el).length).toEqual(5);
        $(".pat-toggle", $el).trigger("click");
        expect($(".toggled", $el).length).toEqual(0);
    });

    it("if all elements already marked, unmark all with first click", function () {
        var $el = $(
            "" +
                '
' +
                "
" +
                "
" +
                "
" +
                '        Button' +
                "
" +
                '

' +
                "
" +
                '

' +
                '

' +
                "
" +
                '

' +
                '

' +
                "
"
        );
        registry.scan($el);
        expect($(".toggled", $el).length).toEqual(5);
        $(".pat-toggle", $el).trigger("click");
        expect($(".toggled", $el).length).toEqual(0);
    });

    it("should also be able to mark the toggle itself", function () {
        var $el = $(
            "" +
                '
' +
                "
" +
                "
" +
                "
" +
                '        Button' +
                "
" +
                '

' +
                "
" +
                '

' +
                '

' +
                "
" +
                '

' +
                '

' +
                "
"
        );
        registry.scan($el);
        expect($(".toggled", $el).length).toEqual(0);
        $(".pat-toggle", $el).trigger("click");
        expect($(".toggled", $el).length).toEqual(6);
        $(".pat-toggle", $el).trigger("click");
        expect($(".toggled", $el).length).toEqual(0);
    });

    it("should target itself when no target is specified", function () {
        var $el = $(
            "" +
                "
" +
                ' Button' +
                "
"
        );
        registry.scan($el);
        expect($(".toggled", $el).length).toEqual(0);
        $(".pat-toggle", $el).trigger("click");
        expect($(".toggled", $el).length).toEqual(1);
        $(".pat-toggle", $el).trigger("click");
        expect($(".toggled", $el).length).toEqual(0);
    });

    it("should use the targetScope option to find the target when specified", function () {
        var $el = $(
            "" +
                "
" +
                '
' +
                '    Button' +
                '
' +
                '     Click here to go somewhere else' +
                "
" +
                "
" +
                '
' +
                '    Button' +
                '
' +
                '     Click here to go somewhere else' +
                "
" +
                "
" +
                "
"
        );
        registry.scan($el);
        var $pattern1 = $el.find(".pat-toggle").first();
        expect($(".toggled", $el).length).toEqual(0);
        $pattern1.trigger("click");
        expect($(".toggled", $el).length).toEqual(1);
        expect($(".parent1 .toggled", $el).length).toEqual(1);
        expect($(".parent2 .toggled", $el).length).toEqual(0);
    });

    it("should use the target when targetScope is global", function () {
        var $el = $(
            "" +
                '
' +
                ' Button' +
                '

' +
                "
"
        ).appendTo("body");
        var $target = $("#target");
        expect($target.hasClass("toggled")).toEqual(false);
        registry.scan($el);
        expect($target.hasClass("toggled")).toEqual(false);
        $el.find(".pat-toggle").first().trigger("click");
        expect($target.hasClass("toggled")).toEqual(true);
        $el.remove();
    });

    it("the targetScope option should also work with other tags like p tag", function () {
        var $el = $(
            "" +
                "
" +
                '
' +
                '    Button' +
                '    Click here to go somewhere else' +
                "
" +
                '
' +
                '    Button' +
                '
Click here to go somewhere else
' +
                "
" +
                "
"
        );
        registry.scan($el);
        var $pattern1 = $el.find(".pat-toggle").first();
        expect($(".toggled", $el).length).toEqual(0);
        $pattern1.trigger("click");
        expect($(".toggled", $el).length).toEqual(1);
        expect($(".parent1 .toggled", $el).length).toEqual(1);
        expect($(".parent2 .toggled", $el).length).toEqual(0);
    });

    it("12 - should throw an error when it cannot find the target", function () {
        var $el = $(
            "" +
                '
' +
                ' Button' +
                '
' +
                '   Click here to go somewhere else' +
                "
" +
                "
"
        );
        const log = logger.getLogger("pat-toggle");
        const spy_log_error = jest.spyOn(log, "error");
        registry.scan($el);
        expect(spy_log_error).toHaveBeenCalledTimes(1);
        expect(spy_log_error).toHaveBeenCalledWith(
            'No target found for "#notarget".'
        );
        spy_log_error.mockRestore();
    });
});
<|endoftext|>


<commit_before>// openSeadragonApp: Defines the Angular application ==================================================================================================================================
var openSeadragonApp = angular.module('openSeadragonApp', ['ERMrest']);

// ERMrestService: A service for operations that deal with the ERMrest db =============================================================================================================
openSeadragonApp.service('ERMrestService', ['ermrestClientFactory', '$http', function(ermrestClientFactory, $http) {
    // Get a reference to the ERMrestService service
    var self = this;

    // Parse Chaise url to determine required parameters to find the requested entity
    this.path = window.location.hash;
    this.params = this.path.split('/');
    this.catalogId = this.params[0].substring(1);
    this.schemaName = this.params[1].split(':')[0];
    this.tableName = this.params[1].split(':')[1];
    this.entityId = this.params[2].split('=')[1];

    // The name of the table where the annotations are stored — currently 'roi'
    this.annotationTableName = 'annotation';
    this.anatomyTableName = 'anatomy';

    var ERMREST_ENDPOINT = window.location.origin + '/ermrest/catalog/';
    if (chaiseConfig['ermrestLocation'] != null) {
        ERMREST_ENDPOINT = chaiseConfig['ermrestLocation'] + '/ermrest/catalog';
    }

    var client = ermrestClientFactory.getClient(window.location.origin + '/ermrest', null);
    var catalog = client.getCatalog(1);

    // Returns a Schema object from ERMrest
    this.getSchema = function getSchema() {
        return catalog.introspect().then(function(schemas) {
            return schemas[self.schemaName];
        });
    };

    // Returns the uri value from a row in rbk:image (filtered by the id found in URL)
    this.getEntity = function getEntity() {
        return this.getSchema().then(function(schema) {
            var table = schema.getTable(self.tableName);
            var filteredTable = table.getFilteredTable(["id=" + self.entityId]);
            return filteredTable.getEntities().then(function(rows) {
                return rows[0].data.uri;
            });
        });
    };

    this.createAnnotation = function createAnnotation(annotation) {
        if (annotation.anatomy === "") {
            annotation.anatomy = null;
        }

        annotation = [{
            "image_id": self.entityId,
            "anatomy": annotation.anatomy,
            "context_uri": annotation.context_uri,
            "coords": [
                annotation.shape.geometry.x,
                annotation.shape.geometry.y,
                annotation.shape.geometry.width,
                annotation.shape.geometry.height
            ],
            "description": annotation.description
        }];
        return this.getSchema().then(function(schema) {
            var table = schema.getTable(self.annotationTableName);
            return table.createEntity(annotation, ['id', 'author', 'created']).then(function(response) {
                return response;
            });
        });
    };

    this.updateAnnotation = function updateAnnotation(annotation) {
        if (annotation.anatomy === "") {
            annotation.anatomy = null;
        }

        var editedAnnotation = [{
            "id": annotation.id,
            "image_id": self.entityId,
            "author": null,
            "created": annotation.created,
            "context_uri": annotation.context_uri,
            "coords": annotation.coords,
            "description": annotation.description,
            "anatomy": annotation.anatomy
        }];
        var entityPath = ERMREST_ENDPOINT + this.catalogId + '/entity/' + this.schemaName + ':' + this.annotationTableName;
        return $http.put(entityPath, editedAnnotation);
    };

    this.getAnnotations = function getAnnotations() {
        var annotations = [];
        this.getSchema().then(function(schema) {
            var table = schema.getTable(self.annotationTableName);
            var filteredTable = table.getFilteredTable(["image_id=" + self.entityId]);
            return filteredTable.getEntities().then(function(rows) {
                if (rows.length > 0) {
                    return Promise.all(rows.map(function(row) {
                        annotations.push(row.data);
                    }));
                } else {
                    console.log('No annotations found for this image.')
                }
            });
        });
        return annotations;
    };

    this.deleteAnnotation = function deleteAnnotation(annotation) {
        this.getSchema().then(function(schema) {
            var table = schema.getTable(self.annotationTableName);
            var filteredTable = table.getFilteredTable(["id=" + annotation.id]);
            return filteredTable.getEntities().then(function(rows) {
                rows[0].delete();
            });
        });
    };

    this.getAnatomies = function getAnatomies() {
        var anatomies = [];
        this.getSchema().then(function(schema) {
            var table = schema.getTable(self.anatomyTableName);
            table.getEntities().then(function(rows) {
                if (rows.length > 0) {
                    return Promise.all(rows.map(function(row) {
                        anatomies.push(row.data.term);
                    }));
                } else {
                    return rows;
                }
            });
        });
        return anatomies;
    };
}]);

// MainController: An Angular controller to update the view ===========================================================================================================================
openSeadragonApp.controller('MainController', ['$scope', '$window', 'ERMrestService', function($scope, $window, ERMrestService) {
    $scope.annotations = ERMrestService.getAnnotations();
    $scope.anatomies = ERMrestService.getAnatomies(); // An array of elements, where each element is a string of an anatomy term
    $scope.viewerSource = null; // The source URL of the iframe/viewer
    $scope.viewerReady = false; // True if viewer (OpenSeadragon/Annotorious) has finished setup
    $scope.viewer = null; // A reference to the iframe window

    $scope.highlightedAnnotation = null; // Track which annotation is highlighted/centered right now

    $scope.createMode = false; // True if user is currently creating a new annotation
    $scope.newAnnotation = null; // Holds the data for a new annotation as it's being created

    $scope.editedAnnotation = null; // Track which one is being edited right now; used to show/hide the right UI elements depending on which one is being edited.
    $scope.originalAnnotation = null; // Holds the original contents of annotation in the event that a user cancels an edit

    // Fetch uri from image table to load OpenSeadragon
    ERMrestService.getEntity().then(function(uri) {
        // TODO: Remove me after pushing to vm-wide version of OpenSeadragon ///////////////
        // Splicing in my ~jessie directory in here so it redirects to my own version of OpenSeadragon and not the VM-wide version..
        uri = uri.substring(0, 34) + '~jessie/' + uri.substring(34);
        ///////////////////////////////////////////////////////////////////////////////////
        // Initialize OpenSeadragon with the uri
        $scope.viewerSource = uri;
    });

    // Listen for events from OpenSeadragon/iframe
    $window.addEventListener('message', function(event) {
        if (event.origin === window.location.origin) {
            var data = event.data;
            var messageType = data.messageType;
            switch (messageType) {
                case 'annotoriousReady':
                    $scope.viewerReady = data.content;
                    if ($scope.viewerReady) {
                        $scope.viewer = window.frames[0];
                        $scope.viewer.postMessage({messageType: 'loadAnnotations', content: $scope.annotations}, window.location.origin);
                    }
                    break;
                case 'annotationDrawn':
                    $scope.newAnnotation = {
                        description: null,
                        shape: data.content.shape
                    };
                    $scope.$apply(function() {
                        $scope.createMode = true;
                    });
                    break;
                default:
                    console.log('Invalid message type. No action performed.');
            }
        } else {
            console.log('Invalid event origin. Event origin: ', origin, '. Expected origin: ', window.location.origin);
        }
    });

    $scope.setHighlightedAnnotation = function setHighlightedAnnotation(annotationId) {
        $scope.highlightedAnnotation = annotationId;
    };

    $scope.highlightAnnotation = function highlightAnnotation(annotation) {
        $scope.viewer.postMessage({messageType: 'highlightAnnotation', content: annotation}, window.location.origin);
    };

    // Activates the drawing selector tool in Annotorious
    $scope.drawAnnotation = function drawAnnotation() {
        $scope.viewer.postMessage({messageType: 'drawAnnotation'}, window.location.origin);
    };

    // Stop creating an annotation: Hides the forms used to create an annotation in Chaise and Annotorious
    $scope.cancelAnnotationCreation = function cancelAnnotationCreation() {
        $scope.createMode = false;
        $scope.viewer.postMessage({messageType: 'cancelAnnotationCreation'}, window.location.origin);
    };

    // Given the new annotation data, it creates the annotation in Annotorious and ERMrest
    $scope.createAnnotation = function createAnnotation(newAnnotation) {
        $scope.createMode = false;
        newAnnotation.context_uri = $scope.viewerSource;
        // Create new annotation in ERMrest
        return ERMrestService.createAnnotation(newAnnotation).then(function(response) {
            if (response.length > 0) {
                newAnnotation = response[0];
                // Send new annotation to Annotorious
                $scope.viewer.postMessage({messageType: 'createAnnotation', content: newAnnotation}, window.location.origin);
                // Add new annotation to controller scope
                $scope.annotations.push(newAnnotation);
                $scope.newAnnotation = null;
                return newAnnotation;
            } else {
                return response;
            }
        });
    }

    // Sets the selected annotation to edit mode
    $scope.editAnnotation = function editAnnotation(annotation) {
        $scope.editedAnnotation = annotation.id;
        $scope.originalAnnotation = annotation;
    };

    // Stop editing an annotation
    $scope.cancelAnnotationEdit = function cancelAnnotationEdit(annotation) {
        $scope.editedAnnotation = null;
        annotation = $scope.originalAnnotation;
    };

    // Given the updated annotation data, it updates the annotation in Annotorious and ERMrest
    $scope.saveAnnotation = function saveAnnotation(annotation) {
        $scope.editedAnnotation = null;
        $scope.viewer.postMessage({messageType: 'updateAnnotation', content: annotation}, window.location.origin);
        ERMrestService.updateAnnotation(annotation);
    }

    $scope.deleteAnnotation = function deleteAnnotation(annotation) {
        var index = $scope.annotations.indexOf(annotation);
        $scope.annotations.splice(index, 1);
        $scope.viewer.postMessage({messageType: 'deleteAnnotation', content: annotation}, window.location.origin);
        ERMrestService.deleteAnnotation(annotation);
    };
}]);

// Trusted: A filter that tells Angular when a url is trusted =========================================================================================================================
openSeadragonApp.filter('trusted', ['$sce', function($sce) {
    return function(url) {
        return $sce.trustAsResourceUrl(url);
    };
}]);

// Refreshes page when the window's hash changes
window.onhashchange = function() {
    if (window.location.hash != '#undefined') {
        location.reload();
    } else {
        history.replaceState("", document.title, window.location.pathname);
        location.reload();
    }
    function goBack() {
        window.location.hash = window.location.lasthash[window.location.lasthash.length-1];
        window.location.lasthash.pop();
    }
}
<commit_msg>Capitalize first letter of each word with a "capitalize" filter<commit_after>// openSeadragonApp: Defines the Angular application ==================================================================================================================================
var openSeadragonApp = angular.module('openSeadragonApp', ['ERMrest']);

// ERMrestService: A service for operations that deal with the ERMrest db =============================================================================================================
openSeadragonApp.service('ERMrestService', ['ermrestClientFactory', '$http', function(ermrestClientFactory, $http) {
    // Get a reference to the ERMrestService service
    var self = this;

    // Parse Chaise url to determine required parameters to find the requested entity
    this.path = window.location.hash;
    this.params = this.path.split('/');
    this.catalogId = this.params[0].substring(1);
    this.schemaName = this.params[1].split(':')[0];
    this.tableName = this.params[1].split(':')[1];
    this.entityId = this.params[2].split('=')[1];

    // The name of the table where the annotations are stored — currently 'roi'
    this.annotationTableName = 'annotation';
    this.anatomyTableName = 'anatomy';

    var ERMREST_ENDPOINT = window.location.origin + '/ermrest/catalog/';
    if (chaiseConfig['ermrestLocation'] != null) {
        ERMREST_ENDPOINT = chaiseConfig['ermrestLocation'] + '/ermrest/catalog';
    }

    var client = ermrestClientFactory.getClient(window.location.origin + '/ermrest', null);
    var catalog = client.getCatalog(1);

    // Returns a Schema object from ERMrest
    this.getSchema = function getSchema() {
        return catalog.introspect().then(function(schemas) {
            return schemas[self.schemaName];
        });
    };

    // Returns the uri value from a row in rbk:image (filtered by the id found in URL)
    this.getEntity = function getEntity() {
        return this.getSchema().then(function(schema) {
            var table = schema.getTable(self.tableName);
            var filteredTable = table.getFilteredTable(["id=" + self.entityId]);
            return filteredTable.getEntities().then(function(rows) {
                return rows[0].data.uri;
            });
        });
    };

    this.createAnnotation = function createAnnotation(annotation) {
        if (annotation.anatomy === "") {
            annotation.anatomy = null;
        }

        annotation = [{
            "image_id": self.entityId,
            "anatomy": annotation.anatomy,
            "context_uri": annotation.context_uri,
            "coords": [
                annotation.shape.geometry.x,
                annotation.shape.geometry.y,
                annotation.shape.geometry.width,
                annotation.shape.geometry.height
            ],
            "description": annotation.description
        }];
        return this.getSchema().then(function(schema) {
            var table = schema.getTable(self.annotationTableName);
            return table.createEntity(annotation, ['id', 'author', 'created']).then(function(response) {
                return response;
            });
        });
    };

    this.updateAnnotation = function updateAnnotation(annotation) {
        if (annotation.anatomy === "") {
            annotation.anatomy = null;
        }

        var editedAnnotation = [{
            "id": annotation.id,
            "image_id": self.entityId,
            "author": null,
            "created": annotation.created,
            "context_uri": annotation.context_uri,
            "coords": annotation.coords,
            "description": annotation.description,
            "anatomy": annotation.anatomy
        }];
        var entityPath = ERMREST_ENDPOINT + this.catalogId + '/entity/' + this.schemaName + ':' + this.annotationTableName;
        return $http.put(entityPath, editedAnnotation);
    };

    this.getAnnotations = function getAnnotations() {
        var annotations = [];
        this.getSchema().then(function(schema) {
            var table = schema.getTable(self.annotationTableName);
            var filteredTable = table.getFilteredTable(["image_id=" + self.entityId]);
            return filteredTable.getEntities().then(function(rows) {
                if (rows.length > 0) {
                    return Promise.all(rows.map(function(row) {
                        annotations.push(row.data);
                    }));
                } else {
                    console.log('No annotations found for this image.')
                }
            });
        });
        return annotations;
    };

    this.deleteAnnotation = function deleteAnnotation(annotation) {
        this.getSchema().then(function(schema) {
            var table = schema.getTable(self.annotationTableName);
            var filteredTable = table.getFilteredTable(["id=" + annotation.id]);
            return filteredTable.getEntities().then(function(rows) {
                rows[0].delete();
            });
        });
    };

    this.getAnatomies = function getAnatomies() {
        var anatomies = [];
        this.getSchema().then(function(schema) {
            var table = schema.getTable(self.anatomyTableName);
            table.getEntities().then(function(rows) {
                if (rows.length > 0) {
                    return Promise.all(rows.map(function(row) {
                        anatomies.push(row.data.term);
                    }));
                } else {
                    return rows;
                }
            });
        });
        return anatomies;
    };
}]);

// MainController: An Angular controller to update the view ===========================================================================================================================
openSeadragonApp.controller('MainController', ['$scope', '$window', 'ERMrestService', function($scope, $window, ERMrestService) {
    $scope.annotations = ERMrestService.getAnnotations();
    $scope.anatomies = ERMrestService.getAnatomies(); // An array of elements, where each element is a string of an anatomy term
    $scope.viewerSource = null; // The source URL of the iframe/viewer
    $scope.viewerReady = false; // True if viewer (OpenSeadragon/Annotorious) has finished setup
    $scope.viewer = null; // A reference to the iframe window

    $scope.highlightedAnnotation = null; // Track which annotation is highlighted/centered right now

    $scope.createMode = false; // True if user is currently creating a new annotation
    $scope.newAnnotation = null; // Holds the data for a new annotation as it's being created

    $scope.editedAnnotation = null; // Track which one is being edited right now; used to show/hide the right UI elements depending on which one is being edited.
    $scope.originalAnnotation = null; // Holds the original contents of annotation in the event that a user cancels an edit

    // Fetch uri from image table to load OpenSeadragon
    ERMrestService.getEntity().then(function(uri) {
        // TODO: Remove me after pushing to vm-wide version of OpenSeadragon ///////////////
        // Splicing in my ~jessie directory in here so it redirects to my own version of OpenSeadragon and not the VM-wide version..
        uri = uri.substring(0, 34) + '~jessie/' + uri.substring(34);
        ///////////////////////////////////////////////////////////////////////////////////
        // Initialize OpenSeadragon with the uri
        $scope.viewerSource = uri;
    });

    // Listen for events from OpenSeadragon/iframe
    $window.addEventListener('message', function(event) {
        if (event.origin === window.location.origin) {
            var data = event.data;
            var messageType = data.messageType;
            switch (messageType) {
                case 'annotoriousReady':
                    $scope.viewerReady = data.content;
                    if ($scope.viewerReady) {
                        $scope.viewer = window.frames[0];
                        $scope.viewer.postMessage({messageType: 'loadAnnotations', content: $scope.annotations}, window.location.origin);
                    }
                    break;
                case 'annotationDrawn':
                    $scope.newAnnotation = {
                        description: null,
                        shape: data.content.shape
                    };
                    $scope.$apply(function() {
                        $scope.createMode = true;
                    });
                    break;
                default:
                    console.log('Invalid message type. No action performed.');
            }
        } else {
            console.log('Invalid event origin. Event origin: ', origin, '. Expected origin: ', window.location.origin);
        }
    });

    $scope.setHighlightedAnnotation = function setHighlightedAnnotation(annotationId) {
        $scope.highlightedAnnotation = annotationId;
    };

    $scope.highlightAnnotation = function highlightAnnotation(annotation) {
        $scope.viewer.postMessage({messageType: 'highlightAnnotation', content: annotation}, window.location.origin);
    };

    // Activates the drawing selector tool in Annotorious
    $scope.drawAnnotation = function drawAnnotation() {
        $scope.viewer.postMessage({messageType: 'drawAnnotation'}, window.location.origin);
    };

    // Stop creating an annotation: Hides the forms used to create an annotation in Chaise and Annotorious
    $scope.cancelAnnotationCreation = function cancelAnnotationCreation() {
        $scope.createMode = false;
        $scope.viewer.postMessage({messageType: 'cancelAnnotationCreation'}, window.location.origin);
    };

    // Given the new annotation data, it creates the annotation in Annotorious and ERMrest
    $scope.createAnnotation = function createAnnotation(newAnnotation) {
        $scope.createMode = false;
        newAnnotation.context_uri = $scope.viewerSource;
        // Create new annotation in ERMrest
        return ERMrestService.createAnnotation(newAnnotation).then(function(response) {
            if (response.length > 0) {
                newAnnotation = response[0];
                // Send new annotation to Annotorious
                $scope.viewer.postMessage({messageType: 'createAnnotation', content: newAnnotation}, window.location.origin);
                // Add new annotation to controller scope
                $scope.annotations.push(newAnnotation);
                $scope.newAnnotation = null;
                return newAnnotation;
            } else {
                return response;
            }
        });
    }

    // Sets the selected annotation to edit mode
    $scope.editAnnotation = function editAnnotation(annotation) {
        $scope.editedAnnotation = annotation.id;
        $scope.originalAnnotation = annotation;
    };

    // Stop editing an annotation
    $scope.cancelAnnotationEdit = function cancelAnnotationEdit(annotation) {
        $scope.editedAnnotation = null;
        annotation = $scope.originalAnnotation;
    };

    // Given the updated annotation data, it updates the annotation in Annotorious and ERMrest
    $scope.saveAnnotation = function saveAnnotation(annotation) {
        $scope.editedAnnotation = null;
        $scope.viewer.postMessage({messageType: 'updateAnnotation', content: annotation}, window.location.origin);
        ERMrestService.updateAnnotation(annotation);
    }

    $scope.deleteAnnotation = function deleteAnnotation(annotation) {
        var index = $scope.annotations.indexOf(annotation);
        $scope.annotations.splice(index, 1);
        $scope.viewer.postMessage({messageType: 'deleteAnnotation', content: annotation}, window.location.origin);
        ERMrestService.deleteAnnotation(annotation);
    };
}]);

// Trusted: A filter that tells Angular when a url is trusted =========================================================================================================================
openSeadragonApp.filter('trusted', ['$sce', function($sce) {
    return function(url) {
        return $sce.trustAsResourceUrl(url);
    };
}]);

// Capitalize: A filter that capitalizes the first character for each word in a string ================================================================================================
openSeadragonApp.filter('capitalize', function() {
    return function(input, all) {
        var regex = (all) ? /([^\W_]+[^\s-]*) */g : /([^\W_]+[^\s-]*)/;
        return (!!input) ? input.replace(regex, function(txt) {
            return txt.charAt(0).toUpperCase() + txt.substr(1).toLowerCase();
        }) : '';
    }
});

// Refreshes page when the window's hash changes
window.onhashchange = function() {
    if (window.location.hash != '#undefined') {
        location.reload();
    } else {
        history.replaceState("", document.title, window.location.pathname);
        location.reload();
    }
    function goBack() {
        window.location.hash = window.location.lasthash[window.location.lasthash.length-1];
        window.location.lasthash.pop();
    }
}
<|endoftext|>


<commit_before>const PLUGIN_NAME = 'stub';

module.exports = function (opts, app, handlerParser) {
  const server = require('http').createServer(app);
  const io = require('socket.io')(server);

  io.on('connection', function (socket) {
    console.log('connection ' + socket.id);

    socket.on('emitStub', function (data) {
      // broadcast.emit 会广播给其他连接了 websocket 的用户，但是不会广播给自己
      // 因此需要额外的 emit 发给自己以便校验
      // 必须要一次 broadcast.emit，因为我们是在 matman 系统进行操作然后广播给其他服务的
      // 必须要一次 emit，因为要进行校验
      socket.broadcast.emit(data.route, data.result);
      socket.emit(data.route, data.result);
    });

    let stubList = handlerParser.getHandlerListByPlugin(PLUGIN_NAME);
    // console.log('--stubList--', stubList);

    stubList.forEach((stubItem) => {
      const SOCKET_ROUTE = stubItem.route;
      console.log('==========SOCKET_ROUTE===========', SOCKET_ROUTE);

      // 每一个 stub 都监听其特定的消息
      // TODO 此处需要确认如果有多个同样的 SOCKET_ROUTE，则会发生什么事情，是否需要程序进行提示？
      socket.on(SOCKET_ROUTE, function (...args) {
        console.log(Date.now(), SOCKET_ROUTE, socket.id, args);

        // TODO 此处应该可以支持任意的参数
        handlerParser.getHandleModuleResult(SOCKET_ROUTE, args)
          .then((result) => {
            socket.emit(SOCKET_ROUTE, result);
          })
          .catch((err) => {
            socket.emit('stub_error!', err);
          });
      });
    });

    // when the user disconnects.. perform this
    socket.on('disconnect', function () {
      console.log('disconnect ' + socket.id);
    });
  });

  return server;
};
<commit_msg>fix: 修复传递值错误的bug #65<commit_after>const PLUGIN_NAME = 'stub';

module.exports = function (opts, app, handlerParser) {
  const server = require('http').createServer(app);
  const io = require('socket.io')(server);

  io.on('connection', function (socket) {
    console.log('connection ' + socket.id);

    socket.on('emitStub', function (data) {
      // broadcast.emit 会广播给其他连接了 websocket 的用户，但是不会广播给自己
      // 因此需要额外的 emit 发给自己以便校验
      // 必须要一次 broadcast.emit，因为我们是在 matman 系统进行操作然后广播给其他服务的
      // 必须要一次 emit，因为要进行校验
      socket.broadcast.emit(data.route, data.result);
      socket.emit(data.route, data.result);
    });

    let stubList = handlerParser.getHandlerListByPlugin(PLUGIN_NAME);
    // console.log('--stubList--', stubList);

    stubList.forEach((stubItem) => {
      const SOCKET_ROUTE = stubItem.route;
      console.log('==========SOCKET_ROUTE===========', SOCKET_ROUTE);

      // 每一个 stub 都监听其特定的消息
      // TODO 此处需要确认如果有多个同样的 SOCKET_ROUTE，则会发生什么事情，是否需要程序进行提示？
      socket.on(SOCKET_ROUTE, function (...args) {
        console.log(Date.now(), SOCKET_ROUTE, socket.id, args);

        // TODO 此处应该可以支持任意的参数
        handlerParser.getHandleModuleResult(SOCKET_ROUTE, ...args)
          .then((result) => {
            socket.emit(SOCKET_ROUTE, result);
          })
          .catch((err) => {
            socket.emit('stub_error!', err);
          });
      });
    });

    // when the user disconnects.. perform this
    socket.on('disconnect', function () {
      console.log('disconnect ' + socket.id);
    });
  });

  return server;
};
<|endoftext|>


<commit_before>const PLUGIN_NAME = 'stub';

module.exports = function (opts, app, handlerParser) {
  const server = require('http').createServer(app);
  const io = require('socket.io')(server);

  io.on('connection', function (socket) {
    console.log('connection ' + socket.id);

    socket.on('emitStub', function (data) {
      // broadcast.emit 会广播给其他连接了 websocket 的用户，但是不会广播给自己
      // 因此需要额外的 emit 发给自己以便校验
      // 必须要一次 broadcast.emit，因为我们是在 matman 系统进行操作然后广播给其他服务的
      // 必须要一次 emit，因为要进行校验
      socket.broadcast.emit(data.route, data.result);
      socket.emit(data.route, data.result);
    });

    let stubList = handlerParser.getHandlerListByPlugin(PLUGIN_NAME);
    // console.log('--stubList--', stubList);

    stubList.forEach((stubItem) => {
      const SOCKET_ROUTE = stubItem.route;
      // console.log('==========SOCKET_ROUTE===========', SOCKET_ROUTE);

      // 每一个 stub 都监听其特定的消息
      // TODO 此处需要确认如果有多个同样的 SOCKET_ROUTE，则会发生什么事情，是否需要程序进行提示？
      socket.on(SOCKET_ROUTE, function (params, opts = {}) {
        console.log(Date.now(), stubItem.disable, SOCKET_ROUTE, socket.id, params, opts);

        // 如果该项打桩为 disable，则不做任何处理
        if (stubItem.disable) {
          return;
        }

        // TODO 此处应该可以支持任意的参数
        handlerParser.getHandleModuleResult(SOCKET_ROUTE, params)
          .then((result) => {
            socket.emit(opts.eventName || SOCKET_ROUTE, result);
          })
          .catch((err) => {
            socket.emit('stub_error', err);
          });
      });
    });

    // when the user disconnects.. perform this
    socket.on('disconnect', function () {
      console.log('disconnect ' + socket.id);
    });
  });

  return server;
};
<commit_msg>feat: 为websocket增加跨域支持 #72<commit_after>const PLUGIN_NAME = 'stub';

module.exports = function (opts, app, handlerParser) {
  const server = require('http').createServer(app);
  const io = require('socket.io')(server);

  // io.set('transports', ['websocket', 'xhr-polling', 'jsonp-polling', 'htmlfile', 'flashsocket']);
  io.set('origins', '*:*');

  io.on('connection', function (socket) {
    console.log('connection ' + socket.id);

    socket.on('emitStub', function (data) {
      // broadcast.emit 会广播给其他连接了 websocket 的用户，但是不会广播给自己
      // 因此需要额外的 emit 发给自己以便校验
      // 必须要一次 broadcast.emit，因为我们是在 matman 系统进行操作然后广播给其他服务的
      // 必须要一次 emit，因为要进行校验
      socket.broadcast.emit(data.route, data.result);
      socket.emit(data.route, data.result);
    });

    let stubList = handlerParser.getHandlerListByPlugin(PLUGIN_NAME);
    // console.log('--stubList--', stubList);

    stubList.forEach((stubItem) => {
      const SOCKET_ROUTE = stubItem.route;
      // console.log('==========SOCKET_ROUTE===========', SOCKET_ROUTE);

      // 每一个 stub 都监听其特定的消息
      // TODO 此处需要确认如果有多个同样的 SOCKET_ROUTE，则会发生什么事情，是否需要程序进行提示？
      socket.on(SOCKET_ROUTE, function (params, opts = {}) {
        console.log(Date.now(), stubItem.disable, SOCKET_ROUTE, socket.id, params, opts);

        // 如果该项打桩为 disable，则不做任何处理
        if (stubItem.disable) {
          return;
        }

        // TODO 此处应该可以支持任意的参数
        handlerParser.getHandleModuleResult(SOCKET_ROUTE, params)
          .then((result) => {
            socket.emit(opts.eventName || SOCKET_ROUTE, result);
          })
          .catch((err) => {
            socket.emit('stub_error', err);
          });
      });
    });

    // when the user disconnects.. perform this
    socket.on('disconnect', function () {
      console.log('disconnect ' + socket.id);
    });
  });

  return server;
};
<|endoftext|>


<commit_before>/**
 * All About Olaf
 * iOS Home page
 */

import React from 'react'
import {
  Navigator,
  ScrollView,
  StyleSheet,
  Text,
  TouchableOpacity,
  View,
} from 'react-native'
import NavigatorScreen from './components/navigator-screen'
import Icon from 'react-native-vector-icons/Entypo'

const views = [
  {usable: true, view: 'MenusView', title: 'Menus', icon: 'bowl'},
  {usable: false, view: 'SISView', title: 'SIS', icon: 'fingerprint'},
  {usable: true, view: 'BuildingHoursView', title: 'Building Hours', icon: 'clock'},
  {usable: true, view: 'CalendarView', title: 'Calendar', icon: 'calendar'},
  {usable: false, view: 'DirectoryView', title: 'Directory', icon: 'v-card'},
  {usable: false, view: 'StreamingView', title: 'Streaming Media', icon: 'video'},
  {usable: false, view: 'NewsView', title: 'News', icon: 'news'},
  {usable: false, view: 'MapView', title: 'Campus Map', icon: 'map'},
  {usable: true, view: 'ContactsView', title: 'Important Contacts', icon: 'phone'},
  {usable: false, view: 'TransportationView', title: 'Transportation', icon: 'address'},
  {usable: true, view: 'DictionaryView', title: 'Campus Dictionary', icon: 'open-book'},
]

export default class HomePage extends React.Component {
  render() {
    return <NavigatorScreen
      {...this.props}
      title="All About Olaf"
      renderScene={this.renderScene.bind(this)}
      leftButton={(route, navigator) => {
        return (
          <TouchableOpacity
            style={styles.navButton}
            onPress={() => navigator.parentNavigator.pop()}
          >
            <Icon name='info' style={styles.navigationButtonIcon} />
          </TouchableOpacity>
        )
      }}
      rightButton={(route, navigator) => {
        return (
          <TouchableOpacity
            style={styles.navButton}
            onPress={() => navigator.parentNavigator.pop()}
          >
            <Text style={styles.navigationButtonText}>Edit</Text>
          </TouchableOpacity>
        )
      }}
    />
  }

  // Go to request page
  pushView(view, viewTitle) {
    this.props.navigator.push({
      id: view,
      title: viewTitle,
      sceneConfig: Navigator.SceneConfigs.FloatFromRight,
    })
  }

  // Render a given scene
  renderScene() {
    return (
      <ScrollView
        automaticallyAdjustContentInsets={false}
        scrollEventThrottle={200}
        overflow={'hidden'}
        alwaysBounceHorizontal={false}
        showsHorizontalScrollIndicator={false}
        showsVerticalScrollIndicator={false}
        style={styles.scrollView}
      >
        <View style={styles.container}>
          {views.map(view =>
            <TouchableOpacity
              key={view.title}
              onPress={() => this.pushView(view.view, view.title)}
              activeOpacity={0.5}
            >
              <View style={[styles.rectangle, styles[`${view.view}Button`]]}>
                <Icon name={view.icon} size={32} style={styles.rectangleButtonIcon} />
                <Text
                  style={styles.rectangleButtonText}
                  autoAdjustsFontSize={true}
                >
                  {view.title}{view.usable ? '' : ' !'}
                </Text>
              </View>
            </TouchableOpacity>
          )}
        </View>
      </ScrollView>
    )
  }
}

import * as c from './components/colors'

const Dimensions = require('Dimensions')
let Viewport = Dimensions.get('window')

let marginTop = 15

let cellMargin = 10
let cellSidePadding = 10
let cellEdgePadding = 10
let cellWidth = (Viewport.width / 2) - (cellMargin * 1.5)

var styles = StyleSheet.create({
  // Body container
  container: {
    flex: 1,
    marginLeft: cellMargin,
    marginTop: marginTop,

    justifyContent: 'center',
    flexDirection: 'row',
    flexWrap: 'wrap',
  },

  // Main buttons for actions on home screen
  rectangle: {
    width: cellWidth,
    alignItems: 'center',
    paddingTop: cellSidePadding,
    paddingBottom: cellSidePadding,
    paddingRight: cellEdgePadding,
    paddingLeft: cellEdgePadding,
    borderRadius: 6,

    marginBottom: cellMargin,
    marginRight: cellMargin,
  },

  MenusViewButton: {
    backgroundColor: c.emerald,
  },
  SISViewButton: {
    backgroundColor: c.goldenrod,
  },
  BuildingHoursViewButton: {
    backgroundColor: c.wave,
  },
  CalendarViewButton: {
    backgroundColor: c.coolPurple,
  },
  DirectoryViewButton: {
    backgroundColor: c.indianRed,
  },
  StreamingViewButton: {
    backgroundColor: c.denim,
  },
  NewsViewButton: {
    backgroundColor: c.eggplant,
  },
  MapViewButton: {
    backgroundColor: c.coffee,
  },
  ContactsViewButton: {
    backgroundColor: c.crimson,
  },
  TransportationViewButton: {
    backgroundColor: c.cardTable,
  },
  DictionaryViewButton: {
    backgroundColor: c.olive,
  },

  navigationButtonText: {
    color: c.mandarin,
  },
  navigationButtonIcon: {
    color: c.mandarin,
    fontSize: 20,
    marginTop: 8,
    marginLeft: 14,
  },

  // Text styling in buttons
  rectangleButtonIcon: {
    color: c.white,
  },
  rectangleButtonText: {
    color: c.white,
    textAlign: 'center',
    fontSize: 14,
  },
})
<commit_msg>accommodate a breaking change in rn@0.28 wrt/flexWrap and alignItems<commit_after>/**
 * All About Olaf
 * iOS Home page
 */

import React from 'react'
import {
  Navigator,
  ScrollView,
  StyleSheet,
  Text,
  TouchableOpacity,
  View,
} from 'react-native'
import NavigatorScreen from './components/navigator-screen'
import Icon from 'react-native-vector-icons/Entypo'

const views = [
  {usable: true, view: 'MenusView', title: 'Menus', icon: 'bowl'},
  {usable: false, view: 'SISView', title: 'SIS', icon: 'fingerprint'},
  {usable: true, view: 'BuildingHoursView', title: 'Building Hours', icon: 'clock'},
  {usable: true, view: 'CalendarView', title: 'Calendar', icon: 'calendar'},
  {usable: false, view: 'DirectoryView', title: 'Directory', icon: 'v-card'},
  {usable: false, view: 'StreamingView', title: 'Streaming Media', icon: 'video'},
  {usable: false, view: 'NewsView', title: 'News', icon: 'news'},
  {usable: false, view: 'MapView', title: 'Campus Map', icon: 'map'},
  {usable: true, view: 'ContactsView', title: 'Important Contacts', icon: 'phone'},
  {usable: false, view: 'TransportationView', title: 'Transportation', icon: 'address'},
  {usable: true, view: 'DictionaryView', title: 'Campus Dictionary', icon: 'open-book'},
]

export default class HomePage extends React.Component {
  render() {
    return <NavigatorScreen
      {...this.props}
      title="All About Olaf"
      renderScene={this.renderScene.bind(this)}
      leftButton={(route, navigator) => {
        return (
          <TouchableOpacity
            style={styles.navButton}
            onPress={() => navigator.parentNavigator.pop()}
          >
            <Icon name='info' style={styles.navigationButtonIcon} />
          </TouchableOpacity>
        )
      }}
      rightButton={(route, navigator) => {
        return (
          <TouchableOpacity
            style={styles.navButton}
            onPress={() => navigator.parentNavigator.pop()}
          >
            <Text style={styles.navigationButtonText}>Edit</Text>
          </TouchableOpacity>
        )
      }}
    />
  }

  // Go to request page
  pushView(view, viewTitle) {
    this.props.navigator.push({
      id: view,
      title: viewTitle,
      sceneConfig: Navigator.SceneConfigs.FloatFromRight,
    })
  }

  // Render a given scene
  renderScene() {
    return (
      <ScrollView
        automaticallyAdjustContentInsets={false}
        scrollEventThrottle={200}
        overflow={'hidden'}
        alwaysBounceHorizontal={false}
        showsHorizontalScrollIndicator={false}
        showsVerticalScrollIndicator={false}
        style={styles.scrollView}
      >
        <View style={styles.container}>
          {views.map(view =>
            <TouchableOpacity
              key={view.title}
              onPress={() => this.pushView(view.view, view.title)}
              activeOpacity={0.5}
            >
              <View style={[styles.rectangle, styles[`${view.view}Button`]]}>
                <Icon name={view.icon} size={32} style={styles.rectangleButtonIcon} />
                <Text
                  style={styles.rectangleButtonText}
                  autoAdjustsFontSize={true}
                >
                  {view.title}{view.usable ? '' : ' !'}
                </Text>
              </View>
            </TouchableOpacity>
          )}
        </View>
      </ScrollView>
    )
  }
}

import * as c from './components/colors'

const Dimensions = require('Dimensions')
let Viewport = Dimensions.get('window')

let marginTop = 15

let cellMargin = 10
let cellSidePadding = 10
let cellEdgePadding = 10
let cellWidth = (Viewport.width / 2) - (cellMargin * 1.5)

var styles = StyleSheet.create({
  // Body container
  container: {
    flex: 1,
    marginLeft: cellMargin,
    marginTop: marginTop,

    alignItems: 'flex-start',
    justifyContent: 'center',
    flexDirection: 'row',
    flexWrap: 'wrap',
  },

  // Main buttons for actions on home screen
  rectangle: {
    width: cellWidth,
    alignItems: 'center',
    paddingTop: cellSidePadding,
    paddingBottom: cellSidePadding,
    paddingRight: cellEdgePadding,
    paddingLeft: cellEdgePadding,
    borderRadius: 6,

    marginBottom: cellMargin,
    marginRight: cellMargin,
  },

  MenusViewButton: {
    backgroundColor: c.emerald,
  },
  SISViewButton: {
    backgroundColor: c.goldenrod,
  },
  BuildingHoursViewButton: {
    backgroundColor: c.wave,
  },
  CalendarViewButton: {
    backgroundColor: c.coolPurple,
  },
  DirectoryViewButton: {
    backgroundColor: c.indianRed,
  },
  StreamingViewButton: {
    backgroundColor: c.denim,
  },
  NewsViewButton: {
    backgroundColor: c.eggplant,
  },
  MapViewButton: {
    backgroundColor: c.coffee,
  },
  ContactsViewButton: {
    backgroundColor: c.crimson,
  },
  TransportationViewButton: {
    backgroundColor: c.cardTable,
  },
  DictionaryViewButton: {
    backgroundColor: c.olive,
  },

  navigationButtonText: {
    color: c.mandarin,
  },
  navigationButtonIcon: {
    color: c.mandarin,
    fontSize: 20,
    marginTop: 8,
    marginLeft: 14,
  },

  // Text styling in buttons
  rectangleButtonIcon: {
    color: c.white,
  },
  rectangleButtonText: {
    color: c.white,
    textAlign: 'center',
    fontSize: 14,
  },
})
<|endoftext|>


<commit_before>/**
 * Author: Jason Farrell
 * Author URI: http://useallfive.com/
 *
 * Description: Checks if a DOM element is truly visible.
 * Package URL: https://github.com/UseAllFive/ua5-js-utils
 */
Element.prototype.isVisible = function() {

    'use strict';

    /**
     * Checks if a DOM element is visible. Takes into
     * consideration its parents and overflow.
     *
     * @param (el)      the DOM element to check if is visible
     *
     * These params are optional that are sent in recursively,
     * you typically won't use these:
     *
     * @param (t)       Top corner position number
     * @param (r)       Right corner position number
     * @param (b)       Bottom corner position number
     * @param (l)       Left corner position number
     * @param (w)       Element width number
     * @param (h)       Element height number
     */
    function _isVisible(el, t, r, b, l, w, h) {
        var p = el.parentNode,
                VISIBLE_PADDING = 2;

        if ( !_elementInDocument(el) ) {
            return false;
        }

        //-- Return true for document node
        if ( 9 === p.nodeType ) {
            return true;
        }

        //-- Return false if our element is invisible
        if (
             '0' === _getStyle(el, 'opacity') ||
             'none' === _getStyle(el, 'display') ||
             'hidden' === _getStyle(el, 'visibility')
        ) {
            return false;
        }

        if (
            'undefined' === typeof(t) ||
            'undefined' === typeof(r) ||
            'undefined' === typeof(b) ||
            'undefined' === typeof(l) ||
            'undefined' === typeof(w) ||
            'undefined' === typeof(h)
        ) {
            t = el.offsetTop;
            l = el.offsetLeft;
            b = t + el.offsetHeight;
            r = l + el.offsetWidth;
            w = el.offsetWidth;
            h = el.offsetHeight;
        }
        //-- If we have a parent, let's continue:
        if ( p ) {
            //-- Check if the parent can hide its children.
            if ( ('hidden' === _getStyle(p, 'overflow') || 'scroll' === _getStyle(p, 'overflow')) ) {
                //-- Only check if the offset is different for the parent
                if (
                    //-- If the target element is to the right of the parent elm
                    l + VISIBLE_PADDING > p.offsetWidth + p.scrollLeft ||
                    //-- If the target element is to the left of the parent elm
                    l + w - VISIBLE_PADDING < p.scrollLeft ||
                    //-- If the target element is under the parent elm
                    t + VISIBLE_PADDING > p.offsetHeight + p.scrollTop ||
                    //-- If the target element is above the parent elm
                    t + h - VISIBLE_PADDING < p.scrollTop
                ) {
                    //-- Our target element is out of bounds:
                    return false;
                }
            }
            //-- Add the offset parent's left/top coords to our element's offset:
            if ( el.offsetParent === p ) {
                l += p.offsetLeft;
                t += p.offsetTop;
            }
            //-- Let's recursively check upwards:
            return _isVisible(p, t, r, b, l, w, h);
        }
        return true;
    }

    //-- Cross browser method to get style properties:
    function _getStyle(el, property) {
        if ( window.getComputedStyle ) {
            return document.defaultView.getComputedStyle(el,null)[property];
        }
        if ( el.currentStyle ) {
            return el.currentStyle[property];
        }
    }

    function _elementInDocument(element) {
        while (element = element.parentNode) {
            if (element == document) {
                    return true;
            }
        }
        return false;
    }

    return _isVisible(this);

};
<commit_msg>Changed github url reference.<commit_after>/**
 * Author: Jason Farrell
 * Author URI: http://useallfive.com/
 *
 * Description: Checks if a DOM element is truly visible.
 * Package URL: https://github.com/UseAllFive/true-visibility
 */
Element.prototype.isVisible = function() {

    'use strict';

    /**
     * Checks if a DOM element is visible. Takes into
     * consideration its parents and overflow.
     *
     * @param (el)      the DOM element to check if is visible
     *
     * These params are optional that are sent in recursively,
     * you typically won't use these:
     *
     * @param (t)       Top corner position number
     * @param (r)       Right corner position number
     * @param (b)       Bottom corner position number
     * @param (l)       Left corner position number
     * @param (w)       Element width number
     * @param (h)       Element height number
     */
    function _isVisible(el, t, r, b, l, w, h) {
        var p = el.parentNode,
                VISIBLE_PADDING = 2;

        if ( !_elementInDocument(el) ) {
            return false;
        }

        //-- Return true for document node
        if ( 9 === p.nodeType ) {
            return true;
        }

        //-- Return false if our element is invisible
        if (
             '0' === _getStyle(el, 'opacity') ||
             'none' === _getStyle(el, 'display') ||
             'hidden' === _getStyle(el, 'visibility')
        ) {
            return false;
        }

        if (
            'undefined' === typeof(t) ||
            'undefined' === typeof(r) ||
            'undefined' === typeof(b) ||
            'undefined' === typeof(l) ||
            'undefined' === typeof(w) ||
            'undefined' === typeof(h)
        ) {
            t = el.offsetTop;
            l = el.offsetLeft;
            b = t + el.offsetHeight;
            r = l + el.offsetWidth;
            w = el.offsetWidth;
            h = el.offsetHeight;
        }
        //-- If we have a parent, let's continue:
        if ( p ) {
            //-- Check if the parent can hide its children.
            if ( ('hidden' === _getStyle(p, 'overflow') || 'scroll' === _getStyle(p, 'overflow')) ) {
                //-- Only check if the offset is different for the parent
                if (
                    //-- If the target element is to the right of the parent elm
                    l + VISIBLE_PADDING > p.offsetWidth + p.scrollLeft ||
                    //-- If the target element is to the left of the parent elm
                    l + w - VISIBLE_PADDING < p.scrollLeft ||
                    //-- If the target element is under the parent elm
                    t + VISIBLE_PADDING > p.offsetHeight + p.scrollTop ||
                    //-- If the target element is above the parent elm
                    t + h - VISIBLE_PADDING < p.scrollTop
                ) {
                    //-- Our target element is out of bounds:
                    return false;
                }
            }
            //-- Add the offset parent's left/top coords to our element's offset:
            if ( el.offsetParent === p ) {
                l += p.offsetLeft;
                t += p.offsetTop;
            }
            //-- Let's recursively check upwards:
            return _isVisible(p, t, r, b, l, w, h);
        }
        return true;
    }

    //-- Cross browser method to get style properties:
    function _getStyle(el, property) {
        if ( window.getComputedStyle ) {
            return document.defaultView.getComputedStyle(el,null)[property];
        }
        if ( el.currentStyle ) {
            return el.currentStyle[property];
        }
    }

    function _elementInDocument(element) {
        while (element = element.parentNode) {
            if (element == document) {
                    return true;
            }
        }
        return false;
    }

    return _isVisible(this);

};
<|endoftext|>


<commit_before>//canvas stuf
var canvas = document.getElementById('visualizer');
var ctx = canvas.getContext('2d');
var width = canvas.width;
var height = canvas.height;
//data
var x, h;                                       //x=position of sample ;h=hight of sample in %
var runing = false ;                            //main loop boolean
var sample_w=7;                                 //sample width
var gap=2;                                      //gap betwean samples
var samples=Math.floor(width/(sample_w+gap));   //number ("int") of samples+gaps could fit in canvas
var d_time=16;                                  //delay betwean iteration of main loop in miliseconds(ms)
var color_setp=Math.floor(360/samples);         //steps color not in use now
var bgcolor="#15151C";                          //color of background canvas

//audio magic don't tauch :P

var audioCtx = new (window.AudioContext || window.webkitAudioContext)();

//var myAudio = document.querySelector('audio');            //no idea
var myAudio = document.getElementById('audio');             //wath is deffrence in these two nodes
var pre = document.querySelector('pre');
var myScript = document.querySelector('script');
var source = audioCtx.createMediaElementSource(myAudio);    //audio and stream audio

var analyser = audioCtx.createAnalyser();

//soffting curves
/*analyser.minDecibels = -90;
analyser.maxDecibels = -10;
analyser.smoothingTimeConstant = 0.85;*/

analyser.fftSize = 512;
var bufferLength = analyser.frequencyBinCount;
  console.log(bufferLength);
var dataArray = new Uint8Array(bufferLength);
source.connect(analyser);
analyser.connect(audioCtx.destination);

function play_pause(){
  if(runing){
    runing=false;
    myAudio.pause();
	  myAudio.src='';
  } else{
    runing=true;
	  myAudio.src='http://s1.thekrzos.eu:8700/;stream.mp3';
    myAudio.play();
  }
  loop();
}

function loop(){
  if(runing){
    analyser.getByteFrequencyData(dataArray);
    frame();
    setTimeout(loop, d_time);
    }
  }

function frame(){
  reset_();
  ctx.fillStyle = bgcolor;
  ctx.fillRect(0, 0, width, height);
  for(var i=0;i<samples;i++){
    rec_chroma(i*(sample_w+gap),Math.floor(dataArray[i]/255*100),i);
  }
}

function rec(x,h){
  ctx.fillStyle = bgcolor;
  var rec_h=height/2*h/100;
  if(rec_h==0){
    rec_h=5;
  }
  ctx.fillRect(x, height/2, sample_w, -rec_h);
  ctx.fillRect(x, height/2, sample_w, rec_h);
}

function reset_(){
  ctx.clearRect(0, 0, width, height);
}
function rec_chroma(x,h,i){
  var rec_h=height/2*h/100;
  if(rec_h==0){
    rec_h=1;
  }
  ctx.fillStyle = ("hsl("+i.toString()+", 100%, 50%)");
  ctx.fillRect(x, height/2, sample_w, -rec_h);
  ctx.fillStyle = ("hsl("+i.toString()+", 100%, 25%)");
  ctx.fillRect(x, height/2, sample_w, rec_h);
}
<commit_msg>:dollar:<commit_after>//canvas stuf
var canvas = document.getElementById('visualizer');
var ctx = canvas.getContext('2d');
var width = canvas.width;
var height = canvas.height;
//data
var x, h;                                       //x=position of sample ;h=hight of sample in %
var runing = false ;                            //main loop boolean
var sample_w=7;                                 //sample width
var gap=2;                                      //gap betwean samples
var samples=Math.floor(width/(sample_w+gap));   //number ("int") of samples+gaps could fit in canvas
var d_time=16;                                  //delay betwean iteration of main loop in miliseconds(ms)
var color_setp=Math.floor(360/samples);         //steps color not in use now
var bgcolor="#15151C";                          //color of background canvas

//audio magic don't tauch :P

var audioCtx = new (window.AudioContext || window.webkitAudioContext)();

//var myAudio = document.querySelector('audio');            //no idea
var myAudio = document.getElementById('audio');             //wath is deffrence in these two nodes
var pre = document.querySelector('pre');
var myScript = document.querySelector('script');
var source = audioCtx.createMediaElementSource(myAudio);    //audio and stream audio

var analyser = audioCtx.createAnalyser();

//soffting curves
/*analyser.minDecibels = -90;
analyser.maxDecibels = -10;
analyser.smoothingTimeConstant = 0.85;*/

analyser.fftSize = 512;
var bufferLength = analyser.frequencyBinCount;
  console.log(bufferLength);
var dataArray = new Uint8Array(bufferLength);
source.connect(analyser);
analyser.connect(audioCtx.destination);

function play_pause(){
  if(runing){
    runing=false;
    myAudio.pause();
	myAudio.src='';
  } else{
    runing=true;
	myAudio.src='http://s1.thekrzos.eu:8700/;stream.mp3';
    myAudio.play();
  }
  loop();
}

function loop(){
  if(runing){
    analyser.getByteFrequencyData(dataArray);
    frame();
    setTimeout(loop, d_time);
    }
  }

function frame(){
  reset_();
  ctx.fillStyle = bgcolor;
  ctx.fillRect(0, 0, width, height);
  for(var i=0;i<samples;i++){
    rec_chroma(i*(sample_w+gap),Math.floor(dataArray[i]/255*100),i);
  }
}

function rec(x,h){
  ctx.fillStyle = bgcolor;
  var rec_h=height/2*h/100;
  if(rec_h==0){
    rec_h=5;
  }
  ctx.fillRect(x, height/2, sample_w, -rec_h);
  ctx.fillRect(x, height/2, sample_w, rec_h);
}

function reset_(){
  ctx.clearRect(0, 0, width, height);
}
function rec_chroma(x,h,i){
  var rec_h=height/2*h/100;
  if(rec_h==0){
    rec_h=1;
  }
  ctx.fillStyle = ("hsl("+i.toString()+", 100%, 50%)");
  ctx.fillRect(x, height/2, sample_w, -rec_h);
  ctx.fillStyle = ("hsl("+i.toString()+", 100%, 25%)");
  ctx.fillRect(x, height/2, sample_w, rec_h);
}
<|endoftext|>