<commit_before>/* @flow weak */

/**
 * OfflineMobile Android
 * Sustainable Solutions (NZ) Ltd. 2016
 */

import React from 'react';
import {
  StyleSheet,
  View,
} from 'react-native';

import { generateUUID } from '../database';
import { Button, BottomModal, TextInput, ToggleBar } from '../widgets';
import globalStyles from '../globalStyles';
import { GenericTablePage } from './GenericTablePage';

const DATA_TYPES_DISPLAYED = ['Item', 'StocktakeItem'];

/**
* Renders the page for managing a stocktake.
* @prop   {Realm}               database    App wide database.
* @prop   {func}                navigateTo  CallBack for navigation stack.
* @state  {Realm.Results}       items       Filtered to have only Items.
*/
export class StocktakeManagePage extends GenericTablePage {
  constructor(props) {
    super(props);
    this.state.items = props.database.objects('Item');
    this.state.stocktake = {};
    this.state.stocktakeName = '';
    this.state.isNewStocktake = false;
    this.state.searchTerm = '';
    this.state.isAscending = true;
    this.state.isSelectAllItems = false;
    this.state.showNoStock = false;
    this.state.sortBy = 'name';
    this.columns = COLUMNS;
    this.dataTypesDisplayed = DATA_TYPES_DISPLAYED;
    this.onColumnSort = this.onColumnSort.bind(this);
    this.renderHeader = this.renderHeader.bind(this);
    this.renderRow = this.renderRow.bind(this);
    this.getUpdatedData = this.getUpdatedData.bind(this);
    this.onConfirmPress = this.onConfirmPress.bind(this);
  }

  componentWillMount() {
    this.databaseListenerId = this.props.database.addListener(this.onDatabaseEvent);
    if (!this.props.stocktake) {
      let stocktake;
      this.props.database.write(() => {
        const date = new Date();
        stocktake = this.props.database.create('Stocktake', {
          id: generateUUID(),
          name: 'New Stocktake',
          createdDate: date,
          status: 'new',
          comment: 'Testing StocktakesPage',
          serialNumber: '1337',
          lines: [],
        });
      });
      this.setState({ stocktake: stocktake, isNewStocktake: true });
    } else {
      const selected = [];
      this.props.stocktake.lines.forEach((line) => {
        const item = line.itemLine.item;
        if (item) selected.push(item.id);
      });
      this.setState({
        stocktake: this.props.stocktake,
        selection: selected,
        stocktakeName: this.props.stocktake.name,
      });
    }
    this.refreshData();
  }

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

  onConfirmPress() {
    const { stocktake, selection, items } = this.state;
    const { database, navigateTo } = this.props;
    let { stocktakeName } = this.state;
    const stocktakeLines = [];

    function getDateTimeString(date) {
      return `${date.getDate()}.${date.getMonth()}.${date.getFullYear()} ` +
             `${date.getHours()}:${date.getMinutes()}`;
    }

    if (stocktakeName === '') {
      stocktakeName = `Stocktake ${getDateTimeString(stocktake.createdDate)}`;
    }

    database.write(() => {
      stocktake.lines.forEach((line, i, lines) => {
        const item = line.itemLine.item;
        const itemIdIndex = selection.indexOf(item.id);
        // If a stocktakeLine for an item already exists in the stocktake, remove it from the
        // selection array.
        if (itemIdIndex >= 0) {
          selection.slice(itemIdIndex, 1);
        }
        // Remove stocktakeLines of items that are not in the selection.
        if (!selection.includes(item.id)) lines.slice(i, 1);
      });

      selection.forEach((itemId) => {
        const item = items.find(i => i.id === itemId);
        item.lines.forEach((itemLine) => {
          const stocktakeLine = database.create('StocktakeLine', {
            id: generateUUID(),
            stocktake: stocktake,
            itemLine: itemLine,
            snapshotNumberOfPacks: itemLine.numberOfPacks,
            packSize: 1,
            expiryDate: itemLine.expiryDate,
            batch: itemLine.batch,
            costPrice: itemLine.costPrice,
            sellPrice: itemLine.sellPrice,
          });
          stocktakeLines.push(stocktakeLine);
        });
      });

      stocktake.name = stocktakeName;
      stocktakeLines.forEach(line => stocktake.lines.push(line));
      this.props.database.update('Stocktake', stocktake);
    });
    navigateTo('stocktakeEditor', stocktake.name, { stocktake: stocktake });
  }

  toggleSelectAllItems() {
    const isSelectAllItems = !this.state.isSelectAllItems;
    const { items } = this.state;
    this.setState({
      isSelectAllItems: isSelectAllItems,
      selection: isSelectAllItems ? items.map(item => item.id) : [],
    }, this.refreshData);
  }

  toggleShowNoStock() {
    this.setState({
      showNoStock: !this.state.showNoStock,
    });
    this.refreshData();
  }

  /**
   * Updates data within dataSource in state according to SortBy and
   * isAscending.
   */
  getUpdatedData(searchTerm, sortBy, isAscending) {
    const {
      items,
      selection,
      showNoStock,
    } = this.state;
    let data;
    data = items.filtered(`name CONTAINS[c] "${searchTerm}"`);
    switch (sortBy) {
      // 'selected' case lists the selected items in alphabetical order, followed by unselected in
      // alphabetical order. This requires the selection array to store the item ids in the
      // same alphabetical order as their respective items.
      case 'selected':
        selection.sort((a, b) => {
          const aName = items.find(item => item.id === a).name;
          const bName = items.find(item => item.id === b).name;
          return bName.localeCompare(aName);
        });
        data = data.sorted('name', !isAscending).slice()
                  .sort((a, b) => selection.indexOf(b.id) - selection.indexOf(a.id));
        if (!isAscending) data.reverse();
        break;
      default:
        data = data.sorted(sortBy, !isAscending);
    }
    if (!showNoStock) {
      data = data.slice().filter((item) => item.getTotal !== 0);
    }
    return data;
  }

  renderCell(key, item) {
    switch (key) {
      default:
      case 'code':
        return item.code;
      case 'name':
        return item.name;
      case 'snapshotQuantity':
        return 1337;
      case 'selected':
        return {
          type: 'checkable',
        };
    }
  }

  render() {
    const { isSelectAllItems, showNoStock } = this.state;
    return (
      <View style={globalStyles.pageContentContainer}>
        <View style={globalStyles.container}>
          <View style={globalStyles.pageTopSectionContainer}>
            {this.renderSearchBar()}
            <View style={localStyles.toggleBarView}>
              <ToggleBar
                style={globalStyles.toggleBar}
                textOffStyle={globalStyles.toggleText}
                textOnStyle={globalStyles.toggleTextSelected}
                toggleOffStyle={globalStyles.toggleOption}
                toggleOnStyle={globalStyles.toggleOptionSelected}
                toggles={[
                  {
                    text: 'Show No Stock',
                    onPress: () => this.toggleShowNoStock(),
                    isOn: showNoStock,
                  },
                  {
                    text: 'Select All Items',
                    onPress: () => this.toggleSelectAllItems(),
                    isOn: isSelectAllItems,
                  },
                ]}
              />
            </View>
          </View>
          {this.renderDataTable()}
          <BottomModal
            isOpen={this.state.selection.length > 0}
            style={localStyles.bottomModal}
          >
            <TextInput
              style={globalStyles.modalTextInput}
              textStyle={globalStyles.modalText}
              placeholderTextColor="white"
              placeholder="Give your stocktake a name"
              value={this.state.stocktakeName}
              onChange={(text) => this.setState({ stocktakeName: text })}
            />
            <Button
              style={[globalStyles.button, globalStyles.modalOrangeButton]}
              textStyle={[globalStyles.buttonText, globalStyles.modalButtonText]}
              text={this.state.isNewStocktake ? 'Create' : 'Confirm'}
              onPress={() => this.onConfirmPress()}
            />
          </BottomModal>
        </View>
      </View>
    );
  }
}

StocktakeManagePage.propTypes = {
  stocktake: React.PropTypes.object,
  database: React.PropTypes.object.isRequired,
  navigateTo: React.PropTypes.func.isRequired,
};

const COLUMNS = [
  {
    key: 'code',
    width: 2,
    title: 'ITEM CODE',
    sortable: true,
  },
  {
    key: 'name',
    width: 6,
    title: 'ITEM NAME',
    sortable: true,
  },
  {
    key: 'snapshotQuantity',
    width: 2,
    title: 'SNAPSHOT QUANTITY',
  },
  {
    key: 'selected',
    width: 1,
    title: 'SELECTED',
  },
];

const localStyles = StyleSheet.create({
  bottomModal: {
    justifyContent: 'space-between',
    paddingLeft: 20,
  },
  toggleBarView: {
    justifyContent: 'flex-end',
    alignItems: 'center',
  },
});
<commit_msg>fixed sorting<commit_after>/* @flow weak */

/**
 * OfflineMobile Android
 * Sustainable Solutions (NZ) Ltd. 2016
 */

import React from 'react';
import {
  StyleSheet,
  View,
} from 'react-native';

import { generateUUID } from '../database';
import { Button, BottomModal, TextInput, ToggleBar } from '../widgets';
import globalStyles from '../globalStyles';
import { GenericTablePage } from './GenericTablePage';

const DATA_TYPES_DISPLAYED = ['Item', 'StocktakeItem'];

/**
* Renders the page for managing a stocktake.
* @prop   {Realm}               database    App wide database.
* @prop   {func}                navigateTo  CallBack for navigation stack.
* @state  {Realm.Results}       items       Filtered to have only Items.
*/
export class StocktakeManagePage extends GenericTablePage {
  constructor(props) {
    super(props);
    this.state.items = props.database.objects('Item');
    this.state.stocktake = {};
    this.state.stocktakeName = '';
    this.state.isNewStocktake = false;
    this.state.isSelectAllItems = false;
    this.state.showNoStock = false;
    this.state.sortBy = 'name';
    this.columns = COLUMNS;
    this.dataTypesDisplayed = DATA_TYPES_DISPLAYED;
    this.onColumnSort = this.onColumnSort.bind(this);
    this.renderHeader = this.renderHeader.bind(this);
    this.renderRow = this.renderRow.bind(this);
    this.getUpdatedData = this.getUpdatedData.bind(this);
    this.onConfirmPress = this.onConfirmPress.bind(this);
  }

  componentWillMount() {
    this.databaseListenerId = this.props.database.addListener(this.onDatabaseEvent);
    if (!this.props.stocktake) {
      let stocktake;
      this.props.database.write(() => {
        const date = new Date();
        stocktake = this.props.database.create('Stocktake', {
          id: generateUUID(),
          name: 'New Stocktake',
          createdDate: date,
          status: 'new',
          comment: 'Testing StocktakesPage',
          serialNumber: '1337',
          lines: [],
        });
      });
      this.setState({ stocktake: stocktake, isNewStocktake: true });
    } else {
      const selected = [];
      this.props.stocktake.lines.forEach((line) => {
        const item = line.itemLine.item;
        if (item) selected.push(item.id);
      });
      this.setState({
        stocktake: this.props.stocktake,
        selection: selected,
        stocktakeName: this.props.stocktake.name,
      });
    }
    this.refreshData();
  }

