<commit_before>/* eslint-disable react/forbid-prop-types */
/* eslint-disable no-undef */
/**
 * mSupply Mobile
 * Sustainable Solutions (NZ) Ltd. 2019
 */

import React from 'react';
import PropTypes from 'prop-types';
import DeviceInfo from 'react-native-device-info';
import { connect } from 'react-redux';
import { BluetoothStatus } from 'react-native-bluetooth-status';
import { AppState, View } from 'react-native';
import { Scheduler } from 'sussol-utilities';

import Settings from './settings/MobileAppSettings';
import Database from './database/BaseDatabase';
import { UIDatabase } from './database';
import { SETTINGS_KEYS } from './settings';

import { MainStackNavigator, Pages } from './navigation/Navigator';
import { ROUTES } from './navigation';
import { Synchroniser, PostSyncProcessor, SyncModal } from './sync';
import { migrateDataToVersion } from './dataMigration';
import { SyncAuthenticator, UserAuthenticator } from './authentication';

import { LoadingIndicatorContext } from './context/LoadingIndicatorContext';
import { selectTitle } from './selectors/supplierCredit';
import { selectIsSyncing } from './selectors/sync';
import { selectCurrentUser } from './selectors/user';
import { selectUsingVaccines } from './selectors/modules';

import { syncCompleteTransaction, setSyncError, openSyncModal } from './actions/SyncActions';
import { FinaliseActions } from './actions/FinaliseActions';
import { UserActions } from './actions';
import { SupplierCreditActions } from './actions/SupplierCreditActions';

import { Spinner } from './widgets';
import { ModalContainer, FinaliseModal, LoginModal } from './widgets/modals';
import { FirstUsePage } from './pages';
import { SupplierCredit } from './widgets/modalChildren/SupplierCredit';

import globalStyles, { SUSSOL_ORANGE } from './globalStyles';
import { BreachDisplay } from './widgets/modalChildren/BreachDisplay';
import { selectIsBreachModalOpen, selectBreachModalTitle } from './selectors/breach';
import { BreachActions } from './actions/BreachActions';
import { RowDetail } from './widgets/RowDetail';
import { PermissionActions } from './actions/PermissionActions';
import BleService from './bluetooth/BleService';
import TemperatureLogManager from './bluetooth/TemperatureLogManager';
import { DevBleManager } from './bluetooth/DevBleManager';
import SensorManager from './bluetooth/SensorManager';
import { VaccineDataAccess } from './bluetooth/VaccineDataAccess';
import { UtilService } from './database/utilities/utilService';
import { SensorDownloadActions } from './actions/Bluetooth/SensorDownloadActions';
import BreachManager from './bluetooth/BreachManager';

const SYNC_INTERVAL = 10 * 60 * 1000; // 10 minutes in milliseconds.
const AUTHENTICATION_INTERVAL = 10 * 60 * 1000; // 10 minutes in milliseconds.

SensorManager(new VaccineDataAccess(UIDatabase), new UtilService());
TemperatureLogManager(new VaccineDataAccess(UIDatabase), new UtilService());
BreachManager(new VaccineDataAccess(UIDatabase), new UtilService());

(async () => {
  const isEmulator = await DeviceInfo.isEmulator();
  if (isEmulator) {
    console.log('Emulator detected - Init Dev BleManager');
    BleService(new DevBleManager());
  } else {
    BleService();
  }
})();

class MSupplyMobileAppContainer extends React.Component {
  constructor(props, ...otherArgs) {
    super(props, ...otherArgs);

    migrateDataToVersion(UIDatabase, Settings);
    this.userAuthenticator = new UserAuthenticator(UIDatabase, Settings);
    this.syncAuthenticator = new SyncAuthenticator(Settings);
    this.synchroniser = new Synchroniser(
      Database,
      this.syncAuthenticator,
      Settings,
      props.dispatch
    );
    this.postSyncProcessor = new PostSyncProcessor(UIDatabase, Settings);
    this.scheduler = new Scheduler();
    const isInitialised = this.synchroniser.isInitialised();
    this.scheduler.schedule(this.synchronise, SYNC_INTERVAL);
    this.scheduler.schedule(() => {
      const { currentUser } = this.props;
      if (currentUser !== null) {
        // Only re-authenticate if currently logged in.
        this.userAuthenticator.reauthenticate(this.onAuthentication);
      }
    }, AUTHENTICATION_INTERVAL);

    this.state = {
      isInitialised,
      isLoading: false,
      appState: null,
      isDownloadingTemperatures: false,
    };
  }

  componentDidUpdate() {
    const { dispatch, usingVaccines, syncTemperatures, requestBluetooth } = this.props;

    if (usingVaccines) {
      const { isDownloadingTemperatures } = this.state;
      if (!isDownloadingTemperatures) this.scheduler.schedule(syncTemperatures, SYNC_INTERVAL);

      BluetoothStatus.addListener(requestBluetooth);
      dispatch(PermissionActions.checkPermissions());
    }
  }

  componentDidMount = () => {
    if (!__DEV__) AppState.addEventListener('change', this.onAppStateChange);
  };

  componentWillUnmount = () => {
    const { usingVaccines } = this.props;

    if (usingVaccines) BluetoothStatus.removeListener();
    if (!__DEV__) AppState.removeEventListener('change', this.onAppStateChange);

    this.scheduler.clearAll();
  };

  onAppStateChange = nextAppState => {
    const { appState } = this.state;
    const { dispatch } = this.props;
    if (nextAppState?.match(/inactive|background/)) dispatch(UserActions.setTime());
    if (appState?.match(/inactive|background/) && nextAppState === 'active') {
      dispatch(PermissionActions.checkPermissions());
      dispatch(UserActions.active());
    }

    this.setState({ appState: nextAppState });
  };

  onAuthentication = user => {
    const { dispatch } = this.props;
    dispatch(UserActions.login(user));
    this.postSyncProcessor.setUser(user);
  };

  onInitialised = () => {
    this.setState({ isInitialised: true });
    this.postSyncProcessor.processAnyUnprocessedRecords();
  };

  runWithLoadingIndicator = async functionToRun => {
    // We here set up an asynchronous promise that will be resolved after a timeout
    // of 1 millisecond. This allows a fraction of a delay for the javascript thread
    // to unblock and allow the spinner animation to start up. The |functionToRun| should
    // not be run inside a |setTimeout| as that relegates to a lower priority, resulting
    // in very slow performance.
    await new Promise(resolve => {
      this.setState({ isLoading: true }, () => setTimeout(resolve, 1));
    });

    await functionToRun();
    this.setState({ isLoading: false });
  };

  synchronise = async () => {
    const { dispatch, isSyncing } = this.props;
    const { isInitialised } = this.state;

    if (!isInitialised || isSyncing) return;

    try {
      const syncUrl = UIDatabase.getSetting(SETTINGS_KEYS.SYNC_URL);
      const syncSiteName = UIDatabase.getSetting(SETTINGS_KEYS.SYNC_SITE_NAME);
      const syncSitePasswordHash = UIDatabase.getSetting(SETTINGS_KEYS.SYNC_SITE_PASSWORD_HASH);

      await this.syncAuthenticator.authenticate(syncUrl, syncSiteName, null, syncSitePasswordHash);

      // True if most recent call to |this.synchroniser.synchronise()| failed.
      const lastSyncFailed = this.synchroniser.lastSyncFailed();
      const lastPostSyncProcessingFailed = this.postSyncProcessor.lastPostSyncProcessingFailed();
      await this.synchroniser.synchronise();
      if (lastSyncFailed || lastPostSyncProcessingFailed) {
        // If last sync was interrupted, it did not enter this block. If the app was closed, it did
        // not store any records left in the sync queue, so tables should be checked for unprocessed
        // records. If the last processing of the record queue was interrupted by app crash then all
        // records need to be checked.
        this.postSyncProcessor.processAnyUnprocessedRecords();
      } else {
        this.postSyncProcessor.processRecordQueue();
      }
      dispatch(syncCompleteTransaction());
    } catch (error) {
      dispatch(setSyncError(error.message));
    }
  };

  renderLoadingIndicator = () => {
    const { isLoading } = this.state;

    return (
      <View style={globalStyles.loadingIndicatorContainer}>
        <Spinner isSpinning={isLoading} color={SUSSOL_ORANGE} />
      </View>
    );
  };

  render() {
    const {
      currentUser,
      closeSupplierCreditModal,
      supplierCreditModalOpen,
      creditTitle,
      isBreachModalOpen,
      closeBreachModal,
      breachModalTitle,
    } = this.props;
    const { isInitialised, isLoading } = this.state;

    if (!isInitialised) {
      return <FirstUsePage synchroniser={this.synchroniser} onInitialised={this.onInitialised} />;
    }

    return (
      <LoadingIndicatorContext.Provider value={this.runWithLoadingIndicator}>
        <View style={globalStyles.appBackground}>
          <MainStackNavigator.Navigator initialRouteName={ROUTES.MENU}>
            {Pages}
          </MainStackNavigator.Navigator>

          <FinaliseModal />
          <SyncModal onPressManualSync={this.synchronise} />
          <LoginModal
            authenticator={this.userAuthenticator}
            settings={Settings}
            isAuthenticated={!!currentUser}
            onAuthentication={this.onAuthentication}
          />
          {isLoading && this.renderLoadingIndicator()}

          <ModalContainer
            isVisible={supplierCreditModalOpen}
            onClose={closeSupplierCreditModal}
            title={creditTitle}
          >
            <SupplierCredit />
          </ModalContainer>

          <ModalContainer
            isVisible={isBreachModalOpen}
            onClose={closeBreachModal}
            title={breachModalTitle}
          >
            <BreachDisplay />
          </ModalContainer>
          <RowDetail />
        </View>
      </LoadingIndicatorContext.Provider>
    );
  }
}

const mapDispatchToProps = dispatch => {
  const openFinaliseModal = () => dispatch(FinaliseActions.openModal());
  const closeFinaliseModal = () => dispatch(FinaliseActions.closeModal());
  const closeSupplierCreditModal = () => dispatch(SupplierCreditActions.close());
  const onOpenSyncModal = () => dispatch(openSyncModal());
  const closeBreachModal = () => dispatch(BreachActions.close());
  const syncTemperatures = () => dispatch(SensorDownloadActions.startDownloadAll());
  const requestBluetooth = newStatus => dispatch(PermissionActions.requestBluetooth(newStatus));

  return {
    requestBluetooth,
    syncTemperatures,
    dispatch,
    onOpenSyncModal,
    openFinaliseModal,
    closeFinaliseModal,
    closeSupplierCreditModal,
    closeBreachModal,
  };
};

const mapStateToProps = state => {
  const { finalise, supplierCredit } = state;
  const { open: supplierCreditModalOpen } = supplierCredit;
  const { finaliseModalOpen } = finalise;

  const usingVaccines = selectUsingVaccines(state);
  const isBreachModalOpen = selectIsBreachModalOpen(state);
  const currentUser = selectCurrentUser(state);
  const isSyncing = selectIsSyncing(state);
  const breachModalTitle = selectBreachModalTitle(state);
  return {
    usingVaccines,

    isSyncing,
    currentUser,
    finaliseModalOpen,
    supplierCreditModalOpen,
    isBreachModalOpen,
    breachModalTitle,
    creditTitle: selectTitle(state),
  };
};

MSupplyMobileAppContainer.defaultProps = {
  currentUser: null,
  creditTitle: '',
};

MSupplyMobileAppContainer.propTypes = {
  usingVaccines: PropTypes.bool.isRequired,
  requestBluetooth: PropTypes.func.isRequired,
  syncTemperatures: PropTypes.func.isRequired,
  isSyncing: PropTypes.bool.isRequired,
  dispatch: PropTypes.func.isRequired,
  currentUser: PropTypes.object,
  closeSupplierCreditModal: PropTypes.func.isRequired,
  supplierCreditModalOpen: PropTypes.bool.isRequired,
  creditTitle: PropTypes.string,
  isBreachModalOpen: PropTypes.bool.isRequired,
  closeBreachModal: PropTypes.func.isRequired,
  breachModalTitle: PropTypes.string.isRequired,
};

