<commit_before>/* @flow weak */

import async from 'async'
import Promise from 'bluebird'
import EventEmitter from 'events'
import clone from 'lodash.clone'
import fs from 'fs-extra'
import path from 'path'
let log = require('printit')({
  prefix: 'Local writer  ',
  date: true
})

import { extractRevNumber } from '../metadata'
import Pouch from '../pouch'
import Prep from '../prep'
import Watcher from './watcher'

import type { FileStreamProvider } from '../file_stream_provider'

// Local is the class that interfaces cozy-desktop with the local filesystem.
// It uses a watcher, based on chokidar, to listen for file and folder changes.
// It also applied changes from the remote cozy on the local filesystem.
class Local {
  prep: Prep
  pouch: Pouch
  events: EventEmitter
  syncPath: string
  tmpPath: string
  watcher: Watcher
  other: FileStreamProvider

  constructor (config, prep, pouch, events) {
    this.prep = prep
    this.pouch = pouch
    this.events = events
    this.syncPath = config.syncPath
    this.tmpPath = path.join(this.syncPath, '.cozy-desktop')
    this.watcher = new Watcher(this.syncPath, this.prep, this.pouch)
    // $FlowFixMe
    this.other = null

    Promise.promisifyAll(this)
  }

  // Start initial replication + watching changes in live
  start (done) {
    return fs.ensureDir(this.syncPath, () => {
      return this.watcher.start(done)
    })
  }

  // Stop watching the file system
  stop (callback) {
    return this.watcher.stop(callback)
  }

  // Create a readable stream for the given doc
  createReadStream (doc, callback) {
    try {
      let filePath = path.resolve(this.syncPath, doc.path)
      let stream = fs.createReadStream(filePath)
      return callback(null, stream)
    } catch (err) {
      log.error(err)
      return callback(new Error('Cannot read the file'))
    }
  }

  /* Helpers */

  // Return a function that will update last modification date
  // and does a chmod +x if the file is executable
  //
  // Note: UNIX has 3 timestamps for a file/folder:
  // - atime for last access
  // - ctime for change (metadata or content)
  // - utime for update (content only)
  // This function updates utime and ctime according to the last
  // modification date.
  metadataUpdater (doc) {
    let filePath = path.resolve(this.syncPath, doc.path)
    return function (callback) {
      let next = function (err) {
        if (doc.executable) {
          return fs.chmod(filePath, '755', callback)
        } else {
          return callback(err)
        }
      }
      if (doc.lastModification) {
        let lastModification = new Date(doc.lastModification)
        return fs.utimes(filePath, lastModification, lastModification, () =>
          // Ignore errors
          next()
        )
      } else {
        return next()
      }
    }
  }

  // Return true if the local file is up-to-date for this document
  isUpToDate (doc) {
    let currentRev = doc.sides.local || 0
    let lastRev = extractRevNumber(doc)
    return currentRev === lastRev
  }

  // Check if a file corresponding to given checksum already exists
  fileExistsLocally (checksum, callback) {
    return this.pouch.byChecksum(checksum, (err, docs) => {
      if (err) {
        return callback(err)
      } else if ((docs == null) || (docs.length === 0)) {
        return callback(null, false)
      } else {
        let paths = Array.from(docs).filter((doc) => this.isUpToDate(doc)).map((doc) =>
                    path.resolve(this.syncPath, doc.path))
        return async.detect(paths, (filePath, next) =>
                    fs.exists(filePath, found => next(null, found))

        , callback)
      }
    })
  }

  /* Write operations */

  // Add a new file, or replace an existing one
  //
  // Steps to create a file:
  //   * Try to find a similar file based on his checksum
  //     (in that case, it just requires a local copy)
  //   * Or download the linked binary from remote
  //   * Write to a temporary file
  //   * Ensure parent folder exists
  //   * Move the temporay file to its final destination
  //   * Update creation and last modification dates
  //
  // Note: if no checksum was available for this file, we download the file
  // from the remote document. Later, chokidar will fire an event for this new
  // file. The checksum will then be computed and added to the document, and
  // then pushed to CouchDB.
  addFile (doc, callback) {
    let tmpFile = path.resolve(this.tmpPath, `${path.basename(doc.path)}.tmp`)
    let filePath = path.resolve(this.syncPath, doc.path)
    let parent = path.resolve(this.syncPath, path.dirname(doc.path))

    log.info(`Put file ${filePath}`)

    return async.waterfall([
      next => {
        if (doc.checksum != null) {
          return this.fileExistsLocally(doc.checksum, next)
        } else {
          return next(null, false)
        }
      },

      (existingFilePath, next) => {
        return fs.ensureDir(this.tmpPath, () => {
          if (existingFilePath) {
            log.info(`Recopy ${existingFilePath} -> ${filePath}`)
            this.events.emit('transfer-copy', doc)
            return fs.copy(existingFilePath, tmpFile, next)
          } else {
            return this.other.createReadStream(doc, (err, stream) => {
              // Don't use async callback here!
              // Async does some magic and the stream can throw an
              // 'error' event before the next async is called...
              if (err) { return next(err) }
              let target = fs.createWriteStream(tmpFile)
              stream.pipe(target)
              target.on('finish', next)
              target.on('error', next)
              // Emit events to track the download progress
              let info = clone(doc)
              info.way = 'down'
              info.eventName = `transfer-down-${doc._id}`
              this.events.emit('transfer-started', info)
              stream.on('data', data => {
                return this.events.emit(info.eventName, data)
              }
                            )
              return target.on('finish', () => {
                return this.events.emit(info.eventName, {finished: true})
              })
            })
          }
        })
      },

      next => {
        if (doc.checksum != null) {
          return this.watcher.checksum(tmpFile, function (err, checksum) {
            if (err) {
              return next(err)
            } else if (checksum === doc.checksum) {
              return next()
            } else {
              return next(new Error('Invalid checksum'))
            }
          })
        } else {
          return next()
        }
      },

      next => fs.ensureDir(parent, () => fs.rename(tmpFile, filePath, next)),

      this.metadataUpdater(doc)

    ], function (err) {
      if (err) { log.warn('addFile failed:', err, doc) }
      return fs.unlink(tmpFile, () => callback(err))
    })
  }

  // Create a new folder
  addFolder (doc, callback) {
    let folderPath = path.join(this.syncPath, doc.path)
    log.info(`Put folder ${folderPath}`)
    return fs.ensureDir(folderPath, err => {
      if (err) {
        return callback(err)
      } else {
        return this.metadataUpdater(doc)(callback)
      }
    })
  }

  // Overwrite a file
  overwriteFile (doc, old, callback) {
    return this.addFile(doc, callback)
  }

  // Update the metadata of a file
  updateFileMetadata (doc, old, callback) {
    return this.metadataUpdater(doc)(callback)
  }

  // Update a folder
  updateFolder (doc, old, callback) {
    return this.addFolder(doc, callback)
  }

  // Move a file from one place to another
  moveFile (doc, old, callback) {
    log.info(`Move file ${old.path} → ${doc.path}`)
    let oldPath = path.join(this.syncPath, old.path)
    let newPath = path.join(this.syncPath, doc.path)
    let parent = path.join(this.syncPath, path.dirname(doc.path))

    return async.waterfall([
      next => fs.exists(oldPath, function (oldPathExists) {
        if (oldPathExists) {
          return fs.ensureDir(parent, () => fs.rename(oldPath, newPath, next))
        } else {
          return fs.exists(newPath, function (newPathExists) {
            if (newPathExists) {
              return next()
            } else {
              log.error(`File ${oldPath} not found`)
              return next(new Error(`${oldPath} not found`))
            }
          })
        }
      }),

      this.metadataUpdater(doc)

    ], err => {
      if (err) {
        log.error(`Error while moving ${JSON.stringify(doc, null, 2)}`)
        log.error(JSON.stringify(old, null, 2))
        log.error(err)
        return this.addFile(doc, callback)
      } else {
        this.events.emit('transfer-move', doc, old)
        return callback(null)
      }
    })
  }

  // Move a folder
  moveFolder (doc, old, callback) {
    log.info(`Move folder ${old.path} → ${doc.path}`)
    let oldPath = path.join(this.syncPath, old.path)
    let newPath = path.join(this.syncPath, doc.path)
    let parent = path.join(this.syncPath, path.dirname(doc.path))

    return async.waterfall([
      next => fs.exists(oldPath, oldPathExists =>
        fs.exists(newPath, function (newPathExists) {
          if (oldPathExists && newPathExists) {
            return fs.rmdir(oldPath, next)
          } else if (oldPathExists) {
            return fs.ensureDir(parent, () => fs.rename(oldPath, newPath, next))
          } else if (newPathExists) {
            return next()
          } else {
            log.error(`Folder ${oldPath} not found`)
            return next(new Error(`${oldPath} not found`))
          }
        })
      ),

      this.metadataUpdater(doc)

    ], err => {
      if (err) {
        log.error(`Error while moving ${JSON.stringify(doc, null, 2)}`)
        log.error(JSON.stringify(old, null, 2))
        log.error(err)
        return this.addFolder(doc, callback)
      } else {
        return callback(null)
      }
    })
  }

  // Delete a file from the local filesystem
  deleteFile (doc, callback) {
    log.info(`Delete ${doc.path}`)
    this.events.emit('delete-file', doc)
    let fullpath = path.join(this.syncPath, doc.path)
    return fs.remove(fullpath, callback)
  }

  // Delete a folder from the local filesystem
  deleteFolder (doc, callback) {
        // For now both operations are similar
    return this.deleteFile(doc, callback)
  }

  // Rename a file/folder to resolve a conflict
  resolveConflict (dst, src, callback) {
    log.info(`Resolve a conflict: ${src.path} → ${dst.path}`)
    let srcPath = path.join(this.syncPath, src.path)
    let dstPath = path.join(this.syncPath, dst.path)
    fs.rename(srcPath, dstPath, callback)
    // Don't fire an event for the deleted file
    return setTimeout(() => {
      return __guard__(this.watcher.pending[src.path], x => x.clear())
    }, 1000)
  }
}

export default Local