  onConfirmPress() {
    const { stocktake, selection, items } = this.state;
    const { database, navigateTo } = this.props;
    let { stocktakeName } = this.state;
    const stocktakeLines = [];

    function getDateTimeString(date) {
      return `${date.getDate()}.${date.getMonth()}.${date.getFullYear()} ` +
             `${date.getHours()}:${date.getMinutes()}`;
    }

    if (stocktakeName === '') {
      stocktakeName = `Stocktake ${getDateTimeString(stocktake.createdDate)}`;
    }

    database.write(() => {
      stocktake.lines.forEach((line, i, lines) => {
        const item = line.itemLine.item;
        const itemIdIndex = selection.indexOf(item.id);
        // If a stocktakeLine for an item already exists in the stocktake, remove it from the
        // selection array.
        if (itemIdIndex >= 0) {
          selection.slice(itemIdIndex, 1);
        }
        // Remove stocktakeLines of items that are not in the selection.
        if (!selection.includes(item.id)) lines.slice(i, 1);
      });

      selection.forEach((itemId) => {
        const item = items.find(i => i.id === itemId);
        item.lines.forEach((itemLine) => {
          const stocktakeLine = database.create('StocktakeLine', {
            id: generateUUID(),
            stocktake: stocktake,
            itemLine: itemLine,
            snapshotNumberOfPacks: itemLine.numberOfPacks,
            packSize: 1,
            expiryDate: itemLine.expiryDate,
            batch: itemLine.batch,
            costPrice: itemLine.costPrice,
            sellPrice: itemLine.sellPrice,
          });
          stocktakeLines.push(stocktakeLine);
        });
      });

      stocktake.name = stocktakeName;
      stocktakeLines.forEach(line => stocktake.lines.push(line));
      this.props.database.update('Stocktake', stocktake);
    });
    navigateTo('stocktakeEditor', stocktake.name, { stocktake: stocktake });
  }

  toggleSelectAllItems() {
    const isSelectAllItems = !this.state.isSelectAllItems;
    const { items } = this.state;
    this.setState({
      isSelectAllItems: isSelectAllItems,
      selection: isSelectAllItems ? items.map(item => item.id) : [],
    }, this.refreshData);
  }

  toggleShowNoStock() {
    this.setState({
      showNoStock: !this.state.showNoStock,
    }, this.refreshData);
  }

  /**
   * Updates data within dataSource in state according to SortBy and
   * isAscending.
   */
  getUpdatedData(searchTerm, sortBy, isAscending) {
    const {
      items,
      selection,
      showNoStock,
    } = this.state;
    let data;
    data = items.filtered(`name CONTAINS[c] "${searchTerm}"`);
    switch (sortBy) {
      // 'selected' case lists the selected items in alphabetical order, followed by unselected in
      // alphabetical order. This requires the selection array to store the item ids in the
      // same alphabetical order as their respective items.
      case 'selected':
        selection.sort((a, b) => {
          const aName = items.find(item => item.id === a).name;
          const bName = items.find(item => item.id === b).name;
          return bName.localeCompare(aName);
        });
        data = data.sorted('name', !isAscending).slice()
                  .sort((a, b) => selection.indexOf(b.id) - selection.indexOf(a.id));
        if (!isAscending) data.reverse();
        break;
      default:
        data = data.sorted(sortBy, !isAscending);
    }
    if (!showNoStock) {
      data = data.slice().filter((item) => item.totalQuantity !== 0);
    }
    return data;
  }

  renderCell(key, item) {
    switch (key) {
      default:
      case 'code':
        return item.code;
      case 'name':
        return item.name;
      case 'snapshotQuantity':
        return 1337;
      case 'selected':
        return {
          type: 'checkable',
        };
    }
  }

  render() {
    const { isSelectAllItems, showNoStock } = this.state;
    return (
      <View style={globalStyles.pageContentContainer}>
        <View style={globalStyles.container}>
          <View style={globalStyles.pageTopSectionContainer}>
            {this.renderSearchBar()}
            <View style={localStyles.toggleBarView}>
              <ToggleBar
                style={globalStyles.toggleBar}
                textOffStyle={globalStyles.toggleText}
                textOnStyle={globalStyles.toggleTextSelected}
                toggleOffStyle={globalStyles.toggleOption}
                toggleOnStyle={globalStyles.toggleOptionSelected}
                toggles={[
                  {
                    text: 'Show No Stock',
                    onPress: () => this.toggleShowNoStock(),
                    isOn: showNoStock,
                  },
                  {
                    text: 'Select All Items',
                    onPress: () => this.toggleSelectAllItems(),
                    isOn: isSelectAllItems,
                  },
                ]}
              />
            </View>
          </View>
          {this.renderDataTable()}
          <BottomModal
            isOpen={this.state.selection.length > 0}
            style={localStyles.bottomModal}
          >
            <TextInput
              style={globalStyles.modalTextInput}
              textStyle={globalStyles.modalText}
              placeholderTextColor="white"
              placeholder="Give your stocktake a name"
              value={this.state.stocktakeName}
              onChange={(text) => this.setState({ stocktakeName: text })}
            />
            <Button
              style={[globalStyles.button, globalStyles.modalOrangeButton]}
              textStyle={[globalStyles.buttonText, globalStyles.modalButtonText]}
              text={this.state.isNewStocktake ? 'Create' : 'Confirm'}
              onPress={() => this.onConfirmPress()}
            />
          </BottomModal>
        </View>
      </View>
    );
  }
}

StocktakeManagePage.propTypes = {
  stocktake: React.PropTypes.object,
  database: React.PropTypes.object.isRequired,
  navigateTo: React.PropTypes.func.isRequired,
};

const COLUMNS = [
  {
    key: 'code',
    width: 2,
    title: 'ITEM CODE',
    sortable: true,
  },
  {
    key: 'name',
    width: 6,
    title: 'ITEM NAME',
    sortable: true,
  },
  {
    key: 'snapshotQuantity',
    width: 2,
    title: 'SNAPSHOT QUANTITY',
  },
  {
    key: 'selected',
    width: 1,
    title: 'SELECTED',
    sortable: true,
  },
];

const localStyles = StyleSheet.create({
  bottomModal: {
    justifyContent: 'space-between',
    paddingLeft: 20,
  },
  toggleBarView: {
    justifyContent: 'flex-end',
    alignItems: 'center',
  },
});
<|endoftext|>


<commit_before>/* eslint-disable import/prefer-default-export */
/* eslint-disable react/forbid-prop-types */
/**
 * mSupply Mobile
 * Sustainable Solutions (NZ) Ltd. 2019
 */

import React, { useCallback } from 'react';
import PropTypes from 'prop-types';
import { View } from 'react-native';
import { SearchBar } from 'react-native-ui-components';
import { recordKeyExtractor, getItemLayout } from './dataTableUtilities/utilities';

import { MODAL_KEYS, getModalTitle } from '../utilities';
import { buttonStrings, modalStrings } from '../localization';
import { UIDatabase } from '../database';

import {
  focusCell,
  focusNext,
  selectRow,
  deselectRow,
  deselectAll,
  sortData,
  filterData,
  closeBasicModal,
  editComment,
  editTheirRef,
  openBasicModal,
  refreshData,
  editTransactionBatchQuantity,
  deleteTransactionBatchesById,
  editTransactionBatchExpiryDate,
  addTransactionBatch,
} from './dataTableUtilities/actions';
import usePageReducer from '../hooks/usePageReducer';

import {
  AutocompleteSelector,
  PageButton,
  PageInfo,
  TextEditor,
  CheckedComponent,
  UncheckedComponent,
  DisabledCheckedComponent,
  DisabledUncheckedComponent,
  NewExpiryDateInput,
} from '../widgets';
import { BottomConfirmModal, PageContentModal } from '../widgets/modals';
import {
  DataTable,
  Row,
  Cell,
  EditableCell,
  CheckableCell,
  DataTableHeaderRow,
} from '../widgets/DataTable';
import DataTablePageView from './containers/DataTablePageView';

import globalStyles, { SUSSOL_ORANGE, newDataTableStyles, newPageStyles } from '../globalStyles';

const keyExtractor = item => item.id;

