<commit_before>import React, { Component } from 'react'
import Immutable from 'immutable'
import PropTypes from 'prop-types'
import { connect } from 'react-redux'
import { loadEditorials } from '../actions/editorials'
import StreamContainer from '../containers/StreamContainer'
import { HeroHeader } from '../components/heros/HeroRenderables'
import { MainView } from '../components/views/MainView'
import { selectQueryPreview } from '../selectors/routing'
import { media } from '../styles/jss'
import { maxBreak2 } from '../styles/jso'
import { selectDPI } from '../selectors/gui'

const streamStyle = media(maxBreak2, {
  paddingLeft: '0 !important',
  paddingRight: '0 !important',
})

const mapStateToProps = state => ({
  dpi: selectDPI(state),
  isPreview: selectQueryPreview(state) === 'true',
})

class EditorialPage extends Component {

  static propTypes = {
    dpi: PropTypes.string.isRequired,
    isPreview: PropTypes.boolean,
    sources: PropTypes.object,
    avatarSources: PropTypes.object,
  }

  static defaultProps = {
    isPreview: false,
    sources: Immutable.fromJS({
      optimized: { url: 'https://assets1.ello.co/uploads/asset/attachment/2542523/ello-optimized-c553b9c7.jpg', metadata: 'optimized' },
      mdpi: { url: 'https://assets1.ello.co/uploads/asset/attachment/2542523/ello-mdpi-c553b9c7.jpg', metadata: 'mdpi' },
      hdpi: { url: 'https://assets2.ello.co/uploads/asset/attachment/2542523/ello-hdpi-c553b9c7.jpg', metadata: 'hdpi' },
      xhdpi: { url: 'https://assets0.ello.co/uploads/asset/attachment/2542523/ello-xhdpi-c553b9c7.jpg', metadata: 'xhdpi' },
    }),
    avatarSources: Immutable.fromJS({
      original: { url: 'https://assets1.ello.co/uploads/user/avatar/930194/ello-b7ae736f-cb24-4994-898e-219be1b0f29d.gif', metadata: 'optimized' },
      small: { url: 'https://assets1.ello.co/uploads/user/avatar/930194/ello-small-b851c6c6.png', metadata: 'mdpi' },
      regular: { url: 'https://assets0.ello.co/uploads/user/avatar/930194/ello-regular-b851c6c6.png', metadata: 'hdpi' },
      large: { url: 'https://assets0.ello.co/uploads/user/avatar/930194/ello-large-b851c6c6.png', metadata: 'xhdpi' },
    }),
  }

  shouldComponentUpdate() {
    return false
  }

  render() {
    const { dpi, sources, avatarSources } = this.props
    return (
      <MainView className="Editorial">
        <HeroHeader
          dpi={dpi}
          headerText="THE CREATOR'S NETWORK"
          subHeaderText="Ello is a global artist community dedicated to creative excellence — made for artists by artists."
          sources={sources}
          avatarSources={avatarSources}
          username="velvetspectrum"
        />
        <StreamContainer
          action={loadEditorials(this.props.isPreview)}
          className={`${streamStyle}`}
          hasShowMoreButton
          paginatorText="Load More"
          paginatorCentered
          shouldInfiniteScroll={false}
        />
      </MainView>
    )
  }
}

export default connect(mapStateToProps)(EditorialPage)

<commit_msg>Remove apostrophe<commit_after>import React, { Component } from 'react'
import Immutable from 'immutable'
import PropTypes from 'prop-types'
import { connect } from 'react-redux'
import { loadEditorials } from '../actions/editorials'
import StreamContainer from '../containers/StreamContainer'
import { HeroHeader } from '../components/heros/HeroRenderables'
import { MainView } from '../components/views/MainView'
import { selectQueryPreview } from '../selectors/routing'
import { media } from '../styles/jss'
import { maxBreak2 } from '../styles/jso'
import { selectDPI } from '../selectors/gui'

const streamStyle = media(maxBreak2, {
  paddingLeft: '0 !important',
  paddingRight: '0 !important',
})

const mapStateToProps = state => ({
  dpi: selectDPI(state),
  isPreview: selectQueryPreview(state) === 'true',
})

class EditorialPage extends Component {

  static propTypes = {
    dpi: PropTypes.string.isRequired,
    isPreview: PropTypes.boolean,
    sources: PropTypes.object,
    avatarSources: PropTypes.object,
  }

  static defaultProps = {
    isPreview: false,
    sources: Immutable.fromJS({
      optimized: { url: 'https://assets1.ello.co/uploads/asset/attachment/2542523/ello-optimized-c553b9c7.jpg', metadata: 'optimized' },
      mdpi: { url: 'https://assets1.ello.co/uploads/asset/attachment/2542523/ello-mdpi-c553b9c7.jpg', metadata: 'mdpi' },
      hdpi: { url: 'https://assets2.ello.co/uploads/asset/attachment/2542523/ello-hdpi-c553b9c7.jpg', metadata: 'hdpi' },
      xhdpi: { url: 'https://assets0.ello.co/uploads/asset/attachment/2542523/ello-xhdpi-c553b9c7.jpg', metadata: 'xhdpi' },
    }),
    avatarSources: Immutable.fromJS({
      original: { url: 'https://assets1.ello.co/uploads/user/avatar/930194/ello-b7ae736f-cb24-4994-898e-219be1b0f29d.gif', metadata: 'optimized' },
      small: { url: 'https://assets1.ello.co/uploads/user/avatar/930194/ello-small-b851c6c6.png', metadata: 'mdpi' },
      regular: { url: 'https://assets0.ello.co/uploads/user/avatar/930194/ello-regular-b851c6c6.png', metadata: 'hdpi' },
      large: { url: 'https://assets0.ello.co/uploads/user/avatar/930194/ello-large-b851c6c6.png', metadata: 'xhdpi' },
    }),
  }

  shouldComponentUpdate() {
    return false
  }

  render() {
    const { dpi, sources, avatarSources } = this.props
    return (
      <MainView className="Editorial">
        <HeroHeader
          dpi={dpi}
          headerText="THE CREATORS NETWORK"
          subHeaderText="Ello is a global artist community dedicated to creative excellence — made for artists by artists."
          sources={sources}
          avatarSources={avatarSources}
          username="velvetspectrum"
        />
        <StreamContainer
          action={loadEditorials(this.props.isPreview)}
          className={`${streamStyle}`}
          hasShowMoreButton
          paginatorText="Load More"
          paginatorCentered
          shouldInfiniteScroll={false}
        />
      </MainView>
    )
  }
}

export default connect(mapStateToProps)(EditorialPage)

<|endoftext|>


<commit_before>'use strict';

var db = require('mano').db,
	user = db.User.prototype;

exports['step-form'] = { class: { 'step-active': true } };

exports.step = function () {
	h1("2. Fill the form");
	div({ class: 'error-main' },
		p(span({ class: 'fa fa-exclamation-circle' }), "Please fill the Guide first"));
	div(
		{ class: 'disabler-range', id: 'forms-disabler-range' },
		section(
			form(
				div({ class: 'section-primary' },
					h2("Business Owner basic informations"),
					hr(),
					fieldset(
						{ class: 'form-elements fieldset' },
						ul(
							['firstName', 'lastName', 'dateOfBirth', 'userEmail'],
							function (name) { return field({ dbjs: user.getObservable(name) }); }
						)
					),
					p({ class: 'submit-placeholder' },
						input({ type: 'submit' }, "Submit")
						)
					)
			)
		),

		section({ class: 'section-primary' },
			form(
				h2("Business Owner secondary informations"),
				hr(),
				div({ class: 'sub-section' },
					h3("First subsection"),
					fieldset(
						{ class: 'form-elements' },
						ul(
							['companyType', 'members', 'inventory', 'surfaceArea', 'isOwner', 'businessActivity',
								'registerIds'],
							function (name) { return field({ dbjs: user.getObservable(name) }); }
						)
					),
					p({ class: 'submit-placeholder' },
						input({ type: 'submit' }, "Submit")
						)
					),
				div({ class: 'sub-section' },
					h3("Second subsection"),
					fieldset(
						{ class: 'form-elements' },
						ul(
							['companyType', 'members', 'inventory', 'surfaceArea', 'isOwner', 'businessActivity',
								'registerIds'],
							function (name) { return field({ dbjs: user.getObservable(name) }); }
						)
					),
					p({ class: 'submit-placeholder' },
						input({ type: 'submit' }, "Submit")
						)
					)
			)
			),

		section({ class: 'section-primary' },
			div(
				div(
					h2("Directors & non-directors owner / partners"),
					hr(),
					table(
						{ class: 'partners-list' },
						thead(
							tr(
								th({ class: 'desktop-only' }, "Entity"),
								th("First name"),
								th("Surname"),
								th({ class: 'desktop-only' }, "Director?"),
								th({ class: 'desktop-only' }, "Subscriber?"),
								th({ class: 'desktop-only' }, ""),
								th({ class: 'actions' }, "Actions")
							)
						),
						tbody(
							tr(
								td({ class: 'desktop-only' },
									a({ href: '/forms/partner-id/' }, "Lorem")
									),
								td(a({ href: '/forms/partner-id/' }, "John")),
								td(a({ href: '/forms/partner-id/' }, "Watson")),
								td({ class: 'desktop-only' }, a({ href: '/forms/partner-id/' }, "Yes")),
								td({ class: 'desktop-only' }, a({ href: '/forms/partner-id/' }, "Yes")),
								td({ class: 'desktop-only confirmed' }, "✓"),
								td({ class: 'actions' },
									a("Edit"),
									a("Delete")
									)
							),
							tr(
								td({ class: 'desktop-only' },
									a({ href: '/forms/partner-id/' }, "Lorem")
									),
								td(a({ href: '/forms/partner-id/' }, "John")),
								td(a({ href: '/forms/partner-id/' }, "Watson")),
								td({ class: 'desktop-only' }, a({ href: '/forms/partner-id/' }, "Yes")),
								td({ class: 'desktop-only' }, a({ href: '/forms/partner-id/' }, "Yes")),
								td({ class: 'desktop-only confirmed' }, "✓"),
								td({ class: 'actions' },
									a("Edit"),
									a("Delete")
									)
							),
							tr(
								td({ class: 'desktop-only' },
									a({ href: '/forms/partner-id/' }, "Lorem")
									),
								td(a({ href: '/forms/partner-id/' }, "John")),
								td(a({ href: '/forms/partner-id/' }, "Watson")),
								td({ class: 'desktop-only' }, a({ href: '/forms/partner-id/' }, "Yes")),
								td({ class: 'desktop-only' }, a({ href: '/forms/partner-id/' }, "Yes")),
								td({ class: 'desktop-only confirmed' }, "✓"),
								td({ class: 'actions' },
									a("Edit"),
									a("Delete")
									)
							),
							tr(
								td({ class: 'desktop-only' },
									a({ href: '/forms/partner-id/' }, "Lorem")
									),
								td(a({ href: '/forms/partner-id/' }, "John")),
								td(a({ href: '/forms/partner-id/' }, "Watson")),
								td({ class: 'desktop-only' }, a({ href: '/forms/partner-id/' }, "Yes")),
								td({ class: 'desktop-only' }, a({ href: '/forms/partner-id/' }, "Yes")),
								td({ class: 'desktop-only confirmed' }, "✓"),
								td({ class: 'actions' },
									a("Edit"),
									a("Delete")
									)
							),
							tr(
								td({ class: 'desktop-only' },
									a({ href: '/forms/partner-id/' }, "Lorem")
									),
								td(a({ href: '/forms/partner-id/' }, "John")),
								td(a({ href: '/forms/partner-id/' }, "Watson")),
								td({ class: 'desktop-only' }, a({ href: '/forms/partner-id/' }, "Yes")),
								td({ class: 'desktop-only' }, a({ href: '/forms/partner-id/' }, "Yes")),
								td({ class: 'desktop-only confirmed' }, "✓"),
								td({ class: 'actions' },
									a("Edit"),
									a("Delete")
									)
							)
						)
					),
					a(
						{ class: 'new-entity', href: '/forms/partner-add/' },
						"Add new partner"
					)
				)
			)
			),

		div({ class: 'next-step' },
			a({ href: '/documents/' }, "Continue to next step")
			),
		div({ class: 'disabler' })
	);
};
<commit_msg>Removed not needed class fieldset in forms. #161<commit_after>'use strict';

