<commit_before>import * as ActionTypes from '../constants/actionTypes';
import { validationError } from './utils';
import { get, put } from '../utils/fetch';
import { toYAML, toJSON, getExtensionFromPath } from '../utils/helpers';
import { validator } from '../utils/validation';
import {
  getContentRequiredMessage,
  getFilenameRequiredMessage
} from '../constants/lang';
import {
  datafilesAPIUrl,
  datafileAPIUrl
} from '../constants/api';

export function fetchDataFiles(directory = '') {
  return (dispatch) => {
    dispatch({ type: ActionTypes.FETCH_DATAFILES_REQUEST});
    return get(
      datafilesAPIUrl(directory),
      { type: ActionTypes.FETCH_DATAFILES_SUCCESS, name: "files"},
      { type: ActionTypes.FETCH_DATAFILES_FAILURE, name: "error"},
      dispatch
    );
  };
}

export function fetchDataFile(directory, filename) {
  return (dispatch) => {
    dispatch({ type: ActionTypes.FETCH_DATAFILE_REQUEST});
    return get(
      datafileAPIUrl(directory, filename),
      { type: ActionTypes.FETCH_DATAFILE_SUCCESS, name: "file"},
      { type: ActionTypes.FETCH_DATAFILE_FAILURE, name: "error"},
      dispatch
    );
  };
}

export function putDataFile(directory, filename, data, new_path = '', source = 'editor') {
  return (dispatch, getState) => {
    const ext = getExtensionFromPath(filename);
    let payload = {};
    let errors;

    if (source == "editor") {
      errors = validateDatafile(filename, data);

      if (new_path) {
        payload = { path: new_path, raw_content: data };
      } else {
        payload = { raw_content: data };
      }

    } else if (source == "gui") {
      const metadata = getState().metadata.metadata;
      const yaml = /yaml|yml/i.test(ext);
      const json = /json/i.test(ext);
      const yaml_string = toYAML(metadata);
      errors = validateDatafile(filename, metadata);

      if (yaml) {
        if (new_path) {
          payload = { path: new_path, raw_content: yaml_string };
        } else {
          payload = { raw_content: yaml_string };
        }
      } else if (json) {
        if (new_path) {
          payload = { path: new_path, raw_content: JSON.stringify(metadata, null, 2) };
        } else {
          payload = { raw_content: JSON.stringify(metadata, null, 2) };
        }
      }
    }

    if (errors.length) {
      return dispatch(validationError(errors));
    }
    // clear errors
    dispatch({type: ActionTypes.CLEAR_ERRORS});

    return put(
      datafileAPIUrl(directory, filename),
      JSON.stringify(payload),
      { type: ActionTypes.PUT_DATAFILE_SUCCESS, name: "file"},
      { type: ActionTypes.PUT_DATAFILE_FAILURE, name: "error"},
      dispatch
    );
  };
}

function validateDatafile(filename, data) {
  return validator(
    { filename, data },
    { 'filename': 'required', 'data': 'required' },
    {
      'filename.required': getFilenameRequiredMessage(),
      'data.required': getContentRequiredMessage()
    }
  );
}

export function deleteDataFile(directory, filename) {
  return (dispatch) => {
    return fetch(datafileAPIUrl(directory, filename), {
      method: 'DELETE'
    })
    .then(data => {
      dispatch({ type: ActionTypes.DELETE_DATAFILE_SUCCESS });
      dispatch(fetchDataFiles());
    })
    .catch(error => dispatch({
      type: ActionTypes.DELETE_DATAFILE_FAILURE,
      error
    }));
  };
}

export function onDataFileChanged() {
  return {
    type: ActionTypes.DATAFILE_CHANGED
  };
}
<commit_msg>refactor `put` action for data files<commit_after>import * as ActionTypes from '../constants/actionTypes';
import { validationError } from './utils';
import { get, put } from '../utils/fetch';
import { toYAML, toJSON, getExtensionFromPath } from '../utils/helpers';
import { validator } from '../utils/validation';
import {
  getContentRequiredMessage,
  getFilenameRequiredMessage
} from '../constants/lang';
import {
  datafilesAPIUrl,
  datafileAPIUrl
} from '../constants/api';

export function fetchDataFiles(directory = '') {
  return (dispatch) => {
    dispatch({ type: ActionTypes.FETCH_DATAFILES_REQUEST});
    return get(
      datafilesAPIUrl(directory),
      { type: ActionTypes.FETCH_DATAFILES_SUCCESS, name: "files"},
      { type: ActionTypes.FETCH_DATAFILES_FAILURE, name: "error"},
      dispatch
    );
  };
}

export function fetchDataFile(directory, filename) {
  return (dispatch) => {
    dispatch({ type: ActionTypes.FETCH_DATAFILE_REQUEST});
    return get(
      datafileAPIUrl(directory, filename),
      { type: ActionTypes.FETCH_DATAFILE_SUCCESS, name: "file"},
      { type: ActionTypes.FETCH_DATAFILE_FAILURE, name: "error"},
      dispatch
    );
  };
}

/**
 * Creates and updates a data file.
 * @param {String} directory : Dirname of data file
 * @param {String} filename  : The data file
 * @param {Object} data      : Content to be written to data file
 * @param {String} path      : File path relative to config['source']
 * @param {String} source    : Point of origin of file-content data.
 *                             Either the default `#brace-editor`, or `<DataGUI/>`
 */
export function putDataFile(directory, filename, data, path, source = 'editor') {
  return (dispatch, getState) => {
    const ext = getExtensionFromPath(path);

    if (source == "gui") {
      const json = /json/i.test(ext);
      let metadata = getState().metadata.metadata;
      data = json ? (JSON.stringify(metadata, null, 2)) : (toYAML(metadata));
    }

    // handle errors
    const errors = validateDatafile(filename, data);
    if (errors.length) {
      return dispatch(validationError(errors));
    }
    dispatch({type: ActionTypes.CLEAR_ERRORS});

    return put(
      datafileAPIUrl(directory, filename),
      JSON.stringify({ path: path, raw_content: data }),
      { type: ActionTypes.PUT_DATAFILE_SUCCESS, name: "file"},
      { type: ActionTypes.PUT_DATAFILE_FAILURE, name: "error"},
      dispatch
    );
  };
}

function validateDatafile(filename, data) {
  return validator(
    { filename, data },
    { 'filename': 'required', 'data': 'required' },
    {
      'filename.required': getFilenameRequiredMessage(),
      'data.required': getContentRequiredMessage()
    }
  );
}

export function deleteDataFile(directory, filename) {
  return (dispatch) => {
    return fetch(datafileAPIUrl(directory, filename), {
      method: 'DELETE'
    })
    .then(data => {
      dispatch({ type: ActionTypes.DELETE_DATAFILE_SUCCESS });
      dispatch(fetchDataFiles());
    })
    .catch(error => dispatch({
      type: ActionTypes.DELETE_DATAFILE_FAILURE,
      error
    }));
  };
}

export function onDataFileChanged() {
  return {
    type: ActionTypes.DATAFILE_CHANGED
  };
}
<|endoftext|>


<commit_before>/*
 * @flow
 */

/**
 * PrincipalsApi gives access to OpenLattice's REST API for getting user data.
 *
 * @module PrincipalsApi
 * @memberof lattice
 *
 * @example
 * import Lattice from 'lattice';
 * // Lattice.PrincipalsApi.get...
 *
 * @example
 * import { PrincipalsApi } from 'lattice';
 * // PrincipalsApi.get...
 */

import Principal, { isValidPrincipal } from '../models/Principal';
import Logger from '../utils/Logger';
import { PRINCIPALS_API } from '../constants/ApiNames';
import { getApiAxiosInstance } from '../utils/axios';
import { isNonEmptyString } from '../utils/LangUtils';

import {
  DB_PATH,
  EMAIL_PATH,
  ROLES_PATH,
  SEARCH_PATH,
  USERS_PATH,
  CURRENT_PATH
} from '../constants/UrlConstants';

const LOG = new Logger('PrincipalsApi');

/**
 * `GET /principals/users/{userId}`
 *
 * @static
 * @memberof lattice.PrincipalsApi
 * @param {string} userId
 * @return {Promise}
 */
export function getUser(userId :string) :Promise<*> {

  let errorMsg = '';

  if (!isNonEmptyString(userId)) {
    errorMsg = 'invalid parameter: userId must be a non-empty string';
    LOG.error(errorMsg, userId);
    return Promise.reject(errorMsg);
  }

  return getApiAxiosInstance(PRINCIPALS_API)
    .get(`/${USERS_PATH}/${userId}`)
    .then((axiosResponse) => axiosResponse.data)
    .catch((error :Error) => {
      LOG.error(error);
      return Promise.reject(error);
    });
}

/**
 * `GET /principals/roles`
 *
 * @static
 * @memberof lattice.PrincipalsApi
 * @return {Promise}
 */
export function getAllRoles() :Promise<*> {

  return getApiAxiosInstance(PRINCIPALS_API)
    .get(`/${ROLES_PATH}`)
    .then((axiosResponse) => axiosResponse.data)
    .catch((error :Error) => {
      LOG.error(error);
      return Promise.reject(error);
    });
}

/**
 * `GET /principals/roles`
 *
 * @static
 * @memberof lattice.PrincipalsApi
 * @return {Promise}
 */
export function getCurrentRoles() :Promise<*> {

  return getApiAxiosInstance(PRINCIPALS_API)
    .get(`/${ROLES_PATH}/${CURRENT_PATH}`)
    .then((axiosResponse) => axiosResponse.data)
    .catch((error :Error) => {
      LOG.error(error);
      return Promise.reject(error);
    });
}

/**
 * `GET /principals/users`
 *
 * @static
 * @memberof lattice.PrincipalsApi
 * @return {Promise}
 */
export function getAllUsers() :Promise<*> {

  return getApiAxiosInstance(PRINCIPALS_API)
    .get(`/${USERS_PATH}`)
    .then((axiosResponse) => axiosResponse.data)
    .catch((error :Error) => {
      LOG.error(error);
      return Promise.reject(error);
    });
}

/**
 * `GET /principals/users/search/email/{searchQuery}`
 *
 * @static
 * @memberof lattice.PrincipalsApi
 * @param {string} emailSearchQuery
 * @return {Promise}
 *
 * TODO: add unit tests
 */
export function searchAllUsersByEmail(emailSearchQuery :string) :Promise<*> {

  let errorMsg = '';

  if (!isNonEmptyString(emailSearchQuery)) {
    errorMsg = 'invalid parameter: emailSearchQuery must be a non-empty string';
    LOG.error(errorMsg, emailSearchQuery);
    return Promise.reject(errorMsg);
  }

  return getApiAxiosInstance(PRINCIPALS_API)
    .get(`/${USERS_PATH}/${SEARCH_PATH}/${EMAIL_PATH}/${emailSearchQuery}`)
    .then((axiosResponse) => axiosResponse.data)
    .catch((error :Error) => {
      LOG.error(error);
      return Promise.reject(error);
    });
}