export const SupplierInvoicePage = ({ routeName, transaction }) => {
  const [state, dispatch, instantDebouncedDispatch, debouncedDispatch] = usePageReducer(routeName, {
    pageObject: transaction,
    backingData: transaction.getTransactionBatches(UIDatabase),
    data: transaction
      .getTransactionBatches(UIDatabase)
      .sorted('itemName')
      .slice(),
    keyExtractor: recordKeyExtractor,
    dataState: new Map(),
    currentFocusedRowKey: null,
    searchTerm: '',
    filterDataKeys: ['itemName'],
    sortBy: 'itemName',
    isAscending: true,
    modalKey: '',
    hasSelection: false,
  });

  const {
    data,
    dataState,
    sortBy,
    isAscending,
    columns,
    modalKey,
    pageInfo,
    pageObject,
    hasSelection,
    backingData,
  } = state;

  const { isFinalised, comment, theirRef } = pageObject;
  const { ITEM_SELECT, COMMENT_EDIT, THEIR_REF_EDIT } = MODAL_KEYS;

  if (isFinalised && data.length !== backingData.length) dispatch(refreshData());

  const renderPageInfo = useCallback(
    () => <PageInfo columns={pageInfo(pageObject, dispatch)} isEditingDisabled={isFinalised} />,
    [comment, theirRef, isFinalised]
  );

  const renderCells = useCallback(
    (rowData, rowState = {}, rowKey) => {
      const {
        cellContainer,
        editableCellText,
        editableCellUnfocused,
        editableCellTextView,
        cellText,
        touchableCellContainer,
      } = newDataTableStyles;
      return columns.map(({ key: colKey, type, width, alignText }, index) => {
        const isLastCell = index === columns.length - 1;
        const isDisabled = isFinalised || (rowState && rowState.isDisabled);
        switch (type) {
          case 'editable':
            return (
              <EditableCell
                key={colKey}
                value={rowData[colKey]}
                rowKey={rowKey}
                columnKey={colKey}
                editAction={editTransactionBatchQuantity}
                isFocused={colKey === (rowState && rowState.focusedColumn)}
                isDisabled={isDisabled}
                focusAction={focusCell}
                focusNextAction={focusNext}
                dispatch={dispatch}
                width={width}
                viewStyle={cellContainer[alignText || 'left']}
                textViewStyle={editableCellTextView}
                isLastCell={isLastCell}
                keyboardType="numeric"
                textInputStyle={cellText[alignText || 'left']}
                textStyle={editableCellUnfocused[alignText || 'left']}
                cellTextStyle={editableCellText}
              />
            );
          case 'checkable':
            return (
              <CheckableCell
                key={colKey}
                rowKey={rowKey}
                columnKey={colKey}
                isChecked={rowState && rowState.isSelected}
                isDisabled={isDisabled}
                CheckedComponent={CheckedComponent}
                UncheckedComponent={UncheckedComponent}
                DisabledCheckedComponent={DisabledCheckedComponent}
                DisabledUncheckedComponent={DisabledUncheckedComponent}
                onCheckAction={selectRow}
                onUncheckAction={deselectRow}
                dispatch={dispatch}
                containerStyle={touchableCellContainer}
                width={width}
                isLastCell={isLastCell}
              />
            );
          case 'date':
            return (
              <NewExpiryDateInput
                key={colKey}
                value={rowData[colKey]}
                rowKey={rowKey}
                columnKey={colKey}
                editAction={editTransactionBatchExpiryDate}
                isFocused={colKey === (rowState && rowState.focusedColumn)}
                isDisabled={isDisabled}
                focusAction={focusCell}
                focusNextAction={focusNext}
                dispatch={dispatch}
                width={width}
                isLastCell={isLastCell}
              />
            );
          default:
            return (
              <Cell
                key={colKey}
                value={rowData[colKey]}
                width={width}
                viewStyle={cellContainer[alignText || 'left']}
                textStyle={cellText[alignText || 'left']}
                isLastCell={isLastCell}
              />
            );
        }
      });
    },
    [isFinalised]
  );

  const renderRow = useCallback(
    listItem => {
      const { item, index } = listItem;
      const rowKey = keyExtractor(item);
      const { row, alternateRow } = newDataTableStyles;
      return (
        <Row
          rowData={data[index]}
          rowState={dataState.get(rowKey)}
          rowKey={rowKey}
          renderCells={renderCells}
          style={index % 2 === 0 ? alternateRow : row}
          debug
        />
      );
    },
    [data, dataState, renderCells]
  );

  const renderModalContent = () => {
    switch (modalKey) {
      case ITEM_SELECT:
        return (
          <AutocompleteSelector
            options={UIDatabase.objects('Item')}
            queryString="name BEGINSWITH[c] $0 OR code BEGINSWITH[c] $0"
            queryStringSecondary="name CONTAINS[c] $0"
            sortByString="name"
            onSelect={item => dispatch(addTransactionBatch(item))}
            renderLeftText={item => `${item.name}`}
            renderRightText={item => `${item.totalQuantity}`}
          />
        );
      case COMMENT_EDIT:
        return (
          <TextEditor
            text={comment}
            onEndEditing={value => dispatch(editComment(value, 'Transaction'))}
          />
        );
      case THEIR_REF_EDIT:
        return (
          <TextEditor
            text={theirRef}
            onEndEditing={value => dispatch(editTheirRef(value, 'Transaction'))}
          />
        );
      default:
        return null;
    }
  };

  const renderButtons = () => {
    const { verticalContainer, topButton } = globalStyles;
    return (
      <View style={verticalContainer}>
        <PageButton
          style={topButton}
          text={buttonStrings.new_item}
          onPress={() => dispatch(openBasicModal(ITEM_SELECT))}
          isDisabled={isFinalised}
        />
      </View>
    );
  };

  const renderHeader = () => (
    <DataTableHeaderRow
      columns={columns}
      dispatch={instantDebouncedDispatch}
      sortAction={sortData}
      isAscending={isAscending}
      sortBy={sortBy}
    />
  );

  const memoizedGetItemLayout = useCallback(getItemLayout, []);

  const {
    newPageTopSectionContainer,
    newPageTopLeftSectionContainer,
    newPageTopRightSectionContainer,
    searchBar,
  } = newPageStyles;
  return (
    <DataTablePageView>
      <View style={newPageTopSectionContainer}>
        <View style={newPageTopLeftSectionContainer}>
          {renderPageInfo()}
          <SearchBar
            onChange={value => debouncedDispatch(filterData(value))}
            style={searchBar}
            color={SUSSOL_ORANGE}
            placeholder=""
          />
        </View>
        <View style={newPageTopRightSectionContainer}>{renderButtons()}</View>
      </View>
      <DataTable
        data={data}
        extraData={dataState}
        renderRow={renderRow}
        renderHeader={renderHeader}
        keyExtractor={keyExtractor}
        getItemLayout={memoizedGetItemLayout}
      />
      <BottomConfirmModal
        isOpen={hasSelection}
        questionText={modalStrings.remove_these_items}
        onCancel={() => dispatch(deselectAll())}
        onConfirm={() => dispatch(deleteTransactionBatchesById('Transaction'))}
        confirmText={modalStrings.remove}
      />
      <PageContentModal
        isOpen={!!modalKey}
        onClose={() => dispatch(closeBasicModal())}
        title={getModalTitle(modalKey)}
      >
        {renderModalContent()}
      </PageContentModal>
    </DataTablePageView>
  );
};

SupplierInvoicePage.propTypes = {
  routeName: PropTypes.string.isRequired,
  transaction: PropTypes.object.isRequired,
};
<commit_msg>Add new SearchBar usage in SupplierInvoicePage<commit_after>/* eslint-disable import/prefer-default-export */
/* eslint-disable react/forbid-prop-types */
/**
 * mSupply Mobile
 * Sustainable Solutions (NZ) Ltd. 2019
 */

import React, { useCallback } from 'react';
import PropTypes from 'prop-types';
import { View } from 'react-native';

import { recordKeyExtractor, getItemLayout } from './dataTableUtilities/utilities';

import { MODAL_KEYS, getModalTitle } from '../utilities';
import { buttonStrings, modalStrings } from '../localization';
import { UIDatabase } from '../database';

import {
  focusCell,
  focusNext,
  selectRow,
  deselectRow,
  deselectAll,
  sortData,
  filterData,
  closeBasicModal,
  editComment,
  editTheirRef,
  openBasicModal,
  refreshData,
  editTransactionBatchQuantity,
  deleteTransactionBatchesById,
  editTransactionBatchExpiryDate,
  addTransactionBatch,
} from './dataTableUtilities/actions';
import usePageReducer from '../hooks/usePageReducer';

import {
  AutocompleteSelector,
  PageButton,
  PageInfo,
  TextEditor,
  CheckedComponent,
  UncheckedComponent,
  DisabledCheckedComponent,
  DisabledUncheckedComponent,
  NewExpiryDateInput,
  SearchBar,
} from '../widgets';
import { BottomConfirmModal, PageContentModal } from '../widgets/modals';
import {
  DataTable,
  Row,
  Cell,
  EditableCell,
  CheckableCell,
  DataTableHeaderRow,
} from '../widgets/DataTable';
import DataTablePageView from './containers/DataTablePageView';

import globalStyles, { SUSSOL_ORANGE, newDataTableStyles, newPageStyles } from '../globalStyles';

const keyExtractor = item => item.id;

export const SupplierInvoicePage = ({ routeName, transaction }) => {
  const [state, dispatch, instantDebouncedDispatch] = usePageReducer(routeName, {
    pageObject: transaction,
    backingData: transaction.getTransactionBatches(UIDatabase),
    data: transaction
      .getTransactionBatches(UIDatabase)
      .sorted('itemName')
      .slice(),
    keyExtractor: recordKeyExtractor,
    dataState: new Map(),
    currentFocusedRowKey: null,
    searchTerm: '',
    filterDataKeys: ['itemName'],
    sortBy: 'itemName',
    isAscending: true,
    modalKey: '',
    hasSelection: false,
  });

  const {
    data,
    dataState,
    sortBy,
    isAscending,
    columns,
    modalKey,
    pageInfo,
    pageObject,
    hasSelection,
    backingData,
    searchTerm,
  } = state;

  const { isFinalised, comment, theirRef } = pageObject;
  const { ITEM_SELECT, COMMENT_EDIT, THEIR_REF_EDIT } = MODAL_KEYS;

  if (isFinalised && data.length !== backingData.length) dispatch(refreshData());

  const renderPageInfo = useCallback(
    () => <PageInfo columns={pageInfo(pageObject, dispatch)} isEditingDisabled={isFinalised} />,
    [comment, theirRef, isFinalised]
  );

  const renderCells = useCallback(
    (rowData, rowState = {}, rowKey) => {
      const {
        cellContainer,
        editableCellText,
        editableCellUnfocused,
        editableCellTextView,
        cellText,
        touchableCellContainer,
      } = newDataTableStyles;
      return columns.map(({ key: colKey, type, width, alignText }, index) => {
        const isLastCell = index === columns.length - 1;
        const isDisabled = isFinalised || (rowState && rowState.isDisabled);
        switch (type) {
          case 'editable':
            return (
              <EditableCell
                key={colKey}
                value={rowData[colKey]}
                rowKey={rowKey}
                columnKey={colKey}
                editAction={editTransactionBatchQuantity}
                isFocused={colKey === (rowState && rowState.focusedColumn)}
                isDisabled={isDisabled}
                focusAction={focusCell}
                focusNextAction={focusNext}
                dispatch={dispatch}
                width={width}
                viewStyle={cellContainer[alignText || 'left']}
                textViewStyle={editableCellTextView}
                isLastCell={isLastCell}
                keyboardType="numeric"
                textInputStyle={cellText[alignText || 'left']}
                textStyle={editableCellUnfocused[alignText || 'left']}
                cellTextStyle={editableCellText}
              />
            );
          case 'checkable':
            return (
              <CheckableCell
                key={colKey}
                rowKey={rowKey}
                columnKey={colKey}
                isChecked={rowState && rowState.isSelected}
                isDisabled={isDisabled}
                CheckedComponent={CheckedComponent}
                UncheckedComponent={UncheckedComponent}
                DisabledCheckedComponent={DisabledCheckedComponent}
                DisabledUncheckedComponent={DisabledUncheckedComponent}
                onCheckAction={selectRow}
                onUncheckAction={deselectRow}
                dispatch={dispatch}
                containerStyle={touchableCellContainer}
                width={width}
                isLastCell={isLastCell}
              />
            );
          case 'date':
            return (
              <NewExpiryDateInput
                key={colKey}
                value={rowData[colKey]}
                rowKey={rowKey}
                columnKey={colKey}
                editAction={editTransactionBatchExpiryDate}
                isFocused={colKey === (rowState && rowState.focusedColumn)}
                isDisabled={isDisabled}
                focusAction={focusCell}
                focusNextAction={focusNext}
                dispatch={dispatch}
                width={width}
                isLastCell={isLastCell}
              />
            );
          default:
            return (
              <Cell
                key={colKey}
                value={rowData[colKey]}
                width={width}
                viewStyle={cellContainer[alignText || 'left']}
                textStyle={cellText[alignText || 'left']}
                isLastCell={isLastCell}
              />
            );
        }
      });
    },
    [isFinalised]
  );

  const renderRow = useCallback(
    listItem => {
      const { item, index } = listItem;
      const rowKey = keyExtractor(item);
      const { row, alternateRow } = newDataTableStyles;
      return (
        <Row
          rowData={data[index]}
          rowState={dataState.get(rowKey)}
          rowKey={rowKey}
          renderCells={renderCells}
          style={index % 2 === 0 ? alternateRow : row}
          debug
        />
      );
    },
    [data, dataState, renderCells]
  );

  const renderModalContent = () => {
    switch (modalKey) {
      case ITEM_SELECT:
        return (
          <AutocompleteSelector
            options={UIDatabase.objects('Item')}
            queryString="name BEGINSWITH[c] $0 OR code BEGINSWITH[c] $0"
            queryStringSecondary="name CONTAINS[c] $0"
            sortByString="name"
            onSelect={item => dispatch(addTransactionBatch(item))}
            renderLeftText={item => `${item.name}`}
            renderRightText={item => `${item.totalQuantity}`}
          />
        );
      case COMMENT_EDIT:
        return (
          <TextEditor
            text={comment}
            onEndEditing={value => dispatch(editComment(value, 'Transaction'))}
          />
        );
      case THEIR_REF_EDIT:
        return (
          <TextEditor
            text={theirRef}
            onEndEditing={value => dispatch(editTheirRef(value, 'Transaction'))}
          />
        );
      default:
        return null;
    }
  };

  const renderButtons = () => {
    const { verticalContainer, topButton } = globalStyles;
    return (
      <View style={verticalContainer}>
        <PageButton
          style={topButton}
          text={buttonStrings.new_item}
          onPress={() => dispatch(openBasicModal(ITEM_SELECT))}
          isDisabled={isFinalised}
        />
      </View>
    );
  };

  const renderHeader = () => (
    <DataTableHeaderRow
      columns={columns}
      dispatch={instantDebouncedDispatch}
      sortAction={sortData}
      isAscending={isAscending}
      sortBy={sortBy}
    />
  );

  const memoizedGetItemLayout = useCallback(getItemLayout, []);

  const {
    newPageTopSectionContainer,
    newPageTopLeftSectionContainer,
    newPageTopRightSectionContainer,
    searchBar,
  } = newPageStyles;
  return (
    <DataTablePageView>
      <View style={newPageTopSectionContainer}>
        <View style={newPageTopLeftSectionContainer}>
          {renderPageInfo()}
          <SearchBar
            onChangeText={value => dispatch(filterData(value))}
            style={searchBar}
            color={SUSSOL_ORANGE}
            placeholder=""
            value={searchTerm}
          />
        </View>
        <View style={newPageTopRightSectionContainer}>{renderButtons()}</View>
      </View>
      <DataTable
        data={data}
        extraData={dataState}
        renderRow={renderRow}
        renderHeader={renderHeader}
        keyExtractor={keyExtractor}
        getItemLayout={memoizedGetItemLayout}
      />
      <BottomConfirmModal
        isOpen={hasSelection}
        questionText={modalStrings.remove_these_items}
        onCancel={() => dispatch(deselectAll())}
        onConfirm={() => dispatch(deleteTransactionBatchesById('Transaction'))}
        confirmText={modalStrings.remove}
      />
      <PageContentModal
        isOpen={!!modalKey}
        onClose={() => dispatch(closeBasicModal())}
        title={getModalTitle(modalKey)}
      >
        {renderModalContent()}
      </PageContentModal>
    </DataTablePageView>
  );
};