var db = require('mano').db,
	user = db.User.prototype;

exports['step-form'] = { class: { 'step-active': true } };

exports.step = function () {
	h1("2. Fill the form");
	div({ class: 'error-main' },
		p(span({ class: 'fa fa-exclamation-circle' }), "Please fill the Guide first"));
	div(
		{ class: 'disabler-range', id: 'forms-disabler-range' },
		section(
			form(
				div({ class: 'section-primary' },
					h2("Business Owner basic informations"),
					hr(),
					fieldset(
						{ class: 'form-elements' },
						ul(
							['firstName', 'lastName', 'dateOfBirth', 'userEmail'],
							function (name) { return field({ dbjs: user.getObservable(name) }); }
						)
					),
					p({ class: 'submit-placeholder' },
						input({ type: 'submit' }, "Submit")
						)
					)
			)
		),

		section({ class: 'section-primary' },
			form(
				h2("Business Owner secondary informations"),
				hr(),
				div({ class: 'sub-section' },
					h3("First subsection"),
					fieldset(
						{ class: 'form-elements' },
						ul(
							['companyType', 'members', 'inventory', 'surfaceArea', 'isOwner', 'businessActivity',
								'registerIds'],
							function (name) { return field({ dbjs: user.getObservable(name) }); }
						)
					),
					p({ class: 'submit-placeholder' },
						input({ type: 'submit' }, "Submit")
						)
					),
				div({ class: 'sub-section' },
					h3("Second subsection"),
					fieldset(
						{ class: 'form-elements' },
						ul(
							['companyType', 'members', 'inventory', 'surfaceArea', 'isOwner', 'businessActivity',
								'registerIds'],
							function (name) { return field({ dbjs: user.getObservable(name) }); }
						)
					),
					p({ class: 'submit-placeholder' },
						input({ type: 'submit' }, "Submit")
						)
					)
			)
			),

		section({ class: 'section-primary' },
			div(
				div(
					h2("Directors & non-directors owner / partners"),
					hr(),
					table(
						{ class: 'partners-list' },
						thead(
							tr(
								th({ class: 'desktop-only' }, "Entity"),
								th("First name"),
								th("Surname"),
								th({ class: 'desktop-only' }, "Director?"),
								th({ class: 'desktop-only' }, "Subscriber?"),
								th({ class: 'desktop-only' }, ""),
								th({ class: 'actions' }, "Actions")
							)
						),
						tbody(
							tr(
								td({ class: 'desktop-only' },
									a({ href: '/forms/partner-id/' }, "Lorem")
									),
								td(a({ href: '/forms/partner-id/' }, "John")),
								td(a({ href: '/forms/partner-id/' }, "Watson")),
								td({ class: 'desktop-only' }, a({ href: '/forms/partner-id/' }, "Yes")),
								td({ class: 'desktop-only' }, a({ href: '/forms/partner-id/' }, "Yes")),
								td({ class: 'desktop-only confirmed' }, "✓"),
								td({ class: 'actions' },
									a("Edit"),
									a("Delete")
									)
							),
							tr(
								td({ class: 'desktop-only' },
									a({ href: '/forms/partner-id/' }, "Lorem")
									),
								td(a({ href: '/forms/partner-id/' }, "John")),
								td(a({ href: '/forms/partner-id/' }, "Watson")),
								td({ class: 'desktop-only' }, a({ href: '/forms/partner-id/' }, "Yes")),
								td({ class: 'desktop-only' }, a({ href: '/forms/partner-id/' }, "Yes")),
								td({ class: 'desktop-only confirmed' }, "✓"),
								td({ class: 'actions' },
									a("Edit"),
									a("Delete")
									)
							),
							tr(
								td({ class: 'desktop-only' },
									a({ href: '/forms/partner-id/' }, "Lorem")
									),
								td(a({ href: '/forms/partner-id/' }, "John")),
								td(a({ href: '/forms/partner-id/' }, "Watson")),
								td({ class: 'desktop-only' }, a({ href: '/forms/partner-id/' }, "Yes")),
								td({ class: 'desktop-only' }, a({ href: '/forms/partner-id/' }, "Yes")),
								td({ class: 'desktop-only confirmed' }, "✓"),
								td({ class: 'actions' },
									a("Edit"),
									a("Delete")
									)
							),
							tr(
								td({ class: 'desktop-only' },
									a({ href: '/forms/partner-id/' }, "Lorem")
									),
								td(a({ href: '/forms/partner-id/' }, "John")),
								td(a({ href: '/forms/partner-id/' }, "Watson")),
								td({ class: 'desktop-only' }, a({ href: '/forms/partner-id/' }, "Yes")),
								td({ class: 'desktop-only' }, a({ href: '/forms/partner-id/' }, "Yes")),
								td({ class: 'desktop-only confirmed' }, "✓"),
								td({ class: 'actions' },
									a("Edit"),
									a("Delete")
									)
							),
							tr(
								td({ class: 'desktop-only' },
									a({ href: '/forms/partner-id/' }, "Lorem")
									),
								td(a({ href: '/forms/partner-id/' }, "John")),
								td(a({ href: '/forms/partner-id/' }, "Watson")),
								td({ class: 'desktop-only' }, a({ href: '/forms/partner-id/' }, "Yes")),
								td({ class: 'desktop-only' }, a({ href: '/forms/partner-id/' }, "Yes")),
								td({ class: 'desktop-only confirmed' }, "✓"),
								td({ class: 'actions' },
									a("Edit"),
									a("Delete")
									)
							)
						)
					),
					a(
						{ class: 'new-entity', href: '/forms/partner-add/' },
						"Add new partner"
					)
				)
			)
			),

		div({ class: 'next-step' },
			a({ href: '/documents/' }, "Continue to next step")
			),
		div({ class: 'disabler' })
	);
};
<|endoftext|>


<commit_before>/**
 * mSupply Mobile
 * Sustainable Solutions (NZ) Ltd. 2019
 */

import React, { useState, useCallback } from 'react';
import { View, VirtualizedList, Text, StyleSheet, ToastAndroid } from 'react-native';
import PropTypes from 'prop-types';

import { UIDatabase } from '../database/index';
import { schema } from '../database/schema';
import { useDebounce } from '../hooks/useDebounce';

import { SearchBar } from '../widgets';

import globalStyles from '../globalStyles';

const DEBOUNCE_TIMEOUT = 750;

const TYPES = {
  BOOLEAN: 'boolean',
  DATE: 'date',
  NUMBER: 'number',
  STRING: 'string',
  ARRAY: 'array',
  OBJECT: 'object',
};

const REALM_TYPES = {
  BOOL: 'bool',
  DATE: 'date',
  DOUBLE: 'double',
  FLOAT: 'float',
  INT: 'int',
  LIST: 'list',
  LINKING_OBJECTS: 'linkingObjects',
  OBJECT: 'object',
  STRING: 'string',
};

const TYPE_MAPPINGS = {
  [TYPES.BOOLEAN]: [REALM_TYPES.BOOL],
  [TYPES.DATE]: [REALM_TYPES.DATE],
  [TYPES.NUMBER]: [REALM_TYPES.INT, REALM_TYPES.FLOAT, REALM_TYPES.DOUBLE],
  [TYPES.STRING]: [REALM_TYPES.STRING],
  [TYPES.ARRAY]: [REALM_TYPES.LIST, REALM_TYPES.LINKING_OBJECTS],
  [TYPES.OBJECT]: [REALM_TYPES.OBJECT],
};

const typeMapper = new Map();

Object.entries(TYPE_MAPPINGS).forEach(([type, realmTypes]) => {
  realmTypes.forEach(realmType => {
    typeMapper.set(realmType, type);
  });
});

const parseType = realmType => {
  const { type } = realmType;
  if (type) return parseType(type);
  return typeMapper.get(realmType) || TYPES.OBJECT;
};

const getRealmObjects = ({ schema: objectSchemas }) => objectSchemas.map(({ name }) => name);

const getRealmObjectsFields = ({ schema: objectSchemas }) =>
  objectSchemas
    .map(({ schema: objectSchema }) => {
      const { name, properties } = objectSchema;
      const fields = Object.entries(properties)
        .map(([field, type]) => ({
          [field]: parseType(type),
        }))
        .reduce((acc, field) => ({ ...acc, ...field }), {});
      return { [name]: fields };
    })
    .reduce((acc, object) => ({ ...acc, ...object }));

const REALM_OBJECTS = getRealmObjects(schema);
const REALM_OBJECTS_FIELDS = getRealmObjectsFields(schema);

const toCapitalCase = value => value.charAt(0).toUpperCase() + value.slice(1);

const parseString = value => String(value);
const parseNumber = value => String(value);
const parseObject = value => value.id;
const parseArray = value => value.length;
const parseBoolean = value => toCapitalCase(String(value));
const parseDate = value => value.toString();

const parseCell = (value, type) => {
  if (value === null || value === undefined) return 'N/A';
  switch (type) {
    case TYPES.STRING:
      return parseString(value);
    case TYPES.NUMBER:
      return parseNumber(value);
    case TYPES.OBJECT:
      return parseObject(value);
    case TYPES.ARRAY:
      return parseArray(value);
    case TYPES.BOOLEAN:
      return parseBoolean(value);
    case TYPES.DATE:
      return parseDate(value);
    default:
      return '';
  }
};

const getInitialState = () => {
  const searchString = '';
  const filterString = '';
  const searchData = {};
  const filteredData = [];
  return { searchString, searchData, filterString, filteredData };
};

const updateSearchData = (newSearchString, state) => {
  try {
    const { searchString } = state;
    const updateObjectString = newSearchString !== searchString;
    // Some single characters are returning true for 'REALM_OBJECTS.indexOf(newSearchString) >= 0;'
    // there are no database objects with names shorter than 3 chars; so just skip those
    if (!updateObjectString || newSearchString.length < 3) return { ...state };
    const updateObject = REALM_OBJECTS.indexOf(newSearchString) >= 0;
    const newSearchData = updateObject ? UIDatabase.objects(newSearchString) : {};
    const newFilteredData = updateObject ? newSearchData.slice() : [];
    return {
      ...state,
      searchString: newSearchString,
      searchData: newSearchData,
      filteredData: newFilteredData,
    };
  } catch (e) {
    ToastAndroid.show(`Error! ${e.message}`, ToastAndroid.LONG);
    return { ...state };
  }
};

const getSearchBarRenderer = onSearchChange => {
  const RealmSearchBar = ({ searchString }) => (
    <SearchBar value={searchString} onChangeText={onSearchChange} placeholder="Object string" />
  );
  RealmSearchBar.propTypes = propTypes.RealmSearchBar;
  return RealmSearchBar;
};

const updateFilteredData = (newFilterString, state) => {
  const { searchData } = state;
  try {
    const newFilteredData =
      newFilterString === '' ? searchData.slice() : searchData.filtered(newFilterString).slice();
    return { ...state, filterString: newFilterString, filteredData: newFilteredData };
  } catch (err) {
    ToastAndroid.show(`Error! ${err.message}`, ToastAndroid.LONG);
    return { ...state, filterString: newFilterString };
  }
};

const getFilterBarRenderer = onFilterChange => {
  const RealmFilterBar = ({ filterString }) => (
    <SearchBar value={filterString} onChangeText={onFilterChange} placeholder="Filter string" />
  );
  RealmFilterBar.propTypes = propTypes.RealmFilterBar;
  return RealmFilterBar;
};

const getHeaderRenderer = realmObjectFields => () => {
  if (!realmObjectFields) return null;
  const headerCells = Object.keys(realmObjectFields).map(columnKey => (
    <View key={columnKey} style={styles.cell}>
      <Text style={styles.cellText}>{columnKey}</Text>
    </View>
  ));
  return <View style={styles.row}>{headerCells}</View>;
};