function __guard__ (value, transform) {
  return (typeof value !== 'undefined' && value !== null) ? transform(value) : undefined
}
<commit_msg>local: Make sure Local implements Side<commit_after>/* @flow weak */

import async from 'async'
import Promise from 'bluebird'
import EventEmitter from 'events'
import clone from 'lodash.clone'
import fs from 'fs-extra'
import path from 'path'
let log = require('printit')({
  prefix: 'Local writer  ',
  date: true
})

import { extractRevNumber } from '../metadata'
import Pouch from '../pouch'
import Prep from '../prep'
import Watcher from './watcher'

import type { FileStreamProvider } from '../file_stream_provider'
import type { Side } from '../side' // eslint-disable-line

// Local is the class that interfaces cozy-desktop with the local filesystem.
// It uses a watcher, based on chokidar, to listen for file and folder changes.
// It also applied changes from the remote cozy on the local filesystem.
class Local implements Side {
  prep: Prep
  pouch: Pouch
  events: EventEmitter
  syncPath: string
  tmpPath: string
  watcher: Watcher
  other: FileStreamProvider

  constructor (config, prep, pouch, events) {
    this.prep = prep
    this.pouch = pouch
    this.events = events
    this.syncPath = config.syncPath
    this.tmpPath = path.join(this.syncPath, '.cozy-desktop')
    this.watcher = new Watcher(this.syncPath, this.prep, this.pouch)
    // $FlowFixMe
    this.other = null

    Promise.promisifyAll(this)
  }

  // Start initial replication + watching changes in live
  start (done) {
    return fs.ensureDir(this.syncPath, () => {
      return this.watcher.start(done)
    })
  }

  // Stop watching the file system
  stop (callback) {
    return this.watcher.stop(callback)
  }

  // Create a readable stream for the given doc
  createReadStream (doc, callback) {
    try {
      let filePath = path.resolve(this.syncPath, doc.path)
      let stream = fs.createReadStream(filePath)
      return callback(null, stream)
    } catch (err) {
      log.error(err)
      return callback(new Error('Cannot read the file'))
    }
  }

  /* Helpers */

  // Return a function that will update last modification date
  // and does a chmod +x if the file is executable
  //
  // Note: UNIX has 3 timestamps for a file/folder:
  // - atime for last access
  // - ctime for change (metadata or content)
  // - utime for update (content only)
  // This function updates utime and ctime according to the last
  // modification date.
  metadataUpdater (doc) {
    let filePath = path.resolve(this.syncPath, doc.path)
    return function (callback) {
      let next = function (err) {
        if (doc.executable) {
          return fs.chmod(filePath, '755', callback)
        } else {
          return callback(err)
        }
      }
      if (doc.lastModification) {
        let lastModification = new Date(doc.lastModification)
        return fs.utimes(filePath, lastModification, lastModification, () =>
          // Ignore errors
          next()
        )
      } else {
        return next()
      }
    }
  }

  // Return true if the local file is up-to-date for this document
  isUpToDate (doc) {
    let currentRev = doc.sides.local || 0
    let lastRev = extractRevNumber(doc)
    return currentRev === lastRev
  }

  // Check if a file corresponding to given checksum already exists
  fileExistsLocally (checksum, callback) {
    return this.pouch.byChecksum(checksum, (err, docs) => {
      if (err) {
        return callback(err)
      } else if ((docs == null) || (docs.length === 0)) {
        return callback(null, false)
      } else {
        let paths = Array.from(docs).filter((doc) => this.isUpToDate(doc)).map((doc) =>
                    path.resolve(this.syncPath, doc.path))
        return async.detect(paths, (filePath, next) =>
                    fs.exists(filePath, found => next(null, found))

        , callback)
      }
    })
  }

  /* Write operations */

  // Add a new file, or replace an existing one
  //
  // Steps to create a file:
  //   * Try to find a similar file based on his checksum
  //     (in that case, it just requires a local copy)
  //   * Or download the linked binary from remote
  //   * Write to a temporary file
  //   * Ensure parent folder exists
  //   * Move the temporay file to its final destination
  //   * Update creation and last modification dates
  //
  // Note: if no checksum was available for this file, we download the file
  // from the remote document. Later, chokidar will fire an event for this new
  // file. The checksum will then be computed and added to the document, and
  // then pushed to CouchDB.
  addFile (doc, callback) {
    let tmpFile = path.resolve(this.tmpPath, `${path.basename(doc.path)}.tmp`)
    let filePath = path.resolve(this.syncPath, doc.path)
    let parent = path.resolve(this.syncPath, path.dirname(doc.path))

    log.info(`Put file ${filePath}`)

    return async.waterfall([
      next => {
        if (doc.checksum != null) {
          return this.fileExistsLocally(doc.checksum, next)
        } else {
          return next(null, false)
        }
      },

      (existingFilePath, next) => {
        return fs.ensureDir(this.tmpPath, () => {
          if (existingFilePath) {
            log.info(`Recopy ${existingFilePath} -> ${filePath}`)
            this.events.emit('transfer-copy', doc)
            return fs.copy(existingFilePath, tmpFile, next)
          } else {
            return this.other.createReadStream(doc, (err, stream) => {
              // Don't use async callback here!
              // Async does some magic and the stream can throw an
              // 'error' event before the next async is called...
              if (err) { return next(err) }
              let target = fs.createWriteStream(tmpFile)
              stream.pipe(target)
              target.on('finish', next)
              target.on('error', next)
              // Emit events to track the download progress
              let info = clone(doc)
              info.way = 'down'
              info.eventName = `transfer-down-${doc._id}`
              this.events.emit('transfer-started', info)
              stream.on('data', data => {
                return this.events.emit(info.eventName, data)
              }
                            )
              return target.on('finish', () => {
                return this.events.emit(info.eventName, {finished: true})
              })
            })
          }
        })
      },

      next => {
        if (doc.checksum != null) {
          return this.watcher.checksum(tmpFile, function (err, checksum) {
            if (err) {
              return next(err)
            } else if (checksum === doc.checksum) {
              return next()
            } else {
              return next(new Error('Invalid checksum'))
            }
          })
        } else {
          return next()
        }
      },

      next => fs.ensureDir(parent, () => fs.rename(tmpFile, filePath, next)),

      this.metadataUpdater(doc)

    ], function (err) {
      if (err) { log.warn('addFile failed:', err, doc) }
      return fs.unlink(tmpFile, () => callback(err))
    })
  }

  // Create a new folder
  addFolder (doc, callback) {
    let folderPath = path.join(this.syncPath, doc.path)
    log.info(`Put folder ${folderPath}`)
    return fs.ensureDir(folderPath, err => {
      if (err) {
        return callback(err)
      } else {
        return this.metadataUpdater(doc)(callback)
      }
    })
  }

  // Overwrite a file
  overwriteFile (doc, old, callback) {
    return this.addFile(doc, callback)
  }

  // Update the metadata of a file
  updateFileMetadata (doc, old, callback) {
    return this.metadataUpdater(doc)(callback)
  }

  // Update a folder
  updateFolder (doc, old, callback) {
    return this.addFolder(doc, callback)
  }

  // Move a file from one place to another
  moveFile (doc, old, callback) {
    log.info(`Move file ${old.path} → ${doc.path}`)
    let oldPath = path.join(this.syncPath, old.path)
    let newPath = path.join(this.syncPath, doc.path)
    let parent = path.join(this.syncPath, path.dirname(doc.path))

    return async.waterfall([
      next => fs.exists(oldPath, function (oldPathExists) {
        if (oldPathExists) {
          return fs.ensureDir(parent, () => fs.rename(oldPath, newPath, next))
        } else {
          return fs.exists(newPath, function (newPathExists) {
            if (newPathExists) {
              return next()
            } else {
              log.error(`File ${oldPath} not found`)
              return next(new Error(`${oldPath} not found`))
            }
          })
        }
      }),

      this.metadataUpdater(doc)

    ], err => {
      if (err) {
        log.error(`Error while moving ${JSON.stringify(doc, null, 2)}`)
        log.error(JSON.stringify(old, null, 2))
        log.error(err)
        return this.addFile(doc, callback)
      } else {
        this.events.emit('transfer-move', doc, old)
        return callback(null)
      }
    })
  }

  // Move a folder
  moveFolder (doc, old, callback) {
    log.info(`Move folder ${old.path} → ${doc.path}`)
    let oldPath = path.join(this.syncPath, old.path)
    let newPath = path.join(this.syncPath, doc.path)
    let parent = path.join(this.syncPath, path.dirname(doc.path))

    return async.waterfall([
      next => fs.exists(oldPath, oldPathExists =>
        fs.exists(newPath, function (newPathExists) {
          if (oldPathExists && newPathExists) {
            return fs.rmdir(oldPath, next)
          } else if (oldPathExists) {
            return fs.ensureDir(parent, () => fs.rename(oldPath, newPath, next))
          } else if (newPathExists) {
            return next()
          } else {
            log.error(`Folder ${oldPath} not found`)
            return next(new Error(`${oldPath} not found`))
          }
        })
      ),

      this.metadataUpdater(doc)

    ], err => {
      if (err) {
        log.error(`Error while moving ${JSON.stringify(doc, null, 2)}`)
        log.error(JSON.stringify(old, null, 2))
        log.error(err)
        return this.addFolder(doc, callback)
      } else {
        return callback(null)
      }
    })
  }

  // Delete a file from the local filesystem
  deleteFile (doc, callback) {
    log.info(`Delete ${doc.path}`)
    this.events.emit('delete-file', doc)
    let fullpath = path.join(this.syncPath, doc.path)
    return fs.remove(fullpath, callback)
  }

  // Delete a folder from the local filesystem
  deleteFolder (doc, callback) {
        // For now both operations are similar
    return this.deleteFile(doc, callback)
  }

  // Rename a file/folder to resolve a conflict
  resolveConflict (dst, src, callback) {
    log.info(`Resolve a conflict: ${src.path} → ${dst.path}`)
    let srcPath = path.join(this.syncPath, src.path)
    let dstPath = path.join(this.syncPath, dst.path)
    fs.rename(srcPath, dstPath, callback)
    // Don't fire an event for the deleted file
    return setTimeout(() => {
      return __guard__(this.watcher.pending[src.path], x => x.clear())
    }, 1000)
  }
}