export default connect(mapStateToProps, mapDispatchToProps)(MSupplyMobileAppContainer);
<commit_msg>Add updated download interval<commit_after>/* eslint-disable react/forbid-prop-types */
/* eslint-disable no-undef */
/**
 * mSupply Mobile
 * Sustainable Solutions (NZ) Ltd. 2019
 */

import React from 'react';
import PropTypes from 'prop-types';
import DeviceInfo from 'react-native-device-info';
import { connect } from 'react-redux';
import { BluetoothStatus } from 'react-native-bluetooth-status';
import { AppState, View } from 'react-native';
import { Scheduler } from 'sussol-utilities';

import Settings from './settings/MobileAppSettings';
import Database from './database/BaseDatabase';
import { UIDatabase } from './database';
import { SETTINGS_KEYS } from './settings';

import { MainStackNavigator, Pages } from './navigation/Navigator';
import { ROUTES } from './navigation';
import { Synchroniser, PostSyncProcessor, SyncModal } from './sync';
import { migrateDataToVersion } from './dataMigration';
import { SyncAuthenticator, UserAuthenticator } from './authentication';

import { LoadingIndicatorContext } from './context/LoadingIndicatorContext';
import { selectTitle } from './selectors/supplierCredit';
import { selectIsSyncing } from './selectors/sync';
import { selectCurrentUser } from './selectors/user';
import { selectUsingVaccines } from './selectors/modules';

import { syncCompleteTransaction, setSyncError, openSyncModal } from './actions/SyncActions';
import { FinaliseActions } from './actions/FinaliseActions';
import { UserActions } from './actions';
import { SupplierCreditActions } from './actions/SupplierCreditActions';

import { Spinner } from './widgets';
import { ModalContainer, FinaliseModal, LoginModal } from './widgets/modals';
import { FirstUsePage } from './pages';
import { SupplierCredit } from './widgets/modalChildren/SupplierCredit';

import globalStyles, { SUSSOL_ORANGE } from './globalStyles';
import { BreachDisplay } from './widgets/modalChildren/BreachDisplay';
import { selectIsBreachModalOpen, selectBreachModalTitle } from './selectors/breach';
import { BreachActions } from './actions/BreachActions';
import { RowDetail } from './widgets/RowDetail';
import { PermissionActions } from './actions/PermissionActions';
import BleService from './bluetooth/BleService';
import TemperatureLogManager from './bluetooth/TemperatureLogManager';
import { DevBleManager } from './bluetooth/DevBleManager';
import SensorManager from './bluetooth/SensorManager';
import { VaccineDataAccess } from './bluetooth/VaccineDataAccess';
import { UtilService } from './database/utilities/utilService';
import { SensorDownloadActions } from './actions/Bluetooth/SensorDownloadActions';
import BreachManager from './bluetooth/BreachManager';

const SYNC_INTERVAL = 60 * 1000; // 1 minutes in milliseconds.
const AUTHENTICATION_INTERVAL = 10 * 60 * 1000; // 10 minutes in milliseconds.

SensorManager(new VaccineDataAccess(UIDatabase), new UtilService());
TemperatureLogManager(new VaccineDataAccess(UIDatabase), new UtilService());
BreachManager(new VaccineDataAccess(UIDatabase), new UtilService());

(async () => {
  const isEmulator = await DeviceInfo.isEmulator();
  if (isEmulator) {
    console.log('Emulator detected - Init Dev BleManager');
    BleService(new DevBleManager());
  } else {
    BleService();
  }
})();

class MSupplyMobileAppContainer extends React.Component {
  constructor(props, ...otherArgs) {
    super(props, ...otherArgs);

    migrateDataToVersion(UIDatabase, Settings);
    this.userAuthenticator = new UserAuthenticator(UIDatabase, Settings);
    this.syncAuthenticator = new SyncAuthenticator(Settings);
    this.synchroniser = new Synchroniser(
      Database,
      this.syncAuthenticator,
      Settings,
      props.dispatch
    );
    this.postSyncProcessor = new PostSyncProcessor(UIDatabase, Settings);
    this.scheduler = new Scheduler();
    const isInitialised = this.synchroniser.isInitialised();
    this.scheduler.schedule(this.synchronise, SYNC_INTERVAL);
    this.scheduler.schedule(() => {
      const { currentUser } = this.props;
      if (currentUser !== null) {
        // Only re-authenticate if currently logged in.
        this.userAuthenticator.reauthenticate(this.onAuthentication);
      }
    }, AUTHENTICATION_INTERVAL);

    this.state = {
      isInitialised,
      isLoading: false,
      appState: null,
      isDownloadingTemperatures: false,
    };
  }

  componentDidUpdate() {
    const { dispatch, usingVaccines, syncTemperatures, requestBluetooth } = this.props;

    if (usingVaccines) {
      const { isDownloadingTemperatures } = this.state;
      if (!isDownloadingTemperatures) this.scheduler.schedule(syncTemperatures, SYNC_INTERVAL);

      BluetoothStatus.addListener(requestBluetooth);
      dispatch(PermissionActions.checkPermissions());
    }
  }

  componentDidMount = () => {
    if (!__DEV__) AppState.addEventListener('change', this.onAppStateChange);
  };

  componentWillUnmount = () => {
    const { usingVaccines } = this.props;

    if (usingVaccines) BluetoothStatus.removeListener();
    if (!__DEV__) AppState.removeEventListener('change', this.onAppStateChange);

    this.scheduler.clearAll();
  };

  onAppStateChange = nextAppState => {
    const { appState } = this.state;
    const { dispatch } = this.props;
    if (nextAppState?.match(/inactive|background/)) dispatch(UserActions.setTime());
    if (appState?.match(/inactive|background/) && nextAppState === 'active') {
      dispatch(PermissionActions.checkPermissions());
      dispatch(UserActions.active());
    }

    this.setState({ appState: nextAppState });
  };

  onAuthentication = user => {
    const { dispatch } = this.props;
    dispatch(UserActions.login(user));
    this.postSyncProcessor.setUser(user);
  };

  onInitialised = () => {
    this.setState({ isInitialised: true });
    this.postSyncProcessor.processAnyUnprocessedRecords();
  };

  runWithLoadingIndicator = async functionToRun => {
    // We here set up an asynchronous promise that will be resolved after a timeout
    // of 1 millisecond. This allows a fraction of a delay for the javascript thread
    // to unblock and allow the spinner animation to start up. The |functionToRun| should
    // not be run inside a |setTimeout| as that relegates to a lower priority, resulting
    // in very slow performance.
    await new Promise(resolve => {
      this.setState({ isLoading: true }, () => setTimeout(resolve, 1));
    });

    await functionToRun();
    this.setState({ isLoading: false });
  };

  synchronise = async () => {
    const { dispatch, isSyncing } = this.props;
    const { isInitialised } = this.state;

    if (!isInitialised || isSyncing) return;

    try {
      const syncUrl = UIDatabase.getSetting(SETTINGS_KEYS.SYNC_URL);
      const syncSiteName = UIDatabase.getSetting(SETTINGS_KEYS.SYNC_SITE_NAME);
      const syncSitePasswordHash = UIDatabase.getSetting(SETTINGS_KEYS.SYNC_SITE_PASSWORD_HASH);

      await this.syncAuthenticator.authenticate(syncUrl, syncSiteName, null, syncSitePasswordHash);

      // True if most recent call to |this.synchroniser.synchronise()| failed.
      const lastSyncFailed = this.synchroniser.lastSyncFailed();
      const lastPostSyncProcessingFailed = this.postSyncProcessor.lastPostSyncProcessingFailed();
      await this.synchroniser.synchronise();
      if (lastSyncFailed || lastPostSyncProcessingFailed) {
        // If last sync was interrupted, it did not enter this block. If the app was closed, it did
        // not store any records left in the sync queue, so tables should be checked for unprocessed
        // records. If the last processing of the record queue was interrupted by app crash then all
        // records need to be checked.
        this.postSyncProcessor.processAnyUnprocessedRecords();
      } else {
        this.postSyncProcessor.processRecordQueue();
      }
      dispatch(syncCompleteTransaction());
    } catch (error) {
      dispatch(setSyncError(error.message));
    }
  };

  renderLoadingIndicator = () => {
    const { isLoading } = this.state;

    return (
      <View style={globalStyles.loadingIndicatorContainer}>
        <Spinner isSpinning={isLoading} color={SUSSOL_ORANGE} />
      </View>
    );
  };

  render() {
    const {
      currentUser,
      closeSupplierCreditModal,
      supplierCreditModalOpen,
      creditTitle,
      isBreachModalOpen,
      closeBreachModal,
      breachModalTitle,
    } = this.props;
    const { isInitialised, isLoading } = this.state;

    if (!isInitialised) {
      return <FirstUsePage synchroniser={this.synchroniser} onInitialised={this.onInitialised} />;
    }

    return (
      <LoadingIndicatorContext.Provider value={this.runWithLoadingIndicator}>
        <View style={globalStyles.appBackground}>
          <MainStackNavigator.Navigator initialRouteName={ROUTES.MENU}>
            {Pages}
          </MainStackNavigator.Navigator>

          <FinaliseModal />
          <SyncModal onPressManualSync={this.synchronise} />
          <LoginModal
            authenticator={this.userAuthenticator}
            settings={Settings}
            isAuthenticated={!!currentUser}
            onAuthentication={this.onAuthentication}
          />
          {isLoading && this.renderLoadingIndicator()}

          <ModalContainer
            isVisible={supplierCreditModalOpen}
            onClose={closeSupplierCreditModal}
            title={creditTitle}
          >
            <SupplierCredit />
          </ModalContainer>

          <ModalContainer
            isVisible={isBreachModalOpen}
            onClose={closeBreachModal}
            title={breachModalTitle}
          >
            <BreachDisplay />
          </ModalContainer>
          <RowDetail />
        </View>
      </LoadingIndicatorContext.Provider>
    );
  }
}

const mapDispatchToProps = dispatch => {
  const openFinaliseModal = () => dispatch(FinaliseActions.openModal());
  const closeFinaliseModal = () => dispatch(FinaliseActions.closeModal());
  const closeSupplierCreditModal = () => dispatch(SupplierCreditActions.close());
  const onOpenSyncModal = () => dispatch(openSyncModal());
  const closeBreachModal = () => dispatch(BreachActions.close());
  const syncTemperatures = () => dispatch(SensorDownloadActions.startDownloadAll());
  const requestBluetooth = newStatus => dispatch(PermissionActions.requestBluetooth(newStatus));

  return {
    requestBluetooth,
    syncTemperatures,
    dispatch,
    onOpenSyncModal,
    openFinaliseModal,
    closeFinaliseModal,
    closeSupplierCreditModal,
    closeBreachModal,
  };
};

const mapStateToProps = state => {
  const { finalise, supplierCredit } = state;
  const { open: supplierCreditModalOpen } = supplierCredit;
  const { finaliseModalOpen } = finalise;

  const usingVaccines = selectUsingVaccines(state);
  const isBreachModalOpen = selectIsBreachModalOpen(state);
  const currentUser = selectCurrentUser(state);
  const isSyncing = selectIsSyncing(state);
  const breachModalTitle = selectBreachModalTitle(state);
  return {
    usingVaccines,

    isSyncing,
    currentUser,
    finaliseModalOpen,
    supplierCreditModalOpen,
    isBreachModalOpen,
    breachModalTitle,
    creditTitle: selectTitle(state),
  };
};

MSupplyMobileAppContainer.defaultProps = {
  currentUser: null,
  creditTitle: '',
};

