<commit_before>/* eslint-disable dot-notation */
/**
 * mSupply Mobile
 * Sustainable Solutions (NZ) Ltd. 2016
 */

import { generalStrings, pageInfoStrings, programStrings, tableStrings } from '../../localization';
import { MODAL_KEYS, formatDate } from '../../utilities';
import { ROUTES } from '../../navigation/constants';
import { MODALS } from '../../widgets/constants';
import { PageActions } from './actions';
import { formatTemperatureExposure, formatTimeDifference } from '../../utilities/formatters';
import { UIDatabase } from '../../database';
import { NUMBER_OF_DAYS_IN_A_MONTH } from '../../database/utilities';

/**
 * PageInfo rows/columns for use with the PageInfo component.
 *
 *
 * To add a page, add the pages routeName (see: pages/index.js)
 * and a 2d array of page info rows to PER_PAGE_INFO_COLUMNS,
 * of the desired pageinfo rows needed on the page.
 *
 * To use in the new page: use the usePageReducer hook.
 * usePageReducer will inject a your pages reducer with a field
 * pageInfo - a function returned by getPageInfo. Passing
 * this function the particular pages base object - i.e,
 * transaction, requisition or stock and it's dispatch will return the
 * required pageInfo columns for the page.
 */

const PER_PAGE_INFO_COLUMNS = {
  [ROUTES.CUSTOMER_INVOICE]: [
    ['entryDate', 'confirmDate', 'enteredBy'],
    ['customer', 'theirRef', 'transactionComment'],
  ],
  [ROUTES.CUSTOMER_INVOICE_WITH_VACCINES]: [
    ['entryDate', 'confirmDate', 'enteredBy'],
    ['customer', 'theirRef', 'transactionComment'],
  ],
  [ROUTES.SUPPLIER_INVOICE]: [
    ['entryDate', 'confirmDate'],
    ['otherParty', 'theirRef', 'transactionComment'],
  ],
  [ROUTES.SUPPLIER_INVOICE_WITH_VACCINES]: [
    ['entryDate', 'confirmDate'],
    ['otherParty', 'theirRef', 'transactionComment'],
  ],
  [ROUTES.SUPPLIER_INVOICE_WITH_PRICES]: [
    ['entryDate', 'confirmDate'],
    ['otherParty', 'theirRef', 'transactionComment'],
  ],
  [ROUTES.SUPPLIER_REQUISITION]: [
    ['entryDate', 'enteredBy'],
    ['otherParty', 'editableMonthsToSupply', 'requisitionComment'],
  ],
  [ROUTES.SUPPLIER_REQUISITION_WITH_PROGRAM]: [
    ['program', 'orderType', 'entryDate', 'enteredBy'],
    ['period', 'otherParty', 'monthsToSupply', 'requisitionComment'],
  ],
  [ROUTES.STOCKTAKE_EDITOR]: [['stocktakeName', 'stocktakeComment']],
  [ROUTES.CUSTOMER_REQUISITION]: [
    ['monthsToSupply', 'entryDate'],
    ['customer', 'requisitionComment'],
  ],
  [ROUTES.CUSTOMER_REQUISITIONS_WITH_PROGRAMS]: [
    ['program', 'orderType', 'monthsToSupply', 'entryDate'],
    ['period', 'customer', 'createdDate', 'requisitionComment'],
  ],
  [ROUTES.PRESCRIPTION]: [
    ['entryDate', 'enteredBy'],
    ['customer', 'transactionComment', 'prescriber'],
  ],
  [MODALS.STOCKTAKE_BATCH_EDIT]: [['itemName']],
  [MODALS.STOCKTAKE_BATCH_EDIT_WITH_REASONS]: [['itemName']],
  [MODALS.STOCKTAKE_BATCH_EDIT_WITH_PRICES]: [['itemName']],
  [MODALS.STOCKTAKE_BATCH_EDIT_WITH_REASONS_AND_PRICES]: [['itemName']],
  [MODALS.STOCKTAKE_BATCH_EDIT_WITH_VACCINES]: [['itemName']],
  [ROUTES.SUPPLIER_CREDIT]: [
    ['entryDate', 'confirmDate', 'transactionCategory'],
    ['enteredBy', 'otherParty'],
  ],
  supplierRequisitionItemDetail: [['lastRequisitionDate', 'openVialWastage', 'closedVialWastage']],
  breach: [
    ['breachTemperatureRange'],
    ['breachDuration', 'location'],
    ['numberOfAffectedBatches', 'affectedQuantity'],
  ],

  customerRequisitionItemDetail: [
    [
      'numberOfDaysInAMonthString',
      'numberOfDaysInAMonthFormula',
      'customerRequisitionProgramAMCFormulaString',
      'customerRequisitionProgramAMCFormula',
      'customerRequisitionProgramSuggestedFormulaString',
      'customerRequisitionProgramSuggestedFormula',
    ],
  ],
};

const PAGE_INFO_ROWS = (pageObject, dispatch, route) => ({
  customerRequisitionProgramSuggestedFormulaString: {
    title: `${pageInfoStrings.suggested_equals}`,
    info: `= ${pageInfoStrings.suggested_formula}`,
  },
  customerRequisitionProgramSuggestedFormula: {
    title: `${Math.ceil(
      pageObject.monthlyUsage * pageObject.monthsToSupply - pageObject.stockOnHand
    )}`,
    info: `= (${pageObject.monthlyUsage?.toFixed(2)} x ${pageObject.monthsToSupply}) - ${
      pageObject.stockOnHand
    } ${
      Math.ceil(
        pageObject.monthlyUsage?.toFixed(2) * pageObject.monthsToSupply - pageObject.stockOnHand
      ) < 0
        ? pageInfoStrings.none_suggested
        : ''
    }`,
  },
  numberOfDaysInAMonthString: {
    title: `${pageInfoStrings.number_of_days_in_a_month}`,
    info: `${pageInfoStrings.number_of_days_in_a_month_formula}`,
  },
  numberOfDaysInAMonthFormula: {
    title: '30.42',
    info: '= 365 / 12',
  },
  customerRequisitionProgramAMCFormulaString: {
    title: `${pageInfoStrings.amc_equals}`,
    info: `= ${pageInfoStrings.amc_formula}`,
  },
  createdDate: {
    title: `${pageInfoStrings.created_date}:`,
    info: `${formatDate(pageObject?.createdDate)}`,
    onPress: pageObject.isRemoteOrder ? () => dispatch(PageActions.openDatePicker(route)) : null,
    editableType: 'date',
  },
  customerRequisitionProgramAMCFormula: {
    title: `${Math.ceil(pageObject.monthlyUsage)}`,
    info: `= (${pageObject.outgoingStock?.toFixed(2)} x ${NUMBER_OF_DAYS_IN_A_MONTH?.toFixed(
      2
    )}) / (${pageObject.numberOfDaysInPeriod} - ${pageObject.daysOutOfStock})`,
  },

  lastRequisitionDate: {
    title: `${pageInfoStrings.last_requisition_date}:`,
    info: formatDate(pageObject?.lastRequisitionDate, 'll') || generalStrings.not_available,
  },
  openVialWastage: {
    title: `${pageInfoStrings.open_vial_wastage}:`,
    info: pageObject?.isVaccine
      ? pageObject?.openVialWastage?.(pageObject.lastRequisitionDate)
      : generalStrings.not_available,
  },
  closedVialWastage: {
    title: `${pageInfoStrings.closed_vial_wastage}:`,
    info: pageObject?.isVaccine
      ? pageObject?.closedVialWastage?.(pageObject.lastRequisitionDate)
      : generalStrings.not_available,
  },
  breachTemperatureRange: {
    title: `${pageInfoStrings.temperature_range}:`,
    info: formatTemperatureExposure(pageObject.temperatureExposure),
  },
  numberOfAffectedBatches: {
    title: `${pageInfoStrings.number_of_affected_batches}:`,
    info: pageObject?.numberOfAffectedBatches?.(UIDatabase),
  },
  breachDuration: {
    title: `${pageInfoStrings.duration}:`,
    info: pageObject.duration ? formatTimeDifference(pageObject.duration) : '',
  },
  location: {
    title: `${pageInfoStrings.location}:`,
    info: pageObject.locationString,
  },
  affectedQuantity: {
    title: `${pageInfoStrings.affected_quantity}:`,
    info: pageObject?.affectedQuantity?.(UIDatabase),
  },
  entryDate: {
    title: `${pageInfoStrings.entry_date}:`,
    info: formatDate(pageObject.entryDate) || 'N/A',
  },
  confirmDate: {
    title: `${pageInfoStrings.confirm_date}:`,
    info: formatDate(pageObject.confirmDate),
  },
  transactionCategory: {
    title: `${pageInfoStrings.category}:`,
    info: pageObject?.category?.name ?? '',
  },
  enteredBy: {
    title: `${pageInfoStrings.entered_by}:`,
    info: pageObject.enteredBy && pageObject.enteredBy.username,
  },
  customer: {
    title: `${pageInfoStrings.customer}:`,
    info: pageObject.otherPartyName,
  },
  theirRef: {
    title: `${pageInfoStrings.their_ref}:`,
    info: pageObject.theirRef,
    onPress: () => dispatch(PageActions.openModal(MODAL_KEYS.THEIR_REF_EDIT, route)),
    editableType: 'text',
  },
  transactionComment: {
    title: `${pageInfoStrings.comment}:`,
    info: pageObject.comment,
    onPress: () => dispatch(PageActions.openModal(MODAL_KEYS.TRANSACTION_COMMENT_EDIT, route)),
    editableType: 'text',
  },
  stocktakeComment: {
    title: `${pageInfoStrings.comment}:`,
    info: pageObject.comment,
    onPress: () => dispatch(PageActions.openModal(MODAL_KEYS.STOCKTAKE_COMMENT_EDIT, route)),
    editableType: 'text',
  },
  requisitionComment: {
    title: `${pageInfoStrings.comment}:`,
    info: pageObject.comment,
    onPress: () => dispatch(PageActions.openModal(MODAL_KEYS.REQUISITION_COMMENT_EDIT, route)),
    editableType: 'text',
  },
  otherParty: {
    title: `${pageInfoStrings.supplier}:`,
    info: pageObject.otherPartyName,
  },
  program: {
    title: `${programStrings.program}:`,
    info: pageObject.program && pageObject.program.name,
  },
  orderType: {
    title: `${programStrings.order_type}:`,
    info: pageObject.orderType,
  },
  editableMonthsToSupply: {
    title: `${pageInfoStrings.months_stock_required}:`,
    info: pageObject.monthsToSupply,
    onPress: () => dispatch(PageActions.openModal(MODAL_KEYS.SELECT_MONTH, route)),
    editableType: 'selectable',
  },
  period: {
    title: `${programStrings.period}:`,
    info: pageObject.period && pageObject.period.toInfoString(),
  },
  monthsToSupply: {
    title: `${pageInfoStrings.months_stock_required}:`,
    info: pageObject.monthsToSupply,
  },
  stocktakeName: {
    title: `${pageInfoStrings.stocktake_name}:`,
    info: pageObject.name,
    onPress: () => dispatch(PageActions.openModal(MODAL_KEYS.STOCKTAKE_NAME_EDIT, route)),
    editableType: 'text',
  },
  itemName: {
    title: `${tableStrings.item_name}`,
    info: pageObject.itemName,
    onPress: null,
  },
  prescriber: {
    title: 'Prescriber',
    info: pageObject?.prescriber?.firstName,
    onPress: () => dispatch(PageActions.openModal(MODAL_KEYS.SELECT_PRESCRIBER, route)),
  },
  number: {
    title: 'Number',
    info: pageObject.number,
    onPress: null,
  },
  name: {
    title: 'Name',
    info: pageObject.name,
    onPress: null,
  },
  type: {
    title: 'Type',
    info: pageObject.type,
    onPress: null,
  },
  comment: {
    title: 'Comment',
    info: pageObject.comment,
    onPress: null,
  },
  status: {
    title: 'Status',
    info: pageObject.status,
    onPress: null,
  },
});

