<commit_before>import React, { Component } from 'react'
import Container from './Container'
import Formsy from 'formsy-react'
import { FormsyDate,
         FormsySelect,
         FormsyText,
         FormsyTime,
         FormsyToggle } from 'formsy-material-ui/lib'

import { RaisedButton,
         MenuItem     } from 'material-ui'

const errorMessages = {
    wordsError: "Please only use letters",
    numericError: "Please provide a number",
    urlError: "Please provide a valid URL"
  }

  const style = {
      referralOptions :{
          display: 'flex',
          justifyContent: 'space-between',
          alignItems: 'center',
      },
      uploadButton :{
          display: 'flex',
          justifyContent: 'center',
          alignItems: 'center'
      },
      card :{
          marginBottom : 15
      },
      cardActions :{
          textAlign: 'right'
      },
      paperStyle: {
      //   width: 300,
        margin: 'auto',
        padding: 20,
      },
      switchStyle: {
        marginBottom: 16,
      },
      submitStyle: {
        marginTop: 32,
      },
      chip: {
          margin: 4,
      },
      wrapper: {
          display: 'flex',
          flexWrap: 'wrap',
        }
  }

class ReferralForm extends Component {
  constructor(props) {
      super(props)
      this.enableButton = this.enableButton.bind(this)
      this.disableButton = this.disableButton.bind(this)
      this.submitForm = this.submitForm.bind(this)
      this.notifyFormError = this.notifyFormError.bind(this)
      this.enableButton = this.enableButton.bind(this)

      this.state = {
          canSubmit: true,
          isFormDisabled: true,
          formDisabledStatus : 1
      }
  }

  disableButton = () => {
      this.setState({canSubmit: false})
  }

  enableButton = () => {
      this.setState({canSubmit: true})
  }

  enableEdit = () => {
      this.setState( { isFormDisabled: this.state.formDisabledStatus ? false : true  })
      this.setState( { formDisabledStatus: this.state.isFormDisabled ? 0 : 1  })
  }

  submitForm = (data) => {
      console.log(JSON.stringify(data,null,2))
  }

  notifyFormError = (data) => {
      console.error('Form error:', data)
  }

  render(){
      return(
          <Container>
            <Formsy.Form
                onValid={this.enableButton}
                onInvalid={this.disableButton}
                onValidSubmit={this.submitForm}
                onInvalidSubmit={this.notifyFormError}
            >

                    <RaisedButton
                        label="Edit details"
                        primary={true}
                        style={style.uploadButton}
                        onClick={this.enableEdit}
                    />

                    <FormsySelect
                        name="referralStatus"
                        floatingLabelText="Status"
                        disabled={this.state.isFormDisabled}
                    >
                        <MenuItem value={'accepted'} primaryText="Accepted" />
                        <MenuItem value={'assigned'} primaryText="Assigned" />
                        <MenuItem value={'declined'} primaryText="Declined" />
                        <MenuItem value={'withdrawn'} primaryText="Withdrawn" />
                        <MenuItem value={'rejected'} primaryText="Rejected" />
                        <MenuItem value={'completed'} primaryText="Completed" />
                        <MenuItem value={'noShow'} primaryText="No show" />
                    </FormsySelect>

                {/* patient data form below */}

Patient Data

                <FormsyText
                    name="patientSurname"
                    validations="isWords"
                    validationError={errorMessages.wordsError}
                    required
                    hintText="Surname"
                    floatingLabelText="Surname"
                    disabled={this.state.isFormDisabled}
                />

                <FormsyText
                    name="patientgivenName"
                    validations="isWords"
                    validationError={errorMessages.wordsError}
                    required
                    hintText="Given name"
                    floatingLabelText="Given name"
                    disabled={this.state.isFormDisabled}
                />

                <FormsySelect
                        name="patientGender"
                        floatingLabelText="Gender"
                        disabled={this.state.isFormDisabled}
                >
                        <MenuItem value={'M'} primaryText="Male" />
                        <MenuItem value={'F'} primaryText="Female" />
                </FormsySelect>

                <FormsyText
                    name="patientAddress"
                    validations="matchRegexp"
                    validationError={errorMessages.wordsError}
                    hintText="Address"
                    floatingLabelText="Address"
                    disabled={this.state.isFormDisabled}
                />

                <FormsyText
                    name="patientPostcode"
                    validations="isInt"
                    validationError={errorMessages.wordsError}
                    hintText="Postcode"
                    floatingLabelText="Postcode"
                    disabled={this.state.isFormDisabled}
                />

                <FormsyDate
                    name="patientDateOfBirth"
                    floatingLabelText="Date of birth"
                    disabled={this.state.isFormDisabled}
                />

                <FormsyText
                    name="patientMedicare"
                    validations="isAlphanumeric"
                    validationError={errorMessages.wordsError}
                    hintText="Medicare number"
                    floatingLabelText="Medicare number"
                    disabled={this.state.isFormDisabled}
                />

                <FormsyText
                    name="patientContactNumber"
                    validations="isInt"
                    validationError={errorMessages.wordsError}
                    hintText="Contact number"
                    floatingLabelText="Contact number"
                    disabled={this.state.isFormDisabled}
                />

                {/* doctor data form below */}

Doctor Data

                <FormsyText
                    name="doctorSurname"
                    validations="isWords"
                    validationError={errorMessages.wordsError}
                    required
                    hintText="Surname"
                    floatingLabelText="Surname"
                    disabled={this.state.isFormDisabled}
                />

                <FormsyText
                    name="doctorGivenName"
                    validations="isWords"
                    validationError={errorMessages.wordsError}
                    required
                    hintText="Given name"
                    floatingLabelText="Give name"
                    disabled={this.state.isFormDisabled}
                />

                <FormsyText
                    name="doctorClinic"
                    validations="isWords"
                    validationError={errorMessages.wordsError}
                    hintText="Clinic"
                    floatingLabelText="Clinic"
                    disabled={this.state.isFormDisabled}
                />

                <FormsyText
                    name="doctorAddress"
                    validations="isAlphanumeric"
                    validationError={errorMessages.wordsError}
                    hintText="Address"
                    floatingLabelText="Address"
                    disabled={this.state.isFormDisabled}
                />

                <FormsyText
                    name="doctorPostcode"
                    validations="isInt"
                    validationError={errorMessages.wordsError}
                    hintText="Postcode"
                    floatingLabelText="Postcode"
                    disabled={this.state.isFormDisabled}
                />

                <FormsyText
                    name="doctorContactNumber"
                    validations="isInt"
                    validationError={errorMessages.wordsError}
                    hintText="Contact number"
                    floatingLabelText="Contact number"
                    disabled={this.state.isFormDisabled}
                />

                {/* referral form below */}

Referral Details

                <FormsyText
                    name="referralReasonForReferral"
                    validations="isWords"
                    validationError={errorMessages.wordsError}
                    hintText="Reason for referral"
                    floatingLabelText="Reason for referral"
                    disabled={this.state.isFormDisabled}
                />

                <FormsySelect
                    name="referralRequestedService"
                    floatingLabelText="Requested service"
                    disabled={this.state.isFormDisabled}
                >
                    <MenuItem value={'mentalHealthAssessment'} primaryText="Mental Health Assessment" />
                    <MenuItem value={'medicationReview'} primaryText="Medication Review" />
                    <MenuItem value={'dianogsticClarification'} primaryText="Diagnostic Clarifiction" />
                    <MenuItem value={'assessmentForManagementOfBPSD'} primaryText="Assessment for Management of BPSD" />
                </FormsySelect>

                <FormsyToggle
                    name="referralUrgent"
                    label="Urgent"
                    style={style.switchStyle}
                    disabled={this.state.isFormDisabled}
                />

                    <RaisedButton
                        label="Upload"
                        primary={true}
                        style={style.uploadButton}
                        disabled={this.state.isFormDisabled}
                    />

Upload additional information (Limited to one PDF file)

                {/* triage below */}

Triage

                <FormsySelect
                    name="referralRuralCode"
                    floatingLabelText="Rural code"
                    disabled={this.state.isFormDisabled}
                >
                    <MenuItem value={'RA1MajorCities'} primaryText="RA1 MajorCities" />
                    <MenuItem value={'RA2InnerRegional'} primaryText="RA2 Inner Regional" />
                    <MenuItem value={'RA3OuterRegional'} primaryText="RA3 Outer Regional" />
                    <MenuItem value={'RA4Remote'} primaryText="RA4 Remote" />
                    <MenuItem value={'RA5VeryRemote'} primaryText="RA5 Very Remote" />
                </FormsySelect>

                <FormsyDate
                    name="referralAppointmentDate"
                    floatingLabelText="Appointment date"
                    disabled={this.state.isFormDisabled}
                />
                <FormsyTime
                    name="referralAppointmentTime"
                    floatingLabelText="Appointment time"
                    disabled={this.state.isFormDisabled}
                />

Last Date reviewed (placeholder)

                <RaisedButton
                style={style.submitStyle}
                type="submit"
                label="Submit"
                disabled={!this.state.canSubmit}
                />
          </Formsy.Form>

        </Container>
      )
  }
}

export default ReferralForm<commit_msg>updated edit referral and submit logic<commit_after>import React, { Component } from 'react'
import Container from './Container'
import Formsy from 'formsy-react'
import { FormsyDate,
         FormsySelect,
         FormsyText,
         FormsyTime,
         FormsyToggle } from 'formsy-material-ui/lib'

import { RaisedButton,
         MenuItem     } from 'material-ui'

const errorMessages = {
    wordsError: "Please only use letters",
    numericError: "Please provide a number",
    urlError: "Please provide a valid URL"
  }

  const style = {
      referralOptions :{
          display: 'flex',
        //   justifyContent: 'space-between',
          alignItems: 'center',
      },
      uploadButton :{
          display: 'flex',
          justifyContent: 'center',
          alignItems: 'center'
      },
      card :{
          marginBottom : 15
      },
      cardActions :{
          textAlign: 'right'
      },
      paperStyle: {
      //   width: 300,
        margin: 'auto',
        padding: 20,
      },
      switchStyle: {
        marginBottom: 16,
      },
      submitStyle: {
        marginTop: 32,
      },
      chip: {
          margin: 4,
      },
      wrapper: {
          display: 'flex',
          flexWrap: 'wrap',
        }
  }