export default Local

function __guard__ (value, transform) {
  return (typeof value !== 'undefined' && value !== null) ? transform(value) : undefined
}
<|endoftext|>


<commit_before>import React from 'react';
import {
  Text,
  View,
  StyleSheet,
  // Switch,
  // Slider,
  TouchableOpacity,
  // Dimensions,
  Platform,
} from 'react-native';
// import Button from 'react-native-button';

import {
  Player,
  Recorder,
  // MediaStates,
} from 'react-native-audio-toolkit';
import moment from 'moment';

import { Icon } from 'react-native-elements';
// import { CircularProgress } from 'react-native-circular-progress';
import { Actions } from 'react-native-router-flux';
import { AppColors } from '../../theme';
// import AudioUtil from './AudioUtil';

const filename = 'mongrov-voice-test.mp4';
const sendIconColor = AppColors.brand().aA_sendIconColor;
const cancelColor = AppColors.brand().aA_cancelColor;

const styles = StyleSheet.create({
  container: {
    position: 'absolute',
    left: 0,
    right: 0,
    bottom: 0,
    borderRadius: 5,
    padding: 5,
  },
  subcontainer: {
    padding: 3,
    flexDirection: 'row',
  },
  recordView: {
    flexDirection: 'column',
    alignItems: 'center',
    justifyContent: 'center',
    margin: 5,
    width: 80,
    marginRight: 20,
  },
  timerView: {
    alignItems: 'center',
    justifyContent: 'center',
    margin: 5,
    width: 70,
    marginRight: 20,
  },
  textColor: {
    color: AppColors.brand().aA_textColor,
  },
  timerText: {
    color: AppColors.brand().aA_textColor,
    paddingBottom: 20,
    fontSize: 20,
  },
  sendView: {
    flexDirection: 'column',
    alignItems: 'center',
    justifyContent: 'center',
    margin: 5,
    width: 70,
    marginRight: 10,
  },
  cancelView: {
    flexDirection: 'column',
    alignItems: 'center',
    justifyContent: 'center',
    margin: 5,
    width: 70,
  },
});

// var styles = StyleSheet.create({
//   button: {
//     padding: 20,
//     fontSize: 20,
//     backgroundColor: 'white',
//   },
//   slider: {
//     height: 10,
//     margin: 10,
//   },
//   buttonContainer: {
//     flex: 1,
//     flexDirection: 'row',
//     justifyContent: 'space-between',
//     alignItems: 'center',
//   },
//   settingsContainer: {
//     flex: 1,
//     justifyContent: 'space-between',
//     alignItems: 'center',
//   },
//   container: {
//     borderRadius: 4,
//     borderWidth: 0.5,
//     borderColor: '#d6d7da',
//   },
//   title: {
//     fontSize: 19,
//     fontWeight: 'bold',
//     textAlign: 'center',
//     padding: 20,
//   },
//   errorMessage: {
//     fontSize: 15,
//     textAlign: 'center',
//     padding: 10,
//     color: 'red',
//   },
// });

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

    this.state = {
      playPauseButton: 'Preparing...',
      recordButton: 'Preparing...',

      stopButtonDisabled: true,
      playButtonDisabled: true,
      recordButtonDisabled: true,

      loopButtonStatus: false,
      progress: 0,

      error: null,
      recordTime: 0,

      recorderView: true,
      filePath: '',
      cancelled: false,
    };
  }

  componentWillMount() {
    this.player = null;
    this.recorder = null;
    this.lastSeek = 0;

    this._reloadPlayer();
    this._reloadRecorder();

    this._progressInterval = setInterval(() => {
      if (this.player && this._shouldUpdateProgressBar()) { // && !this._dragging) {
        this.setState({ progress: Math.max(0, this.player.currentTime) / this.player.duration });
      }
    }, 100);
  }

  componentDidMount() {
    this._toggleRecord();
  }

  componentWillUnmount() {
    // console.log('unmount');
    // TODO
    clearInterval(this._progressInterval);
    clearInterval(this._recordInterval);
  }

  _shouldUpdateProgressBar() {
    // Debounce progress bar update by 200 ms
    return Date.now() - this.lastSeek > 200;
  }

  _updateState(/* err */) {
    this.setState({
      playPauseButton: this.player && this.player.isPlaying ? 'Pause' : 'Play',
      playerButton: this.player && this.player.isPlaying ? 'stop' : 'play',
      recordButton: this.recorder && this.recorder.isRecording ? 'Stop' : 'Record',
      // recordIcon: this.recorder && this.recorder.isRecording ? 'microphone-off' : 'microphone',

      stopButtonDisabled: !this.player || !this.player.canStop,
      playButtonDisabled: !this.player || !this.player.canPlay || this.recorder.isRecording,
      recordButtonDisabled: !this.recorder || (this.player && !this.player.isStopped),
    });
    if (this.recorder && this.recorder.isRecording) {
      console.log('KRec 2', this.recorder);
      this.setState({ filePath: this.recorder._fsPath });
      this._recordInterval = setInterval(() => {
        if (this.state.recordTime < 300) {      // Timeout 5 Min  to send
          console.log('time', this.state.recordTime);
          this.setState({ recordTime: this.state.recordTime + 1 });
          console.log('running-time', this.state.recordTime);
        } else {
          clearInterval(this._recordInterval);
          this._toggleRecord();
        }
      }, 1000);
    }
  }

  _playPause() {
    this.player.playPause((err/* , playing */) => {
      if (err) {
        this.setState({
          error: err.message,
        });
      }
      this._updateState();
    });
  }

  _stop() {
    this.player.stop(() => {
      this._updateState();
    });
  }

  _seek(percentage) {
    if (!this.player) {
      return;
    }

    this.lastSeek = Date.now();

    const position = percentage * this.player.duration;

    this.player.seek(position, () => {
      this._updateState();
    });
  }

  _reloadPlayer() {
    if (this.player) {
      this.player.destroy();
    }

    this.player = new Player(filename, {
      autoDestroy: false,
    }).prepare((err) => {
      if (err) {
        console.log('error at _reloadPlayer():');
        console.log(err);
      } else {
        this.player.looping = this.state.loopButtonStatus;
      }

      this._updateState();
    });

    this.player.volume = 1;

    this._updateState();

    this.player.on('ended', () => {
      this._updateState();
    });
    this.player.on('pause', () => {
      this._updateState();
    });
  }

  _reloadRecorder() {
    if (this.recorder) {
      this.recorder.destroy();
    }

    this.recorder = new Recorder(`${moment().unix().toString()}.mp4`, {
      bitrate: 256000,
      channels: 2,
      sampleRate: 44100,
      quality: 'max',
      // format: 'ac3', // autodetected
      // encoder: 'aac', // autodetected
    });

    console.log('KRec', this.recorder);

    this._updateState();
  }

  _toggleRecord() {
    console.log('record');
    if (this.player) {
      this.player.destroy();
    }

    this.recorder.toggleRecord((err, stopped) => {
      if (err) {
        this.setState({
          error: err.message,
        });
      }
      if (stopped && !this.state.cancelled) {
        clearInterval(this._recordInterval);
        Actions.refresh({
          attachAudio: false,
          attach: {
            cameraData: { path: Platform.OS === 'ios' ? this.state.filePath : `file://${this.state.filePath}` }, // data,
            cameraMessage: 'Audio message',
            video: false,
          },
        });
        // this.setState({ recordTime: 0 });
        // this._reloadPlayer();
        // this._reloadRecorder();
        // this.setState({ recorderView: false });
      } else if (stopped && this.state.cancelled) {
        clearInterval(this._recordInterval);
        Actions.refresh({
          attachAudio: false,
        });
      } else if (!stopped) {
        this._updateState();
      }
    });
  }

  _toggleLooping(value) {
    this.setState({
      loopButtonStatus: value,
    });
    if (this.player) {
      this.player.looping = value;
    }
  }

  render() {
    const recT = this.state.recordTime;
    const recM = (recT - (recT % 60)) / 60;
    const recS = (recT % 60);
    return (
      <View
        style={[styles.container, { backgroundColor: AppColors.brand().fourth }]}
      >
        <View
          style={styles.subcontainer}
        >
          <View
            style={styles.recordView}
          >
            <Icon
              raised
              name="record"
              type="material-community"
              color={'#ff5608'}
              reverse
            />
            <Text
              numberOfLines={1}
              ellipsizeMode={'tail'}
              style={styles.textColor}
            >Recording...</Text>
          </View>
          <View
            style={styles.timerView}
          >
            <Text
              numberOfLines={1}
              ellipsizeMode={'tail'}
              style={[styles.timerText]}
            >{`${recM < 10 ? '0' : ''}${recM}:${recS < 10 ? '0' : ''}${recS}`}</Text>
          </View>
          <TouchableOpacity
            style={styles.sendView}
            onPress={() => {
              this._toggleRecord();
            }}
          >
            <Icon
              raised
              name="send"
              color={sendIconColor}
              reverse
            />
            <Text
              numberOfLines={1}
              ellipsizeMode={'tail'}
              style={styles.textColor}
            >Send</Text>
          </TouchableOpacity>
          <TouchableOpacity
            style={styles.cancelView}
            onPress={() => {
              this.setState({ cancelled: true });
              this._toggleRecord();
            }}
          >
            <Icon
              raised
              name="delete"
              color={cancelColor}
              reverse
            />
            <Text
              numberOfLines={1}
              ellipsizeMode={'tail'}
              style={styles.textColor}
            >Cancel</Text>
          </TouchableOpacity>
          <View style={{ flex: 2 }} />
        </View>
      </View>
    );
  }
}
<commit_msg>audio recorder view corrected<commit_after>import React from 'react';
import {
  Text,
  View,
  StyleSheet,
  // Switch,
  // Slider,
  TouchableOpacity,
  // Dimensions,
  Platform,
} from 'react-native';
// import Button from 'react-native-button';

import {
  Player,
  Recorder,
  // MediaStates,
} from 'react-native-audio-toolkit';
import moment from 'moment';

import { Icon } from 'react-native-elements';
// import { CircularProgress } from 'react-native-circular-progress';
import { Actions } from 'react-native-router-flux';
import { AppColors } from '../../theme';
// import AudioUtil from './AudioUtil';