const getRowRenderer = realmObjectFields => {
  const RealmTableRow = ({ item }) => {
    if (!realmObjectFields) return null;
    const cells = Object.entries(realmObjectFields).map(([columnKey, columnType]) => {
      const cell = item[columnKey];
      const cellValue = parseCell(cell, columnType);
      return (
        <View key={columnKey} style={styles.cell}>
          <Text style={styles.cellText}>{cellValue}</Text>
        </View>
      );
    });
    return <View style={styles.row}>{cells}</View>;
  };
  RealmTableRow.propTypes = propTypes.RealmTableRow;
  return RealmTableRow;
};

const getItem = (data, index) => data[index];
const getItemCount = data => data.length;
const keyExtractor = ({ id }) => id;

const getTableRenderer = realmObjectFields => {
  const RealmTable = ({ data }) => {
    if (!realmObjectFields) return null;
    const TableHeader = getHeaderRenderer(realmObjectFields);
    const TableRow = getRowRenderer(realmObjectFields);
    return (
      <VirtualizedList
        ListHeaderComponent={TableHeader}
        data={data}
        getItem={getItem}
        getItemCount={getItemCount}
        keyExtractor={keyExtractor}
        renderItem={TableRow}
      />
    );
  };
  RealmTable.propTypes = propTypes.RealmTable;
  return RealmTable;
};

/**
 * A page for displaying objects in the local database. Includes search and filtering functionality.
 *
 * @prop   {UIDatabase}     database      App wide database.
 * @state  {string}         searchString  Current search string. Used to update current object.
 * @state  {string}         filterString  Current filter string. Used to update filtered data.
 * @state  {Realm.Results}  objectData    Reference to current database object results. Used to roll
 *                                        back filter state when filter is reset.
 * @state  {Realm.Results}  filteredData  Reference to current database object results after filter
 *                                        has been applied. Displayed to the user.
 */
export const RealmExplorer = () => {
  const [state, setState] = useState(getInitialState());

  const onSearchChange = useCallback(
    useDebounce(
      newObjectString => setState(prevState => updateSearchData(newObjectString, prevState)),
      DEBOUNCE_TIMEOUT
    ),
    []
  );
  const onFilterChange = useCallback(
    useDebounce(
      newFilterString => setState(prevState => updateFilteredData(newFilterString, prevState)),
      DEBOUNCE_TIMEOUT
    ),
    []
  );

  const { searchString, filterString, filteredData } = state;

  const realmObjectFields = REALM_OBJECTS_FIELDS[searchString];

  const RealmSearchBar = useCallback(getSearchBarRenderer(onSearchChange), [onSearchChange]);
  const RealmFilterBar = useCallback(getFilterBarRenderer(onFilterChange), [onFilterChange]);
  const RealmTable = useCallback(getTableRenderer(realmObjectFields), [realmObjectFields]);

  return (
    <View style={[globalStyles.container]}>
      <RealmSearchBar searchString={searchString} />
      <RealmFilterBar filterString={filterString} />
      <RealmTable data={filteredData} />
    </View>
  );
};

const styles = StyleSheet.create({
  cell: {
    flex: 1,
  },
  cellText: {
    textAlign: 'center',
  },
  row: {
    flex: 1,
    flexBasis: 0,
    flexDirection: 'row',
    flexGrow: 1,
    flexWrap: 'nowrap',
    justifyContent: 'space-between',
  },
});

const propTypes = {
  RealmSearchBar: {
    searchString: PropTypes.string.isRequired,
  },
  RealmFilterBar: {
    filterString: PropTypes.string.isRequired,
  },
  RealmTable: {
    data: PropTypes.arrayOf(PropTypes.shape({ id: PropTypes.string.isRequired })).isRequired,
  },
  RealmTableRow: {
    item: PropTypes.shape({ id: PropTypes.string.isRequired }).isRequired,
  },
};
<commit_msg>no toast for you!<commit_after>/**
 * mSupply Mobile
 * Sustainable Solutions (NZ) Ltd. 2019
 */

import React, { useState, useCallback } from 'react';
import { View, VirtualizedList, Text, StyleSheet, ToastAndroid } from 'react-native';
import PropTypes from 'prop-types';

import { UIDatabase } from '../database/index';
import { schema } from '../database/schema';
import { useDebounce } from '../hooks/useDebounce';

import { SearchBar } from '../widgets';

import globalStyles from '../globalStyles';

const DEBOUNCE_TIMEOUT = 750;

const TYPES = {
  BOOLEAN: 'boolean',
  DATE: 'date',
  NUMBER: 'number',
  STRING: 'string',
  ARRAY: 'array',
  OBJECT: 'object',
};

const REALM_TYPES = {
  BOOL: 'bool',
  DATE: 'date',
  DOUBLE: 'double',
  FLOAT: 'float',
  INT: 'int',
  LIST: 'list',
  LINKING_OBJECTS: 'linkingObjects',
  OBJECT: 'object',
  STRING: 'string',
};

const TYPE_MAPPINGS = {
  [TYPES.BOOLEAN]: [REALM_TYPES.BOOL],
  [TYPES.DATE]: [REALM_TYPES.DATE],
  [TYPES.NUMBER]: [REALM_TYPES.INT, REALM_TYPES.FLOAT, REALM_TYPES.DOUBLE],
  [TYPES.STRING]: [REALM_TYPES.STRING],
  [TYPES.ARRAY]: [REALM_TYPES.LIST, REALM_TYPES.LINKING_OBJECTS],
  [TYPES.OBJECT]: [REALM_TYPES.OBJECT],
};

const typeMapper = new Map();

Object.entries(TYPE_MAPPINGS).forEach(([type, realmTypes]) => {
  realmTypes.forEach(realmType => {
    typeMapper.set(realmType, type);
  });
});

const parseType = realmType => {
  const { type } = realmType;
  if (type) return parseType(type);
  return typeMapper.get(realmType) || TYPES.OBJECT;
};

const getRealmObjects = ({ schema: objectSchemas }) => objectSchemas.map(({ name }) => name);

const getRealmObjectsFields = ({ schema: objectSchemas }) =>
  objectSchemas
    .map(({ schema: objectSchema }) => {
      const { name, properties } = objectSchema;
      const fields = Object.entries(properties)
        .map(([field, type]) => ({
          [field]: parseType(type),
        }))
        .reduce((acc, field) => ({ ...acc, ...field }), {});
      return { [name]: fields };
    })
    .reduce((acc, object) => ({ ...acc, ...object }));

const REALM_OBJECTS = getRealmObjects(schema);
const REALM_OBJECTS_FIELDS = getRealmObjectsFields(schema);

const toCapitalCase = value => value.charAt(0).toUpperCase() + value.slice(1);

const parseString = value => String(value);
const parseNumber = value => String(value);
const parseObject = value => value.id;
const parseArray = value => value.length;
const parseBoolean = value => toCapitalCase(String(value));
const parseDate = value => value.toString();

const parseCell = (value, type) => {
  if (value === null || value === undefined) return 'N/A';
  switch (type) {
    case TYPES.STRING:
      return parseString(value);
    case TYPES.NUMBER:
      return parseNumber(value);
    case TYPES.OBJECT:
      return parseObject(value);
    case TYPES.ARRAY:
      return parseArray(value);
    case TYPES.BOOLEAN:
      return parseBoolean(value);
    case TYPES.DATE:
      return parseDate(value);
    default:
      return '';
  }
};

const getInitialState = () => {
  const searchString = '';
  const filterString = '';
  const searchData = {};
  const filteredData = [];
  return { searchString, searchData, filterString, filteredData };
};

const updateSearchData = (newSearchString, state) => {
  try {
    const { searchString } = state;
    const updateObjectString = newSearchString !== searchString;
    // Some single characters are returning true for 'REALM_OBJECTS.indexOf(newSearchString) >= 0;'
    // there are no database objects with names shorter than 3 chars; so just skip those
    if (!updateObjectString || newSearchString.length < 3) return { ...state };
    const updateObject = REALM_OBJECTS.indexOf(newSearchString) >= 0;
    const newSearchData = updateObject ? UIDatabase.objects(newSearchString) : {};
    const newFilteredData = updateObject ? newSearchData.slice() : [];
    return {
      ...state,
      searchString: newSearchString,
      searchData: newSearchData,
      filteredData: newFilteredData,
    };
  } catch (e) {
    ToastAndroid.show(`Error! ${e.message}`, ToastAndroid.LONG);
    return { ...state };
  }
};

const getSearchBarRenderer = onSearchChange => {
  const RealmSearchBar = ({ searchString }) => (
    <SearchBar value={searchString} onChangeText={onSearchChange} placeholder="Object string" />
  );
  RealmSearchBar.propTypes = propTypes.RealmSearchBar;
  return RealmSearchBar;
};

const updateFilteredData = (newFilterString, state) => {
  const { searchData } = state;
  try {
    const newFilteredData =
      newFilterString === '' ? searchData.slice() : searchData.filtered(newFilterString).slice();
    return { ...state, filterString: newFilterString, filteredData: newFilteredData };
  } catch (err) {
    return { ...state, filterString: newFilterString };
  }
};

const getFilterBarRenderer = onFilterChange => {
  const RealmFilterBar = ({ filterString }) => (
    <SearchBar value={filterString} onChangeText={onFilterChange} placeholder="Filter string" />
  );
  RealmFilterBar.propTypes = propTypes.RealmFilterBar;
  return RealmFilterBar;
};

const getHeaderRenderer = realmObjectFields => () => {
  if (!realmObjectFields) return null;
  const headerCells = Object.keys(realmObjectFields).map(columnKey => (
    <View key={columnKey} style={styles.cell}>
      <Text style={styles.cellText}>{columnKey}</Text>
    </View>
  ));
  return <View style={styles.row}>{headerCells}</View>;
};

const getRowRenderer = realmObjectFields => {
  const RealmTableRow = ({ item }) => {
    if (!realmObjectFields) return null;
    const cells = Object.entries(realmObjectFields).map(([columnKey, columnType]) => {
      const cell = item[columnKey];
      const cellValue = parseCell(cell, columnType);
      return (
        <View key={columnKey} style={styles.cell}>
          <Text style={styles.cellText}>{cellValue}</Text>
        </View>
      );
    });
    return <View style={styles.row}>{cells}</View>;
  };
  RealmTableRow.propTypes = propTypes.RealmTableRow;
  return RealmTableRow;
};

const getItem = (data, index) => data[index];
const getItemCount = data => data.length;
const keyExtractor = ({ id }) => id;

const getTableRenderer = realmObjectFields => {
  const RealmTable = ({ data }) => {
    if (!realmObjectFields) return null;
    const TableHeader = getHeaderRenderer(realmObjectFields);
    const TableRow = getRowRenderer(realmObjectFields);
    return (
      <VirtualizedList
        ListHeaderComponent={TableHeader}
        data={data}
        getItem={getItem}
        getItemCount={getItemCount}
        keyExtractor={keyExtractor}
        renderItem={TableRow}
      />
    );
  };
  RealmTable.propTypes = propTypes.RealmTable;
  return RealmTable;
};

/**
 * A page for displaying objects in the local database. Includes search and filtering functionality.
 *
 * @prop   {UIDatabase}     database      App wide database.
 * @state  {string}         searchString  Current search string. Used to update current object.
 * @state  {string}         filterString  Current filter string. Used to update filtered data.
 * @state  {Realm.Results}  objectData    Reference to current database object results. Used to roll
 *                                        back filter state when filter is reset.
 * @state  {Realm.Results}  filteredData  Reference to current database object results after filter
 *                                        has been applied. Displayed to the user.
 */
export const RealmExplorer = () => {
  const [state, setState] = useState(getInitialState());

  const onSearchChange = useCallback(
    useDebounce(
      newObjectString => setState(prevState => updateSearchData(newObjectString, prevState)),
      DEBOUNCE_TIMEOUT
    ),
    []
  );
  const onFilterChange = useCallback(
    useDebounce(
      newFilterString => setState(prevState => updateFilteredData(newFilterString, prevState)),
      DEBOUNCE_TIMEOUT
    ),
    []
  );

  const { searchString, filterString, filteredData } = state;

  const realmObjectFields = REALM_OBJECTS_FIELDS[searchString];

  const RealmSearchBar = useCallback(getSearchBarRenderer(onSearchChange), [onSearchChange]);
  const RealmFilterBar = useCallback(getFilterBarRenderer(onFilterChange), [onFilterChange]);
  const RealmTable = useCallback(getTableRenderer(realmObjectFields), [realmObjectFields]);

  return (
    <View style={[globalStyles.container]}>
      <RealmSearchBar searchString={searchString} />
      <RealmFilterBar filterString={filterString} />
      <RealmTable data={filteredData} />
    </View>
  );
};