/**
 * `GET /principals/users/search/{searchQuery}`
 *
 * @static
 * @memberof lattice.PrincipalsApi
 * @param {string} searchQuery
 * @return {Promise}
 *
 * TODO: add unit tests
 */
export function searchAllUsers(searchQuery :string) :Promise<*> {

  let errorMsg = '';

  if (!isNonEmptyString(searchQuery)) {
    errorMsg = 'invalid parameter: searchQuery must be a non-empty string';
    LOG.error(errorMsg, searchQuery);
    return Promise.reject(errorMsg);
  }

  return getApiAxiosInstance(PRINCIPALS_API)
    .get(`/${USERS_PATH}/${SEARCH_PATH}/${searchQuery}`)
    .then((axiosResponse) => axiosResponse.data)
    .catch((error :Error) => {
      LOG.error(error);
      return Promise.reject(error);
    });
}

/**
 * `POST /principals`
 *
 * @static
 * @memberof lattice.PrincipalsApi
 * @param {Principal} principal
 * @return {Promise}
 *
 * TODO: add unit tests
 */
export function getSecurablePrincipal(principal :Principal) :Promise<*> {

  let errorMsg = '';

  if (!isValidPrincipal(principal)) {
    errorMsg = 'invalid parameter: principal must be a valid Principal';
    LOG.error(errorMsg, principal);
    return Promise.reject(errorMsg);
  }

  return getApiAxiosInstance(PRINCIPALS_API)
    .post('/', principal)
    .then((axiosResponse) => axiosResponse.data)
    .catch((error :Error) => {
      LOG.error(error);
      return Promise.reject(error);
    });
}

/**
 * `GET /principals/db`
 *
 * @static
 * @memberof lattice.PrincipalsApi
 * @return {Promise}
 *
 * TODO: add unit tests
 */
export function getDbAccessCredential() :Promise<*> {

  return getApiAxiosInstance(PRINCIPALS_API)
    .get(`/${DB_PATH}`)
    .then((axiosResponse) => axiosResponse.data)
    .catch((error :Error) => {
      LOG.error(error);
      return Promise.reject(error);
    });
}

/*
 *
 * EVERYTHING BELOW IS DEPRECATED!!! ONLY DELETE AFTER REMOVING REFERENCES FROM GALLERY!!!
 *
 */

export function addRoleToUser(userId :string, role :string) :Promise<*> {

  let errorMsg = '';

  if (!isNonEmptyString(userId)) {
    errorMsg = 'invalid parameter: userId must be a non-empty string';
    LOG.error(errorMsg, userId);
    return Promise.reject(errorMsg);
  }

  if (!isNonEmptyString(role)) {
    errorMsg = 'invalid parameter: role must be a non-empty string';
    LOG.error(errorMsg, role);
    return Promise.reject(errorMsg);
  }

  return getApiAxiosInstance(PRINCIPALS_API)
    .put(`/${USERS_PATH}/${userId}/${ROLES_PATH}/${role}`)
    .then((axiosResponse) => axiosResponse.data)
    .catch((error :Error) => {
      LOG.error(error);
      return Promise.reject(error);
    });
}

export function removeRoleFromUser(userId :string, role :string) :Promise<*> {

  let errorMsg = '';

  if (!isNonEmptyString(userId)) {
    errorMsg = 'invalid parameter: userId must be a non-empty string';
    LOG.error(errorMsg, userId);
    return Promise.reject(errorMsg);
  }

  if (!isNonEmptyString(role)) {
    errorMsg = 'invalid parameter: role must be a non-empty string';
    LOG.error(errorMsg, role);
    return Promise.reject(errorMsg);
  }

  return getApiAxiosInstance(PRINCIPALS_API)
    .delete(`/${USERS_PATH}/${userId}/${ROLES_PATH}/${role}`)
    .then((axiosResponse) => axiosResponse.data)
    .catch((error :Error) => {
      LOG.error(error);
      return Promise.reject(error);
    });
}
<commit_msg>remove PrincipalsApi.addRoleToUser<commit_after>/*
 * @flow
 */

/**
 * PrincipalsApi gives access to OpenLattice's REST API for getting user data.
 *
 * @module PrincipalsApi
 * @memberof lattice
 *
 * @example
 * import Lattice from 'lattice';
 * // Lattice.PrincipalsApi.get...
 *
 * @example
 * import { PrincipalsApi } from 'lattice';
 * // PrincipalsApi.get...
 */

import Principal, { isValidPrincipal } from '../models/Principal';
import Logger from '../utils/Logger';
import { PRINCIPALS_API } from '../constants/ApiNames';
import { getApiAxiosInstance } from '../utils/axios';
import { isNonEmptyString } from '../utils/LangUtils';

import {
  DB_PATH,
  EMAIL_PATH,
  ROLES_PATH,
  SEARCH_PATH,
  USERS_PATH,
  CURRENT_PATH
} from '../constants/UrlConstants';

const LOG = new Logger('PrincipalsApi');

/**
 * `GET /principals/users/{userId}`
 *
 * @static
 * @memberof lattice.PrincipalsApi
 * @param {string} userId
 * @return {Promise}
 */
export function getUser(userId :string) :Promise<*> {

  let errorMsg = '';

  if (!isNonEmptyString(userId)) {
    errorMsg = 'invalid parameter: userId must be a non-empty string';
    LOG.error(errorMsg, userId);
    return Promise.reject(errorMsg);
  }

  return getApiAxiosInstance(PRINCIPALS_API)
    .get(`/${USERS_PATH}/${userId}`)
    .then((axiosResponse) => axiosResponse.data)
    .catch((error :Error) => {
      LOG.error(error);
      return Promise.reject(error);
    });
}

/**
 * `GET /principals/roles`
 *
 * @static
 * @memberof lattice.PrincipalsApi
 * @return {Promise}
 */
export function getAllRoles() :Promise<*> {

  return getApiAxiosInstance(PRINCIPALS_API)
    .get(`/${ROLES_PATH}`)
    .then((axiosResponse) => axiosResponse.data)
    .catch((error :Error) => {
      LOG.error(error);
      return Promise.reject(error);
    });
}

/**
 * `GET /principals/roles`
 *
 * @static
 * @memberof lattice.PrincipalsApi
 * @return {Promise}
 */
export function getCurrentRoles() :Promise<*> {

  return getApiAxiosInstance(PRINCIPALS_API)
    .get(`/${ROLES_PATH}/${CURRENT_PATH}`)
    .then((axiosResponse) => axiosResponse.data)
    .catch((error :Error) => {
      LOG.error(error);
      return Promise.reject(error);
    });
}

/**
 * `GET /principals/users`
 *
 * @static
 * @memberof lattice.PrincipalsApi
 * @return {Promise}
 */
export function getAllUsers() :Promise<*> {

  return getApiAxiosInstance(PRINCIPALS_API)
    .get(`/${USERS_PATH}`)
    .then((axiosResponse) => axiosResponse.data)
    .catch((error :Error) => {
      LOG.error(error);
      return Promise.reject(error);
    });
}

/**
 * `GET /principals/users/search/email/{searchQuery}`
 *
 * @static
 * @memberof lattice.PrincipalsApi
 * @param {string} emailSearchQuery
 * @return {Promise}
 *
 * TODO: add unit tests
 */
export function searchAllUsersByEmail(emailSearchQuery :string) :Promise<*> {

  let errorMsg = '';

  if (!isNonEmptyString(emailSearchQuery)) {
    errorMsg = 'invalid parameter: emailSearchQuery must be a non-empty string';
    LOG.error(errorMsg, emailSearchQuery);
    return Promise.reject(errorMsg);
  }

  return getApiAxiosInstance(PRINCIPALS_API)
    .get(`/${USERS_PATH}/${SEARCH_PATH}/${EMAIL_PATH}/${emailSearchQuery}`)
    .then((axiosResponse) => axiosResponse.data)
    .catch((error :Error) => {
      LOG.error(error);
      return Promise.reject(error);
    });
}

/**
 * `GET /principals/users/search/{searchQuery}`
 *
 * @static
 * @memberof lattice.PrincipalsApi
 * @param {string} searchQuery
 * @return {Promise}
 *
 * TODO: add unit tests
 */
export function searchAllUsers(searchQuery :string) :Promise<*> {

  let errorMsg = '';

  if (!isNonEmptyString(searchQuery)) {
    errorMsg = 'invalid parameter: searchQuery must be a non-empty string';
    LOG.error(errorMsg, searchQuery);
    return Promise.reject(errorMsg);
  }

  return getApiAxiosInstance(PRINCIPALS_API)
    .get(`/${USERS_PATH}/${SEARCH_PATH}/${searchQuery}`)
    .then((axiosResponse) => axiosResponse.data)
    .catch((error :Error) => {
      LOG.error(error);
      return Promise.reject(error);
    });
}

/**
 * `POST /principals`
 *
 * @static
 * @memberof lattice.PrincipalsApi
 * @param {Principal} principal
 * @return {Promise}
 *
 * TODO: add unit tests
 */
export function getSecurablePrincipal(principal :Principal) :Promise<*> {

  let errorMsg = '';

  if (!isValidPrincipal(principal)) {
    errorMsg = 'invalid parameter: principal must be a valid Principal';
    LOG.error(errorMsg, principal);
    return Promise.reject(errorMsg);
  }

  return getApiAxiosInstance(PRINCIPALS_API)
    .post('/', principal)
    .then((axiosResponse) => axiosResponse.data)
    .catch((error :Error) => {
      LOG.error(error);
      return Promise.reject(error);
    });
}

/**
 * `GET /principals/db`
 *
 * @static
 * @memberof lattice.PrincipalsApi
 * @return {Promise}
 *
 * TODO: add unit tests
 */
export function getDbAccessCredential() :Promise<*> {

  return getApiAxiosInstance(PRINCIPALS_API)
    .get(`/${DB_PATH}`)
    .then((axiosResponse) => axiosResponse.data)
    .catch((error :Error) => {
      LOG.error(error);
      return Promise.reject(error);
    });
}

/*
 *
 * EVERYTHING BELOW IS DEPRECATED!!! ONLY DELETE AFTER REMOVING REFERENCES FROM GALLERY!!!
 *
 */