class ReferralForm extends Component {
  constructor(props) {
      super(props)
      this.enableButton = this.enableButton.bind(this)
      this.disableButton = this.disableButton.bind(this)
      this.submitForm = this.submitForm.bind(this)
      this.notifyFormError = this.notifyFormError.bind(this)
      this.enableButton = this.enableButton.bind(this)

      this.state = {
          canSubmit: false,
          submitStatus: 0,
          isFormDisabled: true,
          formDisabledStatus : 1,
          isEditButttonDisabled : false,
          editButttonStatus : 0

      }
  }

  disableButton = () => {
      this.setState({canSubmit: false})
  }

  enableButton = () => {
      this.setState({canSubmit: true})
  }

  enableEdit = () => {
      this.setState( { isFormDisabled: this.state.formDisabledStatus ? false : true  })
      this.setState( { formDisabledStatus: this.state.isFormDisabled ? 0 : 1  })

    //   this.setState( { canSubmit: this.state.submitStatus ? false : true  })
    //   this.setState( { submitStatus: this.state.canSubmit ? 0 : 1  })

      this.setState( { isEditButttonDisabled: this.state.editButttonStatus ? false : true  })
      this.setState( { editButttonStatus: this.state.isEditButttonDisabled ? 0 : 1  })
  }

  submitForm = (data) => {
      this.setState( { isFormDisabled: this.state.formDisabledStatus ? false : true  })
      this.setState( { formDisabledStatus: this.state.isFormDisabled ? 0 : 1  })
      this.setState( { isEditButttonDisabled: this.state.editButttonStatus ? false : true  })
      this.setState( { editButttonStatus: this.state.isEditButttonDisabled ? 0 : 1  })
      this.setState( { canSubmit: false } )

      console.log(JSON.stringify(data,null,2))
  }

  notifyFormError = (data) => {
      console.error('Form error:', data)
  }

  render(){
      return(
          <Container>
            <Formsy.Form
                onValid={this.enableButton}
                onInvalid={this.disableButton}
                onValidSubmit={this.submitForm}
                onInvalidSubmit={this.notifyFormError}
            >

                    <RaisedButton
                        label="Edit details"
                        primary={true}
                        style={style.uploadButton}
                        onClick={this.enableEdit}
                        disabled={this.state.isEditButttonDisabled}
                    />

                     <RaisedButton
                        type="submit"
                        label="Submit"
                        disabled={!this.state.canSubmit}
                    />

Referral status

                    <FormsySelect
                        name="referralStatus"
                        floatingLabelText="Status"
                        disabled={this.state.isFormDisabled}
                    >
                        <MenuItem value={'accepted'} primaryText="Accepted" />
                        <MenuItem value={'assigned'} primaryText="Assigned" />
                        <MenuItem value={'declined'} primaryText="Declined" />
                        <MenuItem value={'withdrawn'} primaryText="Withdrawn" />
                        <MenuItem value={'rejected'} primaryText="Rejected" />
                        <MenuItem value={'completed'} primaryText="Completed" />
                        <MenuItem value={'noShow'} primaryText="No show" />
                    </FormsySelect>

                {/* patient data form below */}

Patient Data

                <FormsyText
                    name="patientSurname"
                    validations="isWords"
                    validationError={errorMessages.wordsError}
                    required
                    hintText="Surname"
                    floatingLabelText="Surname"
                    disabled={this.state.isFormDisabled}
                />

                <FormsyText
                    name="patientgivenName"
                    validations="isWords"
                    validationError={errorMessages.wordsError}
                    required
                    hintText="Given name"
                    floatingLabelText="Given name"
                    disabled={this.state.isFormDisabled}
                />

                <FormsySelect
                        name="patientGender"
                        floatingLabelText="Gender"
                        disabled={this.state.isFormDisabled}
                >
                        <MenuItem value={'M'} primaryText="Male" />
                        <MenuItem value={'F'} primaryText="Female" />
                </FormsySelect>

                <FormsyText
                    name="patientAddress"
                    validations="matchRegexp"
                    validationError={errorMessages.wordsError}
                    hintText="Address"
                    floatingLabelText="Address"
                    disabled={this.state.isFormDisabled}
                />

                <FormsyText
                    name="patientPostcode"
                    validations="isInt"
                    validationError={errorMessages.wordsError}
                    hintText="Postcode"
                    floatingLabelText="Postcode"
                    disabled={this.state.isFormDisabled}
                />

                <FormsyDate
                    name="patientDateOfBirth"
                    floatingLabelText="Date of birth"
                    disabled={this.state.isFormDisabled}
                />

                <FormsyText
                    name="patientMedicare"
                    validations="isAlphanumeric"
                    validationError={errorMessages.wordsError}
                    hintText="Medicare number"
                    floatingLabelText="Medicare number"
                    disabled={this.state.isFormDisabled}
                />

                <FormsyText
                    name="patientContactNumber"
                    validations="isInt"
                    validationError={errorMessages.wordsError}
                    hintText="Contact number"
                    floatingLabelText="Contact number"
                    disabled={this.state.isFormDisabled}
                />

                {/* doctor data form below */}

Doctor Data

                <FormsyText
                    name="doctorSurname"
                    validations="isWords"
                    validationError={errorMessages.wordsError}
                    required
                    hintText="Surname"
                    floatingLabelText="Surname"
                    disabled={this.state.isFormDisabled}
                />

                <FormsyText
                    name="doctorGivenName"
                    validations="isWords"
                    validationError={errorMessages.wordsError}
                    required
                    hintText="Given name"
                    floatingLabelText="Give name"
                    disabled={this.state.isFormDisabled}
                />

                <FormsyText
                    name="doctorClinic"
                    validations="isWords"
                    validationError={errorMessages.wordsError}
                    hintText="Clinic"
                    floatingLabelText="Clinic"
                    disabled={this.state.isFormDisabled}
                />

                <FormsyText
                    name="doctorAddress"
                    validations="isAlphanumeric"
                    validationError={errorMessages.wordsError}
                    hintText="Address"
                    floatingLabelText="Address"
                    disabled={this.state.isFormDisabled}
                />

                <FormsyText
                    name="doctorPostcode"
                    validations="isInt"
                    validationError={errorMessages.wordsError}
                    hintText="Postcode"
                    floatingLabelText="Postcode"
                    disabled={this.state.isFormDisabled}
                />

                <FormsyText
                    name="doctorContactNumber"
                    validations="isInt"
                    validationError={errorMessages.wordsError}
                    hintText="Contact number"
                    floatingLabelText="Contact number"
                    disabled={this.state.isFormDisabled}
                />

                {/* referral form below */}

Referral Details

                <FormsyText
                    name="referralReasonForReferral"
                    validations="isWords"
                    validationError={errorMessages.wordsError}
                    hintText="Reason for referral"
                    floatingLabelText="Reason for referral"
                    disabled={this.state.isFormDisabled}
                />

                <FormsySelect
                    name="referralRequestedService"
                    floatingLabelText="Requested service"
                    disabled={this.state.isFormDisabled}
                >
                    <MenuItem value={'mentalHealthAssessment'} primaryText="Mental Health Assessment" />
                    <MenuItem value={'medicationReview'} primaryText="Medication Review" />
                    <MenuItem value={'dianogsticClarification'} primaryText="Diagnostic Clarifiction" />
                    <MenuItem value={'assessmentForManagementOfBPSD'} primaryText="Assessment for Management of BPSD" />
                </FormsySelect>

                <FormsyToggle
                    name="referralUrgent"
                    label="Urgent"
                    style={style.switchStyle}
                    disabled={this.state.isFormDisabled}
                />

                    <RaisedButton
                        label="Upload"
                        primary={true}
                        style={style.uploadButton}
                        disabled={this.state.isFormDisabled}
                    />

Upload additional information (Limited to one PDF file)

                {/* triage below */}

Triage

                <FormsySelect
                    name="referralRuralCode"
                    floatingLabelText="Rural code"
                    disabled={this.state.isFormDisabled}
                >
                    <MenuItem value={'RA1MajorCities'} primaryText="RA1 MajorCities" />
                    <MenuItem value={'RA2InnerRegional'} primaryText="RA2 Inner Regional" />
                    <MenuItem value={'RA3OuterRegional'} primaryText="RA3 Outer Regional" />
                    <MenuItem value={'RA4Remote'} primaryText="RA4 Remote" />
                    <MenuItem value={'RA5VeryRemote'} primaryText="RA5 Very Remote" />
                </FormsySelect>

                <FormsyDate
                    name="referralAppointmentDate"
                    floatingLabelText="Appointment date"
                    disabled={this.state.isFormDisabled}
                />
                <FormsyTime
                    name="referralAppointmentTime"
                    floatingLabelText="Appointment time"
                    disabled={this.state.isFormDisabled}
                />

Last Date reviewed (placeholder)

          </Formsy.Form>

        </Container>
      )
  }
}

export default ReferralForm<|endoftext|>


<commit_before>const requireProps = require('require-props')(__dirname);
const EventEmitter = require('events').EventEmitter;

function registerWindowButtonHandlers() {
  const BrowserWindow = require('remote').require('browser-window');

  document.querySelector('.titlebar-close').addEventListener('click', () =>{
    window.close();
  });

  document.querySelector('.titlebar-minimize').addEventListener('click', () =>{
    BrowserWindow.getFocusedWindow().minimize();
  });

  document.querySelector('.titlebar-fullscreen').addEventListener('click', () =>{
    const win = BrowserWindow.getFocusedWindow();
    win.setFullScreen(!win.isFullScreen());
  });
}

function registerJsCommands(commands) {
  commands.register('inject-js', code => {
    const script = document.createElement('script');
    script.textContent = code;
    document.body.appendChild(script);
  });

  commands.register('inject-script', path => {
    const script = document.createElement('script');
    script.src = path;
    document.body.appendChild(script);
  });
}

const app = Object.assign(new EventEmitter(), {
  name: 'termite',
  window: null,
  packages: {},
  packagesFolder: __dirname + '/../packages', //this could be removed

  quit() {
    this.commands.execute('quit');
  }
});