MSupplyMobileAppContainer.propTypes = {
  usingVaccines: PropTypes.bool.isRequired,
  requestBluetooth: PropTypes.func.isRequired,
  syncTemperatures: PropTypes.func.isRequired,
  isSyncing: PropTypes.bool.isRequired,
  dispatch: PropTypes.func.isRequired,
  currentUser: PropTypes.object,
  closeSupplierCreditModal: PropTypes.func.isRequired,
  supplierCreditModalOpen: PropTypes.bool.isRequired,
  creditTitle: PropTypes.string,
  isBreachModalOpen: PropTypes.bool.isRequired,
  closeBreachModal: PropTypes.func.isRequired,
  breachModalTitle: PropTypes.string.isRequired,
};

export default connect(mapStateToProps, mapDispatchToProps)(MSupplyMobileAppContainer);
<|endoftext|>


<commit_before>/**
 * Copyright 2022 Google LLC
 *
 * Use of this source code is governed by a BSD-style
 * license that can be found in the LICENSE file or at
 * https://developers.google.com/open-source/licenses/bsd
 */

const Module = Quill.import('core/module');
const Delta = Quill.import('delta');

import {choosePhoto} from '../app.js';
import {QuillPhotoPickerRecipe} from '../Library/QuillPhotoPickerRecipe.js';
import {PhotosByDateRecipe} from '../../photos/Library/PhotosByDateRecipe.js';

class PhotoChooser extends Module {
  constructor(quill, options) {
    super(quill, options);
    this.quill = quill;
    this.toolbar = quill.getModule('toolbar');
    if (typeof this.toolbar !== 'undefined')
      this.toolbar.addHandler('image', this.photoChooser);
  }

  async photoChooser() {
    const chooser = document.getElementById('chooser');
    chooser.setAttribute('show', '');
    const photo = await choosePhoto([PhotosByDateRecipe], 'chooser', true, 12);
    chooser.removeAttribute('show');

    const range = this.quill.getSelection(true);
    const delta = new Delta().retain(range.index).delete(range.length);
    delta.insert({image: photo.photoUrl}, {style: "transform: rotate(" + (photo?.privateData?.rotation || 0) + "deg)"});
    this.quill.updateContents(delta, 'user');
    this.quill.setSelection(
        range.index + 1,
        'silent',
    );
  }
}

export {
  PhotoChooser
};
<commit_msg>Update photochooser.js<commit_after>/**
 * Copyright 2022 Google LLC
 *
 * Use of this source code is governed by a BSD-style
 * license that can be found in the LICENSE file or at
 * https://developers.google.com/open-source/licenses/bsd
 */

const Module = Quill.import('core/module');
const Delta = Quill.import('delta');

import {choosePhoto} from '../app.js';
import {QuillPhotoPickerRecipe} from '../Library/QuillPhotoPickerRecipe.js';
import {PhotosByDateRecipe} from '../../photos/Library/PhotosByDateRecipe.js';

class PhotoChooser extends Module {
  constructor(quill, options) {
    super(quill, options);
    this.quill = quill;
    this.toolbar = quill.getModule('toolbar');
    if (typeof this.toolbar !== 'undefined')
      this.toolbar.addHandler('image', this.photoChooser);
  }

  async photoChooser() {
    const chooser = document.getElementById('chooser');
    chooser.setAttribute('show', '');
    const photo = await choosePhoto([QuillPhotoPickerRecipe], 'chooser', true, 12);
    chooser.removeAttribute('show');

    const range = this.quill.getSelection(true);
    const delta = new Delta().retain(range.index).delete(range.length);
    delta.insert({image: photo.photoUrl}, {style: "transform: rotate(" + (photo?.privateData?.rotation || 0) + "deg)"});
    this.quill.updateContents(delta, 'user');
    this.quill.setSelection(
        range.index + 1,
        'silent',
    );
  }
}

export {
  PhotoChooser
};
<|endoftext|>


<commit_before>var util = require('../core/util.js');
var _ = require('lodash');
var log = require('../core/log.js');
var moment = require('moment');

var mode = util.gekkoMode();

var config = util.getConfig();
var calcConfig = config.profitSimulator;
var watchConfig = config.watch;

var Logger = function() {
  _.bindAll(this);

  this.dates = {
    start: false,
    end: false
  }

  this.startPrice = 0;
  this.endPrice = 0;

  this.verbose = calcConfig.verbose;
  this.fee = 1 - (calcConfig.fee + calcConfig.slippage) / 100;

  this.currency = watchConfig.currency;
  this.asset = watchConfig.asset;

  this.reportInCurrency = calcConfig.reportInCurrency;

  if(this.reportInCurrency)
    this.reportIn = this.currency;
  else
    this.reportIn = this.asset;

  // virtual balance
  this.start = {
    asset: calcConfig.simulationBalance.asset,
    currency: calcConfig.simulationBalance.currency,
    balance: false
  }
  this.current = _.clone(this.start);
  this.trades = 0;
  this.tracks = 0;

  if(config.enabled)
    log.info('Profit reporter active on simulated balance');

}

Logger.prototype.extractFee = function(amount) {
  amount *= 100000000;
  amount *= this.fee;
  amount = Math.floor(amount);
  amount /= 100000000;
  return amount;
}

Logger.prototype.round = function(amount) {
  return amount.toFixed(5);
}

Logger.prototype.calculateStartBalance = function() {
  if(this.reportInCurrency)
    this.start.balance = this.start.currency + this.price * this.start.asset;
  else
    this.start.balance = this.start.asset + this.start.currency / this.price;
}

// after every succesfull trend ride we hopefully end up
// with more BTC than we started with, this function
// calculates Gekko's profit in %.
Logger.prototype.processAdvice = function(advice) {
  this.tracks++;

  var what = advice.recommendation;
  var time = this.dates.end.utc().format('YYYY-MM-DD HH:mm:ss')

  // virtually trade all USD to BTC at the current price
  if(what === 'long') {
    this.current.asset += this.extractFee(this.current.currency / this.price);
    if(mode === 'backtest') {
      log.info(`${time}: Profit simulator got advice to long \t${this.current.currency.toFixed(3)} ${this.currency} => ${this.current.asset.toFixed(3)} ${this.asset}`);
    }
    this.current.currency = 0;
    this.trades++;
  }

  // virtually trade all BTC to USD at the current price
  if(what === 'short') {
    this.current.currency += this.extractFee(this.current.asset * this.price);
    if(mode === 'backtest') {
      log.info(`${time}: Profit simulator got advice to short \t${this.current.currency.toFixed(3)} ${this.currency} <= ${this.current.asset.toFixed(3)} ${this.asset}`);
    }
    this.current.asset = 0;
    this.trades++;
  }

  if(mode === 'realtime')
    this.report();
}

Logger.prototype.processCandle = function(candle, done) {
  if(!this.dates.start) {
    this.dates.start = candle.start;
    this.startPrice = candle.open;
  }

  this.dates.end = candle.start.clone().add(1, 'm');
  this.endPrice = candle.close;

  this.price = candle.close;

  if(!this.start.balance)
    this.calculateStartBalance();

  if(!calcConfig.verbose)
    return done();

  // skip on history
  if(++this.historyReceived < this.historySize)
    return done();

  done();
}

Logger.prototype.report = function(timespan) {
  if(!this.start.balance)
    return log.warn('Unable to simulate profits without starting balance');

  if(this.reportInCurrency)
    this.current.balance = this.current.currency + this.price * this.current.asset;
  else
    this.current.balance = this.current.asset + this.current.currency / this.price;

  this.profit = this.current.balance - this.start.balance;
  this.relativeProfit = this.current.balance / this.start.balance * 100 - 100;

  var start = this.round(this.start.balance);
  var current = this.round(this.current.balance);

  log.info(`(PROFIT REPORT) original simulated balance:\t ${start} ${this.reportIn}`);
  log.info(`(PROFIT REPORT) current simulated balance:\t ${current} ${this.reportIn}`);
  log.info(
    `(PROFIT REPORT) simulated profit:\t\t ${this.round(this.profit)} ${this.reportIn}`,
    '(' + this.round(this.relativeProfit) + '%)'
  );

  if(timespan) {
    log.info(
      '(PROFIT REPORT)',
      'simulated yearly profit:\t',
      this.round(this.profit / timespan.asYears()),
      this.reportIn,
      '(' + this.round(this.relativeProfit / timespan.asYears()) + '%)'
    );
  }
}

// finish up stats for backtesting
Logger.prototype.finalize = function() {
  log.info('')

  log.info(
    '(PROFIT REPORT)',
    'start time:\t\t\t',
    this.dates.start.utc().format('YYYY-MM-DD HH:mm:ss')
  );

  log.info(
    '(PROFIT REPORT)',
    'end time:\t\t\t',
    this.dates.end.utc().format('YYYY-MM-DD HH:mm:ss')
  );

  var timespan = moment.duration(
    this.dates.end.diff(this.dates.start)
  );

  log.info(
    '(PROFIT REPORT)',
    'timespan:\t\t\t',
    timespan.humanize()
  );

  log.info();

  log.info(
    '(PROFIT REPORT)',
    'start price:\t\t\t',
    this.startPrice
  );

  log.info(
    '(PROFIT REPORT)',
    'end price:\t\t\t',
    this.endPrice
  );

  log.info(
    '(PROFIT REPORT)',
    'Buy and Hold profit:\t\t',
    (this.round(this.endPrice * 100 / this.startPrice) - 100) + '%'
  );

  log.info();

  log.info(
    '(PROFIT REPORT)',
    'amount of trades:\t\t',
    this.trades
  );

  this.report(timespan);
}

module.exports = Logger;
<commit_msg>let profit simulator output json in cp<commit_after>var util = require('../core/util.js');
var _ = require('lodash');
var log = require('../core/log.js');
var moment = require('moment');

var mode = util.gekkoMode();

var config = util.getConfig();
var calcConfig = config.profitSimulator;
var watchConfig = config.watch;

var ENV = util.gekkoEnv();

var Logger = function() {
  _.bindAll(this);

  this.dates = {
    start: false,
    end: false
  }

  this.startPrice = 0;
  this.endPrice = 0;

  this.verbose = calcConfig.verbose;
  this.fee = 1 - (calcConfig.fee + calcConfig.slippage) / 100;

  this.currency = watchConfig.currency;
  this.asset = watchConfig.asset;

  this.reportInCurrency = calcConfig.reportInCurrency;

  if(this.reportInCurrency)
    this.reportIn = this.currency;
  else
    this.reportIn = this.asset;

  // virtual balance
  this.start = {
    asset: calcConfig.simulationBalance.asset,
    currency: calcConfig.simulationBalance.currency,
    balance: false
  }
  this.current = _.clone(this.start);
  this.trades = 0;
  this.tracks = 0;

  if(config.enabled)
    log.info('Profit reporter active on simulated balance');

}

Logger.prototype.extractFee = function(amount) {
  amount *= 100000000;
  amount *= this.fee;
  amount = Math.floor(amount);
  amount /= 100000000;
  return amount;
}

Logger.prototype.round = function(amount) {
  return amount.toFixed(5);
}

Logger.prototype.calculateStartBalance = function() {
  if(this.reportInCurrency)
    this.start.balance = this.start.currency + this.price * this.start.asset;
  else
    this.start.balance = this.start.asset + this.start.currency / this.price;
}

// after every succesfull trend ride we hopefully end up
// with more BTC than we started with, this function
// calculates Gekko's profit in %.
Logger.prototype.processAdvice = function(advice) {
  this.tracks++;

  var what = advice.recommendation;

  // virtually trade all USD to BTC at the current price
  if(what === 'long') {
    this.current.asset += this.extractFee(this.current.currency / this.price);
    this.current.currency = 0;
    this.trades++;
  }

  // virtually trade all BTC to USD at the current price
  if(what === 'short') {
    this.current.currency += this.extractFee(this.current.asset * this.price);
    this.current.asset = 0;
    this.trades++;
  }

  if(mode === 'realtime')
    this.verboseReport();

  else if(mode === 'backtest') {

    if(ENV === 'standalone')
      this.summarizedReport(what);
    else if(ENV === 'child-process')
      this.jsonReport(what);
  }
}