SupplierInvoicePage.propTypes = {
  routeName: PropTypes.string.isRequired,
  transaction: PropTypes.object.isRequired,
};
<|endoftext|>


<commit_before>/**
 * Copyright 2009 Google Inc.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS-IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

import("exceptionutils");
import("fileutils.{readFile,fileLastModified}");
import("ejs.EJS");
import("funhtml.*");
import("stringutils");
import("stringutils.startsWith");
import("jsutils.*");

import("etherpad.sessions");
import("etherpad.sessions.getSession");
import("etherpad.globals.*");
import("etherpad.helpers");
import("etherpad.collab.collab_server");
import("etherpad.pad.model");
import("etherpad.pro.domains");
import("etherpad.pne.pne_utils");
import("etherpad.pro.pro_utils");
import("etherpad.pro.pro_config");
import("etherpad.pro.pro_accounts");
import("etherpad.pro.pro_accounts.getSessionProAccount");
import("etherpad.log");
import("etherpad.admin.plugins");

jimport("java.lang.System.out.print");
jimport("java.lang.System.out.println");

//----------------------------------------------------------------
// utilities
//----------------------------------------------------------------

// returns globally-unique padId
function randomUniquePadId() {
  var id = stringutils.randomString(10);
  while (model.accessPadGlobal(id, function(p) { return p.exists(); }, "r")) {
    id = stringutils.randomString(10);
  }
  return id;
}

//----------------------------------------------------------------
// template rendering
//----------------------------------------------------------------

function findTemplate(filename, plugin) {
  if (plugin != undefined)
   return '/plugins/' + plugin + '/templates/' + filename;
  else
   return '/templates/' + filename;
}

function renderTemplateAsString(filename, data, plugin) {
  data = data || {};
  data.helpers = helpers; // global helpers

  var f = findTemplate(filename, plugin); //"/templates/"+filename;
  if (! appjet.scopeCache.ejs) {
    appjet.scopeCache.ejs = {};
  }
  var cacheObj = appjet.scopeCache.ejs[filename];
  if (cacheObj === undefined || fileLastModified(f) > cacheObj.mtime) {
    var templateText = readFile(f);
    cacheObj = {};
    cacheObj.tmpl = new EJS({text: templateText, name: filename});
    cacheObj.mtime = fileLastModified(f);
    appjet.scopeCache.ejs[filename] = cacheObj;
  }
  var html = cacheObj.tmpl.render(data);
  return html;
}

function renderTemplate(filename, data, plugin) {
  response.write(renderTemplateAsString(filename, data, plugin));
  if (request.acceptsGzip) {
    response.setGzip(true);
  }
}

function renderHtml(bodyFileName, data, plugin) {
  var bodyHtml = renderTemplateAsString(bodyFileName, data, plugin);
  bodyHtml = plugins.callHookStr("renderPageBodyPre", {bodyFileName:bodyFileName, data:data, plugin:plugin}) +
             bodyHtml +
             plugins.callHookStr("renderPageBodyPost", {bodyFileName:bodyFileName, data:data, plugin:plugin});
  response.write(renderTemplateAsString("html.ejs", {bodyHtml: bodyHtml}));
  if (request.acceptsGzip) {
    response.setGzip(true);
  }
}

function renderFramedHtml(contentHtml, plugin) {
  var getContentHtml;
  if (typeof(contentHtml) == 'function') {
    getContentHtml = contentHtml;
  } else {
    getContentHtml = function() { return contentHtml; }
  }

  var template = "framed/framedpage.ejs";
  if (isProDomainRequest()) {
    template = "framed/framedpage-pro.ejs";
  }

  renderHtml(template, {
    renderHeader: renderMainHeader,
    renderFooter: renderMainFooter,
    getContentHtml: getContentHtml,
    isProDomainRequest: isProDomainRequest(),
    renderGlobalProNotice: pro_utils.renderGlobalProNotice
  }, plugin);
}

function renderFramed(bodyFileName, data, plugin) {
  function _getContentHtml() {
    return renderTemplateAsString(bodyFileName, data, plugin);
  }
  renderFramedHtml(_getContentHtml);
}

function renderFramedError(error, plugin) {
  var content = DIV({className: 'fpcontent'},
                  DIV({style: "padding: 2em 1em;"},
                    DIV({style: "padding: 1em; border: 1px solid #faa; background: #fdd;"},
                        B("Error: "), error)));
  renderFramedHtml(content, plugin);
}

function renderNotice(bodyFileName, data, plugin) {
  renderNoticeString(renderTemplateAsString(bodyFileName, data, plugin), plugin);
}

function renderNoticeString(contentHtml, plugin) {
  renderFramed("notice.ejs", {content: contentHtml}, plugin);
}

function render404(noStop, plugin) {
  response.reset();
  response.setStatusCode(404);
  renderFramedHtml(DIV({className: "fpcontent"},
                    DIV({style: "padding: 2em 1em;"},
                       DIV({style: "border: 1px solid #aaf; background: #def; padding: 1em; font-size: 150%;"},
                        "404 not found: "+request.path))), plugin);
  if (! noStop) {
    response.stop();
  }
}

function render500(ex, plugin) {
  response.reset();
  response.setStatusCode(500);
  var trace = null;
  if (ex && (!isProduction())) {
    trace = exceptionutils.getStackTracePlain(ex);
  }
  renderFramed("500_body.ejs", {trace: trace}, plugin);
}

function _renderEtherpadDotComHeader(data) {
  if (!data) {
    data = {selected: ''};
  }
  data.html = stringutils.html;
  data.UL = UL;
  data.LI = LI;
  data.A = A;
  data.isPNE = isPrivateNetworkEdition();
  return renderTemplateAsString("framed/framedheader.ejs", data);
}

function _renderProHeader(data) {
  if (!pro_accounts.isAccountSignedIn()) {
    return '

';
  }

  var r = domains.getRequestDomainRecord();
  if (!data) { data = {}; }
  data.navSelection = (data.navSelection || appjet.requestCache.proTopNavSelection || '');
  data.proDomainOrgName = pro_config.getConfig().siteName;
  data.isPNE = isPrivateNetworkEdition();
  data.account = getSessionProAccount();
  data.validLicense = pne_utils.isServerLicensed();
  data.pneTrackerHtml = pne_utils.pneTrackerHtml();
  data.isAnEtherpadAdmin = sessions.isAnEtherpadAdmin();
  data.fullSuperdomain = pro_utils.getFullSuperdomainHost();
  return renderTemplateAsString("framed/framedheader-pro.ejs", data);
}

function renderMainHeader(data) {
  if (isProDomainRequest()) {
    return _renderProHeader(data);
  } else {
    return _renderEtherpadDotComHeader(data);
  }
}

function renderMainFooter() {
  return renderTemplateAsString("framed/framedfooter.ejs", {
    isProDomainRequest: isProDomainRequest()
  });
}

//----------------------------------------------------------------
// isValidEmail
//----------------------------------------------------------------

// TODO: make better and use the better version on the client in
// various places as well (pad.js and etherpad.js)
function isValidEmail(x) {
  return (x &&
          ((x.length > 0) &&
           (x.match(/^[\w\.\_\+\-]+\@[\w\_\-]+\.[\w\_\-\.]+$/))));
}

//----------------------------------------------------------------

function timeAgo(d, now) {
  if (!now) { now = new Date(); }

  function format(n, word) {
    n = Math.round(n);
    return ('' + n + ' ' + word + (n != 1 ? 's' : '') + ' ago');
  }

  d = (+now - (+d)) / 1000;
  if (d < 60) { return format(d, 'second'); }
  d /= 60;
  if (d < 60) { return format(d, 'minute'); }
  d /= 60;
  if (d < 24) { return format(d, 'hour'); }
  d /= 24;
  return format(d, 'day');
};

//----------------------------------------------------------------
// linking to a set of new CGI parameters
//----------------------------------------------------------------
function qpath(m) {
  var q = {};
  if (request.query) {
    request.query.split('&').forEach(function(kv) {
      if (kv) {
        var parts = kv.split('=');
        q[parts[0]] = parts[1];
      }
    });
  }
  eachProperty(m, function(k,v) {
    q[k] = v;
  });
  var r = request.path + '?';
  eachProperty(q, function(k,v) {
    if (v !== undefined && v !== null) {
      r += ('&' + k + '=' + v);
    }
  });
  return r;
}

//----------------------------------------------------------------

function ipToHostname(ip) {
  var DNS = Packages.org.xbill.DNS;

  if (!DNS.Address.isDottedQuad(ip)) {
    return null
  }

  try {
    var addr = DNS.Address.getByAddress(ip);
    return DNS.Address.getHostName(addr);
  } catch (ex) {
    return null;
  }
}

function extractGoogleQuery(ref) {
  ref = String(ref);
  ref = ref.toLowerCase();
  if (!(ref.indexOf("google") >= 0)) {
    return "";
  }

  ref = ref.split('?')[1];

  var q = "";
  ref.split("&").forEach(function(x) {
    var parts = x.split("=");
    if (parts[0] == "q") {
      q = parts[1];
    }
  });

  q = decodeURIComponent(q);
  q = q.replace(/\+/g, " ");

  return q;
}

function isTestEmail(x) {
  return (x.indexOf("+appjetseleniumtest+") >= 0);
}

function isPrivateNetworkEdition() {
  return pne_utils.isPNE();
}

function isProDomainRequest() {
  return pro_utils.isProDomainRequest();
}

function hasOffice() {
  return appjet.config["etherpad.soffice"] || appjet.config["etherpad.sofficeConversionServer"];
}

////////// console progress bar

function startConsoleProgressBar(barWidth, updateIntervalSeconds) {
  barWidth = barWidth || 40;
  updateIntervalSeconds = ((typeof updateIntervalSeconds) == "number" ? updateIntervalSeconds : 1.0);

  var unseenStatus = null;
  var lastPrintTime = 0;
  var column = 0;

  function replaceLineWith(str) {
    //print((new Array(column+1)).join('\b')+str);
    print('\r'+str);
    column = str.length;
  }

  var bar = {
    update: function(frac, msg, force) {
      var t = +new Date();
      if ((!force) && ((t - lastPrintTime)/1000 < updateIntervalSeconds)) {
        unseenStatus = {frac:frac, msg:msg};
      }
      else {
        var pieces = [];
        pieces.push(' ', ('  '+Math.round(frac*100)).slice(-3), '%', ' [');
        var barEndLoc = Math.max(0, Math.min(barWidth-1, Math.floor(frac*barWidth)));
        for(var i=0;i<barWidth;i++) {
          if (i < barEndLoc) pieces.push('=');
          else if (i == barEndLoc) pieces.push('>');
          else pieces.push(' ');
        }
        pieces.push('] ', msg || '');
        replaceLineWith(pieces.join(''));

        unseenStatus = null;
        lastPrintTime = t;
      }
    },
    finish: function() {
      if (unseenStatus) {
        bar.update(unseenStatus.frac, unseenStatus.msg, true);
      }
      println();
    }
  };

  println();
  bar.update(0, null, true);

  return bar;
}

function isStaticRequest() {
  return (startsWith(request.path, '/static/') ||
          startsWith(request.path, '/favicon.ico') ||
          startsWith(request.path, '/robots.txt'));
}

function httpsHost(h) {
  h = h.split(":")[0];  // strip any existing port
  if (appjet.config.listenSecurePort != "443") {
    h = (h + ":" + appjet.config.listenSecurePort);
  }
  return h;
}

function httpHost(h) {
  h = h.split(":")[0];  // strip any existing port
  if (appjet.config.listenPort != "80") {
    h = (h + ":" + appjet.config.listenPort);
  }
  return h;
}

function toJavaException(e) {
  var exc = ((e instanceof java.lang.Throwable) && e) || e.rhinoException || e.javaException ||
    new java.lang.Throwable(e.message+"/"+e.fileName+"/"+e.lineNumber);
  return exc;
}
<commit_msg>Made it possible to callHook from templates<commit_after>/**
 * Copyright 2009 Google Inc.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS-IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

import("exceptionutils");
import("fileutils.{readFile,fileLastModified}");
import("ejs.EJS");
import("funhtml.*");
import("stringutils");
import("stringutils.startsWith");
import("jsutils.*");

import("etherpad.sessions");
import("etherpad.sessions.getSession");
import("etherpad.globals.*");
import("etherpad.helpers");
import("etherpad.collab.collab_server");
import("etherpad.pad.model");
import("etherpad.pro.domains");
import("etherpad.pne.pne_utils");
import("etherpad.pro.pro_utils");
import("etherpad.pro.pro_config");
import("etherpad.pro.pro_accounts");
import("etherpad.pro.pro_accounts.getSessionProAccount");
import("etherpad.log");
import("etherpad.admin.plugins");

jimport("java.lang.System.out.print");
jimport("java.lang.System.out.println");

//----------------------------------------------------------------
// utilities
//----------------------------------------------------------------

// returns globally-unique padId
function randomUniquePadId() {
  var id = stringutils.randomString(10);
  while (model.accessPadGlobal(id, function(p) { return p.exists(); }, "r")) {
    id = stringutils.randomString(10);
  }
  return id;
}

//----------------------------------------------------------------
// template rendering
//----------------------------------------------------------------

function findTemplate(filename, plugin) {
  if (plugin != undefined)
   return '/plugins/' + plugin + '/templates/' + filename;
  else
   return '/templates/' + filename;
}

function renderTemplateAsString(filename, data, plugin) {
  data = data || {};
  data.helpers = helpers; // global helpers
  data.plugins = plugins; // Access callHook and the like...

  var f = findTemplate(filename, plugin); //"/templates/"+filename;
  if (! appjet.scopeCache.ejs) {
    appjet.scopeCache.ejs = {};
  }
  var cacheObj = appjet.scopeCache.ejs[filename];
  if (cacheObj === undefined || fileLastModified(f) > cacheObj.mtime) {
    var templateText = readFile(f);
    cacheObj = {};
    cacheObj.tmpl = new EJS({text: templateText, name: filename});
    cacheObj.mtime = fileLastModified(f);
    appjet.scopeCache.ejs[filename] = cacheObj;
  }
  var html = cacheObj.tmpl.render(data);
  return html;
}

function renderTemplate(filename, data, plugin) {
  response.write(renderTemplateAsString(filename, data, plugin));
  if (request.acceptsGzip) {
    response.setGzip(true);
  }
}

function renderHtml(bodyFileName, data, plugin) {
  var bodyHtml = renderTemplateAsString(bodyFileName, data, plugin);
  bodyHtml = plugins.callHookStr("renderPageBodyPre", {bodyFileName:bodyFileName, data:data, plugin:plugin}) +
             bodyHtml +
             plugins.callHookStr("renderPageBodyPost", {bodyFileName:bodyFileName, data:data, plugin:plugin});
  response.write(renderTemplateAsString("html.ejs", {bodyHtml: bodyHtml}));
  if (request.acceptsGzip) {
    response.setGzip(true);
  }
}

function renderFramedHtml(contentHtml, plugin) {
  var getContentHtml;
  if (typeof(contentHtml) == 'function') {
    getContentHtml = contentHtml;
  } else {
    getContentHtml = function() { return contentHtml; }
  }

  var template = "framed/framedpage.ejs";
  if (isProDomainRequest()) {
    template = "framed/framedpage-pro.ejs";
  }

  renderHtml(template, {
    renderHeader: renderMainHeader,
    renderFooter: renderMainFooter,
    getContentHtml: getContentHtml,
    isProDomainRequest: isProDomainRequest(),
    renderGlobalProNotice: pro_utils.renderGlobalProNotice
  }, plugin);
}

function renderFramed(bodyFileName, data, plugin) {
  function _getContentHtml() {
    return renderTemplateAsString(bodyFileName, data, plugin);
  }
  renderFramedHtml(_getContentHtml);
}

function renderFramedError(error, plugin) {
  var content = DIV({className: 'fpcontent'},
                  DIV({style: "padding: 2em 1em;"},
                    DIV({style: "padding: 1em; border: 1px solid #faa; background: #fdd;"},
                        B("Error: "), error)));
  renderFramedHtml(content, plugin);
}

function renderNotice(bodyFileName, data, plugin) {
  renderNoticeString(renderTemplateAsString(bodyFileName, data, plugin), plugin);
}

function renderNoticeString(contentHtml, plugin) {
  renderFramed("notice.ejs", {content: contentHtml}, plugin);
}

function render404(noStop, plugin) {
  response.reset();
  response.setStatusCode(404);
  renderFramedHtml(DIV({className: "fpcontent"},
                    DIV({style: "padding: 2em 1em;"},
                       DIV({style: "border: 1px solid #aaf; background: #def; padding: 1em; font-size: 150%;"},
                        "404 not found: "+request.path))), plugin);
  if (! noStop) {
    response.stop();
  }
}

function render500(ex, plugin) {
  response.reset();
  response.setStatusCode(500);
  var trace = null;
  if (ex && (!isProduction())) {
    trace = exceptionutils.getStackTracePlain(ex);
  }
  renderFramed("500_body.ejs", {trace: trace}, plugin);
}

function _renderEtherpadDotComHeader(data) {
  if (!data) {
    data = {selected: ''};
  }
  data.html = stringutils.html;
  data.UL = UL;
  data.LI = LI;
  data.A = A;
  data.isPNE = isPrivateNetworkEdition();
  return renderTemplateAsString("framed/framedheader.ejs", data);
}

function _renderProHeader(data) {
  if (!pro_accounts.isAccountSignedIn()) {
    return '

';
  }

  var r = domains.getRequestDomainRecord();
  if (!data) { data = {}; }
  data.navSelection = (data.navSelection || appjet.requestCache.proTopNavSelection || '');
  data.proDomainOrgName = pro_config.getConfig().siteName;
  data.isPNE = isPrivateNetworkEdition();
  data.account = getSessionProAccount();
  data.validLicense = pne_utils.isServerLicensed();
  data.pneTrackerHtml = pne_utils.pneTrackerHtml();
  data.isAnEtherpadAdmin = sessions.isAnEtherpadAdmin();
  data.fullSuperdomain = pro_utils.getFullSuperdomainHost();
  return renderTemplateAsString("framed/framedheader-pro.ejs", data);
}

function renderMainHeader(data) {
  if (isProDomainRequest()) {
    return _renderProHeader(data);
  } else {
    return _renderEtherpadDotComHeader(data);
  }
}

function renderMainFooter() {
  return renderTemplateAsString("framed/framedfooter.ejs", {
    isProDomainRequest: isProDomainRequest()
  });
}

//----------------------------------------------------------------
// isValidEmail
//----------------------------------------------------------------

// TODO: make better and use the better version on the client in
// various places as well (pad.js and etherpad.js)
function isValidEmail(x) {
  return (x &&
          ((x.length > 0) &&
           (x.match(/^[\w\.\_\+\-]+\@[\w\_\-]+\.[\w\_\-\.]+$/))));
}

//----------------------------------------------------------------

function timeAgo(d, now) {
  if (!now) { now = new Date(); }

  function format(n, word) {
    n = Math.round(n);
    return ('' + n + ' ' + word + (n != 1 ? 's' : '') + ' ago');
  }

  d = (+now - (+d)) / 1000;
  if (d < 60) { return format(d, 'second'); }
  d /= 60;
  if (d < 60) { return format(d, 'minute'); }
  d /= 60;
  if (d < 24) { return format(d, 'hour'); }
  d /= 24;
  return format(d, 'day');
};

//----------------------------------------------------------------
// linking to a set of new CGI parameters
//----------------------------------------------------------------
function qpath(m) {
  var q = {};
  if (request.query) {
    request.query.split('&').forEach(function(kv) {
      if (kv) {
        var parts = kv.split('=');
        q[parts[0]] = parts[1];
      }
    });
  }
  eachProperty(m, function(k,v) {
    q[k] = v;
  });
  var r = request.path + '?';
  eachProperty(q, function(k,v) {
    if (v !== undefined && v !== null) {
      r += ('&' + k + '=' + v);
    }
  });
  return r;
}

//----------------------------------------------------------------

function ipToHostname(ip) {
  var DNS = Packages.org.xbill.DNS;

  if (!DNS.Address.isDottedQuad(ip)) {
    return null
  }

  try {
    var addr = DNS.Address.getByAddress(ip);
    return DNS.Address.getHostName(addr);
  } catch (ex) {
    return null;
  }
}

function extractGoogleQuery(ref) {
  ref = String(ref);
  ref = ref.toLowerCase();
  if (!(ref.indexOf("google") >= 0)) {
    return "";
  }

  ref = ref.split('?')[1];

  var q = "";
  ref.split("&").forEach(function(x) {
    var parts = x.split("=");
    if (parts[0] == "q") {
      q = parts[1];
    }
  });

  q = decodeURIComponent(q);
  q = q.replace(/\+/g, " ");

  return q;
}

function isTestEmail(x) {
  return (x.indexOf("+appjetseleniumtest+") >= 0);
}

function isPrivateNetworkEdition() {
  return pne_utils.isPNE();
}

function isProDomainRequest() {
  return pro_utils.isProDomainRequest();
}

function hasOffice() {
  return appjet.config["etherpad.soffice"] || appjet.config["etherpad.sofficeConversionServer"];
}

////////// console progress bar

function startConsoleProgressBar(barWidth, updateIntervalSeconds) {
  barWidth = barWidth || 40;
  updateIntervalSeconds = ((typeof updateIntervalSeconds) == "number" ? updateIntervalSeconds : 1.0);

  var unseenStatus = null;
  var lastPrintTime = 0;
  var column = 0;

  function replaceLineWith(str) {
    //print((new Array(column+1)).join('\b')+str);
    print('\r'+str);
    column = str.length;
  }

  var bar = {
    update: function(frac, msg, force) {
      var t = +new Date();
      if ((!force) && ((t - lastPrintTime)/1000 < updateIntervalSeconds)) {
        unseenStatus = {frac:frac, msg:msg};
      }
      else {
        var pieces = [];
        pieces.push(' ', ('  '+Math.round(frac*100)).slice(-3), '%', ' [');
        var barEndLoc = Math.max(0, Math.min(barWidth-1, Math.floor(frac*barWidth)));
        for(var i=0;i<barWidth;i++) {
          if (i < barEndLoc) pieces.push('=');
          else if (i == barEndLoc) pieces.push('>');
          else pieces.push(' ');
        }
        pieces.push('] ', msg || '');
        replaceLineWith(pieces.join(''));

        unseenStatus = null;
        lastPrintTime = t;
      }
    },
    finish: function() {
      if (unseenStatus) {
        bar.update(unseenStatus.frac, unseenStatus.msg, true);
      }
      println();
    }
  };

  println();
  bar.update(0, null, true);

  return bar;
}

function isStaticRequest() {
  return (startsWith(request.path, '/static/') ||
          startsWith(request.path, '/favicon.ico') ||
          startsWith(request.path, '/robots.txt'));
}

function httpsHost(h) {
  h = h.split(":")[0];  // strip any existing port
  if (appjet.config.listenSecurePort != "443") {
    h = (h + ":" + appjet.config.listenSecurePort);
  }
  return h;
}

function httpHost(h) {
  h = h.split(":")[0];  // strip any existing port
  if (appjet.config.listenPort != "80") {
    h = (h + ":" + appjet.config.listenPort);
  }
  return h;
}

function toJavaException(e) {
  var exc = ((e instanceof java.lang.Throwable) && e) || e.rhinoException || e.javaException ||
    new java.lang.Throwable(e.message+"/"+e.fileName+"/"+e.lineNumber);
  return exc;
}
<|endoftext|>


<commit_before>var Request = require('./request');
var Helpers = require('../../helpers');
var _ = require('lodash');
var Media = require('./media');
var Account = require('./account');

module.exports = function(session, inSingup) {
    return new Request(session)
        .setMethod('POST')
        .setResource('discoverAyml')
        .generateUUID()
        .setData({
            phone_id: Helpers.generateUUID(),
            in_singup: inSingup ? 'true' : 'false',
            module: 'ayml_recommended_users'
        })
        .send()
        .then(function(json) {
            var groups = _.first(json.groups || []);
            var items = groups.items || [];
            return _.map(items, function(item) {
                return {
                    account: new Account(session, item.user),
                    mediaIds: item.media_ids
                }
            })
        })
};

<commit_msg>Fix request property and response properties<commit_after>var Request = require('./request');
var Helpers = require('../../helpers');
var _ = require('lodash');
var Media = require('./media');
var Account = require('./account');

module.exports = function(session, inSingup) {
    return new Request(session)
        .setMethod('POST')
        .setResource('discoverAyml')
        .generateUUID()
        .setData({
            phone_id: Helpers.generateUUID(),
            in_signup: inSingup ? 'true' : 'false',
            module: 'discover_people'
        })
        .send()
        .then(function(json) {
            var items = _.property('suggested_users.suggestions')(json) || [];
            return _.map(items, function(item) {
                return {
                    account: new Account(session, item.user),
                    mediaIds: item.media_ids
                }
            })
        })
};

<|endoftext|>


<commit_before>var rle = require('../shared/rle');
var highlight = require('voxel-highlight');
var skin = require('minecraft-skin');
var player = require('voxel-player');
var io = require('socket.io-client');
var engine = require('voxel-engine');
var chat = require('./chat');
var auth = require('./auth');

module.exports = Client;

function Client(server) {
  if(!(this instanceof Client)) {
    return new Client(server);
  }

  this.connected = false;
  this.lerpPercent = 0.1;
  this.others = {};
  this.connect(server);
}

Client.prototype.connect = function(server) {
  var self = this;
  var socket = io.connect(server);
  socket.on('disconnect', function() {
    self.connected = false;
  });
  this.socket = socket;
  chat.init(auth.getName(), socket);
  this.bindEvents(socket);
};

Client.prototype.bindEvents = function(socket) {
  var self = this;
  this.connected = true;

  socket.on('id', function(id) {
    self.playerID = id;
  });

  var processChunk = function(chunk) {
    var voxels = rle.decode(chunk.voxels);
    chunk.voxels = voxels;
    self.game.showChunk(chunk);
  };

  socket.on('settings', function(settings) {
    settings.generateChunks = false;
    self.game = self.createGame(settings);
    socket.emit('created');
    socket.on('chunk', processChunk);
    socket.on('noMoreChunks', function() {
      self.game.voxels.on('missingChunk', function(chunkPosition) {
        socket.emit('requestChunk', chunkPosition, processChunk);
      });
    });
  });

  socket.on('set', function(pos, val) {
    self.game.setBlock(pos, val);
  });
};

Client.prototype.createGame = function(settings) {
  var self = this;
  var socket = this.socket;
  settings.controlsDisabled = false;
  self.game = engine(settings);
  self.game.settings = settings;

  function sendState() {
    if (!self.connected) {
      return;
    }
    var player = self.game.controls.target();
    var state = {
      position: player.yaw.position,
      rotation: {
        y: player.yaw.rotation.y,
        x: player.pitch.rotation.x
      }
    };
    socket.emit('state', state);
  }

  self.game.controls.on('data', function(state) {
    var interacting = false;
    Object.keys(state).map(function(control) {
      if (state[control] > 0) {
        interacting = true;
      }
    });

    if (interacting) {
      sendState();
    }
  });

  // setTimeout is because three.js seems to throw errors if you add stuff too soon
  setTimeout(function() {
    socket.on('update', function(updates) {
      Object.keys(updates.positions).map(function(player) {
        var update = updates.positions[player];
        if (player === self.playerID) {
          return self.onServerUpdate(update); // local player
        }
        self.updatePlayerPosition(player, update); // other players
      })
    })
  }, 1000)

  socket.on('leave', function(id) {
    if (!self.others[id]) {
      return;
    }
    self.game.scene.remove(self.others[id].mesh);
    delete self.others[id];
  });

  return self.game;
};

Client.prototype.onServerUpdate = function(update) {
  // TODO use server sent location
};

Client.prototype.updatePlayerPosition = function(id, update) {
  var pos = update.position;
  var player = this.others[id];
  if (!player) {
    var playerSkin = skin(this.game.THREE, 'assets/avatars/player.png', {
      scale: new this.game.THREE.Vector3(0.04, 0.04, 0.04)
    });
    var playerMesh = playerSkin.mesh;
    this.others[id] = playerSkin;
    playerMesh.children[0].position.y = 10;
    this.game.scene.add(playerMesh);
  }

  var playerSkin = this.others[id];
  var playerMesh = playerSkin.mesh;
  playerMesh.position.copy(playerMesh.position.lerp(pos, this.lerpPercent));

  playerMesh.children[0].rotation.y = update.rotation.y + (Math.PI / 2);
  playerSkin.head.rotation.z = scale(update.rotation.x, -1.5, 1.5, -0.75, 0.75);
};

function scale( x, fromLow, fromHigh, toLow, toHigh ) {
  return ( x - fromLow ) * ( toHigh - toLow ) / ( fromHigh - fromLow ) + toLow;
}
<commit_msg>keeping the connected property of the voxel client updated<commit_after>var rle = require('../shared/rle');
var highlight = require('voxel-highlight');
var skin = require('minecraft-skin');
var player = require('voxel-player');
var io = require('socket.io-client');
var engine = require('voxel-engine');
var chat = require('./chat');
var auth = require('./auth');

module.exports = Client;

function Client(server) {
  if(!(this instanceof Client)) {
    return new Client(server);
  }

  this.connected = false;
  this.lerpPercent = 0.1;
  this.others = {};
  this.connect(server);
}

Client.prototype.connect = function(server) {
  var self = this;
  var socket = io.connect(server);
  socket.on('disconnect', function() {
    self.connected = false;
  });
  this.socket = socket;
  chat.init(auth.getName(), socket);
  this.bindEvents(socket);
};

Client.prototype.bindEvents = function(socket) {
  var self = this;

  socket.on('id', function(id) {
    this.connected = true;
    self.playerID = id;
  });

  var processChunk = function(chunk) {
    var voxels = rle.decode(chunk.voxels);
    chunk.voxels = voxels;
    self.game.showChunk(chunk);
  };

  socket.on('settings', function(settings) {
    settings.generateChunks = false;
    self.game = self.createGame(settings);
    socket.emit('created');
    socket.on('chunk', processChunk);
    socket.on('noMoreChunks', function() {
      self.game.voxels.on('missingChunk', function(chunkPosition) {
        socket.emit('requestChunk', chunkPosition, processChunk);
      });
    });
  });

  socket.on('set', function(pos, val) {
    self.game.setBlock(pos, val);
  });
};

Client.prototype.createGame = function(settings) {
  var self = this;
  var socket = this.socket;
  settings.controlsDisabled = false;
  self.game = engine(settings);
  self.game.settings = settings;

  function sendState() {
    if (!self.connected) {
      return;
    }
    var player = self.game.controls.target();
    var state = {
      position: player.yaw.position,
      rotation: {
        y: player.yaw.rotation.y,
        x: player.pitch.rotation.x
      }
    };
    socket.emit('state', state);
  }

  self.game.controls.on('data', function(state) {
    var interacting = false;
    Object.keys(state).map(function(control) {
      if (state[control] > 0) {
        interacting = true;
      }
    });

    if (interacting) {
      sendState();
    }
  });

  // setTimeout is because three.js seems to throw errors if you add stuff too soon
  setTimeout(function() {
    socket.on('update', function(updates) {
      Object.keys(updates.positions).map(function(player) {
        var update = updates.positions[player];
        if (player === self.playerID) {
          return self.onServerUpdate(update); // local player
        }
        self.updatePlayerPosition(player, update); // other players
      })
    })
  }, 1000)

  socket.on('leave', function(id) {
    if (!self.others[id]) {
      return;
    }
    self.game.scene.remove(self.others[id].mesh);
    delete self.others[id];
  });

  return self.game;
};

Client.prototype.onServerUpdate = function(update) {
  // TODO use server sent location
};

Client.prototype.updatePlayerPosition = function(id, update) {
  var pos = update.position;
  var player = this.others[id];
  if (!player) {
    var playerSkin = skin(this.game.THREE, 'assets/avatars/player.png', {
      scale: new this.game.THREE.Vector3(0.04, 0.04, 0.04)
    });
    var playerMesh = playerSkin.mesh;
    this.others[id] = playerSkin;
    playerMesh.children[0].position.y = 10;
    this.game.scene.add(playerMesh);
  }

  var playerSkin = this.others[id];
  var playerMesh = playerSkin.mesh;
  playerMesh.position.copy(playerMesh.position.lerp(pos, this.lerpPercent));

  playerMesh.children[0].rotation.y = update.rotation.y + (Math.PI / 2);
  playerSkin.head.rotation.z = scale(update.rotation.x, -1.5, 1.5, -0.75, 0.75);
};

function scale( x, fromLow, fromHigh, toLow, toHigh ) {
  return ( x - fromLow ) * ( toHigh - toLow ) / ( fromHigh - fromLow ) + toLow;
}
<|endoftext|>


<commit_before>$(document).ready(function() {
 $("#changements_dateDemande").datepicker({
        maxDate: "+5Y",
        minDate: "-5Y",
        changeMonth: true,
        changeYear: true,
        numberOfMonths: 1,
        dateFormat: "yy-mm-dd",
        onClose: function(selectedDate) {
            $("#form_bis").datepicker("option", "minDate", selectedDate);
        }
    });
    //$(function() {
    $("#changements_dateDebut").datepicker({
        minDate: "-5Y",
        maxDate: "+10Y",
        changeMonth: true,
        changeYear: true,
        numberOfMonths: 1,
         dateFormat: "yy-mm-dd 08:00",
        onClose: function(selectedDate) {
            $("#form_bis").datepicker("option", "minDate", selectedDate);
        }
    });
    $("#changements_dateFin").datepicker({
        maxDate: "+5Y",
        minDate: "-5Y",
        changeMonth: true,
        changeYear: true,
        numberOfMonths: 1,
        dateFormat: "yy-mm-dd 17:00",
        onClose: function(selectedDate) {
            $("#form_bis").datepicker("option", "minDate", selectedDate);
        }
    });
    $("#changements_dateComep").datepicker({
        maxDate: "+5Y",
        minDate: "-5Y",
        changeMonth: true,
        changeYear: true,
        numberOfMonths: 1,
        dateFormat: "yy-mm-dd",
        onClose: function(selectedDate) {
            $("#form_bis").datepicker("option", "minDate", selectedDate);
        }
    });
    $("#changements_dateVsr").datepicker({
        maxDate: "+5Y",
        minDate: "-5Y",
        changeMonth: true,
        changeYear: true,
        numberOfMonths: 1,
        dateFormat: "yy-mm-dd",
        onClose: function(selectedDate) {
            $("#form_bis").datepicker("option", "minDate", selectedDate);
        }
    });
     $("select#changements_idapplis").select2({
                placeholder: "-- Choisir Application(s) --",
                allowClear: true
            });
             $("#changements_searchfilter_demandeur").select2();
              $("#changements_idProjet").select2({
                placeholder: "-- Choisir Projet(s) --",
                allowClear: true
            });
 /* select id="changements_idStatus"*/
}); //Eof:: ready
<commit_msg>Update jquery recherche<commit_after>$(document).ready(function() {
    var img_s_path = '/bundles/applicationchangements/images/';

 $("#changements_dateDemande").datepicker({
        maxDate: "+5Y",
        minDate: "-5Y",
        changeMonth: true,
        changeYear: true,
        numberOfMonths: 1,
        dateFormat: "yy-mm-dd",
        onClose: function(selectedDate) {
            $("#form_bis").datepicker("option", "minDate", selectedDate);
        }
    });
    //$(function() {
    $("#changements_dateDebut").datepicker({
        minDate: "-5Y",
        maxDate: "+10Y",
        changeMonth: true,
        changeYear: true,
        numberOfMonths: 1,
         dateFormat: "yy-mm-dd 08:00",
        onClose: function(selectedDate) {
            $("#form_bis").datepicker("option", "minDate", selectedDate);
        }
    });
    $("#changements_dateFin").datepicker({
        maxDate: "+5Y",
        minDate: "-5Y",
        changeMonth: true,
        changeYear: true,
        numberOfMonths: 1,
        dateFormat: "yy-mm-dd 17:00",
        onClose: function(selectedDate) {
            $("#form_bis").datepicker("option", "minDate", selectedDate);
        }
    });
    $("#changements_dateComep").datepicker({
        maxDate: "+5Y",
        minDate: "-5Y",
        changeMonth: true,
        changeYear: true,
        numberOfMonths: 1,
        dateFormat: "yy-mm-dd",
        onClose: function(selectedDate) {
            $("#form_bis").datepicker("option", "minDate", selectedDate);
        }
    });
    $("#changements_dateVsr").datepicker({
        maxDate: "+5Y",
        minDate: "-5Y",
        changeMonth: true,
        changeYear: true,
        numberOfMonths: 1,
        dateFormat: "yy-mm-dd",
        onClose: function(selectedDate) {
            $("#form_bis").datepicker("option", "minDate", selectedDate);
        }
    });

      function format(state) {
            if (!state.id) return state.text; // optgroup
            return "<img class='flag' src='" + img_s_path + state.id.toLowerCase() + ".png'/> " + state.text;
        }
        $("#changements_idStatus").select2({
             placeholder: "-- Choisir Statut(s) --",
                allowClear: true,
            formatResult: format,
            formatSelection: format,
            escapeMarkup: function(m) { return m; }
        });

     $("select#changements_idapplis").select2({
                placeholder: "-- Choisir Application(s) --",
                allowClear: true
            });
             $("select#changements_idusers").select2({
                placeholder: "-- Choisir User(s) --",
                allowClear: true
            });

               $("#changements_demandeur").select2();
                $("#changements_searchfilter_idProjet").select2({
                placeholder: "-- Choisir Projet(s) --",
                allowClear: true
            });

              $("#changements_idProjet").select2({
                placeholder: "-- Choisir Projet(s) --",
                allowClear: true
            });
}); //Eof:: ready
<|endoftext|>


