<commit_before>import classNames from 'classnames';
import {Confirm, Dropdown} from 'reactjs-components';
import mixin from 'reactjs-mixin';
import prettycron from 'prettycron';
/* eslint-disable no-unused-vars */
import React from 'react';
/* eslint-enable no-unused-vars */
import {routerShape} from 'react-router';

import {StoreMixin} from 'mesosphere-shared-reactjs';

import Icon from '../../components/Icon';
import JobConfiguration from './JobConfiguration';
import JobFormModal from '../../components/modals/JobFormModal';
import JobRunHistoryTable from './JobRunHistoryTable';
import JobsBreadcrumbs from '../../components/breadcrumbs/JobsBreadcrumbs';
import Loader from '../../components/Loader';
import MetronomeStore from '../../stores/MetronomeStore';
import Page from '../../components/Page';
import RequestErrorMsg from '../../components/RequestErrorMsg';
import StringUtil from '../../utils/StringUtil';
import TabsMixin from '../../mixins/TabsMixin';
import TimeAgo from '../../components/TimeAgo';
import TaskStates from '../../../../plugins/services/src/js/constants/TaskStates';

const METHODS_TO_BIND = [
  'closeDialog',
  'handleEditButtonClick',
  'handleMoreDropdownSelection',
  'handleRunNowButtonClick',
  'onMetronomeStoreJobDeleteError',
  'onMetronomeStoreJobDeleteSuccess',
  'onMetronomeStoreJobDetailError',
  'onMetronomeStoreJobDetailChange'
];

const JobActionItem = {
  EDIT: 'edit',
  DESTROY: 'destroy',
  SCHEDULE_DISABLE: 'schedule_disable',
  SCHEDULE_ENABLE: 'schedule_enable',
  MORE: 'more'
};

class JobDetailPage extends mixin(StoreMixin, TabsMixin) {
  constructor() {
    super(...arguments);

    this.store_listeners = [{
      name: 'metronome',
      events: [
        'jobDeleteSuccess',
        'jobDeleteError',
        'jobDetailChange',
        'jobDetailError',
        'jobRunError',
        'jobRunSuccess',
        'jobScheduleUpdateError',
        'jobScheduleUpdateSuccess'
      ],
      suppressUpdate: false
    }];

    this.tabs_tabs = {
      runHistory: 'Run History',
      configuration: 'Configuration'
    };

    this.state = {
      currentTab: Object.keys(this.tabs_tabs).shift(),
      disabledDialog: null,
      jobActionDialog: null,
      errorMsg: null,
      errorCount: 0,
      isJobFormModalOpen: false,
      isLoading: true
    };

    METHODS_TO_BIND.forEach((method) => {
      this[method] = this[method].bind(this);
    });
  }

  componentDidMount() {
    super.componentDidMount(...arguments);
    MetronomeStore.monitorJobDetail(this.props.params.id);
  }

  componentWillUnmount() {
    super.componentWillUnmount(...arguments);
    MetronomeStore.stopJobDetailMonitor(this.props.params.id);
  }

  onMetronomeStoreJobDeleteError(id, {message:errorMsg}) {
    if (id !== this.props.params.id || errorMsg == null) {
      return;
    }

    this.setState({
      jobActionDialog: JobActionItem.DESTROY,
      disabledDialog: null,
      errorMsg
    });
  }

  onMetronomeStoreJobDeleteSuccess() {
    this.closeDialog();
    this.context.router.push('/jobs');
  }

  onMetronomeStoreJobDetailError() {
    this.setState({errorCount: this.state.errorCount + 1});
  }

  onMetronomeStoreJobDetailChange() {
    this.setState({errorCount: 0, isLoading: false});
  }

  handleEditButtonClick() {
    this.setState({jobActionDialog: JobActionItem.EDIT});
  }

  handleRunNowButtonClick() {
    let job = MetronomeStore.getJob(this.props.params.id);

    MetronomeStore.runJob(job.getId());
  }

  handleAcceptDestroyDialog(stopCurrentJobRuns = false) {
    this.setState({disabledDialog: JobActionItem.DESTROY}, () => {
      MetronomeStore.deleteJob(this.props.params.id, stopCurrentJobRuns);
    });
  }

  handleMoreDropdownSelection(selection) {
    if (selection.id === JobActionItem.SCHEDULE_DISABLE) {
      MetronomeStore.toggleSchedule(this.props.params.id, false);
      return;
    }

    if (selection.id === JobActionItem.SCHEDULE_ENABLE) {
      MetronomeStore.toggleSchedule(this.props.params.id, true);
      return;
    }

    this.setState({jobActionDialog: selection.id});
  }

  closeDialog() {
    this.setState({
      disabledDialog: null,
      errorMsg: null,
      jobActionDialog: null
    });
  }

  getActionButtons() {
    let job = MetronomeStore.getJob(this.props.params.id);
    let dropdownItems = [];
    let [schedule] = job.getSchedules();

    dropdownItems.push({
      className: 'hidden',
      html: 'More',
      id: JobActionItem.MORE
    });

    if (schedule != null && schedule.enabled) {
      dropdownItems.push({
        html: 'Disable Schedule',
        id: JobActionItem.SCHEDULE_DISABLE
      });
    }

    if (schedule != null && !schedule.enabled) {
      dropdownItems.push({
        html: 'Enable Schedule',
        id: JobActionItem.SCHEDULE_ENABLE
      });
    }

    dropdownItems.push({
      html: Destroy,
      id: JobActionItem.DESTROY
    });

    return [
      <button
        className="button button-stroke"
        key="edit"
        onClick={this.handleEditButtonClick}>
        Edit
      </button>,
      <button
        className="button button-stroke"
        key="run-now"
        onClick={this.handleRunNowButtonClick}>
        Run Now
      </button>,
      <Dropdown
        anchorRight={true}
        buttonClassName="dropdown-toggle button button-stroke"
        dropdownMenuClassName="dropdown-menu"
        dropdownMenuListClassName="dropdown-menu-list"
        dropdownMenuListItemClassName="clickable"
        initialID="more"
        items={dropdownItems}
        key="more"
        onItemSelection={this.handleMoreDropdownSelection}
        persistentID="more"
        scrollContainer=".gm-scroll-view"
        scrollContainerParentSelector=".gm-prevented"
        transition={true}
        wrapperClassName="dropdown" />
    ];
  }

  getDestroyConfirmDialog() {
    const {id} = this.props.params;
    const {disabledDialog, jobActionDialog, errorMsg} = this.state;
    let stopCurrentJobRuns = false;
    let actionButtonLabel = 'Destroy Job';
    let message = `Are you sure you want to destroy ${id}? ` +
      'This action is irreversible.';

    if (/stopCurrentJobRuns=true/.test(errorMsg)) {
      actionButtonLabel = 'Stop Current Runs and Destroy Job';
      stopCurrentJobRuns = true;
      message = `Couldn't destroy ${id} as there are currently active job ` +
        'runs. Do you want to stop all runs and destroy the job?';
    }

    let content = (

          Destroy Job

{message}

    );

    return (
      <Confirm children={content}
        disabled={disabledDialog === JobActionItem.DESTROY}
        open={jobActionDialog === JobActionItem.DESTROY}
        onClose={this.closeDialog}
        leftButtonText="Cancel"
        leftButtonCallback={this.closeDialog}
        rightButtonText={actionButtonLabel}
        rightButtonClassName="button button-danger"
        rightButtonCallback=
          {this.handleAcceptDestroyDialog.bind(this, stopCurrentJobRuns)} />
    );
  }

  getErrorScreen() {
    return (

        <RequestErrorMsg />

    );
  }

  getLoadingScreen() {
    return (
      <Page>
        <Page.Header breadcrumbs={<JobsBreadcrumbs/>} />
        <Loader />
      </Page>
    );
  }

  getNavigationTabs() {
    return (

        {this.tabs_getUnroutedTabs()}

    );
  }

  getPrettySchedule(job) {
    let schedules = job.getSchedules();
    if (schedules == null || schedules.length === 0) {
      return null;
    }

    let schedule = schedules[0];
    if (schedule.enabled) {
      return prettycron.toString(schedule.cron);
    }
  }

  getSubTitle(job) {
    let nodes = [];
    let scheduleText = this.getPrettySchedule(job);
    let longestRunningTask = null;
    let longestRunningActiveRun = job.getActiveRuns()
      .getLongestRunningActiveRun();

    if (longestRunningActiveRun != null) {
      longestRunningTask = longestRunningActiveRun.getTasks()
        .getLongestRunningTask();
    }

    if (longestRunningTask == null && scheduleText == null) {
      return null;
    }

    if (scheduleText != null) {
      nodes.push(

          <Icon
            className="icon-margin-right"
            key="schedule-icon"
            color="grey"
            id="repeat"
            size="mini" />
          Scheduled {StringUtil.lowercase(scheduleText)}

      );
    }

    if (longestRunningTask != null) {
      let dateCompleted = longestRunningTask.getDateCompleted();

      let status = TaskStates[longestRunningTask.getStatus()];
      let statusClasses = classNames('job-details-header-status', {
        'text-success': status.stateTypes.includes('success')
        && !status.stateTypes.includes('failure'),
        'text-danger': status.stateTypes.includes('failure')
      });

      let timePrefix = null;
      let shouldSuppressRelativeTime = dateCompleted == null;

      if (shouldSuppressRelativeTime) {
        timePrefix = 'for ';
      }

      nodes.push(

          {status.displayName}
        ,

          <TimeAgo
            prefix={timePrefix}
            suppressSuffix={shouldSuppressRelativeTime}
            time={longestRunningActiveRun.getDateCreated()} />

      );
    }

    return nodes;
  }

  renderConfigurationTabView(job) {
    return <JobConfiguration job={job} />;
  }

  renderRunHistoryTabView(job) {
    return <JobRunHistoryTable job={job} />;
  }

  render() {
    if (this.state.errorCount > 3) {
      return this.getErrorScreen();
    }

    if (this.state.isLoading) {
      return this.getLoadingScreen();
    }

    // TaskDetailView
    if (this.props.params.taskID) {
      return this.props.children;
    }

    let job = MetronomeStore.getJob(this.props.params.id);

    let tabs = [
      {label: 'Run History', callback: () => { this.setState({currentTab: 'runHistory'}); } },
      {label: 'Configuration', callback: () => { this.setState({currentTab: 'configuration'}); } }
    ];

    return (
      <Page>
        <Page.Header
          breadcrumbs={<JobsBreadcrumbs jobID={job.getId()} />}
          tabs={tabs} />
        {this.tabs_getTabView(job)}
        <JobFormModal isEdit={true}
          job={job}
          open={this.state.jobActionDialog === JobActionItem.EDIT}
          onClose={this.closeDialog} />
        {this.getDestroyConfirmDialog()}
      </Page>
    );
  }
}

JobDetailPage.contextTypes = {
  router: routerShape
};

module.exports = JobDetailPage;
<commit_msg>Add job detail actions<commit_after>import classNames from 'classnames';
import {Confirm} from 'reactjs-components';
import mixin from 'reactjs-mixin';
import prettycron from 'prettycron';
/* eslint-disable no-unused-vars */
import React from 'react';
/* eslint-enable no-unused-vars */
import {routerShape} from 'react-router';