export function removeRoleFromUser(userId :string, role :string) :Promise<*> {

  let errorMsg = '';

  if (!isNonEmptyString(userId)) {
    errorMsg = 'invalid parameter: userId must be a non-empty string';
    LOG.error(errorMsg, userId);
    return Promise.reject(errorMsg);
  }

  if (!isNonEmptyString(role)) {
    errorMsg = 'invalid parameter: role must be a non-empty string';
    LOG.error(errorMsg, role);
    return Promise.reject(errorMsg);
  }

  return getApiAxiosInstance(PRINCIPALS_API)
    .delete(`/${USERS_PATH}/${userId}/${ROLES_PATH}/${role}`)
    .then((axiosResponse) => axiosResponse.data)
    .catch((error :Error) => {
      LOG.error(error);
      return Promise.reject(error);
    });
}
<|endoftext|>


<commit_before>import customChartModel from '../processors/customChartModel'
import customTableModel from '../processors/customTableModel'
import nullModel from '../processors/nullModel'
import { number } from '../processors/formats'
import Chart from '../components/Charts/Chart.jsx'
import TextLabel from '../components/Charts/TextLabel.jsx'
import SimpleTable from '../components/Charts/SimpleTable.jsx'

import CustomSettingsModal from '../components/SettingsModals/CustomSettingsModal.jsx'
import TextLabelModal from '../components/SettingsModals/TextLabelModal.jsx'

export default function _charts(config) {
  if (!config.enableCustomWidgetFeature) return []

  return [
    {
      chartId: 'custom-chart',
      group: 'Custom',
      title: 'Custom chart',
      tooltipText: 'Allows you to configure a basic chart displaying any available metric',
      processor: customChartModel,
      visualisation: Chart,
      // metrics spec and transforms are handled in the customModel
      metricNames: [],
      lineType: 'line',
      converted: false,
      conversionFunction: '',
      forceYAxis: false,
      percentage: false,
      cumulative: false,
      settingsComponent: CustomSettingsModal,
      yTickFormat: number,
    },
    {
      chartId: 'custom-table',
      group: 'Custom',
      title: 'Custom table',
      tooltipText: 'Allows you to present a table displaying latest data of any available metric',
      processor: customTableModel,
      visualisation: SimpleTable,
      // metrics spec and transforms are handled in the customModel
      metricNames: [],
      converted: false,
      conversionFunction: '',
      percentage: false,
      cumulative: false,
      settingsComponent: CustomSettingsModal,
      yTickFormat: number,
    },
    {
      chartId: 'text-label',
      group: 'Custom',
      title: 'Text label',
      tooltipText: 'Displays a text panel with a message of your choice (edit settings)',
      processor: nullModel,
      visualisation: TextLabel,
      settingsComponent: TextLabelModal,
      content: '<click settings to edit>',
      size: 'medium',
    },
  ]
}
<commit_msg>remove text label from custom menu (until serializing charts works)<commit_after>import customChartModel from '../processors/customChartModel'
import customTableModel from '../processors/customTableModel'
import { number } from '../processors/formats'
import Chart from '../components/Charts/Chart.jsx'
import SimpleTable from '../components/Charts/SimpleTable.jsx'

import CustomSettingsModal from '../components/SettingsModals/CustomSettingsModal.jsx'

export default function _charts(config) {
  if (!config.enableCustomWidgetFeature) return []

  return [
    {
      chartId: 'custom-chart',
      group: 'Custom',
      title: 'Custom chart',
      tooltipText: 'Allows you to configure a basic chart displaying any available metric',
      processor: customChartModel,
      visualisation: Chart,
      // metrics spec and transforms are handled in the customModel
      metricNames: [],
      lineType: 'line',
      converted: false,
      conversionFunction: '',
      forceYAxis: false,
      percentage: false,
      cumulative: false,
      settingsComponent: CustomSettingsModal,
      yTickFormat: number,
    },
    {
      chartId: 'custom-table',
      group: 'Custom',
      title: 'Custom table',
      tooltipText: 'Allows you to present a table displaying latest data of any available metric',
      processor: customTableModel,
      visualisation: SimpleTable,
      // metrics spec and transforms are handled in the customModel
      metricNames: [],
      converted: false,
      conversionFunction: '',
      percentage: false,
      cumulative: false,
      settingsComponent: CustomSettingsModal,
      yTickFormat: number,
    },
    /*
     * TODO add this when url can handle full serialisation
     * otherwise this is only really useful for bundles, as it cannot be 'persisted'
    {
      chartId: 'text-label',
      group: 'Custom',
      title: 'Text label',
      tooltipText: 'Displays a text panel with a message of your choice (edit settings)',
      processor: nullModel,
      visualisation: TextLabel,
      settingsComponent: TextLabelModal,
      content: '<click settings to edit>',
      size: 'medium',
    },
    */
  ]
}
<|endoftext|>


<commit_before>import { v4 } from "uuid"

import Redis from "./redis"

import Universe from "./universe"
import Player from "./player"
import Game from "./game"

import { logger } from "~/app/index"

export default class Client {
  constructor(socket, user) {
    this.socket = socket
    this.user = user

    this.uuid = v4()

    this.player = new Player(this)
    this.redis = new Redis(this)

    this.socket.on("close", this.close.bind(this))
    this.socket.on("message", this.message.bind(this))
  }

  async connected() {
    logger.info(`[Websocket OPEN] (${this.uuid}) ${this.userUUID}`)
    Universe.addClient(this)

    if (this.user) {
      this.send({
        action: "user",
        user: await this.user.serialize()
      })
    }
  }

  close() {
    logger.info(`[Websocket CLOSE] (${this.uuid}) ${this.userUUID}`)
    Universe.removeClient(this)
  }

  async message(message) {
    logger.info(`[Websocket RECV] (${this.uuid}) ${message}`)

    try {
      const { action, ...rest } = JSON.parse(message)
      await this.player[action](rest)
    } catch(err) {
      logger.exception(err)
    }
  }

  sendPosition(game, position) {
    this.send({ action: "position", game, position })
  }

  async sendGames(gameUUIDs) {
    const games = await Game.where("uuid", "in", gameUUIDs).fetchAll()
    const gamesData = {
      before: await games.at(0).serialize(),
      primary: await games.at(1).serialize(),
      after: await games.at(2).serialize()
    }

    this.send({ action: "games", ...gamesData })
  }

  async sendUniverse() {
    this.send({ action: "universe", ...await Universe.serialize() })
  }

  send(command) {
    const message = JSON.stringify(command)

    logger.info(`[Websocket SEND] (${this.uuid}) ${message}`)

    try {
      this.socket.send(message)
    } catch(err) {
      if (this.socket.readstate > 1) {
        return
      }

      logger.exception(err)
    }
  }

  get userUUID() {
    return this.user ? this.user.get("uuid") : "unknown"
  }
}
<commit_msg>only send primary game if only one game<commit_after>import { v4 } from "uuid"

import Redis from "./redis"

import Universe from "./universe"
import Player from "./player"
import Game from "./game"

import { logger } from "~/app/index"

export default class Client {
  constructor(socket, user) {
    this.socket = socket
    this.user = user

    this.uuid = v4()

    this.player = new Player(this)
    this.redis = new Redis(this)

    this.socket.on("close", this.close.bind(this))
    this.socket.on("message", this.message.bind(this))
  }

  async connected() {
    logger.info(`[Websocket OPEN] (${this.uuid}) ${this.userUUID}`)
    Universe.addClient(this)

    if (this.user) {
      this.send({
        action: "user",
        user: await this.user.serialize()
      })
    }
  }

  close() {
    logger.info(`[Websocket CLOSE] (${this.uuid}) ${this.userUUID}`)
    Universe.removeClient(this)
  }

  async message(message) {
    logger.info(`[Websocket RECV] (${this.uuid}) ${message}`)

    try {
      const { action, ...rest } = JSON.parse(message)
      await this.player[action](rest)
    } catch(err) {
      logger.exception(err)
    }
  }

  sendPosition(game, position) {
    this.send({ action: "position", game, position })
  }

  async sendGames(gameUUIDs) {
    let before, primary, after

    const games = await Game.where("uuid", "in", gameUUIDs).fetchAll()

    if (games.length === 1) {
      primary = games.at(0)
    } else {
      [before, primary, after] = [games.at(0), games.at(1), games.at(2)]
    }

    const gamesData = {
      before: before ? await before.serialize() : null,
      primary: primary ? await primary.serialize() : null,
      after: after ? await after.serialize() : null,
    }

    this.send({ action: "games", ...gamesData })
  }

  async sendUniverse() {
    this.send({ action: "universe", ...await Universe.serialize() })
  }

  send(command) {
    const message = JSON.stringify(command)

    logger.info(`[Websocket SEND] (${this.uuid}) ${message}`)

    try {
      this.socket.send(message)
    } catch(err) {
      if (this.socket.readstate > 1) {
        return
      }

      logger.exception(err)
    }
  }

  get userUUID() {
    return this.user ? this.user.get("uuid") : "unknown"
  }
}
<|endoftext|>