const styles = StyleSheet.create({
  cell: {
    flex: 1,
  },
  cellText: {
    textAlign: 'center',
  },
  row: {
    flex: 1,
    flexBasis: 0,
    flexDirection: 'row',
    flexGrow: 1,
    flexWrap: 'nowrap',
    justifyContent: 'space-between',
  },
});

const propTypes = {
  RealmSearchBar: {
    searchString: PropTypes.string.isRequired,
  },
  RealmFilterBar: {
    filterString: PropTypes.string.isRequired,
  },
  RealmTable: {
    data: PropTypes.arrayOf(PropTypes.shape({ id: PropTypes.string.isRequired })).isRequired,
  },
  RealmTableRow: {
    item: PropTypes.shape({ id: PropTypes.string.isRequired }).isRequired,
  },
};
<|endoftext|>


<commit_before>import watchAlarm from '../static/watchAlarm.mp3'
import gongAlarm from '../static/gongAlarm.mp3'

import {storageAvailable, populateStorage} from './storageHandler'

export function createAlarmPicker () {
  const dropdown = document.createElement('select')
  dropdown.setAttribute('id', 'alarmDropdown')

  const watchOption = document.createElement('option')
  watchOption.setAttribute('value', 'watchAlarm')
  watchOption.innerHTML = 'Watch Alarm'

  const gongOption = document.createElement('option')
  gongOption.setAttribute('value', 'gongAlarm')
  gongOption.innerHTML = 'Gong'

  dropdown.appendChild(watchOption)
  dropdown.appendChild(gongOption)

  dropdown.addEventListener('change', function (event, optionalUrl) {
    if (document.getElementById('alarmDropdown').value === 'watchAlarm') {
      chooseAlarmSound(watchAlarm)
    } else if (document.getElementById('alarmDropdown').value === 'gong') {
      chooseAlarmSound(gongAlarm)
    } /* else if (document.getElementById('alarmDropdown').value === 'userInputAlarm') {
      chooseAlarmSound(optionalUrl)
    } */

    if (storageAvailable('localStorage') && window.localStorage.getItem('pomodoro')) {
      populateStorage()
    }
  })

  return dropdown
}

export function chooseAlarmSound (name) {
  const alarm = document.getElementById('alarm')
  alarm.setAttribute('src', name)
}

export function addAlarm (URL, fileName) {
  chooseAlarmSound(URL)

  // How to get the objectURL to the event listener on the dropdown menu?
  const alarmDropdown = document.getElementById('alarmDropdown')
  const userAlarm = document.createElement('option')

  userAlarm.setAttribute('value', 'userInputAlarm')
  // use String.replace to clean up the fileName here
  userAlarm.innerHTML = fileName

  alarmDropdown.appendChild(userAlarm)
}
<commit_msg>moved code to set the alarm sound to seperate function<commit_after>import watchAlarm from '../static/watchAlarm.mp3'
import gongAlarm from '../static/gongAlarm.mp3'

import {storageAvailable, populateStorage} from './storageHandler'

export function createAlarmPicker () {
  const dropdown = document.createElement('select')
  dropdown.setAttribute('id', 'alarmDropdown')

  const watchOption = document.createElement('option')
  watchOption.setAttribute('value', 'watchAlarm')
  watchOption.innerHTML = 'Watch Alarm'

  const gongOption = document.createElement('option')
  gongOption.setAttribute('value', 'gongAlarm')
  gongOption.innerHTML = 'Gong'

  dropdown.appendChild(watchOption)
  dropdown.appendChild(gongOption)

  dropdown.addEventListener('change', setAlarm)

  return dropdown
}

function setAlarm (alarmID) {
  if (document.getElementById('alarmDropdown').value === 'watchAlarm') {
    chooseAlarmSound(watchAlarm)
  } else if (document.getElementById('alarmDropdown').value === 'gong') {
    chooseAlarmSound(gongAlarm)
  }
  // else if (document.getElementById('alarmDropdown').value === 'userInputAlarm') {
  //   chooseAlarmSound(optionalUrl)
  // }

  if (storageAvailable('localStorage') && window.localStorage.getItem('pomodoro')) {
    populateStorage()
  }
}

export function chooseAlarmSound (name) {
  const alarm = document.getElementById('alarm')
  alarm.setAttribute('src', name)
}

export function addAlarm (URL, fileName) {
  chooseAlarmSound(URL)

  // How to get the objectURL to the event listener on the dropdown menu?
  const alarmDropdown = document.getElementById('alarmDropdown')
  const userAlarm = document.createElement('option')

  userAlarm.setAttribute('value', 'userInputAlarm')
  // use String.replace to clean up the fileName here
  userAlarm.innerHTML = fileName

  alarmDropdown.appendChild(userAlarm)
}
<|endoftext|>


<commit_before>// @flow
import React, {PropTypes} from 'react'
import {WebView} from 'react-native'

import type {StoryType} from './types'

export default function NewsItemView({story: {content, title}}: {source: string, story: StoryType}) {
  content = `

${title}

    ${content}
  `
  return (
    <WebView source={{html: content}} />
  )
}

NewsItemView.propTypes = {
  story: PropTypes.shape({
    author: PropTypes.string,
    categories: PropTypes.arrayOf(PropTypes.string),
    content: PropTypes.string,
    link: PropTypes.string,
    publishedDate: PropTypes.string,
    title: PropTypes.string,
  }).isRequired,
}
<commit_msg>remove PropTypes from NewsItem<commit_after>// @flow
import React, {PropTypes} from 'react'
import {WebView} from 'react-native'

import type {StoryType} from './types'

export default function NewsItemView({story: {content, title}}: {source: string, story: StoryType}) {
  content = `

${title}

    ${content}
  `
  return (
    <WebView source={{html: content}} />
  )
}
<|endoftext|>


<commit_before>/**
 * FIELDS
 *
 * Angular-powered input components go into this file.
 */

var module = angular.module('fields', []);

/**
 * Combobox input element.
 *
 * Adds a autocomplete-like dropdown to an input element.
 *
 * @param {string} rpCombobox Pass a function that takes a string and returns
 *   the matching autocompletions.
 */