import {StoreMixin} from 'mesosphere-shared-reactjs';

import Icon from '../../components/Icon';
import JobConfiguration from './JobConfiguration';
import JobFormModal from '../../components/modals/JobFormModal';
import JobRunHistoryTable from './JobRunHistoryTable';
import JobsBreadcrumbs from '../../components/breadcrumbs/JobsBreadcrumbs';
import Loader from '../../components/Loader';
import MetronomeStore from '../../stores/MetronomeStore';
import Page from '../../components/Page';
import RequestErrorMsg from '../../components/RequestErrorMsg';
import StringUtil from '../../utils/StringUtil';
import TabsMixin from '../../mixins/TabsMixin';
import TimeAgo from '../../components/TimeAgo';
import TaskStates from '../../../../plugins/services/src/js/constants/TaskStates';

const METHODS_TO_BIND = [
  'closeDialog',
  'handleEditButtonClick',
  'handleRunNowButtonClick',
  'handleDisableScheduleButtonClick',
  'handleEnableScheduleButtonClick',
  'handleDestroyButtonClick',
  'onMetronomeStoreJobDeleteError',
  'onMetronomeStoreJobDeleteSuccess',
  'onMetronomeStoreJobDetailError',
  'onMetronomeStoreJobDetailChange'
];

const JobActionItem = {
  EDIT: 'edit',
  DESTROY: 'destroy'
};

class JobDetailPage extends mixin(StoreMixin, TabsMixin) {
  constructor() {
    super(...arguments);

    this.store_listeners = [{
      name: 'metronome',
      events: [
        'jobDeleteSuccess',
        'jobDeleteError',
        'jobDetailChange',
        'jobDetailError',
        'jobRunError',
        'jobRunSuccess',
        'jobScheduleUpdateError',
        'jobScheduleUpdateSuccess'
      ],
      suppressUpdate: false
    }];

    this.tabs_tabs = {
      runHistory: 'Run History',
      configuration: 'Configuration'
    };

    this.state = {
      currentTab: Object.keys(this.tabs_tabs).shift(),
      disabledDialog: null,
      jobActionDialog: null,
      errorMsg: null,
      errorCount: 0,
      isJobFormModalOpen: false,
      isLoading: true
    };

    METHODS_TO_BIND.forEach((method) => {
      this[method] = this[method].bind(this);
    });
  }

  componentDidMount() {
    super.componentDidMount(...arguments);
    MetronomeStore.monitorJobDetail(this.props.params.id);
  }

  componentWillUnmount() {
    super.componentWillUnmount(...arguments);
    MetronomeStore.stopJobDetailMonitor(this.props.params.id);
  }

  onMetronomeStoreJobDeleteError(id, {message:errorMsg}) {
    if (id !== this.props.params.id || errorMsg == null) {
      return;
    }

    this.setState({
      jobActionDialog: JobActionItem.DESTROY,
      disabledDialog: null,
      errorMsg
    });
  }

  onMetronomeStoreJobDeleteSuccess() {
    this.closeDialog();
    this.context.router.push('/jobs');
  }

  onMetronomeStoreJobDetailError() {
    this.setState({errorCount: this.state.errorCount + 1});
  }

  onMetronomeStoreJobDetailChange() {
    this.setState({errorCount: 0, isLoading: false});
  }

  handleEditButtonClick() {
    this.setState({jobActionDialog: JobActionItem.EDIT});
  }

  handleRunNowButtonClick() {
    let job = MetronomeStore.getJob(this.props.params.id);

    MetronomeStore.runJob(job.getId());
  }

  handleDisableScheduleButtonClick() {
    MetronomeStore.toggleSchedule(this.props.params.id, false);
  };

  handleEnableScheduleButtonClick() {
    MetronomeStore.toggleSchedule(this.props.params.id, true);
  };

  handleDestroyButtonClick() {
    this.setState({jobActionDialog: JobActionItem.DESTROY});
  };

  handleAcceptDestroyDialog(stopCurrentJobRuns = false) {
    this.setState({disabledDialog: JobActionItem.DESTROY}, () => {
      MetronomeStore.deleteJob(this.props.params.id, stopCurrentJobRuns);
    });
  }

  handleMoreDropdownSelection(selection) {
    if (selection.id === JobActionItem.SCHEDULE_DISABLE) {
      MetronomeStore.toggleSchedule(this.props.params.id, false);
      return;
    }

    if (selection.id === JobActionItem.SCHEDULE_ENABLE) {
      MetronomeStore.toggleSchedule(this.props.params.id, true);
      return;
    }

    this.setState({jobActionDialog: selection.id});
  }

  closeDialog() {
    this.setState({
      disabledDialog: null,
      errorMsg: null,
      jobActionDialog: null
    });
  }

  getDestroyConfirmDialog() {
    const {id} = this.props.params;
    const {disabledDialog, jobActionDialog, errorMsg} = this.state;
    let stopCurrentJobRuns = false;
    let actionButtonLabel = 'Destroy Job';
    let message = `Are you sure you want to destroy ${id}? ` +
      'This action is irreversible.';

    if (/stopCurrentJobRuns=true/.test(errorMsg)) {
      actionButtonLabel = 'Stop Current Runs and Destroy Job';
      stopCurrentJobRuns = true;
      message = `Couldn't destroy ${id} as there are currently active job ` +
        'runs. Do you want to stop all runs and destroy the job?';
    }

    let content = (

          Destroy Job

{message}

    );

    return (
      <Confirm children={content}
        disabled={disabledDialog === JobActionItem.DESTROY}
        open={jobActionDialog === JobActionItem.DESTROY}
        onClose={this.closeDialog}
        leftButtonText="Cancel"
        leftButtonCallback={this.closeDialog}
        rightButtonText={actionButtonLabel}
        rightButtonClassName="button button-danger"
        rightButtonCallback=
          {this.handleAcceptDestroyDialog.bind(this, stopCurrentJobRuns)} />
    );
  }

  getErrorScreen() {
    return (

        <RequestErrorMsg />

    );
  }

  getLoadingScreen() {
    return (
      <Page>
        <Page.Header breadcrumbs={<JobsBreadcrumbs/>} />
        <Loader />
      </Page>
    );
  }

  getNavigationTabs() {
    return (

        {this.tabs_getUnroutedTabs()}

    );
  }

  getPrettySchedule(job) {
    let schedules = job.getSchedules();
    if (schedules == null || schedules.length === 0) {
      return null;
    }

    let schedule = schedules[0];
    if (schedule.enabled) {
      return prettycron.toString(schedule.cron);
    }
  }

  getSubTitle(job) {
    let nodes = [];
    let scheduleText = this.getPrettySchedule(job);
    let longestRunningTask = null;
    let longestRunningActiveRun = job.getActiveRuns()
      .getLongestRunningActiveRun();

    if (longestRunningActiveRun != null) {
      longestRunningTask = longestRunningActiveRun.getTasks()
        .getLongestRunningTask();
    }

    if (longestRunningTask == null && scheduleText == null) {
      return null;
    }

    if (scheduleText != null) {
      nodes.push(

          <Icon
            className="icon-margin-right"
            key="schedule-icon"
            color="grey"
            id="repeat"
            size="mini" />
          Scheduled {StringUtil.lowercase(scheduleText)}

      );
    }

    if (longestRunningTask != null) {
      let dateCompleted = longestRunningTask.getDateCompleted();

      let status = TaskStates[longestRunningTask.getStatus()];
      let statusClasses = classNames('job-details-header-status', {
        'text-success': status.stateTypes.includes('success')
        && !status.stateTypes.includes('failure'),
        'text-danger': status.stateTypes.includes('failure')
      });

      let timePrefix = null;
      let shouldSuppressRelativeTime = dateCompleted == null;

      if (shouldSuppressRelativeTime) {
        timePrefix = 'for ';
      }

      nodes.push(

          {status.displayName}
        ,

          <TimeAgo
            prefix={timePrefix}
            suppressSuffix={shouldSuppressRelativeTime}
            time={longestRunningActiveRun.getDateCreated()} />

      );
    }

    return nodes;
  }

  renderConfigurationTabView(job) {
    return <JobConfiguration job={job} />;
  }

  renderRunHistoryTabView(job) {
    return <JobRunHistoryTable job={job} />;
  }

  getActions() {
    let job = MetronomeStore.getJob(this.props.params.id);
    let [schedule] = job.getSchedules();

    const actions = [];

    actions.push({
      label: 'Edit',
      onItemSelect: this.handleEditButtonClick
    });

    actions.push({
      label: 'Run Now',
      onItemSelect: this.handleRunNowButtonClick
    });

    if (schedule != null && schedule.enabled) {
      actions.push({
        label: 'Disable Schedule',
        onItemSelect: this.handleDisableScheduleButtonClick
      });
    }

    if (schedule != null && !schedule.enabled) {
      actions.push({
        label: 'Enable Schedule',
        onItemSelect: this.handleEnableScheduleButtonClick
      });
    }

    actions.push({
      className: 'text-danger',
      label: 'Destroy',
      onItemSelect: this.handleDestroyButtonClick
    });

    return actions;
  }

  getTabs() {
    return [
      {
        label: 'Run History', callback: () => {
          this.setState({currentTab: 'runHistory'});
        }
      },
      {
        label: 'Configuration', callback: () => {
          this.setState({currentTab: 'configuration'});
        }
      }
    ];
  }

  render() {
    if (this.state.errorCount > 3) {
      return this.getErrorScreen();
    }

    if (this.state.isLoading) {
      return this.getLoadingScreen();
    }

    // TaskDetailView
    if (this.props.params.taskID) {
      return this.props.children;
    }

    let job = MetronomeStore.getJob(this.props.params.id);

    return (
        <Page>
          <Page.Header
              actions={this.getActions()}
              breadcrumbs={<JobsBreadcrumbs jobID={job.getId()} />}
              tabs={this.getTabs()} />
          {this.tabs_getTabView(job)}
          <JobFormModal isEdit={true}
              job={job}
              open={this.state.jobActionDialog === JobActionItem.EDIT}
              onClose={this.closeDialog} />
          {this.getDestroyConfirmDialog()}
        </Page>
    );
  }
}

JobDetailPage.contextTypes = {
  router: routerShape
};

module.exports = JobDetailPage;
<|endoftext|>


<commit_before>/*!
 * Copyright 2018 Google LLC. 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.
 */

'use strict';