<commit_before>define([
    'dojo/text!./templates/Search.html',

    'dojo/_base/declare',
    'dojo/_base/array',
    'dojo/_base/lang',
    'dojo/dom-construct',
    'dojo/topic',
    'dojo/request',
    'dojo/aspect',
    'dojo/query',
    'dojo/dom-class',
    'dojo/dom-attr',

    'dijit/_WidgetBase',
    'dijit/_TemplatedMixin',
    'dijit/_WidgetsInTemplateMixin',
    'dijit/registry',

    'ijit/modules/_ErrorMessageMixin',

    'app/_CollapsibleMixin',
    'app/search/QueryLayer',
    'app/search/QueryLayerHeader',
    'app/search/Address',
    'app/search/City',
    'app/search/County',
    'app/search/SiteName',
    'app/search/ID',
    'app/search/Shape',
    'app/search/AdditionalSearch',
    'app/config',
    'app/map/MapController',
    'app/download/Download',

    'lodash'

], function(
    template,

    declare,
    array,
    lang,
    domConstruct,
    topic,
    request,
    aspect,
    query,
    domClass,
    domAttr,

    _WidgetBase,
    _TemplatedMixin,
    _WidgetsInTemplateMixin,
    registry,

    _ErrorMessageMixin,

    _CollapsibleMixin,
    QueryLayer,
    QueryLayerHeader,
    Address,
    City,
    County,
    SiteName,
    ID,
    Shape,
    AdditionalSearch,
    config,
    MapController,
    Download,

    _
) {
    return declare(
        [_WidgetBase, _TemplatedMixin, _WidgetsInTemplateMixin, _CollapsibleMixin, _ErrorMessageMixin], {
        // description:
        //      Encapsulates the search functionality for the app.

        templateString: template,
        baseClass: 'search panel-group',
        widgetsInTemplate: true,

        // selectedQueryLayers: QueryLayer[]
        //      A list of all selected query layers.
        selectedQueryLayers: null,

        // childWidgets: _WidgetBase[]
        //      widgets to call startup on
        childWidgets: null,

        // currentPane: ContentPane
        //      the currently selected search pane
        currentPane: null,

        // address: Address
        address: null,

        // city: City
        city: null,

        // county: County
        county: null,

        // site: SiteName
        site: null,

        // program: ProgramID
        program: null,

        // shape: Shape
        shape: null,

        // download: Download
        download: null,

        // searchServiceErrorMsg: String
        searchServiceErrorMsg: 'There was an error with the search service!',

        // noQueryLayersSelectedErrMsg: String
        noQueryLayersSelectedErrMsg: 'You must select at least one query layer!',

        // noSearchTypeSelectedErrMsg: String
        noSearchTypeSelectedErrMsg: 'You must select a search criteria type!',

        // additionalSearches: AdditionalSearch[]
        additionalSearches: null,

        // Properties to be sent into constructor

        constructor: function () {
            // summary:
            //      description
            console.log('app/search/Search:constructor', arguments);

            this.selectedQueryLayers = [];

            this.own(
                topic.subscribe(config.topics.appQueryLayer.addLayer,
                    lang.hitch(this, 'onAddQueryLayer')),
                topic.subscribe(config.topics.appQueryLayer.removeLayer,
                    lang.hitch(this, 'onRemoveQueryLayer'))
            );

            this.additionalSearches = [];
        },
        postCreate: function() {
            // summary:
            //    Overrides method of same name in dijit._Widget.
            // tags:
            //    private
            console.log('app/search/Search::postCreate', arguments);

            var that = this;
            config.getAppJson().then(function (json) {
                that.buildQueryLayers(json.queryLayers);
            });

            var showBtn = function (btn) {
                query('.grid-btn', this.domNode).addClass('hidden');
                domClass.remove(btn, 'hidden');
            };
            this.own(
                this.address = new Address({
                    apiKey: config.apiKey
                }, this.addressPane),
                this.city = new City({
                    map: MapController.map,
                    promptMessage: 'please begin typing a city name',
                    mapServiceURL: config.urls.terrain,
                    searchLayerIndex: config.layerIndices.city,
                    searchField: config.fieldNames.cities.NAME,
                    placeHolder: 'city name...',
                    maxResultsToDisplay: 5,
                    symbolFill: config.symbols.zoom.polygon,
                    graphicsLayer: MapController.map.graphics,
                    preserveGraphics: true
                }, this.cityPane),
                this.county = new County({}, this.countyPane),
                this.site = new SiteName(null, this.sitePane),
                this.id = new ID(null, this.idPane),
                this.shape = new Shape({}, this.shapePane),
                topic.subscribe(config.topics.app.showGrid, _.partial(showBtn, this.hideGridBtn)),
                topic.subscribe(config.topics.app.hideGrid, _.partial(showBtn, this.showGridBtn)),
                this.download = new Download({}, this.downloadDiv)
            );

            this.childWidgets = [this.address, this.city, this.county, this.shape, this.download];

            this.inherited(arguments);
        },
        startup: function () {
            // summary:
            //      startup
            console.log('app/search/Search::startup', arguments);

            array.forEach(this.childWidgets, function (w) {
                w.startup();
            });

            this.inherited(arguments);
        },
        onAddQueryLayer: function (qLayer) {
            // summary:
            //      description
            // qLayer: QueryLayer
            console.log('app/search/Search:onAddQueryLayer', arguments);

            this.selectedQueryLayers.push(qLayer);

            this.checkForAdditionalSearches();
            this.checkSiteIDSearches();
        },
        checkSiteIDSearches: function () {
            // summary:
            //      description
            console.log('app/search/Search:checkSiteIDSearches', arguments);

            if (this.selectedQueryLayers.length > 0) {
                var that = this;
                var checkSome = function (fieldName) {
                    return array.some(that.selectedQueryLayers, function (ql) {
                        return ql[fieldName] === 'n/a';
                    });
                };

                var name = checkSome(config.fieldNames.queryLayers.NAME);
                var id = checkSome(config.fieldNames.queryLayers.ID);

                var updateOption = function (hide, option) {
                    if (hide) {
                        domClass.add(option, 'hidden');

                        // reset select if the current pane is being hidden
                        if (that.select.value === option.value) {
                            that.select.value = 'empty';
                            that.onSelectChange();
                        }
                    } else {
                        domClass.remove(option, 'hidden');
                    }
                };

                updateOption(id, this.idOption);
                updateOption(name, this.nameOption);
            }
        },
        checkForAdditionalSearches: function () {
            // summary:
            //      description
            console.log('app/search/Search:checkForAdditionalSearches', arguments);

            this.removeAdditionalSearches();

            if (this.selectedQueryLayers.length === 1) {
                var qLayer = this.selectedQueryLayers[0];
                if (qLayer.additionalSearchObjects && qLayer.additionalSearchObjects.length) {
                    array.forEach(qLayer.additionalSearchObjects, lang.hitch(this, 'addAdditionalSearch'));
                }
            }
        },
        addAdditionalSearch: function (obj) {
            // summary:
            //      description
            // obj: Object
            console.log('app/search/Search:addAdditionalSearch', arguments);

            var as = new AdditionalSearch(obj);
            this[obj.fieldName] = as;
            this.stackContainer.addChild(as);
            this.additionalSearches.push(as);

            domConstruct.create('option', {
                value: obj.fieldName,
                innerHTML: obj.fieldAlias
            }, this.select);
        },
        removeAdditionalSearches: function () {
            // summary:
            //      description
            console.log('app/search/Search:removeAdditionalSearches', arguments);

            var that = this;

            array.forEach(this.additionalSearches, function (as) {
                if (that.currentPane === as) {
                    that.select.value = 'empty';
                    that.currentPane = that.empty;
                    that.onSelectChange();
                }
                that.stackContainer.removeChild(as);
                array.every(that.select.children, function (node) {
                    if (node.value === as.fieldName) {
                        domConstruct.destroy(node);
                        return false;
                    } else {
                        return true;
                    }
                });
                as.destroy();
            });
            this.additionalSearches = [];
        },
        onRemoveQueryLayer: function (qLayer) {
            // summary:
            //      description
            // qLayer: QueryLayer
            console.log('app/search/Search:onRemoveQueryLayer', arguments);

            this.selectedQueryLayers.splice(array.indexOf(this.selectedQueryLayers, qLayer), 1);

            this.checkForAdditionalSearches();
            this.checkSiteIDSearches();
        },
        buildQueryLayers: function (queryLayers) {
            // summary:
            //      builds the query layer widgets and their associated panels
            // queryLayers: {}
            //      The array returned from queryLayers.json
            console.log('app/search/Search:buildQueryLayers', arguments);

            var headers = {};
            array.forEach(queryLayers, function (ql) {
                // skip query layers that are not published to the service
                if (!ql.index) { return ; }

                if (!headers[ql.heading]) {
                    headers[ql.heading] = new QueryLayerHeader({
                        name: ql.heading,
                        index: ql.index
                    }, domConstruct.create('div', {}, this.queryLayersContainer));
                }
                this.own(new QueryLayer(ql, domConstruct.create('div', {}, headers[ql.heading].panelBody)));
            }, this);
        },
        onSelectChange: function () {
            // summary:
            //      fires when the user changes the value of the select
            console.log('app.search.Search::onSelectChange', arguments);

            // clear previous pane
            if (this.currentPane && this.currentPane.clear) {
                this.currentPane.clear();
            }

            // switch to new pane
            this.currentPane = this[this.select.value];
            this.stackContainer.selectChild(this.currentPane);
            this.zoomedGeometry = null;
            this.hideErrMsg();
        },
        search: function () {
            // summary:
            //      description
            console.log('app/search/Search::search', arguments);

            var params = {};
            var that = this;
            var makeRequest = function () {
                lang.mixin(params, that.getQueryLayersParam());
                if (config.user) {
                    params.token = config.user.token;
                    params.userId = config.user.userId;
                }
                request(config.urls.search, {
                    method: 'POST',
                    data: JSON.stringify(params),
                    headers: {'Content-Type': 'application/json'},
                    handleAs: 'json',
                    timeout: 40000
                }).then(
                    function (response) {
                        if (response.status !== 200 ||
                            (response.queryLayers && response.queryLayers.status !== 200) ||
                            (response.secureQueryLayers && response.secureQueryLayers.status !== 200)) {
                            onError(that.searchServiceErrorMsg);
                        } else {
                            var results = {};
                            if (response.secureQueryLayers) {
                                // add s's to secure layer indices
                                var result = response.secureQueryLayers.result;
                                for (var p in result) {
                                    if (result.hasOwnProperty(p)) {
                                        result['s' + p] = result[p];
                                        delete result[p];
                                    }
                                }
                                lang.mixin(results, response.secureQueryLayers.result);
                            }

                            if (response.queryLayers) {
                                lang.mixin(results, response.queryLayers.result);
                            }
                            topic.publish(config.topics.appSearch.featuresFound, results);
                            topic.publish(config.topics.app.showGrid);
                            that.hideLoader();
                        }
                    },
                    function () {
                        onError(that.searchServiceErrorMsg);
                    }
                );
                topic.publish(config.topics.appSearch.searchStarted);
            };
            var onError = function (errTxt) {
                that.showErrMsg(errTxt);
                that.hideLoader();
                topic.publish(config.topics.appSearch.searchError);
            };

            this.hideErrMsg();

            this.showLoader();

            // check for no search type selected
            if (!this.currentPane) {
                onError(this.noSearchTypeSelectedErrMsg);
                return;
            }

            if (this.currentPane.getGeometry) {
                // search type requires an async process to get geometry
                this.currentPane.getGeometry().then(
                    function (geo) {
                        try {
                            params.geometry = geo;
                            makeRequest();
                        } catch (er) {
                            onError(er);
                        }
                    },
                    onError
                );
            } else {
                // search type just has a value to pass to the search service (ID & Name searches)
                try {
                    params[this.currentPane.paramName] = this.currentPane.getSearchParam();
                    makeRequest();

                } catch (er) {
                    onError(er);
                }
            }
        },
        getQueryLayersParam: function () {
            // summary:
            //      loops through the selected query layers and returns objects
            //      suitable for passing into the search api
            console.log('app/search/Search::getQueryLayersParam', arguments);

            if (this.selectedQueryLayers.length === 0) {
                throw this.noQueryLayersSelectedErrMsg;
            }

            var obj = {
                queryLayers: [],
                secureQueryLayers: []
            };

            array.forEach(this.selectedQueryLayers, function (ql) {
                var a = (ql.secure === 'No') ? obj.queryLayers : obj.secureQueryLayers;
                a.push(ql.toJson());
            });

            // convert empty arrays to nulls
            if (obj.queryLayers.length === 0) {
                obj.queryLayers = null;
            }
            if (obj.secureQueryLayers.length === 0) {
                obj.secureQueryLayers = null;
            }

            return obj;
        },
        onSearchComplete: function (response) {
            // summary:
            //      callback for search service request
            // response: Object
            //      response object from server. Has status and result props
            console.log('app/search/Search:onSearchComplete', arguments);

            if (response.status !== 200) {
                throw this.searchServiceErrorMsg;
            }

            topic.publish(config.topics.appSearch.featuresFound, response.result);
        },
        clear: function () {
            // summary:
            //      clears all of the search parameters and query layers
            console.log('app/search/Search:clear', arguments);

            this.hideGrid();
            query('.grid-btn', this.domNode).addClass('hidden');

            if (this.currentPane) {
                this.currentPane.clear();
            }

            topic.publish(config.topics.appSearch.clear);

            this.hideErrMsg();
            this.select.value = 'empty';
            this.onSelectChange();
        },
        hideGrid: function () {
            // summary:
            //      description
            console.log('app/search/Search:hideGrid', arguments);

            topic.publish(config.topics.app.hideGrid);
        },
        showGrid: function () {
            // summary:
            //      description
            console.log('app/search/Search:showGrid', arguments);

            topic.publish(config.topics.app.showGrid);
        },
        showLoader: function () {
            // summary:
            //      description
            console.log('app/download/Download:showLoader', arguments);

            domAttr.set(this.searchBtn, 'disabled', true);
            this.searchBtn.innerHTML = 'Searching';
        },
        hideLoader: function () {
            // summary:
            //      description
            console.log('app/download/Download:hideLoader', arguments);

            domAttr.set(this.searchBtn, 'disabled', false);
            this.searchBtn.innerHTML = 'Search';
        }
    });
});
<commit_msg>Added "Accepts" header to search request.<commit_after>define([
    'dojo/text!./templates/Search.html',

    'dojo/_base/declare',
    'dojo/_base/array',
    'dojo/_base/lang',
    'dojo/dom-construct',
    'dojo/topic',
    'dojo/request',
    'dojo/aspect',
    'dojo/query',
    'dojo/dom-class',
    'dojo/dom-attr',

    'dijit/_WidgetBase',
    'dijit/_TemplatedMixin',
    'dijit/_WidgetsInTemplateMixin',
    'dijit/registry',

    'ijit/modules/_ErrorMessageMixin',

    'app/_CollapsibleMixin',
    'app/search/QueryLayer',
    'app/search/QueryLayerHeader',
    'app/search/Address',
    'app/search/City',
    'app/search/County',
    'app/search/SiteName',
    'app/search/ID',
    'app/search/Shape',
    'app/search/AdditionalSearch',
    'app/config',
    'app/map/MapController',
    'app/download/Download',

    'lodash'

], function(
    template,

    declare,
    array,
    lang,
    domConstruct,
    topic,
    request,
    aspect,
    query,
    domClass,
    domAttr,

    _WidgetBase,
    _TemplatedMixin,
    _WidgetsInTemplateMixin,
    registry,

    _ErrorMessageMixin,

    _CollapsibleMixin,
    QueryLayer,
    QueryLayerHeader,
    Address,
    City,
    County,
    SiteName,
    ID,
    Shape,
    AdditionalSearch,
    config,
    MapController,
    Download,

    _
) {
    return declare(
        [_WidgetBase, _TemplatedMixin, _WidgetsInTemplateMixin, _CollapsibleMixin, _ErrorMessageMixin], {
        // description:
        //      Encapsulates the search functionality for the app.

        templateString: template,
        baseClass: 'search panel-group',
        widgetsInTemplate: true,

        // selectedQueryLayers: QueryLayer[]
        //      A list of all selected query layers.
        selectedQueryLayers: null,

        // childWidgets: _WidgetBase[]
        //      widgets to call startup on
        childWidgets: null,

        // currentPane: ContentPane
        //      the currently selected search pane
        currentPane: null,

        // address: Address
        address: null,

        // city: City
        city: null,

        // county: County
        county: null,

        // site: SiteName
        site: null,

        // program: ProgramID
        program: null,

        // shape: Shape
        shape: null,

        // download: Download
        download: null,

        // searchServiceErrorMsg: String
        searchServiceErrorMsg: 'There was an error with the search service!',

        // noQueryLayersSelectedErrMsg: String
        noQueryLayersSelectedErrMsg: 'You must select at least one query layer!',

        // noSearchTypeSelectedErrMsg: String
        noSearchTypeSelectedErrMsg: 'You must select a search criteria type!',

        // additionalSearches: AdditionalSearch[]
        additionalSearches: null,

        // Properties to be sent into constructor

        constructor: function () {
            // summary:
            //      description
            console.log('app/search/Search:constructor', arguments);

            this.selectedQueryLayers = [];

            this.own(
                topic.subscribe(config.topics.appQueryLayer.addLayer,
                    lang.hitch(this, 'onAddQueryLayer')),
                topic.subscribe(config.topics.appQueryLayer.removeLayer,
                    lang.hitch(this, 'onRemoveQueryLayer'))
            );

            this.additionalSearches = [];
        },
        postCreate: function() {
            // summary:
            //    Overrides method of same name in dijit._Widget.
            // tags:
            //    private
            console.log('app/search/Search::postCreate', arguments);

            var that = this;
            config.getAppJson().then(function (json) {
                that.buildQueryLayers(json.queryLayers);
            });

            var showBtn = function (btn) {
                query('.grid-btn', this.domNode).addClass('hidden');
                domClass.remove(btn, 'hidden');
            };
            this.own(
                this.address = new Address({
                    apiKey: config.apiKey
                }, this.addressPane),
                this.city = new City({
                    map: MapController.map,
                    promptMessage: 'please begin typing a city name',
                    mapServiceURL: config.urls.terrain,
                    searchLayerIndex: config.layerIndices.city,
                    searchField: config.fieldNames.cities.NAME,
                    placeHolder: 'city name...',
                    maxResultsToDisplay: 5,
                    symbolFill: config.symbols.zoom.polygon,
                    graphicsLayer: MapController.map.graphics,
                    preserveGraphics: true
                }, this.cityPane),
                this.county = new County({}, this.countyPane),
                this.site = new SiteName(null, this.sitePane),
                this.id = new ID(null, this.idPane),
                this.shape = new Shape({}, this.shapePane),
                topic.subscribe(config.topics.app.showGrid, _.partial(showBtn, this.hideGridBtn)),
                topic.subscribe(config.topics.app.hideGrid, _.partial(showBtn, this.showGridBtn)),
                this.download = new Download({}, this.downloadDiv)
            );

            this.childWidgets = [this.address, this.city, this.county, this.shape, this.download];

            this.inherited(arguments);
        },
        startup: function () {
            // summary:
            //      startup
            console.log('app/search/Search::startup', arguments);

            array.forEach(this.childWidgets, function (w) {
                w.startup();
            });

            this.inherited(arguments);
        },
        onAddQueryLayer: function (qLayer) {
            // summary:
            //      description
            // qLayer: QueryLayer
            console.log('app/search/Search:onAddQueryLayer', arguments);

            this.selectedQueryLayers.push(qLayer);

            this.checkForAdditionalSearches();
            this.checkSiteIDSearches();
        },
        checkSiteIDSearches: function () {
            // summary:
            //      description
            console.log('app/search/Search:checkSiteIDSearches', arguments);

            if (this.selectedQueryLayers.length > 0) {
                var that = this;
                var checkSome = function (fieldName) {
                    return array.some(that.selectedQueryLayers, function (ql) {
                        return ql[fieldName] === 'n/a';
                    });
                };

                var name = checkSome(config.fieldNames.queryLayers.NAME);
                var id = checkSome(config.fieldNames.queryLayers.ID);

                var updateOption = function (hide, option) {
                    if (hide) {
                        domClass.add(option, 'hidden');

                        // reset select if the current pane is being hidden
                        if (that.select.value === option.value) {
                            that.select.value = 'empty';
                            that.onSelectChange();
                        }
                    } else {
                        domClass.remove(option, 'hidden');
                    }
                };

                updateOption(id, this.idOption);
                updateOption(name, this.nameOption);
            }
        },
        checkForAdditionalSearches: function () {
            // summary:
            //      description
            console.log('app/search/Search:checkForAdditionalSearches', arguments);

            this.removeAdditionalSearches();

            if (this.selectedQueryLayers.length === 1) {
                var qLayer = this.selectedQueryLayers[0];
                if (qLayer.additionalSearchObjects && qLayer.additionalSearchObjects.length) {
                    array.forEach(qLayer.additionalSearchObjects, lang.hitch(this, 'addAdditionalSearch'));
                }
            }
        },
        addAdditionalSearch: function (obj) {
            // summary:
            //      description
            // obj: Object
            console.log('app/search/Search:addAdditionalSearch', arguments);

            var as = new AdditionalSearch(obj);
            this[obj.fieldName] = as;
            this.stackContainer.addChild(as);
            this.additionalSearches.push(as);

            domConstruct.create('option', {
                value: obj.fieldName,
                innerHTML: obj.fieldAlias
            }, this.select);
        },
        removeAdditionalSearches: function () {
            // summary:
            //      description
            console.log('app/search/Search:removeAdditionalSearches', arguments);

            var that = this;

            array.forEach(this.additionalSearches, function (as) {
                if (that.currentPane === as) {
                    that.select.value = 'empty';
                    that.currentPane = that.empty;
                    that.onSelectChange();
                }
                that.stackContainer.removeChild(as);
                array.every(that.select.children, function (node) {
                    if (node.value === as.fieldName) {
                        domConstruct.destroy(node);
                        return false;
                    } else {
                        return true;
                    }
                });
                as.destroy();
            });
            this.additionalSearches = [];
        },
        onRemoveQueryLayer: function (qLayer) {
            // summary:
            //      description
            // qLayer: QueryLayer
            console.log('app/search/Search:onRemoveQueryLayer', arguments);

            this.selectedQueryLayers.splice(array.indexOf(this.selectedQueryLayers, qLayer), 1);

            this.checkForAdditionalSearches();
            this.checkSiteIDSearches();
        },
        buildQueryLayers: function (queryLayers) {
            // summary:
            //      builds the query layer widgets and their associated panels
            // queryLayers: {}
            //      The array returned from queryLayers.json
            console.log('app/search/Search:buildQueryLayers', arguments);

            var headers = {};
            array.forEach(queryLayers, function (ql) {
                // skip query layers that are not published to the service
                if (!ql.index) { return ; }

                if (!headers[ql.heading]) {
                    headers[ql.heading] = new QueryLayerHeader({
                        name: ql.heading,
                        index: ql.index
                    }, domConstruct.create('div', {}, this.queryLayersContainer));
                }
                this.own(new QueryLayer(ql, domConstruct.create('div', {}, headers[ql.heading].panelBody)));
            }, this);
        },
        onSelectChange: function () {
            // summary:
            //      fires when the user changes the value of the select
            console.log('app.search.Search::onSelectChange', arguments);

            // clear previous pane
            if (this.currentPane && this.currentPane.clear) {
                this.currentPane.clear();
            }

            // switch to new pane
            this.currentPane = this[this.select.value];
            this.stackContainer.selectChild(this.currentPane);
            this.zoomedGeometry = null;
            this.hideErrMsg();
        },
        search: function () {
            // summary:
            //      description
            console.log('app/search/Search::search', arguments);

            var params = {};
            var that = this;
            var makeRequest = function () {
                lang.mixin(params, that.getQueryLayersParam());
                if (config.user) {
                    params.token = config.user.token;
                    params.userId = config.user.userId;
                }
                request(config.urls.search, {
                    method: 'POST',
                    data: JSON.stringify(params),
                    headers: {
                        'Content-Type': 'application/json',
                        'Accept': 'application/json'
                    },
                    handleAs: 'json',
                    timeout: 40000
                }).then(
                    function (response) {
                        if (response.status !== 200 ||
                            (response.queryLayers && response.queryLayers.status !== 200) ||
                            (response.secureQueryLayers && response.secureQueryLayers.status !== 200)) {
                            onError(that.searchServiceErrorMsg);
                        } else {
                            var results = {};
                            if (response.secureQueryLayers) {
                                // add s's to secure layer indices
                                var result = response.secureQueryLayers.result;
                                for (var p in result) {
                                    if (result.hasOwnProperty(p)) {
                                        result['s' + p] = result[p];
                                        delete result[p];
                                    }
                                }
                                lang.mixin(results, response.secureQueryLayers.result);
                            }

                            if (response.queryLayers) {
                                lang.mixin(results, response.queryLayers.result);
                            }
                            topic.publish(config.topics.appSearch.featuresFound, results);
                            topic.publish(config.topics.app.showGrid);
                            that.hideLoader();
                        }
                    },
                    function () {
                        onError(that.searchServiceErrorMsg);
                    }
                );
                topic.publish(config.topics.appSearch.searchStarted);
            };
            var onError = function (errTxt) {
                that.showErrMsg(errTxt);
                that.hideLoader();
                topic.publish(config.topics.appSearch.searchError);
            };

            this.hideErrMsg();

            this.showLoader();

            // check for no search type selected
            if (!this.currentPane) {
                onError(this.noSearchTypeSelectedErrMsg);
                return;
            }

            if (this.currentPane.getGeometry) {
                // search type requires an async process to get geometry
                this.currentPane.getGeometry().then(
                    function (geo) {
                        try {
                            params.geometry = geo;
                            makeRequest();
                        } catch (er) {
                            onError(er);
                        }
                    },
                    onError
                );
            } else {
                // search type just has a value to pass to the search service (ID & Name searches)
                try {
                    params[this.currentPane.paramName] = this.currentPane.getSearchParam();
                    makeRequest();

                } catch (er) {
                    onError(er);
                }
            }
        },
        getQueryLayersParam: function () {
            // summary:
            //      loops through the selected query layers and returns objects
            //      suitable for passing into the search api
            console.log('app/search/Search::getQueryLayersParam', arguments);

            if (this.selectedQueryLayers.length === 0) {
                throw this.noQueryLayersSelectedErrMsg;
            }

            var obj = {
                queryLayers: [],
                secureQueryLayers: []
            };

            array.forEach(this.selectedQueryLayers, function (ql) {
                var a = (ql.secure === 'No') ? obj.queryLayers : obj.secureQueryLayers;
                a.push(ql.toJson());
            });

            // convert empty arrays to nulls
            if (obj.queryLayers.length === 0) {
                obj.queryLayers = null;
            }
            if (obj.secureQueryLayers.length === 0) {
                obj.secureQueryLayers = null;
            }

            return obj;
        },
        onSearchComplete: function (response) {
            // summary:
            //      callback for search service request
            // response: Object
            //      response object from server. Has status and result props
            console.log('app/search/Search:onSearchComplete', arguments);

            if (response.status !== 200) {
                throw this.searchServiceErrorMsg;
            }

            topic.publish(config.topics.appSearch.featuresFound, response.result);
        },
        clear: function () {
            // summary:
            //      clears all of the search parameters and query layers
            console.log('app/search/Search:clear', arguments);

            this.hideGrid();
            query('.grid-btn', this.domNode).addClass('hidden');

            if (this.currentPane) {
                this.currentPane.clear();
            }

            topic.publish(config.topics.appSearch.clear);

            this.hideErrMsg();
            this.select.value = 'empty';
            this.onSelectChange();
        },
        hideGrid: function () {
            // summary:
            //      description
            console.log('app/search/Search:hideGrid', arguments);

            topic.publish(config.topics.app.hideGrid);
        },
        showGrid: function () {
            // summary:
            //      description
            console.log('app/search/Search:showGrid', arguments);

            topic.publish(config.topics.app.showGrid);
        },
        showLoader: function () {
            // summary:
            //      description
            console.log('app/download/Download:showLoader', arguments);

            domAttr.set(this.searchBtn, 'disabled', true);
            this.searchBtn.innerHTML = 'Searching';
        },
        hideLoader: function () {
            // summary:
            //      description
            console.log('app/download/Download:hideLoader', arguments);

            domAttr.set(this.searchBtn, 'disabled', false);
            this.searchBtn.innerHTML = 'Search';
        }
    });
});
<|endoftext|>