<commit_before>var test = require('tape');
var TypeSelector = require('../lib/TypeSelector');

var fixtures = F = {
    option1: {
        'label': 'label1',
        'value': 'value1'
    },
    option2: {
        'label': 'label2',
        'value': 'value2'
    },
    option3: {
        'label': 'label3',
        'value': 'value3'
    },
};

test('Pass Settings Into constructor', function(t) {
    t.plan(3);

    var testSelector = new TypeSelector({foo: 'bar'});

    t.equals(testSelector.settings.foo, 'bar');
    t.equals(testSelector.settings.options.length, 0);

    testSelector = new TypeSelector({options: [F.option1, F.option2]});
    t.equals(testSelector.settings.options.length, 2);
});

test('UsePlaceholder as default setting', function(t) {
    t.plan(2);

    var testSelector = new TypeSelector();
    t.equals(testSelector.options.length, 1);

    testSelector = new TypeSelector({usePlaceholder: false});
    t.equals(testSelector.options.length, 0);
});

test('Add Options', function(t) {
    t.plan(4);

    var testSelector = new TypeSelector({usePlaceholder: false});

    testSelector.addOption(F.option1);
    t.equals(testSelector.options.length, 1);

    testSelector.addOption('baz');
    t.equals(testSelector.options.length, 2);

    testSelector.addOption(F.option1);
    t.equals(testSelector.options.length, 2);

    testSelector.addOption(F.option2);
    t.equals(testSelector.options.length, 3);
});