const filename = 'mongrov-voice-test.mp4';
const sendIconColor = AppColors.brand().aA_sendIconColor;
const cancelColor = AppColors.brand().aA_cancelColor;

const styles = StyleSheet.create({
  container: {
    position: 'absolute',
    left: 0,
    right: 0,
    bottom: 0,
    borderRadius: 5,
    padding: 5,
  },
  subcontainer: {
    flex: 1,
    flexDirection: 'row',
  },
  timerView: {
    flex: 1,
    alignItems: 'center',
    justifyContent: 'center',
    margin: 5,
  },
  textColor: {
    color: AppColors.brand().aA_textColor,
  },
  timerText: {
    color: AppColors.brand().aA_textColor,
    paddingBottom: 20,
    fontSize: 20,
  },
  iconView: {
    flex: 1,
    flexDirection: 'column',
    alignItems: 'center',
    justifyContent: 'center',
    margin: 5,
  },
});

// var styles = StyleSheet.create({
//   button: {
//     padding: 20,
//     fontSize: 20,
//     backgroundColor: 'white',
//   },
//   slider: {
//     height: 10,
//     margin: 10,
//   },
//   buttonContainer: {
//     flex: 1,
//     flexDirection: 'row',
//     justifyContent: 'space-between',
//     alignItems: 'center',
//   },
//   settingsContainer: {
//     flex: 1,
//     justifyContent: 'space-between',
//     alignItems: 'center',
//   },
//   container: {
//     borderRadius: 4,
//     borderWidth: 0.5,
//     borderColor: '#d6d7da',
//   },
//   title: {
//     fontSize: 19,
//     fontWeight: 'bold',
//     textAlign: 'center',
//     padding: 20,
//   },
//   errorMessage: {
//     fontSize: 15,
//     textAlign: 'center',
//     padding: 10,
//     color: 'red',
//   },
// });

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

    this.state = {
      playPauseButton: 'Preparing...',
      recordButton: 'Preparing...',

      stopButtonDisabled: true,
      playButtonDisabled: true,
      recordButtonDisabled: true,

      loopButtonStatus: false,
      progress: 0,

      error: null,
      recordTime: 0,

      recorderView: true,
      filePath: '',
      cancelled: false,
    };
  }

  componentWillMount() {
    this.player = null;
    this.recorder = null;
    this.lastSeek = 0;

    this._reloadPlayer();
    this._reloadRecorder();

    this._progressInterval = setInterval(() => {
      if (this.player && this._shouldUpdateProgressBar()) { // && !this._dragging) {
        this.setState({ progress: Math.max(0, this.player.currentTime) / this.player.duration });
      }
    }, 100);
  }

  componentDidMount() {
    this._toggleRecord();
  }

  componentWillUnmount() {
    // console.log('unmount');
    // TODO
    clearInterval(this._progressInterval);
    clearInterval(this._recordInterval);
  }

  _shouldUpdateProgressBar() {
    // Debounce progress bar update by 200 ms
    return Date.now() - this.lastSeek > 200;
  }

  _updateState(/* err */) {
    this.setState({
      playPauseButton: this.player && this.player.isPlaying ? 'Pause' : 'Play',
      playerButton: this.player && this.player.isPlaying ? 'stop' : 'play',
      recordButton: this.recorder && this.recorder.isRecording ? 'Stop' : 'Record',
      // recordIcon: this.recorder && this.recorder.isRecording ? 'microphone-off' : 'microphone',

      stopButtonDisabled: !this.player || !this.player.canStop,
      playButtonDisabled: !this.player || !this.player.canPlay || this.recorder.isRecording,
      recordButtonDisabled: !this.recorder || (this.player && !this.player.isStopped),
    });
    if (this.recorder && this.recorder.isRecording) {
      console.log('KRec 2', this.recorder);
      this.setState({ filePath: this.recorder._fsPath });
      this._recordInterval = setInterval(() => {
        if (this.state.recordTime < 300) {      // Timeout 5 Min  to send
          console.log('time', this.state.recordTime);
          this.setState({ recordTime: this.state.recordTime + 1 });
          console.log('running-time', this.state.recordTime);
        } else {
          clearInterval(this._recordInterval);
          this._toggleRecord();
        }
      }, 1000);
    }
  }

  _playPause() {
    this.player.playPause((err/* , playing */) => {
      if (err) {
        this.setState({
          error: err.message,
        });
      }
      this._updateState();
    });
  }

  _stop() {
    this.player.stop(() => {
      this._updateState();
    });
  }

  _seek(percentage) {
    if (!this.player) {
      return;
    }

    this.lastSeek = Date.now();

    const position = percentage * this.player.duration;

    this.player.seek(position, () => {
      this._updateState();
    });
  }

  _reloadPlayer() {
    if (this.player) {
      this.player.destroy();
    }

    this.player = new Player(filename, {
      autoDestroy: false,
    }).prepare((err) => {
      if (err) {
        console.log('error at _reloadPlayer():');
        console.log(err);
      } else {
        this.player.looping = this.state.loopButtonStatus;
      }

      this._updateState();
    });

    this.player.volume = 1;

    this._updateState();

    this.player.on('ended', () => {
      this._updateState();
    });
    this.player.on('pause', () => {
      this._updateState();
    });
  }

  _reloadRecorder() {
    if (this.recorder) {
      this.recorder.destroy();
    }

    this.recorder = new Recorder(`${moment().unix().toString()}.mp4`, {
      bitrate: 256000,
      channels: 2,
      sampleRate: 44100,
      quality: 'max',
      // format: 'ac3', // autodetected
      // encoder: 'aac', // autodetected
    });

    console.log('KRec', this.recorder);

    this._updateState();
  }

  _toggleRecord() {
    console.log('record');
    if (this.player) {
      this.player.destroy();
    }

    this.recorder.toggleRecord((err, stopped) => {
      if (err) {
        this.setState({
          error: err.message,
        });
      }
      if (stopped && !this.state.cancelled) {
        clearInterval(this._recordInterval);
        Actions.refresh({
          attachAudio: false,
          attach: {
            cameraData: { path: Platform.OS === 'ios' ? this.state.filePath : `file://${this.state.filePath}` }, // data,
            cameraMessage: 'Audio message',
            video: false,
          },
        });
        // this.setState({ recordTime: 0 });
        // this._reloadPlayer();
        // this._reloadRecorder();
        // this.setState({ recorderView: false });
      } else if (stopped && this.state.cancelled) {
        clearInterval(this._recordInterval);
        Actions.refresh({
          attachAudio: false,
        });
      } else if (!stopped) {
        this._updateState();
      }
    });
  }

  _toggleLooping(value) {
    this.setState({
      loopButtonStatus: value,
    });
    if (this.player) {
      this.player.looping = value;
    }
  }

  render() {
    const recT = this.state.recordTime;
    const recM = (recT - (recT % 60)) / 60;
    const recS = (recT % 60);
    return (
      <View
        style={[styles.container, { backgroundColor: AppColors.brand().fourth }]}
      >
        <View
          style={styles.subcontainer}
        >
          <View
            style={styles.iconView}
          >
            <Icon
              raised
              name="record"
              type="material-community"
              color={'#ff5608'}
              reverse
            />
            <Text
              numberOfLines={1}
              ellipsizeMode={'tail'}
              style={styles.textColor}
            >Recording...</Text>
          </View>
          <View
            style={styles.timerView}
          >
            <Text
              numberOfLines={1}
              ellipsizeMode={'tail'}
              style={[styles.timerText]}
            >{`${recM < 10 ? '0' : ''}${recM}:${recS < 10 ? '0' : ''}${recS}`}</Text>
          </View>
          <TouchableOpacity
            style={styles.iconView}
            onPress={() => {
              this._toggleRecord();
            }}
          >
            <Icon
              raised
              name="send"
              color={sendIconColor}
              reverse
            />
            <Text
              numberOfLines={1}
              ellipsizeMode={'tail'}
              style={styles.textColor}
            >Send</Text>
          </TouchableOpacity>
          <TouchableOpacity
            style={styles.iconView}
            onPress={() => {
              this.setState({ cancelled: true });
              this._toggleRecord();
            }}
          >
            <Icon
              raised
              name="delete"
              color={cancelColor}
              reverse
            />
            <Text
              numberOfLines={1}
              ellipsizeMode={'tail'}
              style={styles.textColor}
            >Cancel</Text>
          </TouchableOpacity>
        </View>
      </View>
    );
  }
}
<|endoftext|>


<commit_before>/*
 * This file is part of Invenio.
 * Copyright (C) 2022 CERN.
 *
 * Invenio is free software; you can redistribute it and/or modify it
 * under the terms of the MIT License; see LICENSE file for more details.
 */

import React, { Component } from "react";
import { withState } from "react-searchkit";
import { Search, Label } from "semantic-ui-react";
import { i18next } from "@translations/invenio_search_ui/i18next";
import PropTypes from "prop-types";

const resultRenderer = ({ text }, queryString) => {
  return (

{queryString}

      {text}

  );
};

export class MultipleOptionsSearchBar extends Component {
  constructor(props) {
    super(props);
    this.state = {
      queryString: "",
    };
  }

  render() {
    const { placeholder, options } = this.props;
    const { queryString } = this.state;

    return (
      <Search
        fluid
        onResultSelect={(e, { result }) => {
          const { value: url } = result;
          window.location = `${url}?q=${queryString}`;
        }}
        onSearchChange={(e, { value }) => {
          this.setState({ queryString: value });
        }}
        resultRenderer={(props) => resultRenderer(props, queryString)}
        results={options}
        value={queryString}
        placeholder={placeholder}
        selectFirstResult
      />
    );
  }
}

MultipleOptionsSearchBar.propTypes = {
  options: PropTypes.array.isRequired,
  placeholder: PropTypes.string,
};

MultipleOptionsSearchBar.defaultProps = {
  placeholder: i18next.t("Search"),
};

export class MultipleOptionsSearchBarCmp extends Component {
  onBtnSearchClick = () => {
    const { queryString, updateQueryState, currentQueryState } = this.props;
    updateQueryState({ ...currentQueryState, queryString });
  };