<commit_before>angular.module('prx.series', ['ui.router', 'angular-hal', 'prx.stories'])
.config(function ($stateProvider, ngHalProvider) {
  $stateProvider.state('series', {
    url: '/series/:seriesId',
    controller: 'SeriesCtrl',
    templateUrl: 'series/series.html',
    resolve: {
      series: ['ngHal', '$stateParams', function (ngHal, $stateParams) {
        return ngHal.followOne('series', {id: $stateParams.seriesId});
      }],
      stories: ['series', function(series) {
        return series.follow('stories').follow('items');
      }]
    }
  });

ngHalProvider.setRootUrl(FEAT.apiServer)
  .mixin('http://meta.prx.org/model/series', ['resolved', function (resolved) {
    resolved.imageUrl = resolved.follow('image').call('link', 'enclosure').call('url');
  }]);
})
.controller('SeriesCtrl', function ($scope, series, stories) {
  $scope.series = series;
  $scope.stories = stories;
});
<commit_msg>Updating series controller<commit_after>angular.module('prx.series', ['ui.router', 'angular-hal', 'prx.stories'])
.config(function ($stateProvider, ngHalProvider) {
  $stateProvider.state('series', {
    url: '/series/:seriesId',
    controller: 'SeriesCtrl as series',
    templateUrl: 'series/series.html',
    resolve: {
      series: ['ngHal', '$stateParams', function (ngHal, $stateParams) {
        return ngHal.followOne('prx:series', {id: $stateParams.seriesId});
      }],
      stories: ['series', function(series) {
        return series.follow('prx:stories').follow('prx:items');
      }]
    }
  });

ngHalProvider.setRootUrl(FEAT.apiServer)
  .mixin('http://meta.prx.org/model/series', ['resolved', function (resolved) {
    resolved.imageUrl = resolved.follow('prx:image').call('link', 'enclosure').call('url');
  }]);
})
.controller('SeriesCtrl', function (series, stories) {
  this.current = series;
  this.stories = stories;
});
<|endoftext|>