test('Remove Options', function(t) {
    t.plan(3);

    var testSelector = new TypeSelector({usePlaceholder: false});

    testSelector.addOption(F.option1);
    testSelector.addOption('baz');
    testSelector.addOption(F.option2);

    testSelector.removeOption('baz');
    t.equals(testSelector.options.length, 2);

    testSelector.removeOption('bar');
    t.equals(testSelector.options.length, 2);

    testSelector.removeOption(F.option1);
    t.equals(testSelector.options.length, 1);
});

test('Rendered Structure', function(t) {
    t.plan(1);

    var testSelector = new TypeSelector({usePlaceholder: false});
    document.body.appendChild(testSelector.el);

    testSelector.addOption(F.option1);
    testSelector.addOption('baz');
    testSelector.addOption(F.option2);
    testSelector.removeOption(F.option1);

    var selectBox = document.getElementsByClassName('eol-interaction-type-selector')[0];
    var options = selectBox.children;

    t.equals(options.length, 2);
});

test('Clear', function(t) {
    t.plan(1);

    var testSelector = new TypeSelector();
    document.body.appendChild(testSelector.el);

    testSelector.addOption(F.option1);
    testSelector.addOption('baz');
    testSelector.addOption(F.option2);
    testSelector.removeOption(F.option1);

    testSelector.clear();
    t.equals(testSelector.options.length, 0);
});
<commit_msg>remove comma<commit_after>var test = require('tape');
var TypeSelector = require('../lib/TypeSelector');