  render() {
    const { placeholder, queryString, onInputChange, options } = this.props;

    return (
      <Search
        fluid
        onResultSelect={(e, { result }) => {
          const { value: url } = result;
          if (window.location.pathname === url) {
            this.onBtnSearchClick();
          } else {
            window.location = `${url}?q=${queryString}`;
          }
        }}
        onSearchChange={(e, { value }) => {
          onInputChange(value);
        }}
        resultRenderer={(props) => resultRenderer(props, queryString)}
        results={options}
        value={queryString}
        placeholder={placeholder}
        selectFirstResult
      />
    );
  }
}

MultipleOptionsSearchBarCmp.propTypes = {
  options: PropTypes.array.isRequired,
  queryString: PropTypes.string.isRequired,
  updateQueryState: PropTypes.func.isRequired,
  onInputChange: PropTypes.func.isRequired,
  placeholder: PropTypes.string,
};

MultipleOptionsSearchBarCmp.defaultProps = {
  placeholder: i18next.t("Search"),
};

export const MultipleOptionsSearchBarRSK = withState(MultipleOptionsSearchBarCmp);
<commit_msg>Header Searchbar: allow empty search<commit_after>/*
 * This file is part of Invenio.
 * Copyright (C) 2022 CERN.
 *
 * Invenio is free software; you can redistribute it and/or modify it
 * under the terms of the MIT License; see LICENSE file for more details.
 */

import React, { Component } from "react";
import { withState } from "react-searchkit";
import { Search, Label, Button, Icon } from "semantic-ui-react";
import { i18next } from "@translations/invenio_search_ui/i18next";
import PropTypes from "prop-types";

const resultRenderer = ({ text }, queryString) => {
  return (

{queryString}

      {text}

  );
};

export class MultipleOptionsSearchBar extends Component {
  constructor(props) {
    super(props);
    this.state = {
      queryString: "",
    };
  }

  render() {
    const { placeholder, options } = this.props;
    const { queryString } = this.state;
    const button = (
      <Button
        icon
        className="right-floated search"
        onClick={() => {
          const url = options[0]?.value;
          window.location = `${url}?q=${queryString}`;
        }}
      >
        <Icon name="search" />
      </Button>
    );
    return (
      <Search
        fluid
        onResultSelect={(e, { result }) => {
          const { value: url } = result;
          window.location = `${url}?q=${queryString}`;
        }}
        onSearchChange={(e, { value }) => {
          this.setState({ queryString: value });
        }}
        resultRenderer={(props) => resultRenderer(props, queryString)}
        results={options}
        value={queryString}
        placeholder={placeholder}
        minCharacters={0}
        icon={button}
        className="right-angle-search-content"
        selectFirstResult
      />
    );
  }
}

MultipleOptionsSearchBar.propTypes = {
  options: PropTypes.array.isRequired,
  placeholder: PropTypes.string,
};

MultipleOptionsSearchBar.defaultProps = {
  placeholder: i18next.t("Search"),
};

export class MultipleOptionsSearchBarCmp extends Component {
  onBtnSearchClick = () => {
    const { queryString, updateQueryState, currentQueryState } = this.props;
    updateQueryState({ ...currentQueryState, queryString });
  };

  render() {
    const { placeholder, queryString, onInputChange, options } = this.props;
    const button = (
      <Button
        icon
        className="right-floated search"
        onClick={() => {
          const url = options[0]?.value;
          if (window.location.pathname === url) {
            this.onBtnSearchClick();
          } else {
            window.location = `${url}?q=${queryString}`;
          }
        }}
      >
        <Icon name="search" />
      </Button>
    );

    return (
      <Search
        fluid
        onResultSelect={(e, { result }) => {
          const { value: url } = result;
          if (window.location.pathname === url) {
            this.onBtnSearchClick();
          } else {
            window.location = `${url}?q=${queryString}`;
          }
        }}
        onSearchChange={(e, { value }) => {
          onInputChange(value);
        }}
        resultRenderer={(props) => resultRenderer(props, queryString)}
        results={options}
        value={queryString}
        placeholder={placeholder}
        className="right-angle-search-content"
        selectFirstResult
        icon={button}
        minCharacters={0}
      />
    );
  }
}

MultipleOptionsSearchBarCmp.propTypes = {
  options: PropTypes.array.isRequired,
  queryString: PropTypes.string.isRequired,
  updateQueryState: PropTypes.func.isRequired,
  onInputChange: PropTypes.func.isRequired,
  placeholder: PropTypes.string,
};

MultipleOptionsSearchBarCmp.defaultProps = {
  placeholder: i18next.t("Search"),
};