module.exports = {
  opts: {
    readme: './README.md',
    package: './package.json',
    template: './node_modules/jsdoc-baseline',
    recurse: true,
    verbose: true,
    destination: './docs/'
  },
  plugins: [
    'plugins/markdown'
  ],
  source: {
    excludePattern: '(^|\\/|\\\\)[._]',
    include: [
      'src'
    ],
    includePattern: '\\.js$'
  },
  templates: {
    copyright: 'Copyright 2018 Google, LLC.',
    includeDate: false,
    sourceFiles: false,
    systemName: '@google-cloud/logging-winston',
    theme: 'lumen'
  },
  markdown: {
    idInHeadings: true
  }
};
<commit_msg>docs: use the jsdoc-fresh theme (#377)<commit_after>/*!
 * Copyright 2018 Google LLC. 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.
 */

'use strict';

module.exports = {
  opts: {
    readme: './README.md',
    package: './package.json',
    template: './node_modules/jsdoc-fresh',
    recurse: true,
    verbose: true,
    destination: './docs/'
  },
  plugins: [
    'plugins/markdown'
  ],
  source: {
    excludePattern: '(^|\\/|\\\\)[._]',
    include: [
      'src'
    ],
    includePattern: '\\.js$'
  },
  templates: {
    copyright: 'Copyright 2018 Google, LLC.',
    includeDate: false,
    sourceFiles: false,
    systemName: '@google-cloud/logging-winston',
    theme: 'lumen'
  },
  markdown: {
    idInHeadings: true
  }
};
<|endoftext|>


<commit_before>import React from 'react';
import { storiesOf, action } from '@kadira/storybook';
import Boundingbox from '../src/react-bounding-box';

import './static/styles.css';

import demoImage from './static/image.jpg';
import demoImageLarge from './static/imageLarge.png';
import demoImageDog from './static/dog.jpg';

import segmentationJson from './static/segmentation.json';
import segmentationMasksJson from './static/segmentationMasks.json';
import segmentationMasksBoxesJson from './static/segmentationMasksBoxes.json';

storiesOf('Boundingbox', module)
  .add('default view', () => {
    const params = {
      image: demoImageLarge,
      boxes: [
        // coord(0,0) = top left corner of image
        // [x, y, width, height]
        [0, 0, 250, 250],
        [300, 0, 250, 250],
        [700, 0, 300, 25],
        [1100, 0, 25, 300],
      ],
    };
    return (<Boundingbox
      image={params.image}
      boxes={params.boxes}
    />);
  })
  .add('box selection', () => {
    const params = {
      image: demoImageLarge,
      boxes: [
        // coord(0,0) = top left corner of image
        // [x, y, width, height]
        [0, 0, 250, 250],
        [300, 0, 250, 250],
        [700, 0, 300, 25],
        [1100, 0, 25, 300],
      ],
    };
    return (<Boundingbox
      image={params.image}
      boxes={params.boxes}
      onSelected={action('selected')}
    />);
  })
  .add('label display', () => {
    const params = {
      image: demoImageLarge,
      boxes: [
        // coord(0,0) = top left corner of image
        // [x, y, width, height]
        { coord: [0, 0, 250, 250], label: 'test' },
        { coord: [300, 0, 250, 250], label: 'A' },
        { coord: [700, 0, 300, 25], label: 'B' },
        { coord: [1100, 0, 25, 300], label: 'C' },
      ],
    };
    return (<Boundingbox
      image={params.image}
      boxes={params.boxes}
    />);
  })
  .add('pixel segmentation', () => {
    return (<Boundingbox
      image={demoImage}
      pixelSegmentation={segmentationJson.body.predictions[0].vals}
    />);
  })
  .add('pixel segmentation', () => {
    return (<Boundingbox
      image={demoImage}
      pixelSegmentation={segmentationJson.body.predictions[0].vals}
    />);
  })
  .add('pixel segmentation from json url', () => {
    //
    // If github is down, you can use these url locally:
    //   image={'http://localhost:9010/image.jpg'}
    //   segmentationJsonUrl={'./segmentation.json'}
    //
    return (<Boundingbox
      image={demoImage}
      segmentationJsonUrl={'./segmentation.json'}
    />);
  })
  .add('pixel segmentation color scheme', () => {
    return (<Boundingbox
      image={demoImage}
      segmentationJsonUrl={'./segmentation.json'}
      segmentationColors={
        ['#1b9e77','#d95f02','#7570b3','#e7298a','#66a61e','#e6ab02','#a6761d','#666666']
      }
    />);
  })
  .add('pixel segmentation on remote image', () => {
    //
    // If github is down, you can use these url locally:
    //   image={'http://localhost:9010/image.jpg'}
    //
    return (<Boundingbox
      image={'https://alx.github.io/react-bounding-box/image.jpg'}
      segmentationJsonUrl={'./segmentation.json'}
      separateSegmentation={true}
    />);
  })
  .add('pixel segmentation on remote image with css', () => {
    //
    // If github is down, you can use these url locally:
    //   image={'http://localhost:9010/image.jpg'}
    //
    //
    // css for this story is avaible in ./static/styles.css
    //
    return (<div className="cssStory">
      <Boundingbox
        image={'https://alx.github.io/react-bounding-box/image.jpg'}
        segmentationJsonUrl={'./segmentation.json'}
        separateSegmentation={true}
      />
    </div>);
  })
  .add('segmentation masks', () => {
    return (<Boundingbox
      image={demoImageDog}
      segmentationMasks={segmentationMasksJson}
      boxes={segmentationMasksBoxesJson}
      separateSegmentation={true}
    />);
  });
<commit_msg>fix: remove unused backup file<commit_after><|endoftext|>


<commit_before>import {DeviceEventEmitter} from 'react-native';

class KeyboardObserver {

  constructor() {
    this.keyboardHeight = 0;
    this.animationWillEndAt = 0;
    this.listeners = {};
    this.listenerId = 0;
    DeviceEventEmitter.addListener('keyboardWillShow', this.handleKeyboardWillShow.bind(this));
    DeviceEventEmitter.addListener('keyboardWillHide', this.handleKeyboardWillHide.bind(this));
  }

  addListener(callback) {
    const id = this.listenerId++;
    this.listeners.id = callback;
    return {remove: () => delete this.listeners.id};
  }

  notifyListeners() {
    const info = this.getKeyboardInfo();
    Object.keys(this.listeners).forEach(key => this.listeners[key](info));
  }

  handleKeyboardWillShow(frames) {
    this.keyboardHeight = frames.endCoordinates.height;
    this.animationWillEndAt = new Date().getTime() + frames.duration;
    this.notifyListeners()
  }

  handleKeyboardWillHide(frames) {
    this.animationWillEndAt = new Date().getTime() + frames.duration;
    this.keyboardHeight = 0;
    this.notifyListeners()
  }

  getKeyboardInfo() {
    return {
      keyboardHeight: this.keyboardHeight,
      animationWillEndAt: this.animationWillEndAt,
    };
  }
}

export default new KeyboardObserver();
<commit_msg>Fix typo in KeyboardObserver<commit_after>import {DeviceEventEmitter} from 'react-native';

class KeyboardObserver {

  constructor() {
    this.keyboardHeight = 0;
    this.animationWillEndAt = 0;
    this.listeners = {};
    this.listenerId = 0;
    DeviceEventEmitter.addListener('keyboardWillShow', this.handleKeyboardWillShow.bind(this));
    DeviceEventEmitter.addListener('keyboardWillHide', this.handleKeyboardWillHide.bind(this));
  }

  addListener(callback) {
    const id = this.listenerId++;
    this.listeners[id] = callback;
    return {remove: () => delete this.listeners[id]};
  }

  notifyListeners() {
    const info = this.getKeyboardInfo();
    Object.keys(this.listeners).forEach(key => this.listeners[key](info));
  }

  handleKeyboardWillShow(frames) {
    this.keyboardHeight = frames.endCoordinates.height;
    this.animationWillEndAt = new Date().getTime() + frames.duration;
    this.notifyListeners()
  }

  handleKeyboardWillHide(frames) {
    this.animationWillEndAt = new Date().getTime() + frames.duration;
    this.keyboardHeight = 0;
    this.notifyListeners()
  }

  getKeyboardInfo() {
    return {
      keyboardHeight: this.keyboardHeight,
      animationWillEndAt: this.animationWillEndAt,
    };
  }
}

export default new KeyboardObserver();
<|endoftext|>


<commit_before>'use strict';

const Controller = require('../controller');

class ConfigController extends Controller {
    constructor(config, { versionService }) {
        super(config);
        this.versionService = versionService;
        const authenticationType = config.authentication.type;
        this.uiConfig = {
            ...config.ui,
            authenticationType,
            unleashUrl: config.unleashUrl,
            baseUriPath: config.baseUriPath,
            version: config.version,
        };
        this.get('/', this.getUIConfig);
    }

    async getUIConfig(req, res) {
        const config = this.uiConfig;
        if (this.versionService) {
            const versionInfo = this.versionService.getVersionInfo();
            res.json({ ...config, versionInfo });
        } else {
            res.json(config);
        }
    }
}

module.exports = ConfigController;
<commit_msg>fix: failing tests<commit_after>'use strict';

const Controller = require('../controller');

class ConfigController extends Controller {
    constructor(config, { versionService }) {
        super(config);
        this.versionService = versionService;
        const authenticationType =
            config.authentication && config.authentication.type;
        this.uiConfig = {
            ...config.ui,
            authenticationType,
            unleashUrl: config.unleashUrl,
            baseUriPath: config.baseUriPath,
            version: config.version,
        };
        this.get('/', this.getUIConfig);
    }

    async getUIConfig(req, res) {
        const config = this.uiConfig;
        if (this.versionService) {
            const versionInfo = this.versionService.getVersionInfo();
            res.json({ ...config, versionInfo });
        } else {
            res.json(config);
        }
    }
}

module.exports = ConfigController;
<|endoftext|>


<commit_before>import Request from 'request-promise';
import ProductSearchFormat from './Helpers/ProductSearchFormat';

class TescoAPI {
	constructor(key) {

		if (!key) {
			throw new Error('You need to provide an API key. Go to https://devportal.tescolabs.com/ and grab one.');
		}

		this.baseURL = 'https://dev.tescolabs.com';
		this.request = {
			uri: this.baseURL,
			json: true,
			resolveWithFullResponse: true,
			headers: {
				'Ocp-Apim-Subscription-Key': key
			},
			qs: {
				query:  String,
				offset: Number,
				limit:  Number
			}
		};
	}

	search(query, props, cb) {

		if (!query) {
			throw new Error('You must pass a query string parameter.');
		}

		// At this moment in time, the API only accepts a subscription
		// to the Grocery Product Search API.
		// This will need case switching/split in to modules to in
		// order to define what the URL will be.
		this.request.uri = `${this.baseURL}/grocery/products/`;
		this.request.qs.query = query;

		// If nothing is passed to the props object, we still need to
		// set them in order to return data.
		if (!props.limit) props.limit = 10;
		if (!props.offset) props.offset = 0;

		// Merge the request query string with the props.
		Object.assign(this.request.qs, props);

		let res, err;

		// Send request.
		Request(this.request)

		.then((response) => {

			// Build pagination URL's.
			const Pagination = {};
			const URI = response.request.uri;

			Pagination.next     = `${URI.protocol}//${URI.host}/?query=${query}&offset=${(props.offset + 1)}&limit=${props.limit}`;
			Pagination.previous = `${URI.protocol}//${URI.host}/?query=${query}&offset=${(props.offset > 0 ? props.offset - 1 : props.offset)}&limit=${props.limit}`;

			// Set Pagination inside response.
			response.body.uk.ghs.products.pagination = Pagination;

			// Format that lovely data.
			res = new ProductSearchFormat(response.body.uk.ghs.products);
		})

		.catch((error)   => err = error)
		.then(()         => cb(err, res));
	}
}

export default TescoAPI;
<commit_msg>Define constant reference.<commit_after>import Request from 'request-promise';
import ProductSearchFormat from './Helpers/ProductSearchFormat';

class TescoAPI {
	constructor(key) {

		if (!key) {
			throw new Error('You need to provide an API key. Go to https://devportal.tescolabs.com/ and grab one.');
		}

		this.baseURL = 'https://dev.tescolabs.com';
		this.request = {
			uri: this.baseURL,
			json: true,
			resolveWithFullResponse: true,
			headers: {
				'Ocp-Apim-Subscription-Key': key
			},
			qs: {
				query:  String,
				offset: Number,
				limit:  Number
			}
		};
	}

	search(query, props, cb) {

		if (!query) {
			throw new Error('You must pass a query string parameter.');
		}

		// At this moment in time, the API only accepts a subscription
		// to the Grocery Product Search API.
		// This will need case switching/split in to modules to in
		// order to define what the URL will be.
		this.request.uri = `${this.baseURL}/grocery/products/`;
		this.request.qs.query = query;

		// If nothing is passed to the props object, we still need to
		// set them in order to return data.
		if (!props.limit) props.limit = 10;
		if (!props.offset) props.offset = 0;

		// Merge the request query string with the props.
		Object.assign(this.request.qs, props);

		let res, err;

		// Send request.
		Request(this.request)

		.then((response) => {

			const products = response.body.uk.ghs.products;

			// Build pagination URL's.
			const Pagination = {};
			const URI = response.request.uri;

			Pagination.next     = `${URI.protocol}//${URI.host}/?query=${query}&offset=${(props.offset + 1)}&limit=${props.limit}`;
			Pagination.previous = `${URI.protocol}//${URI.host}/?query=${query}&offset=${(props.offset > 0 ? props.offset - 1 : props.offset)}&limit=${props.limit}`;

			// Set Pagination inside response.
			products.pagination = Pagination;

			// Format that lovely data.
			res = new ProductSearchFormat(products);
		})

		.catch((error)   => err = error)
		.then(()         => cb(err, res));
	}
}

export default TescoAPI;
<|endoftext|>


<commit_before>/* SettingsBox.js
KC3改 Settings Box

To be dynamically used on the settings page
*/
(function(){
	"use strict";

	window.SettingsBox = function( info ){
		var self = this;
		this.config = info.id;
		this.element = $("#factory .settingBox").clone().appendTo("#wrapper .settings");
		$(".title", this.element).text( KC3Meta.term( info.name ) );
		this.soundPreview = false;
		this.bound = $.extend({
			min:-Infinity,
			max:Infinity,
			length_min:0,
			length_max:Infinity,
			type:"String"
		}, info.bound || {});
		this.disabled = info.disabled;
		this[info.type]( info.options );
		// If different with default, show reset button
		if(this.config != "language" &&
			JSON.stringify(ConfigManager[this.config]).hashCode()
			!== JSON.stringify(ConfigManager.defaults()[this.config]).hashCode()){
			$(".resetButton", this.element).show();
		}
		$(".resetButton", this.element).on("click", function(){
			console.log("Reset", self.config, "=", ConfigManager[self.config],
				"to default:", ConfigManager.defaults()[self.config]);
			ConfigManager.resetValueOf(self.config);
			elementControl($(this).siblings(".note"),'',KC3Meta.term("SettingsErrorNG"));
			// Refresh this option
			//window.location.reload();
			//$(this).hide();
			$(".options", self.element).empty();
			self[info.type]( info.options );
		});
		if(parseInt(info.chui) || 0 === 1){
			$(this.element).addClass("dangerous");
		}
	};

	SettingsBox.prototype.check = function( options ){
		var self = this;
		$(".options", this.element).append(
			$("<input/>")
			.attr("type", "checkbox")
			.addClass("checkbox")
			.prop("disabled", this.disabled)
			.prop("checked", ConfigManager[ this.config ])
			.on("change", function(){
				// Dangerous Settings Change Attempt
				if(isDangerous($(this).parent().parent(),self.config,$(this).prop("checked"))) {
					$(this).prop("checked",ConfigManager[self.config]);
					return false;
				}

				ConfigManager[ self.config ] = $(this).prop("checked");
				ConfigManager.save();
				elementControl($(this).parent().siblings(".note"),'',KC3Meta.term("SettingsErrorNG"));
			})
		);
	};

	SettingsBox.prototype.small_text = function( options ){
		var self = this;
		$(".options", this.element).append(
			$("<input/>")
			.attr("type", "text")
			.addClass("small_text")
			.prop("disabled", this.disabled)
			.val( ConfigManager[ this.config ] )
			.on("change", function(){
				// Dangerous Settings Change Attempt
				if(isDangerous($(this).parent().parent(),self.config,$(this).val())) {
					$(this).val(ConfigManager[self.config]);
					return false;
				}

				// Invalid Value Attempt
				var ERRCODE = isInvalid(self.bound,$(this).val());
				if(!!ERRCODE) {
					var errstr = KC3Meta.term("SettingsErrorOrder");
					if(ERRCODE === -1) {
						errstr = KC3Meta.term("SettingsErrorSuper");
					} else {
						errstr = errstr
							.replace("%TYP",KC3Meta.term("SettingsError" + ((ERRCODE & 4) == 4 ? "Value" : "Length")))
							.replace("%CMP",KC3Meta.term("SettingsError" + ((ERRCODE & 2) == 2 ? "Above" :  "Below")))
							.replace("%VAL",self.bound[((ERRCODE & 4) == 4 ? "" : "length_") + ((ERRCODE & 2) == 2 ? "max" : "min")]);
					}
					console.info(errstr);
					elementControl($(this).parent().siblings(".note"),'red',errstr);
					$(this).val(ConfigManager[self.config]);
					return false;
				}

				ConfigManager[ self.config ] = window[self.bound.type==="Integer"?"Number":self.bound.type]($(this).val());
				ConfigManager.save();
				elementControl($(this).parent().siblings(".note"),'',KC3Meta.term("SettingsErrorNG"));

				// If changed volume, test play the alert sound
				if(self.config == "alert_volume"){
					if(self.soundPreview){
						self.soundPreview.pause();
					}
					switch(ConfigManager.alert_type){
						case 1: self.soundPreview = new Audio("../../../../assets/snd/pop.mp3"); break;
						case 2: self.soundPreview = new Audio(ConfigManager.alert_custom); break;
						case 3: self.soundPreview = new Audio("../../../../assets/snd/ding.mp3"); break;
						default: self.soundPreview = false; break;
					}
					if(self.soundPreview){
						self.soundPreview.volume = ConfigManager.alert_volume / 100;
						self.soundPreview.play();
					}
				}
			})
		);
		$(".options", this.element).append( KC3Meta.term( options.label ) );
	};

	SettingsBox.prototype.long_text = function( options ){
		var self = this;
		$(".options", this.element).append(
			$("<input/>")
			.attr("type", "text")
			.attr("placeholder", KC3Meta.term( options.placeholder ) )
			.addClass("long_text")
			.prop("disabled", this.disabled)
			.val( ConfigManager[ this.config ] )
			.on("change", function(){
				// Dangerous Settings Change Attempt
				if(isDangerous($(this).parent().parent(),self.config,$(this).val())) {
					$(this).val(ConfigManager[self.config]);
					return false;
				}

				ConfigManager[ self.config ] = $(this).val();
				ConfigManager.save();
				elementControl($(this).parent().siblings(".note"),'',KC3Meta.term("SettingsErrorNG"));
			})
		);
		$(".options", this.element).append( options.label );
	};

	SettingsBox.prototype.radio = function( options ){
		var self = this;
		var choiceClass = "choices_" + this.config;
		for(var ctr in options.choices){
			$(".options", this.element).append(
				$("#factory .radioBox")
				.clone()
				.addClass( choiceClass )
				.addClass( (options.choices[ctr][0]==ConfigManager[ self.config ])?"active":"" )
				.data("class", choiceClass )
				.data("value", options.choices[ctr][0] )
				.html( KC3Meta.term( options.choices[ctr][1] ) )
			);
		}

		$("."+choiceClass, this.element).on("click", function(){
			console.log(this,arguments);
			$("."+$(this).data("class")).removeClass("active");
			$(this).addClass("active");
			ConfigManager[ self.config ] = $(this).data("value");
			ConfigManager.save();
			elementControl($(this).parent().siblings(".note"),'',KC3Meta.term("SettingsErrorNG"));

			// If changed sound type, test play the alert sound
			if(self.config == "alert_type"){
				if(self.soundPreview){
					self.soundPreview.pause();
				}
				switch(ConfigManager.alert_type){
					case 1: self.soundPreview = new Audio("../../../../assets/snd/pop.mp3"); break;
					case 2: self.soundPreview = new Audio(ConfigManager.alert_custom); break;
					case 3: self.soundPreview = new Audio("../../../../assets/snd/ding.mp3"); break;
					default: self.soundPreview = false; break;
				}
				if(self.soundPreview){
					self.soundPreview.volume = ConfigManager.alert_volume / 100;
					self.soundPreview.play();
				}
			}

			// Refresh page when a language option is clicked
			if(self.config == "language"){
				window.location.reload();
			}
		});
	};

	SettingsBox.prototype.json = function( options ){
		var self = this;
		$(".options", this.element).append(
			$("<textarea/>")
				.addClass("json_text")
				.prop("disabled", this.disabled)
				.val( JSON.stringify(ConfigManager[ this.config ]) )
				.on("change", function(){
					var newValue = false;
					try {
						newValue = JSON.parse($(this).val());
						ConfigManager[ self.config ] = newValue;
						ConfigManager.save();
						elementControl($(this).parent().siblings(".note"), '', KC3Meta.term("SettingsErrorNG"));
					} catch (e) {
						elementControl($(this).parent().siblings(".note"), 'red', KC3Meta.term("SettingsErrorSuper"));
					}
				})
		);
	};

	SettingsBox.prototype.textarea = function( options ){
		var self = this;
		$(".options", this.element).append(
			$("<textarea/>")
				.addClass("huge_text")
				.prop("disabled", this.disabled)
				.val( ConfigManager[ this.config ] )
				.on("change", function(){
					ConfigManager[ self.config ] = $(this).val();
					ConfigManager.save();
					elementControl($(this).parent().siblings(".note"), '', KC3Meta.term("SettingsErrorNG"));
				})
		);
	};

	SettingsBox.prototype.dropdown = function( options ){
		var self = this;
		var choiceClass = "choices_" + this.config;

		$(".options", this.element).append(
			$("<select/>")
				.addClass("dropdown")
				.prop("disabled", this.disabled)
				.on("change", function(){
					ConfigManager[ self.config ] = $(this).val();
					ConfigManager.save();
					elementControl($(this).parent().siblings(".note"), '', KC3Meta.term("SettingsErrorNG"));
				})
		);

		for(var ctr in options.choices){
			$(".options select", this.element).append(
				$("<option/>")
				.attr("value", options.choices[ctr][0] )
				.prop("selected", options.choices[ctr][0] == ConfigManager[ self.config ])
				.text( KC3Meta.term( options.choices[ctr][1] ) )
				.prop("disabled", typeof options.choices[ctr][2] != "undefined")
			);
		}
	};

	function elementControl(ele,colorCSS,msg) {
		return ele.stop(true, true).css('color',colorCSS).text(msg).show().fadeOut(colorCSS ? 5000 : 2000);
	}

	function isDangerous(element,key,current) {
		var
			isDG = $(element).hasClass("dangerous"),
			isEqPrev = ConfigManager[key] === current,
			isEqDef  = ConfigManager.defaults()[key] === current;
		if(isDG&&!isEqPrev&&!isEqDef)
			return !confirm(KC3Meta.term("SettingsChuuiWarningDangerousFeature")); // cancelling the prompt
		else
			return false;
	}
	function isInvalid(bound,value) {
		// 00000
		// lsb-0 : is error
		// lsb-1 : greater if set, lesser if not
		// lsb-2 : value check if set, length check if not
		// having all bit is set means invalid value
		// otherwise, having all bit is unset means valid value
		console.log(bound);
		var isNumber = function(str){
			return !(isNaN(str) || str === "" || str === null || str === false);
		};
		var isInteger = function(str){
			return isNumber(str) && Number.isFinite(Number(str)) && Math.floor(Number(str)) === Number(str);
		};
		switch(true) {
			case(bound.type === "Number" && !isNumber(value)): return -1; // Number Expectation
			case(bound.type === "Integer" && !isInteger(value)): return -1;
			case(String(value).length > (Number(bound.length_max) || Infinity)): return  3;
			case(String(value).length < (Number(bound.length_min) ||        0)): return  1;
			case(value > (isInteger(bound.max) ? Number(bound.max) :  Infinity)): return  7;
			case(value < (isInteger(bound.min) ? Number(bound.min) : -Infinity)): return  5;
			default: return 0;
		}
	}

})();<commit_msg>Update setting value comparing<commit_after>/* SettingsBox.js
KC3改 Settings Box

To be dynamically used on the settings page
*/
(function(){
	"use strict";

	window.SettingsBox = function( info ){
		var self = this;
		this.config = info.id;
		this.element = $("#factory .settingBox").clone().appendTo("#wrapper .settings");
		$(".title", this.element).text( KC3Meta.term( info.name ) );
		this.soundPreview = false;
		this.bound = $.extend({
			min:-Infinity,
			max:Infinity,
			length_min:0,
			length_max:Infinity,
			type:"String"
		}, info.bound || {});
		this.disabled = info.disabled;
		this[info.type]( info.options );
		// If different with default, show reset button
		if( this.config != "language" &&
			JSON.stringify(ConfigManager[this.config])
			!== JSON.stringify(ConfigManager.defaults()[this.config]) ){
			$(".resetButton", this.element).show();
		}
		$(".resetButton", this.element).on("click", function(){
			console.log("Reset", self.config, "=", ConfigManager[self.config],
				"to default:", ConfigManager.defaults()[self.config]);
			ConfigManager.resetValueOf(self.config);
			elementControl($(this).siblings(".note"),'',KC3Meta.term("SettingsErrorNG"));
			// Refresh this option
			//window.location.reload();
			//$(this).hide();
			$(".options", self.element).empty();
			self[info.type]( info.options );
		});
		if(parseInt(info.chui) || 0 === 1){
			$(this.element).addClass("dangerous");
		}
	};

	SettingsBox.prototype.check = function( options ){
		var self = this;
		$(".options", this.element).append(
			$("<input/>")
			.attr("type", "checkbox")
			.addClass("checkbox")
			.prop("disabled", this.disabled)
			.prop("checked", ConfigManager[ this.config ])
			.on("change", function(){
				// Dangerous Settings Change Attempt
				if(isDangerous($(this).parent().parent(),self.config,$(this).prop("checked"))) {
					$(this).prop("checked",ConfigManager[self.config]);
					return false;
				}

				ConfigManager[ self.config ] = $(this).prop("checked");
				ConfigManager.save();
				elementControl($(this).parent().siblings(".note"),'',KC3Meta.term("SettingsErrorNG"));
			})
		);
	};

	SettingsBox.prototype.small_text = function( options ){
		var self = this;
		$(".options", this.element).append(
			$("<input/>")
			.attr("type", "text")
			.addClass("small_text")
			.prop("disabled", this.disabled)
			.val( ConfigManager[ this.config ] )
			.on("change", function(){
				// Dangerous Settings Change Attempt
				if(isDangerous($(this).parent().parent(),self.config,$(this).val())) {
					$(this).val(ConfigManager[self.config]);
					return false;
				}

				// Invalid Value Attempt
				var ERRCODE = isInvalid(self.bound,$(this).val());
				if(!!ERRCODE) {
					var errstr = KC3Meta.term("SettingsErrorOrder");
					if(ERRCODE === -1) {
						errstr = KC3Meta.term("SettingsErrorSuper");
					} else {
						errstr = errstr
							.replace("%TYP",KC3Meta.term("SettingsError" + ((ERRCODE & 4) == 4 ? "Value" : "Length")))
							.replace("%CMP",KC3Meta.term("SettingsError" + ((ERRCODE & 2) == 2 ? "Above" :  "Below")))
							.replace("%VAL",self.bound[((ERRCODE & 4) == 4 ? "" : "length_") + ((ERRCODE & 2) == 2 ? "max" : "min")]);
					}
					console.info(errstr);
					elementControl($(this).parent().siblings(".note"),'red',errstr);
					$(this).val(ConfigManager[self.config]);
					return false;
				}

				ConfigManager[ self.config ] = window[self.bound.type==="Integer"?"Number":self.bound.type]($(this).val());
				ConfigManager.save();
				elementControl($(this).parent().siblings(".note"),'',KC3Meta.term("SettingsErrorNG"));

				// If changed volume, test play the alert sound
				if(self.config == "alert_volume"){
					if(self.soundPreview){
						self.soundPreview.pause();
					}
					switch(ConfigManager.alert_type){
						case 1: self.soundPreview = new Audio("../../../../assets/snd/pop.mp3"); break;
						case 2: self.soundPreview = new Audio(ConfigManager.alert_custom); break;
						case 3: self.soundPreview = new Audio("../../../../assets/snd/ding.mp3"); break;
						default: self.soundPreview = false; break;
					}
					if(self.soundPreview){
						self.soundPreview.volume = ConfigManager.alert_volume / 100;
						self.soundPreview.play();
					}
				}
			})
		);
		$(".options", this.element).append( KC3Meta.term( options.label ) );
	};

	SettingsBox.prototype.long_text = function( options ){
		var self = this;
		$(".options", this.element).append(
			$("<input/>")
			.attr("type", "text")
			.attr("placeholder", KC3Meta.term( options.placeholder ) )
			.addClass("long_text")
			.prop("disabled", this.disabled)
			.val( ConfigManager[ this.config ] )
			.on("change", function(){
				// Dangerous Settings Change Attempt
				if(isDangerous($(this).parent().parent(),self.config,$(this).val())) {
					$(this).val(ConfigManager[self.config]);
					return false;
				}

				ConfigManager[ self.config ] = $(this).val();
				ConfigManager.save();
				elementControl($(this).parent().siblings(".note"),'',KC3Meta.term("SettingsErrorNG"));
			})
		);
		$(".options", this.element).append( options.label );
	};

	SettingsBox.prototype.radio = function( options ){
		var self = this;
		var choiceClass = "choices_" + this.config;
		for(var ctr in options.choices){
			$(".options", this.element).append(
				$("#factory .radioBox")
				.clone()
				.addClass( choiceClass )
				.addClass( (options.choices[ctr][0]==ConfigManager[ self.config ])?"active":"" )
				.data("class", choiceClass )
				.data("value", options.choices[ctr][0] )
				.html( KC3Meta.term( options.choices[ctr][1] ) )
			);
		}

		$("."+choiceClass, this.element).on("click", function(){
			console.log(this,arguments);
			$("."+$(this).data("class")).removeClass("active");
			$(this).addClass("active");
			ConfigManager[ self.config ] = $(this).data("value");
			ConfigManager.save();
			elementControl($(this).parent().siblings(".note"),'',KC3Meta.term("SettingsErrorNG"));

			// If changed sound type, test play the alert sound
			if(self.config == "alert_type"){
				if(self.soundPreview){
					self.soundPreview.pause();
				}
				switch(ConfigManager.alert_type){
					case 1: self.soundPreview = new Audio("../../../../assets/snd/pop.mp3"); break;
					case 2: self.soundPreview = new Audio(ConfigManager.alert_custom); break;
					case 3: self.soundPreview = new Audio("../../../../assets/snd/ding.mp3"); break;
					default: self.soundPreview = false; break;
				}
				if(self.soundPreview){
					self.soundPreview.volume = ConfigManager.alert_volume / 100;
					self.soundPreview.play();
				}
			}

			// Refresh page when a language option is clicked
			if(self.config == "language"){
				window.location.reload();
			}
		});
	};

	SettingsBox.prototype.json = function( options ){
		var self = this;
		$(".options", this.element).append(
			$("<textarea/>")
				.addClass("json_text")
				.prop("disabled", this.disabled)
				.val( JSON.stringify(ConfigManager[ this.config ]) )
				.on("change", function(){
					var newValue = false;
					try {
						newValue = JSON.parse($(this).val());
						ConfigManager[ self.config ] = newValue;
						ConfigManager.save();
						elementControl($(this).parent().siblings(".note"), '', KC3Meta.term("SettingsErrorNG"));
					} catch (e) {
						elementControl($(this).parent().siblings(".note"), 'red', KC3Meta.term("SettingsErrorSuper"));
					}
				})
		);
	};

	SettingsBox.prototype.textarea = function( options ){
		var self = this;
		$(".options", this.element).append(
			$("<textarea/>")
				.addClass("huge_text")
				.prop("disabled", this.disabled)
				.val( ConfigManager[ this.config ] )
				.on("change", function(){
					ConfigManager[ self.config ] = $(this).val();
					ConfigManager.save();
					elementControl($(this).parent().siblings(".note"), '', KC3Meta.term("SettingsErrorNG"));
				})
		);
	};

	SettingsBox.prototype.dropdown = function( options ){
		var self = this;
		var choiceClass = "choices_" + this.config;

		$(".options", this.element).append(
			$("<select/>")
				.addClass("dropdown")
				.prop("disabled", this.disabled)
				.on("change", function(){
					ConfigManager[ self.config ] = $(this).val();
					ConfigManager.save();
					elementControl($(this).parent().siblings(".note"), '', KC3Meta.term("SettingsErrorNG"));
				})
		);

		for(var ctr in options.choices){
			$(".options select", this.element).append(
				$("<option/>")
				.attr("value", options.choices[ctr][0] )
				.prop("selected", options.choices[ctr][0] == ConfigManager[ self.config ])
				.text( KC3Meta.term( options.choices[ctr][1] ) )
				.prop("disabled", typeof options.choices[ctr][2] != "undefined")
			);
		}
	};

	function elementControl(ele,colorCSS,msg) {
		return ele.stop(true, true).css('color',colorCSS).text(msg).show().fadeOut(colorCSS ? 5000 : 2000);
	}

	function isDangerous(element,key,current) {
		var
			isDG = $(element).hasClass("dangerous"),
			isEqPrev = ConfigManager[key] === current,
			isEqDef  = ConfigManager.defaults()[key] === current;
		if(isDG&&!isEqPrev&&!isEqDef)
			return !confirm(KC3Meta.term("SettingsChuuiWarningDangerousFeature")); // cancelling the prompt
		else
			return false;
	}
	function isInvalid(bound,value) {
		// 00000
		// lsb-0 : is error
		// lsb-1 : greater if set, lesser if not
		// lsb-2 : value check if set, length check if not
		// having all bit is set means invalid value
		// otherwise, having all bit is unset means valid value
		console.log(bound);
		var isNumber = function(str){
			return !(isNaN(str) || str === "" || str === null || str === false);
		};
		var isInteger = function(str){
			return isNumber(str) && Number.isFinite(Number(str)) && Math.floor(Number(str)) === Number(str);
		};
		switch(true) {
			case(bound.type === "Number" && !isNumber(value)): return -1; // Number Expectation
			case(bound.type === "Integer" && !isInteger(value)): return -1;
			case(String(value).length > (Number(bound.length_max) || Infinity)): return  3;
			case(String(value).length < (Number(bound.length_min) ||        0)): return  1;
			case(value > (isInteger(bound.max) ? Number(bound.max) :  Infinity)): return  7;
			case(value < (isInteger(bound.min) ? Number(bound.min) : -Infinity)): return  5;
			default: return 0;
		}
	}

})();<|endoftext|>


<commit_before>import React from 'react';

import {
  ListView,
  View,
} from 'react-native';

import shallowequal from 'shallowequal';
import InvertibleScrollView from 'react-native-invertible-scroll-view';
import md5 from 'md5';
import LoadEarlier from './LoadEarlier';
import Message from './Message';

export default class MessageContainer extends React.Component {
  constructor(props) {
    super(props);

    this.renderRow = this.renderRow.bind(this);
    this.renderFooter = this.renderFooter.bind(this);
    this.renderLoadEarlier = this.renderLoadEarlier.bind(this);
    this.renderScrollComponent = this.renderScrollComponent.bind(this);

    const dataSource = new ListView.DataSource({
      rowHasChanged: (r1, r2) => {
        return r1.hash !== r2.hash;
      }
    });

    const messagesData = this.prepareMessages(props.messages);
    this.state = {
      dataSource: dataSource.cloneWithRows(messagesData.blob, messagesData.keys)
    };
  }

  prepareMessages(messages) {
    return {
      keys: messages.map(m => m._id),
      blob: messages.reduce((o, m, i) => {
        const previousMessage = messages[i + 1] || {};
        const nextMessage = messages[i - 1] || {};
        // add next and previous messages to hash to ensure updates
        const toHash = JSON.stringify(m) + previousMessage._id + nextMessage._id;
        o[m._id] = {
          ...m,
          previousMessage,
          nextMessage,
          hash: md5(toHash)
        };
        return o;
      }, {})
    };
  }

  shouldComponentUpdate(nextProps, nextState) {
    if (!shallowequal(this.props, nextProps)) {
      return true;
    }
    if (!shallowequal(this.state, nextState)) {
      return true;
    }
    return false;
  }

  componentWillReceiveProps(nextProps) {
    if (this.props.messages === nextProps.messages) {
      return;
    }
    const messagesData = this.prepareMessages(nextProps.messages);
    this.setState({
      dataSource: this.state.dataSource.cloneWithRows(messagesData.blob, messagesData.keys)
    });
  }

  renderFooter() {
    if (this.props.renderFooter) {
      const footerProps = {
        ...this.props,
      };
      return this.props.renderFooter(footerProps);
    }
    return null;
  }

  renderLoadEarlier() {
    if (this.props.loadEarlier === true) {
      const loadEarlierProps = {
        ...this.props,
      };
      if (this.props.renderLoadEarlier) {
        return this.props.renderLoadEarlier(loadEarlierProps);
      }
      return (
        <LoadEarlier {...loadEarlierProps}/>
      );
    }
    return null;
  }

  scrollTo(options) {
    this._invertibleScrollViewRef.scrollTo(options);
  }

  renderRow(message, sectionId, rowId) {
    if (!message._id && message._id !== 0) {
      console.warn('GiftedChat: `_id` is missing for message', JSON.stringify(message));
    }
    if (!message.user) {
      console.warn('GiftedChat: `user` is missing for message', JSON.stringify(message));
      message.user = {};
    }

    const messageProps = {
      ...this.props,
      key: message._id,
      currentMessage: message,
      previousMessage: message.previousMessage,
      nextMessage: message.nextMessage,
      position: message.user._id === this.props.user._id ? 'right' : 'left',
    };

    if (this.props.renderMessage) {
      return this.props.renderMessage(messageProps);
    }
    return <Message {...messageProps}/>;
  }

  renderScrollComponent(props) {
    const invertibleScrollViewProps = this.props.invertibleScrollViewProps;
    return (
      <InvertibleScrollView
        {...props}
        {...invertibleScrollViewProps}
        ref={component => this._invertibleScrollViewRef = component}
      />
    );
  }

  render() {
    return (
      <View ref='container' style={{flex:1}}>
        <ListView
          enableEmptySections={true}
          keyboardShouldPersistTaps={'always'}
          automaticallyAdjustContentInsets={false}
          initialListSize={20}
          pageSize={20}

          dataSource={this.state.dataSource}

          renderRow={this.renderRow}
          renderHeader={this.renderFooter}
          renderFooter={this.renderLoadEarlier}
          renderScrollComponent={this.renderScrollComponent}
          {...this.props.listViewProps}
        />
      </View>
    );
  }
}

MessageContainer.defaultProps = {
  messages: [],
  user: {},
  renderFooter: null,
  renderMessage: null,
  onLoadEarlier: () => {
  },
};

MessageContainer.propTypes = {
  messages: React.PropTypes.array,
  user: React.PropTypes.object,
  renderFooter: React.PropTypes.func,
  renderMessage: React.PropTypes.func,
  onLoadEarlier: React.PropTypes.func,
};
<commit_msg>avoid listViewProps to override render methd and datasources<commit_after>import React from 'react';

import {
  ListView,
  View,
} from 'react-native';

import shallowequal from 'shallowequal';
import InvertibleScrollView from 'react-native-invertible-scroll-view';
import md5 from 'md5';
import LoadEarlier from './LoadEarlier';
import Message from './Message';

export default class MessageContainer extends React.Component {
  constructor(props) {
    super(props);

    this.renderRow = this.renderRow.bind(this);
    this.renderFooter = this.renderFooter.bind(this);
    this.renderLoadEarlier = this.renderLoadEarlier.bind(this);
    this.renderScrollComponent = this.renderScrollComponent.bind(this);

    const dataSource = new ListView.DataSource({
      rowHasChanged: (r1, r2) => {
        return r1.hash !== r2.hash;
      }
    });

    const messagesData = this.prepareMessages(props.messages);
    this.state = {
      dataSource: dataSource.cloneWithRows(messagesData.blob, messagesData.keys)
    };
  }

  prepareMessages(messages) {
    return {
      keys: messages.map(m => m._id),
      blob: messages.reduce((o, m, i) => {
        const previousMessage = messages[i + 1] || {};
        const nextMessage = messages[i - 1] || {};
        // add next and previous messages to hash to ensure updates
        const toHash = JSON.stringify(m) + previousMessage._id + nextMessage._id;
        o[m._id] = {
          ...m,
          previousMessage,
          nextMessage,
          hash: md5(toHash)
        };
        return o;
      }, {})
    };
  }

  shouldComponentUpdate(nextProps, nextState) {
    if (!shallowequal(this.props, nextProps)) {
      return true;
    }
    if (!shallowequal(this.state, nextState)) {
      return true;
    }
    return false;
  }

  componentWillReceiveProps(nextProps) {
    if (this.props.messages === nextProps.messages) {
      return;
    }
    const messagesData = this.prepareMessages(nextProps.messages);
    this.setState({
      dataSource: this.state.dataSource.cloneWithRows(messagesData.blob, messagesData.keys)
    });
  }

  renderFooter() {
    if (this.props.renderFooter) {
      const footerProps = {
        ...this.props,
      };
      return this.props.renderFooter(footerProps);
    }
    return null;
  }

  renderLoadEarlier() {
    if (this.props.loadEarlier === true) {
      const loadEarlierProps = {
        ...this.props,
      };
      if (this.props.renderLoadEarlier) {
        return this.props.renderLoadEarlier(loadEarlierProps);
      }
      return (
        <LoadEarlier {...loadEarlierProps}/>
      );
    }
    return null;
  }

  scrollTo(options) {
    this._invertibleScrollViewRef.scrollTo(options);
  }

  renderRow(message, sectionId, rowId) {
    if (!message._id && message._id !== 0) {
      console.warn('GiftedChat: `_id` is missing for message', JSON.stringify(message));
    }
    if (!message.user) {
      console.warn('GiftedChat: `user` is missing for message', JSON.stringify(message));
      message.user = {};
    }

    const messageProps = {
      ...this.props,
      key: message._id,
      currentMessage: message,
      previousMessage: message.previousMessage,
      nextMessage: message.nextMessage,
      position: message.user._id === this.props.user._id ? 'right' : 'left',
    };

    if (this.props.renderMessage) {
      return this.props.renderMessage(messageProps);
    }
    return <Message {...messageProps}/>;
  }

  renderScrollComponent(props) {
    const invertibleScrollViewProps = this.props.invertibleScrollViewProps;
    return (
      <InvertibleScrollView
        {...props}
        {...invertibleScrollViewProps}
        ref={component => this._invertibleScrollViewRef = component}
      />
    );
  }

  render() {
    return (
      <View ref='container' style={{flex:1}}>
        <ListView
          keyboardShouldPersistTaps={'always'}
          automaticallyAdjustContentInsets={false}
          initialListSize={20}
          pageSize={20}

          {...this.props.listViewProps}

          enableEmptySections={true}
          dataSource={this.state.dataSource}

          renderRow={this.renderRow}
          renderHeader={this.renderFooter}
          renderFooter={this.renderLoadEarlier}
          renderScrollComponent={this.renderScrollComponent}
        />
      </View>
    );
  }
}

MessageContainer.defaultProps = {
  messages: [],
  user: {},
  renderFooter: null,
  renderMessage: null,
  onLoadEarlier: () => {
  },
};

MessageContainer.propTypes = {
  messages: React.PropTypes.array,
  user: React.PropTypes.object,
  renderFooter: React.PropTypes.func,
  renderMessage: React.PropTypes.func,
  onLoadEarlier: React.PropTypes.func,
};
<|endoftext|>


<commit_before>import React, {Component} from "react";
import { FormattedDate } from 'react-intl';

import './Movie.css';

import {FormatList} from '../../Format';
import {Duration} from '../../Duration';

class Movie extends Component {
  render() {
    let movie = this.props.movie;
    return (

        {movie.cover && }

{movie.title}

          {movie.originalTitle && {movie.originalTitle}}

            {movie.duration && <Duration className="duration" value={movie.duration} />}
            <FormattedDate className="theater-release-Date" value={new Date(movie.releaseDate)}/>
            {movie.Formats && <FormatList formats={movie.Formats}/>}

{movie.synopsis}

    )
  }
}

export default Movie;
<commit_msg>uses formats instead of Format (API model update)<commit_after>import React, {Component} from "react";
import { FormattedDate } from 'react-intl';

import './Movie.css';

import {FormatList} from '../../Format';
import {Duration} from '../../Duration';

class Movie extends Component {
  render() {
    let movie = this.props.movie;
    return (

        {movie.cover && }

{movie.title}

          {movie.originalTitle && {movie.originalTitle}}

            {movie.duration && <Duration className="duration" value={movie.duration} />}
            <FormattedDate className="theater-release-Date" value={new Date(movie.releaseDate)}/>
            {movie.formats && <FormatList formats={movie.formats}/>}

{movie.synopsis}

    )
  }
}

export default Movie;
<|endoftext|>


<commit_before>var STATUS_NEW = 1;
var STATUS_LOADING = 2;
var STATUS_LOADED = 4;
var STATUS_MISSING = 8;
var STATUS_ERROR = 16;

_.extend(Namespace.prototype, {
  /**
   * The namespace is actually a bounch of files but one file per locale. eg.:
   * myTool.en_US.lang.yml
   * myTool.de_DE.lang.yml
   *
   * This is the client implementation which means it uses Dependencies.
   * Also it uses the HTTP interface of Meteor instead of the Asset one.
   *
   * @constructor
   * @private
   * @param {string} name
   */
  _init: function (name) {
    var self = this;

    // get the existing locales from injection
    var namespaces = Injected.obj('translator-namespaces');
    if (! namespaces.hasOwnProperty(self._name)) {
      throw new Error("Namespace '" + self._name + "' does not exist");
    }

    self._locales = {}; // see loop in prepare
    self._loading = 0; // for telling if a request is running
    self._loadingDep = new Deps.Dependency(); // only important for #isLoading
    self._localesWarned = false; // "out of locales to load"
    self._existingLocales = namespaces[self._name]; // set of locale strings
  },

  /**
   * Tells if a reactive call could happen.
   * @return {bool}
   */
  isLoading: function () {
    this._loadingDep.depend();
    return this._loading > 0;
  },

  /**
   * Prepares the Namespace for a language.
   * It may download/interpret files for that.
   *
   * @param {!Language} language
   */
  prepare: function (language) {
    var self = this;
    var locales = language.getLocales();

    // prepare data structure for all locales
    _.each(locales, function (locale) {
      var hasLocale = self._locales.hasOwnProperty(locale);
      if (! hasLocale) {
        // data to that locale
        self._locales[locale] = {
          dep: new Deps.Dependency,
          data: {},
          status: self._localeFileExists(locale) ? STATUS_NEW : STATUS_MISSING,
        };
        self._warned = false; // more locales, more warnings
      }
    });

    self._prepareLocales(locales);
  },

  /**
   * @private
   * @param {Locale} locale
   * @return {string}
   */
  _filenameForLocale: function (locale) {
    return this._name + '.' + locale.toString() + '.json';
  },

  /**
   * @private
   * @param {Locale} locale
   * @return {boolean}
   */
  _localeFileExists: function (locale) {
    return this._existingLocales.hasOwnProperty(locale);
  },

  /**
   * @private
   * @param {Array.<string>} locale
   */
  _prepareLocales: function (locales) {
    var self = this;
    var locale = locales.shift(); // only prepare the first locale
    if (locale == null) {
      if (! self._localesWarned) {
        console.warn("No more locales to load. All hope is lost", self);
        self._localesWarned = true;
      }
      return;
    }

    var localeData = self._locales[locale];
    switch (localeData.status) {
      case STATUS_NEW:
        self._loadLocale(locale, localeData);
        break;
      case STATUS_MISSING:
      case STATUS_ERROR:
        self._prepareLocales(locales); // locales is already shifted
        break;
    }
  },

  /**
   * @private
   * @param {Locale} locale
   * @param {object} localeData
   */
  _loadLocale: function (locale, localeData) {
    var self = this;
    self._loading++;
    self._loadingDep.changed();
    localeData.status = STATUS_LOADING;
    var filename = self._filenameForLocale(locale);
    HTTP.get('/' + filename, function (error, data) {
      self._loading--;
      self._loadingDep.changed();
      localeData.status = STATUS_LOADED;

      if (error) {
        localeData.status = STATUS_ERROR;
        self._loadError(locale, filename, error);
        self._prepareLocale(locales); // prepare the next locale
      } else try {
        localeData.data = JSON.parse(data.content);
      } catch (e) {
        self._loadError(locale, filename, e);
      }

      localeData.dep.changed();
    });
  },

  /**
   * @param {string}    key
   * @param {!Language} language
   * @return {undefined|mixed}
   */
  get: function (key, language) {
    var self = this;
    self.prepare(language);

    if (! Deps.active) {
      console.warn(
        "translation of '" + key + "' reqested outside of an reactive pipe!"
        + " Cross your fingers that everything is loaded already"
      );
    }

    // check all locales of the language
    var locales = language.getLocales();
    var matchedLocale = _.find(locales, function (locale, index) {
      var localeData = self._locales[locale]; // must exist because we prepared the lang
      localeData.dep.depend(); // file could change if it is not loaded yet

      // because this implementation only loads the first locale the fallback is
      // never prepared. If it is needed to it now
      if (localeData.status < STATUS_LOADING) {
        self._prepareLocales(locales.slice(index)); // this and all after this
        return true; // this is our current best match (none :D)
      }

      return localeData.data.hasOwnProperty(key);
    });

    var data = matchedLocale && self._locales[matchedLocale].data;
    return {
      value: data && data[key],
      meta: (data && data.$) || {},
      locale: matchedLocale
    };
  }
});
<commit_msg>fixed typo/text for reactive warning<commit_after>var STATUS_NEW = 1;
var STATUS_LOADING = 2;
var STATUS_LOADED = 4;
var STATUS_MISSING = 8;
var STATUS_ERROR = 16;

_.extend(Namespace.prototype, {
  /**
   * The namespace is actually a bounch of files but one file per locale. eg.:
   * myTool.en_US.lang.yml
   * myTool.de_DE.lang.yml
   *
   * This is the client implementation which means it uses Dependencies.
   * Also it uses the HTTP interface of Meteor instead of the Asset one.
   *
   * @constructor
   * @private
   * @param {string} name
   */
  _init: function (name) {
    var self = this;

    // get the existing locales from injection
    var namespaces = Injected.obj('translator-namespaces');
    if (! namespaces.hasOwnProperty(self._name)) {
      throw new Error("Namespace '" + self._name + "' does not exist");
    }

    self._locales = {}; // see loop in prepare
    self._loading = 0; // for telling if a request is running
    self._loadingDep = new Deps.Dependency(); // only important for #isLoading
    self._localesWarned = false; // "out of locales to load"
    self._existingLocales = namespaces[self._name]; // set of locale strings
  },

  /**
   * Tells if a reactive call could happen.
   * @return {bool}
   */
  isLoading: function () {
    this._loadingDep.depend();
    return this._loading > 0;
  },

  /**
   * Prepares the Namespace for a language.
   * It may download/interpret files for that.
   *
   * @param {!Language} language
   */
  prepare: function (language) {
    var self = this;
    var locales = language.getLocales();

    // prepare data structure for all locales
    _.each(locales, function (locale) {
      var hasLocale = self._locales.hasOwnProperty(locale);
      if (! hasLocale) {
        // data to that locale
        self._locales[locale] = {
          dep: new Deps.Dependency,
          data: {},
          status: self._localeFileExists(locale) ? STATUS_NEW : STATUS_MISSING,
        };
        self._warned = false; // more locales, more warnings
      }
    });

    self._prepareLocales(locales);
  },

  /**
   * @private
   * @param {Locale} locale
   * @return {string}
   */
  _filenameForLocale: function (locale) {
    return this._name + '.' + locale.toString() + '.json';
  },

  /**
   * @private
   * @param {Locale} locale
   * @return {boolean}
   */
  _localeFileExists: function (locale) {
    return this._existingLocales.hasOwnProperty(locale);
  },

  /**
   * @private
   * @param {Array.<string>} locale
   */
  _prepareLocales: function (locales) {
    var self = this;
    var locale = locales.shift(); // only prepare the first locale
    if (locale == null) {
      if (! self._localesWarned) {
        console.warn("No more locales to load. All hope is lost", self);
        self._localesWarned = true;
      }
      return;
    }

    var localeData = self._locales[locale];
    switch (localeData.status) {
      case STATUS_NEW:
        self._loadLocale(locale, localeData);
        break;
      case STATUS_MISSING:
      case STATUS_ERROR:
        self._prepareLocales(locales); // locales is already shifted
        break;
    }
  },

  /**
   * @private
   * @param {Locale} locale
   * @param {object} localeData
   */
  _loadLocale: function (locale, localeData) {
    var self = this;
    self._loading++;
    self._loadingDep.changed();
    localeData.status = STATUS_LOADING;
    var filename = self._filenameForLocale(locale);
    HTTP.get('/' + filename, function (error, data) {
      self._loading--;
      self._loadingDep.changed();
      localeData.status = STATUS_LOADED;

      if (error) {
        localeData.status = STATUS_ERROR;
        self._loadError(locale, filename, error);
        self._prepareLocale(locales); // prepare the next locale
      } else try {
        localeData.data = JSON.parse(data.content);
      } catch (e) {
        self._loadError(locale, filename, e);
      }

      localeData.dep.changed();
    });
  },

  /**
   * @param {string}    key
   * @param {!Language} language
   * @return {undefined|mixed}
   */
  get: function (key, language) {
    var self = this;
    self.prepare(language);

    if (! Deps.active && typeof console !== 'undefined') {
      console.warn("Translation of '" + key + "' requested outside of a reactive context!");
    }

    // check all locales of the language
    var locales = language.getLocales();
    var matchedLocale = _.find(locales, function (locale, index) {
      var localeData = self._locales[locale]; // must exist because we prepared the lang
      localeData.dep.depend(); // file could change if it is not loaded yet

      // because this implementation only loads the first locale the fallback is
      // never prepared. If it is needed to it now
      if (localeData.status < STATUS_LOADING) {
        self._prepareLocales(locales.slice(index)); // this and all after this
        return true; // this is our current best match (none :D)
      }

      return localeData.data.hasOwnProperty(key);
    });

    var data = matchedLocale && self._locales[matchedLocale].data;
    return {
      value: data && data[key],
      meta: (data && data.$) || {},
      locale: matchedLocale
    };
  }
});
<|endoftext|>


<commit_before>const registerCallback = (replicationFrom, callback) => {
  replicationFrom.then(callback)
}

class LocationsService {
  constructor ($injector, pouchDB, angularNavDataUtilsService) {
    let dataModuleRemoteDB

    try {
      dataModuleRemoteDB = $injector.get('dataModuleRemoteDB')
    } catch (e) {
      throw new Error('dataModuleRemoteDB should be provided in the data module configuration')
    }

    this.pouchDB = pouchDB
    this.angularNavDataUtilsService = angularNavDataUtilsService

    this.remoteDB = this.pouchDB(dataModuleRemoteDB)
    this.replicationFrom
    this.localDB
    this.registeredOnReplicationCompleteCallbackIds = []
    this.callbacksPendingRegistration = []
  }

  startReplication (zone, state) {
    var options = {
      filter: 'locations/by-state',
      query_params: {
        zone: zone,
        state: state
      }
    }

    this.localDB = this.pouchDB('navIntLocationsDB')
    this.replicationFrom = this.localDB.replicate.from(this.remoteDB, options)

    while (this.callbacksPendingRegistration.length) {
      let callback = this.callbacksPendingRegistration.shift()
      registerCallback(this.replicationFrom, callback)
    }
  }

  callOnReplicationComplete (callbackId, callback) {
    if (this.registeredOnReplicationCompleteCallbackIds.indexOf(callbackId) === -1) {
      this.registeredOnReplicationCompleteCallbackIds.push(callbackId)
      if (this.replicationFrom) {
        registerCallback(this.replicationFrom, callback)
      } else { // in case the registration happens before starting the replication
        this.callbacksPendingRegistration.push(callback)
      }
    }
  }

  allDocs (options) {
    const db = this.localDB || this.remoteDB
    return this.angularNavDataUtilsService.allDocs(db, options)
  }

  query (view, options) {
    const db = this.localDB || this.remoteDB
    return this.angularNavDataUtilsService.query(db, view, options)
  }

  get (id) {
    const db = this.localDB || this.remoteDB
    return db.get(id)
  }
}

LocationsService.$inject = ['$injector', 'pouchDB', 'angularNavDataUtilsService']

export default LocationsService
<commit_msg>refactor: by-state -> by-level, optional state<commit_after>const registerCallback = (replicationFrom, callback) => {
  replicationFrom.then(callback)
}

class LocationsService {
  constructor ($injector, pouchDB, angularNavDataUtilsService) {
    let dataModuleRemoteDB

    try {
      dataModuleRemoteDB = $injector.get('dataModuleRemoteDB')
    } catch (e) {
      throw new Error('dataModuleRemoteDB should be provided in the data module configuration')
    }

    this.pouchDB = pouchDB
    this.angularNavDataUtilsService = angularNavDataUtilsService

    this.remoteDB = this.pouchDB(dataModuleRemoteDB)
    this.replicationFrom
    this.localDB
    this.registeredOnReplicationCompleteCallbackIds = []
    this.callbacksPendingRegistration = []
  }

  startReplication (zone, state) {
    var options = {
      filter: 'locations/by-level',
      query_params: {
        zone: zone
      }
    }

    if (state) {
      options.query_params.state = state
    }

    this.localDB = this.pouchDB('navIntLocationsDB')
    this.replicationFrom = this.localDB.replicate.from(this.remoteDB, options)

    while (this.callbacksPendingRegistration.length) {
      let callback = this.callbacksPendingRegistration.shift()
      registerCallback(this.replicationFrom, callback)
    }
  }

  callOnReplicationComplete (callbackId, callback) {
    if (this.registeredOnReplicationCompleteCallbackIds.indexOf(callbackId) === -1) {
      this.registeredOnReplicationCompleteCallbackIds.push(callbackId)
      if (this.replicationFrom) {
        registerCallback(this.replicationFrom, callback)
      } else { // in case the registration happens before starting the replication
        this.callbacksPendingRegistration.push(callback)
      }
    }
  }

  allDocs (options) {
    const db = this.localDB || this.remoteDB
    return this.angularNavDataUtilsService.allDocs(db, options)
  }

  query (view, options) {
    const db = this.localDB || this.remoteDB
    return this.angularNavDataUtilsService.query(db, view, options)
  }

  get (id) {
    const db = this.localDB || this.remoteDB
    return db.get(id)
  }
}

LocationsService.$inject = ['$injector', 'pouchDB', 'angularNavDataUtilsService']

export default LocationsService
<|endoftext|>


<commit_before>'use strict';

const Database = require('nedb');
const define = require('../define');
const D = require('../d');

const databasePath = define.rootPath + "/.Jukeboks/virtual_directory.json";
let db = new Database({ filename: databasePath, autoload: true });
db.ensureIndex({ fieldName: 'path', unique: true }, (err) => { });

var storage = {};

async function sortStorage() {
  for (var key in storage) {
    storage[key] = storage[key].sort((a, b) => {
      if (a.name > b.name) {
        return 1;
      } else {
        return -1;
      }
    })
  }
}

function stream(filePath, callback, completion) {
  const fs = require('fs');
  const readStream = fs.createReadStream(filePath);
  const readline = require('readline');
  const reader = readline.createInterface(readStream, {});
  reader.on('line', (line) => {
    var s = line.split(',').map(e => e.trim());
    if (s < 3) return;
    var path = s.shift();
    var name = s.shift();
    var d = new D(name, path);
    callback(d, s);
  });
  reader.on('close', completion);
}

function normalizeDirname(dirPath) {
  if (dirPath.startsWith(define.virtualPath)) {
    return dirPath.slice(define.virtualPath.length + 1)
  }
  return dirPath
}

function getStorageKeys() {
  return Object.keys(storage).map(e => {
    return new D(e, define.virtualPath + "/" + e)
  });
}

class VirtualFinder {

  constructor() {
    this._setup()
  }

  search(dirPath, callback) {
    var dirname = normalizeDirname(dirPath)

    if (!dirname || dirname.length == 0) {
      callback(getStorageKeys())
      return;
    }

    var ds = storage[dirname]
    if (!ds) ds = []
    callback(ds)
  }

  create(d, terms, callback) {
    this._select(d.path, (doc) => {
      var data = {
        name: d.name,
        terms: terms,
      }

      if (doc) {
        this._update(doc.path, data, (replaced) => {
          callback(replaced, true);
        })
        return;
      }

      data.path = d.path
      db.insert(data);
      callback(data, false);
    });
  }

  _update(path, data, callback) {
    db.update({ path: path },
      { $set: data },
      {},
      (err, replaced) => {
        callback(replaced)
      }
    );
  }

  remove(filePath) {
    if (filePath.startsWith(define.virtualPath)) {
      var dirname = normalizeDirname(filePath)
      if (!dirname || dirname.length == 0) { return; }
      this.search(dirname, (ds) => {
        ds.forEach(d => {
          this._select(d.path, (doc) => {
            var data = {
              name: doc.name,
              terms: doc.terms.filter(t => t != dirname),
            }
            this._update(doc.path, data, () => { })
          })
        })
        delete storage[dirname];
      })
      return;
    }

    this._select(filePath, (doc) => {
      if (doc) {
        doc.terms.forEach(e => {
          var array = storage[e].filter(d => d.path != filePath);
          storage[e] = array;
        })
        this._remove(filePath)
      }
    });

  }

  _remove(path) {
    db.remove({ path: path }, {}, (err, numRemoved) => { });
  }

  _setup() {
    this._selectAll(docs => {
      docs.forEach(doc => {
        let d = new D(doc.name, doc.path, true)
        doc.terms.forEach(e => {
          if (!storage[e]) storage[e] = [];
          storage[e].push(d);
        })
      })
    })
  }

  _select(path, callback) {
    db.findOne({ path: path }, (err, doc) => {
      callback(doc);
    });
  }

  _selectAll(callback) {
    db.find({}).exec((err, docs) => {
      callback(docs);
    });
  }

  _saveToStorage(d, terms) {
    terms.forEach(e => {
      if (!storage[e]) storage[e] = [];
      var hit = storage[e].filter(_d => _d.path == d.path)
      if (hit.length > 0) { return; }
      storage[e].push(d);
    });
  }

  importFile(filePath) {
    stream(filePath, (d, terms) => {
      this.create(d, terms, () => {
        this._saveToStorage(d, terms)
      });
    }, () => { sortStorage() })
  }

}

module.exports = new VirtualFinder();
<commit_msg>Improve to output data from storage with sorting<commit_after>'use strict';

const Database = require('nedb');
const define = require('../define');
const D = require('../d');

const databasePath = define.rootPath + "/.Jukeboks/virtual_directory.json";
let db = new Database({ filename: databasePath, autoload: true });
db.ensureIndex({ fieldName: 'path', unique: true }, (err) => { });

var storage = {};

function stream(filePath, callback, completion) {
  const fs = require('fs');
  const readStream = fs.createReadStream(filePath);
  const readline = require('readline');
  const reader = readline.createInterface(readStream, {});
  reader.on('line', (line) => {
    var s = line.split(',').map(e => e.trim());
    if (s < 3) return;
    var path = s.shift();
    var name = s.shift();
    var d = new D(name, path);
    callback(d, s);
  });
  reader.on('close', completion);
}

function normalizeDirname(dirPath) {
  if (dirPath.startsWith(define.virtualPath)) {
    return dirPath.slice(define.virtualPath.length + 1)
  }
  return dirPath
}

function getStorageKeys() {
  return Object.keys(storage).map(e => {
    return new D(e, define.virtualPath + "/" + e)
  }).sort((a, b) => {
    if (a.name > b.name) {
      return 1;
    } else {
      return -1;
    }
  })
}

class VirtualFinder {

  constructor() {
    this._setup()
  }

  search(dirPath, callback) {
    var dirname = normalizeDirname(dirPath)

    if (!dirname || dirname.length == 0) {
      callback(getStorageKeys())
      return;
    }

    var ds = storage[dirname]
    if (!ds) ds = []
    ds = ds.sort((a, b) => {
      if (a.name > b.name) {
        return 1;
      } else {
        return -1;
      }
    })
    callback(ds)
  }

  create(d, terms, callback) {
    this._select(d.path, (doc) => {
      var data = {
        name: d.name,
        terms: terms,
      }

      if (doc) {
        this._update(doc.path, data, (replaced) => {
          callback(replaced, true);
        })
        return;
      }

      data.path = d.path
      db.insert(data);
      callback(data, false);
    });
  }

  _update(path, data, callback) {
    db.update({ path: path },
      { $set: data },
      {},
      (err, replaced) => {
        callback(replaced)
      }
    );
  }

  remove(filePath) {
    if (filePath.startsWith(define.virtualPath)) {
      var dirname = normalizeDirname(filePath)
      if (!dirname || dirname.length == 0) { return; }
      this.search(dirname, (ds) => {
        ds.forEach(d => {
          this._select(d.path, (doc) => {
            var data = {
              name: doc.name,
              terms: doc.terms.filter(t => t != dirname),
            }
            this._update(doc.path, data, () => { })
          })
        })
        delete storage[dirname];
      })
      return;
    }

    this._select(filePath, (doc) => {
      if (doc) {
        doc.terms.forEach(e => {
          var array = storage[e].filter(d => d.path != filePath);
          storage[e] = array;
        })
        this._remove(filePath)
      }
    });

  }

  _remove(path) {
    db.remove({ path: path }, {}, (err, numRemoved) => { });
  }

  _setup() {
    this._selectAll(docs => {
      docs.forEach(doc => {
        let d = new D(doc.name, doc.path, true)
        doc.terms.forEach(e => {
          if (!e || e.length == 0) { return; }
          if (!storage[e]) storage[e] = [];
          storage[e].push(d);
        })
      })
    })
  }

  _select(path, callback) {
    db.findOne({ path: path }, (err, doc) => {
      callback(doc);
    });
  }

  _selectAll(callback) {
    db.find({}).exec((err, docs) => {
      callback(docs);
    });
  }

  _saveToStorage(d, terms) {
    terms.forEach(e => {
      if (!e || e.length == 0) { return; }
      if (!storage[e]) storage[e] = [];
      var hit = storage[e].filter(_d => _d.path == d.path)
      if (hit.length > 0) { return; }
      storage[e].push(d);
    });
  }

  importFile(filePath) {
    stream(filePath, (d, terms) => {
      this.create(d, terms, () => {
        this._saveToStorage(d, terms)
      });
    }, () => { })
  }

}

module.exports = new VirtualFinder();
<|endoftext|>