requireProps(app, [
  './commands',
  './palette',
  './tabs',
  './config',
  './menus',
  './plugins'
]);

registerJsCommands(app.commands);
registerWindowButtonHandlers();

global.termite = app;

app.plugins.load()
  .then(() =>
    app.emit('packages-init-done')
  )
  .then(() =>
    require('remote').getCurrentWindow().show()
  )
  .catch(err => {
    process.stderr.write(`Error loading plugins:\n${err.stack}\n`);
  });

module.exports = app;
<commit_msg>extracted cmds & menu from core plugin<commit_after>const requireProps = require('require-props')(__dirname);
const EventEmitter = require('events').EventEmitter;

function registerWindowButtonHandlers() {
  const BrowserWindow = require('remote').require('browser-window');

  document.querySelector('.titlebar-close').addEventListener('click', () =>{
    window.close();
  });

  document.querySelector('.titlebar-minimize').addEventListener('click', () =>{
    BrowserWindow.getFocusedWindow().minimize();
  });

  document.querySelector('.titlebar-fullscreen').addEventListener('click', () =>{
    const win = BrowserWindow.getFocusedWindow();
    win.setFullScreen(!win.isFullScreen());
  });
}

function registerCommands(commands) {
  commands.register('inject-js', code => {
    const script = document.createElement('script');
    script.textContent = code;
    document.body.appendChild(script);
  });

  commands.register('inject-script', path => {
    const script = document.createElement('script');
    script.src = path;
    document.body.appendChild(script);
  });

  commands.register('quit', () => {
    window.close();
  });
}

const app = Object.assign(new EventEmitter(), {
  name: 'termite',
  window: null,
  packages: {},
  packagesFolder: __dirname + '/../packages', //this could be removed

  quit() {
    this.commands.execute('quit');
  }
});

requireProps(app, [
  './commands',
  './palette',
  './tabs',
  './config',
  './menus',
  './plugins'
]);

app.menus.merge({
  File: [{
    label: 'Exit',
    accelerator: 'CmdOrCtrl+Q',
    command: 'quit'
  }, {
    label: 'Close current shell',
    accelerator: 'CmdOrCtrl+D',
    command: 'close-current'
  }]
});

registerCommands(app.commands);
registerWindowButtonHandlers();

global.termite = app;

app.plugins.load()
  .then(() =>
    app.emit('packages-init-done')
  )
  .then(() =>
    require('remote').getCurrentWindow().show()
  )
  .catch(err => {
    process.stderr.write(`Error loading plugins:\n${err.stack}\n`);
  });

module.exports = app;
<|endoftext|>