export const MultipleOptionsSearchBarRSK = withState(
  MultipleOptionsSearchBarCmp
);
<|endoftext|>


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

    function valueUpdated(a, b) {
        return a !== undefined && a !== b;
    }

    function isFloating(a) {
        return a !== (a | 0);
    }

    function checkGeometry(a) {
        if (isFloating(a))
            throw new Error("sub-pixel geometry");
        return a;
    }

    function roundedRectRegion(geom, corners) {
        var shapeRegion = new Region();
        shapeRegion.init_rect(0, 0, geom.width, geom.height);

        var cornerRegion = new Region();

        function widthForRadiusSegment(radius, i) {
            var r = radius-i-0.5;
            return (0.5 + radius - Math.sqrt(radius*radius - r*r)) | 0;
        }

        if (corners.topRight) {
            for (var i = 0; i < corners.topLeft; i++) {
                var width = widthForRadiusSegment(corners.topLeft, i);
                cornerRegion.union_rect(cornerRegion, 0, i, width, 1);
            }
        }

        if (corners.topRight) {
            for (var i = 0; i < corners.topRight; i++) {
                var width = widthForRadiusSegment(corners.topRight, i);
                cornerRegion.union_rect(cornerRegion, geom.width - width, i, width, 1);
            }
        }

        if (corners.bottomLeft) {
            for (var i = 0; i < corners.bottomLeft; i++) {
                var width = widthForRadiusSegment(corners.bottomLeft, i);
                cornerRegion.union_rect(cornerRegion, 0, geom.height - i - 1, width, 1);
            }
        }

        if (corners.bottomRight) {
            for (var i = 0; i < corners.bottomRight; i++) {
                var width = widthForRadiusSegment(corners.bottomRight, i);
                cornerRegion.union_rect(cornerRegion, geom.width - width, geom.height - i - 1, width, 1);
            }
        }

        shapeRegion.subtract(shapeRegion, cornerRegion);
        cornerRegion.finalize();

        return shapeRegion;
    }

    // Don't extend Window as this needs to be in the
    // WM client, not its own client.
    var WindowFrame = new Class({
        Extends: Window,
        initialize: function(wm, server, windowId) {
            this._wm = wm;
            this._server = server;
            this._clientWindowId = windowId;

            // Client geometry relative to the parent frame
            this._clientGeometry = {};

            // Frame geometry relative to the root
            this._frameGeometry = {};
        },

        _updateGeometry: function(clientGeometry) {
            var border = { top: 30, left: 5, right: 5, bottom: 5 };
            var positionUpdated = false;
            var sizeUpdated = false;

            if (valueUpdated(clientGeometry.x, this._frameGeometry.x)) {
                this._frameGeometry.x = checkGeometry(clientGeometry.x);
                positionUpdated = true;
            }

            if (valueUpdated(clientGeometry.y, this._frameGeometry.y)) {
                this._frameGeometry.y = checkGeometry(clientGeometry.y);
                positionUpdated = true;
            }

            if (valueUpdated(clientGeometry.width, this._clientGeometry.width)) {
                this._clientGeometry.width = checkGeometry(clientGeometry.width);
                this._frameGeometry.width = clientGeometry.width + border.left + border.right;
                sizeUpdated = true;
            }

            if (valueUpdated(clientGeometry.height, this._clientGeometry.height)) {
                this._clientGeometry.height = checkGeometry(clientGeometry.height);
                this._frameGeometry.height = clientGeometry.height + border.top + border.bottom;
                sizeUpdated = true;
            }

            this._clientGeometry.x = border.left;
            this._clientGeometry.y = border.top;

            if (positionUpdated || sizeUpdated)
                this._server.configureWindow(this._wm, this.frameWindowId, this._frameGeometry);

            if (sizeUpdated) {
                // Update the client window
                this._server.configureWindow(this._wm, this._clientWindowId, this._clientGeometry);

                this._server.configureWindow(this._wm, this._closeWindowId, { x: border.left + this._clientGeometry.width - 20,
                                                                              y: 8 });

                // Invalidate the frame that's already been partially painted.
                this._server.invalidateWindow(this._wm, this.frameWindowId);
            }

            var shapeRegion = roundedRectRegion(this._frameGeometry, { topLeft: 10, topRight: 10 });
            this._server.setWindowShapeRegion(this._wm, this.frameWindowId, "Bounding", shapeRegion);
            shapeRegion.finalize();
        },

        _makeButton: function() {
            var buttonWindowId = this._server.createWindow(this._wm);
            this._wm.register(buttonWindowId, this);
            this._server.selectInput(this._wm, buttonWindowId, ["ButtonRelease"]);
            this._server.changeAttributes(this._wm, buttonWindowId, { cursor: "pointer" });
            var size = 15;
            var geom = { width: size, height: size };
            this._server.configureWindow(this._wm, buttonWindowId, geom);
            var radius = size / 2;
            var corners = { topLeft: radius, topRight: radius, bottomLeft: radius, bottomRight: radius };
            var shapeRegion = roundedRectRegion(geom, corners);
            this._server.setWindowShapeRegion(this._wm, buttonWindowId, "Bounding", shapeRegion);
            this._server.reparentWindow(this._wm, buttonWindowId, this.frameWindowId);
            this._server.mapWindow(this._wm, buttonWindowId);
            return buttonWindowId;
        },

        construct: function() {
            var geom = this._server.getGeometry(this._wm, this._clientWindowId);

            this._wm.register(this._clientWindowId, this);
            this._server.grabButton(this._wm, this._clientWindowId, 1, false, ["ButtonPress", "ButtonRelease"], "Sync", "");

            this.frameWindowId = this._server.createWindow(this._wm);
            this._wm.register(this.frameWindowId, this);
            this._server.selectInput(this._wm, this.frameWindowId, ["Expose", "ButtonPress", "FocusIn", "FocusOut"]);
            this._server.changeAttributes(this._wm, this.frameWindowId, { hasInput: true, backgroundColor: 'orange' });

            this._closeWindowId = this._makeButton();
            this._server.changeAttributes(this._wm, this._closeWindowId, { backgroundColor: 'red' });

            this._server.reparentWindow(this._wm, this._clientWindowId, this.frameWindowId);
            this._server.mapWindow(this._wm, this.frameWindowId);

            this._updateGeometry(geom);
        },
        destroy: function() {
            this._server.destroyWindow(this._wm, this.frameWindowId);
        },
        unregister: function() {
            this._wm.unregister(this.frameWindowId);
            this._wm.unregister(this._clientWindowId);
            this._wm.unregister(this._closeWindowId);
        },

        _configureRequestStack: function(event) {
            this._server.configureWindow(this._wm, this.frameWindowId, { stackMode: event.detail });
        },
        configureRequest: function(event) {
            // ICCCM 4.1.5

            // The coordinate system of the ConfigureRequest is that of the root;
            // that is, the X/Y in the ConfigureNotify are of the top-left of the
            // outer frame window. Note that the width/height are of the client
            // window.

            // We don't generate synthetic events quite yet.
            this._updateGeometry(event);

            if (event.detail !== undefined)
                this._configureRequestStack(event);
        },
        _handleFrameEvent: function(event) {
            switch (event.type) {
            case "ButtonPress":
                return this._frameButtonPress(event);
            case "ButtonRelease":
                return this._frameButtonRelease(event);
            case "Motion":
                return this._frameMotion(event);
            case "FocusIn":
                return this._frameFocusIn(event);
            case "FocusOut":
                return this._frameFocusOut(event);
            case "Expose":
                return this._frameExpose(event.ctx);
            }
        },
        _frameButtonPress: function(event) {
            this._origMousePos = { x: event.rootX, y: event.rootY };
            var frameCoords = this._server.getGeometry(this, this.frameWindowId);
            this._origWindowPos = { x: frameCoords.x, y: frameCoords.y };
            this._server.grabPointer(this._wm, this.frameWindowId, true, ["ButtonRelease", "Motion"], "Async", "-moz-grabbing");
        },
        _frameButtonRelease: function(event) {
            this._server.ungrabPointer(this._wm, this.frameWindowId);

            this._origMousePos = null;
            this._origWindowPos = null;
        },
        _frameMotion: function(event) {
            var newX = this._origWindowPos.x + event.rootX - this._origMousePos.x;
            var newY = this._origWindowPos.y + event.rootY - this._origMousePos.y;
            this._updateGeometry({ x: newX, y: newY });
        },
        _frameFocusIn: function(event) {
            this._server.changeAttributes(this._wm, this.frameWindowId, { backgroundColor: 'yellow' });
            this._server.invalidateWindow(this._wm, this.frameWindowId);
        },
        _frameFocusOut: function(event) {
            if (event.detail == "Inferior")
                return;
            this._server.changeAttributes(this._wm, this.frameWindowId, { backgroundColor: 'orange' });
            this._server.invalidateWindow(this._wm, this.frameWindowId);
        },
        _frameExpose: function(wrapper) {
            // background color takes care of the base

            // Draw title.
            var title = this._server.getProperty(this._wm, this._clientWindowId, "WM_NAME");
            if (title) {
                var geom = this._server.getGeometry(this._wm, this._clientWindowId);
                wrapper.drawWithContext(function(ctx) {
                    ctx.fillStyle = '#000';
                    ctx.textAlign = 'center';
                    ctx.font = '12pt sans-serif';
                    ctx.fillText(title, geom.width / 2, 21);
                });
            }

            wrapper.clearDamage();
        },
        _handleButtonEvent: function(event) {
            if (event.windowId == this._closeWindowId && event.type == "ButtonRelease")
                this._server.destroyWindow(this._wm, this._clientWindowId);
        },
        handleEvent: function(event) {
            if (event.windowId == this._closeWindowId)
                return this._handleButtonEvent(event);
            if (event.windowId == this.frameWindowId)
                return this._handleFrameEvent(event);
        },
        focus: function() {
            this._server.setInputFocus(this._wm, this._clientWindowId);
        },
    });

    var WindowManager = new Class({
        connect: function(server) {
            this._server = server;
            this._server.clientConnected(this);
            this._server.selectInput(this, this._server.rootWindowId, ["SubstructureRedirect", "SubstructureNotify"]);

            // window ID => WindowFrame
            this._windowFrames = {};
        },

        handleEvent: function(event) {
            var frame = this._windowFrames[event.windowId];
            var frameWasReceiver = !!(frame && frame.frameWindowId == event.windowId);

            switch (event.type) {
            case "MapRequest":
                return this.mapRequest(event);
            case "UnmapNotify":
                return this.unmapNotify(event);
            case "DestroyNotify":
                return this.destroyNotify(event);
            case "ConfigureRequest":
                return this.configureRequest(event);
            case "ButtonPress":
                // Raise on click.
                this._server.configureWindow(this, frame.frameWindowId, { stackMode: "Above" });
                frame.focus();
                if (frameWasReceiver)
                    return frame.handleEvent(event);
                else
                    this._server.allowEvents(this, "Replay");
                break;

            // These should only happen for frame windows.
            case "ButtonRelease":
            case "Motion":
            case "Expose":
            case "FocusIn":
            case "FocusOut":
                return frame.handleEvent(event);
            }
        },
        configureRequest: function(event) {
            var frame = this._windowFrames[event.windowId];

            // If we don't have a frame for a window, it was never
            // mapped, simply re-configure the window with whatever
            // it requested.
            if (!frame) {
                this._server.configureWindow(this, event.windowId,
                                             { x: event.x, y: event.y, width: event.width, height: event.height });
            } else {
                // The frame will move/resize the window to its
                // client coordinates.
                frame.configureRequest(event);
            }
        },
        unmapNotify: function(event) {
            var frame = this._windowFrames[event.windowId];

            if (!frame || event.windowId == frame.frameWindowId)
                return;

            frame.destroy();
        },
        destroyNotify: function(event) {
            var frame = this._windowFrames[event.windowId];

            if (!frame || event.windowId == frame.frameWindowId)
                return;

            frame.unregister();
        },
        mapRequest: function(event) {
            var frame = new WindowFrame(this, this._server, event.windowId);

            // Reparent the original window and map the frame.
            frame.construct();

            // Map the original window, now that we've reparented it
            // back into the frame.
            this._server.mapWindow(this, event.windowId);

            frame.focus();
        },
        register: function(windowId, frame) {
            this._windowFrames[windowId] = frame;
        },
        unregister: function(windowId) {
            this._windowFrames[windowId] = null;
        },
    });

    exports.WindowManager = WindowManager;

})(window);
<commit_msg>Only respect the client window for DestroyNotify/UnmapNotify<commit_after>(function(exports) {
    "use strict";

    function valueUpdated(a, b) {
        return a !== undefined && a !== b;
    }

    function isFloating(a) {
        return a !== (a | 0);
    }

    function checkGeometry(a) {
        if (isFloating(a))
            throw new Error("sub-pixel geometry");
        return a;
    }

    function roundedRectRegion(geom, corners) {
        var shapeRegion = new Region();
        shapeRegion.init_rect(0, 0, geom.width, geom.height);

        var cornerRegion = new Region();

        function widthForRadiusSegment(radius, i) {
            var r = radius-i-0.5;
            return (0.5 + radius - Math.sqrt(radius*radius - r*r)) | 0;
        }

        if (corners.topRight) {
            for (var i = 0; i < corners.topLeft; i++) {
                var width = widthForRadiusSegment(corners.topLeft, i);
                cornerRegion.union_rect(cornerRegion, 0, i, width, 1);
            }
        }

        if (corners.topRight) {
            for (var i = 0; i < corners.topRight; i++) {
                var width = widthForRadiusSegment(corners.topRight, i);
                cornerRegion.union_rect(cornerRegion, geom.width - width, i, width, 1);
            }
        }

        if (corners.bottomLeft) {
            for (var i = 0; i < corners.bottomLeft; i++) {
                var width = widthForRadiusSegment(corners.bottomLeft, i);
                cornerRegion.union_rect(cornerRegion, 0, geom.height - i - 1, width, 1);
            }
        }

        if (corners.bottomRight) {
            for (var i = 0; i < corners.bottomRight; i++) {
                var width = widthForRadiusSegment(corners.bottomRight, i);
                cornerRegion.union_rect(cornerRegion, geom.width - width, geom.height - i - 1, width, 1);
            }
        }

        shapeRegion.subtract(shapeRegion, cornerRegion);
        cornerRegion.finalize();

        return shapeRegion;
    }

    // Don't extend Window as this needs to be in the
    // WM client, not its own client.
    var WindowFrame = new Class({
        Extends: Window,
        initialize: function(wm, server, windowId) {
            this._wm = wm;
            this._server = server;
            this._clientWindowId = windowId;

            // Client geometry relative to the parent frame
            this._clientGeometry = {};

            // Frame geometry relative to the root
            this._frameGeometry = {};
        },

        _updateGeometry: function(clientGeometry) {
            var border = { top: 30, left: 5, right: 5, bottom: 5 };
            var positionUpdated = false;
            var sizeUpdated = false;

            if (valueUpdated(clientGeometry.x, this._frameGeometry.x)) {
                this._frameGeometry.x = checkGeometry(clientGeometry.x);
                positionUpdated = true;
            }

            if (valueUpdated(clientGeometry.y, this._frameGeometry.y)) {
                this._frameGeometry.y = checkGeometry(clientGeometry.y);
                positionUpdated = true;
            }

            if (valueUpdated(clientGeometry.width, this._clientGeometry.width)) {
                this._clientGeometry.width = checkGeometry(clientGeometry.width);
                this._frameGeometry.width = clientGeometry.width + border.left + border.right;
                sizeUpdated = true;
            }

            if (valueUpdated(clientGeometry.height, this._clientGeometry.height)) {
                this._clientGeometry.height = checkGeometry(clientGeometry.height);
                this._frameGeometry.height = clientGeometry.height + border.top + border.bottom;
                sizeUpdated = true;
            }

            this._clientGeometry.x = border.left;
            this._clientGeometry.y = border.top;

            if (positionUpdated || sizeUpdated)
                this._server.configureWindow(this._wm, this.frameWindowId, this._frameGeometry);

            if (sizeUpdated) {
                // Update the client window
                this._server.configureWindow(this._wm, this._clientWindowId, this._clientGeometry);

                this._server.configureWindow(this._wm, this._closeWindowId, { x: border.left + this._clientGeometry.width - 20,
                                                                              y: 8 });

                // Invalidate the frame that's already been partially painted.
                this._server.invalidateWindow(this._wm, this.frameWindowId);
            }

            var shapeRegion = roundedRectRegion(this._frameGeometry, { topLeft: 10, topRight: 10 });
            this._server.setWindowShapeRegion(this._wm, this.frameWindowId, "Bounding", shapeRegion);
            shapeRegion.finalize();
        },

        _makeButton: function() {
            var buttonWindowId = this._server.createWindow(this._wm);
            this._wm.register(buttonWindowId, this);
            this._server.selectInput(this._wm, buttonWindowId, ["ButtonRelease"]);
            this._server.changeAttributes(this._wm, buttonWindowId, { cursor: "pointer" });
            var size = 15;
            var geom = { width: size, height: size };
            this._server.configureWindow(this._wm, buttonWindowId, geom);
            var radius = size / 2;
            var corners = { topLeft: radius, topRight: radius, bottomLeft: radius, bottomRight: radius };
            var shapeRegion = roundedRectRegion(geom, corners);
            this._server.setWindowShapeRegion(this._wm, buttonWindowId, "Bounding", shapeRegion);
            this._server.reparentWindow(this._wm, buttonWindowId, this.frameWindowId);
            this._server.mapWindow(this._wm, buttonWindowId);
            return buttonWindowId;
        },

        construct: function() {
            var geom = this._server.getGeometry(this._wm, this._clientWindowId);

            this._wm.register(this._clientWindowId, this);
            this._server.grabButton(this._wm, this._clientWindowId, 1, false, ["ButtonPress", "ButtonRelease"], "Sync", "");

            this.frameWindowId = this._server.createWindow(this._wm);
            this._wm.register(this.frameWindowId, this);
            this._server.selectInput(this._wm, this.frameWindowId, ["Expose", "ButtonPress", "FocusIn", "FocusOut"]);
            this._server.changeAttributes(this._wm, this.frameWindowId, { hasInput: true, backgroundColor: 'orange' });

            this._closeWindowId = this._makeButton();
            this._server.changeAttributes(this._wm, this._closeWindowId, { backgroundColor: 'red' });

            this._server.reparentWindow(this._wm, this._clientWindowId, this.frameWindowId);
            this._server.mapWindow(this._wm, this.frameWindowId);

            this._updateGeometry(geom);
        },
        destroy: function() {
            this._server.destroyWindow(this._wm, this.frameWindowId);
        },
        unregister: function() {
            this._wm.unregister(this.frameWindowId);
            this._wm.unregister(this._clientWindowId);
            this._wm.unregister(this._closeWindowId);
        },
        frameWasReceiver: function(event) {
            // The frame has several internal helper windows for buttons, etc.
            // that we want to respond to. Just check if it's the client window
            // ID or not.
            return event.windowId != this._clientWindowId;
        },

        _configureRequestStack: function(event) {
            this._server.configureWindow(this._wm, this.frameWindowId, { stackMode: event.detail });
        },
        configureRequest: function(event) {
            // ICCCM 4.1.5

            // The coordinate system of the ConfigureRequest is that of the root;
            // that is, the X/Y in the ConfigureNotify are of the top-left of the
            // outer frame window. Note that the width/height are of the client
            // window.

            // We don't generate synthetic events quite yet.
            this._updateGeometry(event);

            if (event.detail !== undefined)
                this._configureRequestStack(event);
        },
        _handleFrameEvent: function(event) {
            switch (event.type) {
            case "ButtonPress":
                return this._frameButtonPress(event);
            case "ButtonRelease":
                return this._frameButtonRelease(event);
            case "Motion":
                return this._frameMotion(event);
            case "FocusIn":
                return this._frameFocusIn(event);
            case "FocusOut":
                return this._frameFocusOut(event);
            case "Expose":
                return this._frameExpose(event.ctx);
            }
        },
        _frameButtonPress: function(event) {
            this._origMousePos = { x: event.rootX, y: event.rootY };
            var frameCoords = this._server.getGeometry(this, this.frameWindowId);
            this._origWindowPos = { x: frameCoords.x, y: frameCoords.y };
            this._server.grabPointer(this._wm, this.frameWindowId, true, ["ButtonRelease", "Motion"], "Async", "-moz-grabbing");
        },
        _frameButtonRelease: function(event) {
            this._server.ungrabPointer(this._wm, this.frameWindowId);

            this._origMousePos = null;
            this._origWindowPos = null;
        },
        _frameMotion: function(event) {
            var newX = this._origWindowPos.x + event.rootX - this._origMousePos.x;
            var newY = this._origWindowPos.y + event.rootY - this._origMousePos.y;
            this._updateGeometry({ x: newX, y: newY });
        },
        _frameFocusIn: function(event) {
            this._server.changeAttributes(this._wm, this.frameWindowId, { backgroundColor: 'yellow' });
            this._server.invalidateWindow(this._wm, this.frameWindowId);
        },
        _frameFocusOut: function(event) {
            if (event.detail == "Inferior")
                return;
            this._server.changeAttributes(this._wm, this.frameWindowId, { backgroundColor: 'orange' });
            this._server.invalidateWindow(this._wm, this.frameWindowId);
        },
        _frameExpose: function(wrapper) {
            // background color takes care of the base

            // Draw title.
            var title = this._server.getProperty(this._wm, this._clientWindowId, "WM_NAME");
            if (title) {
                var geom = this._server.getGeometry(this._wm, this._clientWindowId);
                wrapper.drawWithContext(function(ctx) {
                    ctx.fillStyle = '#000';
                    ctx.textAlign = 'center';
                    ctx.font = '12pt sans-serif';
                    ctx.fillText(title, geom.width / 2, 21);
                });
            }

            wrapper.clearDamage();
        },
        _handleButtonEvent: function(event) {
            if (event.windowId == this._closeWindowId && event.type == "ButtonRelease")
                this._server.destroyWindow(this._wm, this._clientWindowId);
        },
        handleEvent: function(event) {
            if (event.windowId == this._closeWindowId)
                return this._handleButtonEvent(event);
            if (event.windowId == this.frameWindowId)
                return this._handleFrameEvent(event);
        },
        focus: function() {
            this._server.setInputFocus(this._wm, this._clientWindowId);
        },
    });

    var WindowManager = new Class({
        connect: function(server) {
            this._server = server;
            this._server.clientConnected(this);
            this._server.selectInput(this, this._server.rootWindowId, ["SubstructureRedirect", "SubstructureNotify"]);

            // window ID => WindowFrame
            this._windowFrames = {};
        },

        handleEvent: function(event) {
            var frame = this._windowFrames[event.windowId];
            var frameWasReceiver = frame && frame.frameWasReceiver(event);

            switch (event.type) {
            case "MapRequest":
                return this.mapRequest(event);
            case "ConfigureRequest":
                return this.configureRequest(event, frame);
            case "UnmapNotify":
                if (!frameWasReceiver)
                    return frame.destroy();
                break;
            case "DestroyNotify":
                if (!frameWasReceiver)
                    return frame.unregister();
                break;
            case "ButtonPress":
                // Raise on click.
                this._server.configureWindow(this, frame.frameWindowId, { stackMode: "Above" });
                frame.focus();
                if (frameWasReceiver)
                    return frame.handleEvent(event);
                else
                    this._server.allowEvents(this, "Replay");
                break;

            // These should only happen for frame windows.
            case "ButtonRelease":
            case "Motion":
            case "Expose":
            case "FocusIn":
            case "FocusOut":
                return frame.handleEvent(event);
            }
        },
        configureRequest: function(event, frame) {
            // If we don't have a frame for a window, it was never
            // mapped, simply re-configure the window with whatever
            // it requested.
            if (!frame) {
                this._server.configureWindow(this, event.windowId,
                                             { x: event.x, y: event.y, width: event.width, height: event.height });
            } else {
                // The frame will move/resize the window to its
                // client coordinates.
                frame.configureRequest(event);
            }
        },
        unmapNotify: function(event) {
        },
        mapRequest: function(event) {
            var frame = new WindowFrame(this, this._server, event.windowId);

            // Reparent the original window and map the frame.
            frame.construct();

            // Map the original window, now that we've reparented it
            // back into the frame.
            this._server.mapWindow(this, event.windowId);

            frame.focus();
        },
        register: function(windowId, frame) {
            this._windowFrames[windowId] = frame;
        },
        unregister: function(windowId) {
            this._windowFrames[windowId] = null;
        },
    });

    exports.WindowManager = WindowManager;

})(window);
<|endoftext|>