<commit_before>(function (window) {
    "use strict";

    var AppEnlight = {
        version: '0.4.1',
        options: {
            apiKey: ""
        },
        errorReportBuffer: [],
        slowReportBuffer: [],
        logBuffer: [],
        requestInfo: null,

        init: function (options) {
            var self = this;
            if (typeof options.server === 'undefined') {
                options.server = "https://api.appenlight.com";
            }
            if (typeof options.apiKey === 'undefined') {
                options.apiKey = "undefined";
            }
            if (typeof options.protocol_version === 'undefined') {
                options.protocol_version = "0.5";
            }
            if (typeof options.windowOnError === 'undefined' ||
                options.windowOnError == false) {
                TraceKit.collectWindowErrors = false;
            }
            if (typeof options.sendInterval === 'undefined') {
                options.sendInterval = 1000;
            }
            if (typeof options.tracekitRemoteFetching === 'undefined') {
                options.tracekitRemoteFetching = true;
            }
            if (typeof options.tracekitContextLines === 'undefined') {
                options.tracekitContextLines = 11;
            }
            if (options.sendInterval >= 1000) {
                this.createSendInterval(options.sendInterval);
            }
            this.options = options;
            this.requestInfo = { url: window.location.href };
            this.reportsEndpoint = options.server
                + '/api/reports?public_api_key=' + this.options.apiKey
                + "&protocol_version=" + this.options.protocol_version;
            this.logsEndpoint = options.server
                + '/api/logs?public_api_key=' + this.options.apiKey
                + "&protocol_version=" + this.options.protocol_version;

            TraceKit.remoteFetching = options.tracekitRemoteFetching;
            TraceKit.linesOfContext = options.tracekitContextLines;
            TraceKit.report.subscribe(function (errorReport) {
                self.handleError(errorReport);
            });
        },

        createSendInterval: function (time_iv) {
            var self = this;
            this.send_iv = setInterval(function () {
                self.sendReports();
                self.sendLogs();
            }, time_iv);
        },

        setRequestInfo: function (info) {
            for (var i in info) {
                this.requestInfo[i] = info[i];
            }
        },

        grabError: function (exception) {
            // we need to catch rethrown exception but throw an error from TraceKit
            try {
                TraceKit.report(exception);
            } catch (new_exception) {
                if (exception !== new_exception) {
                    throw new_exception;
                }
            }

        },

        handleError: function (errorReport) {
            var error_msg = '';
            if (errorReport.mode == 'stack') {
                error_msg = errorReport.name + ': ' + errorReport.message;
            }
            else {
                error_msg = errorReport.message;
            }
            var report = {
                "client": "javascript",
                "language": "javascript",
                "error": error_msg,
                "occurences": 1,
                "priority": 5,
                "server": '',
                "http_status": 500,
                "request": {},
                "traceback": [],
            };
            report.user_agent = window.navigator.userAgent;
            report.start_time = new Date().toJSON();

            if (this.requestInfo != null) {
                for (var i in this.requestInfo) {
                    report[i] = this.requestInfo[i];
                }
            }

            if (typeof report.request_id == 'undefined' || !report.request_id) {
                report.request_id = this.genUUID4();
            }
            // grab last 100 frames in reversed order
            var stack_slice = errorReport.stack.reverse().slice(-100);
            for (var i = 0; i < stack_slice.length; i++) {
                var context = '';
                try{
                    if (stack_slice[i].context){
                        for(var j = 0; j < stack_slice[i].context.length; j++){
                            var line = stack_slice[i].context[j];
                            if (line.length > 300){
                                context += '<minified-context>';
                            }
                            else{
                                context += line;
                            }
                            context += '\n';
                        }
                    }
                }
                catch(e){}
                var stackline = {'cline': context,
                    'file': stack_slice[i].url,
                    'fn': stack_slice[i].func,
                    'line': stack_slice[i].line,
                    'vars': []};
                report.traceback.push(stackline);
            }
            if(report.traceback.length > 0){
                report.traceback[report.traceback.length - 1].cline = context + '\n' + error_msg;
            }
            this.errorReportBuffer.push(report);
        },
        log: function (level, message, namespace, uuid) {
            if (typeof namespace == 'undefined') {
                namespace = window.location.pathname;
            }
            if (typeof uuid == 'undefined') {
                uuid = null;
            }
            this.logBuffer.push(
                {
                    "log_level": level.toUpperCase(),
                    "message": message,
                    "date": new Date().toJSON(),
                    "namespace": namespace
                });
            if (this.requestInfo != null && typeof this.requestInfo.server != 'undefined') {
                this.logBuffer[this.logBuffer.length - 1].server = this.requestInfo.server;
            }
        },

        genUUID4: function () {
            return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(
                /[xy]/g, function (c) {
                    var r = Math.random() * 16 | 0, v = c == 'x' ? r : r & 0x3 | 0x8;
                    return v.toString(16);
                }
            );
        },

        sendReports: function () {
            if (this.errorReportBuffer.length < 1) {
                return true;
            }
            var data = this.errorReportBuffer;
            this.submitData(this.reportsEndpoint, data);
            this.errorReportBuffer = [];
            return true;
        },
        sendLogs: function () {
            if (this.logBuffer.length < 1) {
                return true;
            }
            var data = this.logBuffer;
            this.submitData(this.logsEndpoint, data);
            this.logBuffer = [];
            return true;
        },

        submitData: function (endpoint, data) {
            var xhr = new window.XMLHttpRequest();
            if (!xhr && window.ActiveXObject) {
                xhr = new window.ActiveXObject("Microsoft.XMLHTTP");
            }
            xhr.open("POST", endpoint, true);
            xhr.setRequestHeader("Content-Type", "application/json");
            xhr.send(JSON.stringify(data));
        }
    };
    window.AppEnlight = AppEnlight;

    if ( typeof define === "function" && define.amd ) {
        define( "appenlight", [], function() {
            return AppEnlight;
        });
    }

}(window));
<commit_msg>linter: small cleanups<commit_after>(function (window) {
    "use strict";

    var AppEnlight = {
        version: '0.4.1',
        options: {
            apiKey: ""
        },
        errorReportBuffer: [],
        slowReportBuffer: [],
        logBuffer: [],
        requestInfo: null,

        init: function (options) {
            var self = this;
            if (typeof options.server === 'undefined') {
                options.server = "https://api.appenlight.com";
            }
            if (typeof options.apiKey === 'undefined') {
                options.apiKey = "undefined";
            }
            if (typeof options.protocol_version === 'undefined') {
                options.protocol_version = "0.5";
            }
            if (typeof options.windowOnError === 'undefined' ||
                options.windowOnError === false) {
                TraceKit.collectWindowErrors = false;
            }
            if (typeof options.sendInterval === 'undefined') {
                options.sendInterval = 1000;
            }
            if (typeof options.tracekitRemoteFetching === 'undefined') {
                options.tracekitRemoteFetching = true;
            }
            if (typeof options.tracekitContextLines === 'undefined') {
                options.tracekitContextLines = 11;
            }
            if (options.sendInterval >= 1000) {
                this.createSendInterval(options.sendInterval);
            }
            this.options = options;
            this.requestInfo = { url: window.location.href };
            this.reportsEndpoint = options.server +
                '/api/reports?public_api_key=' + this.options.apiKey +
                "&protocol_version=" + this.options.protocol_version;
            this.logsEndpoint = options.server +
                '/api/logs?public_api_key=' + this.options.apiKey +
                "&protocol_version=" + this.options.protocol_version;

            TraceKit.remoteFetching = options.tracekitRemoteFetching;
            TraceKit.linesOfContext = options.tracekitContextLines;
            TraceKit.report.subscribe(function (errorReport) {
                self.handleError(errorReport);
            });
        },

        createSendInterval: function (time_iv) {
            var self = this;
            this.send_iv = setInterval(function () {
                self.sendReports();
                self.sendLogs();
            }, time_iv);
        },

        setRequestInfo: function (info) {
            for (var i in info) {
                this.requestInfo[i] = info[i];
            }
        },

        grabError: function (exception) {
            // we need to catch rethrown exception but throw an error from TraceKit
            try {
                TraceKit.report(exception);
            } catch (new_exception) {
                if (exception !== new_exception) {
                    throw new_exception;
                }
            }

        },

        handleError: function (errorReport) {
            var error_msg = '';
            if (errorReport.mode == 'stack') {
                error_msg = errorReport.name + ': ' + errorReport.message;
            }
            else {
                error_msg = errorReport.message;
            }
            var report = {
                "client": "javascript",
                "language": "javascript",
                "error": error_msg,
                "occurences": 1,
                "priority": 5,
                "server": '',
                "http_status": 500,
                "request": {},
                "traceback": [],
            };
            report.user_agent = window.navigator.userAgent;
            report.start_time = new Date().toJSON();

            if (this.requestInfo !== null) {
                for (var i in this.requestInfo) {
                    report[i] = this.requestInfo[i];
                }
            }

            if (typeof report.request_id == 'undefined' || !report.request_id) {
                report.request_id = this.genUUID4();
            }
            // grab last 100 frames in reversed order
            var stack_slice = errorReport.stack.reverse().slice(-100);
            for (var i = 0; i < stack_slice.length; i++) {
                var context = '';
                try{
                    if (stack_slice[i].context){
                        for(var j = 0; j < stack_slice[i].context.length; j++){
                            var line = stack_slice[i].context[j];
                            if (line.length > 300){
                                context += '<minified-context>';
                            }
                            else{
                                context += line;
                            }
                            context += '\n';
                        }
                    }
                }
                catch(e){}
                var stackline = {'cline': context,
                    'file': stack_slice[i].url,
                    'fn': stack_slice[i].func,
                    'line': stack_slice[i].line,
                    'vars': []};
                report.traceback.push(stackline);
            }
            if(report.traceback.length > 0){
                var lastFrameContext = stack_slice[i][-1].context;
                report.traceback[report.traceback.length - 1].cline = lastFrameContext + '\n' + error_msg;
            }
            this.errorReportBuffer.push(report);
        },
        log: function (level, message, namespace, uuid) {
            if (typeof namespace == 'undefined') {
                namespace = window.location.pathname;
            }
            if (typeof uuid == 'undefined') {
                uuid = null;
            }
            this.logBuffer.push(
                {
                    "log_level": level.toUpperCase(),
                    "message": message,
                    "date": new Date().toJSON(),
                    "namespace": namespace
                });
            if (this.requestInfo !== null && typeof this.requestInfo.server != 'undefined') {
                this.logBuffer[this.logBuffer.length - 1].server = this.requestInfo.server;
            }
        },

        genUUID4: function () {
            return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(
                /[xy]/g, function (c) {
                    var r = Math.random() * 16 | 0, v = c == 'x' ? r : r & 0x3 | 0x8;
                    return v.toString(16);
                }
            );
        },

        sendReports: function () {
            if (this.errorReportBuffer.length < 1) {
                return true;
            }
            var data = this.errorReportBuffer;
            this.submitData(this.reportsEndpoint, data);
            this.errorReportBuffer = [];
            return true;
        },
        sendLogs: function () {
            if (this.logBuffer.length < 1) {
                return true;
            }
            var data = this.logBuffer;
            this.submitData(this.logsEndpoint, data);
            this.logBuffer = [];
            return true;
        },

        submitData: function (endpoint, data) {
            var xhr = new window.XMLHttpRequest();
            if (!xhr && window.ActiveXObject) {
                xhr = new window.ActiveXObject("Microsoft.XMLHTTP");
            }
            xhr.open("POST", endpoint, true);
            xhr.setRequestHeader("Content-Type", "application/json");
            xhr.send(JSON.stringify(data));
        }
    };
    window.AppEnlight = AppEnlight;

    if ( typeof define === "function" && define.amd ) {
        define( "appenlight", [], function() {
            return AppEnlight;
        });
    }

}(window));
<|endoftext|>