<commit_before>$A.bind(window, 'load', function(){

	// Configure the form fields for the Error Validation Demo

	$A.setFormFields($A.getEl('addressForm'),
					{

					// Configure error handling for Inputs with specific ID attribute values

					// Billing Address1 field
					'b-add1':
									{
									// Set default error text
									errorText: 'Please enter a valid street address.',
									// Configure a validation function to check the field
									validate: function(ev, dc){
										var s = this.value, r = true;

										if (!s){
											r = false;
											// Overwrite the default error text with custom values
											dc.source = 'The street address cannot be blank.';
										}

										else if (s.length < 5){
											r = false;
											dc.source = 'A valid street address is required.';
										}
										// Return true to pass, or false to fail validation
										return r;
									},
									// Optionally choose to hide or show the tooltip visually
									// false is set by default if omitted
									hideError: false,
// Set a class to be toggled when an error is detected
// This will be bound to the dc.triggerObj DOM node, or optionally to the dc.targetObj or dc.classObj DOM node instead if declared.
// 'validateError' is set by default if omitted
									toggleClass: 'validateError',
									// Pass AccDC overrides to change the behavior of the tooltip object when rendered
									// All API properties and methods documented on the Core API tab at WhatSock.com are applicable here.
									overrides:
													{
													// Change the beginning and ending boundary role text for screen reader users
													// 'Error' is set by default if omitted.
													role: 'Error',
// Change the accStart and accEnd properties to change the 'Start' and 'End' keywords, which are part of the boundary text for screen reader users.
// E.G
													accStart: 'Start',
													accEnd: 'End',
													// Change the DOM insertion point to the parent Div element by setting targetObj.
													// The triggering Input field is used by default if omitted
													targetObj: $A.getEl('b-add1').parentNode,
													// Set a node where the toggleClass property value will be toggled when validation fails
													// The triggering Input field or targetObj element is used by default if omitted
													classObj: $A.getEl('b-add1').parentNode.parentNode.parentNode,
													// Manually set the element where visual positioning calculations will be bound to
													posAnchor: $A.getEl('b-add1').parentNode.parentNode.parentNode,
													// Set the tooltip to appear on the left of the posAnchor element
													// If posAnchor is not set, triggerObj or targetObj is used for this purpose instead.
													// 3 is set by default if omitted
													autoPosition: 8,
													// Add an additional offset for spacing
													// 10 is set by default if omitted
													offsetLeft: -10,
													// Set a class for the tooltip container
													// 'errorTooltip' is set by default if omitted
													className: 'errorTooltip',
													// Set proper heading nesting for screen reader users.
													// 3 is set by default if omitted
													ariaLevel: 4
													}
									},

					// Billing City field
					'b-city':
									{
									errorText: 'Please enter a valid City.',
									validate: function(ev, dc){
										// The simplest form of validation
										return this.value ? true : false;
									},
									overrides:
													{
													targetObj: $A.getEl('b-city').parentNode,
													classObj: $A.getEl('b-city').parentNode.parentNode.parentNode,
													posAnchor: $A.getEl('b-city').parentNode.parentNode.parentNode,
													autoPosition: 8,
													offsetLeft: -10,
													ariaLevel: 4
													}
									},

					// Billing State field
					'b-state':
									{
									errorText: 'Please enter a valid State.',
									validate: function(ev, dc){
										return this.value ? true : false;
									},
									overrides:
													{
													targetObj: $A.getEl('b-state').parentNode,
													classObj: $A.getEl('b-state').parentNode.parentNode.parentNode,
													posAnchor: $A.getEl('b-state').parentNode.parentNode.parentNode,
													autoPosition: 8,
													offsetLeft: -10,
													ariaLevel: 4
													}
									},

					// Billing Zipcode field
					'b-zip':
									{
									errorText: 'Please enter a valid Zipcode.',
									validate: function(ev, dc){
										return this.value ? true : false;
									},
									overrides:
													{
													targetObj: $A.getEl('b-zip').parentNode,
													classObj: $A.getEl('b-zip').parentNode.parentNode.parentNode,
													posAnchor: $A.getEl('b-zip').parentNode.parentNode.parentNode,
													autoPosition: 8,
													offsetLeft: -10,
													ariaLevel: 4
													}
									},

					// Shipping Address1 field
					'm-add1':
									{
									errorText: 'Please enter a valid street address.',
									validate: function(ev, dc){
										return this.value ? true : false;
									},
									overrides:
													{
													targetObj: $A.getEl('m-add1').parentNode,
													classObj: $A.getEl('m-add1').parentNode.parentNode.parentNode,
													posAnchor: $A.getEl('m-add1').parentNode.parentNode.parentNode,
													// Set the tooltip to appear on the right of the targetObj element
													autoPosition: 3,
													offsetLeft: 10,
													ariaLevel: 4
													}
									},

					// Shipping City field
					'm-city':
									{
									errorText: 'Please enter a valid City.',
									validate: function(ev, dc){
										return this.value ? true : false;
									},
									overrides:
													{
													targetObj: $A.getEl('m-city').parentNode,
													classObj: $A.getEl('m-city').parentNode.parentNode.parentNode,
													posAnchor: $A.getEl('m-city').parentNode.parentNode.parentNode,
													autoPosition: 3,
													offsetLeft: 10,
													ariaLevel: 4
													}
									},

					// Shipping State field
					'm-state':
									{
									errorText: 'Please enter a valid State.',
									validate: function(ev, dc){
										return this.value ? true : false;
									},
									overrides:
													{
													targetObj: $A.getEl('m-state').parentNode,
													classObj: $A.getEl('m-state').parentNode.parentNode.parentNode,
													posAnchor: $A.getEl('m-state').parentNode.parentNode.parentNode,
													autoPosition: 3,
													offsetLeft: 10,
													ariaLevel: 4
													}
									},

					// Shipping Zipcode field
					'm-zip':
									{
									errorText: 'Please enter a valid Zipcode.',
									validate: function(ev, dc){
										return this.value ? true : false;
									},
									overrides:
													{
													targetObj: $A.getEl('m-zip').parentNode,
													classObj: $A.getEl('m-zip').parentNode.parentNode.parentNode,
													posAnchor: $A.getEl('m-zip').parentNode.parentNode.parentNode,
													autoPosition: 3,
													offsetLeft: 10,
													ariaLevel: 4,
													runAfter: function(dc){
													//alert('test');
													}
													}
									}
					}, function(ev){
		// Optionally do stuff when the form is submitted
		alert('YAY!');
		ev.preventDefault();
	});
});<commit_msg>Fixed the Dynamic Help Tooltip demo<commit_after>$A.bind(window, 'load', function(){

	// Configure the form fields for the Error Validation Demo

	$A.setFormFields($A.getEl('addressForm'),
					{

					// Configure error handling for Inputs with specific ID attribute values

					// Billing Address1 field
					'b-add1':
									{
									// Set default error text
									errorText: 'Please enter a valid street address.',
									// Configure a validation function to check the field
									validate: function(ev, dc){
										var s = this.value, r = true;

										if (!s){
											r = false;
											// Overwrite the default error text with custom values
											dc.source = 'The street address cannot be blank.';
										}

										else if (s.length < 5){
											r = false;
											dc.source = 'A valid street address is required.';
										}
										// Return true to pass, or false to fail validation
										return r;
									},
									// Optionally choose to hide or show the tooltip visually
									// false is set by default if omitted
									hideError: false,
// Set a class to be toggled when an error is detected
// This will be bound to the dc.triggerObj DOM node, or optionally to the dc.targetObj or dc.classObj DOM node instead if declared.
// 'validateError' is set by default if omitted
									toggleClass: 'validateError',
									// Pass AccDC overrides to change the behavior of the tooltip object when rendered
									// All API properties and methods documented on the Core API tab at WhatSock.com are applicable here.
									overrides:
													{
													// Change the beginning and ending boundary role text for screen reader users
													// 'Error' is set by default if omitted.
													role: 'Error',
// Change the accStart and accEnd properties to change the 'Start' and 'End' keywords, which are part of the boundary text for screen reader users.
// E.G
													accStart: 'Start',
													accEnd: 'End',
													// Change the DOM insertion point to the parent Div element by setting targetObj.
													// The triggering Input field is used by default if omitted
													targetObj: $A.getEl('b-add1').parentNode,
													// Set a node where the toggleClass property value will be toggled when validation fails
													// The triggering Input field or targetObj element is used by default if omitted
													classObj: $A.getEl('b-add1').parentNode.parentNode.parentNode,
													// Manually set the element where visual positioning calculations will be bound to
													posAnchor: $A.getEl('b-add1').parentNode.parentNode.parentNode,
													// Set the tooltip to appear on the left of the posAnchor element
													// If posAnchor is not set, triggerObj or targetObj is used for this purpose instead.
													// 3 is set by default if omitted
													autoPosition: 8,
													// Add an additional offset for spacing
													// 10 is set by default if omitted
													offsetLeft: -10,
													// Set a class for the tooltip container
													// 'errorTooltip' is set by default if omitted
													className: 'errorTooltip',
													// Set proper heading nesting for screen reader users.
													// 3 is set by default if omitted
													ariaLevel: 4
													}
									},

					// Billing City field
					'b-city':
									{
									errorText: 'Please enter a valid City.',
									validate: function(ev, dc){
										// The simplest form of validation
										return this.value ? true : false;
									},
									overrides:
													{
													targetObj: $A.getEl('b-city').parentNode,
													classObj: $A.getEl('b-city').parentNode.parentNode.parentNode,
													posAnchor: $A.getEl('b-city').parentNode.parentNode.parentNode,
													autoPosition: 8,
													offsetLeft: -10,
													ariaLevel: 4
													}
									},

					// Billing State field
					'b-state':
									{
									errorText: 'Please enter a valid State.',
									validate: function(ev, dc){
										return this.value ? true : false;
									},
									overrides:
													{
													targetObj: $A.getEl('b-state').parentNode,
													classObj: $A.getEl('b-state').parentNode.parentNode.parentNode,
													posAnchor: $A.getEl('b-state').parentNode.parentNode.parentNode,
													autoPosition: 8,
													offsetLeft: -10,
													ariaLevel: 4
													}
									},

					// Billing Zipcode field
					'b-zip':
									{
									errorText: 'Please enter a valid Zipcode.',
									validate: function(ev, dc){
										return this.value ? true : false;
									},
									overrides:
													{
													targetObj: $A.getEl('b-zip').parentNode,
													classObj: $A.getEl('b-zip').parentNode.parentNode.parentNode,
													posAnchor: $A.getEl('b-zip').parentNode.parentNode.parentNode,
													autoPosition: 8,
													offsetLeft: -10,
													ariaLevel: 4
													}
									},

					// Shipping Address1 field
					'm-add1':
									{
									errorText: 'Please enter a valid street address.',
									validate: function(ev, dc){
										return this.value ? true : false;
									},
									overrides:
													{
													targetObj: $A.getEl('m-add1').parentNode,
													classObj: $A.getEl('m-add1').parentNode.parentNode.parentNode,
													posAnchor: $A.getEl('m-add1').parentNode.parentNode.parentNode,
													// Set the tooltip to appear on the right of the targetObj element
													autoPosition: 3,
													offsetLeft: 10,
													ariaLevel: 4
													}
									},

					// Shipping City field
					'm-city':
									{
									errorText: 'Please enter a valid City.',
									validate: function(ev, dc){
										return this.value ? true : false;
									},
									overrides:
													{
													targetObj: $A.getEl('m-city').parentNode,
													classObj: $A.getEl('m-city').parentNode.parentNode.parentNode,
													posAnchor: $A.getEl('m-city').parentNode.parentNode.parentNode,
													autoPosition: 3,
													offsetLeft: 10,
													ariaLevel: 4
													}
									},

					// Shipping State field
					'm-state':
									{
									errorText: 'Please enter a valid State.',
									validate: function(ev, dc){
										return this.value ? true : false;
									},
									overrides:
													{
													targetObj: $A.getEl('m-state').parentNode,
													classObj: $A.getEl('m-state').parentNode.parentNode.parentNode,
													posAnchor: $A.getEl('m-state').parentNode.parentNode.parentNode,
													autoPosition: 3,
													offsetLeft: 10,
													ariaLevel: 4
													}
									},

					// Shipping Zipcode field
					'm-zip':
									{
									errorText: 'Please enter a valid Zipcode.',
									validate: function(ev, dc){
										return this.value ? true : false;
									},
									overrides:
													{
													targetObj: $A.getEl('m-zip').parentNode,
													classObj: $A.getEl('m-zip').parentNode.parentNode.parentNode,
													posAnchor: $A.getEl('m-zip').parentNode.parentNode.parentNode,
													autoPosition: 3,
													offsetLeft: 10,
													ariaLevel: 4,
													runAfter: function(dc){
													//alert('test');
													}
													}
									}
					}, function(ev){
		// Optionally do stuff when the form is submitted
		alert('Yay!');
		ev.preventDefault();
	});

	// Configure the form field for the Dynamic Help Tooltip demo

	$A.setFormFields($A.getEl('questionForm'),
					{

					// Configure the Input with id="questionId"
					'questionId':
									{
									// Set default help text
									helpText: 'Would you like a hint? It rymes with "Sarah".',
									validate: function(ev, dc){
										// First check if the field is blank
										if (!this.value){
											dc.source = 'Would you like a hint? It rymes with "Sarah".';
											dc.open();
											return false;
										}
										// Otherwise, check the inputted text against the correct character sequence
										var a = 'terra', s = this.value.toLowerCase(), pos = function(){
											var r = -1;

											for (var i = 0; i < s.length; i++){
												if (s.charAt(i) === a.charAt(i) && s.substring(0, i) === a.substring(0, i))
													r = i;
											}
											return r;
										}();

										// Customize the tooltip based on the values entered
										if (s.length === 1 && pos === -1){
											dc.source = 'Hmmm, it doesn\'t look like you\'re off to a good start.';
											// Now reopen the tooltip so that it renders the new message
											dc.open();
										}

										else if (s.length === 3 && pos === -1){
											dc.source = 'Well, you can keep going if you want, but it won\'t help much.';
											dc.open();
										}

										else if (s.length === 1 && pos === 0){
											dc.source = 'Ah, now you are on the right track!';
											dc.open();
										}

										else if (s.length === 3 && pos < 2){
											dc.source = 'It looks like you\'ve wandered off there a bit...';
											dc.open();
										}

										else if (s.length === 4 && pos === 3){
											dc.source = 'Excellent! You are almost there.';
											dc.open();
										}

										else if (s.length > 5){
											dc.source = 'Wo, hold on there cowboy!';
											dc.open();
										}

										else if (s.length === 5 && s.substring(0) !== a.substring(0)){
											dc.source = 'Ah, nice try with that one...';
											dc.open();
										}

										else if (s.length === 5 && s === a){
											dc.source = 'That\'s correct, you\'ve got it!';

											// Now, only render the confirmation message if previous tooltips are open first.
											// This prevents the tooltip from appearing again when setting focus to the field after it passes validation
											if (dc.loaded)
												dc.open();
											// Then return true to pass validation
											return true;
										}
										// Otherwise, return false if validation fails
										return false;
									},
									// Assign a class to be toggled
									toggleClass: 'passedValidation',
									// Set the above class to be toggled only when validation is true, and not when false
									togglePassed: true,
									overrides:
													{
													// Set a node where the toggleClass property value will be toggled when validation passes
													classObj: $A.getEl('questionId').parentNode,
													// Position the help tooltip on the right of the Input field
													autoPosition: 3,
													offsetLeft: 10,
													className: 'helpTooltip',
													ariaLevel: 3
													}
									}
					});
});<|endoftext|>


<commit_before>// @ngInject
export class invitationUtilsService {
  constructor(invitationService,
              usersService,
              ncUtilsFlash,
              $q,
              $auth,
              $state,
              $rootScope,
              $timeout,
              $uibModal) {
    this.invitationService = invitationService;
    this.usersService = usersService;
    this.ncUtilsFlash = ncUtilsFlash;
    this.$q = $q;
    this.$auth = $auth;
    this.$state = $state;
    this.$rootScope = $rootScope;
    this.$timeout = $timeout;
    this.$uibModal = $uibModal;
  }

  init() {
    // After successful login/sign up accept invitation and display message to user.
    this.$rootScope.$on('$stateChangeSuccess', () => {
      if (this.$auth.isAuthenticated()) {
        this.usersService.getCurrentUser().then(user => {
          let token = this.invitationService.getInvitationToken();
          if (token && !this.usersService.mandatoryFieldsMissing(user)) {
            this.validateInvitationToken(token).then(() => {
              this.acceptInvitation(token);
            }).catch(() => {
              this.ncUtilsFlash.error(gettext('Invitation could not be accepted'));
            });
          }
        });
      }
    });
  }

  checkAndAccept(token) {
    /* 1) If invitation token is invalid then display 404.

     2) If invitation token is valid and user is already logged in
     then accept invitation and display message to user.

     3) If invitation is valid and user is anonymous then
     redirect him to registration page.
     */

    return this.validateInvitationToken(token).then(() => {
      this.invitationService.check(token).then(() => {
        if (this.$auth.isAuthenticated()) {
          this.acceptInvitation(token).then(() => {
            this.$state.go('profile.details');
          });
        } else {
          this.invitationService.setInvitationToken(token);
          this.$state.go('register');
        }
      }).catch(response => {
        this.showError(response);
        if (this.$auth.isAuthenticated()) {
          this.$state.go('profile.details');
        } else {
          this.$state.go('login');
        }
      });
    }).catch(() => {
      this.ncUtilsFlash.error(gettext('Invitation is not valid anymore.'));
      this.$state.go('errorPage.notFound');
    });
  }