<commit_before>var request = require('request-promise');

module.exports = function (context, message) {

    let path = '/' + message.request.mediaid + '?fields=id,message,picture,place,name,comments,reactions';

    let options = {
        uri: "https://graph.facebook.com" + path,
        method: 'GET',
        headers: {
            'Authorization': 'OAuth ' + message.social.facebook.token
        }
    };

    request(options)
        .then((response) => {
            let data = message;

            data.response = {
                platform: "facebook",
                type: "media",
                data: JSON.parse(response)
            };

            context.log(JSON.stringify(data, null, 4));
            context.bindings.out = data;
            context.done();
        })
        .catch((error) => context.log(error));
}<commit_msg>adding message_tags to FB grpah call<commit_after>var request = require('request-promise');

module.exports = function (context, message) {

    let path = '/' + message.request.mediaid + '?fields=id,message,message_tags,picture,place,name,comments,reactions';

    let options = {
        uri: "https://graph.facebook.com" + path,
        method: 'GET',
        headers: {
            'Authorization': 'OAuth ' + message.social.facebook.token
        }
    };

    request(options)
        .then((response) => {
            let data = message;

            data.response = {
                platform: "facebook",
                type: "media",
                data: JSON.parse(response)
            };

            context.log(JSON.stringify(data, null, 4));
            context.bindings.out = data;
            context.done();
        })
        .catch((error) => context.log(error));
}<|endoftext|>