module.directive('rpCombobox', [function () {
  return {
    restrict: 'A',
    require: '?ngModel',
    link: function (scope, el, attrs, ngModel) {
      var keyCursor = -1;

      el.wrap('
');
      el.attr('autocomplete', 'off');
      var cplEl = $('

').hide();
      el.parent().append(cplEl);

      // Explicit select button
      if (attrs.rpComboboxSelect) {
        var selectEl = $('
').appendTo(el.parent());
        selectEl.addClass('select');
        selectEl.mousedown(function (e) {
          e.preventDefault();
        });
        selectEl.click(function () {
          setCompletions(scope.$eval(attrs.rpCombobox)());
          if (cplEl.is(':visible')) {
            el.blur();
          } else {
            setCompletions(scope.$eval(attrs.rpCombobox)());
            el.focus();
          }
        });
      }

      // Listen for keyup events to enable binding
      el.keyup(function(e) {
        if (e.which >= 37 && e.which <= 40) return;
        if (e.which === 13) return;

        updateCompletions();
      });

      el.keydown(function (e) {
        if (e.which === 38 || e.which === 40) {
          if (!cplEl.children().length) {
            updateCompletions();
          }
          e.preventDefault();

          if (e.which === 38) keyCursor--;
          else keyCursor++;

          updateKeyCursor();
        } else if (e.which === 13) {
          e.preventDefault();

          var curEl = cplEl.find('li.cursor');
          if (curEl.length === 1) {
            selectCompletion(curEl);
          }
        }
      });

      el.focus(function() {
        keyCursor = -1;
        setVisible(!!cplEl.children().length);
      });

      el.blur(function() {
        setVisible(false);
      });

      cplEl.mousedown(function (e) {
        e.preventDefault();
      });

      function setVisible(to) {
        el.parent()[to ? 'addClass' : 'removeClass']('active');
        cplEl[to ? 'fadeIn' : 'fadeOut']('fast');
      }

      function updateCompletions() {
        var match = ngModel.$viewValue;
        var re = new RegExp('('+match+')', 'i');

        var completions = match.length ? scope.$eval(attrs.rpCombobox)(match) : [];

        // By fading out without updating the completions we get a smoother effect
        if (!completions.length) {
          setVisible(false);
          return;
        }

        setCompletions(completions, re);
        setVisible(!!cplEl.children().length);
      }

      function setCompletions(completions, re) {
        cplEl.empty();
        keyCursor = -1;
        completions.forEach(function (val) {
          if (re) val = val.replace(re, '$1');
          var completion = $('
'+val+'
');
          el.parent().find('.completions').append(completion);
        });
      }

      function updateKeyCursor() {
        var opts = cplEl.find('li');
        keyCursor = Math.max(keyCursor, 0);
        keyCursor = Math.min(keyCursor, opts.length - 1);
        opts.removeClass('cursor');
        opts.eq(keyCursor).addClass('cursor');
      }

      function selectCompletion(liEl) {
        var val = $(liEl).text();
        scope.$apply(function () {
          el.val(val);
          ngModel.$setViewValue(val);
          setVisible(false);
        });
      }

      cplEl.on('click', 'li', function () {
        selectCompletion(this);
      });
    }
  };
}]);
<commit_msg>Fix down arrow crash in comcobox.<commit_after>/**
 * FIELDS
 *
 * Angular-powered input components go into this file.
 */

var module = angular.module('fields', []);

/**
 * Combobox input element.
 *
 * Adds a autocomplete-like dropdown to an input element.
 *
 * @param {string} rpCombobox Pass a function that takes a string and returns
 *   the matching autocompletions.
 */
module.directive('rpCombobox', [function () {
  return {
    restrict: 'A',
    require: '?ngModel',
    link: function (scope, el, attrs, ngModel) {
      var keyCursor = -1;

      el.wrap('
');
      el.attr('autocomplete', 'off');
      var cplEl = $('

').hide();
      el.parent().append(cplEl);

      // Explicit select button
      if (attrs.rpComboboxSelect) {
        var selectEl = $('
').appendTo(el.parent());
        selectEl.addClass('select');
        selectEl.mousedown(function (e) {
          e.preventDefault();
        });
        selectEl.click(function () {
          setCompletions(scope.$eval(attrs.rpCombobox)());
          if (cplEl.is(':visible')) {
            el.blur();
          } else {
            setCompletions(scope.$eval(attrs.rpCombobox)());
            el.focus();
          }
        });
      }

      // Listen for keyup events to enable binding
      el.keyup(function(e) {
        if (e.which >= 37 && e.which <= 40) return;
        if (e.which === 13) return;

        updateCompletions();
      });

      el.keydown(function (e) {
        if (e.which === 38 || e.which === 40) {
          if (!cplEl.children().length) {
            updateCompletions();
          }
          e.preventDefault();

          if (e.which === 38) keyCursor--;
          else keyCursor++;

          updateKeyCursor();
        } else if (e.which === 13) {
          e.preventDefault();

          var curEl = cplEl.find('li.cursor');
          if (curEl.length === 1) {
            selectCompletion(curEl);
          }
        }
      });

      el.focus(function() {
        keyCursor = -1;
        setVisible(!!cplEl.children().length);
      });

      el.blur(function() {
        setVisible(false);
      });

      cplEl.mousedown(function (e) {
        e.preventDefault();
      });

      function setVisible(to) {
        el.parent()[to ? 'addClass' : 'removeClass']('active');
        cplEl[to ? 'fadeIn' : 'fadeOut']('fast');
      }

      function updateCompletions() {
        var match = ngModel.$viewValue;
        if (!match) return;
        var re = new RegExp('('+match+')', 'i');

        var completions = match.length ? scope.$eval(attrs.rpCombobox)(match) : [];

        // By fading out without updating the completions we get a smoother effect
        if (!completions.length) {
          setVisible(false);
          return;
        }

        setCompletions(completions, re);
        setVisible(!!cplEl.children().length);
      }

      function setCompletions(completions, re) {
        cplEl.empty();
        keyCursor = -1;
        completions.forEach(function (val) {
          if (re) val = val.replace(re, '$1');
          var completion = $('
'+val+'
');
          el.parent().find('.completions').append(completion);
        });
      }

      function updateKeyCursor() {
        var opts = cplEl.find('li');
        keyCursor = Math.max(keyCursor, 0);
        keyCursor = Math.min(keyCursor, opts.length - 1);
        opts.removeClass('cursor');
        opts.eq(keyCursor).addClass('cursor');
      }

      function selectCompletion(liEl) {
        var val = $(liEl).text();
        scope.$apply(function () {
          el.val(val);
          ngModel.$setViewValue(val);
          setVisible(false);
        });
      }

      cplEl.on('click', 'li', function () {
        selectCompletion(this);
      });
    }
  };
}]);
<|endoftext|>


<commit_before>(function(angular, document){
  'use strict';
  // Please see ./intercept.js for the functions that intercept these connections (outbound AND inbound).

  angular.module('butcher').factory('meat', ['$http', '$rootScope', '$log', function($http, $rootScope, $log) {
    $log.log("meat factory is running");

    var meat = {};
    var timeout = 60000; // timeout in milliseconds

    meat.get = function(url, params, isArray, happy_meat, sad_meat) { // sad_meat is an optional function to execute if there was a failure
      return $http({ // return for the sake of the promise
        'method': 'GET',
        'url': url, // this can't have a query string on it
        'params': params, // an object literal
        'isArray': isArray,
        'timeout': timeout
      })
      .then(
        function(obj) { happy_meat(obj); }, // Success
        function(obj) { if(sad_meat) { sad_meat(obj); } } // Fails via intercept.js, but you can also send in a fail func
      );
    };

    meat.read = meat.get; // map "get" to "read"

    return meat;
  }]);

}(window.angular, document));
<commit_msg>returning the obj in meat.get() for promise chaining<commit_after>(function(angular, document){
  'use strict';
  // Please see ./intercept.js for the functions that intercept these connections (outbound AND inbound).

  angular.module('butcher').factory('meat', ['$http', '$rootScope', '$log', function($http, $rootScope, $log) {
    $log.log("meat factory is running");

    var meat = {};
    var timeout = 60000; // timeout in milliseconds

    meat.get = function(url, params, isArray, happy_meat, sad_meat) { // sad_meat is an optional function to execute if there was a failure
      return $http({ // return for the sake of the promise
        'method': 'GET',
        'url': url, // this can't have a query string on it
        'params': params, // an object literal
        'isArray': isArray,
        'timeout': timeout
      })
      .then(
        function(obj) { happy_meat(obj); return(obj); }, // Success
        function(obj) { if(sad_meat) { sad_meat(obj); } } // Fails via intercept.js, but you can also send in a fail func
      );
    };

    meat.read = meat.get; // map "get" to "read"

    return meat;
  }]);

}(window.angular, document));
<|endoftext|>


<commit_before>module.exports = {
    PLEASE_SELECT: '请选择',
    PLEASE_INPUT: '请输入',
    NO_MATCH: '无匹配项目',
    CAL_MONDAY: '一',
    CAL_TUESDAY: '二',
    CAL_WENSDAY: '三',
    CAL_THURSDAY: '四',
    CAL_FRIDAY: '五',
    CAL_SATURDAY: '六',
    CAL_SUNDAY: '日',
    CONFIRM: '确定',
    ALL: '全部',
    UPLOAD: '上传',
    DROPDOWN_LIST: '下拉菜单',
    MULTILEVEL_MENU: '多级菜单',
    GOTO: '跳至',
    PAGE_NEXT: '下一页',
    PAGE_PREV: '上一页',
    CANCEL: '取消',
    NOTICE: '提示',
    ITEM_PAGE: '条/页',
    TOTAL: '共',
    ITEMS: '条',
    PAGE: '页',
    FORMAT_ERROR: '格式错误',
    VALUE_ERROR: '错误的值',
    LENGTH_ERROR: '长度错误',
}<commit_msg>fix: i18n tweaks<commit_after>module.exports = {
    PLEASE_SELECT: '请选择',
    PLEASE_INPUT: '请输入',
    NO_MATCH: '无匹配项目',
    CAL_MONDAY: '一',
    CAL_TUESDAY: '二',
    CAL_WENSDAY: '三',
    CAL_THURSDAY: '四',
    CAL_FRIDAY: '五',
    CAL_SATURDAY: '六',
    CAL_SUNDAY: '日',
    CONFIRM: '确定',
    ALL: '全部',
    UPLOAD: '上传',
    DROPDOWN_LIST: '下拉菜单',
    MULTILEVEL_MENU: '多级菜单',
    GOTO: '跳至',
    PAGE_NEXT: '下一页',
    PAGE_PREV: '上一页',
    CANCEL: '取消',
    NOTICE: '提示',
    ITEM_PAGE: '条/页',
    TOTAL: '共',
    ITEMS: '条',
    PAGE: '页',
    FORMAT_ERROR: '格式错误',
    VALUE_ERROR: '值错误',
    LENGTH_ERROR: '长度错误',
}<|endoftext|>


<commit_before>var ko = require('knockout')
var _ = require('lodash')
var ajax = require('basic-ajax')
var Endpoints = require('../endpoint-builder')
var getUrlParameter = require('../get-url-parameter')
var cookies = require('../cookies')
var OpeningTime = require('./OpeningTime')
var Address = require('./Address')
var adminUrls = require('../admin-urls')

function Service (data) {
  var self = this
  self.serviceProviderId = data.serviceProviderId
  self.id = ko.observable(data.key)

  self.name = data.name
  self.info = ko.observable(data.info)

  var tags = data.tags !== null ? data.tags.join(', ') : ''

  self.tags = ko.observable(tags)
  self.openingTimes = ko.observableArray(data.openingTimes.map(ot => new OpeningTime(ot)))
  self.address = new Address(data.address)

  self.savedName = ko.observable(data.name)
  self.savedInfo = ko.observable(data.info)
  self.savedTags = ko.observable(tags)
  self.savedOpeningTimes = ko.observableArray(data.openingTimes.map(ot => new OpeningTime(ot)))
  self.savedAddress = new Address(data.address)

  self.isEditing = ko.observable(false)
  self.message = ko.observable()
  self.endpoints = new Endpoints()
  self.listeners = ko.observableArray()

  self.editServiceUrl = adminUrls.serviceProviderServicesEdit + '?providerId=' + data.serviceProviderId + '&serviceId=' + self.id()

  self.edit = function () {
    self.isEditing(true)
  }

  self.cancelEdit = function () {
    self.restoreFields()
  }

  self.restoreFields = function () {
    self.isEditing(false)
    self.info(self.savedInfo())
    self.tags(self.savedTags())

    var restoredOpeningTimes = _.map(self.savedOpeningTimes(), function (ot) {
      return new OpeningTime({
        'day': ot.day(),
        'startTime': ot.startTime(),
        'endTime': ot.endTime()
      })
    })
    self.openingTimes(restoredOpeningTimes)

    self.address.cancel()
  }

  self.newOpeningTime = function () {
    var openingTimes = self.openingTimes()
    openingTimes.push(new OpeningTime({
      'day': '',
      'startTime': '',
      'endTime': ''
    }))
    self.openingTimes(openingTimes)
  }

  self.removeOpeningTime = function (openingTimeToRemove) {
    var remaining = _.filter(self.openingTimes(), function (o) {
      return o.day() !== openingTimeToRemove.day()
          || o.startTime() !== openingTimeToRemove.startTime()
          || o.endTime() !== openingTimeToRemove.endTime()
    })

    self.openingTimes(remaining)
  }

  self.save = function () {
    var endpoint = self.endpoints.serviceProviders(self.serviceProviderId).services(self.id()).build()
    var headers = {
      'content-type': 'application/json',
      'session-token': cookies.get('session-token')
    }
    var tags = []
    if (self.tags().length > 0) tags = self.tags().split(',').map(t => t.trim())

    var model = JSON.stringify({
      'Info': self.info(),
      'Tags': tags,
      'OpeningTimes': self.openingTimes().map(openingTime => {
        return {
          'StartTime': openingTime.startTime(),
          'EndTime': openingTime.endTime(),
          'Day': openingTime.day()
        }
      }),
      'Address': {
        'Street1': self.address.street1(),
        'Street2': self.address.street2(),
        'Street3': self.address.street3(),
        'Street4': self.address.street4(),
        'City': self.address.city(),
        'Postcode': self.address.postcode()
      }
    })

    ajax.put(endpoint,
      headers,
      model
    ).then(function (result) {
      self.isEditing(false)
      self.listeners().forEach(l => {
        l.serviceSaved(self)
      })
    }, function (error) {
      var response = JSON.parse(error.response)
      self.message(response.messages.join('
'))
    })
  }

  self.deleteService = function () {
    var endpoint = self.endpoints.serviceProviders(getUrlParameter.parameter('key')).services(self.id()).build()
    var headers = {
      'content-type': 'application/json',
      'session-token': cookies.get('session-token')
    }
    ajax.delete(endpoint, headers, JSON.stringify({}))
    .then(function (result) {
      _.forEach(self.listeners(), function (listener) {
        listener.deleteService(self)
      })
    }, function (error) {

    })
  }

  self.addListener = function (listener) {
    self.listeners().push(listener)
  }
}

module.exports = Service
<commit_msg>use lodash map<commit_after>var ko = require('knockout')
var _ = require('lodash')
var ajax = require('basic-ajax')
var Endpoints = require('../endpoint-builder')
var getUrlParameter = require('../get-url-parameter')
var cookies = require('../cookies')
var OpeningTime = require('./OpeningTime')
var Address = require('./Address')
var adminUrls = require('../admin-urls')

function Service (data) {
  var self = this
  self.serviceProviderId = data.serviceProviderId
  self.id = ko.observable(data.key)

  self.name = data.name
  self.info = ko.observable(data.info)

  var tags = data.tags !== null ? data.tags.join(', ') : ''

  self.tags = ko.observable(tags)
  self.openingTimes = ko.observableArray(_.map(data.openingTimes, function (ot) {
    return new OpeningTime(ot)
  }))
  self.address = new Address(data.address)

  self.savedName = ko.observable(data.name)
  self.savedInfo = ko.observable(data.info)
  self.savedTags = ko.observable(tags)
  self.savedOpeningTimes = ko.observableArray(_.map(data.openingTimes, function (ot) {
    return new OpeningTime(ot)
  }))
  self.savedAddress = new Address(data.address)

  self.isEditing = ko.observable(false)
  self.message = ko.observable()
  self.endpoints = new Endpoints()
  self.listeners = ko.observableArray()

  self.editServiceUrl = adminUrls.serviceProviderServicesEdit + '?providerId=' + data.serviceProviderId + '&serviceId=' + self.id()

  self.edit = function () {
    self.isEditing(true)
  }

  self.cancelEdit = function () {
    self.restoreFields()
  }

  self.restoreFields = function () {
    self.isEditing(false)
    self.info(self.savedInfo())
    self.tags(self.savedTags())

    var restoredOpeningTimes = _.map(self.savedOpeningTimes(), function (ot) {
      return new OpeningTime({
        'day': ot.day(),
        'startTime': ot.startTime(),
        'endTime': ot.endTime()
      })
    })
    self.openingTimes(restoredOpeningTimes)

    self.address.cancel()
  }

  self.newOpeningTime = function () {
    var openingTimes = self.openingTimes()
    openingTimes.push(new OpeningTime({
      'day': '',
      'startTime': '',
      'endTime': ''
    }))
    self.openingTimes(openingTimes)
  }

  self.removeOpeningTime = function (openingTimeToRemove) {
    var remaining = _.filter(self.openingTimes(), function (o) {
      return o.day() !== openingTimeToRemove.day()
          || o.startTime() !== openingTimeToRemove.startTime()
          || o.endTime() !== openingTimeToRemove.endTime()
    })

    self.openingTimes(remaining)
  }

  self.save = function () {
    var endpoint = self.endpoints.serviceProviders(self.serviceProviderId).services(self.id()).build()
    var headers = {
      'content-type': 'application/json',
      'session-token': cookies.get('session-token')
    }
    var tags = []
    if (self.tags().length > 0) tags = self.tags().split(',').map(t => t.trim())

    var model = JSON.stringify({
      'Info': self.info(),
      'Tags': tags,
      'OpeningTimes': self.openingTimes().map(openingTime => {
        return {
          'StartTime': openingTime.startTime(),
          'EndTime': openingTime.endTime(),
          'Day': openingTime.day()
        }
      }),
      'Address': {
        'Street1': self.address.street1(),
        'Street2': self.address.street2(),
        'Street3': self.address.street3(),
        'Street4': self.address.street4(),
        'City': self.address.city(),
        'Postcode': self.address.postcode()
      }
    })

    ajax.put(endpoint,
      headers,
      model
    ).then(function (result) {
      self.isEditing(false)
      self.listeners().forEach(l => {
        l.serviceSaved(self)
      })
    }, function (error) {
      var response = JSON.parse(error.response)
      self.message(response.messages.join('
'))
    })
  }

  self.deleteService = function () {
    var endpoint = self.endpoints.serviceProviders(getUrlParameter.parameter('key')).services(self.id()).build()
    var headers = {
      'content-type': 'application/json',
      'session-token': cookies.get('session-token')
    }
    ajax.delete(endpoint, headers, JSON.stringify({}))
    .then(function (result) {
      _.forEach(self.listeners(), function (listener) {
        listener.deleteService(self)
      })
    }, function (error) {

    })
  }

  self.addListener = function (listener) {
    self.listeners().push(listener)
  }
}

module.exports = Service
<|endoftext|>


<commit_before>////////////////////  dialog  ////////////////////
/**
 * Demo: http://cmui.net/demo/v2/theme/baixing/dialog.php
 * API Doc: CMUI/doc#3
 */

void function (window, CMUI) {
	'use strict'

	// namespace
	var moduleName = 'dialog'
	var module = CMUI[moduleName] = CMUI[moduleName] || {}

	// const
	var CLS = 'cm-dialog'
	var TMPL = [
		'<% var tagName = data.tag || \'div\' %>',
		'<<%- tagName %> class="cm-dialog"',
			'<% if (data.id) { %>',
			'id="<%= data.id %>"',
			'<% } %>',
		'>',
			'<% if (data.img) { %>',
			'
',
				'<img src="<%= data.img %>">',
			'
',
			'<% } %>',
			'<% if (!data.img || data.title) { %>',
			'',
				'
<%- data.title || \'提示\' %>
',
			'',
			'<% } %>',
			'<% if (data.content) { %>',
			'<%- data.content %>',
			'<% } %>',
			'<% if (_.isObject(data.btn)) { %>',
			'<%',
				'var btn = data.btn',
				'var primary = btn.primary',
				'var minor = btn.minor',
			'%>',
			'',
				'<% if (_.isObject(primary)) { %>',
					'<%',
						'var btnLabel = primary.html || \'确定\'',
						'var btnClass = _.isArray(primary.className) ?',
							'primary.className.join(\' \') :',
							'(_.isString(primary.className) ?',
								'primary.className :',
								'\'cm-btn cm-btn-primary\')',
					'%>',
					'<% if (!primary.tag || primary.tag === \'button\') { %>',
						'<button',
							'class="<%= btnClass %>"',
							'<% if (primary.action) { %>',
								'data-action="<%= primary.action %>"',
							'<% } %>',
							'<% if (primary.hideDialog) { %>',
								'data-cm-dialog-btn-hide-dialog="<%= 1 %>"',
							'<% } %>',
						'><%- btnLabel %></button>',
					'<% } else if (primary.tag === \'a\') { %>',
						'<a',
							'class="<%= btnClass %>"',
							'href="<%= primary.link || \'#\' %>"',
							'<% if (primary.action) { %>',
								'data-action="<%= primary.action %>"',
							'<% } %>',
							'<% if (primary.hideDialog) { %>',
								'data-cm-dialog-btn-hide-dialog="<%= 1 %>"',
							'<% } %>',
						'><%- btnLabel %>',
					'<% } else if (primary.tag === \'input\') { %>',
						'<input',
							'class="<%= btnClass %>"',
							'<% if (primary.action) { %>',
								'data-action="<%= primary.action %>"',
							'<% } %>',
							'<% if (primary.hideDialog) { %>',
								'data-cm-dialog-btn-hide-dialog="<%= 1 %>"',
							'<% } %>',
							'value="<%= btnLabel %>"',
						'>',
					'<% } %>',
				'<% } %>',
				'<% if (_.isObject(minor)) { %>',
					'<%',
						'var btnLabel = minor.html || \'取消\'',
						'var btnClass = _.isArray(minor.className) ?',
							'minor.className.join(\' \') :',
							'(_.isString(minor.className) ?',
								'minor.className :',
								'\'cm-btn cm-btn-primary cm-btn-bordered\'',
							')',
					'%>',
					'<% if (!minor.tag || minor.tag === \'button\') { %>',
						'<button',
							'class="<%= btnClass %> cm-btn-minor"',
							'<% if (minor.action) { %>',
								'data-action="<%= minor.action %>"',
							'<% } %>',
							'<% if (minor.hideDialog) { %>',
								'data-cm-dialog-btn-hide-dialog',
							'<% } %>',
						'><%- btnLabel %></button>',
					'<% } else if (minor.tag === \'a\') { %>',
						'<a',
							'class="<%= btnClass %> cm-btn-minor"',
							'href="<%= minor.link || \'#\' %>"',
							'<% if (minor.action) { %>',
								'data-action="<%= minor.action %>"',
							'<% } %>',
							'<% if (minor.hideDialog) { %>',
								'data-cm-dialog-btn-hide-dialog="<%= 1 %>"',
							'<% } %>',
						'><%- btnLabel %>',
					'<% } else if (minor.tag === \'input\') { %>',
						'<input',
							'class="<%= btnClass %> cm-btn-minor"',
							'<% if (minor.action) { %>',
								'data-action="<%= minor.action %>"',
							'<% } %>',
							'<% if (minor.hideDialog) { %>',
								'data-cm-dialog-btn-hide-dialog',
							'<% } %>',
							'value="<%= btnLabel %>"',
						'>',
					'<% } %>',
				'<% } %>',
			'',
			'<% } %>',
		'</<%- tagName %>>'
	].join('\n')

	var _stack = []
	var _isEventReady = false
	var _isTemplateReady = false

	var _root = document.documentElement
	var _body = document.body

	// action
	gearbox.action.add({
		'cm-dialog-hide': function () {
			var $this = $(this)
			// means current action is triggered by user click, not by js
			if (this !== window) {
				var $dialog = $this.closest('.' + CLS)
				if (_.last(_stack).elem === $dialog[0]) {
					CMUI.dialog.hide()
				} else {
					return false
				}
			} else {
				CMUI.dialog.hide()
			}
		},
	})

	// api
	function show(elem, options) {
		_prepareEvent()
		var $elem = $(elem).first()	// only allow to show one dialog
		if (!$elem.hasClass(CLS)) return false
		var dialog = new Dialog($elem, options)
		dialog.show()
	}
	function hide() {
		if (!_stack.length) return false
		var dialog = _.last(_stack)
		dialog.hide()
	}
	function create(config) {
		_prepareTemplate()
		if (!_.isObject(config)) config = {}
		var html = gearbox.template.render('cm-dialog', config)
		var $dialog = $(html).appendTo(gearbox.dom.$body)
		return $dialog[0]
	}

	// util
	function _prepareEvent() {
		if (_isEventReady) return
		_isEventReady = true
		gearbox.dom.$win.on('resize', function () {
			// console.log('resize')
			if (!_stack.length) return
			var dialog = _.last(_stack)
			dialog.adjust()
		})
		gearbox.dom.$body.on('click', '[data-cm-dialog-btn-hide-dialog]', function () {
			gearbox.action.trigger('cm-dialog-hide', this)
		})
	}
	function _prepareTemplate() {
		if (_isTemplateReady) return
		_isTemplateReady = true
		gearbox.template.add('cm-dialog', TMPL)
	}

	// class
	function Dialog($elem, options) {
		this.$elem = $elem
		this.elem = $elem[0]
		this._init(options)
	}
	_.extend(Dialog.prototype, {
		_init: function (options) {
			var $elem = this.$elem
			// move to root of body
			if (!$elem.parent().is('body')) $elem.appendTo(gearbox.dom.$body)

			// handle options
			if (!_.isObject(options)) options = {}
			var autoHideDelay = gearbox.str.toFloat(options.autoHideDelay)
			if (autoHideDelay > 0) {
				// convert ms to a integer in seconds
				this.autoHideDelay = Math.ceil(autoHideDelay / 1000)
			}
		},
		_prepareAutoHide: function () {
			var $elem = this.$elem
			// structure
			var html = [
				'
',
					'',
						this.autoHideDelay,
					'',
					' 秒后自动关闭……',
				'
',
			].join('')
			this.$autoHide = $(html)
			this.$autoHideCountdown = this.$autoHide.find('.cm-dialog-auto-hide-countdown')
			$elem.append(this.$autoHide)

			// timer
			var _this = this
			this.autoHideRemain = this.autoHideDelay
			this.timer = setInterval(function () {
				_this.autoHideRemain--
				if (_this.autoHideRemain > 0) {
					_this.$autoHideCountdown.html(_this.autoHideRemain)
				} else {
					// if it is visible, hide it
					if (_this === _.last(_stack)) _this.hide()
					_this._destroyAutoHide()
				}
			}, 1000)
		},
		_destroyAutoHide: function () {
			if (this.timer) clearInterval(this.timer)
			if (this.$autoHide) this.$autoHide.remove()
			delete this.$autoHide
			delete this.$autoHideCountdown
			delete this.autoHideDelay
			delete this.autoHideRemain
			delete this.timer
		},
		_prepareCloseBtn: function () {
			var $elem = this.$elem
			if ($elem.find('.cm-dialog-close-btn[data-action]').length) return
			var html = ''
			this.$elem.prepend(html)
		},
		_pos: function (onlyX) {
			var $elem = this.$elem
			var elem = $elem[0]

			// if its not current dialog, show it to get its actual size
			if ($elem.css('display') === 'none') {
				$elem.css({
					visibility: 'hidden',
					left: '-101%',
					top: 0,
				}).show()
			}

			var l = Math.round((_root.clientWidth - elem.offsetWidth) / 2)
			var t
			if (!onlyX) {
				// on ios, `doc.clientHeight` never change even when scrolling causes addr bar hidden,
				// so use `window.innerHeight` instead.
				var absT = Math.round((window.innerHeight * 0.95 - elem.offsetHeight) / 2)
				absT = absT < 5 ? 5 : absT
				t = absT + (_root.scrollTop || _body.scrollTop)
				// console.log('absT: ' + absT)
				// console.log('scrollTop: ' + (_root.scrollTop || _body.scrollTop))
				// console.log('t: ' + t)
			}

			var css = {left: l + 'px'}
			if (!onlyX) css.top = t + 'px'

			$elem.css(css)
		},
		show: function () {
			var $elem = this.$elem
			this._prepareCloseBtn()
			if (this.autoHideDelay) {
				this._prepareAutoHide()
			}

			// hide last dialog
			if (_stack.length) {
				var dialogLast = _.last(_stack)
				dialogLast.$elem.hide()
			}

			this._pos()
			$elem.css({ visibility: 'visible' }).show()

			if (!_stack.length) CMUI.mask.fadeIn()
			_stack.push(this)
		},
		hide: function () {
			_stack.pop()
			this.$elem.hide()
			this.destroy()

			// restore previous dialog
			if (_stack.length) {
				var dialogLast = _.last(_stack)
				dialogLast._pos()
				dialogLast.$elem.css({ visibility: 'visible' }).show()
			} else {
				CMUI.mask.fadeOut()
			}
		},
		destroy: function () {
			this._destroyAutoHide()
			delete this.$elem
			delete this.elem
		},
		adjust: function () {
			this._pos(true)
		},
	})

	// exports
	module.show = show
	module.hide = hide
	module.create = create

	module._stack = _stack

	// init
	CMUI._initModule(moduleName)

}(window, CMUI)
<commit_msg>Improve "dialog" widget - remove support of `input` tag for buttons.<commit_after>////////////////////  dialog  ////////////////////
/**
 * Demo: http://cmui.net/demo/v2/theme/baixing/dialog.php
 * API Doc: CMUI/doc#3
 */

void function (window, CMUI) {
	'use strict'

	// namespace
	var moduleName = 'dialog'
	var module = CMUI[moduleName] = CMUI[moduleName] || {}

	// const
	var CLS = 'cm-dialog'
	var TMPL = [
		'<% var tagName = data.tag || \'div\' %>',
		'<<%- tagName %> class="cm-dialog"',
			'<% if (data.id) { %>',
			'id="<%= data.id %>"',
			'<% } %>',
		'>',
			'<% if (data.img) { %>',
			'
',
				'<img src="<%= data.img %>">',
			'
',
			'<% } %>',
			'<% if (!data.img || data.title) { %>',
			'',
				'
<%- data.title || \'提示\' %>
',
			'',
			'<% } %>',
			'<% if (data.content) { %>',
			'<%- data.content %>',
			'<% } %>',
			'<% if (_.isObject(data.btn)) { %>',
			'<%',
				'var btn = data.btn',
				'var primary = btn.primary',
				'var minor = btn.minor',
			'%>',
			'',
				'<% if (_.isObject(primary)) { %>',
					'<%',
						'var btnLabel = primary.html || \'确定\'',
						'var btnClass = _.isArray(primary.className) ?',
							'primary.className.join(\' \') :',
							'(_.isString(primary.className) ?',
								'primary.className :',
								'\'cm-btn cm-btn-primary\')',
					'%>',
					'<% if (primary.tag === \'a\') { %>',
						'<a',
							'class="<%= btnClass %>"',
							'href="<%= primary.link || \'#\' %>"',
							'<% if (primary.action) { %>',
								'data-action="<%= primary.action %>"',
							'<% } %>',
							'<% if (primary.hideDialog) { %>',
								'data-cm-dialog-btn-hide-dialog="<%= 1 %>"',
							'<% } %>',
						'><%- btnLabel %>',
					'<% } else { %>',
						'<button',
							'class="<%= btnClass %>"',
							'<% if (primary.action) { %>',
								'data-action="<%= primary.action %>"',
							'<% } %>',
							'<% if (primary.hideDialog) { %>',
								'data-cm-dialog-btn-hide-dialog="<%= 1 %>"',
							'<% } %>',
						'><%- btnLabel %></button>',
					'<% } %>',
				'<% } %>',
				'<% if (_.isObject(minor)) { %>',
					'<%',
						'var btnLabel = minor.html || \'取消\'',
						'var btnClass = _.isArray(minor.className) ?',
							'minor.className.join(\' \') :',
							'(_.isString(minor.className) ?',
								'minor.className :',
								'\'cm-btn cm-btn-primary cm-btn-bordered\'',
							')',
					'%>',
					'<% if (minor.tag === \'a\') { %>',
						'<a',
							'class="<%= btnClass %>"',
							'href="<%= minor.link || \'#\' %>"',
							'<% if (minor.action) { %>',
								'data-action="<%= minor.action %>"',
							'<% } %>',
							'<% if (minor.hideDialog) { %>',
								'data-cm-dialog-btn-hide-dialog="<%= 1 %>"',
							'<% } %>',
						'><%- btnLabel %>',
					'<% } else { %>',
						'<button',
							'class="<%= btnClass %>"',
							'<% if (minor.action) { %>',
								'data-action="<%= minor.action %>"',
							'<% } %>',
							'<% if (minor.hideDialog) { %>',
								'data-cm-dialog-btn-hide-dialog',
							'<% } %>',
						'><%- btnLabel %></button>',
					'<% } %>',
				'<% } %>',
			'',
			'<% } %>',
		'</<%- tagName %>>'
	].join('\n')

	var _stack = []
	var _isEventReady = false
	var _isTemplateReady = false

	var _root = document.documentElement
	var _body = document.body

	// action
	gearbox.action.add({
		'cm-dialog-hide': function () {
			var $this = $(this)
			// means current action is triggered by user click, not by js
			if (this !== window) {
				var $dialog = $this.closest('.' + CLS)
				if (_.last(_stack).elem === $dialog[0]) {
					CMUI.dialog.hide()
				} else {
					return false
				}
			} else {
				CMUI.dialog.hide()
			}
		},
	})

	// api
	function show(elem, options) {
		_prepareEvent()
		var $elem = $(elem).first()	// only allow to show one dialog
		if (!$elem.hasClass(CLS)) return false
		var dialog = new Dialog($elem, options)
		dialog.show()
	}
	function hide() {
		if (!_stack.length) return false
		var dialog = _.last(_stack)
		dialog.hide()
	}
	function create(config) {
		_prepareTemplate()
		if (!_.isObject(config)) config = {}
		var html = gearbox.template.render('cm-dialog', config)
		var $dialog = $(html).appendTo(gearbox.dom.$body)
		return $dialog[0]
	}

	// util
	function _prepareEvent() {
		if (_isEventReady) return
		_isEventReady = true
		gearbox.dom.$win.on('resize', function () {
			// console.log('resize')
			if (!_stack.length) return
			var dialog = _.last(_stack)
			dialog.adjust()
		})
		gearbox.dom.$body.on('click', '[data-cm-dialog-btn-hide-dialog]', function () {
			gearbox.action.trigger('cm-dialog-hide', this)
		})
	}
	function _prepareTemplate() {
		if (_isTemplateReady) return
		_isTemplateReady = true
		gearbox.template.add('cm-dialog', TMPL)
	}

	// class
	function Dialog($elem, options) {
		this.$elem = $elem
		this.elem = $elem[0]
		this._init(options)
	}
	_.extend(Dialog.prototype, {
		_init: function (options) {
			var $elem = this.$elem
			// move to root of body
			if (!$elem.parent().is('body')) $elem.appendTo(gearbox.dom.$body)

			// handle options
			if (!_.isObject(options)) options = {}
			var autoHideDelay = gearbox.str.toFloat(options.autoHideDelay)
			if (autoHideDelay > 0) {
				// convert ms to a integer in seconds
				this.autoHideDelay = Math.ceil(autoHideDelay / 1000)
			}
		},
		_prepareAutoHide: function () {
			var $elem = this.$elem
			// structure
			var html = [
				'
',
					'',
						this.autoHideDelay,
					'',
					' 秒后自动关闭……',
				'
',
			].join('')
			this.$autoHide = $(html)
			this.$autoHideCountdown = this.$autoHide.find('.cm-dialog-auto-hide-countdown')
			$elem.append(this.$autoHide)

			// timer
			var _this = this
			this.autoHideRemain = this.autoHideDelay
			this.timer = setInterval(function () {
				_this.autoHideRemain--
				if (_this.autoHideRemain > 0) {
					_this.$autoHideCountdown.html(_this.autoHideRemain)
				} else {
					// if it is visible, hide it
					if (_this === _.last(_stack)) _this.hide()
					_this._destroyAutoHide()
				}
			}, 1000)
		},
		_destroyAutoHide: function () {
			if (this.timer) clearInterval(this.timer)
			if (this.$autoHide) this.$autoHide.remove()
			delete this.$autoHide
			delete this.$autoHideCountdown
			delete this.autoHideDelay
			delete this.autoHideRemain
			delete this.timer
		},
		_prepareCloseBtn: function () {
			var $elem = this.$elem
			if ($elem.find('.cm-dialog-close-btn[data-action]').length) return
			var html = ''
			this.$elem.prepend(html)
		},
		_pos: function (onlyX) {
			var $elem = this.$elem
			var elem = $elem[0]

			// if its not current dialog, show it to get its actual size
			if ($elem.css('display') === 'none') {
				$elem.css({
					visibility: 'hidden',
					left: '-101%',
					top: 0,
				}).show()
			}

			var l = Math.round((_root.clientWidth - elem.offsetWidth) / 2)
			var t
			if (!onlyX) {
				// on ios, `doc.clientHeight` never change even when scrolling causes addr bar hidden,
				// so use `window.innerHeight` instead.
				var absT = Math.round((window.innerHeight * 0.95 - elem.offsetHeight) / 2)
				absT = absT < 5 ? 5 : absT
				t = absT + (_root.scrollTop || _body.scrollTop)
				// console.log('absT: ' + absT)
				// console.log('scrollTop: ' + (_root.scrollTop || _body.scrollTop))
				// console.log('t: ' + t)
			}

			var css = {left: l + 'px'}
			if (!onlyX) css.top = t + 'px'

			$elem.css(css)
		},
		show: function () {
			var $elem = this.$elem
			this._prepareCloseBtn()
			if (this.autoHideDelay) {
				this._prepareAutoHide()
			}

			// hide last dialog
			if (_stack.length) {
				var dialogLast = _.last(_stack)
				dialogLast.$elem.hide()
			}

			this._pos()
			$elem.css({ visibility: 'visible' }).show()

			if (!_stack.length) CMUI.mask.fadeIn()
			_stack.push(this)
		},
		hide: function () {
			_stack.pop()
			this.$elem.hide()
			this.destroy()

			// restore previous dialog
			if (_stack.length) {
				var dialogLast = _.last(_stack)
				dialogLast._pos()
				dialogLast.$elem.css({ visibility: 'visible' }).show()
			} else {
				CMUI.mask.fadeOut()
			}
		},
		destroy: function () {
			this._destroyAutoHide()
			delete this.$elem
			delete this.elem
		},
		adjust: function () {
			this._pos(true)
		},
	})

	// exports
	module.show = show
	module.hide = hide
	module.create = create

	module._stack = _stack

	// init
	CMUI._initModule(moduleName)

}(window, CMUI)
<|endoftext|>


<commit_before>/*----------------------------------
 - plain.js -
 JS全般で利用されるグローバル処理定義
----------------------------------*/

import { Level } from 'constants.js'
import Param from 'variables'

// ## 日時ユーティリティ
import "moment/locale/ja"

// ## ログユーティリティ
// variables.jsでログ出力レベルを変更させる事が可能です。
export class Log {
  static debug(msg) {
    if (this.valid(Level.DEBUG)) this.console('[DEBUG] ', msg)
  }
  static info(msg) {
    if (this.valid(Level.INFO)) this.console('[INFO] ', msg)
  }
  static warn(msg) {
    if (this.valid(Level.WARN)) this.console('[WARN] ', msg)
  }
  static error(msg) {
    if (this.valid(Level.ERROR)) this.console('[ERROR] ', msg)
  }
  static valid(checkLevel) {
    return Param.System.logLevel <= checkLevel
  }
  static console(prefix, obj) {
    if (console) console.log(prefix, obj)
  }
}

// ## 非同期API要求ユーティリティ
// JSON形式での接続前提とします。
import axios from 'axios'
axios.defaults.headers['Content-Type'] = 'application/x-www-form-urlencoded'
axios.defaults.headers['Expires'] = -1
axios.defaults.headers['Cache-Control'] = 'no-cache,no-store,must-revalidate,max-age=-1,private'
axios.defaults.withCredentials = true
export class Ajax {
  // GET形式のPromiseを返します。
  static promiseGet(url, data = {}) {
    return axios.get(this.requestUrl(url), { params: data })
  }
  // GET形式でサーバ側へリクエスト処理をします。
  static get(url, data = {}, success = this.handleSuccess, failure = this.handleFailure) {
    this.promiseGet(url, data).then((res) => {
      if (success) success(res.data)
    }).catch((err) => {
      this.handlePreFailure(err)
      if (failure) failure(err)
    })
  }
  // POST形式のPromiseを返します。
  static promisePost(url, data = {}) {
    let form = new FormData()
    Object.keys(data).forEach((key) => form.append(key, data[key]))
    return axios.post(this.requestUrl(url), form)
  }
  // POST形式でサーバ側へリクエスト処理をします。
  static post(url, data = {}, success = this.handleSuccess, failure = this.handleFailure) {
    this.promisePost(url, data).then((res) => {
      if (success) success(res.data)
    }).catch((err) => {
      this.handlePreFailure(err)
      if (failure) failure(err)
    })
  }
  // アップロード形式のPromiseを返します。
  static promiseUpload(url, data = {}) {
    let form = new FormData()
    Object.keys(data).forEach((key) => form.append(key, data[key]))
    return axios(this.requestUrl(url), form)
  }
  // 指定したURLに対するアップロード処理をします。
  // 指定されたハッシュデータはFormDataへ紐付けられて送信されます。
  static upload(url, data = {}, success = this.handleSuccess, failure = this.handleFailure) {
    this.promiseUpload(url, data).then((res) => {
      if (success) success(res.body)
    }).catch((err) => {
      this.handlePreFailure(err)
      if (failure) failure(err)
    })
  }
  // 接続先URLパスを整形します。
  static requestUrl(url) {
    // for nocache
    let time = Date.now().toString()
    if (url && 0 <= url.indexOf('?')) {
      url += '&' + time
    } else {
      url += '?' + time
    }
    return url
  }
  // リクエスト成功時の標準処理を行います。
  static handleSuccess(data) { Log.info(data) }
  // リクエスト失敗時の事前処理を行います。
  static handlePreFailure(err) {
    const res = err.response
    if (res.status) {
      Log.warn("[" + res.status + "] " + res.statusText)
      switch (res.status) {
        case 0:
          Log.error('接続先が見つかりませんでした')
          break
        case 200:
          Log.error('戻り値の解析に失敗しました。JSON形式で応答が返されているか確認してください')
          break
        case 400:
          Log.warn(res.data)
          break
        case 401:
          Log.error('機能実行権限がありません')
          break
        default:
          Log.error(res.data)
      }
    } else {
      Log.error(err)
    }
  }
  // リクエスト失敗時の処理を行います。
  static handleFailure(err) {
    // nothing.
  }
}

// ## UI側セッションユーティリティ
// WebStorage(LocalStrage)利用を前提としたセッション概念を提供します。
export class Session {
  // ログインさせます。引数に与えたハッシュはWebStrage(Local)に保存されます。
  static login(sessionHash) {
    this.logout()
    this.valueSet(Param.Session.key, sessionHash)
  }
  // ログイン中のセッション情報を取得します。key未指定時はログイン情報を返します。
  static value(key = null) {
    let v = localStorage.getItem(key ? key : Param.Session.key)
    return v ? JSON.parse(v) : null
  }
  // セッション情報に値を設定します。リークしやすいため、安易な利用は避けてください。
  static valueSet(key, value) {
    localStorage.setItem(key, value ? JSON.stringify(value) : null)
  }
  // ログアウトさせます。個別にvalueSetした情報は忘れずにdeleteしてください。
  static logout() { this.remove() }
  // セッション情報の値を削除します。
  static remove(key = null) { localStorage.removeItem(key ? key : Param.Session.key) }
  // セッションを持っているか
  static hasSession() { return this.value() }
}

// ## フォーマットユーティリティ
export class Formatter {
  static format(type, value) {
    switch (type) {
      case "day":
        return value ? moment(value).format("YYYY/MM/DD") : ""
      case "amount":
        return (value || value === 0) ? value.toString().replace(/(\d)(?=(\d\d\d)+(?!\d))/g, '$1,') : ""
      default:
        return value.toString()
    }
  }
}

// ## グローバルユーティリティ
export class Utils {
  // URLの引数を解析してハッシュを返します
  static parseQuery(url = null) {
    let target = url ? url : window.location.search
    if (!target) return {}
    let idx = target.indexOf('?')
    let query = idx === -1 ? target : target.substring(idx + 1)
    let values = query.split('&')
    let ret = {}
    Array.from(values).map((v) => v.split('=')).filter((pair) => pair && pair.length === 2)
      .forEach((pair) => ret[decodeURIComponent(pair[0])] = decodeURIComponent(pair[1]))
    return ret
  }
  // 引数に与えたハッシュオブジェクトでネストされたものを「.」付の一階層へ変換します。(引数は上書きしません)
  // {a: {b: {c: 'd'}}} -> {'a.b.c': 'd'}
  // see http://stackoverflow.com/questions/19098797/fastest-way-to-flatten-un-flatten-nested-json-objects
  static flatten(data) {
    let ret = {}
    const recurse = (v, prop) => {
      if (Object(v) !== v) {
        ret[prop] = v
      } else if (Array.isArray(v)) {
        Array.from(v).forEach((value, i) => recurse(value, `prop[${i}]`))
        if (v.length === 0) ret[prop] = []
      } else {
        var empty = true
        Object.keys(v).forEach((key) => {
          let value = v[key]
          empty = false
          recurse(value, prop ? `${prop}.${key}` : key)
        })
        if (empty && prop) ret[prop] = {}
      }
    }
    recurse(data, "")
    return ret
  }
}
<commit_msg>Polish!<commit_after>/*----------------------------------
 - plain.js -
 JS全般で利用されるグローバル処理定義
----------------------------------*/

import { Level } from 'constants.js'
import Param from 'variables'

// ## 日時ユーティリティ
import "moment/locale/ja"

// ## ログユーティリティ
// variables.jsでログ出力レベルを変更させる事が可能です。
export class Log {
  static debug(msg) {
    if (this.valid(Level.DEBUG)) this.console('[DEBUG] ', msg)
  }
  static info(msg) {
    if (this.valid(Level.INFO)) this.console('[INFO] ', msg)
  }
  static warn(msg) {
    if (this.valid(Level.WARN)) this.console('[WARN] ', msg)
  }
  static error(msg) {
    if (this.valid(Level.ERROR)) this.console('[ERROR] ', msg)
  }
  static valid(checkLevel) {
    return Param.System.logLevel <= checkLevel
  }
  static console(prefix, obj) {
    if (console) console.log(prefix, obj)
  }
}

// ## 非同期API要求ユーティリティ
// リクエストは Form 戻り値はJSON形式を想定とします。
import axios from 'axios'
axios.defaults.headers['Content-Type'] = 'application/x-www-form-urlencoded'
axios.defaults.headers['Expires'] = -1
axios.defaults.headers['Cache-Control'] = 'no-cache,no-store,must-revalidate,max-age=-1,private'
axios.defaults.withCredentials = true
export class Ajax {
  // GET形式のPromiseを返します。
  static promiseGet(url, data = {}) {
    return axios.get(this.requestUrl(url), { params: data })
  }
  // GET形式でサーバ側へリクエスト処理をします。
  static get(url, data = {}, success = this.handleSuccess, failure = this.handleFailure) {
    this.promiseGet(url, data).then((res) => {
      if (success) success(res.data)
    }).catch((err) => {
      this.handlePreFailure(err)
      if (failure) failure(err)
    })
  }
  // POST形式のPromiseを返します。
  static promisePost(url, data = {}) {
    let form = new FormData()
    Object.keys(data).forEach((key) => form.append(key, data[key]))
    return axios.post(this.requestUrl(url), form)
  }
  // POST形式でサーバ側へリクエスト処理をします。
  static post(url, data = {}, success = this.handleSuccess, failure = this.handleFailure) {
    this.promisePost(url, data).then((res) => {
      if (success) success(res.data)
    }).catch((err) => {
      this.handlePreFailure(err)
      if (failure) failure(err)
    })
  }
  // アップロード形式のPromiseを返します。
  static promiseUpload(url, data = {}) {
    let form = new FormData()
    Object.keys(data).forEach((key) => form.append(key, data[key]))
    return axios(this.requestUrl(url), form)
  }
  // 指定したURLに対するアップロード処理をします。
  // 指定されたハッシュデータはFormDataへ紐付けられて送信されます。
  static upload(url, data = {}, success = this.handleSuccess, failure = this.handleFailure) {
    this.promiseUpload(url, data).then((res) => {
      if (success) success(res.body)
    }).catch((err) => {
      this.handlePreFailure(err)
      if (failure) failure(err)
    })
  }
  // 接続先URLパスを整形します。
  static requestUrl(url) {
    // for nocache
    let time = Date.now().toString()
    if (url && 0 <= url.indexOf('?')) {
      url += '&' + time
    } else {
      url += '?' + time
    }
    return url
  }
  // リクエスト成功時の標準処理を行います。
  static handleSuccess(data) { Log.info(data) }
  // リクエスト失敗時の事前処理を行います。
  static handlePreFailure(err) {
    const res = err.response
    if (res.status) {
      Log.warn("[" + res.status + "] " + res.statusText)
      switch (res.status) {
        case 0:
          Log.error('接続先が見つかりませんでした')
          break
        case 200:
          Log.error('戻り値の解析に失敗しました。JSON形式で応答が返されているか確認してください')
          break
        case 400:
          Log.warn(res.data)
          break
        case 401:
          Log.error('機能実行権限がありません')
          break
        default:
          Log.error(res.data)
      }
    } else {
      Log.error(err)
    }
  }
  // リクエスト失敗時の処理を行います。
  static handleFailure(err) {
    // nothing.
  }
}

// ## UI側セッションユーティリティ
// WebStorage(LocalStrage)利用を前提としたセッション概念を提供します。
export class Session {
  // ログインさせます。引数に与えたハッシュはWebStrage(Local)に保存されます。
  static login(sessionHash) {
    this.logout()
    this.valueSet(Param.Session.key, sessionHash)
  }
  // ログイン中のセッション情報を取得します。key未指定時はログイン情報を返します。
  static value(key = null) {
    let v = localStorage.getItem(key ? key : Param.Session.key)
    return v ? JSON.parse(v) : null
  }
  // セッション情報に値を設定します。リークしやすいため、安易な利用は避けてください。
  static valueSet(key, value) {
    localStorage.setItem(key, value ? JSON.stringify(value) : null)
  }
  // ログアウトさせます。個別にvalueSetした情報は忘れずにdeleteしてください。
  static logout() { this.remove() }
  // セッション情報の値を削除します。
  static remove(key = null) { localStorage.removeItem(key ? key : Param.Session.key) }
  // セッションを持っているか
  static hasSession() { return this.value() }
}

// ## フォーマットユーティリティ
export class Formatter {
  static format(type, value) {
    switch (type) {
      case "day":
        return value ? moment(value).format("YYYY/MM/DD") : ""
      case "amount":
        return (value || value === 0) ? value.toString().replace(/(\d)(?=(\d\d\d)+(?!\d))/g, '$1,') : ""
      default:
        return value.toString()
    }
  }
}

// ## グローバルユーティリティ
export class Utils {
  // URLの引数を解析してハッシュを返します
  static parseQuery(url = null) {
    let target = url ? url : window.location.search
    if (!target) return {}
    let idx = target.indexOf('?')
    let query = idx === -1 ? target : target.substring(idx + 1)
    let values = query.split('&')
    let ret = {}
    Array.from(values).map((v) => v.split('=')).filter((pair) => pair && pair.length === 2)
      .forEach((pair) => ret[decodeURIComponent(pair[0])] = decodeURIComponent(pair[1]))
    return ret
  }
  // 引数に与えたハッシュオブジェクトでネストされたものを「.」付の一階層へ変換します。(引数は上書きしません)
  // {a: {b: {c: 'd'}}} -> {'a.b.c': 'd'}
  // see http://stackoverflow.com/questions/19098797/fastest-way-to-flatten-un-flatten-nested-json-objects
  static flatten(data) {
    let ret = {}
    const recurse = (v, prop) => {
      if (Object(v) !== v) {
        ret[prop] = v
      } else if (Array.isArray(v)) {
        Array.from(v).forEach((value, i) => recurse(value, `prop[${i}]`))
        if (v.length === 0) ret[prop] = []
      } else {
        var empty = true
        Object.keys(v).forEach((key) => {
          let value = v[key]
          empty = false
          recurse(value, prop ? `${prop}.${key}` : key)
        })
        if (empty && prop) ret[prop] = {}
      }
    }
    recurse(data, "")
    return ret
  }
}
<|endoftext|>