  acceptInvitation(token) {
    return this.shallChangeEmail(token).then(replace_email => {
      return this.invitationService.accept(token, replace_email).then(() => {
        this.ncUtilsFlash.success(gettext('Your invitation was accepted.'));
        this.invitationService.clearInvitationToken();
        this.$rootScope.$broadcast('refreshCustomerList', {updateSignal: true});
      }).catch(this.showError.bind(this));
    });
  }

  validateInvitationToken(token) {
    const dialog = this.$uibModal.open({
      component: 'invitationCheckDialog',
      resolve: {
        token: () => token,
      }
    });
    const deferred = this.$q.defer();
    dialog.result.then(() => deferred.resolve());
    dialog.closed.then(() => deferred.reject());
    return deferred.promise;
  }

  shallChangeEmail(token) {
    return this.loadUserInvitation(token).then(context => {
      if (!context.user.email || context.user.email === context.invitation.email) {
        return true;
      }

      const dialog = this.$uibModal.open({
        component: 'invitationConfirmDialog',
        resolve: {
          user: () => context.user,
          invitation: () => context.invitation,
        }
      });
      const deferred = this.$q.defer();
      dialog.result.then(() => deferred.resolve(true));
      dialog.closed.then(() => deferred.resolve(false));
      return deferred.promise;
    });
  }

  loadUserInvitation(token) {
    return this.usersService.getCurrentUser().then(user => {
      return this.invitationService.check(token).then(response => {
        return { invitation: response.data, user };
      });
    });
  }

  showError(response) {
    if (response.status === 404) {
      this.ncUtilsFlash.error(gettext('Invitation is not found.'));
    } else if (response.status === 400) {
      this.invitationService.clearInvitationToken();
      this.ncUtilsFlash.error(gettext('Invitation is not valid.'));
    } else if (response.status === 500) {
      this.ncUtilsFlash.error(gettext('Internal server error occurred. Please try again or contact support.'));
    }
  }
}

// @ngInject
export function attachInvitationUtils(invitationUtilsService) {
  invitationUtilsService.init();
}
<commit_msg>Revert indentation [WAL-778]<commit_after>// @ngInject
export class invitationUtilsService {
  constructor(
    invitationService,
    usersService,
    ncUtilsFlash,
    $q,
    $auth,
    $state,
    $rootScope,
    $timeout,
    $uibModal) {
    this.invitationService = invitationService;
    this.usersService = usersService;
    this.ncUtilsFlash = ncUtilsFlash;
    this.$q = $q;
    this.$auth = $auth;
    this.$state = $state;
    this.$rootScope = $rootScope;
    this.$timeout = $timeout;
    this.$uibModal = $uibModal;
  }

  init() {
    // After successful login/sign up accept invitation and display message to user.
    this.$rootScope.$on('$stateChangeSuccess', () => {
      if (this.$auth.isAuthenticated()) {
        this.usersService.getCurrentUser().then(user => {
          let token = this.invitationService.getInvitationToken();
          if (token && !this.usersService.mandatoryFieldsMissing(user)) {
            this.validateInvitationToken(token).then(() => {
              this.acceptInvitation(token);
            }).catch(() => {
              this.ncUtilsFlash.error(gettext('Invitation could not be accepted'));
            });
          }
        });
      }
    });
  }

  checkAndAccept(token) {
    /* 1) If invitation token is invalid then display 404.

     2) If invitation token is valid and user is already logged in
     then accept invitation and display message to user.

     3) If invitation is valid and user is anonymous then
     redirect him to registration page.
     */

    return this.validateInvitationToken(token).then(() => {
      this.invitationService.check(token).then(() => {
        if (this.$auth.isAuthenticated()) {
          this.acceptInvitation(token).then(() => {
            this.$state.go('profile.details');
          });
        } else {
          this.invitationService.setInvitationToken(token);
          this.$state.go('register');
        }
      }).catch(response => {
        this.showError(response);
        if (this.$auth.isAuthenticated()) {
          this.$state.go('profile.details');
        } else {
          this.$state.go('login');
        }
      });
    }).catch(() => {
      this.ncUtilsFlash.error(gettext('Invitation is not valid anymore.'));
      this.$state.go('errorPage.notFound');
    });
  }

  acceptInvitation(token) {
    return this.shallChangeEmail(token).then(replace_email => {
      return this.invitationService.accept(token, replace_email).then(() => {
        this.ncUtilsFlash.success(gettext('Your invitation was accepted.'));
        this.invitationService.clearInvitationToken();
        this.$rootScope.$broadcast('refreshCustomerList', {updateSignal: true});
      }).catch(this.showError.bind(this));
    });
  }

  validateInvitationToken(token) {
    const dialog = this.$uibModal.open({
      component: 'invitationCheckDialog',
      resolve: {
        token: () => token,
      }
    });
    const deferred = this.$q.defer();
    dialog.result.then(() => deferred.resolve());
    dialog.closed.then(() => deferred.reject());
    return deferred.promise;
  }

  shallChangeEmail(token) {
    return this.loadUserInvitation(token).then(context => {
      if (!context.user.email || context.user.email === context.invitation.email) {
        return true;
      }

      const dialog = this.$uibModal.open({
        component: 'invitationConfirmDialog',
        resolve: {
          user: () => context.user,
          invitation: () => context.invitation,
        }
      });
      const deferred = this.$q.defer();
      dialog.result.then(() => deferred.resolve(true));
      dialog.closed.then(() => deferred.resolve(false));
      return deferred.promise;
    });
  }

  loadUserInvitation(token) {
    return this.usersService.getCurrentUser().then(user => {
      return this.invitationService.check(token).then(response => {
        return { invitation: response.data, user };
      });
    });
  }

  showError(response) {
    if (response.status === 404) {
      this.ncUtilsFlash.error(gettext('Invitation is not found.'));
    } else if (response.status === 400) {
      this.invitationService.clearInvitationToken();
      this.ncUtilsFlash.error(gettext('Invitation is not valid.'));
    } else if (response.status === 500) {
      this.ncUtilsFlash.error(gettext('Internal server error occurred. Please try again or contact support.'));
    }
  }
}

// @ngInject
export function attachInvitationUtils(invitationUtilsService) {
  invitationUtilsService.init();
}
<|endoftext|>