const getPageInfoColumns = (page, isProgramBased) => {
  const pageInfoColumns = PER_PAGE_INFO_COLUMNS[page];

  if (!pageInfoColumns) return null;

  return (pageObjectParameter, dispatch, route) => {
    const pageInfoRows = PAGE_INFO_ROWS(pageObjectParameter, dispatch, route);
    return pageInfoColumns.map((pageInfoColumn, idx) => {
      const columnArray =
        isProgramBased && idx === 0 ? ['program', ...pageInfoColumn] : pageInfoColumn;
      return columnArray.map(pageInfoKey => pageInfoRows[pageInfoKey]);
    });
  };
};

export default getPageInfoColumns;
<commit_msg>Add ceil -> round<commit_after>/* eslint-disable dot-notation */
/**
 * mSupply Mobile
 * Sustainable Solutions (NZ) Ltd. 2016
 */

import { generalStrings, pageInfoStrings, programStrings, tableStrings } from '../../localization';
import { MODAL_KEYS, formatDate } from '../../utilities';
import { ROUTES } from '../../navigation/constants';
import { MODALS } from '../../widgets/constants';
import { PageActions } from './actions';
import { formatTemperatureExposure, formatTimeDifference } from '../../utilities/formatters';
import { UIDatabase } from '../../database';
import { NUMBER_OF_DAYS_IN_A_MONTH } from '../../database/utilities';

/**
 * PageInfo rows/columns for use with the PageInfo component.
 *
 *
 * To add a page, add the pages routeName (see: pages/index.js)
 * and a 2d array of page info rows to PER_PAGE_INFO_COLUMNS,
 * of the desired pageinfo rows needed on the page.
 *
 * To use in the new page: use the usePageReducer hook.
 * usePageReducer will inject a your pages reducer with a field
 * pageInfo - a function returned by getPageInfo. Passing
 * this function the particular pages base object - i.e,
 * transaction, requisition or stock and it's dispatch will return the
 * required pageInfo columns for the page.
 */

const PER_PAGE_INFO_COLUMNS = {
  [ROUTES.CUSTOMER_INVOICE]: [
    ['entryDate', 'confirmDate', 'enteredBy'],
    ['customer', 'theirRef', 'transactionComment'],
  ],
  [ROUTES.CUSTOMER_INVOICE_WITH_VACCINES]: [
    ['entryDate', 'confirmDate', 'enteredBy'],
    ['customer', 'theirRef', 'transactionComment'],
  ],
  [ROUTES.SUPPLIER_INVOICE]: [
    ['entryDate', 'confirmDate'],
    ['otherParty', 'theirRef', 'transactionComment'],
  ],
  [ROUTES.SUPPLIER_INVOICE_WITH_VACCINES]: [
    ['entryDate', 'confirmDate'],
    ['otherParty', 'theirRef', 'transactionComment'],
  ],
  [ROUTES.SUPPLIER_INVOICE_WITH_PRICES]: [
    ['entryDate', 'confirmDate'],
    ['otherParty', 'theirRef', 'transactionComment'],
  ],
  [ROUTES.SUPPLIER_REQUISITION]: [
    ['entryDate', 'enteredBy'],
    ['otherParty', 'editableMonthsToSupply', 'requisitionComment'],
  ],
  [ROUTES.SUPPLIER_REQUISITION_WITH_PROGRAM]: [
    ['program', 'orderType', 'entryDate', 'enteredBy'],
    ['period', 'otherParty', 'monthsToSupply', 'requisitionComment'],
  ],
  [ROUTES.STOCKTAKE_EDITOR]: [['stocktakeName', 'stocktakeComment']],
  [ROUTES.CUSTOMER_REQUISITION]: [
    ['monthsToSupply', 'entryDate'],
    ['customer', 'requisitionComment'],
  ],
  [ROUTES.CUSTOMER_REQUISITIONS_WITH_PROGRAMS]: [
    ['program', 'orderType', 'monthsToSupply', 'entryDate'],
    ['period', 'customer', 'createdDate', 'requisitionComment'],
  ],
  [ROUTES.PRESCRIPTION]: [
    ['entryDate', 'enteredBy'],
    ['customer', 'transactionComment', 'prescriber'],
  ],
  [MODALS.STOCKTAKE_BATCH_EDIT]: [['itemName']],
  [MODALS.STOCKTAKE_BATCH_EDIT_WITH_REASONS]: [['itemName']],
  [MODALS.STOCKTAKE_BATCH_EDIT_WITH_PRICES]: [['itemName']],
  [MODALS.STOCKTAKE_BATCH_EDIT_WITH_REASONS_AND_PRICES]: [['itemName']],
  [MODALS.STOCKTAKE_BATCH_EDIT_WITH_VACCINES]: [['itemName']],
  [ROUTES.SUPPLIER_CREDIT]: [
    ['entryDate', 'confirmDate', 'transactionCategory'],
    ['enteredBy', 'otherParty'],
  ],
  supplierRequisitionItemDetail: [['lastRequisitionDate', 'openVialWastage', 'closedVialWastage']],
  breach: [
    ['breachTemperatureRange'],
    ['breachDuration', 'location'],
    ['numberOfAffectedBatches', 'affectedQuantity'],
  ],

  customerRequisitionItemDetail: [
    [
      'numberOfDaysInAMonthString',
      'numberOfDaysInAMonthFormula',
      'customerRequisitionProgramAMCFormulaString',
      'customerRequisitionProgramAMCFormula',
      'customerRequisitionProgramSuggestedFormulaString',
      'customerRequisitionProgramSuggestedFormula',
    ],
  ],
};

const PAGE_INFO_ROWS = (pageObject, dispatch, route) => ({
  customerRequisitionProgramSuggestedFormulaString: {
    title: `${pageInfoStrings.suggested_equals}`,
    info: `= ${pageInfoStrings.suggested_formula}`,
  },
  customerRequisitionProgramSuggestedFormula: {
    title: `${Math.ceil(
      pageObject.monthlyUsage * pageObject.monthsToSupply - pageObject.stockOnHand
    )}`,
    info: `= (${pageObject.monthlyUsage?.toFixed(2)} x ${pageObject.monthsToSupply}) - ${
      pageObject.stockOnHand
    } ${
      Math.ceil(
        pageObject.monthlyUsage?.toFixed(2) * pageObject.monthsToSupply - pageObject.stockOnHand
      ) < 0
        ? pageInfoStrings.none_suggested
        : ''
    }`,
  },
  numberOfDaysInAMonthString: {
    title: `${pageInfoStrings.number_of_days_in_a_month}`,
    info: `${pageInfoStrings.number_of_days_in_a_month_formula}`,
  },
  numberOfDaysInAMonthFormula: {
    title: '30.42',
    info: '= 365 / 12',
  },
  customerRequisitionProgramAMCFormulaString: {
    title: `${pageInfoStrings.amc_equals}`,
    info: `= ${pageInfoStrings.amc_formula}`,
  },
  createdDate: {
    title: `${pageInfoStrings.created_date}:`,
    info: `${formatDate(pageObject?.createdDate)}`,
    onPress: pageObject.isRemoteOrder ? () => dispatch(PageActions.openDatePicker(route)) : null,
    editableType: 'date',
  },
  customerRequisitionProgramAMCFormula: {
    title: `${Math.round(pageObject.monthlyUsage)}`,
    info: `= (${pageObject.outgoingStock?.toFixed(2)} x ${NUMBER_OF_DAYS_IN_A_MONTH?.toFixed(
      2
    )}) / (${pageObject.numberOfDaysInPeriod} - ${pageObject.daysOutOfStock})`,
  },

  lastRequisitionDate: {
    title: `${pageInfoStrings.last_requisition_date}:`,
    info: formatDate(pageObject?.lastRequisitionDate, 'll') || generalStrings.not_available,
  },
  openVialWastage: {
    title: `${pageInfoStrings.open_vial_wastage}:`,
    info: pageObject?.isVaccine
      ? pageObject?.openVialWastage?.(pageObject.lastRequisitionDate)
      : generalStrings.not_available,
  },
  closedVialWastage: {
    title: `${pageInfoStrings.closed_vial_wastage}:`,
    info: pageObject?.isVaccine
      ? pageObject?.closedVialWastage?.(pageObject.lastRequisitionDate)
      : generalStrings.not_available,
  },
  breachTemperatureRange: {
    title: `${pageInfoStrings.temperature_range}:`,
    info: formatTemperatureExposure(pageObject.temperatureExposure),
  },
  numberOfAffectedBatches: {
    title: `${pageInfoStrings.number_of_affected_batches}:`,
    info: pageObject?.numberOfAffectedBatches?.(UIDatabase),
  },
  breachDuration: {
    title: `${pageInfoStrings.duration}:`,
    info: pageObject.duration ? formatTimeDifference(pageObject.duration) : '',
  },
  location: {
    title: `${pageInfoStrings.location}:`,
    info: pageObject.locationString,
  },
  affectedQuantity: {
    title: `${pageInfoStrings.affected_quantity}:`,
    info: pageObject?.affectedQuantity?.(UIDatabase),
  },
  entryDate: {
    title: `${pageInfoStrings.entry_date}:`,
    info: formatDate(pageObject.entryDate) || 'N/A',
  },
  confirmDate: {
    title: `${pageInfoStrings.confirm_date}:`,
    info: formatDate(pageObject.confirmDate),
  },
  transactionCategory: {
    title: `${pageInfoStrings.category}:`,
    info: pageObject?.category?.name ?? '',
  },
  enteredBy: {
    title: `${pageInfoStrings.entered_by}:`,
    info: pageObject.enteredBy && pageObject.enteredBy.username,
  },
  customer: {
    title: `${pageInfoStrings.customer}:`,
    info: pageObject.otherPartyName,
  },
  theirRef: {
    title: `${pageInfoStrings.their_ref}:`,
    info: pageObject.theirRef,
    onPress: () => dispatch(PageActions.openModal(MODAL_KEYS.THEIR_REF_EDIT, route)),
    editableType: 'text',
  },
  transactionComment: {
    title: `${pageInfoStrings.comment}:`,
    info: pageObject.comment,
    onPress: () => dispatch(PageActions.openModal(MODAL_KEYS.TRANSACTION_COMMENT_EDIT, route)),
    editableType: 'text',
  },
  stocktakeComment: {
    title: `${pageInfoStrings.comment}:`,
    info: pageObject.comment,
    onPress: () => dispatch(PageActions.openModal(MODAL_KEYS.STOCKTAKE_COMMENT_EDIT, route)),
    editableType: 'text',
  },
  requisitionComment: {
    title: `${pageInfoStrings.comment}:`,
    info: pageObject.comment,
    onPress: () => dispatch(PageActions.openModal(MODAL_KEYS.REQUISITION_COMMENT_EDIT, route)),
    editableType: 'text',
  },
  otherParty: {
    title: `${pageInfoStrings.supplier}:`,
    info: pageObject.otherPartyName,
  },
  program: {
    title: `${programStrings.program}:`,
    info: pageObject.program && pageObject.program.name,
  },
  orderType: {
    title: `${programStrings.order_type}:`,
    info: pageObject.orderType,
  },
  editableMonthsToSupply: {
    title: `${pageInfoStrings.months_stock_required}:`,
    info: pageObject.monthsToSupply,
    onPress: () => dispatch(PageActions.openModal(MODAL_KEYS.SELECT_MONTH, route)),
    editableType: 'selectable',
  },
  period: {
    title: `${programStrings.period}:`,
    info: pageObject.period && pageObject.period.toInfoString(),
  },
  monthsToSupply: {
    title: `${pageInfoStrings.months_stock_required}:`,
    info: pageObject.monthsToSupply,
  },
  stocktakeName: {
    title: `${pageInfoStrings.stocktake_name}:`,
    info: pageObject.name,
    onPress: () => dispatch(PageActions.openModal(MODAL_KEYS.STOCKTAKE_NAME_EDIT, route)),
    editableType: 'text',
  },
  itemName: {
    title: `${tableStrings.item_name}`,
    info: pageObject.itemName,
    onPress: null,
  },
  prescriber: {
    title: 'Prescriber',
    info: pageObject?.prescriber?.firstName,
    onPress: () => dispatch(PageActions.openModal(MODAL_KEYS.SELECT_PRESCRIBER, route)),
  },
  number: {
    title: 'Number',
    info: pageObject.number,
    onPress: null,
  },
  name: {
    title: 'Name',
    info: pageObject.name,
    onPress: null,
  },
  type: {
    title: 'Type',
    info: pageObject.type,
    onPress: null,
  },
  comment: {
    title: 'Comment',
    info: pageObject.comment,
    onPress: null,
  },
  status: {
    title: 'Status',
    info: pageObject.status,
    onPress: null,
  },
});