var fixtures = F = {
    option1: {
        'label': 'label1',
        'value': 'value1'
    },
    option2: {
        'label': 'label2',
        'value': 'value2'
    },
    option3: {
        'label': 'label3',
        'value': 'value3'
    }
};

test('Pass Settings Into constructor', function(t) {
    t.plan(3);

    var testSelector = new TypeSelector({foo: 'bar'});

    t.equals(testSelector.settings.foo, 'bar');
    t.equals(testSelector.settings.options.length, 0);

    testSelector = new TypeSelector({options: [F.option1, F.option2]});
    t.equals(testSelector.settings.options.length, 2);
});

test('UsePlaceholder as default setting', function(t) {
    t.plan(2);

    var testSelector = new TypeSelector();
    t.equals(testSelector.options.length, 1);

    testSelector = new TypeSelector({usePlaceholder: false});
    t.equals(testSelector.options.length, 0);
});

test('Add Options', function(t) {
    t.plan(4);

    var testSelector = new TypeSelector({usePlaceholder: false});

    testSelector.addOption(F.option1);
    t.equals(testSelector.options.length, 1);

    testSelector.addOption('baz');
    t.equals(testSelector.options.length, 2);

    testSelector.addOption(F.option1);
    t.equals(testSelector.options.length, 2);

    testSelector.addOption(F.option2);
    t.equals(testSelector.options.length, 3);
});

test('Remove Options', function(t) {
    t.plan(3);

    var testSelector = new TypeSelector({usePlaceholder: false});

    testSelector.addOption(F.option1);
    testSelector.addOption('baz');
    testSelector.addOption(F.option2);

    testSelector.removeOption('baz');
    t.equals(testSelector.options.length, 2);

    testSelector.removeOption('bar');
    t.equals(testSelector.options.length, 2);

    testSelector.removeOption(F.option1);
    t.equals(testSelector.options.length, 1);
});

test('Rendered Structure', function(t) {
    t.plan(1);

    var testSelector = new TypeSelector({usePlaceholder: false});
    document.body.appendChild(testSelector.el);

    testSelector.addOption(F.option1);
    testSelector.addOption('baz');
    testSelector.addOption(F.option2);
    testSelector.removeOption(F.option1);

    var selectBox = document.getElementsByClassName('eol-interaction-type-selector')[0];
    var options = selectBox.children;

    t.equals(options.length, 2);
});

test('Clear', function(t) {
    t.plan(1);

    var testSelector = new TypeSelector();
    document.body.appendChild(testSelector.el);

    testSelector.addOption(F.option1);
    testSelector.addOption('baz');
    testSelector.addOption(F.option2);
    testSelector.removeOption(F.option1);

    testSelector.clear();
    t.equals(testSelector.options.length, 0);
});
<|endoftext|>