<commit_before>define([
  'dojo/_base/declare', 'dojo/on', 'dojo/dom-class',
  'dojo/text!./templates/GenomeAlignment.html', './AppBase', 'dojo/dom-construct', 'dijit/registry',
  'dojo/_base/lang', 'dojo/query', 'dijit/Dialog', 'dojo/dom-style',
  '../../WorkspaceManager', 'dojo/when', 'dojo/request', '../SelectedTable'
], function (
  declare, on, domClass,
  Template, AppBase, domConstruct, registry,
  lang, query, Dialog, domStyle,
  WorkspaceManager, when, request, SelectedTable
) {
  return declare([AppBase], {
    apiServiceUrl: window.App.dataAPI,
    baseClass: 'App Assembly',
    templateString: Template,
    applicationName: 'GenomeAlignment',
    applicationHelp: 'user_guides/services/genome_alignment_service.html',
    tutorialLink: 'tutorial/genome_alignment/genome_alignment.html',
    pageTitle: 'Genome Alignment',
    defaultPath: '',
    startingRows: 1,

    constructor: function () {
    },

    startup: function () {
      var _self = this;
      if (this._started) {
        return;
      }
      this.inherited(arguments);

      _self.defaultPath = WorkspaceManager.getDefaultFolder() || _self.activeWorkspacePath;
      _self.output_path.set('value', _self.defaultPath);

      // initialize selected genome table
      var selectedTable = this.selectedTable = new SelectedTable({
        name: 'selectedGenomes',
        colNames: ['Name', 'ID'],
        colKeys: ['name', 'id'],
        label: {
          rowIndex: 1,
          colKey: 'name',
          format: function (obj) { return obj.name + ' [Reference Genome]'; }
        }
      });
      domConstruct.place(selectedTable.domNode, this.genomeTable);

      this.advrow.turnedOn = (this.advrow.style.display != 'none');
      on(this.advanced, 'click', lang.hitch(this, function () {
        this.advrow.turnedOn = (this.advrow.style.display != 'none');
        if (!this.advrow.turnedOn) {
          this.advrow.turnedOn = true;
          this.advrow.style.display = 'block';
          this.advicon.className = 'fa icon-caret-left fa-1';
        }
        else {
          this.advrow.turnedOn = false;
          this.advrow.style.display = 'none';
          this.advicon.className = 'fa icon-caret-down fa-1';
        }
      }));

      this._started = true;
    },

    validate: function () {
      var self = this;

      var isValid;

      if (self.selectedTable.getRows().length &&
        self.output_path.value &&
        self.output_file.value) {
        isValid = true;
      }

      if (isValid) {
        self.submitButton.set('disabled', false);
      } else {
        self.submitButton.set('disabled', true);
      }

      return isValid;
    },

    // called on event from dom
    onAddGenome: function (evt) {
      this.addGenome();
    },

    // adds currently selected genome, or provided genome
    addGenome: function (genomeInfo /* optional */) {
      var self = this;

      // if adding genome directly from object selector
      if (!genomeInfo) {
        var id = self.genomeSelector.value,
          name = self.genomeSelector.get('displayedValue');

        if (!id) return;

        var genomeInfo = {
          id: id,
          name: name
        };
      }

      self.selectedTable.addRow(genomeInfo);
    },

    onAddGenomeGroup: function () {
      var self = this;

      var path = self.genomeGroupSelector.value;

      domStyle.set( query('.loading-status')[0], 'display', 'block');
      when(WorkspaceManager.getObject(path), lang.hitch(this, function (res) {

        var data = typeof res.data == 'string' ? JSON.parse(res.data) : res.data;
        var genomeIDs =  data.id_list.genome_id;

        when(self.getGenomeInfo(genomeIDs), function (genomeInfos) {
          genomeInfos.forEach(function (info) {

            self.addGenome(info);
          });

          domStyle.set( query('.loading-status')[0], 'display', 'none');
        });
      }));

    },

    // takes genome ids, returns prom with {id: xxxx.x, name: 'org_name}
    getGenomeInfo: function (genomeIDs) {
      var url = this.apiServiceUrl +
        'genome/?in(genome_id,(' + genomeIDs.join(',') + '))&select(genome_id,genome_name)';

      return when(request.get(url, {
        headers: {
          Accept: 'application/json',
          Authorization: window.App.authorizationToken
        },
        handleAs: 'json'
      }), function (res) {
        // order matters, organize into  {id: xxxx.x, name: 'org_name}
        var info = genomeIDs.map(function (id) {
          var name = res.filter(function (obj) { return obj.genome_id == id; })[0].genome_name;

          return {
            id: id,
            name: name
          };
        });

        return info;
      });
    },

    onSubmit: function (evt) {
      var _self = this;

      evt.preventDefault();
      evt.stopPropagation();
      if (this.validate()) {
        var values = this.getValues();

        domClass.add(this.domNode, 'Working');
        domClass.remove(this.domNode, 'Error');
        domClass.remove(this.domNode, 'Submitted');

        // this could be moved to app base
        if (window.App.noJobSubmission) {
          var dlg = new Dialog({
            title: 'Job Submission Params: ',
            content: '
' + JSON.stringify(values, null, 4) + '
'
          });
          dlg.startup();
          dlg.show();
          return;
        }

        this.submitButton.set('disabled', true);
        window.App.api.service('AppService.start_app', [this.applicationName, values]).then(function (results) {
          domClass.remove(_self.domNode, 'Working');
          domClass.add(_self.domNode, 'Submitted');
          _self.submitButton.set('disabled', false);
          registry.byClass('p3.widget.WorkspaceFilenameValidationTextBox').forEach(function (obj) {
            obj.reset();
          });
        }, function (err) {
          console.log('Error:', err);
          domClass.remove(_self.domNode, 'Working');
          domClass.add(_self.domNode, 'Error');
          _self.errorMessage.innerHTML = err;
        });
      } else {
        domClass.add(this.domNode, 'Error');
        console.log('Form is incomplete');
      }
    },

    onReset: function (evt) {
      domClass.remove(this.domNode, 'Working');
      domClass.remove(this.domNode, 'Error');
      domClass.remove(this.domNode, 'Submitted');

      this.selectedTable.clear();
    },

    getValues: function () {
      var values = this.inherited(arguments);
      var obj = Object.assign({}, values);

      // ignore unneeded values
      delete obj.genomeGroupSelector;
      delete obj.genomeSelector;

      // get the ids from table selection
      var genomeIDs = this.selectedTable.getRows().map(function (obj) { return obj.id; });

      obj.genome_ids = genomeIDs;
      obj.recipe =
      'progressiveMauve';
      obj.output_path = values.output_path;
      obj.output_file = values.output_file;

      return obj;
    }
  });

});
<commit_msg>remove newline<commit_after>define([
  'dojo/_base/declare', 'dojo/on', 'dojo/dom-class',
  'dojo/text!./templates/GenomeAlignment.html', './AppBase', 'dojo/dom-construct', 'dijit/registry',
  'dojo/_base/lang', 'dojo/query', 'dijit/Dialog', 'dojo/dom-style',
  '../../WorkspaceManager', 'dojo/when', 'dojo/request', '../SelectedTable'
], function (
  declare, on, domClass,
  Template, AppBase, domConstruct, registry,
  lang, query, Dialog, domStyle,
  WorkspaceManager, when, request, SelectedTable
) {
  return declare([AppBase], {
    apiServiceUrl: window.App.dataAPI,
    baseClass: 'App Assembly',
    templateString: Template,
    applicationName: 'GenomeAlignment',
    applicationHelp: 'user_guides/services/genome_alignment_service.html',
    tutorialLink: 'tutorial/genome_alignment/genome_alignment.html',
    pageTitle: 'Genome Alignment',
    defaultPath: '',
    startingRows: 1,

    constructor: function () {
    },

    startup: function () {
      var _self = this;
      if (this._started) {
        return;
      }
      this.inherited(arguments);

      _self.defaultPath = WorkspaceManager.getDefaultFolder() || _self.activeWorkspacePath;
      _self.output_path.set('value', _self.defaultPath);

      // initialize selected genome table
      var selectedTable = this.selectedTable = new SelectedTable({
        name: 'selectedGenomes',
        colNames: ['Name', 'ID'],
        colKeys: ['name', 'id'],
        label: {
          rowIndex: 1,
          colKey: 'name',
          format: function (obj) { return obj.name + ' [Reference Genome]'; }
        }
      });
      domConstruct.place(selectedTable.domNode, this.genomeTable);

      this.advrow.turnedOn = (this.advrow.style.display != 'none');
      on(this.advanced, 'click', lang.hitch(this, function () {
        this.advrow.turnedOn = (this.advrow.style.display != 'none');
        if (!this.advrow.turnedOn) {
          this.advrow.turnedOn = true;
          this.advrow.style.display = 'block';
          this.advicon.className = 'fa icon-caret-left fa-1';
        }
        else {
          this.advrow.turnedOn = false;
          this.advrow.style.display = 'none';
          this.advicon.className = 'fa icon-caret-down fa-1';
        }
      }));

      this._started = true;
    },

    validate: function () {
      var self = this;

      var isValid;

      if (self.selectedTable.getRows().length &&
        self.output_path.value &&
        self.output_file.value) {
        isValid = true;
      }

      if (isValid) {
        self.submitButton.set('disabled', false);
      } else {
        self.submitButton.set('disabled', true);
      }

      return isValid;
    },

    // called on event from dom
    onAddGenome: function (evt) {
      this.addGenome();
    },

    // adds currently selected genome, or provided genome
    addGenome: function (genomeInfo /* optional */) {
      var self = this;

      // if adding genome directly from object selector
      if (!genomeInfo) {
        var id = self.genomeSelector.value,
          name = self.genomeSelector.get('displayedValue');

        if (!id) return;

        var genomeInfo = {
          id: id,
          name: name
        };
      }

      self.selectedTable.addRow(genomeInfo);
    },

    onAddGenomeGroup: function () {
      var self = this;

      var path = self.genomeGroupSelector.value;

      domStyle.set( query('.loading-status')[0], 'display', 'block');
      when(WorkspaceManager.getObject(path), lang.hitch(this, function (res) {

        var data = typeof res.data == 'string' ? JSON.parse(res.data) : res.data;
        var genomeIDs =  data.id_list.genome_id;

        when(self.getGenomeInfo(genomeIDs), function (genomeInfos) {
          genomeInfos.forEach(function (info) {

            self.addGenome(info);
          });

          domStyle.set( query('.loading-status')[0], 'display', 'none');
        });
      }));

    },

    // takes genome ids, returns prom with {id: xxxx.x, name: 'org_name}
    getGenomeInfo: function (genomeIDs) {
      var url = this.apiServiceUrl +
        'genome/?in(genome_id,(' + genomeIDs.join(',') + '))&select(genome_id,genome_name)';

      return when(request.get(url, {
        headers: {
          Accept: 'application/json',
          Authorization: window.App.authorizationToken
        },
        handleAs: 'json'
      }), function (res) {
        // order matters, organize into  {id: xxxx.x, name: 'org_name}
        var info = genomeIDs.map(function (id) {
          var name = res.filter(function (obj) { return obj.genome_id == id; })[0].genome_name;

          return {
            id: id,
            name: name
          };
        });

        return info;
      });
    },

    onSubmit: function (evt) {
      var _self = this;

      evt.preventDefault();
      evt.stopPropagation();
      if (this.validate()) {
        var values = this.getValues();

        domClass.add(this.domNode, 'Working');
        domClass.remove(this.domNode, 'Error');
        domClass.remove(this.domNode, 'Submitted');

        // this could be moved to app base
        if (window.App.noJobSubmission) {
          var dlg = new Dialog({
            title: 'Job Submission Params: ',
            content: '
' + JSON.stringify(values, null, 4) + '
'
          });
          dlg.startup();
          dlg.show();
          return;
        }

        this.submitButton.set('disabled', true);
        window.App.api.service('AppService.start_app', [this.applicationName, values]).then(function (results) {
          domClass.remove(_self.domNode, 'Working');
          domClass.add(_self.domNode, 'Submitted');
          _self.submitButton.set('disabled', false);
          registry.byClass('p3.widget.WorkspaceFilenameValidationTextBox').forEach(function (obj) {
            obj.reset();
          });
        }, function (err) {
          console.log('Error:', err);
          domClass.remove(_self.domNode, 'Working');
          domClass.add(_self.domNode, 'Error');
          _self.errorMessage.innerHTML = err;
        });
      } else {
        domClass.add(this.domNode, 'Error');
        console.log('Form is incomplete');
      }
    },

    onReset: function (evt) {
      domClass.remove(this.domNode, 'Working');
      domClass.remove(this.domNode, 'Error');
      domClass.remove(this.domNode, 'Submitted');

      this.selectedTable.clear();
    },

    getValues: function () {
      var values = this.inherited(arguments);
      var obj = Object.assign({}, values);

      // ignore unneeded values
      delete obj.genomeGroupSelector;
      delete obj.genomeSelector;

      // get the ids from table selection
      var genomeIDs = this.selectedTable.getRows().map(function (obj) { return obj.id; });

      obj.genome_ids = genomeIDs;
      obj.recipe = 'progressiveMauve';
      obj.output_path = values.output_path;
      obj.output_file = values.output_file;

      return obj;
    }
  });

});
<|endoftext|>


<commit_before>'use strict';

app.directive('donutChart', function() {

  const link = (scope, el, attr) => {
    //sets color for each level in data array
    let color = ['#99d6ff', '#99ff99', '#ffff99', '#ff9999'];
    //sets width and height of SVG, radii of pie chart relative to these values
    let width = 350;
    let height = 350;
    let min = Math.min(width, height);
    let svg = d3.select(el[0]).append('svg');
    let pie = d3.layout.pie().sort(null);
    let arc = d3.svg.arc()
      .outerRadius(min / 2 * 0.9)
      .innerRadius(10);

    svg.attr({width: width, height: height});
    var g = svg.append('g')
      .attr('transform', 'translate(' + width / 2 + ',' + height / 2 + ')');

      //when given an array for scope.data, svg updates with paths for chart
    scope.$watch('data', function(data) {

      let percentages = data.map(d => d.percentage);
      console.log("percentages", percentages, arc.centroid([10,100]));

        g.selectAll('path').data(pie(percentages))
            .enter().append('path')
            .style('stroke', 'white')
            .attr('d', arc)
            .attr('fill', function(d, i){ return color[i]; });

    }, true);
  };

  return {
    link: link,
    restrict: 'A',
    scope: {data: '='}
  };
});