const getPageInfoColumns = (page, isProgramBased) => {
  const pageInfoColumns = PER_PAGE_INFO_COLUMNS[page];

  if (!pageInfoColumns) return null;

  return (pageObjectParameter, dispatch, route) => {
    const pageInfoRows = PAGE_INFO_ROWS(pageObjectParameter, dispatch, route);
    return pageInfoColumns.map((pageInfoColumn, idx) => {
      const columnArray =
        isProgramBased && idx === 0 ? ['program', ...pageInfoColumn] : pageInfoColumn;
      return columnArray.map(pageInfoKey => pageInfoRows[pageInfoKey]);
    });
  };
};

export default getPageInfoColumns;
<|endoftext|>


<commit_before>
function updatePyramid(sites){
    var possiblecounts = [];
    for(var patient in seqID_lookup){
      if(seqID_lookup[patient].mismatch != undefined){
        mmcount = 0;
        for(var i = 0; i < sites.length; i++){
            mmcount += seqID_lookup[patient].mismatch[sites[i]];
        }
      }
      possiblecounts.push(mmcount);
    }
    maxcounts = d3.max(possiblecounts)
    skipcount = Math.ceil(maxcounts/16)
    var tickvals = d3.range(maxcounts).filter(function(d,i){return (i % skipcount === 0)});
    PyramidDiv = d3.select("#group");
    mdata = [];

    for(var i = 0; i < d3.max(possiblecounts)+1; i++){
        mdata.push({mismatches:i.toString(), vaccine:0, placebo:0});
        }
    for(var patient in seqID_lookup){
        if(seqID_lookup[patient].mismatch != undefined){
          mmcount = 0;
          for(var i = 0; i < sites.length; i++){
              mmcount += seqID_lookup[patient].mismatch[sites[i]];
          }
          if(seqID_lookup[patient].vaccine){
              mdata[mmcount].vaccine += 1;
          } else {
              mdata[mmcount].placebo += 1;
          }
        }
    }

  var w = 400,
      h = 150;
  var maxValue = Math.max(
    d3.max(mdata, function(d) { return d.vaccine/numvac; }),
    d3.max(mdata, function(d) { return d.placebo/numplac; })
  );
  var margin = {
      top: 50,
      right: 20,
      bottom: 24,
      left: 25,
      middle: 20
    };

    // the width of each side of the chart
  var regionWidth = w/2 - margin.middle;

    // these are the x-coordinates of the y-axes
  var pointA = regionWidth,
        pointB = w - regionWidth;

  var xScale = d3.scale.linear()
      .domain([0, maxValue])
      .range([0, regionWidth])
      .nice();

  var yScale = d3.scale.ordinal()
      .domain(d3.range(maxcounts))
      .rangeRoundBands([h,0],0.1);
  console.log(yScale.domain());
  var yAxisLeft = d3.svg.axis()
      .scale(yScale)
      .orient('right')
      .tickValues(tickvals)
      .tickSize(4,0)
      .tickPadding(margin.middle-4);

  var yAxisRight = d3.svg.axis()
      .scale(yScale)
      .orient('left')
      .tickValues(tickvals)
      .tickSize(4,0)
      .tickFormat('');

  var xAxisRight = d3.svg.axis()
      .scale(xScale)
      .orient('bottom')
      .ticks(5)
      .tickFormat(d3.format(''));

  var xAxisLeft = d3.svg.axis()
      .scale(xScale.copy().range([pointA, 0]))
      .orient('bottom')
      .ticks(5)
      .tickFormat(d3.format(''));

  PyramidDiv.select(".axis.y.left")
            .transition()
            .call(yAxisLeft)
            .selectAll('text')
            .style('text-anchor', 'middle');

  PyramidDiv.select('.axis.y.right')
            .transition()
            .call(yAxisRight);

  PyramidDiv.transition()
            .select('.axis.x.left')
            .call(xAxisLeft);

  PyramidDiv.select('.axis.x.right')
            .transition()
            .call(xAxisRight);

  var leftBars = PyramidDiv.select('.lgroup')
                           .selectAll('.bar.left')
                           .data(mdata,id);
  leftBars.exit().remove();

  var rightBars = PyramidDiv.select('.rgroup')
                            .selectAll('.bar.right')
                            .data(mdata,id);
  rightBars.exit().remove();
  leftBars.enter().append('rect')
        .attr('class','bar left')
        .attr('x', 0)
        .attr('y', function(d) {return yScale(d.mismatches); })
        .attr('width', function(d) {return xScale(d.vaccine / numvac); })
        .attr('height', yScale.rangeBand())
        .style("fill","red");

  rightBars.enter().append('rect')
        .attr('class','bar right')
        .attr('x', 0)
        .attr('y', function(d) {return yScale(d.mismatches); })
        .attr('width', function(d) {return xScale(d.vaccine / numvac); })
        .attr('height', yScale.rangeBand())
        .style("fill","steelblue");
  leftBars.transition()
          .attr('y', function(d) {return yScale(d.mismatches); })
          .attr('width', function(d) {return xScale(d.vaccine / numvac); })
          .attr('height', yScale.rangeBand());

  rightBars.transition()
           .attr('y', function(d) {return yScale(d.mismatches); })
           .attr('width', function(d) { return xScale(d.placebo / numplac); })
           .attr('height', yScale.rangeBand());

}
function drawPyramid(sites){
    var possiblecounts = [];
    for(var patient in seqID_lookup){
      if(seqID_lookup[patient].mismatch != undefined){
        mmcount = 0;
        for(var i = 0; i < sites.length; i++){
            mmcount += seqID_lookup[patient].mismatch[sites[i]];
        }
      }
      possiblecounts.push(mmcount);
    }

    mmdata = [];
    for(var i = 0; i < sites.length+1; i++){
        mmdata.push({mismatches:i.toString(), vaccine:0, placebo:0});
        }
    for(var patient in seqID_lookup){
        if(seqID_lookup[patient].mismatch != undefined){
          mmcount = 0;
          for(var i = 0; i < sites.length; i++){
              mmcount += seqID_lookup[patient].mismatch[sites[i]];
          }
          if(seqID_lookup[patient].vaccine){
              mmdata[mmcount].vaccine += 1;
          } else {
              mmdata[mmcount].placebo += 1;
          }
        }

    }
    var w = 400,
        h = 150;

    // margin.middle is distance from center line to each y-axis
    var margin = {
      top: 50,
      right: 20,
      bottom: 24,
      left: 25,
      middle: 20
    };

    // the width of each side of the chart
    var regionWidth = w/2 - margin.middle;

    // these are the x-coordinates of the y-axes
    var pointA = regionWidth,
        pointB = w - regionWidth;

    // CREATE SVG
    var svg = d3.select('#group').append('svg')
      .attr('width', margin.left + w + margin.right)
      .attr('height', margin.top + h + margin.bottom)
      // ADD A GROUP FOR THE SPACE WITHIN THE MARGINS
      .append('g')
        .attr('transform', translation(margin.left, margin.top));

    // the xScale goes from 0 to the width of a region
    //  it will be reversed for the left x-axis
    var xScale = d3.scale.linear()
      .domain([0, 1])
      .range([0, regionWidth])
      .nice();

    var xScaleLeft = d3.scale.linear()
      .domain([0, 1])
      .range([regionWidth, 0]);

    var xScaleRight = d3.scale.linear()
      .domain([0, 1])
      .range([0, regionWidth]);

    var yScale = d3.scale.ordinal()
      .domain(mmdata.map(function(d) { return d.mismatches; }))
      .rangeRoundBands([h,0], 0.1);

    var yAxisLeft = d3.svg.axis()
      .scale(yScale)
      .orient('right')
      .ticks(18)
      .tickSize(4,0)
      .tickPadding(margin.middle-4);

    var yAxisRight = d3.svg.axis()
      .scale(yScale)
      .orient('left')
      .ticks(18)
      .tickSize(4,0)
      .tickFormat('');

    var xAxisRight = d3.svg.axis()
      .scale(xScale)
      .orient('bottom')
      .ticks(5)
      .tickFormat(d3.format(''));

    var xAxisLeft = d3.svg.axis()
      .scale(xScale.copy().range([pointA, 0]))
      .orient('bottom')
      .ticks(5)
      .tickFormat(d3.format(''));

    var leftBarGroup = svg.append('g')
      .attr('class', 'lgroup')
      .attr('transform', translation(pointA, 0) + 'scale(-1,1)');
    var rightBarGroup = svg.append('g')
      .attr('class', 'rgroup')
      .attr('transform', translation(pointB, 0));

    svg.append('g')
      .attr('class', 'axis y left')
      .attr('transform', translation(pointA, 0))
      .call(yAxisLeft)
      .selectAll('text')
      .style('text-anchor', 'middle');

    svg.append('g')
      .attr('class', 'axis y right')
      .attr('transform', translation(pointB, 0))
      .call(yAxisRight);

     // draw title
    svg.append("text")
      .attr("x",w/2)
      .attr("y",-30)
      .style("text-anchor","middle")
      .style("font-size","15px")
      .text("Distribution of Mismatch Counts Across Selected Sites");
    // labels etc.
    svg.append('text')
      .text("Vaccine Group")
      .attr('x',0)
      .attr('y',0);
    svg.append('text')
      .text("Placebo Group")
      .attr('x',330)
      .attr('y',0);

    svg.append("text")
      .text("Number of Mismatches")
      .attr("x",w/2)
      .attr("y",-2)
      .style("text-anchor","middle");

    svg.append('g')
      .attr('class', 'axis x left')
      .attr('transform', translation(0, h))
      .text("Vaccine Group")
      .call(xAxisLeft);

    svg.append('g')
      .attr('class', 'axis x right')
      .attr('transform', translation(pointB, h))
      .call(xAxisRight);

    leftBarGroup.selectAll('.bar.left')
      .data(mmdata)
      .enter().append('rect')
        .attr('class', 'bar left')
        .attr('x', 0)
        .attr('y', function(d) {return yScale(d.mismatches); })
        .attr('width', function(d) { return xScale(d.vaccine / numvac); })
        .attr('height', yScale.rangeBand())
        .style("fill","red");

    rightBarGroup.selectAll('.bar.right')
      .data(mmdata)
      .enter().append('rect')
        .attr('class', 'bar right')
        .attr('x', 0)
        .attr('y', function(d) { return yScale(d.mismatches); })
        .attr('width', function(d) { return xScale(d.placebo / numplac); })
        .attr('height', yScale.rangeBand()).style("fill","steelblue");
    }