<commit_before>"use strict";

const expect = require('chai').expect;
const localNpmHelpers = require('../../../src/extract/resolve/localNpmHelpers');

describe("localNpmHelpers.getPackageJson", () => {
    it("returns null if the module does not exist", () => {
        expect(
            localNpmHelpers.getPackageJson(
                './module-does-not-exist'
            )
        ).to.be.null;
    });

    it("returns null if there's no package.json for the module (no basePath specified)", () => {
        expect(
            localNpmHelpers.getPackageJson(
                'test/extract/fixtures/deprecated-node-module/require-something-deprecated'
            )
        ).to.be.null;
    });

    it("returns null if there's no package.json for the module (basePath specified)", () => {
        expect(
            localNpmHelpers.getPackageJson(
                './require-something-deprecated',
                './fixtures/deprecated-node-module/'
            )
        ).to.be.null;
    });

    it("returns a package.json when there is one", () => {
        let lPackageJson = localNpmHelpers.getPackageJson(
            'chai'
        );

        expect(
            lPackageJson
        ).to.be.not.null;
        expect(lPackageJson.hasOwnProperty('name')).to.be.true;
        expect(lPackageJson.name).to.equal('chai');
    });

    it("returns a package.json when there is one ('local' node_modules)", () => {
        let lPackageJson = localNpmHelpers.getPackageJson(
            'deprecated-at-the-start-for-test-purposes',
            './test/extract/fixtures/deprecated-node-module/'
        );

        expect(
            lPackageJson
        ).to.be.not.null;
        expect(lPackageJson.hasOwnProperty('name')).to.be.true;
        expect(lPackageJson.name).to.equal('deprecated-at-the-start-for-test-purposes');
    });
});

describe("localNpmHelpers.getPackageRoot", () => {
    it("returns undefined if called without parameters", () => {
        expect(
            typeof localNpmHelpers.getPackageRoot()
        ).to.equal('undefined');
    });

    it("returns null if called without null", () => {
        expect(
            localNpmHelpers.getPackageRoot(null)
        ).to.equal(null);
    });

    it("locals unchanged: './localThing' => './localThing'", () => {
        expect(
            localNpmHelpers.getPackageRoot('./localThing')
        ).to.equal('./localThing');
    });

    it("returns the module name unchanged if called with a module name without a '/'", () => {
        expect(
            localNpmHelpers.getPackageRoot('lodash')
        ).to.equal('lodash');
    });

    it("returns the 'root' of the name when called with a module name with a '/'", () => {
        expect(
            localNpmHelpers.getPackageRoot('lodash/fp')
        ).to.equal('lodash');
    });

    it("@scoped/bla => @scoped/bla", () => {
        expect(
            localNpmHelpers.getPackageRoot('@scoped/bla')
        ).to.equal('@scoped/bla');
    });

    it("@scoped/bla/subthing => @scoped/bla", () => {
        expect(
            localNpmHelpers.getPackageRoot('@scoped/bla/subthing/sub/bla.json')
        ).to.equal('@scoped/bla');
    });

    it("@scoped => @scoped (note: weird edge case - shouldn't occur)", () => {
        expect(
            localNpmHelpers.getPackageRoot('@scoped')
        ).to.equal('@scoped');
    });
});

describe("localNpmHelpers.getLicense", () => {
    it("returns '' if the module does not exist", () => {
        expect(
            localNpmHelpers.getLicense('this-module-does-not-exist')
        ).to.equal('');
    });

    it("returns '' if the module does exist but has no associated package.json", () => {
        expect(
            localNpmHelpers.getLicense('./test/extract/resolve/fixtures/no-package-json')
        ).to.equal('');
    });

    it("returns '' if the module does exist, has a package.json, but no license field", () => {
        expect(
            localNpmHelpers.getLicense(
                'no-license',
                './test/extract/resolve/fixtures/licenses/'
            )
        ).to.equal('');
    });

    it("returns '' if the module exists, has a package.json, and a license field that is a boolean", () => {
        expect(
            localNpmHelpers.getLicense(
                'boolean-license',
                './test/extract/resolve/fixtures/licenses/'
            )
        ).to.equal('');
    });

    it("returns '' if the module exists, has a package.json, and a license field that is an object", () => {
        expect(
            localNpmHelpers.getLicense(
                'object-license',
                './test/extract/resolve/fixtures/licenses/'
            )
        ).to.equal('');
    });

    it("returns '' package.json has a licenses field that is an array (and no license field)", () => {
        expect(
            localNpmHelpers.getLicense(
                'array-license',
                './test/extract/resolve/fixtures/licenses/'
            )
        ).to.equal('');
    });

    it("returns the license if the module exists, has a package.json, and a string license field", () => {
        expect(
            localNpmHelpers.getLicense(
                'GPL-license',
                './test/extract/resolve/fixtures/licenses/'
            )
        ).to.equal('GPL-3.0');
    });

});
/* eslint no-unused-expressions: 0 */
<commit_msg>fix(test): correct a typo in a test description<commit_after>"use strict";

const expect = require('chai').expect;
const localNpmHelpers = require('../../../src/extract/resolve/localNpmHelpers');

describe("localNpmHelpers.getPackageJson", () => {
    it("returns null if the module does not exist", () => {
        expect(
            localNpmHelpers.getPackageJson(
                './module-does-not-exist'
            )
        ).to.be.null;
    });

    it("returns null if there's no package.json for the module (no basePath specified)", () => {
        expect(
            localNpmHelpers.getPackageJson(
                'test/extract/fixtures/deprecated-node-module/require-something-deprecated'
            )
        ).to.be.null;
    });

    it("returns null if there's no package.json for the module (basePath specified)", () => {
        expect(
            localNpmHelpers.getPackageJson(
                './require-something-deprecated',
                './fixtures/deprecated-node-module/'
            )
        ).to.be.null;
    });

    it("returns a package.json when there is one", () => {
        let lPackageJson = localNpmHelpers.getPackageJson(
            'chai'
        );

        expect(
            lPackageJson
        ).to.be.not.null;
        expect(lPackageJson.hasOwnProperty('name')).to.be.true;
        expect(lPackageJson.name).to.equal('chai');
    });

    it("returns a package.json when there is one ('local' node_modules)", () => {
        let lPackageJson = localNpmHelpers.getPackageJson(
            'deprecated-at-the-start-for-test-purposes',
            './test/extract/fixtures/deprecated-node-module/'
        );

        expect(
            lPackageJson
        ).to.be.not.null;
        expect(lPackageJson.hasOwnProperty('name')).to.be.true;
        expect(lPackageJson.name).to.equal('deprecated-at-the-start-for-test-purposes');
    });
});

describe("localNpmHelpers.getPackageRoot", () => {
    it("returns undefined if called without parameters", () => {
        expect(
            typeof localNpmHelpers.getPackageRoot()
        ).to.equal('undefined');
    });

    it("returns null if called with null", () => {
        expect(
            localNpmHelpers.getPackageRoot(null)
        ).to.equal(null);
    });

    it("locals unchanged: './localThing' => './localThing'", () => {
        expect(
            localNpmHelpers.getPackageRoot('./localThing')
        ).to.equal('./localThing');
    });

    it("returns the module name unchanged if called with a module name without a '/'", () => {
        expect(
            localNpmHelpers.getPackageRoot('lodash')
        ).to.equal('lodash');
    });

    it("returns the 'root' of the name when called with a module name with a '/'", () => {
        expect(
            localNpmHelpers.getPackageRoot('lodash/fp')
        ).to.equal('lodash');
    });

    it("@scoped/bla => @scoped/bla", () => {
        expect(
            localNpmHelpers.getPackageRoot('@scoped/bla')
        ).to.equal('@scoped/bla');
    });

    it("@scoped/bla/subthing => @scoped/bla", () => {
        expect(
            localNpmHelpers.getPackageRoot('@scoped/bla/subthing/sub/bla.json')
        ).to.equal('@scoped/bla');
    });

    it("@scoped => @scoped (note: weird edge case - shouldn't occur)", () => {
        expect(
            localNpmHelpers.getPackageRoot('@scoped')
        ).to.equal('@scoped');
    });
});

describe("localNpmHelpers.getLicense", () => {
    it("returns '' if the module does not exist", () => {
        expect(
            localNpmHelpers.getLicense('this-module-does-not-exist')
        ).to.equal('');
    });

    it("returns '' if the module does exist but has no associated package.json", () => {
        expect(
            localNpmHelpers.getLicense('./test/extract/resolve/fixtures/no-package-json')
        ).to.equal('');
    });

    it("returns '' if the module does exist, has a package.json, but no license field", () => {
        expect(
            localNpmHelpers.getLicense(
                'no-license',
                './test/extract/resolve/fixtures/licenses/'
            )
        ).to.equal('');
    });

    it("returns '' if the module exists, has a package.json, and a license field that is a boolean", () => {
        expect(
            localNpmHelpers.getLicense(
                'boolean-license',
                './test/extract/resolve/fixtures/licenses/'
            )
        ).to.equal('');
    });

    it("returns '' if the module exists, has a package.json, and a license field that is an object", () => {
        expect(
            localNpmHelpers.getLicense(
                'object-license',
                './test/extract/resolve/fixtures/licenses/'
            )
        ).to.equal('');
    });

    it("returns '' package.json has a licenses field that is an array (and no license field)", () => {
        expect(
            localNpmHelpers.getLicense(
                'array-license',
                './test/extract/resolve/fixtures/licenses/'
            )
        ).to.equal('');
    });

    it("returns the license if the module exists, has a package.json, and a string license field", () => {
        expect(
            localNpmHelpers.getLicense(
                'GPL-license',
                './test/extract/resolve/fixtures/licenses/'
            )
        ).to.equal('GPL-3.0');
    });

});
/* eslint no-unused-expressions: 0 */
<|endoftext|>