<commit_before>
/**
 *  No georeference data dialog appears when there is no data or data georeferenced
 *
 *  var dlg = new cdb.admin.NoGeoRefDataDialog({
 *    model: table_model,
 *    geocoder: geocoder
 *  })
 *
 */

cdb.admin.NoGeoRefDataDialog = cdb.admin.BaseDialog.extend({

  _TEXTS: {
    title:      _t('No georeferenced data on your layer'),
    content:    _t('Although we can see you have data, it does not \
                appear to be georeferenced. You will not see anything on the map if \
                your records are not georeferenced. Click on Georeference if you want \
                to use a geocoder to get location out of your data, or cancel.'),
    no_content: _t('There seems to be no data on this layer, so there is nothing \
                to represent on the map. You can add some data or run SQL queries \
                to select some.'),
    close:      _t('Ok, close'),
    georef:     _t('Georeference'),
    cancel:     _t('Cancel')
  },

  initialize: function(options) {
    _.extend(this.options, {
      title: this._TEXTS.title,
      content_classes: "grey",
      content: '',
      template_name: 'table/views/noGeoRef_dialog',
      clean_on_hide: true,
      hasContent: false,
      ok_button_classes: "button grey",
      cancel_button_classes: "enabled",
      cancel_title: this._TEXTS.cancel,
      modal_type: "noGeoRef",
      width: 525,
      error_messages: {}
    });

    this.user = this.options.user;

    this.elder('initialize');
  },

  /**
   * Render the content for the create dialog
   */
  render_content: function() {
    this.hasContent = (this.model._data.length  > 0);
    this.$('a.ok').text(this.hasContent ? this._TEXTS.georef : this._TEXTS.close );

    if (!this.hasContent) {
      this.$('a.cancel').hide();
    }

    return this.hasContent ? this._TEXTS.content : this._TEXTS.no_content;
  },

  _onGeocodingChosen: function(data) {
    // Lon-lat type?
    if (data.type == "lonlat") {
      this.model.geocodeLatAndLng(data.latitude, data.longitude);
      return false;
    }

    // Address, city, region, postal or ip?
    if (data.type !== "lonlat") {
      // Remove unnecesary data in any case and start geocoder
      this.options.geocoder.set(_.omit(data, 'table_name', 'type'));
      return false;
    }

    cdb.log.info('No geocoder option for ' + data.type, obj);
  },

  ok: function() {
    if (this.hasContent) {
      var dlg;
      if (!this.options.geocoder.isGeocoding() && !this.model.isSync()) {

        dlg = new cdb.admin.GeocodingDialog({
          table:  this.model,
          user:   this.user,
          tabs:   ['lonlat', 'city', 'admin', 'postal', 'ip', 'address'],
          option: 'lonlat'
        });

        dlg.bind('geocodingChosen', this._onGeocodingChosen, this);

      } else if (this.options.geocoder.isGeocoding()) {
        dlg = new cdb.admin.GeocoderWorking();
      } else {
        // If table can't geocode == is synched, return!
        return;
      }

      dlg.appendToBody().open({ center:true });
    }
  }

});<commit_msg>Cleaned up the NoGeoRefDataDialog<commit_after>
/**
 *  No georeference data dialog appears when there is no data or data georeferenced
 *
 *  var dlg = new cdb.admin.NoGeoRefDataDialog({
 *    model: table_model,
 *    geocoder: geocoder
 *  })
 *
 */

cdb.admin.NoGeoRefDataDialog = cdb.admin.BaseDialog.extend({

  _TEXTS: {
    title:      _t('No georeferenced data on your layer'),
    content:    _t('Although we can see you have data, it does not \
                appear to be georeferenced. You will not see anything on the map if \
                your records are not georeferenced. Click on Georeference if you want \
                to use a geocoder to get location out of your data, or cancel.'),
    georef:     _t('Georeference'),
    cancel:     _t('Cancel')
  },

  initialize: function(options) {
    _.extend(this.options, {
      title: this._TEXTS.title,
      content_classes: "grey",
      content: '',
      template_name: 'table/views/noGeoRef_dialog',
      clean_on_hide: true,
      hasContent: false,
      ok_button_classes: "button grey",
      cancel_button_classes: "enabled",
      cancel_title: this._TEXTS.cancel,
      modal_type: "noGeoRef",
      width: 525,
      error_messages: {}
    });

    this.user = this.options.user;

    this.elder('initialize');
  },

  /**
   * Render the content for the create dialog
   */
  render_content: function() {
    this.hasContent = (this.model._data.length  > 0);
    this.$('a.ok').text(this._TEXTS.georef);

    return this._TEXTS.content;
  },

  _onGeocodingChosen: function(data) {
    // Lon-lat type?
    if (data.type == "lonlat") {
      this.model.geocodeLatAndLng(data.latitude, data.longitude);
      return false;
    }

    // Address, city, region, postal or ip?
    if (data.type !== "lonlat") {
      // Remove unnecesary data in any case and start geocoder
      this.options.geocoder.set(_.omit(data, 'table_name', 'type'));
      return false;
    }

    cdb.log.info('No geocoder option for ' + data.type, obj);
  },

  ok: function() {
    if (this.hasContent) {
      var dlg;
      if (!this.options.geocoder.isGeocoding() && !this.model.isSync()) {

        dlg = new cdb.admin.GeocodingDialog({
          table:  this.model,
          user:   this.user,
          tabs:   ['lonlat', 'city', 'admin', 'postal', 'ip', 'address'],
          option: 'lonlat'
        });

        dlg.bind('geocodingChosen', this._onGeocodingChosen, this);

      } else if (this.options.geocoder.isGeocoding()) {
        dlg = new cdb.admin.GeocoderWorking();
      } else {
        // If table can't geocode == is synched, return!
        return;
      }

      dlg.appendToBody().open({ center:true });
    }
  }

});<|endoftext|>


<commit_before>
/**
 * view for dropdown show when user click on row options
 */
cdb.admin.RowHeaderDropdown = cdb.admin.DropdownMenu.extend({

  className: 'dropdown border',

  events: {
    'click .delete_row': 'deleteRow',
    'click .add_row': 'addRow'
  },

  initialize: function(options) {
    this.tableData = options.tableData;
    this.table = this.options.table;
    this.elder('initialize');
  },

  // New show function
  show: function() {
    var dfd = $.Deferred();
    var self = this;
    //sometimes this dialog is child of a node that is removed
    //for that reason we link again DOM events just in case
    this.delegateEvents();

    this.$el
      .css({
        marginTop: self.options.vertical_position == "down" ? "-1px" : "-50px",
        marginLeft: -5,
        opacity:0,
        display:"block"
      })
      .animate({
        marginLeft: 5,
        opacity: 1
      }, {
        "duration": this.options.speedIn,
        "complete": function(){
          dfd.resolve();
        }
      });
    this.trigger("onDropdownShown",this.el);

    return dfd.promise();
  },

  hide: function(done) {

    // don't attempt to hide the dropdown if it's already hidden
    if (!this.isOpen) { done && done(); return; }

    var self    = this;
    this.isOpen = false;

    this.$el.animate({

      marginLeft: 15,
      opacity: 0

    }, this.options.speedOut, function(){

      // Remove selected class
      $(self.options.target).removeClass("selected");

      // And hide it
      self.$el.hide();
      done && done();

    });

    this.trigger("onDropdownHidden",this.el);

  },

  setRow: function(row) {
    this.row = row;
  },

  deleteRow: function(e) {
    var self = this;
    this.killEvent(e);

    var delete_confirmation = new cdb.admin.BaseDialog({
      title: "Delete this row",
      description: "Are you sure you want to delete this row and all its associated data?",
      template_name: 'common/views/confirm_dialog',
      clean_on_hide: true,
      enter_to_confirm: true,
      ok_button_classes: "right button grey",
      ok_title: "Yes, do it",
      cancel_button_classes: "underline margin15",
      cancel_title: "Cancel",
      modal_type: "confirmation",
      width: 500
    });

    // If user confirms, app removes the row
    delete_confirmation.ok = function() {
      self.table.trigger('removing:row');
      self.row.destroy({
        success: function() {
          self.table.trigger('remove:row', self.row);
        }
      });
    }

    delete_confirmation
      .appendToBody()
      .open();

    this.hide();

    return false;
  },

  addRow: function(e) {
    this.killEvent(e);
    var rowIndex = this.row.collection.indexOf(this.row);

    this.tableData.addRow({ at: rowIndex + 1 });
    this.hide();
    return false;
  }
});
<commit_msg>fixed problem with row options dropdown<commit_after>
/**
 * view for dropdown show when user click on row options
 */
cdb.admin.RowHeaderDropdown = cdb.admin.DropdownMenu.extend({

  className: 'dropdown border',

  events: {
    'click .delete_row': 'deleteRow',
    'click .add_row': 'addRow'
  },

  initialize: function(options) {
    this.tableData = options.tableData;
    this.table = this.options.table;
    this.elder('initialize');
  },

  openAt: function(x, y) {
    this.$el.removeClass('vertical_top vertical_bottom horizontal_right horigonzal_left tick_top tick_bottom');
    this.constructor.__super__.openAt.apply(this, arguments);
  },

  // New show function
  show: function() {
    var dfd = $.Deferred();
    var self = this;
    //sometimes this dialog is child of a node that is removed
    //for that reason we link again DOM events just in case
    this.delegateEvents();

    this.$el
      .css({
        marginTop: self.options.vertical_position == "down" ? "-1px" : "-50px",
        marginLeft: -5,
        opacity:0,
        display:"block"
      })
      .animate({
        marginLeft: 5,
        opacity: 1
      }, {
        "duration": this.options.speedIn,
        "complete": function(){
          dfd.resolve();
        }
      });
    this.trigger("onDropdownShown",this.el);

    return dfd.promise();
  },

  hide: function(done) {

    // don't attempt to hide the dropdown if it's already hidden
    if (!this.isOpen) { done && done(); return; }

    var self    = this;
    this.isOpen = false;

    this.$el.animate({

      marginLeft: 15,
      opacity: 0

    }, this.options.speedOut, function(){

      // Remove selected class
      $(self.options.target).removeClass("selected");

      // And hide it
      self.$el.hide();
      done && done();

    });

    this.trigger("onDropdownHidden",this.el);

  },

  setRow: function(row) {
    this.row = row;
  },

  deleteRow: function(e) {
    var self = this;
    this.killEvent(e);

    var delete_confirmation = new cdb.admin.BaseDialog({
      title: "Delete this row",
      description: "Are you sure you want to delete this row and all its associated data?",
      template_name: 'common/views/confirm_dialog',
      clean_on_hide: true,
      enter_to_confirm: true,
      ok_button_classes: "right button grey",
      ok_title: "Yes, do it",
      cancel_button_classes: "underline margin15",
      cancel_title: "Cancel",
      modal_type: "confirmation",
      width: 500
    });

    // If user confirms, app removes the row
    delete_confirmation.ok = function() {
      self.table.trigger('removing:row');
      self.row.destroy({
        success: function() {
          self.table.trigger('remove:row', self.row);
        }
      });
    }

    delete_confirmation
      .appendToBody()
      .open();

    this.hide();

    return false;
  },

  addRow: function(e) {
    this.killEvent(e);
    var rowIndex = this.row.collection.indexOf(this.row);

    this.tableData.addRow({ at: rowIndex + 1 });
    this.hide();
    return false;
  }
});
<|endoftext|>