function translation(x,y) {
  return 'translate(' + x + ',' + y + ')';
  }

 function id(d){
   return d.mismatches;
 }<commit_msg>formatting fix to pyramid axis labels<commit_after>
function updatePyramid(sites){
    var possiblecounts = [];
    for(var patient in seqID_lookup){
      if(seqID_lookup[patient].mismatch != undefined){
        mmcount = 0;
        for(var i = 0; i < sites.length; i++){
            mmcount += seqID_lookup[patient].mismatch[sites[i]];
        }
      }
      possiblecounts.push(mmcount);
    }
    maxcounts = d3.max(possiblecounts)
    skipcount = Math.ceil(maxcounts/16)
    var tickvals = d3.range(maxcounts+1).filter(function(d,i){return (i % skipcount === 0)});
    PyramidDiv = d3.select("#group");
    mdata = [];

    for(var i = 0; i < d3.max(possiblecounts)+1; i++){
        mdata.push({mismatches:i.toString(), vaccine:0, placebo:0});
        }
    for(var patient in seqID_lookup){
        if(seqID_lookup[patient].mismatch != undefined){
          mmcount = 0;
          for(var i = 0; i < sites.length; i++){
              mmcount += seqID_lookup[patient].mismatch[sites[i]];
          }
          if(seqID_lookup[patient].vaccine){
              mdata[mmcount].vaccine += 1;
          } else {
              mdata[mmcount].placebo += 1;
          }
        }
    }

  var w = 400,
      h = 150;
  var maxValue = Math.max(
    d3.max(mdata, function(d) { return d.vaccine/numvac; }),
    d3.max(mdata, function(d) { return d.placebo/numplac; })
  );
  var margin = {
      top: 50,
      right: 20,
      bottom: 24,
      left: 25,
      middle: 20
    };

    // the width of each side of the chart
  var regionWidth = w/2 - margin.middle;

    // these are the x-coordinates of the y-axes
  var pointA = regionWidth,
        pointB = w - regionWidth;

  var xScale = d3.scale.linear()
      .domain([0, maxValue])
      .range([0, regionWidth])
      .nice();

  var yScale = d3.scale.ordinal()
      .domain(d3.range(maxcounts+1))
      .rangeRoundBands([h,0],0.1);
  console.log(yScale.domain());

  var yAxisLeft = d3.svg.axis()
      .scale(yScale)
      .orient('right')
      .tickValues(tickvals)
      .tickSize(4,0)
      .tickPadding(margin.middle-4);

  var yAxisRight = d3.svg.axis()
      .scale(yScale)
      .orient('left')
      .tickValues(tickvals)
      .tickSize(4,0)
      .tickFormat('');

  var xAxisRight = d3.svg.axis()
      .scale(xScale)
      .orient('bottom')
      .ticks(5)
      .tickFormat(d3.format(''));

  var xAxisLeft = d3.svg.axis()
      .scale(xScale.copy().range([pointA, 0]))
      .orient('bottom')
      .ticks(5)
      .tickFormat(d3.format(''));

  PyramidDiv.select(".axis.y.left")
            .transition()
            .call(yAxisLeft)
            .selectAll('text')
            .style('text-anchor', 'middle');

  PyramidDiv.select('.axis.y.right')
            .transition()
            .call(yAxisRight);

  PyramidDiv.transition()
            .select('.axis.x.left')
            .call(xAxisLeft);

  PyramidDiv.select('.axis.x.right')
            .transition()
            .call(xAxisRight);

  var leftBars = PyramidDiv.select('.lgroup')
                           .selectAll('.bar.left')
                           .data(mdata,id);
  leftBars.exit().remove();

  var rightBars = PyramidDiv.select('.rgroup')
                            .selectAll('.bar.right')
                            .data(mdata,id);
  rightBars.exit().remove();
  leftBars.enter().append('rect')
        .attr('class','bar left')
        .attr('x', 0)
        .attr('y', function(d) {return yScale(d.mismatches); })
        .attr('width', function(d) {return xScale(d.vaccine / numvac); })
        .attr('height', yScale.rangeBand())
        .style("fill","red");

  rightBars.enter().append('rect')
        .attr('class','bar right')
        .attr('x', 0)
        .attr('y', function(d) {return yScale(d.mismatches); })
        .attr('width', function(d) {return xScale(d.vaccine / numvac); })
        .attr('height', yScale.rangeBand())
        .style("fill","steelblue");
  leftBars.transition()
          .attr('y', function(d) {return yScale(d.mismatches); })
          .attr('width', function(d) {return xScale(d.vaccine / numvac); })
          .attr('height', yScale.rangeBand());

  rightBars.transition()
           .attr('y', function(d) {return yScale(d.mismatches); })
           .attr('width', function(d) { return xScale(d.placebo / numplac); })
           .attr('height', yScale.rangeBand());

}
function drawPyramid(sites){
    var possiblecounts = [];
    for(var patient in seqID_lookup){
      if(seqID_lookup[patient].mismatch != undefined){
        mmcount = 0;
        for(var i = 0; i < sites.length; i++){
            mmcount += seqID_lookup[patient].mismatch[sites[i]];
        }
      }
      possiblecounts.push(mmcount);
    }

    mmdata = [];
    for(var i = 0; i < sites.length+1; i++){
        mmdata.push({mismatches:i.toString(), vaccine:0, placebo:0});
        }
    for(var patient in seqID_lookup){
        if(seqID_lookup[patient].mismatch != undefined){
          mmcount = 0;
          for(var i = 0; i < sites.length; i++){
              mmcount += seqID_lookup[patient].mismatch[sites[i]];
          }
          if(seqID_lookup[patient].vaccine){
              mmdata[mmcount].vaccine += 1;
          } else {
              mmdata[mmcount].placebo += 1;
          }
        }

    }
    var w = 400,
        h = 150;

    // margin.middle is distance from center line to each y-axis
    var margin = {
      top: 50,
      right: 20,
      bottom: 24,
      left: 25,
      middle: 20
    };

    // the width of each side of the chart
    var regionWidth = w/2 - margin.middle;

    // these are the x-coordinates of the y-axes
    var pointA = regionWidth,
        pointB = w - regionWidth;

    // CREATE SVG
    var svg = d3.select('#group').append('svg')
      .attr('width', margin.left + w + margin.right)
      .attr('height', margin.top + h + margin.bottom)
      // ADD A GROUP FOR THE SPACE WITHIN THE MARGINS
      .append('g')
        .attr('transform', translation(margin.left, margin.top));

    // the xScale goes from 0 to the width of a region
    //  it will be reversed for the left x-axis
    var xScale = d3.scale.linear()
      .domain([0, 1])
      .range([0, regionWidth])
      .nice();

    var xScaleLeft = d3.scale.linear()
      .domain([0, 1])
      .range([regionWidth, 0]);

    var xScaleRight = d3.scale.linear()
      .domain([0, 1])
      .range([0, regionWidth]);

    var yScale = d3.scale.ordinal()
      .domain(mmdata.map(function(d) { return d.mismatches; }))
      .rangeRoundBands([h,0], 0.1);

    var yAxisLeft = d3.svg.axis()
      .scale(yScale)
      .orient('right')
      .ticks(18)
      .tickSize(4,0)
      .tickPadding(margin.middle-4);

    var yAxisRight = d3.svg.axis()
      .scale(yScale)
      .orient('left')
      .ticks(18)
      .tickSize(4,0)
      .tickFormat('');

    var xAxisRight = d3.svg.axis()
      .scale(xScale)
      .orient('bottom')
      .ticks(5)
      .tickFormat(d3.format(''));

    var xAxisLeft = d3.svg.axis()
      .scale(xScale.copy().range([pointA, 0]))
      .orient('bottom')
      .ticks(5)
      .tickFormat(d3.format(''));

    var leftBarGroup = svg.append('g')
      .attr('class', 'lgroup')
      .attr('transform', translation(pointA, 0) + 'scale(-1,1)');
    var rightBarGroup = svg.append('g')
      .attr('class', 'rgroup')
      .attr('transform', translation(pointB, 0));

    svg.append('g')
      .attr('class', 'axis y left')
      .attr('transform', translation(pointA, 0))
      .call(yAxisLeft)
      .selectAll('text')
      .style('text-anchor', 'middle');

    svg.append('g')
      .attr('class', 'axis y right')
      .attr('transform', translation(pointB, 0))
      .call(yAxisRight);

     // draw title
    svg.append("text")
      .attr("x",w/2)
      .attr("y",-30)
      .style("text-anchor","middle")
      .style("font-size","15px")
      .text("Distribution of Mismatch Counts Across Selected Sites");
    // labels etc.
    svg.append('text')
      .text("Vaccine Group")
      .attr('x',0)
      .attr('y',0);
    svg.append('text')
      .text("Placebo Group")
      .attr('x',330)
      .attr('y',0);

    svg.append("text")
      .text("Number of Mismatches")
      .attr("x",w/2)
      .attr("y",-2)
      .style("text-anchor","middle");

    svg.append('g')
      .attr('class', 'axis x left')
      .attr('transform', translation(0, h))
      .text("Vaccine Group")
      .call(xAxisLeft);

    svg.append('g')
      .attr('class', 'axis x right')
      .attr('transform', translation(pointB, h))
      .call(xAxisRight);

    leftBarGroup.selectAll('.bar.left')
      .data(mmdata)
      .enter().append('rect')
        .attr('class', 'bar left')
        .attr('x', 0)
        .attr('y', function(d) {return yScale(d.mismatches); })
        .attr('width', function(d) { return xScale(d.vaccine / numvac); })
        .attr('height', yScale.rangeBand())
        .style("fill","red");

    rightBarGroup.selectAll('.bar.right')
      .data(mmdata)
      .enter().append('rect')
        .attr('class', 'bar right')
        .attr('x', 0)
        .attr('y', function(d) { return yScale(d.mismatches); })
        .attr('width', function(d) { return xScale(d.placebo / numplac); })
        .attr('height', yScale.rangeBand()).style("fill","steelblue");
    }