// scope: {data: '=chartData'},<commit_msg>refactored donut chart<commit_after>'use strict';

app.directive('donutChart', function() {

  const link = (scope, el, attr) => {
    //sets color for each level in data array
    let color = ['#99d6ff', '#99ff99', '#ffff99', '#ff9999'];
    //sets width and height of SVG, radii of pie chart relative to these values
    let width = 350;
    let height = 350;
    let min = Math.min(width, height);
    let svg = d3.select(el[0]).append('svg');
    let pie = d3.layout.pie().sort(null);
    let arc = d3.svg.arc()
      .outerRadius(min / 2 * 0.9)
      .innerRadius(10);

    svg.attr({width: width, height: height});
    var g = svg.append('g')
      .attr('transform', 'translate(' + width / 2 + ',' + height / 2 + ')');

    //when given an array for scope.data, svg updates with paths for chart
    scope.$watch('data', function(data) {

      let percentages = data.map(d => d.percentage);
      g.selectAll('path').data(pie(percentages))
          .enter().append('path')
          .style('stroke', 'white')
          .attr('d', arc)
          .attr('fill', function(d, i){ return color[i]; });

    }, true);
  };

  return {
    link: link,
    restrict: 'A',
    scope: {data: '='}
  };
});<|endoftext|>


<commit_before>// Shows a top level menu for all test files if ( not running an automated test and not part of doc page )
if( !window.QUnit ){
	var docPath = '../../../docs/';

	// output any blocking scripts that need to be ready before dom ready:
	document.write( '' );
	document.write( '' );
	document.write( '' );

	// inject all the twitter bootstrap css and js ( ok to be injected after page is rendering )
	$('head').append(
		$( '<link rel="shortcut icon" href="' + docPath + 'css/favicon.ico">' ),
		$( '<link href="' + docPath + 'bootstrap/docs/assets/css/bootstrap.css" rel="stylesheet">' ),
		$( '<link href="' + docPath + 'css/kdoc.css" rel="stylesheet">'),
		// pretify:
		$( '' ),
		$( '<link href="' + docPath + 'bootstrap/docs/assets/js/google-code-prettify/prettify.css" rel="stylesheet">' )
	);
} else{
	// provide a stub for prettyKalturaConfig so that tests don't have javascript errors:
	$.fn.prettyKalturaConfig = function( pluginName, flashVars, flashvarCallback ){
		$(this).text( 'running qunit test');
	};
	// hide all prettyconfig:
	$(function(){
		$('pre.prettyprint').hide();
	});
}

// Detect if in an doc iframe:
if( window.parent && window.parent['mw'] && window.parent.mw.getConfig('KalutraDocContext') ){
	window.isKalturaDocsIframe =  true;
} else {
	// if not in an iframe add some padding
	$('head').append(
		$('')
	);
}