<commit_before>'use strict';

var I = require('immutable');

var matrix = function matrix(scalar, zero, one) {

  var Matrix = I.Record({
    nrows: undefined,
    ncols: undefined,
    data : undefined
  });

  Matrix.prototype.toString = function() {
    return '<'+this.nrows+'x'+this.ncols+' matrix: '+this.data+'>'
  };

  var get = function set(A, i, j) {
    return A.data.getIn([i, j]);
  };

  var _make = function _make(data) {
    return new Matrix({
      nrows: data.size,
      ncols: data.first().size,
      data : I.List(data.map(I.List))
    });
  };

  var make = function make(data) {
    var tmp = I.List(data.map(I.List));
    var m = tmp.map(function(row) { return row.size; }).max();
    return _make(tmp.map(function(row) {
      return row.concat(I.Repeat(zero, m - row.size));
    }));
  };

  var constant = function constant(nrows, ncols, value) {
    var x = value === undefined ? zero : value;
    return _make(I.List(I.Repeat(I.List(I.Repeat(x, ncols)), nrows)));
  };

  var identity = function identity(n) {
    var zrow = I.List(I.Repeat(zero, n));
    return _make(I.Range(0, n).map(function(i) { return zrow.set(i, one); }));
  };

  var transposed = function transposed(A) {
    return _make(I.Range(0, A.ncols).map(function(j) {
      return I.Range(0, A.nrows).map(function(i) {
        return get(A, i, j);
      });
    }));
  };

  var set = function set(A, i, j, x) {
    return _make(A.data.setIn([i, j], x));
  };

  var _findPivot = function _findPivot(A, row, col, overField) {
    var best = row;
    for (var i = row; i < A.nrows; ++i) {
      var x = get(A, i, col);
      if (scalar.sgn(x) != 0) {
        var d = scalar.cmp(x, get(A, best, col));
        if (overField ? d > 0 : d < 0)
          best = i;
      }
    }
    return best;
  };

  var _swapRows = function _swapRows(A, i, j) {
    return _make(A.data.set(i, A.data.get(j)).set(j, A.data.get(i)));
  };

  var _negateRow = function _swapRows(A, i) {
    return _make(A.data.set(i, A.data.get(i).map(scalar.negative)));
  };

  var _adjustRow = function _swapRows(A, i, j, f) {
    return _make(A.data.set(i, I.Range(0, A.ncols).map(function(k) {
      return scalar.toJS(scalar.plus(get(A, i, k),
                                     scalar.times(get(A, j, k), f)));
    })));
  };

  var _Triangulation = I.Record({
    R: undefined,
    U: undefined,
    sign: undefined
  });

  var triangulation = function triangulation(A, overField) {
    var R = A;
    var U = identity(R.ncols);
    var col = 0;
    var sign = 1;
    var overField = !!overField;
    var divide = overField ? scalar.div : scalar.idiv;

    for (var row = 0; row < R.nrows; ++row) {
      var cleared = false;

      while (!cleared && col < R.ncols) {
        var pivotRow = _findPivot(A, row, col, overField);
        var pivot = get(R, pivotRow, col);

        if (scalar.sgn(pivot) == 0) {
          ++col;
          continue;
        }

        if (pivotRow != row) {
          R = _swapRows(R, row, pivotRow);
          U = _swapRows(U, row, pivotRow);
          sign *= -1;
        }

        if (scalar.sgn(pivot) < 0) {
          R = _negateRow(R, row);
          U = _negateRow(U, row);
          sign *= -1;
        }

        cleared = true;

        for (var k = row + 1; k < R.nrows; ++k) {
          if (scalar.sgn(get(R, k, col)) != 0) {
            var f = scalar.negative(divide(get(R, k, col), get(R, row, col)));

            R = _adjustRow(R, k, row, f);
            U = _adjustRow(U, k, row, f);

            if (overField)
              R = set(R, k, col, zero);
            else
              cleared = scalar.sgn(get(R, k, col)) == 0;
          }
        }

        if (cleared)
          ++col;
      }
    }

    return new _Triangulation({ R: R, U: U, sign: sign });
  };

  return {
    make         : make,
    constant     : constant,
    identity     : identity,
    transposed   : transposed,
    set          : set,
    get          : get,
    triangulation: triangulation
  };
};