function translation(x,y) {
  return 'translate(' + x + ',' + y + ')';
  }

 function id(d){
   return d.mismatches;
 }<|endoftext|>


<commit_before>(function() {
  'use strict'

  function queryLink() {
    return {
      restrict: 'E',
      scope: {
        'query': '=',
        'visualization': '=?'
      },
      template: '{{query.name}}',
      link: function(scope, element) {
        var hash = null;
        if (scope.visualization) {
          if (scope.visualization.type === 'TABLE') {
            // link to hard-coded table tab instead of the (hidden) visualization tab
            hash = 'table';
          } else {
            hash = scope.visualization.id;
          }
        }
        scope.link = scope.query.getUrl(false, hash);
      }
    }
  }

  function querySourceLink($location) {
    return {
      restrict: 'E',
      template: '\
                    Show Source\
                    \
                    Hide Source\
                    \
                '
    }
  }

  function queryResultLink() {
    return {
      restrict: 'A',
      link: function (scope, element, attrs) {

        var fileType = attrs.fileType ? attrs.fileType : "csv";
        scope.$watch('queryResult && queryResult.getData()', function(data) {
          if (!data) {
            return;
          }

          if (scope.queryResult.getId() == null) {
            element.attr('href', '');
          } else {
            element.attr('href', 'api/queries/' + scope.query.id + '/results/' + scope.queryResult.getId() + '.' + fileType);
            element.attr('download', scope.query.name.replace(" ", "_") + moment(scope.queryResult.getUpdatedAt()).format("_YYYY_MM_DD") + "." + fileType);
          }
        });
      }
    }
  }

  function queryEditor() {
    return {
      restrict: 'E',
      scope: {
        'query': '=',
        'lock': '=',
        'schema': '=',
        'syntax': '='
      },
      template: '<textarea></textarea>',
      link: {
        pre: function ($scope, element) {
          $scope.syntax = $scope.syntax || 'sql';

          var modes = {
            'sql': 'text/x-sql',
            'python': 'text/x-python',
            'json': 'application/json'
          };

          var textarea = element.children()[0];
          var editorOptions = {
            mode: modes[$scope.syntax],
            lineWrapping: true,
            lineNumbers: true,
            readOnly: false,
            matchBrackets: true,
            autoCloseBrackets: true,
            extraKeys: {"Ctrl-Space": "autocomplete"}
          };

          var additionalHints = [];

          CodeMirror.commands.autocomplete = function(cm) {
            var hinter  = function(editor, options) {
              var hints = CodeMirror.hint.anyword(editor, options);
              var cur = editor.getCursor(), token = editor.getTokenAt(cur).string;

              hints.list = _.union(hints.list, _.filter(additionalHints, function (h) {
                return h.search(token) === 0;
              }));

              return hints;
            };

//            CodeMirror.showHint(cm, CodeMirror.hint.anyword);
            CodeMirror.showHint(cm, hinter);
          };

          var codemirror = CodeMirror.fromTextArea(textarea, editorOptions);

          codemirror.on('change', function(instance) {
            var newValue = instance.getValue();

            if (newValue !== $scope.query.query) {
              $scope.$evalAsync(function() {
                $scope.query.query = newValue;
              });
            }
          });

          $scope.$watch('query.query', function () {
            if ($scope.query.query !== codemirror.getValue()) {
              codemirror.setValue($scope.query.query);
            }
          });

          $scope.$watch('schema', function (schema) {
            if (schema) {
              var keywords = [];
              _.each(schema, function (table) {
                keywords.push(table.name);
                _.each(table.columns, function (c) {
                  keywords.push(c);
                });
              });

              additionalHints = _.unique(keywords);
            }

            codemirror.refresh();
          });

          $scope.$watch('syntax', function(syntax) {
            codemirror.setOption('mode', modes[syntax]);
          });

          $scope.$watch('lock', function (locked) {
            var readOnly = locked ? 'nocursor' : false;
            codemirror.setOption('readOnly', readOnly);
          });
        }
      }
    };
  }

  function queryFormatter($http, growl) {
    return {
      restrict: 'E',
      // don't create new scope to avoid ui-codemirror bug
      // seehttps://github.com/angular-ui/ui-codemirror/pull/37
      scope: false,
      template: '<button type="button" class="btn btn-default btn-s"\
                   ng-click="formatQuery()">\
                    \
                     Format Query\
                </button>',
      link: function($scope) {
        $scope.formatQuery = function formatQuery() {
          if ($scope.dataSource.syntax == 'json') {
            try {
              $scope.query.query = JSON.stringify(JSON.parse($scope.query.query), ' ', 4);
            } catch(err) {
              growl.addErrorMessage(err);
            }
          } else if ($scope.dataSource.syntax =='sql') {

            $scope.queryFormatting = true;
            $http.post('api/queries/format', {
              'query': $scope.query.query
            }).success(function (response) {
              $scope.query.query = response;
            }).finally(function () {
              $scope.queryFormatting = false;
            });
          } else {
            growl.addInfoMessage("Query formatting is not supported for your data source syntax.");
          }
        };
      }
    }
  }

  function queryTimePicker() {
    return {
      restrict: 'E',
      template: '<select ng-disabled="refreshType != \'daily\'" ng-model="hour" ng-change="updateSchedule()" ng-options="c as c for c in hourOptions"></select> :\
                 <select ng-disabled="refreshType != \'daily\'" ng-model="minute" ng-change="updateSchedule()" ng-options="c as c for c in minuteOptions"></select>',
      link: function($scope) {
        var padWithZeros = function(size, v) {
          v = String(v);
          if (v.length < size) {
            v = "0" + v;
          }
          return v;
        };

        $scope.hourOptions = _.map(_.range(0, 24), _.partial(padWithZeros, 2));
        $scope.minuteOptions = _.map(_.range(0, 60, 5), _.partial(padWithZeros, 2));

        if ($scope.query.hasDailySchedule()) {
          var parts = $scope.query.scheduleInLocalTime().split(':');
          $scope.minute = parts[1];
          $scope.hour = parts[0];
        } else {
          $scope.minute = "15";
          $scope.hour = "00";
        }

        $scope.updateSchedule = function() {
          var newSchedule = moment().hour($scope.hour).minute($scope.minute).utc().format('HH:mm');
          if (newSchedule != $scope.query.schedule) {
            $scope.query.schedule = newSchedule;
            $scope.saveQuery();
          }
        };

        $scope.$watch('refreshType', function() {
          if ($scope.refreshType == 'daily') {
            $scope.updateSchedule();
          }
        });
      }
    }
  }

  function queryRefreshSelect() {
    return {
      restrict: 'E',
      template: '<select\
                  ng-disabled="refreshType != \'periodic\'"\
                  ng-model="query.schedule"\
                  ng-change="saveQuery()"\
                  ng-options="c.value as c.name for c in refreshOptions">\
                  <option value="">No Refresh</option>\
                  </select>',
      link: function($scope) {
        $scope.refreshOptions = [
            {
                value: "60",
                name: 'Every minute'
            }
        ];

        _.each([5, 10, 15, 30], function(i) {
          $scope.refreshOptions.push({
            value: String(i*60),
            name: "Every " + i + " minutes"
          })
        });

        _.each(_.range(1, 13), function (i) {
            $scope.refreshOptions.push({
                value: String(i * 3600),
                name: 'Every ' + i + 'h'
            });
        })

        $scope.refreshOptions.push({
            value: String(24 * 3600),
            name: 'Every 24h'
        });
        $scope.refreshOptions.push({
            value: String(7 * 24 * 3600),
            name: 'Once a week'
        });
        $scope.refreshOptions.push({
            value: String(30 * 24 * 3600),
            name: 'Every 30d'
        });

        $scope.$watch('refreshType', function() {
          if ($scope.refreshType == 'periodic') {
            if ($scope.query.hasDailySchedule()) {
              $scope.query.schedule = null;
              $scope.saveQuery();
            }
          }
        });
      }

    }
  }

  angular.module('redash.directives')
  .directive('queryLink', queryLink)
  .directive('querySourceLink', ['$location', querySourceLink])
  .directive('queryResultLink', queryResultLink)
  .directive('queryEditor', queryEditor)
  .directive('queryRefreshSelect', queryRefreshSelect)
  .directive('queryTimePicker', queryTimePicker)
  .directive('queryFormatter', ['$http', 'growl', queryFormatter]);
})();
<commit_msg>Add every 14 days refresh option<commit_after>(function() {
  'use strict'

  function queryLink() {
    return {
      restrict: 'E',
      scope: {
        'query': '=',
        'visualization': '=?'
      },
      template: '{{query.name}}',
      link: function(scope, element) {
        var hash = null;
        if (scope.visualization) {
          if (scope.visualization.type === 'TABLE') {
            // link to hard-coded table tab instead of the (hidden) visualization tab
            hash = 'table';
          } else {
            hash = scope.visualization.id;
          }
        }
        scope.link = scope.query.getUrl(false, hash);
      }
    }
  }

  function querySourceLink($location) {
    return {
      restrict: 'E',
      template: '\
                    Show Source\
                    \
                    Hide Source\
                    \
                '
    }
  }

  function queryResultLink() {
    return {
      restrict: 'A',
      link: function (scope, element, attrs) {

        var fileType = attrs.fileType ? attrs.fileType : "csv";
        scope.$watch('queryResult && queryResult.getData()', function(data) {
          if (!data) {
            return;
          }

          if (scope.queryResult.getId() == null) {
            element.attr('href', '');
          } else {
            element.attr('href', 'api/queries/' + scope.query.id + '/results/' + scope.queryResult.getId() + '.' + fileType);
            element.attr('download', scope.query.name.replace(" ", "_") + moment(scope.queryResult.getUpdatedAt()).format("_YYYY_MM_DD") + "." + fileType);
          }
        });
      }
    }
  }

  function queryEditor() {
    return {
      restrict: 'E',
      scope: {
        'query': '=',
        'lock': '=',
        'schema': '=',
        'syntax': '='
      },
      template: '<textarea></textarea>',
      link: {
        pre: function ($scope, element) {
          $scope.syntax = $scope.syntax || 'sql';

          var modes = {
            'sql': 'text/x-sql',
            'python': 'text/x-python',
            'json': 'application/json'
          };

          var textarea = element.children()[0];
          var editorOptions = {
            mode: modes[$scope.syntax],
            lineWrapping: true,
            lineNumbers: true,
            readOnly: false,
            matchBrackets: true,
            autoCloseBrackets: true,
            extraKeys: {"Ctrl-Space": "autocomplete"}
          };

          var additionalHints = [];

          CodeMirror.commands.autocomplete = function(cm) {
            var hinter  = function(editor, options) {
              var hints = CodeMirror.hint.anyword(editor, options);
              var cur = editor.getCursor(), token = editor.getTokenAt(cur).string;

              hints.list = _.union(hints.list, _.filter(additionalHints, function (h) {
                return h.search(token) === 0;
              }));

              return hints;
            };

//            CodeMirror.showHint(cm, CodeMirror.hint.anyword);
            CodeMirror.showHint(cm, hinter);
          };

          var codemirror = CodeMirror.fromTextArea(textarea, editorOptions);

          codemirror.on('change', function(instance) {
            var newValue = instance.getValue();

            if (newValue !== $scope.query.query) {
              $scope.$evalAsync(function() {
                $scope.query.query = newValue;
              });
            }
          });

          $scope.$watch('query.query', function () {
            if ($scope.query.query !== codemirror.getValue()) {
              codemirror.setValue($scope.query.query);
            }
          });

          $scope.$watch('schema', function (schema) {
            if (schema) {
              var keywords = [];
              _.each(schema, function (table) {
                keywords.push(table.name);
                _.each(table.columns, function (c) {
                  keywords.push(c);
                });
              });

              additionalHints = _.unique(keywords);
            }

            codemirror.refresh();
          });

          $scope.$watch('syntax', function(syntax) {
            codemirror.setOption('mode', modes[syntax]);
          });

          $scope.$watch('lock', function (locked) {
            var readOnly = locked ? 'nocursor' : false;
            codemirror.setOption('readOnly', readOnly);
          });
        }
      }
    };
  }

  function queryFormatter($http, growl) {
    return {
      restrict: 'E',
      // don't create new scope to avoid ui-codemirror bug
      // seehttps://github.com/angular-ui/ui-codemirror/pull/37
      scope: false,
      template: '<button type="button" class="btn btn-default btn-s"\
                   ng-click="formatQuery()">\
                    \
                     Format Query\
                </button>',
      link: function($scope) {
        $scope.formatQuery = function formatQuery() {
          if ($scope.dataSource.syntax == 'json') {
            try {
              $scope.query.query = JSON.stringify(JSON.parse($scope.query.query), ' ', 4);
            } catch(err) {
              growl.addErrorMessage(err);
            }
          } else if ($scope.dataSource.syntax =='sql') {

            $scope.queryFormatting = true;
            $http.post('api/queries/format', {
              'query': $scope.query.query
            }).success(function (response) {
              $scope.query.query = response;
            }).finally(function () {
              $scope.queryFormatting = false;
            });
          } else {
            growl.addInfoMessage("Query formatting is not supported for your data source syntax.");
          }
        };
      }
    }
  }

  function queryTimePicker() {
    return {
      restrict: 'E',
      template: '<select ng-disabled="refreshType != \'daily\'" ng-model="hour" ng-change="updateSchedule()" ng-options="c as c for c in hourOptions"></select> :\
                 <select ng-disabled="refreshType != \'daily\'" ng-model="minute" ng-change="updateSchedule()" ng-options="c as c for c in minuteOptions"></select>',
      link: function($scope) {
        var padWithZeros = function(size, v) {
          v = String(v);
          if (v.length < size) {
            v = "0" + v;
          }
          return v;
        };

        $scope.hourOptions = _.map(_.range(0, 24), _.partial(padWithZeros, 2));
        $scope.minuteOptions = _.map(_.range(0, 60, 5), _.partial(padWithZeros, 2));

        if ($scope.query.hasDailySchedule()) {
          var parts = $scope.query.scheduleInLocalTime().split(':');
          $scope.minute = parts[1];
          $scope.hour = parts[0];
        } else {
          $scope.minute = "15";
          $scope.hour = "00";
        }

        $scope.updateSchedule = function() {
          var newSchedule = moment().hour($scope.hour).minute($scope.minute).utc().format('HH:mm');
          if (newSchedule != $scope.query.schedule) {
            $scope.query.schedule = newSchedule;
            $scope.saveQuery();
          }
        };

        $scope.$watch('refreshType', function() {
          if ($scope.refreshType == 'daily') {
            $scope.updateSchedule();
          }
        });
      }
    }
  }

  function queryRefreshSelect() {
    return {
      restrict: 'E',
      template: '<select\
                  ng-disabled="refreshType != \'periodic\'"\
                  ng-model="query.schedule"\
                  ng-change="saveQuery()"\
                  ng-options="c.value as c.name for c in refreshOptions">\
                  <option value="">No Refresh</option>\
                  </select>',
      link: function($scope) {
        $scope.refreshOptions = [
            {
                value: "60",
                name: 'Every minute'
            }
        ];

        _.each([5, 10, 15, 30], function(i) {
          $scope.refreshOptions.push({
            value: String(i*60),
            name: "Every " + i + " minutes"
          })
        });

        _.each(_.range(1, 13), function (i) {
            $scope.refreshOptions.push({
                value: String(i * 3600),
                name: 'Every ' + i + 'h'
            });
        })

        $scope.refreshOptions.push({
            value: String(24 * 3600),
            name: 'Every 24h'
        });
        $scope.refreshOptions.push({
            value: String(7 * 24 * 3600),
            name: 'Every 7 days'
        });
        $scope.refreshOptions.push({
          value: String(14 * 24 * 3600),
          name: 'Every 14 days'
        });
        $scope.refreshOptions.push({
            value: String(30 * 24 * 3600),
            name: 'Every 30 days'
        });

        $scope.$watch('refreshType', function() {
          if ($scope.refreshType == 'periodic') {
            if ($scope.query.hasDailySchedule()) {
              $scope.query.schedule = null;
              $scope.saveQuery();
            }
          }
        });
      }

    }
  }

  angular.module('redash.directives')
  .directive('queryLink', queryLink)
  .directive('querySourceLink', ['$location', querySourceLink])
  .directive('queryResultLink', queryResultLink)
  .directive('queryEditor', queryEditor)
  .directive('queryRefreshSelect', queryRefreshSelect)
  .directive('queryTimePicker', queryTimePicker)
  .directive('queryFormatter', ['$http', 'growl', queryFormatter]);
})();
<|endoftext|>