// don't set flag if any special properties are set:
if( localStorage.kdoc_player == 'html5' && window['mw'] &&
		mw.getConfig( 'Kaltura.LeadWithHTML5') == null &&
		mw.getConfig( 'disableForceMobileHTML5') == null
){
	mw.setConfig("forceMobileHTML5", true);
}
// clock player render time
var kdocPlayerStartTime = new Date().getTime();
if( typeof kWidget != 'undefined' && kWidget.addReadyCallback ){
	kWidget.addReadyCallback( function( pId ){
		$( '#' + pId )[0].kBind("mediaReady.pTimeReady", function(){
			$( '#' + pId )[0].kUnbind(".pTimeReady");
			$('body').append( '
player ready in:' + ( new Date().getTime() - kdocPlayerStartTime )/1000 + ' seconds
');
			if( parent && parent.sycnIframeContentHeight ){
				parent.sycnIframeContentHeight();
			}
		});
	});
}
// the update player button:
$(document).on('click',  '.kdocUpdatePlayer', function(){
	$('.kdocPlayerRenderTime').empty();
	kdocPlayerStartTime = new Date().getTime();
})

// document ready events:
$(function(){
	// Do any configuration substitutions
	if( localStorage.kdoc_html5url ){
		$('pre.prettyprint').each(function(){
			$(this).html( $(this).html().replace('{{HTML5LibraryURL}}', localStorage.kdoc_html5url) )
		})
	}

	// make active all the pref links:
	$('.adjust-your-preferences').click(function(){
		// invoke the pref menu
		return false;
	})

	// TODO special case test pages that have to do with player selection
	if( localStorage.kdoc_player == 'html5' ){
		$('#playbackModeSelector').append(
			$( 'Leading with HTML5 player, ' ),
			$( 'restore browser default').click(function(){
				localStorage.kdoc_player = 'default';
				location.reload()
			}),
			$( ' ( flash if enabled ) ' )
		)
	} else {
		$('#playbackModeSelector').append(
			$('Lead with HTML5 ').click( function(){
				localStorage.kdoc_player = 'html5';
				location.reload()
				return false;
			}),
			$(' to view the html5 player' )
		)
	};

	// make code pretty
	window.prettyPrint && prettyPrint();

});

<commit_msg>note kUnbind seems to unbind all assoicated event; maybe kdp issue<commit_after>// Shows a top level menu for all test files if ( not running an automated test and not part of doc page )
if( !window.QUnit ){
	var docPath = '../../../docs/';

	// output any blocking scripts that need to be ready before dom ready:
	document.write( '' );
	document.write( '' );
	document.write( '' );

	// inject all the twitter bootstrap css and js ( ok to be injected after page is rendering )
	$('head').append(
		$( '<link rel="shortcut icon" href="' + docPath + 'css/favicon.ico">' ),
		$( '<link href="' + docPath + 'bootstrap/docs/assets/css/bootstrap.css" rel="stylesheet">' ),
		$( '<link href="' + docPath + 'css/kdoc.css" rel="stylesheet">'),
		// pretify:
		$( '' ),
		$( '<link href="' + docPath + 'bootstrap/docs/assets/js/google-code-prettify/prettify.css" rel="stylesheet">' )
	);
} else{
	// provide a stub for prettyKalturaConfig so that tests don't have javascript errors:
	$.fn.prettyKalturaConfig = function( pluginName, flashVars, flashvarCallback ){
		$(this).text( 'running qunit test');
	};
	// hide all prettyconfig:
	$(function(){
		$('pre.prettyprint').hide();
	});
}

// Detect if in an doc iframe:
if( window.parent && window.parent['mw'] && window.parent.mw.getConfig('KalutraDocContext') ){
	window.isKalturaDocsIframe =  true;
} else {
	// if not in an iframe add some padding
	$('head').append(
		$('')
	);
}

// don't set flag if any special properties are set:
if( localStorage.kdoc_player == 'html5' && window['mw'] &&
		mw.getConfig( 'Kaltura.LeadWithHTML5') == null &&
		mw.getConfig( 'disableForceMobileHTML5') == null
){
	mw.setConfig("forceMobileHTML5", true);
}
// clock player render time
var kdocPlayerStartTime = new Date().getTime();
if( typeof kWidget != 'undefined' && kWidget.addReadyCallback ){
	var alreadyRun = false;
	kWidget.addReadyCallback( function( pId ){
		$( '#' + pId )[0].kBind("mediaReady.pTimeReady", function(){
			if( alreadyRun ){
				return ;
			}
			alreadyRun = true;
			// note kUnbind seems to unbind all mediaReady
			//$( '#' + pId )[0].kUnbind(".pTimeReady");
			$('body').append( '
player ready in:' + ( new Date().getTime() - kdocPlayerStartTime )/1000 + ' seconds
');
			if( parent && parent.sycnIframeContentHeight ){
				parent.sycnIframeContentHeight();
			}
		});
	});
}
// the update player button:
$(document).on('click',  '.kdocUpdatePlayer', function(){
	$('.kdocPlayerRenderTime').empty();
	kdocPlayerStartTime = new Date().getTime();
})

// document ready events:
$(function(){
	// Do any configuration substitutions
	if( localStorage.kdoc_html5url ){
		$('pre.prettyprint').each(function(){
			$(this).html( $(this).html().replace('{{HTML5LibraryURL}}', localStorage.kdoc_html5url) )
		})
	}

	// make active all the pref links:
	$('.adjust-your-preferences').click(function(){
		// invoke the pref menu
		return false;
	})

	// TODO special case test pages that have to do with player selection
	if( localStorage.kdoc_player == 'html5' ){
		$('#playbackModeSelector').append(
			$( 'Leading with HTML5 player, ' ),
			$( 'restore browser default').click(function(){
				localStorage.kdoc_player = 'default';
				location.reload()
			}),
			$( ' ( flash if enabled ) ' )
		)
	} else {
		$('#playbackModeSelector').append(
			$('Lead with HTML5 ').click( function(){
				localStorage.kdoc_player = 'html5';
				location.reload()
				return false;
			}),
			$(' to view the html5 player' )
		)
	};

	// make code pretty
	window.prettyPrint && prettyPrint();

});

<|endoftext|>


<commit_before>'use strict';

const fs = require('original-fs');
const co = require('co');
const lo_reject = require('lodash.reject');
const lo_findIndex = require('lodash.findindex');
const path = require('path');

const readdir = require('./readdir');
const util = require('./util');

const RECENT_ITEM_COUNT = 100;
const RECENT_ITEM_RATIO = 1.5;

const matchFunc = (filePath, stats) => {
  const ext = path.extname(filePath).toLowerCase();
  if (stats.isDirectory())
    return true;
  return (ext === '.exe' || ext === '.lnk' || ext === '.appref-ms');
};

function injectEnvVariable(dirPath) {
  let _path = dirPath;
  for (const envVar in process.env) {
    const value = process.env[envVar];
    _path = _path.replace(`\${${envVar}}`, value);
  }
  return _path;
}

function injectEnvVariables(dirArr) {
  const newArr = [];
  for (let i = 0; i < dirArr.length; ++i) {
    const dirPath = dirArr[i];
    newArr.push(injectEnvVariable(dirPath));
  }
  return newArr;
}

module.exports = (context) => {
  const logger = context.logger;
  const shell = context.shell;
  const app = context.app;
  const initialPref = context.preferences.get();
  const localStorage = context.localStorage;
  const toast = context.toast;
  let _recentUsedItems = [];

  const recursiveSearchDirs = injectEnvVariables(initialPref.recursiveFolders || []);
  const flatSearchDirs = injectEnvVariables(initialPref.flatFolders || []);

  const db = {};
  const lazyIndexingKeys = {};

  function* refreshIndex(dirs, recursive) {
    for (const dir of dirs) {
      logger.log(`refreshIndex ${dir}`);
      if (fs.existsSync(dir) === false) {
        logger.log(`can't find a dir: ${dir}`);
        continue;
      }
      const files = yield co(readdir(dir, recursive, matchFunc));
      db[dir] = files;
      logger.log(`index updated ${dir}, ${files.length} files`);
    }
  }

  function lazyRefreshIndex(dir, recursive) {
    const _lazyKey = lazyIndexingKeys[dir];
    if (_lazyKey !== undefined)
      clearTimeout(_lazyKey);

    lazyIndexingKeys[dir] = setTimeout(() => {
      co(refreshIndex([dir], recursive));
    }, 10000);
  }

  function* setupWatchers(dirs, recursive) {
    for (const dir of dirs) {
      const _dir = dir;
      fs.watch(_dir, {
        persistent: true,
        recursive: recursive
      }, (evt, filename) => {
        lazyRefreshIndex(_dir, recursive);
      });
    }
  }

  function addRecentItem(item) {
    const idx = _recentUsedItems.indexOf(item);
    if (idx >= 0)
      _recentUsedItems.splice(idx, 1);

    if (fs.existsSync(item))
      _recentUsedItems.unshift(item);

    _recentUsedItems = _recentUsedItems.slice(0, RECENT_ITEM_COUNT);
    localStorage.setItem('recentUsedItems', _recentUsedItems);
  }

  function updateRecentItems() {
    const aliveItems = [];
    for (const item of _recentUsedItems) {
      if (fs.existsSync(item))
        aliveItems.push(item);
    }
    _recentUsedItems = aliveItems;
  }

  function startup() {
    _recentUsedItems = localStorage.getItemSync('recentUsedItems') || [];
    updateRecentItems();

    co(function* () {
      yield* refreshIndex(recursiveSearchDirs, true);
      yield* refreshIndex(flatSearchDirs, false);
      yield* setupWatchers(recursiveSearchDirs, true);
      yield* setupWatchers(flatSearchDirs, false);
    }).catch((err) => {
      logger.log(err);
      logger.log(err.stack);
    });
  }

  function _fuzzyResultToSearchResult(results, ratio = 1) {
    return results.map(x => {
      const path_base64 = new Buffer(x.path).toString('base64');
      const score = x.score * ratio;
      return {
        id: x.path,
        title: path.basename(x.path, path.extname(x.path)),
        desc: x.html,
        icon: `icon://${path_base64}`,
        group: 'Files & Folders',
        score
      };
    });
  }

  function search(query, res) {
    const query_trim = query.replace(' ', '');
    const recentFuzzyResults = util.fuzzy(_recentUsedItems, query_trim).slice(0, 2);
    const defaultFuzzyResults = util.fuzzy(db, query_trim).slice(0, 10);

    let recentSearchResults = [];
    if (recentFuzzyResults.length > 0)
      recentSearchResults = _fuzzyResultToSearchResult(recentFuzzyResults, RECENT_ITEM_RATIO);

    // Reject if it is duplicated with recent items
    const sanitizedFuzzyResults = lo_reject(defaultFuzzyResults, x => lo_findIndex(recentFuzzyResults, { path: x.path }) >= 0);
    const fileSearchResults = _fuzzyResultToSearchResult(sanitizedFuzzyResults);
    const searchResults = recentSearchResults.concat(fileSearchResults);
    res.add(searchResults);
  }

  function execute(id, payload) {
    // Update recent item, and it will be deleted if file don't exists
    addRecentItem(id);

    if (fs.existsSync(id) === false) {
      toast.enqueue('Sorry, Could\'nt Find a File');
      return;
    }

    shell.openItem(id);
    app.close();
  }

  return { startup, search, execute };
};
<commit_msg>hain-plugin-filesearch: Update scoring algorithm<commit_after>'use strict';

const fs = require('original-fs');
const co = require('co');
const lo_reject = require('lodash.reject');
const lo_findIndex = require('lodash.findindex');
const path = require('path');

const readdir = require('./readdir');
const util = require('./util');

const RECENT_ITEM_COUNT = 50;
const RECENT_ITEM_RATIO_HIGH = 3;
const RECENT_ITEM_RATIO_LOW = 1.5;

const matchFunc = (filePath, stats) => {
  const ext = path.extname(filePath).toLowerCase();
  if (stats.isDirectory())
    return true;
  return (ext === '.exe' || ext === '.lnk' || ext === '.appref-ms');
};

function injectEnvVariable(dirPath) {
  let _path = dirPath;
  for (const envVar in process.env) {
    const value = process.env[envVar];
    _path = _path.replace(`\${${envVar}}`, value);
  }
  return _path;
}

function injectEnvVariables(dirArr) {
  const newArr = [];
  for (let i = 0; i < dirArr.length; ++i) {
    const dirPath = dirArr[i];
    newArr.push(injectEnvVariable(dirPath));
  }
  return newArr;
}

module.exports = (context) => {
  const logger = context.logger;
  const shell = context.shell;
  const app = context.app;
  const initialPref = context.preferences.get();
  const localStorage = context.localStorage;
  const toast = context.toast;
  let _recentUsedItems = [];

  const recursiveSearchDirs = injectEnvVariables(initialPref.recursiveFolders || []);
  const flatSearchDirs = injectEnvVariables(initialPref.flatFolders || []);

  const db = {};
  const lazyIndexingKeys = {};

  function* refreshIndex(dirs, recursive) {
    for (const dir of dirs) {
      logger.log(`refreshIndex ${dir}`);
      if (fs.existsSync(dir) === false) {
        logger.log(`can't find a dir: ${dir}`);
        continue;
      }
      const files = yield co(readdir(dir, recursive, matchFunc));
      db[dir] = files;
      logger.log(`index updated ${dir}, ${files.length} files`);
    }
  }

  function lazyRefreshIndex(dir, recursive) {
    const _lazyKey = lazyIndexingKeys[dir];
    if (_lazyKey !== undefined)
      clearTimeout(_lazyKey);

    lazyIndexingKeys[dir] = setTimeout(() => {
      co(refreshIndex([dir], recursive));
    }, 10000);
  }

  function* setupWatchers(dirs, recursive) {
    for (const dir of dirs) {
      const _dir = dir;
      fs.watch(_dir, {
        persistent: true,
        recursive: recursive
      }, (evt, filename) => {
        lazyRefreshIndex(_dir, recursive);
      });
    }
  }

  function addRecentItem(item) {
    const idx = _recentUsedItems.indexOf(item);
    if (idx >= 0)
      _recentUsedItems.splice(idx, 1);

    if (fs.existsSync(item))
      _recentUsedItems.unshift(item);

    _recentUsedItems = _recentUsedItems.slice(0, RECENT_ITEM_COUNT);
    localStorage.setItem('recentUsedItems', _recentUsedItems);
  }

  function updateRecentItems() {
    const aliveItems = [];
    for (const item of _recentUsedItems) {
      if (fs.existsSync(item))
        aliveItems.push(item);
    }
    _recentUsedItems = aliveItems;
  }

  function startup() {
    _recentUsedItems = localStorage.getItemSync('recentUsedItems') || [];
    updateRecentItems();

    co(function* () {
      yield* refreshIndex(recursiveSearchDirs, true);
      yield* refreshIndex(flatSearchDirs, false);
      yield* setupWatchers(recursiveSearchDirs, true);
      yield* setupWatchers(flatSearchDirs, false);
    }).catch((err) => {
      logger.log(err);
      logger.log(err.stack);
    });
  }

  function _fuzzyResultToSearchResult(results) {
    return results.map(x => {
      const path_base64 = new Buffer(x.path).toString('base64');
      return {
        id: x.path,
        title: path.basename(x.path, path.extname(x.path)),
        desc: x.html,
        icon: `icon://${path_base64}`,
        group: 'Files & Folders',
        score: x.score
      };
    });
  }

  function search(query, res) {
    const query_trim = query.replace(' ', '');
    const recentFuzzyResults = util.fuzzy(_recentUsedItems, query_trim);
    const defaultFuzzyResults = util.fuzzy(db, query_trim);

    let recentSearchResults = [];
    if (recentFuzzyResults.length > 0) {
      // Update score by usage frequency
      const RATIO_DELTA = (RECENT_ITEM_RATIO_HIGH - RECENT_ITEM_RATIO_LOW);
      const scoredRecentFuzzyResults = recentFuzzyResults.map((x) => {
        const nearIdx = _recentUsedItems.indexOf(x.path);
        const ratio = ((RECENT_ITEM_COUNT - nearIdx) / RECENT_ITEM_COUNT) * RATIO_DELTA + RECENT_ITEM_RATIO_LOW
        x.score = x.score * ratio;
        return x;
      });
      recentSearchResults = _fuzzyResultToSearchResult(scoredRecentFuzzyResults);
    }

    // Reject if it is duplicated with recent items
    const sanitizedFuzzyResults = lo_reject(defaultFuzzyResults, x => lo_findIndex(recentFuzzyResults, { path: x.path }) >= 0);
    const fileSearchResults = _fuzzyResultToSearchResult(sanitizedFuzzyResults);
    const searchResults = recentSearchResults.concat(fileSearchResults).slice(0, 15);
    res.add(searchResults);
  }

  function execute(id, payload) {
    // Update recent item, and it will be deleted if file don't exists
    addRecentItem(id);

    if (fs.existsSync(id) === false) {
      toast.enqueue('Sorry, Could\'nt Find a File');
      return;
    }

    shell.openItem(id);
    app.close();
  }

  return { startup, search, execute };
};
<|endoftext|>