Logger.prototype.processCandle = function(candle, done) {
  if(!this.dates.start) {
    this.dates.start = candle.start;
    this.startPrice = candle.open;
  }

  this.dates.end = candle.start.clone().add(1, 'm');
  this.endPrice = candle.close;

  this.price = candle.close;

  if(!this.start.balance)
    this.calculateStartBalance();

  if(!calcConfig.verbose)
    return done();

  // skip on history
  if(++this.historyReceived < this.historySize)
    return done();

  done();
}

Logger.prototype.jsonReport = function(what) {

  var ts = this.dates.end.utc().unix();

  if(what === 'short')
    process.send({
      from: 'profitSimulator',
      type: 'position change',
      action: 'sell',
      date: ts,
      balance: this.current.currency
    });

  else if(what === 'long')
    process.send({
      from: 'profitSimulator',
      type: 'position change',
      action: 'buy',
      date: ts,
      balance: this.current.asset * this.price
    });

}

Logger.prototype.summarizedReport = function(what) {

  var time = this.dates.end.utc().format('YYYY-MM-DD HH:mm:ss');

  if(what === 'short')

      log.info(
        `${time}: Profit simulator got advice to short`,
        `\t${this.current.currency.toFixed(3)}`,
        `${this.currency} <= ${this.current.asset.toFixed(3)}`,
        `${this.asset}`
      );

  else if(what === 'long')

    log.info(
      `${time}: Profit simulator got advice to long`,
      `\t${this.current.currency.toFixed(3)}`,
      `${this.currency} => ${this.current.asset.toFixed(3)}`,
      `${this.asset}`
    );

}

Logger.prototype.verboseReport = function(timespan) {
  if(!this.start.balance)
    return log.warn('Unable to simulate profits without starting balance');

  if(this.reportInCurrency)
    this.current.balance = this.current.currency + this.price * this.current.asset;
  else
    this.current.balance = this.current.asset + this.current.currency / this.price;

  this.profit = this.current.balance - this.start.balance;
  this.relativeProfit = this.current.balance / this.start.balance * 100 - 100;

  var start = this.round(this.start.balance);
  var current = this.round(this.current.balance);

  log.info(`(PROFIT REPORT) original simulated balance:\t ${start} ${this.reportIn}`);
  log.info(`(PROFIT REPORT) current simulated balance:\t ${current} ${this.reportIn}`);
  log.info(
    `(PROFIT REPORT) simulated profit:\t\t ${this.round(this.profit)} ${this.reportIn}`,
    '(' + this.round(this.relativeProfit) + '%)'
  );

  if(timespan) {
    log.info(
      '(PROFIT REPORT)',
      'simulated yearly profit:\t',
      this.round(this.profit / timespan.asYears()),
      this.reportIn,
      '(' + this.round(this.relativeProfit / timespan.asYears()) + '%)'
    );
  }
}

// finish up stats for backtesting
if(ENV === 'standalone') {
  Logger.prototype.finalize = function() {
    log.info('')

    log.info(
      '(PROFIT REPORT)',
      'start time:\t\t\t',
      this.dates.start.utc().format('YYYY-MM-DD HH:mm:ss')
    );

    log.info(
      '(PROFIT REPORT)',
      'end time:\t\t\t',
      this.dates.end.utc().format('YYYY-MM-DD HH:mm:ss')
    );

    var timespan = moment.duration(
      this.dates.end.diff(this.dates.start)
    );

    log.info(
      '(PROFIT REPORT)',
      'timespan:\t\t\t',
      timespan.humanize()
    );

    log.info();

    log.info(
      '(PROFIT REPORT)',
      'start price:\t\t\t',
      this.startPrice
    );

    log.info(
      '(PROFIT REPORT)',
      'end price:\t\t\t',
      this.endPrice
    );

    log.info(
      '(PROFIT REPORT)',
      'Buy and Hold profit:\t\t',
      (this.round(this.endPrice * 100 / this.startPrice) - 100) + '%'
    );

    log.info();

    log.info(
      '(PROFIT REPORT)',
      'amount of trades:\t\t',
      this.trades
    );

    this.verboseReport(timespan);
  }
} else if(ENV === 'child-process') {
  Logger.prototype.finalize = function() {
    // todo!
    process.send('done!')
  }
}

module.exports = Logger;
<|endoftext|>


<commit_before>/*jshint node: true */
'use strict';

/*
* Name :  MapChart.js
* Module : FrontEnd::View
* Location : /frontend/app/View
*
* History :
* Version       Date        Programmer                  Description
* =================================================================================================
* 0.0.1         2015-06-02  Maria Giovanna Chinellato   Initial code
* =================================================================================================
*/

angular.module('norris-nrti')
.directive('tableChart', function($compile){
	return {
		restrict: 'E',
		//controller : 'TableController',
		replace: false,
		scope: {
            url: '@'
		},
		bindToController: true,
        //template: '
table {{url}}
',
        link: function(scope, element, attrs){

        	attrs.$observe('url', function(value) {
                console.log('TABLE observ url ' + value);
                if (value) {
                    scope.$parent.socketConnection(value);
                }
            });

            scope.$parent.$watch('changed', function(newValue, oldValue){
                if (newValue !== oldValue) {
                    console.log('TABLE watch changedP');
                    scope.setData();
                    console.log('scope.data.length: ' +scope.data.length);
                    if (scope.data.length > 0) {
                        scope.init();
                    }
                }
            }, true);

            /*scope.$parent.$watch('changedD', function(newValue, oldValue){
                if(newValue !== oldValue){
                    console.log('TABLE watch changedD');
                    scope.setData();
                    scope.init();
                }
            }, true);*/

            scope.init = function(){
                console.log('TABLE init');
            	element.empty();

                var table = '
'+ scope.$parent.table.getTitle() +'
';

                table = table + '

';
                } else if () {
                    table = table + 'class="table-condensed table-striped">';
                }*/

                table = table + '

';
                for (var i=0; i<scope.$parent.table.getHeaders().length; i++) {
                    table = table + '<th ';
                    if (scope.$parent.table.getSortable()) {
                        if (scope.$parent.table.getHeaders()[i] === scope.$parent.table.getSort().column ) {
                            var ordinamento;
                            if (scope.$parent.table.getSort().ordering === 'ASC') {
                                ordinamento = 'true';
                            } else if (scope.$parent.table.getSort().ordering === 'DESC'){
                                ordinamento = 'reverse';
                            }
                            table = table + 'st-sort-default="'+ ordinamento +'" st-sort="'+ scope.$parent.table.getHeaders()[i] +'"';
                        }
                    }
                    table = table + '>'+ scope.$parent.table.getHeaders()[i] +'	';
                }
                table = table + '

';

                table = table + '

';
                i = 0;
                for (var j=0; j<scope.$parent.table.getHeaders().length; j++){
                    table = table + '{{record.'+ scope.$parent.table.getHeaders()[j] +'}}	';
                    i++;
                }
                table = table + '

';

                table = table + '

' +
                                    '' +
                                        '

' +
                                    '	' +
                                '

';

                table = table + '

';

            	var compiled = $compile(table)(scope);
                element.append(compiled);
            };

            scope.data = [];
            scope.appearance = [];

            scope.setData = function(){
                console.log('TABLE setData');
                var data = [];
                var appearance = [];
                for (var k=0; k<scope.$parent.table.getFlowList().length; k++) {
                    console.log('TABLE getData.length ' + scope.$parent.table.getFlowList()[k].flow.getData().length);
                    for (var i=0; i<scope.$parent.table.getFlowList()[k].flow.getData().length; i++) {
                        var record = {};
                        var look = [];
                        for (var j=0; j<scope.$parent.table.getHeaders().length; j++) {
                            record[scope.$parent.table.getHeaders()[j]] = scope.$parent.table.getFlowList()[k].flow.getData()[i].value[j];
                            look[j] = scope.$parent.table.getFlowList()[k].flow.getData()[i].appearance[j];
                        }
                        data.push(record);
                        appearance.push(look);
                    }
                }
                console.log('data. length: ' + data.length);
                /*console.log('appearance length ' + appearance.length);
                for (var g=0; g<appearance.length; g++) {
                    console.log('appearance['+ g +'] length ' +appearance[g].length);
                    for (var f=0; f<appearance[g].length; f++) {
                        console.log('TABLE appearance: ' + appearance[g][f].bg + ' ' + appearance[g][f].text);
                    }
                }*/
                scope.data = data;
                scope.appearance = appearance;
                scope.itemsByPage = scope.$parent.table.getMaxItemsPage();
            };
        }
    };
});<commit_msg>fix tableview<commit_after>/*jshint node: true */
'use strict';

/*
* Name :  MapChart.js
* Module : FrontEnd::View
* Location : /frontend/app/View
*
* History :
* Version       Date        Programmer                  Description
* =================================================================================================
* 0.0.1         2015-06-02  Maria Giovanna Chinellato   Initial code
* =================================================================================================
*/

angular.module('norris-nrti')
.directive('tableChart', function($compile){
	return {
		restrict: 'E',
		//controller : 'TableController',
		replace: false,
		scope: {
            url: '@'
		},
		bindToController: true,
        //template: '
table {{url}}
',
        link: function(scope, element, attrs){

        	attrs.$observe('url', function(value) {
                console.log('TABLE observ url ' + value);
                if (value) {
                    scope.$parent.socketConnection(value);
                }
            });

            scope.$parent.$watch('changed', function(newValue, oldValue){
                if (newValue !== oldValue) {
                    console.log('TABLE watch changedP');
                    scope.setData();
                    console.log('scope.data.length: ' +scope.data.length);
                    if (scope.data.length > 0) {
                        scope.init();
                    }
                }
            }, true);

            /*scope.$parent.$watch('changedD', function(newValue, oldValue){
                if(newValue !== oldValue){
                    console.log('TABLE watch changedD');
                    scope.setData();
                    scope.init();
                }
            }, true);*/

            scope.init = function(){
                console.log('TABLE init');
            	element.empty();

                var table = '
'+ scope.$parent.table.getTitle() +'
';

                table = table + '

';
                } else {
                    table = table + 'class="table-condensed table-striped">';
                }

                table = table + '

';
                for (var i=0; i<scope.$parent.table.getHeaders().length; i++) {
                    table = table + '<th ';
                    if (scope.$parent.table.getSortable()) {
                        if (scope.$parent.table.getHeaders()[i] === scope.$parent.table.getSort().column ) {
                            var ordinamento;
                            if (scope.$parent.table.getSort().ordering === 'ASC') {
                                ordinamento = 'true';
                            } else if (scope.$parent.table.getSort().ordering === 'DESC'){
                                ordinamento = 'reverse';
                            }
                            table = table + 'st-sort-default="'+ ordinamento +'" st-sort="'+ scope.$parent.table.getHeaders()[i] +'"';
                        }
                    }
                    table = table + '>'+ scope.$parent.table.getHeaders()[i] +'	';
                }
                table = table + '

';

                table = table + '

';
                i = 0;
                for (var j=0; j<scope.$parent.table.getHeaders().length; j++){
                    table = table + '{{record.'+ scope.$parent.table.getHeaders()[j] +'}}	';
                    i++;
                }
                table = table + '

';

                table = table + '

' +
                                    '' +
                                        '

' +
                                    '	' +
                                '

';

                table = table + '

';

            	var compiled = $compile(table)(scope);
                element.append(compiled);
            };

            scope.data = [];
            scope.appearance = [];

            scope.setData = function(){
                console.log('TABLE setData');
                var data = [];
                var appearance = [];
                for (var k=0; k<scope.$parent.table.getFlowList().length; k++) {
                    console.log('TABLE getData.length ' + scope.$parent.table.getFlowList()[k].flow.getData().length);
                    for (var i=0; i<scope.$parent.table.getFlowList()[k].flow.getData().length; i++) {
                        var record = {};
                        var look = [];
                        for (var j=0; j<scope.$parent.table.getHeaders().length; j++) {
                            record[scope.$parent.table.getHeaders()[j]] = scope.$parent.table.getFlowList()[k].flow.getData()[i].value[j];
                            look[j] = scope.$parent.table.getFlowList()[k].flow.getData()[i].appearance[j];
                        }
                        data.push(record);
                        appearance.push(look);
                    }
                }
                console.log('data. length: ' + data.length);
                /*console.log('appearance length ' + appearance.length);
                for (var g=0; g<appearance.length; g++) {
                    console.log('appearance['+ g +'] length ' +appearance[g].length);
                    for (var f=0; f<appearance[g].length; f++) {
                        console.log('TABLE appearance: ' + appearance[g][f].bg + ' ' + appearance[g][f].text);
                    }
                }*/
                scope.data = data;
                scope.appearance = appearance;
                scope.itemsByPage = scope.$parent.table.getMaxItemsPage();
            };
        }
    };
});<|endoftext|>