<commit_before>define(function(require, exports, module) {
    "use strict";

    var app = require("app");
    var AbstractModel = require("modules/model/AbstractModel");
    var AbstractView = require("modules/view/AbstractView");
    var ArticleRegistFileItemView = require("modules/view/posting/news/ArticleRegistFileItemView");
    var ArticleRegistConfirmView = require("modules/view/posting/news/ArticleRegistConfirmView");
    var FileAPIUtil = require("modules/util/FileAPIUtil");
    var DateUtil = require("modules/util/DateUtil");
    var BusinessUtil = require("modules/util/BusinessUtil");
    var CommonUtil = require("modules/util/CommonUtil");
    var Code = require("modules/util/Code");
    var ArticleModel = require("modules/model/article/ArticleModel");
    var vexDialog = require("vexDialog");

    /**
     * 記事新規登録画面のViewクラス
     *
     * @class 記事新規登録画面のViewクラス
     * @exports ArticleRegistView
     * @constructor
     */
    var ArticleRegistView = AbstractView.extend({
        template : require("ldsh!templates/{mode}/news/articleRegist"),
        /**
         * このビューのModel
         */
        model : null,
        /**
         * フォーム要素のID
         */
        formId : '#articleRegistForm',
        /**
         * 画像名
         */
        fileName : '',
        /**
         * 読み込み時のイメージ配列
         */
        imgArray : null,
        /**
         * サムネイルの元画像のByteArray
         * サムネイルを生成する必要がない場合はnull
         */
        thumbImageByteArray : null,

        beforeRendered : function() {

        },

        afterRendered : function() {
            $("#snap-content").scrollTop(0);

            // 記事カテゴリ選択肢
            var categories = _.filter(Code.ARTICLE_CATEGORY_LIST, function(category) {
                return _.find(Code.ARTICLE_CATEGORY_LIST_BY_MODE[app.config.basic.mode], function(key) {
                    return category.key === key;
                });
            });
            _.each(categories, function(category) {
                $("#articleCategory").append("<option value='" + category.key + "'>" + category.value + "</option>");
            });

            if (this.model) {
                // 編集時
                this.setData();
            } else {
                var tomorrow = DateUtil.addDay(new Date(), 1);
                $("#articleRangeDate1").val(DateUtil.formatDate(tomorrow, "yyyy-MM-dd"));
                this.insertView("#fileArea", new ArticleRegistFileItemView()).render();

                // 記事カテゴリ初期値
                if (this.articleCategory) {
                    $("#articleCategory").val(this.articleCategory);
                }
                // レポートを書く場合
                if (this.parentModel) {
                    $("#articleTitle").val(this.parentModel.get("title") + "の報告");
                    $("#articleDate1").val(this.parentModel.get("startDate"));
                    if (this.parentModel.get("endDate")) {
                        $("#articleMultiDate").attr("checked", "checked");
                        $("#articleDate2").val(this.parentModel.get("endDate"));
                    }
                    $("#articleTime1").val(this.parentModel.get("startTime"));
                    $("#articleTime2").val(this.parentModel.get("endTime"));
                    $("#articlePlace").val(this.parentModel.get("place"));
                    $("#articleContact").val(this.parentModel.get("contactInfo"));
                }
            }
            this.chageMultiDateCheckbox();
            this.setValidator();
        },

        setValidator : function() {
            var articleCategory = $("#articleCategory").val();
            if(_.find(Code.ARTICLE_CATEGORY_LIST_BY_MODE[Code.APP_MODE_POSTING], function(category){
                return category === articleCategory;
            })){
                $("#articleDate1").addClass("required");
            } else {
                $("#articleDate1").removeClass("required");
            }
            if (articleCategory === "6") {
                $("#articleTitle").removeClass("required");
                $("#articleDetail").addClass("required");
            } else {
                $("#articleTitle").addClass("required");
                $("#articleDetail").removeClass("required");
            }
        },

        initialize : function() {
        },

        events : {
            "click #addFileForm" : "onAddFileForm",
            "click #articleConfirmButton" : "onClickArticleConfirmButton",
            "click #articleCancelButton" : "onClickArticleCancelButton",
            "change #articleMultiDate" : "chageMultiDateCheckbox",
            "change #articleCategory" : "setValidator"
        },
        /**
         * 編集時にデータを各フォームにセットする
         */
        setData : function() {
            $("#articleRegistTitle").text("記事編集");
            $("#articleCategory").val(this.model.get("type"));
            $("#articleTitle").val(this.model.get("title"));
            $("#articleDate1").val(this.model.get("startDate"));
            if (this.model.get("endDate")) {
                $("#articleMultiDate").attr("checked", "checked");
                $("#articleDate2").val(this.model.get("endDate"));
            }
            $("#articleTime1").val(this.model.get("startTime"));
            $("#articleTime2").val(this.model.get("endTime"));
            $("#articlePlace").val(this.model.get("place"));
            $("#articleDetail").val(this.model.get("description"));
            $("#articleContact").val(this.model.get("contactInfo"));
            $("#articleRangeDate1").val(this.model.get("publishedAt"));
            $("#articleRangeDate2").val(this.model.get("depublishedAt"));
            if (this.model.get("type") !== "2") {
                this.imgArray = [];
                if (this.model.get("imageUrl")) {
                    this.imgArray.push({
                        fileName : this.model.get("imageUrl"),
                        comment : this.model.get("imageComment")
                    });
                }
                if (this.model.get("imageUrl2")) {
                    this.imgArray.push({
                        fileName : this.model.get("imageUrl2"),
                        comment : this.model.get("imageComment2")
                    });
                }
                if (this.model.get("imageUrl3")) {
                    this.imgArray.push({
                        fileName : this.model.get("imageUrl3"),
                        comment : this.model.get("imageComment3")
                    });
                }
                if (this.imgArray.length === 0) {
                    this.insertView("#fileArea", new ArticleRegistFileItemView()).render();
                    this.hideLoading();
                } else if (this.imgArray.length >= 3) {
                    $("#addFileForm").hide();
                }
                var index = this.imgArray.length;
                _.each(this.imgArray, $.proxy(function(img) {
                    var view = new ArticleRegistFileItemView();
                    view.imageUrl = this.model.get("imagePath") + "/" + img.fileName;
                    view.imageComment = img.comment;
                    this.insertView("#fileArea", view).render();
                    // 全ての画像の読み込み処理が完了したタイミングでローディングマスクを解除したいため
                    // 子要素で画像読み込み完了時に発火したchangeイベントを拾って最後の画像読み込み完了時にhideLoadingする
                    view.$el.find("#previewFile").on("change", $.proxy(function() {
                        if (--index <= 0) {
                            this.hideLoading();
                            // 全ての画像読み込み完了した場合は、もうchageイベントは拾わない
                            $("img#previewFile").off("change");
                        }
                    }, this));
                }, this));
            }
        },
        /**
         * キャンセルボタン押下時のコールバック関数
         */
        onClickArticleCancelButton : function() {
            if (this.backFunction) {
                this.backFunction();
            } else {
                app.router.back();
            }
        },
        /**
         * 複数日チェックボックスのチェック有無でフォームを切り替える関数
         */
        chageMultiDateCheckbox : function() {
            if ($("#articleMultiDate").is(":checked")) {
                $(".articleDateTo").show();
            } else {
                $(".articleDateTo").hide();
            }
        },
        /**
         * 画像を追加ボタンを押された際のコールバック関数
         */
        onAddFileForm : function() {
            this.insertView("#fileArea", new ArticleRegistFileItemView()).render();
            if ($("#fileArea").children().size() >= 3) {
                $("#addFileForm").hide();
            }
        },
        /**
         * 確認画面へボタンを押された際のコールバック関数
         */
        onClickArticleConfirmButton : function() {
            if ($(this.formId).validate().form()) {
                var errmsg = this.validate();
                if (errmsg) {
                    vexDialog.defaultOptions.className = 'vex-theme-default';
                    vexDialog.alert(errmsg);
                } else {
                    this.onSubmit();
                }
            }
        },
        /**
         * バリデーションチェック
         */
        validate : function() {
            if ($("#articleMultiDate").is(":checked")) {
                if ($("#articleDate1").val() > $("#articleDate2").val()) {
                    return "日時の日付が開始と終了で逆になっています。";
                }
            }
            if ($("#articleTime1").val() && $("#articleTime2").val() &&
                    $("#articleTime1").val() > $("#articleTime2").val()) {
                return "日時の時間が開始と終了で逆になっています。";
            }
            if ($("#articleRangeDate1").val() && $("#articleRangeDate2").val() &&
                    $("#articleRangeDate1").val() > $("#articleRangeDate2").val()) {
                return "掲載期間の日付が開始と終了で逆になっています。";
            }
            if ($("#articleRangeDate1").val() <= DateUtil.formatDate(BusinessUtil.getCurrentPublishDate(), "yyyy-MM-dd")) {
                return "掲載開始日に指定した日付は、すでに新聞が発行済みです。";
            }
            return null;
        },
        /**
         * モデルにデータをセットする関数
         */
        setInputValue : function() {
            if (this.model === null) {
                this.model = new ArticleModel();
                this.model.id = AbstractModel.createNewId();
            }
            if(!this.model.get("imagePath")){
                this.model.set("imagePath", this.generateFilePath());
            }

            if (this.parentModel) {
                this.model.set("parent", this.parentModel.get("__id"));
            }

            this.model.set("type", $("#articleCategory").val());
            this.model.set("site", $("#articleCategory option:selected").text());
            this.model.set("title", $("#articleTitle").val());

            this.model.set("startDate", $("#articleDate1").val());

            if ($("#articleMultiDate").is(":checked")) {
                this.model.set("endDate", $("#articleDate2").val());
            }

            this.model.set("startTime", $("#articleTime1").val());
            this.model.set("endTime", $("#articleTime2").val());

            this.model.set("place", $("#articlePlace").val());
            this.model.set("description", $("#articleDetail").val());
            this.model.set("contactInfo", $("#articleContact").val());

            this.model.set("publishedAt", $("#articleRangeDate1").val());
            this.model.set("depublishedAt", $("#articleRangeDate2").val());

            this.model.set("status", "0");
            this.model.set("createUserId", app.user.get("__id"));

            var imageCount = this.$el.find("#fileArea").children().size();
            var images = [];
            var self = this;
            var $fileAreas = this.$el.find("#fileArea").children();

            /**
             * ファイル名を元に、ユニークなID付きのファイル名を生成する
             *
             * @param {String} fileName
             * @return {String}
             */
            var generateFileName = function(fileName) {
                fileName = CommonUtil.blankTrim(fileName);
                var preName = fileName.substr(0, fileName.lastIndexOf("."));
                var suffName = fileName.substr(fileName.lastIndexOf("."));

                return preName + "_" + new Date().getTime() + _.uniqueId("") + suffName;
            };

            // サムネイルの基となる画像
            this.thumbImageByteArray = undefined;

            // サムネイル画像に使うviewの

            // 画像に変更があるかチェックする
            for (var i = 0; i < $fileAreas.length; i++) {
                var $fileArea = $fileAreas.eq(i);
                var fileAreaView = $fileArea.data("view");
                if( fileAreaView.imageByteArray ) {
                    if( this.thumbImageByteArray === undefined) {
                        if(fileAreaView.isChangeImage || i != 0) {
                            this.thumbImageByteArray = fileAreaView.imageByteArray;
                        } else {
                            this.thumbImageByteArray = null;
                        }
                    }
                    var $previewImg = $fileArea.find("[data-preview-file]");
                    var file = $previewImg.prop("file");
                    var src = $previewImg.attr("src");
                    var comment = $fileArea.find("#articleFileComent").val();
                    var image;

                    if (!src) {
                        continue;
                    }

                    // 画像に変更があれば情報を更新する
                    if (file) {
                        image = {};

                        image.fileName = generateFileName(file.name);
                        image.contentType = file.type;
                        image.data = $fileArea.find("#articleFile").prop("data");
                    } else {
                        image = this.imgArray[i];
                    }

                    // 画像の情報を更新する
                    if (image) {
                        image.comment = comment;
                        image.src = src;
                    }

                    images.push(image);
                }
            }

            // imagesを元に、imageUrlとimageCommentを更新する
            for (var imageIndex = 0; imageIndex < 3; imageIndex++) {
                // { "", 2, 3, ... }
                var surfix = (imageIndex === 0) ? "" : "" + (imageIndex + 1);
                var img = images[imageIndex] || {};

                this.model.set("imageUrl" + surfix, img.fileName);
                this.model.set("imageComment" + surfix, img.comment);
            }

            this.model.set("images", images);

            // サムネイルのURL
            if ( this.thumbImageByteArray === undefined ) {
                // 画像なしなので、サムネイルもなし
                this.model.set("imageThumbUrl", null);
            } else {
                this.model.set("imageThumbUrl", "thumbnail.png");
            }
        },

        /**
         * バリデーションチェックがOKとなり、登録処理が開始された際に呼び出されるコールバック関数。
         */
        onSubmit : function() {
            // 登録処理を開始する
            this.setInputValue();
            $("#articleRegistPage").hide();
            this.setView("#articleRegistConfirmWrapperPage", new ArticleRegistConfirmView({
                model : this.model,
                thumbImageByteArray : this.thumbImageByteArray
            })).render();
            $("#snap-content").scrollTop(0);
        },

    });
    module.exports = ArticleRegistView;
});
<commit_msg>NAM-607 サムネイル対応<commit_after>define(function(require, exports, module) {
    "use strict";

    var app = require("app");
    var AbstractModel = require("modules/model/AbstractModel");
    var AbstractView = require("modules/view/AbstractView");
    var ArticleRegistFileItemView = require("modules/view/posting/news/ArticleRegistFileItemView");
    var ArticleRegistConfirmView = require("modules/view/posting/news/ArticleRegistConfirmView");
    var FileAPIUtil = require("modules/util/FileAPIUtil");
    var DateUtil = require("modules/util/DateUtil");
    var BusinessUtil = require("modules/util/BusinessUtil");
    var CommonUtil = require("modules/util/CommonUtil");
    var Code = require("modules/util/Code");
    var ArticleModel = require("modules/model/article/ArticleModel");
    var vexDialog = require("vexDialog");

    /**
     * 記事新規登録画面のViewクラス
     *
     * @class 記事新規登録画面のViewクラス
     * @exports ArticleRegistView
     * @constructor
     */
    var ArticleRegistView = AbstractView.extend({
        template : require("ldsh!templates/{mode}/news/articleRegist"),
        /**
         * このビューのModel
         */
        model : null,
        /**
         * フォーム要素のID
         */
        formId : '#articleRegistForm',
        /**
         * 画像名
         */
        fileName : '',
        /**
         * 読み込み時のイメージ配列
         */
        imgArray : null,
        /**
         * サムネイルの元画像のByteArray
         * サムネイルを生成する必要がない場合はnull
         */
        thumbImageByteArray : null,

        beforeRendered : function() {

        },

        afterRendered : function() {
            $("#snap-content").scrollTop(0);

            // 記事カテゴリ選択肢
            var categories = _.filter(Code.ARTICLE_CATEGORY_LIST, function(category) {
                return _.find(Code.ARTICLE_CATEGORY_LIST_BY_MODE[app.config.basic.mode], function(key) {
                    return category.key === key;
                });
            });
            _.each(categories, function(category) {
                $("#articleCategory").append("<option value='" + category.key + "'>" + category.value + "</option>");
            });

            if (this.model) {
                // 編集時
                this.setData();
            } else {
                var tomorrow = DateUtil.addDay(new Date(), 1);
                $("#articleRangeDate1").val(DateUtil.formatDate(tomorrow, "yyyy-MM-dd"));
                this.insertView("#fileArea", new ArticleRegistFileItemView()).render();

                // 記事カテゴリ初期値
                if (this.articleCategory) {
                    $("#articleCategory").val(this.articleCategory);
                }
                // レポートを書く場合
                if (this.parentModel) {
                    $("#articleTitle").val(this.parentModel.get("title") + "の報告");
                    $("#articleDate1").val(this.parentModel.get("startDate"));
                    if (this.parentModel.get("endDate")) {
                        $("#articleMultiDate").attr("checked", "checked");
                        $("#articleDate2").val(this.parentModel.get("endDate"));
                    }
                    $("#articleTime1").val(this.parentModel.get("startTime"));
                    $("#articleTime2").val(this.parentModel.get("endTime"));
                    $("#articlePlace").val(this.parentModel.get("place"));
                    $("#articleContact").val(this.parentModel.get("contactInfo"));
                }
            }
            this.chageMultiDateCheckbox();
            this.setValidator();
        },

        setValidator : function() {
            var articleCategory = $("#articleCategory").val();
            if(_.find(Code.ARTICLE_CATEGORY_LIST_BY_MODE[Code.APP_MODE_POSTING], function(category){
                return category === articleCategory;
            })){
                $("#articleDate1").addClass("required");
            } else {
                $("#articleDate1").removeClass("required");
            }
            if (articleCategory === "6") {
                $("#articleTitle").removeClass("required");
                $("#articleDetail").addClass("required");
            } else {
                $("#articleTitle").addClass("required");
                $("#articleDetail").removeClass("required");
            }
        },

        initialize : function() {
        },

        events : {
            "click #addFileForm" : "onAddFileForm",
            "click #articleConfirmButton" : "onClickArticleConfirmButton",
            "click #articleCancelButton" : "onClickArticleCancelButton",
            "change #articleMultiDate" : "chageMultiDateCheckbox",
            "change #articleCategory" : "setValidator"
        },
        /**
         * 編集時にデータを各フォームにセットする
         */
        setData : function() {
            $("#articleRegistTitle").text("記事編集");
            $("#articleCategory").val(this.model.get("type"));
            $("#articleTitle").val(this.model.get("title"));
            $("#articleDate1").val(this.model.get("startDate"));
            if (this.model.get("endDate")) {
                $("#articleMultiDate").attr("checked", "checked");
                $("#articleDate2").val(this.model.get("endDate"));
            }
            $("#articleTime1").val(this.model.get("startTime"));
            $("#articleTime2").val(this.model.get("endTime"));
            $("#articlePlace").val(this.model.get("place"));
            $("#articleDetail").val(this.model.get("description"));
            $("#articleContact").val(this.model.get("contactInfo"));
            $("#articleRangeDate1").val(this.model.get("publishedAt"));
            $("#articleRangeDate2").val(this.model.get("depublishedAt"));
            if (this.model.get("type") !== "2") {
                this.imgArray = [];
                if (this.model.get("imageUrl")) {
                    this.imgArray.push({
                        fileName : this.model.get("imageUrl"),
                        comment : this.model.get("imageComment")
                    });
                }
                if (this.model.get("imageUrl2")) {
                    this.imgArray.push({
                        fileName : this.model.get("imageUrl2"),
                        comment : this.model.get("imageComment2")
                    });
                }
                if (this.model.get("imageUrl3")) {
                    this.imgArray.push({
                        fileName : this.model.get("imageUrl3"),
                        comment : this.model.get("imageComment3")
                    });
                }
                if (this.imgArray.length === 0) {
                    this.insertView("#fileArea", new ArticleRegistFileItemView()).render();
                    this.hideLoading();
                } else if (this.imgArray.length >= 3) {
                    $("#addFileForm").hide();
                }
                var index = this.imgArray.length;
                _.each(this.imgArray, $.proxy(function(img) {
                    var view = new ArticleRegistFileItemView();
                    view.imageUrl = this.model.get("imagePath") + "/" + img.fileName;
                    view.imageComment = img.comment;
                    this.insertView("#fileArea", view).render();
                    // 全ての画像の読み込み処理が完了したタイミングでローディングマスクを解除したいため
                    // 子要素で画像読み込み完了時に発火したchangeイベントを拾って最後の画像読み込み完了時にhideLoadingする
                    view.$el.find("#previewFile").on("change", $.proxy(function() {
                        if (--index <= 0) {
                            this.hideLoading();
                            // 全ての画像読み込み完了した場合は、もうchageイベントは拾わない
                            $("img#previewFile").off("change");
                        }
                    }, this));
                }, this));
            }
        },
        /**
         * キャンセルボタン押下時のコールバック関数
         */
        onClickArticleCancelButton : function() {
            if (this.backFunction) {
                this.backFunction();
            } else {
                app.router.back();
            }
        },
        /**
         * 複数日チェックボックスのチェック有無でフォームを切り替える関数
         */
        chageMultiDateCheckbox : function() {
            if ($("#articleMultiDate").is(":checked")) {
                $(".articleDateTo").show();
            } else {
                $(".articleDateTo").hide();
            }
        },
        /**
         * 画像を追加ボタンを押された際のコールバック関数
         */
        onAddFileForm : function() {
            this.insertView("#fileArea", new ArticleRegistFileItemView()).render();
            if ($("#fileArea").children().size() >= 3) {
                $("#addFileForm").hide();
            }
        },
        /**
         * 確認画面へボタンを押された際のコールバック関数
         */
        onClickArticleConfirmButton : function() {
            if ($(this.formId).validate().form()) {
                var errmsg = this.validate();
                if (errmsg) {
                    vexDialog.defaultOptions.className = 'vex-theme-default';
                    vexDialog.alert(errmsg);
                } else {
                    this.onSubmit();
                }
            }
        },
        /**
         * バリデーションチェック
         */
        validate : function() {
            if ($("#articleMultiDate").is(":checked")) {
                if ($("#articleDate1").val() > $("#articleDate2").val()) {
                    return "日時の日付が開始と終了で逆になっています。";
                }
            }
            if ($("#articleTime1").val() && $("#articleTime2").val() &&
                    $("#articleTime1").val() > $("#articleTime2").val()) {
                return "日時の時間が開始と終了で逆になっています。";
            }
            if ($("#articleRangeDate1").val() && $("#articleRangeDate2").val() &&
                    $("#articleRangeDate1").val() > $("#articleRangeDate2").val()) {
                return "掲載期間の日付が開始と終了で逆になっています。";
            }
            if ($("#articleRangeDate1").val() <= DateUtil.formatDate(BusinessUtil.getCurrentPublishDate(), "yyyy-MM-dd")) {
                return "掲載開始日に指定した日付は、すでに新聞が発行済みです。";
            }
            return null;
        },
        /**
         * モデルにデータをセットする関数
         */
        setInputValue : function() {
            if (this.model === null) {
                this.model = new ArticleModel();
                this.model.id = AbstractModel.createNewId();
            }
            if(!this.model.get("imagePath")){
                this.model.set("imagePath", this.generateFilePath());
            }

            if (this.parentModel) {
                this.model.set("parent", this.parentModel.get("__id"));
            }

            this.model.set("type", $("#articleCategory").val());
            this.model.set("site", $("#articleCategory option:selected").text());
            this.model.set("title", $("#articleTitle").val());

            this.model.set("startDate", $("#articleDate1").val());

            if ($("#articleMultiDate").is(":checked")) {
                this.model.set("endDate", $("#articleDate2").val());
            }

            this.model.set("startTime", $("#articleTime1").val());
            this.model.set("endTime", $("#articleTime2").val());

            this.model.set("place", $("#articlePlace").val());
            this.model.set("description", $("#articleDetail").val());
            this.model.set("contactInfo", $("#articleContact").val());

            this.model.set("publishedAt", $("#articleRangeDate1").val());
            this.model.set("depublishedAt", $("#articleRangeDate2").val());

            this.model.set("status", "0");
            this.model.set("createUserId", app.user.get("__id"));

            var imageCount = this.$el.find("#fileArea").children().size();
            var images = [];
            var self = this;
            var $fileAreas = this.$el.find("#fileArea").children();

            /**
             * ファイル名を元に、ユニークなID付きのファイル名を生成する
             *
             * @param {String} fileName
             * @return {String}
             */
            var generateFileName = function(fileName) {
                fileName = CommonUtil.blankTrim(fileName);
                var preName = fileName.substr(0, fileName.lastIndexOf("."));
                var suffName = fileName.substr(fileName.lastIndexOf("."));

                return preName + "_" + new Date().getTime() + _.uniqueId("") + suffName;
            };

            // サムネイルの基となる画像
            this.thumbImageByteArray = undefined;

            // サムネイル画像に使うviewの

            // 画像に変更があるかチェックする
            for (var i = 0; i < $fileAreas.length; i++) {
                var $fileArea = $fileAreas.eq(i);
                var fileAreaView = $fileArea.data("view");
                if( fileAreaView.imageByteArray ) {
                    if( this.thumbImageByteArray === undefined) {
                        if(fileAreaView.isChangeImage || i !== 0) {
                            this.thumbImageByteArray = fileAreaView.imageByteArray;
                        } else {
                            this.thumbImageByteArray = null;
                        }
                    }
                    var $previewImg = $fileArea.find("[data-preview-file]");
                    var file = $previewImg.prop("file");
                    var src = $previewImg.attr("src");
                    var comment = $fileArea.find("#articleFileComent").val();
                    var image;

                    if (!src) {
                        continue;
                    }

                    // 画像に変更があれば情報を更新する
                    if (file) {
                        image = {};

                        image.fileName = generateFileName(file.name);
                        image.contentType = file.type;
                        image.data = $fileArea.find("#articleFile").prop("data");
                    } else {
                        image = this.imgArray[i];
                    }

                    // 画像の情報を更新する
                    if (image) {
                        image.comment = comment;
                        image.src = src;
                    }

                    images.push(image);
                }
            }

            // imagesを元に、imageUrlとimageCommentを更新する
            for (var imageIndex = 0; imageIndex < 3; imageIndex++) {
                // { "", 2, 3, ... }
                var surfix = (imageIndex === 0) ? "" : "" + (imageIndex + 1);
                var img = images[imageIndex] || {};

                this.model.set("imageUrl" + surfix, img.fileName);
                this.model.set("imageComment" + surfix, img.comment);
            }

            this.model.set("images", images);

            // サムネイルのURL
            if ( this.thumbImageByteArray === undefined ) {
                // 画像なしなので、サムネイルもなし
                this.model.set("imageThumbUrl", null);
            } else {
                this.model.set("imageThumbUrl", "thumbnail.png");
            }
        },

        /**
         * バリデーションチェックがOKとなり、登録処理が開始された際に呼び出されるコールバック関数。
         */
        onSubmit : function() {
            // 登録処理を開始する
            this.setInputValue();
            $("#articleRegistPage").hide();
            this.setView("#articleRegistConfirmWrapperPage", new ArticleRegistConfirmView({
                model : this.model,
                thumbImageByteArray : this.thumbImageByteArray
            })).render();
            $("#snap-content").scrollTop(0);
        },

    });
    module.exports = ArticleRegistView;
});
<|endoftext|>