if (require.main == module) {
  var Q = require('./number');
  var M = matrix(Q, 0, 1);

  console.log(M.make([[1],[2,3],[4,5,6]]));
  console.log(M.constant(3, 4));
  console.log(M.constant(3, 4, 5));
  console.log(M.identity(3));
  console.log(M.transposed(M.constant(3, 4, 5)));
  console.log(M.set(M.identity(3), 0, 1, 4));
  console.log(M.transposed(M.set(M.identity(3), 0, 1, 4)));
  console.log(M.triangulation(M.set(M.identity(3), 1, 0, 4)));
}
<commit_msg>fix pivot selection<commit_after>'use strict';

var I = require('immutable');

var matrix = function matrix(scalar, zero, one) {

  var Matrix = I.Record({
    nrows: undefined,
    ncols: undefined,
    data : undefined
  });

  Matrix.prototype.toString = function() {
    return '<'+this.nrows+'x'+this.ncols+' matrix: '+this.data+'>'
  };

  var get = function set(A, i, j) {
    return A.data.getIn([i, j]);
  };

  var _make = function _make(data) {
    return new Matrix({
      nrows: data.size,
      ncols: data.first().size,
      data : I.List(data.map(I.List))
    });
  };

  var make = function make(data) {
    var tmp = I.List(data.map(I.List));
    var m = tmp.map(function(row) { return row.size; }).max();
    return _make(tmp.map(function(row) {
      return row.concat(I.Repeat(zero, m - row.size));
    }));
  };

  var constant = function constant(nrows, ncols, value) {
    var x = value === undefined ? zero : value;
    return _make(I.List(I.Repeat(I.List(I.Repeat(x, ncols)), nrows)));
  };

  var identity = function identity(n) {
    var zrow = I.List(I.Repeat(zero, n));
    return _make(I.Range(0, n).map(function(i) { return zrow.set(i, one); }));
  };

  var transposed = function transposed(A) {
    return _make(I.Range(0, A.ncols).map(function(j) {
      return I.Range(0, A.nrows).map(function(i) {
        return get(A, i, j);
      });
    }));
  };

  var set = function set(A, i, j, x) {
    return _make(A.data.setIn([i, j], x));
  };

  var _findPivot = function _findPivot(A, row, col, overField) {
    var best = row;
    for (var i = row; i < A.nrows; ++i) {
      var x = scalar.abs(get(A, i, col));
      if (scalar.sgn(x) != 0) {
        var d = scalar.cmp(x, scalar.abs(get(A, best, col)));
        if (overField ? d > 0 : d < 0)
          best = i;
      }
    }
    return best;
  };

  var _swapRows = function _swapRows(A, i, j) {
    return _make(A.data.set(i, A.data.get(j)).set(j, A.data.get(i)));
  };

  var _negateRow = function _swapRows(A, i) {
    return _make(A.data.set(i, A.data.get(i).map(scalar.negative)));
  };

  var _adjustRow = function _swapRows(A, i, j, f) {
    return _make(A.data.set(i, I.Range(0, A.ncols).map(function(k) {
      return scalar.toJS(scalar.plus(get(A, i, k),
                                     scalar.times(get(A, j, k), f)));
    })));
  };

  var _Triangulation = I.Record({
    R: undefined,
    U: undefined,
    sign: undefined
  });

  var triangulation = function triangulation(A, overField) {
    var R = A;
    var U = identity(R.ncols);
    var col = 0;
    var sign = 1;
    var overField = !!overField;
    var divide = overField ? scalar.div : scalar.idiv;

    for (var row = 0; row < R.nrows; ++row) {
      var cleared = false;

      while (!cleared && col < R.ncols) {
        var pivotRow = _findPivot(R, row, col, overField);
        var pivot = get(R, pivotRow, col);

        if (scalar.sgn(pivot) == 0) {
          ++col;
          continue;
        }

        if (pivotRow != row) {
          R = _swapRows(R, row, pivotRow);
          U = _swapRows(U, row, pivotRow);
          sign *= -1;
        }

        if (scalar.sgn(pivot) < 0) {
          R = _negateRow(R, row);
          U = _negateRow(U, row);
          sign *= -1;
        }

        cleared = true;

        for (var k = row + 1; k < R.nrows; ++k) {
          if (scalar.sgn(get(R, k, col)) != 0) {
            var f = scalar.negative(divide(get(R, k, col), get(R, row, col)));

            R = _adjustRow(R, k, row, f);
            U = _adjustRow(U, k, row, f);

            if (overField)
              R = set(R, k, col, zero);
            else
              cleared = scalar.sgn(get(R, k, col)) == 0;
          }
        }

        if (cleared)
          ++col;
      }
    }

    return new _Triangulation({ R: R, U: U, sign: sign });
  };

  return {
    make         : make,
    constant     : constant,
    identity     : identity,
    transposed   : transposed,
    set          : set,
    get          : get,
    triangulation: triangulation
  };
};

if (require.main == module) {
  var Q = require('./number');
  var M = matrix(Q, 0, 1);

  console.log(M.make([[1],[2,3],[4,5,6]]));
  console.log(M.constant(3, 4));
  console.log(M.constant(3, 4, 5));
  console.log(M.identity(3));
  console.log(M.transposed(M.constant(3, 4, 5)));
  console.log(M.set(M.identity(3), 0, 1, 4));
  console.log(M.transposed(M.set(M.identity(3), 0, 1, 4)));
  console.log(M.triangulation(M.set(M.identity(3), 1, 0, 4)));
}
<|endoftext|>


<commit_before>var chunk = require('./chunk');

describe('Array/chunk', function () {
  it('creates an array of elements split into groups the length of size specified', function () {
    expect(chunk([1, 2, 3, 4], 2)).toEqual([
      [1, 2],
      [3, 4]
    ]);

    expect(chunk([1, 2, 3, 4, 5], 2)).toEqual([
      [1, 2],
      [3, 4],
      [5]
    ]);

    expect(chunk([1, 2, 3, 4, 5], -1)).toEqual([]);

    expect(chunk([1, 2, 3, 4, 5])).toEqual([]);

    expect(chunk([1, 2, 3, 4, 5], null)).toEqual([]);

    expect(chunk([1, 2, 3, 4], 3)).toEqual([
      [1, 2, 3],
      [4]
    ]);

    expect(chunk([1, 2, 3, 4], '3')).toEqual([
      [1, 2, 3],
      [4]
    ]);

    expect(chunk([1, 2, 3, 4], Infinity)).toEqual([
      [1, 2, 3, 4]
    ]);

    expect(chunk([1, 2, 3, 4], -Infinity)).toEqual([]);

    expect(chunk([1, 2, 3, 4], 2.7)).toEqual([
      [1, 2],
      [3, 4]
    ]);

    expect(chunk([1, 2, 3, 4], 'test')).toEqual([]);
  });
});
<commit_msg>Test scenario that throws error<commit_after>var chunk = require('./chunk');

describe('Array/chunk', function () {
  it('creates an array of elements split into groups the length of size specified', function () {
    expect(chunk([1, 2, 3, 4], 2)).toEqual([
      [1, 2],
      [3, 4]
    ]);

    expect(chunk([1, 2, 3, 4, 5], 2)).toEqual([
      [1, 2],
      [3, 4],
      [5]
    ]);

    expect(chunk([1, 2, 3, 4, 5], -1)).toEqual([]);

    expect(chunk([1, 2, 3, 4, 5])).toEqual([]);

    expect(chunk([1, 2, 3, 4, 5], null)).toEqual([]);

    expect(chunk([1, 2, 3, 4], 3)).toEqual([
      [1, 2, 3],
      [4]
    ]);

    expect(chunk([1, 2, 3, 4], '3')).toEqual([
      [1, 2, 3],
      [4]
    ]);

    expect(chunk([1, 2, 3, 4], Infinity)).toEqual([
      [1, 2, 3, 4]
    ]);

    expect(chunk([1, 2, 3, 4], -Infinity)).toEqual([]);

    expect(chunk([1, 2, 3, 4], 2.7)).toEqual([
      [1, 2],
      [3, 4]
    ]);

    expect(chunk([1, 2, 3, 4], 'test')).toEqual([]);

    expect(function () {
      return chunk({
        a: 'a',
        b: 'b'
      }, 2);
    }).toThrow();
  });
});
<|endoftext|>