<commit_before>(function ($) {
    $(function () {
        var terminal = $("#terminal"),
            history = $("#history"),
            cursor = $("#cursor"),
            writer = $("#writer"),
            textarea = $('#setter'),
            template = 'yulia at air.local ~ ';

        initTextarea();
        initNewLine();
        textarea.focus();

        function initTextarea() {
            textarea.keydown(function () {
                writeit(this, event);
                moveIt(this.value.length, event);
            });
            textarea.keyup(function () {
                writeit(this, event);
            });
            textarea.keypress(function () {
                writeit(this, event);
            });
            terminal.click(function () {
                textarea.focus();
            });
        }

        function initNewLine(txt) {
            history.append("
").append(template);
        }

        function addToHistory(command, txt) {
            history.append("
$ ").append(command).append("
").append(nl2br(JSON.stringify(txt)));
        }

        function nl2br(txt) {
            return txt.replace(/\n/g, "
");
        }

        function writeit(from, e) {
            e = e || window.event;
            if (e.keyCode === 13) {
                from.value = "";
            } else {
                var tw = from.value;
                writer.html(nl2br(tw));
                //TODO: here to parse key words, make them key words
            }
        }

        function moveIt(count, e) {
            e = e || window.event;
            var keycode = e.keyCode || e.which;

            if (keycode == 13) {
                var command = writer.html();
                getData(command);
            } else {
                if (keycode == 37 && parseInt(cursor.style.left) >= (0 - ((count - 1) * 10))) {
                    cursor.style.left = parseInt(cursor.style.left) - 10 + "px";
                } else if (keycode == 39 && (parseInt(cursor.style.left) + 10) <= 0) {
                    cursor.style.left = parseInt(cursor.style.left) + 10 + "px";
                }
            }
        }

        function reset(command, data) {
            addToHistory(command, data);
            initNewLine();
            writer.html("");
        }

        function getData(command) {
            $.ajax({
                type: "POST",
                url: "api/mongo",
                data: command,
                contentType: "application/json",
                success: function (data) {
                    reset(command, data);
                },
                error: function (e) {
                    reset(command, e);
                }
            });
        }

    });

})(jQuery);
<commit_msg>web browser console: empty commasnd case<commit_after>(function ($) {
    $(function () {
        var terminal = $("#terminal"),
            history = $("#history"),
            cursor = $("#cursor"),
            writer = $("#writer"),
            textarea = $('#setter'),
            template = 'yulia at air.local ~\/Projects\/any-console\/ ';

        initTextarea();
        initNewLine();
        textarea.focus();

        function initTextarea() {
            textarea.keydown(function () {
                writeit(this, event);
                moveIt(this.value.length, event);
            });
            textarea.keyup(function () {
                writeit(this, event);
            });
            textarea.keypress(function () {
                writeit(this, event);
            });
            terminal.click(function () {
                textarea.focus();
            });
        }

        function initNewLine(txt) {
            history.append("
").append(template);
        }

        function addToHistory(command, txt) {
            history.append("
$ ");

            if (txt !== ""){
                history.append(command).append("
").append(nl2br(JSON.stringify(txt)));
            }
        }

        function nl2br(txt) {
            return txt.replace(/\n/g, "
");
        }

        function writeit(from, e) {
            e = e || window.event;
            if (e.keyCode === 13) {
                from.value = "";
            } else {
                var tw = from.value;
                writer.html(nl2br(tw));
                //TODO: here to parse key words, make them key words
            }
        }

        function moveIt(count, e) {
            e = e || window.event;
            var keycode = e.keyCode || e.which, command = writer.html();

            if ( command.length === 0) {
                reset("", "");
                return 0;
            }
            if (keycode == 13) {
                getData(command);
            } else {
                if (keycode == 37 && parseInt(cursor.style.left) >= (0 - ((count - 1) * 10))) {
                    cursor.style.left = parseInt(cursor.style.left) - 10 + "px";
                } else if (keycode == 39 && (parseInt(cursor.style.left) + 10) <= 0) {
                    cursor.style.left = parseInt(cursor.style.left) + 10 + "px";
                }
            }
        }

        function reset(command, data) {
            addToHistory(command, data);
            initNewLine();
            writer.html("");
        }

        function getData(command) {
            $.ajax({
                type: "POST",
                url: "api/mongo",
                data: command,
                contentType: "application/json",
                success: function (data) {
                    reset(command, data);
                },
                error: function (e) {
                    reset(command, e);
                }
            });
        }

    });

})(jQuery);
<|endoftext|>


<commit_before>tinymce.PluginManager.add("save", function (e) {
    function t() {
        var t, n;
        return t = tinymce.DOM.getParent(e.id, "form"), !e.getParam("save_enablewhendirty", !0) || e.isDirty() ? (tinymce.triggerSave(), (n = e.getParam("save_onsavecallback")) ? (e.execCallback("save_onsavecallback", e) && (e.startContent = tinymce.trim(e.getContent({
            format: "raw"
        })), e.nodeChanged()), void 0) : (t ? (e.isNotDirty = !0, (!t.onsubmit || t.onsubmit()) && ("function" == typeof t.submit ? t.submit() : e.windowManager.alert("Error: Form submit field collision.")), e.nodeChanged()) : e.windowManager.alert("Error: No form element found."), void 0)) : void 0
    }

    function n() {
        var t, n = tinymce.trim(e.startContent);
        return (t = e.getParam("save_oncancelcallback")) ? (e.execCallback("save_oncancelcallback", e), void 0) : (e.setContent(n), e.undoManager.clear(), e.nodeChanged(), void 0)
    }

    function i() {
        var t = this;
        e.on("nodeChange", function () {
//            t.disabled(e.getParam("save_enablewhendirty", !0) && !e.isDirty())
        })
    }
    e.addCommand("mceSave", t), e.addCommand("mceCancel", n), e.addButton("save", {
        icon: "save",
        text: "Save",
        cmd: "mceSave",
//        disabled: !0,
        onPostRender: i
    }), e.addButton("cancel", {
        text: "Cancel",
        icon: !1,
        cmd: "mceCancel",
//        disabled: !0,
        onPostRender: i
    }), e.addShortcut("ctrl+s", "", "mceSave")
});<commit_msg>save always enabled<commit_after>tinymce.PluginManager.add("save", function (e) {
    function t() {
        var t, n;
        return t = tinymce.DOM.getParent(e.id, "form"), !e.getParam("save_enablewhendirty", !1) || e.isDirty() ? (tinymce.triggerSave(), (n = e.getParam("save_onsavecallback")) ? (e.execCallback("save_onsavecallback", e) && (e.startContent = tinymce.trim(e.getContent({
            format: "raw"
        })), e.nodeChanged()), void 0) : (t ? (e.isNotDirty = !0, (!t.onsubmit || t.onsubmit()) && ("function" == typeof t.submit ? t.submit() : e.windowManager.alert("Error: Form submit field collision.")), e.nodeChanged()) : e.windowManager.alert("Error: No form element found."), void 0)) : void 0
    }

    function n() {
        var t, n = tinymce.trim(e.startContent);
        return (t = e.getParam("save_oncancelcallback")) ? (e.execCallback("save_oncancelcallback", e), void 0) : (e.setContent(n), e.undoManager.clear(), e.nodeChanged(), void 0)
    }

    function i() {
        var t = this;
        e.on("nodeChange", function () {
//            t.disabled(e.getParam("save_enablewhendirty", !0) && !e.isDirty())
        })
    }
    e.addCommand("mceSave", t), e.addCommand("mceCancel", n), e.addButton("save", {
        icon: "save",
//        text: "Save",
        cmd: "mceSave",
        disabled: !1,
        onPostRender: i
    }), e.addButton("cancel", {
        text: "Cancel",
        icon: !1,
        cmd: "mceCancel",
        disabled: !1,
        onPostRender: i
    }), e.addShortcut("ctrl+s", "", "mceSave")
});<|endoftext|>


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

foam.CLASS({
  package: 'foam.dao',
  name: 'JoinedSink',
  extends: 'foam.dao.ProxySink',

  methods: [
    {
      name: 'put',
      code: function put(obj, fc) {
        return this.dao.find(this.property.f(obj)).then(function() {
          debugger
        });
      }
    },
    {
      name: 'remove',
      code: function remove(obj, fc) {
      }
    }
  ]
});

foam.CLASS({
  package: 'foam.dao',
  name: 'ManyToManyRelationshipDAO',
  extends: 'foam.dao.RelationshipDAO',

  implements: [ 'foam.mlang.Expressions' ],

  documentation: 'Adapts a DAO based on a *:* Relationship.',

  properties: [
    'junctionProperty',
    'joinDAOKey',
    'targetProperty'
  ],

  methods: [/*
    function put(obj, sink) {
      return this.SUPER(this.relationship.adaptTarget(this.obj, obj), sink);
      }*/
    function select(sink, skip, limit, order, predicate) {
      var self    = this;
      var joinDAO = this.__context__[this.joinDAOKey];

      return new Promise(function(resolve, reject) {
        self.SUPER(self.MAP(self.junctionProperty)).then(function(array) {
          resolve(joinDAO.select(sink));
        });
      });
      /*
      var joinSink = this.JoinedSink.create({dao: , property: this.property});

      if ( predicate ) {
        joinSink = this.PredicatedSink.create({predicate: predicate, delgate: joinSink});
      }

      this.delegate.select(joinSink, skip, limit, order, this.predicate)
*/
    }
  ]
});
<commit_msg>Cleanup.<commit_after>/**
 * @license
 * Copyright 2017 Google Inc. All Rights Reserved.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

foam.CLASS({
  package: 'foam.dao',
  name: 'ManyToManyRelationshipDAO',
  extends: 'foam.dao.RelationshipDAO',

  implements: [ 'foam.mlang.Expressions' ],

  documentation: 'Adapts a DAO based on a *:* Relationship.',

  properties: [
    'junctionProperty',
    'joinDAOKey',
    'targetProperty'
  ],

  methods: [
    function select(sink, skip, limit, order, predicate) {
      var self    = this;
      var joinDAO = this.__context__[this.joinDAOKey];

      return new Promise(function(resolve, reject) {
        self.SUPER(self.MAP(self.junctionProperty)).then(function(array) {
          resolve(joinDAO.select(sink));
        });
      });
    }
  ]
});
<|endoftext|>


<commit_before>import Ember from 'ember';

export default [
  Ember.Object.create({ value: 0, label: "Daily" }),
  Ember.Object.create({ value: 1, label: "Weekly" }),
  Ember.Object.create({ value: 2, label: "Montly" }),
  Ember.Object.create({ value: 3, label: "Yearly" })
];
<commit_msg>We don't support some recurrences by now<commit_after>import Ember from 'ember';

export default [
//  Ember.Object.create({ value: 0, label: "Daily" }),
  Ember.Object.create({ value: 1, label: "Weekly" }),
  Ember.Object.create({ value: 2, label: "Montly" }),
//  Ember.Object.create({ value: 3, label: "Yearly" })
];
<|endoftext|>


<commit_before>// Mathieu Jacomy @ Sciences Po Médialab & WebAtlas
// (requires sigma.js to be loaded)
sigma.publicPrototype.parseGexf = function(gexfPath) {
  // Load XML file:
  var gexfhttp, gexf;
  var sigmaInstance = this;
  gexfhttp = window.XMLHttpRequest ?
    new XMLHttpRequest() :
    new ActiveXObject('Microsoft.XMLHTTP');

  gexfhttp.overrideMimeType('text/xml');
  gexfhttp.open('GET', gexfPath, false);
  gexfhttp.send();
  gexf = gexfhttp.responseXML;

  var viz='http://www.gexf.net/1.2draft/viz'; // Vis namespace
  var i, j, k;

  // Parse Attributes
  // This is confusing, so I'll comment heavily
  var nodesAttributes = [];   // The list of attributes of the nodes of the graph that we build in json
  var edgesAttributes = [];   // The list of attributes of the edges of the graph that we build in json
  var attributesNodes = gexf.getElementsByTagName('attributes');  // In the gexf (that is an xml), the list of xml nodes 'attributes' (note the plural 's')

  for(i = 0; i<attributesNodes.length; i++){
    var attributesNode = attributesNodes[i];  // attributesNode is each xml node 'attributes' (plural)
    if(attributesNode.getAttribute('class') == 'node'){
      var attributeNodes = attributesNode.getElementsByTagName('attribute');  // The list of xml nodes 'attribute' (no 's')
      for(j = 0; j<attributeNodes.length; j++){
        var attributeNode = attributeNodes[j];  // Each xml node 'attribute'

        var id = attributeNode.getAttribute('id'),
          title = attributeNode.getAttribute('title'),
          type = attributeNode.getAttribute('type');

        var attribute = {id:id, title:title, type:type};
        nodesAttributes.push(attribute);

      }
    } else if(attributesNode.getAttribute('class') == 'edge'){
      var attributeNodes = attributesNode.getElementsByTagName('attribute');  // The list of xml nodes 'attribute' (no 's')
      for(j = 0; j<attributeNodes.length; j++){
        var attributeNode = attributeNodes[j];  // Each xml node 'attribute'

        var id = attributeNode.getAttribute('id'),
          title = attributeNode.getAttribute('title'),
          type = attributeNode.getAttribute('type');

        var attribute = {id:id, title:title, type:type};
        edgesAttributes.push(attribute);

      }
    }
  }

  var nodes = []; // The nodes of the graph
  var nodesNodes = gexf.getElementsByTagName('nodes') // The list of xml nodes 'nodes' (plural)

  for(i=0; i<nodesNodes.length; i++){
    var nodesNode = nodesNodes[i];  // Each xml node 'nodes' (plural)
    var nodeNodes = nodesNode.getElementsByTagName('node'); // The list of xml nodes 'node' (no 's')

    for(j=0; j<nodeNodes.length; j++){
      var nodeNode = nodeNodes[j];  // Each xml node 'node' (no 's')

      window.NODE = nodeNode;

      var id = nodeNode.getAttribute('id');
      var label = nodeNode.getAttribute('label') || id;

      //viz
      var size = 1;
      var x = 100 - 200*Math.random();
      var y = 100 - 200*Math.random();
      var color;

      var poorBrowserXmlNsSupport = (nodeNode.getElementsByTagNameNS == null);

      var sizeNodes = nodeNode.getElementsByTagName('size');
      sizeNodes = sizeNodes.length || poorBrowserXmlNsSupport ?
                  sizeNodes :
                  nodeNode.getElementsByTagNameNS('*','size');
      if(sizeNodes.length>0){
        sizeNode = sizeNodes[0];
        size = parseFloat(sizeNode.getAttribute('value'));
      }

      var positionNodes = nodeNode.getElementsByTagName('position');
      positionNodes = positionNodes.length || poorBrowserXmlNsSupport ?
                      positionNodes :
                      nodeNode.getElementsByTagNameNS('*','position');
      if(positionNodes.length>0){
        var positionNode = positionNodes[0];
        x = parseFloat(positionNode.getAttribute('x'));
        y = parseFloat(positionNode.getAttribute('y'));
      }

      var colorNodes = nodeNode.getElementsByTagName('color');
      colorNodes = colorNodes.length || poorBrowserXmlNsSupport ?
                   colorNodes :
                   nodeNode.getElementsByTagNameNS('*','color');
      if(colorNodes.length>0){
        colorNode = colorNodes[0];
        color = '#'+sigma.tools.rgbToHex(parseFloat(colorNode.getAttribute('r')),
                                         parseFloat(colorNode.getAttribute('g')),
                                         parseFloat(colorNode.getAttribute('b')));
      }

      // Create Node
      var node = {label:label, size:size, x:x, y:y, attributes:[], color:color};  // The graph node

      // Attribute values
      var attvalueNodes = nodeNode.getElementsByTagName('attvalue');
      for(k=0; k<attvalueNodes.length; k++){
        var attvalueNode = attvalueNodes[k];
        var attr = attvalueNode.getAttribute('for');
        var val = attvalueNode.getAttribute('value');
        node.attributes.push({attr:attr, val:val});
      }

      sigmaInstance.addNode(id,node);
    }
  }

  var edges = [];
  var edgesNodes = gexf.getElementsByTagName('edges');
  for(i=0; i<edgesNodes.length; i++){
    var edgesNode = edgesNodes[i];
    var edgeNodes = edgesNode.getElementsByTagName('edge');
    for(j=0; j<edgeNodes.length; j++){
      var edgeNode = edgeNodes[j];
      var id = edgeNode.getAttribute('id');
      var source = edgeNode.getAttribute('source');
      var target = edgeNode.getAttribute('target');
      var label = edgeNode.getAttribute('label');
      var edge = {
        id:         id,
        sourceID:   source,
        targetID:   target,
        label:      label,
        attributes: []
      };

      var weight = edgeNode.getAttribute('weight');
      if(weight!=undefined){
        edge['weight'] = weight;
        edge['size'] = weight;
      }

      var attvalueNodes = edgeNode.getElementsByTagName('attvalue');
      for(k=0; k<attvalueNodes.length; k++){
        var attvalueNode = attvalueNodes[k];
        var attr = attvalueNode.getAttribute('for');
        var val = attvalueNode.getAttribute('value');
        edge.attributes.push({attr:attr, val:val});
      }

      sigmaInstance.addEdge(id,source,target,edge);
    }
  }
};
<commit_msg>IE doesn't support namespaces and getElementsByTagNameNS for XML, but it is still possible to retrieve namespaced nodes using getElementsByTagName with a 'theNamespace:theNodeName' argument.<commit_after>// Mathieu Jacomy @ Sciences Po Médialab & WebAtlas
// (requires sigma.js to be loaded)
sigma.publicPrototype.parseGexf = function(gexfPath) {
  // Load XML file:
  var gexfhttp, gexf;
  var sigmaInstance = this;
  gexfhttp = window.XMLHttpRequest ?
    new XMLHttpRequest() :
    new ActiveXObject('Microsoft.XMLHTTP');

  gexfhttp.overrideMimeType('text/xml');
  gexfhttp.open('GET', gexfPath, false);
  gexfhttp.send();
  gexf = gexfhttp.responseXML;

  var viz='http://www.gexf.net/1.2draft/viz'; // Vis namespace
  var i, j, k;

  // Parse Attributes
  // This is confusing, so I'll comment heavily
  var nodesAttributes = [];   // The list of attributes of the nodes of the graph that we build in json
  var edgesAttributes = [];   // The list of attributes of the edges of the graph that we build in json
  var attributesNodes = gexf.getElementsByTagName('attributes');  // In the gexf (that is an xml), the list of xml nodes 'attributes' (note the plural 's')

  for(i = 0; i<attributesNodes.length; i++){
    var attributesNode = attributesNodes[i];  // attributesNode is each xml node 'attributes' (plural)
    if(attributesNode.getAttribute('class') == 'node'){
      var attributeNodes = attributesNode.getElementsByTagName('attribute');  // The list of xml nodes 'attribute' (no 's')
      for(j = 0; j<attributeNodes.length; j++){
        var attributeNode = attributeNodes[j];  // Each xml node 'attribute'

        var id = attributeNode.getAttribute('id'),
          title = attributeNode.getAttribute('title'),
          type = attributeNode.getAttribute('type');

        var attribute = {id:id, title:title, type:type};
        nodesAttributes.push(attribute);

      }
    } else if(attributesNode.getAttribute('class') == 'edge'){
      var attributeNodes = attributesNode.getElementsByTagName('attribute');  // The list of xml nodes 'attribute' (no 's')
      for(j = 0; j<attributeNodes.length; j++){
        var attributeNode = attributeNodes[j];  // Each xml node 'attribute'

        var id = attributeNode.getAttribute('id'),
          title = attributeNode.getAttribute('title'),
          type = attributeNode.getAttribute('type');

        var attribute = {id:id, title:title, type:type};
        edgesAttributes.push(attribute);

      }
    }
  }

  var nodes = []; // The nodes of the graph
  var nodesNodes = gexf.getElementsByTagName('nodes') // The list of xml nodes 'nodes' (plural)
  var getElementsByTagNameNS = function(node, ns, tagName) {
	  if (node.getElementsByTagNameNS) {
		  return node.getElementsByTagNameNS(ns, tagName);
	  }

	  return node.getElementsByTagName(ns + ':' + tagName);
  };

  for(i=0; i<nodesNodes.length; i++){
    var nodesNode = nodesNodes[i];  // Each xml node 'nodes' (plural)
    var nodeNodes = nodesNode.getElementsByTagName('node'); // The list of xml nodes 'node' (no 's')

    for(j=0; j<nodeNodes.length; j++){
      var nodeNode = nodeNodes[j];  // Each xml node 'node' (no 's')

      window.NODE = nodeNode;

      var id = nodeNode.getAttribute('id');
      var label = nodeNode.getAttribute('label') || id;

      //viz
      var size = 1;
      var x = 100 - 200*Math.random();
      var y = 100 - 200*Math.random();
      var color;

      var sizeNodes = nodeNode.getElementsByTagName('size');
      sizeNodes = sizeNodes.length ?
                  sizeNodes :
                  getElementsByTagNameNS(nodeNode, 'viz','size');
      if(sizeNodes.length>0){
        sizeNode = sizeNodes[0];
        size = parseFloat(sizeNode.getAttribute('value'));
      }

      var positionNodes = nodeNode.getElementsByTagName('position');
      positionNodes = positionNodes.length ?
                      positionNodes :
                      getElementsByTagNameNS(nodeNode, 'viz','position');
      if(positionNodes.length>0){
        var positionNode = positionNodes[0];
        x = parseFloat(positionNode.getAttribute('x'));
        y = parseFloat(positionNode.getAttribute('y'));
      }

      var colorNodes = nodeNode.getElementsByTagName('color');
      colorNodes = colorNodes.length ?
                   colorNodes :
                   getElementsByTagNameNS(nodeNode, 'viz','color');
      if(colorNodes.length>0){
        colorNode = colorNodes[0];
        color = '#'+sigma.tools.rgbToHex(parseFloat(colorNode.getAttribute('r')),
                                         parseFloat(colorNode.getAttribute('g')),
                                         parseFloat(colorNode.getAttribute('b')));
      }

      // Create Node
      var node = {label:label, size:size, x:x, y:y, attributes:[], color:color};  // The graph node

      // Attribute values
      var attvalueNodes = nodeNode.getElementsByTagName('attvalue');
      for(k=0; k<attvalueNodes.length; k++){
        var attvalueNode = attvalueNodes[k];
        var attr = attvalueNode.getAttribute('for');
        var val = attvalueNode.getAttribute('value');
        node.attributes.push({attr:attr, val:val});
      }

      sigmaInstance.addNode(id,node);
    }
  }

  var edges = [];
  var edgesNodes = gexf.getElementsByTagName('edges');
  for(i=0; i<edgesNodes.length; i++){
    var edgesNode = edgesNodes[i];
    var edgeNodes = edgesNode.getElementsByTagName('edge');
    for(j=0; j<edgeNodes.length; j++){
      var edgeNode = edgeNodes[j];
      var id = edgeNode.getAttribute('id');
      var source = edgeNode.getAttribute('source');
      var target = edgeNode.getAttribute('target');
      var label = edgeNode.getAttribute('label');
      var edge = {
        id:         id,
        sourceID:   source,
        targetID:   target,
        label:      label,
        attributes: []
      };

      var weight = edgeNode.getAttribute('weight');
      if(weight!=undefined){
        edge['weight'] = weight;
        edge['size'] = weight;
      }

      var attvalueNodes = edgeNode.getElementsByTagName('attvalue');
      for(k=0; k<attvalueNodes.length; k++){
        var attvalueNode = attvalueNodes[k];
        var attr = attvalueNode.getAttribute('for');
        var val = attvalueNode.getAttribute('value');
        edge.attributes.push({attr:attr, val:val});
      }

      sigmaInstance.addEdge(id,source,target,edge);
    }
  }
};<|endoftext|>


<commit_before>'use strict';

angular.module('frontendApp')
  .controller('BoardCtrl', function ($scope, Board) {
    $scope.isShip = function(val){
      return val === 's' && $scope.settings.gameStart;
    };

    $scope.isMiss = function(val){
      return val === 'm';
    };

    $scope.isHit = function(val){
      return true;
    };

    $scope.$watch('board', function(newVal, oldVal){
      //Update board logic
    }, true);
  });
<commit_msg>Travis fail test 2<commit_after>'use strict';

angular.module('frontendApp')
  .controller('BoardCtrl', function ($scope, Board) {
    $scope.isShip = function(val){
      return val === 's' && $scope.settings.gameStart;
    };

    $scope.isMiss = function(val){
      return val === 'm';
    };

    $scope.isHit = function(val){
      return val === 'h';
    };

    $scope.$watch('board', function(newVal, oldVal){
      //Update board logic
    }, true);
  });
<|endoftext|>


<commit_before>import React, {Component} from 'react';
import PropTypes from 'prop-types';
import List from 'material-ui/List';
import { withStyles } from 'material-ui/styles';
import axios from 'axios';

import CustomProgress from '../CustomProgress/CustomProgress.js';
import CategoryWidget from '../CategoryWidget/CategoryWidget.js';
import CategoryCreator from '../CategoryCreator/CategoryCreator.js';

axios.defaults.xsrfCookieName = 'csrftoken';
axios.defaults.xsrfHeaderName = 'X-CSRFToken';

const styles = theme => ({
  root: {
    margin: '25px auto 0px',
    width: '100%',
    maxWidth: 360,
    backgroundColor: theme.palette.background.paper,
  },
  progressWrapper: {
    display: 'flex',
  },
  progress: {
    margin: '25px auto 0px',
  },
});

class CategoryView extends Component {
  constructor(props) {
    super(props);
    this.state = {
      error: null,
      isLoaded: false,
      data: []
    };
    this.handleDelete = this.handleDelete.bind(this);
    this.handleCreate = this.handleCreate.bind(this);
    this.handleUpdate = this.handleUpdate.bind(this);
  }

  // https://reactjs.org/docs/faq-ajax.html
  componentDidMount() {
    axios.get('/api/categories')
      .then(
        (response) => {
          this.setState({
            isLoaded: true,
            data: response.data
          });
        },
        (error) => {
          this.setState({
            isLoaded: true,
            error
          });
        }
      );
  }

  // Create, Update, Delete handlers.
  handleCreate(e, category, onSuccess, onError) {
    axios.post('/api/categories/', {category: category})
      .then(
        (response) => {
          onSuccess();
          this.setState((prevState) => {
            return {data: prevState.data.concat([response.data])};
          });
        },
        (error) => {
          if (error.response && error.response.status === 400) {
            onError(error);
          } else {
            this.setState({error});
          }
        }
      );
  }

  handleUpdate(e, category, url, onSuccess, onError) {
    axios.patch(url, {category: category})
      .then(
        (response) => {
          onSuccess();
          this.setState((prevState) => {
            let oldData = prevState.data
            let replaceIndex = oldData.findIndex(item => item.url === response.data.url);
            let newData = oldData.splice(replaceIndex, 1, response.data);
            return newData;
          })
        },
        (error) => {
          if (error.response && error.response.status === 400) {
            onError(error);
          } else {
            this.setState({error});
          }
        }
      );
  }

  handleDelete(url) {
    axios.delete(url)
      .then(
        (response) => {
          this.setState((prevState) => {
            return {data: prevState.data.filter(datum => datum.url !==  url)}
          });
        },
        (error) => {
          this.setState({
            error
          });
        }
      );
  }

  render() {
    const { error, isLoaded, data } = this.state;
    if (error) {
      return
Error: {error.message}
;
    } else if (!isLoaded) {
      return (

          <CustomProgress />

      );
    } else {
      let categories = data.map(datum =>
        <CategoryWidget
          key={datum.url}
          url={datum.url}
          rank={datum.rank}
          category={datum.category}
          handleDelete={this.handleDelete}
          handleUpdate={this.handleUpdate}>
        </CategoryWidget>
      );
      return (

          <List disablePadding>
            {categories}
            <CategoryCreator handleCreate={this.handleCreate}></CategoryCreator>
          </List>

      )
    }
  }
}

CategoryView.propTypes = {
  handleBadToken: PropTypes.func.isRequired,
}

export default withStyles(styles)(CategoryView)
<commit_msg>CategoryView handleBadToken handling<commit_after>import React, {Component} from 'react';
import PropTypes from 'prop-types';
import List from 'material-ui/List';
import { withStyles } from 'material-ui/styles';
import axios from 'axios';

import CustomProgress from '../CustomProgress/CustomProgress.js';
import CategoryWidget from '../CategoryWidget/CategoryWidget.js';
import CategoryCreator from '../CategoryCreator/CategoryCreator.js';

axios.defaults.xsrfCookieName = 'csrftoken';
axios.defaults.xsrfHeaderName = 'X-CSRFToken';

const styles = theme => ({
  root: {
    margin: '25px auto 0px',
    width: '100%',
    maxWidth: 360,
    backgroundColor: theme.palette.background.paper,
  },
  progressWrapper: {
    display: 'flex',
  },
  progress: {
    margin: '25px auto 0px',
  },
});

class CategoryView extends Component {
  constructor(props) {
    super(props);
    this.state = {
      error: null,
      isLoaded: false,
      data: []
    };
    this.handleDelete = this.handleDelete.bind(this);
    this.handleCreate = this.handleCreate.bind(this);
    this.handleUpdate = this.handleUpdate.bind(this);
  }

  // https://reactjs.org/docs/faq-ajax.html
  componentDidMount() {
    axios.get('/api/categories')
      .then(
        (response) => {
          this.setState({
            isLoaded: true,
            data: response.data
          });
        },
        (error) => {
          if (error.response && error.response.status === 401) {
            this.props.handleBadToken();
          } else {
            this.setState({
              isLoaded: true,
              error
            });
          }
        }
      );
  }

  // Create, Update, Delete handlers.
  handleCreate(e, category, onSuccess, onError) {
    axios.post('/api/categories/', {category: category})
      .then(
        (response) => {
          onSuccess();
          this.setState((prevState) => {
            return {data: prevState.data.concat([response.data])};
          });
        },
        (error) => {
          if (error.response && error.response.status === 400) {
            onError(error);
          } else if (error.response && error.response.status === 401) {
            this.props.handleBadToken();
          } else {
            this.setState({error});
          }
        }
      );
  }

  handleUpdate(e, category, url, onSuccess, onError) {
    axios.patch(url, {category: category})
      .then(
        (response) => {
          onSuccess();
          this.setState((prevState) => {
            let oldData = prevState.data
            let replaceIndex = oldData.findIndex(item => item.url === response.data.url);
            let newData = oldData.splice(replaceIndex, 1, response.data);
            return newData;
          })
        },
        (error) => {
          if (error.response && error.response.status === 400) {
            onError(error);
          } else if (error.response && error.response.status === 401) {
            this.props.handleBadToken();
          } else {
            this.setState({error});
          }
        }
      );
  }

  handleDelete(url) {
    axios.delete(url)
      .then(
        (response) => {
          this.setState((prevState) => {
            return {data: prevState.data.filter(datum => datum.url !==  url)}
          });
        },
        (error) => {
          if (error.response && error.response.status === 401) {
            this.props.handleBadToken();
          } else {
            this.setState({
              error
            });
          }
        }
      );
  }

  render() {
    const { error, isLoaded, data } = this.state;
    if (error) {
      return
Error: {error.message}
;
    } else if (!isLoaded) {
      return (

          <CustomProgress />

      );
    } else {
      let categories = data.map(datum =>
        <CategoryWidget
          key={datum.url}
          url={datum.url}
          rank={datum.rank}
          category={datum.category}
          handleDelete={this.handleDelete}
          handleUpdate={this.handleUpdate}>
        </CategoryWidget>
      );
      return (

          <List disablePadding>
            {categories}
            <CategoryCreator handleCreate={this.handleCreate}></CategoryCreator>
          </List>

      )
    }
  }
}

CategoryView.propTypes = {
  handleBadToken: PropTypes.func.isRequired,
}

export default withStyles(styles)(CategoryView)
<|endoftext|>


<commit_before>import React from 'react';
import { observer } from 'mobx-react-lite';

const SpacesCarousel = observer(({ spaces }) => {
  return (
    spaces.length > 0 && (

        {spaces.map(space => (

{space.id}

        ))}

Add new space

    )
  );
});

export default SpacesCarousel;
<commit_msg>only show spaces carousel if spaces > 1<commit_after>import React from 'react';
import { observer } from 'mobx-react-lite';

const SpacesCarousel = observer(({ spaces }) => {
  return (
    spaces.length > 1 && (

        {spaces.map((space, index) => (

{index}

        ))}

Add new space

    )
  );
});

export default SpacesCarousel;
<|endoftext|>


<commit_before>import React from 'react';
import MediaQuery from 'react-responsive';
import { connect } from 'react-redux';
import { Map, fromJS} from 'immutable'

import { Card, CardTitle, CardText } from 'material-ui/Card';
import { List, ListItem } from 'material-ui/List';
import Paper from 'material-ui/Paper';
import Subheader from 'material-ui/Subheader';
import Checkbox from 'material-ui/Checkbox'

import {
  selectDrawerOpen,
  selectNodesMetadata,
  selectNodeName,
  selectNodeMeasurements,
  selectMeasurementName,
} from '../App/selectors';
import { drawerChange } from '../App/actions';
import ChartCard from '../ChartCard/index'
import {
  selectShownMeasurements,
} from './selectors'
import {
  showMeasurement,
  hideMeasurement,
  changeShownMeasurements,
} from './actions'
import {
  requestNewLiveData,
  cancelRequest,
} from '../RequestManager/actions'
import {
  selectActiveRequests,
} from '../RequestManager/selectors'
import {
  PENDING_STATE
} from '../RequestManager/constants'

// Subcomponent of NodePage that renders right toolbar
class RightBar extends React.Component {
  constructor(props) {
    super(props)
    this.state = { measurementRequestMap: new Map() }
    this.updateSubscriptions = this.updateSubscriptions.bind(this)
  }

  updateSubscriptions(newMeasurementIds, mounting = false) {
    const nodeId = parseInt(this.props.params.nodeId)
    // If measurements have changed, we should drop current subscriptions and make new ones
    if (!newMeasurementIds.equals(this.props.measurementIds) || mounting) {
      // Drop current subscriptions
      const subscriptions = this.state.measurementRequestMap
      let cancelRequestActions = []
      subscriptions.forEach((requestId, measurementId) => {
        cancelRequestActions.push(cancelRequest(requestId))
      })
      cancelRequestActions.forEach((action) => this.props.dispatch(action))

      // Create new ones
      let newMeasurementRequestMap = new Map()
      let subscriptionRequestActions = []
      newMeasurementIds.forEach((measurementId) => {
        const subscriptionRequestAction = requestNewLiveData(nodeId, measurementId)
        newMeasurementRequestMap =
          newMeasurementRequestMap.set(measurementId, subscriptionRequestAction.message.request_id)
        subscriptionRequestActions.push(subscriptionRequestAction)
      })
      this.setState({ measurementRequestMap: newMeasurementRequestMap })
      subscriptionRequestActions.forEach((action) => this.props.dispatch(action))
    }
  }

  // Subscribe to live data on mount
  componentWillMount() {
    this.updateSubscriptions(this.props.measurementIds, true)
  }

  componentWillReceiveProps(nextProps) {
    this.updateSubscriptions(nextProps.measurementIds)
  }

  componentWillUnmount() {
    // Calling updateSubscriptions with empty measurementIds list will result in request cleanup
    this.updateSubscriptions(fromJS([]))
  }

  render() {
    const nodeMetadata = this.props.nodesMetadata.get(this.props.params.nodeId)
    return (
      <List>
        <Subheader>{nodeMetadata.get("name")}</Subheader>
        {this.props.measurementIds &&
          this.props.measurementIds.map((id) => {
            const request =
              this.props.activeRequests.get(this.state.measurementRequestMap.get(id))
            let val = null
            if (request && request.get('state') !== PENDING_STATE) {
              val = request.get('data').value
            }
            return (<ListItem
              leftCheckbox={
                <Checkbox
                  checked={this.props.measurementIdsShown.get(id) === undefined ?
                    false : this.props.measurementIdsShown.get(id)}
                  onClick={this.props.measurementIdsShown.get(id) ?
                    this.props.onHideMeasurement(id) : this.props.onShowMeasurement(id)}
                />
              }
              key={id}
            >
              {this.props.measurementNameGetter(id.toString())}
              {val ? ": " + val : ""}
            </ListItem>)})
        }
      </List>
    )
  }
}

class NodePage extends React.Component {
  componentWillMount() {
    this.props.sendCloseDrawer()
    this.props.onChangeShownMeasurements(this.props.measurementIds)
  }

  componentWillReceiveProps(nextProps) {
    const measurementIds = nextProps.measurementIds
    // We updated list of measurements (e.g. node metadata was downloaded)
    if (measurementIds.size !== this.props.measurementIdsShown.size) {
      this.props.onChangeShownMeasurements(measurementIds)
    }
  }

  render() {
    // Define chart and right bar styles
    const chartStyle = {
      width: 800,
      margin: 20,
      textAlign: 'center',
      display: 'inline-block',
    };
    const rightBarStyle = {
      width: 200,
      height: "100vh",
      margin: 0,
      textAlign: 'left',
      display: 'inline-block',
      float: 'right',
    }

    // Render error panel if node metadata was not found
    const nodeMetadata = this.props.nodesMetadata.get(this.props.params.nodeId);
    if (!nodeMetadata) {
      return (
        <Paper style={chartStyle} zDepth={3}>
          <Card>
            <CardTitle title="Error"/>
            <CardText>
              Node not found
            </CardText>
          </Card>
        </Paper>
      )
    }

    return (

        <MediaQuery minWidth={1050}>
          <Paper style={rightBarStyle} zDepth={3}>
            <RightBar {...this.props}/>
          </Paper>
          <Paper style={chartStyle} zDepth={3}>
            <ChartCard {...this.props}/>
          </Paper>
        </MediaQuery>
        <MediaQuery maxWidth={1049}>
          <Paper zDepth={3}>
            <RightBar {...this.props}/>
          </Paper>
          <Paper zDepth={3} style={{ margin: 20 }}>
            <ChartCard {...this.props}/>
          </Paper>
        </MediaQuery>

    )
  }
}

const mapDispatchToProps = (dispatch) => {
  return {
    sendCloseDrawer: () => dispatch(drawerChange(false)),
    onShowMeasurement: (measurementId) => () => dispatch(showMeasurement(measurementId)),
    onHideMeasurement: (measurementId) => () => dispatch(hideMeasurement(measurementId)),
    onChangeShownMeasurements: (measurementIds) => dispatch(changeShownMeasurements(measurementIds)),
    dispatch,
  }
}

const mapStateToProps = (state, ownProps) => {
  let nodeId = ownProps.params.nodeId.toString()
  let name = selectNodeName(state)(nodeId)
  let measurementIds = selectNodeMeasurements(state)(nodeId)
  let getMeasurementName = selectMeasurementName(state)
  let measurementIdsShown = selectShownMeasurements(state)
  return {
    nodeName: name,
    drawerOpen: selectDrawerOpen(state),
    nodesMetadata: selectNodesMetadata(state),
    measurementIds: measurementIds,
    measurementNameGetter: getMeasurementName,
    measurementIdsShown: measurementIdsShown,
    activeRequests: selectActiveRequests(state),
  }
}

export default connect(mapStateToProps, mapDispatchToProps)(NodePage)
<commit_msg>Fix warning<commit_after>import React from 'react';
import MediaQuery from 'react-responsive';
import { connect } from 'react-redux';
import { Map, fromJS} from 'immutable'

import { Card, CardTitle, CardText } from 'material-ui/Card';
import { List, ListItem } from 'material-ui/List';
import Paper from 'material-ui/Paper';
import Subheader from 'material-ui/Subheader';
import Checkbox from 'material-ui/Checkbox'

import {
  selectDrawerOpen,
  selectNodesMetadata,
  selectNodeName,
  selectNodeMeasurements,
  selectMeasurementName,
} from '../App/selectors';
import { drawerChange } from '../App/actions';
import ChartCard from '../ChartCard/index'
import {
  selectShownMeasurements,
} from './selectors'
import {
  showMeasurement,
  hideMeasurement,
  changeShownMeasurements,
} from './actions'
import {
  requestNewLiveData,
  cancelRequest,
} from '../RequestManager/actions'
import {
  selectActiveRequests,
} from '../RequestManager/selectors'
import {
  PENDING_STATE
} from '../RequestManager/constants'

// Subcomponent of NodePage that renders right toolbar
class RightBar extends React.Component {
  constructor(props) {
    super(props)
    this.state = { measurementRequestMap: new Map() }
    this.updateSubscriptions = this.updateSubscriptions.bind(this)
  }

  updateSubscriptions(newMeasurementIds, mounting = false) {
    const nodeId = parseInt(this.props.params.nodeId, 10)
    // If measurements have changed, we should drop current subscriptions and make new ones
    if (!newMeasurementIds.equals(this.props.measurementIds) || mounting) {
      // Drop current subscriptions
      const subscriptions = this.state.measurementRequestMap
      let cancelRequestActions = []
      subscriptions.forEach((requestId, measurementId) => {
        cancelRequestActions.push(cancelRequest(requestId))
      })
      cancelRequestActions.forEach((action) => this.props.dispatch(action))

      // Create new ones
      let newMeasurementRequestMap = new Map()
      let subscriptionRequestActions = []
      newMeasurementIds.forEach((measurementId) => {
        const subscriptionRequestAction = requestNewLiveData(nodeId, measurementId)
        newMeasurementRequestMap =
          newMeasurementRequestMap.set(measurementId, subscriptionRequestAction.message.request_id)
        subscriptionRequestActions.push(subscriptionRequestAction)
      })
      this.setState({ measurementRequestMap: newMeasurementRequestMap })
      subscriptionRequestActions.forEach((action) => this.props.dispatch(action))
    }
  }

  // Subscribe to live data on mount
  componentWillMount() {
    this.updateSubscriptions(this.props.measurementIds, true)
  }

  componentWillReceiveProps(nextProps) {
    this.updateSubscriptions(nextProps.measurementIds)
  }

  componentWillUnmount() {
    // Calling updateSubscriptions with empty measurementIds list will result in request cleanup
    this.updateSubscriptions(fromJS([]))
  }

  render() {
    const nodeMetadata = this.props.nodesMetadata.get(this.props.params.nodeId)
    return (
      <List>
        <Subheader>{nodeMetadata.get("name")}</Subheader>
        {this.props.measurementIds &&
          this.props.measurementIds.map((id) => {
            const request =
              this.props.activeRequests.get(this.state.measurementRequestMap.get(id))
            let val = null
            if (request && request.get('state') !== PENDING_STATE) {
              val = request.get('data').value
            }
            return (<ListItem
              leftCheckbox={
                <Checkbox
                  checked={this.props.measurementIdsShown.get(id) === undefined ?
                    false : this.props.measurementIdsShown.get(id)}
                  onClick={this.props.measurementIdsShown.get(id) ?
                    this.props.onHideMeasurement(id) : this.props.onShowMeasurement(id)}
                />
              }
              key={id}
            >
              {this.props.measurementNameGetter(id.toString())}
              {val ? ": " + val : ""}
            </ListItem>)})
        }
      </List>
    )
  }
}

class NodePage extends React.Component {
  componentWillMount() {
    this.props.sendCloseDrawer()
    this.props.onChangeShownMeasurements(this.props.measurementIds)
  }

  componentWillReceiveProps(nextProps) {
    const measurementIds = nextProps.measurementIds
    // We updated list of measurements (e.g. node metadata was downloaded)
    if (measurementIds.size !== this.props.measurementIdsShown.size) {
      this.props.onChangeShownMeasurements(measurementIds)
    }
  }

  render() {
    // Define chart and right bar styles
    const chartStyle = {
      width: 800,
      margin: 20,
      textAlign: 'center',
      display: 'inline-block',
    };
    const rightBarStyle = {
      width: 200,
      height: "100vh",
      margin: 0,
      textAlign: 'left',
      display: 'inline-block',
      float: 'right',
    }

    // Render error panel if node metadata was not found
    const nodeMetadata = this.props.nodesMetadata.get(this.props.params.nodeId);
    if (!nodeMetadata) {
      return (
        <Paper style={chartStyle} zDepth={3}>
          <Card>
            <CardTitle title="Error"/>
            <CardText>
              Node not found
            </CardText>
          </Card>
        </Paper>
      )
    }

    return (

        <MediaQuery minWidth={1050}>
          <Paper style={rightBarStyle} zDepth={3}>
            <RightBar {...this.props}/>
          </Paper>
          <Paper style={chartStyle} zDepth={3}>
            <ChartCard {...this.props}/>
          </Paper>
        </MediaQuery>
        <MediaQuery maxWidth={1049}>
          <Paper zDepth={3}>
            <RightBar {...this.props}/>
          </Paper>
          <Paper zDepth={3} style={{ margin: 20 }}>
            <ChartCard {...this.props}/>
          </Paper>
        </MediaQuery>

    )
  }
}

const mapDispatchToProps = (dispatch) => {
  return {
    sendCloseDrawer: () => dispatch(drawerChange(false)),
    onShowMeasurement: (measurementId) => () => dispatch(showMeasurement(measurementId)),
    onHideMeasurement: (measurementId) => () => dispatch(hideMeasurement(measurementId)),
    onChangeShownMeasurements: (measurementIds) => dispatch(changeShownMeasurements(measurementIds)),
    dispatch,
  }
}

const mapStateToProps = (state, ownProps) => {
  let nodeId = ownProps.params.nodeId.toString()
  let name = selectNodeName(state)(nodeId)
  let measurementIds = selectNodeMeasurements(state)(nodeId)
  let getMeasurementName = selectMeasurementName(state)
  let measurementIdsShown = selectShownMeasurements(state)
  return {
    nodeName: name,
    drawerOpen: selectDrawerOpen(state),
    nodesMetadata: selectNodesMetadata(state),
    measurementIds: measurementIds,
    measurementNameGetter: getMeasurementName,
    measurementIdsShown: measurementIdsShown,
    activeRequests: selectActiveRequests(state),
  }
}

export default connect(mapStateToProps, mapDispatchToProps)(NodePage)
<|endoftext|>