<commit_before>'use strict';

/**
 * Module dependencies
 */
var decouple = require('decouple');

/**
 * Privates
 */
var scrollTimeout;
var scrolling = false;
var doc = window.document;
var html = doc.documentElement;
var msPointerSupported = window.navigator.msPointerEnabled;
var touch = {
  'start': msPointerSupported ? 'MSPointerDown' : 'touchstart',
  'move': msPointerSupported ? 'MSPointerMove' : 'touchmove',
  'end': msPointerSupported ? 'MSPointerUp' : 'touchend'
};
var prefix = (function prefix() {
  var regex = /^(Webkit|Khtml|Moz|ms|O)(?=[A-Z])/;
  var styleDeclaration = doc.getElementsByTagName('script')[0].style;
  for (var prop in styleDeclaration) {
    if (regex.test(prop)) {
      return '-' + prop.match(regex)[0].toLowerCase() + '-';
    }
  }
  // Nothing found so far? Webkit does not enumerate over the CSS properties of the style object.
  // However (prop in style) returns the correct value, so we'll have to test for
  // the precence of a specific property
  if ('WebkitOpacity' in styleDeclaration) { return '-webkit-'; }
  if ('KhtmlOpacity' in styleDeclaration) { return '-khtml-'; }
  return '';
}());

/**
 * Slideout constructor
 */
function Slideout(options) {
  options = options || {};

  // Sets default values
  this._startOffsetX = 0;
  this._currentOffsetX = 0;
  this._opening = false;
  this._moved = false;
  this._opened = false;

  // Sets panel
  this.panel = options.panel;

  // Sets options
  this._fx = options.fx || 'ease';
  this._duration = options.duration || 300;
  this._tolerance = options.tolerance || 70;
  this._padding = options.padding || 256;

  // Init touch events
  this._initTouchEvents();
}

/**
 * Opens the slideout menu.
 */
Slideout.prototype.open = function() {
  var self = this;
  if (html.className.search('slideout-open') === -1) { html.className += ' slideout-open'; }
  this._translateXTo(this._padding);
  this._opened = true;
  setTimeout(function() {
    self.panel.style.transition = '';
  }, 350);
  return this;
};

/**
 * Closes slideout menu.
 */
Slideout.prototype.close = function() {
  var self = this;
  this._translateXTo(0);
  this._opened = false;
  setTimeout(function() {
    html.className = html.className.replace(/ slideout-open/, '');
    self.panel.style.transition = '';
  }, 350);
  return this;
};

/**
 * Toggles (open/close) slideout menu.
 */
Slideout.prototype.toggle = function() {
  this.isOpen() ? this.close() : this.open();
  return this;
};

/**
 * Returns true if the slideout is currently open, and false if it is closed.
 */
Slideout.prototype.isOpen = function() {
  return this._opened;
};

/**
 * Translates panel and updates currentOffset with a given X point
 */
Slideout.prototype._translateXTo = function(translateX) {
  this._currentOffsetX = translateX;
  this.panel.style.transition = prefix + 'transform ' + this._duration + 'ms ' + this._fx;
  this.panel.style[prefix + 'transform'] = 'translate3d(' + translateX + 'px, 0, 0)';
};

/**
 *
 */
Slideout.prototype._close = function(eve) {
  eve.preventDefault();
  eve.stopPropagation();
  this.close();
};

/**
 * Initializes touch event
 */
Slideout.prototype._initTouchEvents = function() {
  var self = this;

  /**
   * Decouple scroll event
   */
  decouple(doc, 'scroll', function(eve) {
    if (!self._moved) {
      clearTimeout(scrollTimeout);
      scrolling = true;
      scrollTimeout = setTimeout(function() {
        scrolling = false;
      }, 250);
    }
  });

  /**
   * Prevents touchmove event if slideout is moving
   */
  doc.addEventListener(touch.move, function(eve) {
    if (self._moved) {
      eve.preventDefault();
    }
  });

  /**
   * Resets values on touchstart
   */
  this.panel.addEventListener(touch.start, function(eve) {
    self._moved = false;
    self._opening = false;
    self._startOffsetX = eve.touches[0].pageX;
  });

  /**
   * Resets values on touchcancel
   */
  this.panel.addEventListener('touchcancel', function() {
    self._moved = false;
    self._opening = false;
    self._startOffsetX = eve.touches[0].pageX;
  });

  /**
   * Toggles slideout on touchend
   */
  this.panel.addEventListener(touch.end, function() {
    if (self._moved) {
      if (self._opening && Math.abs(self._currentOffsetX) > self._tolerance) {
        self.open();
      } else {
        self.close();
      }
    }
    self._moved = false;
  });

  /**
   * Translates panel on touchmove
   */
  this.panel.addEventListener(touch.move, function(eve) {

    if (scrolling) { return; }

    var dif_x = eve.touches[0].clientX - self._startOffsetX;
    var translateX = self._currentOffsetX = dif_x;

    if (translateX > self._padding) {
      return;
    }

    self._opening = true;

    if (Math.abs(dif_x) > 20) {

      if (self._opened && dif_x > 0 || !self._opened && dif_x < 0) {
        return;
      }

      if (!self._moved && html.className.search('slideout-open') === -1) {
        html.className += ' slideout-open';
      }

      if (dif_x <= 0) {
        translateX = dif_x + self._padding;
        self._opening = false;
      }

      self.panel.style[prefix + 'transform'] = 'translate3d(' + translateX + 'px, 0, 0)';

      self._moved = true;
    }

  });

};

/**
 * Expose Slideout
 */
module.exports = Slideout;
<commit_msg>Add improvements, fixes and remove deprecated code.<commit_after>'use strict';

/**
 * Module dependencies
 */
var decouple = require('decouple');

/**
 * Privates
 */
var scrollTimeout;
var scrolling = false;
var doc = window.document;
var html = doc.documentElement;
var msPointerSupported = window.navigator.msPointerEnabled;
var touch = {
  'start': msPointerSupported ? 'MSPointerDown' : 'touchstart',
  'move': msPointerSupported ? 'MSPointerMove' : 'touchmove',
  'end': msPointerSupported ? 'MSPointerUp' : 'touchend'
};
var prefix = (function prefix() {
  var regex = /^(Webkit|Khtml|Moz|ms|O)(?=[A-Z])/;
  var styleDeclaration = doc.getElementsByTagName('script')[0].style;
  for (var prop in styleDeclaration) {
    if (regex.test(prop)) {
      return '-' + prop.match(regex)[0].toLowerCase() + '-';
    }
  }
  // Nothing found so far? Webkit does not enumerate over the CSS properties of the style object.
  // However (prop in style) returns the correct value, so we'll have to test for
  // the precence of a specific property
  if ('WebkitOpacity' in styleDeclaration) { return '-webkit-'; }
  if ('KhtmlOpacity' in styleDeclaration) { return '-khtml-'; }
  return '';
}());

/**
 * Slideout constructor
 */
function Slideout(options) {
  options = options || {};

  // Sets default values
  this._startOffsetX = 0;
  this._currentOffsetX = 0;
  this._opening = false;
  this._moved = false;
  this._opened = false;
  this._preventOpen = false;

  // Sets panel
  this.panel = options.panel;
  this.menu = options.menu;

  // Sets  classnames
  this.panel.className += ' slideout-panel';
  this.menu.className += ' slideout-menu';

  // Sets options
  this._fx = options.fx || 'ease';
  this._duration = parseInt(options.duration, 10) || 300;
  this._tolerance = parseInt(options.tolerance, 10) || 70;
  this._padding = parseInt(options.padding, 10) || 256;

  // Init touch events
  this._initTouchEvents();
}

/**
 * Opens the slideout menu.
 */
Slideout.prototype.open = function() {
  var self = this;
  if (html.className.search('slideout-open') === -1) { html.className += ' slideout-open'; }
  this._translateXTo(this._padding);
  this._opened = true;
  setTimeout(function() {
    self.panel.style.transition = '';
  }, this._duration + 50);
  return this;
};

/**
 * Closes slideout menu.
 */
Slideout.prototype.close = function() {
  var self = this;
  if (!this.isOpen() && !this._opening) { return this; }
  this._translateXTo(0);
  this._opened = false;
  setTimeout(function() {
    html.className = html.className.replace(/ slideout-open/, '');
    self.panel.style.transition = '';
  }, this._duration + 50);
  return this;
};

/**
 * Toggles (open/close) slideout menu.
 */
Slideout.prototype.toggle = function() {
  return this.isOpen() ? this.close() : this.open();
};

/**
 * Returns true if the slideout is currently open, and false if it is closed.
 */
Slideout.prototype.isOpen = function() {
  return this._opened;
};

/**
 * Translates panel and updates currentOffset with a given X point
 */
Slideout.prototype._translateXTo = function(translateX) {
  this._currentOffsetX = translateX;
  this.panel.style.transition = prefix + 'transform ' + this._duration + 'ms ' + this._fx;
  this.panel.style[prefix + 'transform'] = 'translate3d(' + translateX + 'px, 0, 0)';
};

/**
 * Initializes touch event
 */
Slideout.prototype._initTouchEvents = function() {
  var self = this;

  /**
   * Decouple scroll event
   */
  decouple(doc, 'scroll', function() {
    if (!self._moved) {
      clearTimeout(scrollTimeout);
      scrolling = true;
      scrollTimeout = setTimeout(function() {
        scrolling = false;
      }, 250);
    }
  });

  /**
   * Prevents touchmove event if slideout is moving
   */
  doc.addEventListener(touch.move, function(eve) {
    if (self._moved) {
      eve.preventDefault();
    }
  });

  /**
   * Resets values on touchstart
   */
  this.panel.addEventListener(touch.start, function(eve) {
    self._moved = false;
    self._opening = false;
    self._startOffsetX = eve.touches[0].pageX;
    self._preventOpen = (!self.isOpen() && self.menu.clientWidth !== 0);
  });

  /**
   * Resets values on touchcancel
   */
  this.panel.addEventListener('touchcancel', function() {
    self._moved = false;
    self._opening = false;
  });

  /**
   * Toggles slideout on touchend
   */
  this.panel.addEventListener(touch.end, function() {
    if (self._moved) {
      (self._opening && Math.abs(self._currentOffsetX) > self._tolerance) ? self.open() : self.close();
    }
    self._moved = false;
  });

  /**
   * Translates panel on touchmove
   */
  this.panel.addEventListener(touch.move, function(eve) {
    if (scrolling) { return; }

    var dif_x = eve.touches[0].clientX - self._startOffsetX;
    var translateX = self._currentOffsetX = dif_x;

    if (Math.abs(translateX) > self._padding) { return; }

    if (Math.abs(dif_x) > 20) {
      self._opening = true;

      if (self._opened && dif_x > 0 || !self._opened && dif_x < 0) { return; }

      if (!self._moved && html.className.search('slideout-open') === -1) {
        html.className += ' slideout-open';
      }

      if (dif_x <= 0) {
        translateX = dif_x + self._padding;
        self._opening = false;
      }

      self.panel.style[prefix + 'transform'] = 'translate3d(' + translateX + 'px, 0, 0)';

      self._moved = true;
    }

  });

};

/**
 * Expose Slideout
 */
module.exports = Slideout;
<|endoftext|>


<commit_before>import React, { Component, PropTypes } from 'react';
import {
  DeviceEventEmitter, // android
  NativeAppEventEmitter, // ios
  NativeModules,
  Platform,
  StyleSheet,
  requireNativeComponent,
  View,
  ViewPropTypes
} from 'react-native';

const CameraManager = NativeModules.CameraManager || NativeModules.CameraModule;
const CAMERA_REF = 'camera';

function convertNativeProps(props) {
  const newProps = { ...props };
  if (typeof props.aspect === 'string') {
    newProps.aspect = Camera.constants.Aspect[props.aspect];
  }

  if (typeof props.flashMode === 'string') {
    newProps.flashMode = Camera.constants.FlashMode[props.flashMode];
  }

  if (typeof props.orientation === 'string') {
    newProps.orientation = Camera.constants.Orientation[props.orientation];
  }

  if (typeof props.torchMode === 'string') {
    newProps.torchMode = Camera.constants.TorchMode[props.torchMode];
  }

  if (typeof props.type === 'string') {
    newProps.type = Camera.constants.Type[props.type];
  }

  if (typeof props.captureQuality === 'string') {
    newProps.captureQuality = Camera.constants.CaptureQuality[props.captureQuality];
  }

  if (typeof props.captureMode === 'string') {
    newProps.captureMode = Camera.constants.CaptureMode[props.captureMode];
  }

  if (typeof props.captureTarget === 'string') {
    newProps.captureTarget = Camera.constants.CaptureTarget[props.captureTarget];
  }

  // do not register barCodeTypes if no barcode listener
  if (typeof props.onBarCodeRead !== 'function') {
    newProps.barCodeTypes = [];
  }

  newProps.barcodeScannerEnabled = typeof props.onBarCodeRead === 'function'

  return newProps;
}

export default class Camera extends Component {

  static constants = {
    Aspect: CameraManager.Aspect,
    BarCodeType: CameraManager.BarCodeType,
    Type: CameraManager.Type,
    CaptureMode: CameraManager.CaptureMode,
    CaptureTarget: CameraManager.CaptureTarget,
    CaptureQuality: CameraManager.CaptureQuality,
    Orientation: CameraManager.Orientation,
    FlashMode: CameraManager.FlashMode,
    TorchMode: CameraManager.TorchMode,
    Iso: CameraManager.Iso
  };

  static propTypes = {
    ...ViewPropTypes,
    aspect: PropTypes.oneOfType([
      PropTypes.string,
      PropTypes.number
    ]),
    captureAudio: PropTypes.bool,
    captureMode: PropTypes.oneOfType([
      PropTypes.string,
      PropTypes.number
    ]),
    captureQuality: PropTypes.oneOfType([
      PropTypes.string,
      PropTypes.number
    ]),
    captureTarget: PropTypes.oneOfType([
      PropTypes.string,
      PropTypes.number
    ]),
    defaultOnFocusComponent: PropTypes.bool,
    flashMode: PropTypes.oneOfType([
      PropTypes.string,
      PropTypes.number
    ]),
    keepAwake: PropTypes.bool,
    onBarCodeRead: PropTypes.func,
    barcodeScannerEnabled: PropTypes.bool,
    onFocusChanged: PropTypes.func,
    onZoomChanged: PropTypes.func,
    mirrorImage: PropTypes.bool,
    fixOrientation: PropTypes.bool,
    barCodeTypes: PropTypes.array,
    orientation: PropTypes.oneOfType([
      PropTypes.string,
      PropTypes.number
    ]),
    playSoundOnCapture: PropTypes.bool,
    torchMode: PropTypes.oneOfType([
      PropTypes.string,
      PropTypes.number
    ]),
    type: PropTypes.oneOfType([
      PropTypes.string,
      PropTypes.number
    ]),
    iso: PropTypes.string
  };

  static defaultProps = {
    aspect: CameraManager.Aspect.fill,
    type: CameraManager.Type.back,
    orientation: CameraManager.Orientation.auto,
    fixOrientation: false,
    captureAudio: false,
    captureMode: CameraManager.CaptureMode.still,
    captureTarget: CameraManager.CaptureTarget.cameraRoll,
    captureQuality: CameraManager.CaptureQuality.high,
    defaultOnFocusComponent: true,
    flashMode: CameraManager.FlashMode.off,
    playSoundOnCapture: true,
    torchMode: CameraManager.TorchMode.off,
    mirrorImage: false,
    barCodeTypes: Object.values(CameraManager.BarCodeType),
    iso: "200"
  };

  static checkDeviceAuthorizationStatus = CameraManager.checkDeviceAuthorizationStatus;
  static checkVideoAuthorizationStatus = CameraManager.checkVideoAuthorizationStatus;
  static checkAudioAuthorizationStatus = CameraManager.checkAudioAuthorizationStatus;

  setNativeProps(props) {
    this.refs[CAMERA_REF].setNativeProps(props);
  }

  constructor() {
    super();
    this.state = {
      isAuthorized: false,
      isRecording: false
    };
  }

  async componentWillMount() {
    this._addOnBarCodeReadListener()

    let { captureMode } = convertNativeProps({ captureMode: this.props.captureMode })
    let hasVideoAndAudio = this.props.captureAudio && captureMode === Camera.constants.CaptureMode.video
    let check = hasVideoAndAudio ? Camera.checkDeviceAuthorizationStatus : Camera.checkVideoAuthorizationStatus;

    if (check) {
      const isAuthorized = await check();
      this.setState({ isAuthorized });
    }
  }

  componentWillUnmount() {
    this._removeOnBarCodeReadListener()

    if (this.state.isRecording) {
      this.stopCapture();
    }
  }

  componentWillReceiveProps(newProps) {
    const { onBarCodeRead } = this.props
    if (onBarCodeRead !== newProps.onBarCodeRead) {
      this._addOnBarCodeReadListener(newProps)
    }
  }

  _addOnBarCodeReadListener(props) {
    const { onBarCodeRead } = props || this.props
    this._removeOnBarCodeReadListener()
    if (onBarCodeRead) {
      this.cameraBarCodeReadListener = Platform.select({
        ios: NativeAppEventEmitter.addListener('CameraBarCodeRead', this._onBarCodeRead),
        android: DeviceEventEmitter.addListener('CameraBarCodeReadAndroid',  this._onBarCodeRead)
      })
    }
  }
  _removeOnBarCodeReadListener() {
    const listener = this.cameraBarCodeReadListener
    if (listener) {
      listener.remove()
    }
  }

  render() {
    const style = [styles.base, this.props.style];
    const nativeProps = convertNativeProps(this.props);

    return <RCTCamera ref={CAMERA_REF} {...nativeProps} />;
  }

  _onBarCodeRead = (data) => {
    if (this.props.onBarCodeRead) {
      this.props.onBarCodeRead(data)
    }
  };

  capture(options) {
    const props = convertNativeProps(this.props);
    options = {
      audio: props.captureAudio,
      barCodeTypes: props.barCodeTypes,
      mode: props.captureMode,
      playSoundOnCapture: props.playSoundOnCapture,
      target: props.captureTarget,
      quality: props.captureQuality,
      type: props.type,
      title: '',
      description: '',
      mirrorImage: props.mirrorImage,
      fixOrientation: props.fixOrientation,
      iso: props.iso,
      ...options
    };

    if (options.mode === Camera.constants.CaptureMode.video) {
      options.totalSeconds = (options.totalSeconds > -1 ? options.totalSeconds : -1);
      options.preferredTimeScale = options.preferredTimeScale || 30;
      this.setState({ isRecording: true });
    }

    return CameraManager.capture(options);
  }

  stopCapture() {
    if (this.state.isRecording) {
      this.setState({ isRecording: false });
      return CameraManager.stopCapture();
    }
    return Promise.resolve("Not Recording.");
  }

  getFOV() {
    return CameraManager.getFOV();
  }

  hasFlash() {
    if (Platform.OS === 'android') {
      const props = convertNativeProps(this.props);
      return CameraManager.hasFlash({
        type: props.type
      });
    }
    return CameraManager.hasFlash();
  }
}

export const constants = Camera.constants;

const RCTCamera = requireNativeComponent('RCTCamera', Camera, {nativeOnly: {
  renderToHardwareTextureAndroid: true,
  accessibilityLabel: true,
  importantForAccessibility: true,
  accessibilityLiveRegion: true,
  accessibilityComponentType: true,
  onLayout: true,
  testID: true,
  iso: true
}});

const styles = StyleSheet.create({
  base: {},
});
<commit_msg>updated iso prop integration in /index.js<commit_after>import React, { Component, PropTypes } from 'react';
import {
  DeviceEventEmitter, // android
  NativeAppEventEmitter, // ios
  NativeModules,
  Platform,
  StyleSheet,
  requireNativeComponent,
  View,
  ViewPropTypes
} from 'react-native';

const CameraManager = NativeModules.CameraManager || NativeModules.CameraModule;
const CAMERA_REF = 'camera';

function convertNativeProps(props) {
  const newProps = { ...props };
  if (typeof props.aspect === 'string') {
    newProps.aspect = Camera.constants.Aspect[props.aspect];
  }

  if (typeof props.flashMode === 'string') {
    newProps.flashMode = Camera.constants.FlashMode[props.flashMode];
  }

  if (typeof props.orientation === 'string') {
    newProps.orientation = Camera.constants.Orientation[props.orientation];
  }

  if (typeof props.torchMode === 'string') {
    newProps.torchMode = Camera.constants.TorchMode[props.torchMode];
  }

  if (typeof props.type === 'string') {
    newProps.type = Camera.constants.Type[props.type];
  }

  if (typeof props.captureQuality === 'string') {
    newProps.captureQuality = Camera.constants.CaptureQuality[props.captureQuality];
  }

  if (typeof props.captureMode === 'string') {
    newProps.captureMode = Camera.constants.CaptureMode[props.captureMode];
  }

  if (typeof props.captureTarget === 'string') {
    newProps.captureTarget = Camera.constants.CaptureTarget[props.captureTarget];
  }

  // do not register barCodeTypes if no barcode listener
  if (typeof props.onBarCodeRead !== 'function') {
    newProps.barCodeTypes = [];
  }

  newProps.barcodeScannerEnabled = typeof props.onBarCodeRead === 'function'

  return newProps;
}

export default class Camera extends Component {

  static constants = {
    Aspect: CameraManager.Aspect,
    BarCodeType: CameraManager.BarCodeType,
    Type: CameraManager.Type,
    CaptureMode: CameraManager.CaptureMode,
    CaptureTarget: CameraManager.CaptureTarget,
    CaptureQuality: CameraManager.CaptureQuality,
    Orientation: CameraManager.Orientation,
    FlashMode: CameraManager.FlashMode,
    TorchMode: CameraManager.TorchMode,
    Iso: CameraManager.ISO
  };

  static propTypes = {
    ...ViewPropTypes,
    aspect: PropTypes.oneOfType([
      PropTypes.string,
      PropTypes.number
    ]),
    captureAudio: PropTypes.bool,
    captureMode: PropTypes.oneOfType([
      PropTypes.string,
      PropTypes.number
    ]),
    captureQuality: PropTypes.oneOfType([
      PropTypes.string,
      PropTypes.number
    ]),
    captureTarget: PropTypes.oneOfType([
      PropTypes.string,
      PropTypes.number
    ]),
    defaultOnFocusComponent: PropTypes.bool,
    flashMode: PropTypes.oneOfType([
      PropTypes.string,
      PropTypes.number
    ]),
    keepAwake: PropTypes.bool,
    onBarCodeRead: PropTypes.func,
    barcodeScannerEnabled: PropTypes.bool,
    onFocusChanged: PropTypes.func,
    onZoomChanged: PropTypes.func,
    mirrorImage: PropTypes.bool,
    fixOrientation: PropTypes.bool,
    barCodeTypes: PropTypes.array,
    orientation: PropTypes.oneOfType([
      PropTypes.string,
      PropTypes.number
    ]),
    playSoundOnCapture: PropTypes.bool,
    torchMode: PropTypes.oneOfType([
      PropTypes.string,
      PropTypes.number
    ]),
    type: PropTypes.oneOfType([
      PropTypes.string,
      PropTypes.number
    ]),
    iso: PropTypes.string
  };

  static defaultProps = {
    aspect: CameraManager.Aspect.fill,
    type: CameraManager.Type.back,
    orientation: CameraManager.Orientation.auto,
    fixOrientation: false,
    captureAudio: false,
    captureMode: CameraManager.CaptureMode.still,
    captureTarget: CameraManager.CaptureTarget.cameraRoll,
    captureQuality: CameraManager.CaptureQuality.high,
    defaultOnFocusComponent: true,
    flashMode: CameraManager.FlashMode.off,
    playSoundOnCapture: true,
    torchMode: CameraManager.TorchMode.off,
    mirrorImage: false,
    barCodeTypes: Object.values(CameraManager.BarCodeType),
    iso: CameraManager['200']
  };

  static checkDeviceAuthorizationStatus = CameraManager.checkDeviceAuthorizationStatus;
  static checkVideoAuthorizationStatus = CameraManager.checkVideoAuthorizationStatus;
  static checkAudioAuthorizationStatus = CameraManager.checkAudioAuthorizationStatus;

  setNativeProps(props) {
    this.refs[CAMERA_REF].setNativeProps(props);
  }

  constructor() {
    super();
    this.state = {
      isAuthorized: false,
      isRecording: false
    };
  }

  async componentWillMount() {
    this._addOnBarCodeReadListener()

    let { captureMode } = convertNativeProps({ captureMode: this.props.captureMode })
    let hasVideoAndAudio = this.props.captureAudio && captureMode === Camera.constants.CaptureMode.video
    let check = hasVideoAndAudio ? Camera.checkDeviceAuthorizationStatus : Camera.checkVideoAuthorizationStatus;

    if (check) {
      const isAuthorized = await check();
      this.setState({ isAuthorized });
    }
  }

  componentWillUnmount() {
    this._removeOnBarCodeReadListener()

    if (this.state.isRecording) {
      this.stopCapture();
    }
  }

  componentWillReceiveProps(newProps) {
    const { onBarCodeRead } = this.props
    if (onBarCodeRead !== newProps.onBarCodeRead) {
      this._addOnBarCodeReadListener(newProps)
    }
  }

  _addOnBarCodeReadListener(props) {
    const { onBarCodeRead } = props || this.props
    this._removeOnBarCodeReadListener()
    if (onBarCodeRead) {
      this.cameraBarCodeReadListener = Platform.select({
        ios: NativeAppEventEmitter.addListener('CameraBarCodeRead', this._onBarCodeRead),
        android: DeviceEventEmitter.addListener('CameraBarCodeReadAndroid',  this._onBarCodeRead)
      })
    }
  }
  _removeOnBarCodeReadListener() {
    const listener = this.cameraBarCodeReadListener
    if (listener) {
      listener.remove()
    }
  }

  render() {
    const style = [styles.base, this.props.style];
    const nativeProps = convertNativeProps(this.props);

    return <RCTCamera ref={CAMERA_REF} {...nativeProps} />;
  }

  _onBarCodeRead = (data) => {
    if (this.props.onBarCodeRead) {
      this.props.onBarCodeRead(data)
    }
  };

  capture(options) {
    const props = convertNativeProps(this.props);
    options = {
      audio: props.captureAudio,
      barCodeTypes: props.barCodeTypes,
      mode: props.captureMode,
      playSoundOnCapture: props.playSoundOnCapture,
      target: props.captureTarget,
      quality: props.captureQuality,
      type: props.type,
      title: '',
      description: '',
      mirrorImage: props.mirrorImage,
      fixOrientation: props.fixOrientation,
      iso: props.iso,
      ...options
    };

    if (options.mode === Camera.constants.CaptureMode.video) {
      options.totalSeconds = (options.totalSeconds > -1 ? options.totalSeconds : -1);
      options.preferredTimeScale = options.preferredTimeScale || 30;
      this.setState({ isRecording: true });
    }

    return CameraManager.capture(options);
  }

  stopCapture() {
    if (this.state.isRecording) {
      this.setState({ isRecording: false });
      return CameraManager.stopCapture();
    }
    return Promise.resolve("Not Recording.");
  }

  getFOV() {
    return CameraManager.getFOV();
  }

  hasFlash() {
    if (Platform.OS === 'android') {
      const props = convertNativeProps(this.props);
      return CameraManager.hasFlash({
        type: props.type
      });
    }
    return CameraManager.hasFlash();
  }
}

export const constants = Camera.constants;

const RCTCamera = requireNativeComponent('RCTCamera', Camera, {nativeOnly: {
  renderToHardwareTextureAndroid: true,
  accessibilityLabel: true,
  importantForAccessibility: true,
  accessibilityLiveRegion: true,
  accessibilityComponentType: true,
  onLayout: true,
  testID: true,
  iso: true
}});

const styles = StyleSheet.create({
  base: {},
});
<|endoftext|>


<commit_before>var async = require('async');
var _ = require('lodash');
var extend = require('extend');

module.exports = fancyPage;

function fancyPage(options, callback) {
  return new fancyPage.FancyPage(options, callback);
}

fancyPage.FancyPage = function(options, callback) {
  var self = this;

  // "Protected" properties. We want modules like the blog to be able
  // to access these, thus no variables defined in the closure
  self._apos = options.apos;
  self._pages = options.pages;
  self._app = options.app;
  self._options = options;
  self._schemas = options.schemas;

  // If set to true this type of page is always an "orphan"
  // (hidden from conventional navigation menus, but accessible)
  self.orphan = options.orphan;

  // Mix in the ability to serve assets and templates
  self._apos.mixinModuleAssets(self, 'fancyPage', __dirname, options);

  // These are "public" so the object can be passed directly to pages.addType
  self.name = options.name;
  if (!options.name) {
    throw "You must set the name option when calling the FancyPage superclass constructor";
  }
  self.label = options.label;
  if (!self.label) {
    throw "You must set the label option when calling the FancyPage superclass constructor";
  }
  self.pluralLabel = options.pluralLabel || (self.label + 's');

  self._typeCss = self._apos.cssName(self.name);

  // All partials generated via self.renderer can see these properties
  self._rendererGlobals = options.rendererGlobals || {};

  self._action = '/apos-' + self._apos.cssName(self.name);

  extend(true, self._rendererGlobals, {
    type: _.pick(self, [ 'name', 'label', '_action' ])
  });

  self.setBridge = function(modules) {
    self._bridge = modules;
  };

  // self.schema = extra fields
  self.schema = self._schemas.compose(options);

  // SEARCH AND VERSIONING SUPPORT

  // The default properties for fancy pages are already covered by the
  // default properties for pages in general. Extend this to add more
  // lines of diff-friendly text representing metadata relating to
  // this type of page. Always call the superclass version
  self.addDiffLines = function(page, lines) {
  };

  // Improve the search index by adding custom searchable texts.
  // Note that you do not need to override this method just to make
  // schema properties of type "text", "select", "area" or "singleton"
  // searchable, or to cover "title" and "tags."
  //
  // Extend this to add more search texts representing metadata relating to
  // this type of page. Example: texts.push({ weight: 20, text: page.address })
  //
  // The default search engine is very simple: searches that match
  // something weighted greater than 10 appear before everything else.

  self.addSearchTexts = function(page, texts) {
  };

  // Add a listener so we can contribute our own metadata to the set of lines used
  // for the diffs between versions. Pass an inline function so that self.addDiffLines
  // can be changed by a subclass of fancyPage (if we just assign it now, it'll be
  // the default version above no matter what).

  self._apos.addListener('diff', function(page, lines) {
    if (page.type === self.name) {
      self.addDiffLines(page, lines);
    }
  });

  self._apos.addListener('index', function(page, lines) {
    if (page.type === self.name) {
      self._schemas.indexFields(self.schema, page, lines);
      // Custom search indexing
      self.addSearchTexts(page, lines);
    }
  });

  // Push our assets at the last possible moment. This allows us
  // to make decisions about the data to be passed to assets based
  // on other modules that may not have been initialized yet when
  // we were first constructed

  self._apos.on('beforeEndAssets', function() {
    self.pushAllAssets();
  });

  self.pushAllAssets = function() {
    // Make sure that aposScripts and aposStylesheets summon our
    // browser-side UI assets for managing fancy pages

    // CUSTOM PAGE SETTINGS TEMPLATE
    self.pushAsset('template', options.pageSettingsTemplate || 'pageSettings', {
      when: 'user',
      data: {
        fields: self.schema,
        name: self.name,
        label: self.label,
        action: self._action,
        pageSettingsClass: 'apos-page-settings-' + self._apos.cssName(self.name)
      }
    });

    self.pushAsset('script', 'editor', { when: 'user' });
    self.pushAsset('script', 'content', { when: 'always' });

    // Modules that subclass fancy page more than once may wish to
    // push alternate editor and content javascript for the front end.
    // We push these in addition to, not instead of, the usual
    // base versions above so that they can subclass successfully
    if (options.editorScript && (options.editorScript !== 'editor')) {
      self.pushAsset('script', options.editorScript, { when: 'user' });
    }
    if (options.contentScript && (options.contentScript !== 'content')) {
      self.pushAsset('script', options.contentScript, { when: 'always' });
    }

    // We've decided not to push stylesheets that live in the core
    // Apostrophe modules, because we prefer to write LESS files in the
    // sandbox project that can share imports. But you can add these calls
    // back to your subclasses if you like keeping the LESS files with
    // the modules.

    // self.pushAsset('stylesheet', 'editor', { when: 'user' });
    // self.pushAsset('stylesheet', 'content', { when: 'always' });
  };

  // END OF MANAGER FUNCTIONALITY

  // Returns pages of this type that the current user is permitted to visit.
  //
  // Normally viewers reach these by browsing, however in some cases
  // you will want to know about all of the pages of a particular type.
  //
  // The result passed as the second argument to the callback is an
  // object with a "pages" property containing the pages. (Although
  // we currently pass no other properties, we may in the future,
  // and wish to be forwards-compatible.)
  //
  // CRITERIA
  //
  // The criteria argument is combined with the standard MongoDB
  // criteria for fetching pages via MongoDB's `$and` keyword.
  // This allows you to use any valid MongoDB criteria when
  // fetching pages.
  //
  // OPTIONS
  //
  // The `options` argument provides *everything offered by
  // the `apos.get` method's `options` argument*, plus the following:
  //
  // PERMALINKING
  //
  // The slug property is copied to the url property for the convenience
  // of code that deals with both snippets and pages.
  //
  // JOINS
  //
  // Carries out joins as specified by the schema.

  self.get = function(req, userCriteria, optionsArg, callback) {
    var options = {};
    var filterCriteria = {};
    var results = null;
    extend(true, options, optionsArg);

    // Default sort is alpha
    if (options.sort === undefined) {
      options.sort = { sortTitle: 1 };
    }
    // filterCriteria is the right place to build up criteria
    // specific to this method; we'll $and it with the user's
    // criteria before passing it on to apos.get
    filterCriteria.type = self.name;
    var fetch = options.fetch;
    var permalink = options.permalink;

    // Final criteria to pass to apos.get
    var criteria = {
      $and: [
        userCriteria,
        filterCriteria
      ]
    };

    return async.series([ query, join, permalinker ], function(err) {
      return callback(err, results);
    });

    function query(callback) {
      return self._apos.get(req, criteria, options, function(err, resultsArg) {
        if (err) {
          return callback(err);
        }
        results = resultsArg;
        return callback(null);
      });
    }

    function join(callback) {
      var withJoins = options.withJoins;
      return self._schemas.join(req, self.schema, results.pages, withJoins, callback);
    }

    function permalinker(callback) {
      _.each(results.pages, function(page) {
        page.url = page.slug;
      });
      return callback(null);
    }
  };

  // Get just one page. Otherwise identical to get.

  self.getOne = function(req, criteria, optionsArg, callback) {
    var options = {};
    extend(true, options, optionsArg);
    options.limit = 1;
    if (!options.skip) {
      options.skip = 0;
    }
    return self.get(req, criteria, options, function(err, results) {
      if (err) {
        return callback(err);
      }
      return callback(err, results.pages[0]);
    });
  };

  /**
   * Store a page of this type. By default this just wraps apos.putPage.
   * All parameters are required. This method is called when the user
   * saves page settings for a page of this type or creates a new page
   * of this type. Note that empty beforePutOne and afterPutOne methods are
   * provided for your overriding convenience. Keep in mind
   * that in most cases adding fields to the schema is easier.
   */
  self.putOne = function(req, slug, options, page, callback) {
    if (!page.type) {
      page.type = self.name;
    }
    return async.series({
      beforePutOne: function(callback) {
        return self.beforePutOne(req, slug, options, page, callback);
      },
      putPage: function(callback) {
        return self._apos.putPage(req, slug, options, page, callback);
      },
      afterPutOne: function(callback) {
        return self.afterPutOne(req, slug, options, page, callback);
      }
    }, callback);
  };

  /**
   * Automatically invoked before putOne() proceeds. Empty by default
   * for your overriding convenience.
   */
  self.beforePutOne = function(req, slug, options, page, callback) {
    return callback(null);
  };

  /**
   * Automatically invoked after putOne() updates the page. Empty by default
   * for your overriding convenience.
   */
  self.afterPutOne = function(req, slug, options, page, callback) {
    return callback(null);
  };

  // This is a loader function, for use with the `load` option of
  // the pages module's `serve` method. apostrophe-sites wires it up
  // automatically.

  self.loader = function(req, callback) {
    if (!options.greedy) {
      if (!req.page) {
        return callback(null);
      }
    }

    if (!req.bestPage) {
      return callback(null);
    }

    // If the page type doesn't share our type name
    // this page isn't relevant for us
    if (req.bestPage.type !== self.name) {
      return callback(null);
    }

    // Provide a custom context menu for this page type if specified
    // and editable
    if (self._options.contextMenu && req.bestPage._edit) {
      req.contextMenu = self._options.contextMenu;
    }

    async.series([joins, dispatch], callback);

    function joins(callback) {
      var withJoins = options.withJoins;
      return self._schemas.join(req, self.schema, [ req.bestPage ], null, callback);
    }

    function dispatch(callback) {
      // We consider a partial match to be good enough, depending on the
      // remainder of the URL
      req.page = req.bestPage;
      self.dispatch(req, callback);
    }
  };

  // Decide what to do based on the remainder of the URL. The default behavior is
  // just to render the page template with the same name as the module, and that
  // is usually what you want, even for fancyPages. But you can change this
  // behavior, see commented-out examples below.

  self.dispatch = function(req, callback) {
    // Now we know it's of the right type.

    // If I want to, I can override this to render via
    // a custom method. If I use self.renderer I can render
    // teplates that live in this module's views folder,
    // rather than templates in the project-level views/pages folder:

    // req.template = self.renderer('index')

    // I could also send a 404:
    // req.notfound = true;

    // Or redirect somewhere:
    // req.redirect = 'http://somewhere....';

    // The default behavior is to render the page template matching
    // the module name.

    return callback(null);
  };

  // Sanitize newly submitted page settings (never trust a browser)
  extend(true, self, {
    settings: {
      sanitize: function(data, callback) {
        var ok = {};
        self._schemas.convertFields(self.schema, 'form', data, ok);
        return callback(null, ok);
      }
    }
  });

  // Get ready to push information about this page type to the browser

  var browser = options.browser || {};
  self._browser = browser;
  var pages = browser.pages || 'aposPages';
  var construct = getBrowserConstructor();

  // Register this page type on the server
  self._pages.addType(self);

  // Data for the browser
  var args = {
    name: self.name,
    label: self.label,
    action: self._action,
    schema: self.schema,
    typeCss: self._typeCss,
    orphan: self.orphan
  };
  extend(true, args, browser.options || {});

  // Synthesize a constructor for this type on the browser side if there
  // isn't one. This allows trivial subclassing of snippets for cases where
  // no custom browser side code is actually needed
  self._apos.pushGlobalCallWhen('user', 'AposFancyPage.subclassIfNeeded(?, ?, ?)', getBrowserConstructor(), getBaseBrowserConstructor(), args);
  self._apos.pushGlobalCallWhen('user', '@.replaceType(?, new @(?))', pages, self.name, construct, args);

  function getBrowserConstructor() {
    return self._browser.construct || 'Apos' + self.name.charAt(0).toUpperCase() + self.name.substr(1);
  }

  // Figure out the name of the base class constructor on the browser side. If
  // it's not available set a dummy name; this will work out OK because this
  // typically means subclassing was done explicitly on the browser side.
  function getBaseBrowserConstructor() {
    return self._browser.baseConstruct || 'AposPresumablyExplicit';
  }

  if (callback) {
    // Invoke callback on next tick so that the constructor's return
    // value can be assigned to a variable in the same closure where
    // the callback resides
    process.nextTick(function() { return callback(null); });
  }
};

<commit_msg>Compatible with the new async convertFields<commit_after>var async = require('async');
var _ = require('lodash');
var extend = require('extend');

module.exports = fancyPage;

function fancyPage(options, callback) {
  return new fancyPage.FancyPage(options, callback);
}

fancyPage.FancyPage = function(options, callback) {
  var self = this;

  // "Protected" properties. We want modules like the blog to be able
  // to access these, thus no variables defined in the closure
  self._apos = options.apos;
  self._pages = options.pages;
  self._app = options.app;
  self._options = options;
  self._schemas = options.schemas;

  // If set to true this type of page is always an "orphan"
  // (hidden from conventional navigation menus, but accessible)
  self.orphan = options.orphan;

  // Mix in the ability to serve assets and templates
  self._apos.mixinModuleAssets(self, 'fancyPage', __dirname, options);

  // These are "public" so the object can be passed directly to pages.addType
  self.name = options.name;
  if (!options.name) {
    throw "You must set the name option when calling the FancyPage superclass constructor";
  }
  self.label = options.label;
  if (!self.label) {
    throw "You must set the label option when calling the FancyPage superclass constructor";
  }
  self.pluralLabel = options.pluralLabel || (self.label + 's');

  self._typeCss = self._apos.cssName(self.name);

  // All partials generated via self.renderer can see these properties
  self._rendererGlobals = options.rendererGlobals || {};

  self._action = '/apos-' + self._apos.cssName(self.name);

  extend(true, self._rendererGlobals, {
    type: _.pick(self, [ 'name', 'label', '_action' ])
  });

  self.setBridge = function(modules) {
    self._bridge = modules;
  };

  // self.schema = extra fields
  self.schema = self._schemas.compose(options);

  // SEARCH AND VERSIONING SUPPORT

  // The default properties for fancy pages are already covered by the
  // default properties for pages in general. Extend this to add more
  // lines of diff-friendly text representing metadata relating to
  // this type of page. Always call the superclass version
  self.addDiffLines = function(page, lines) {
  };

  // Improve the search index by adding custom searchable texts.
  // Note that you do not need to override this method just to make
  // schema properties of type "text", "select", "area" or "singleton"
  // searchable, or to cover "title" and "tags."
  //
  // Extend this to add more search texts representing metadata relating to
  // this type of page. Example: texts.push({ weight: 20, text: page.address })
  //
  // The default search engine is very simple: searches that match
  // something weighted greater than 10 appear before everything else.

  self.addSearchTexts = function(page, texts) {
  };

  // Add a listener so we can contribute our own metadata to the set of lines used
  // for the diffs between versions. Pass an inline function so that self.addDiffLines
  // can be changed by a subclass of fancyPage (if we just assign it now, it'll be
  // the default version above no matter what).

  self._apos.addListener('diff', function(page, lines) {
    if (page.type === self.name) {
      self.addDiffLines(page, lines);
    }
  });

  self._apos.addListener('index', function(page, lines) {
    if (page.type === self.name) {
      self._schemas.indexFields(self.schema, page, lines);
      // Custom search indexing
      self.addSearchTexts(page, lines);
    }
  });

  // Push our assets at the last possible moment. This allows us
  // to make decisions about the data to be passed to assets based
  // on other modules that may not have been initialized yet when
  // we were first constructed

  self._apos.on('beforeEndAssets', function() {
    self.pushAllAssets();
  });

  self.pushAllAssets = function() {
    // Make sure that aposScripts and aposStylesheets summon our
    // browser-side UI assets for managing fancy pages

    // CUSTOM PAGE SETTINGS TEMPLATE
    self.pushAsset('template', options.pageSettingsTemplate || 'pageSettings', {
      when: 'user',
      data: {
        fields: self.schema,
        name: self.name,
        label: self.label,
        action: self._action,
        pageSettingsClass: 'apos-page-settings-' + self._apos.cssName(self.name)
      }
    });

    self.pushAsset('script', 'editor', { when: 'user' });
    self.pushAsset('script', 'content', { when: 'always' });

    // Modules that subclass fancy page more than once may wish to
    // push alternate editor and content javascript for the front end.
    // We push these in addition to, not instead of, the usual
    // base versions above so that they can subclass successfully
    if (options.editorScript && (options.editorScript !== 'editor')) {
      self.pushAsset('script', options.editorScript, { when: 'user' });
    }
    if (options.contentScript && (options.contentScript !== 'content')) {
      self.pushAsset('script', options.contentScript, { when: 'always' });
    }

    // We've decided not to push stylesheets that live in the core
    // Apostrophe modules, because we prefer to write LESS files in the
    // sandbox project that can share imports. But you can add these calls
    // back to your subclasses if you like keeping the LESS files with
    // the modules.

    // self.pushAsset('stylesheet', 'editor', { when: 'user' });
    // self.pushAsset('stylesheet', 'content', { when: 'always' });
  };

  // END OF MANAGER FUNCTIONALITY

  // Returns pages of this type that the current user is permitted to visit.
  //
  // Normally viewers reach these by browsing, however in some cases
  // you will want to know about all of the pages of a particular type.
  //
  // The result passed as the second argument to the callback is an
  // object with a "pages" property containing the pages. (Although
  // we currently pass no other properties, we may in the future,
  // and wish to be forwards-compatible.)
  //
  // CRITERIA
  //
  // The criteria argument is combined with the standard MongoDB
  // criteria for fetching pages via MongoDB's `$and` keyword.
  // This allows you to use any valid MongoDB criteria when
  // fetching pages.
  //
  // OPTIONS
  //
  // The `options` argument provides *everything offered by
  // the `apos.get` method's `options` argument*, plus the following:
  //
  // PERMALINKING
  //
  // The slug property is copied to the url property for the convenience
  // of code that deals with both snippets and pages.
  //
  // JOINS
  //
  // Carries out joins as specified by the schema.

  self.get = function(req, userCriteria, optionsArg, callback) {
    var options = {};
    var filterCriteria = {};
    var results = null;
    extend(true, options, optionsArg);

    // Default sort is alpha
    if (options.sort === undefined) {
      options.sort = { sortTitle: 1 };
    }
    // filterCriteria is the right place to build up criteria
    // specific to this method; we'll $and it with the user's
    // criteria before passing it on to apos.get
    filterCriteria.type = self.name;
    var fetch = options.fetch;
    var permalink = options.permalink;

    // Final criteria to pass to apos.get
    var criteria = {
      $and: [
        userCriteria,
        filterCriteria
      ]
    };

    return async.series([ query, join, permalinker ], function(err) {
      return callback(err, results);
    });

    function query(callback) {
      return self._apos.get(req, criteria, options, function(err, resultsArg) {
        if (err) {
          return callback(err);
        }
        results = resultsArg;
        return callback(null);
      });
    }

    function join(callback) {
      var withJoins = options.withJoins;
      return self._schemas.join(req, self.schema, results.pages, withJoins, callback);
    }

    function permalinker(callback) {
      _.each(results.pages, function(page) {
        page.url = page.slug;
      });
      return callback(null);
    }
  };

  // Get just one page. Otherwise identical to get. Invokes get,
  // so you only need to override one.

  self.getOne = function(req, criteria, optionsArg, callback) {
    var options = {};
    extend(true, options, optionsArg);
    options.limit = 1;
    if (!options.skip) {
      options.skip = 0;
    }
    return self.get(req, criteria, options, function(err, results) {
      if (err) {
        return callback(err);
      }
      return callback(err, results.pages[0]);
    });
  };

  /**
   * Store a page of this type. By default this just wraps apos.putPage.
   * All parameters are required. This method is called when the user
   * saves page settings for a page of this type or creates a new page
   * of this type. Note that empty beforePutOne and afterPutOne methods are
   * provided for your overriding convenience. Keep in mind
   * that in most cases adding fields to the schema is easier.
   */
  self.putOne = function(req, slug, options, page, callback) {
    if (!page.type) {
      page.type = self.name;
    }
    return async.series({
      beforePutOne: function(callback) {
        return self.beforePutOne(req, slug, options, page, callback);
      },
      putPage: function(callback) {
        return self._apos.putPage(req, slug, options, page, callback);
      },
      afterPutOne: function(callback) {
        return self.afterPutOne(req, slug, options, page, callback);
      }
    }, callback);
  };

  /**
   * Automatically invoked before putOne() proceeds. Empty by default
   * for your overriding convenience.
   */
  self.beforePutOne = function(req, slug, options, page, callback) {
    return callback(null);
  };

  /**
   * Automatically invoked after putOne() updates the page. Empty by default
   * for your overriding convenience.
   */
  self.afterPutOne = function(req, slug, options, page, callback) {
    return callback(null);
  };

  // This is a loader function, for use with the `load` option of
  // the pages module's `serve` method. apostrophe-sites wires it up
  // automatically.

  self.loader = function(req, callback) {
    if (!options.greedy) {
      if (!req.page) {
        return callback(null);
      }
    }

    if (!req.bestPage) {
      return callback(null);
    }

    // If the page type doesn't share our type name
    // this page isn't relevant for us
    if (req.bestPage.type !== self.name) {
      return callback(null);
    }

    // Provide a custom context menu for this page type if specified
    // and editable
    if (self._options.contextMenu && req.bestPage._edit) {
      req.contextMenu = self._options.contextMenu;
    }

    async.series([joins, dispatch], callback);

    function joins(callback) {
      var withJoins = options.withJoins;
      return self._schemas.join(req, self.schema, [ req.bestPage ], null, callback);
    }

    function dispatch(callback) {
      // We consider a partial match to be good enough, depending on the
      // remainder of the URL
      req.page = req.bestPage;
      self.dispatch(req, callback);
    }
  };

  // Decide what to do based on the remainder of the URL. The default behavior is
  // just to render the page template with the same name as the module, and that
  // is usually what you want, even for fancyPages. But you can change this
  // behavior, see commented-out examples below.

  self.dispatch = function(req, callback) {
    // Now we know it's of the right type.

    // If I want to, I can override this to render via
    // a custom method. If I use self.renderer I can render
    // teplates that live in this module's views folder,
    // rather than templates in the project-level views/pages folder:

    // req.template = self.renderer('index')

    // I could also send a 404:
    // req.notfound = true;

    // Or redirect somewhere:
    // req.redirect = 'http://somewhere....';

    // The default behavior is to render the page template matching
    // the module name.

    return callback(null);
  };

  // Sanitize newly submitted page settings (never trust a browser)
  extend(true, self, {
    settings: {
      sanitize: function(req, data, callback) {
        var ok = {};
        return self._schemas.convertFields(req, self.schema, 'form', data, ok, function(err) {
          return callback(err, ok);
        });
      }
    }
  });

  // Get ready to push information about this page type to the browser

  var browser = options.browser || {};
  self._browser = browser;
  var pages = browser.pages || 'aposPages';
  var construct = getBrowserConstructor();

  // Register this page type on the server
  self._pages.addType(self);

  // Data for the browser
  var args = {
    name: self.name,
    label: self.label,
    action: self._action,
    schema: self.schema,
    typeCss: self._typeCss,
    orphan: self.orphan
  };
  extend(true, args, browser.options || {});

  // Synthesize a constructor for this type on the browser side if there
  // isn't one. This allows trivial subclassing of snippets for cases where
  // no custom browser side code is actually needed
  self._apos.pushGlobalCallWhen('user', 'AposFancyPage.subclassIfNeeded(?, ?, ?)', getBrowserConstructor(), getBaseBrowserConstructor(), args);
  self._apos.pushGlobalCallWhen('user', '@.replaceType(?, new @(?))', pages, self.name, construct, args);

  function getBrowserConstructor() {
    return self._browser.construct || 'Apos' + self.name.charAt(0).toUpperCase() + self.name.substr(1);
  }

  // Figure out the name of the base class constructor on the browser side. If
  // it's not available set a dummy name; this will work out OK because this
  // typically means subclassing was done explicitly on the browser side.
  function getBaseBrowserConstructor() {
    return self._browser.baseConstruct || 'AposPresumablyExplicit';
  }

  if (callback) {
    // Invoke callback on next tick so that the constructor's return
    // value can be assigned to a variable in the same closure where
    // the callback resides
    process.nextTick(function() { return callback(null); });
  }
};

<|endoftext|>


<commit_before>/*
Name:         pastecapi
Description:  Lightweight module for Pastec image recognition API
Author:       Franklin van de Meent (https://frankl.in)
Source code:  https://github.com/fvdm/nodejs-pastecapi
Feedback:     https://github.com/fvdm/nodejs-pastecapi/issues
License:      Unlicense (Public Domain, see LICENSE file)
*/

var httpreq = require ('httpreq');
var fs = require ('fs');

var config = {
  endpoint: 'http://localhost:4121',
  timeout: 5000
};

/**
 * Process response error
 *
 * @callback callback
 * @param message {string} - error.message
 * @param err {Error, string} - error.error
 * @param res {object} - httpreq response details
 * @param callback {function} - `function (err) {}`
 * @returns {void}
 */

function processError (message, err, res, callback) {
  var error = new Error (message);

  error.statusCode = res && res.statusCode;
  error.error = err;
  return callback (error);
}

/**
 * Process HTTP response
 *
 * @callback callback
 * @param err {Error, null} - httpreq error
 * @param res {object} - httpreq response details
 * @param callback {function} - `function (err, data) {}`
 * @returns {void}
 */

function processResponse (err, res, callback) {
  var data = res && res.body || '';

  if (err) {
    return processError ('request failed', err, res, callback);
  }

  try {
    data = JSON.parse (data);
  } catch (e) {
    return processError ('request failed', e, res, callback);
  }

  if (data.type && data.type.match (/(AUTHENTIFICATION_ERROR|NO_AUTH_KEY)/)) {
    return processError ('invalid authkey', data.type, res, callback);
  }

  return callback (null, data);
}

/**
 * Communicate with API
 *
 * @callback callback
 * @param {object} options
 * @param {string} options.method=GET - HTTP method
 * @param {string} options.path=/ - Request path, i.e. /index/searcher
 * @param {object} options.parameters - Request query parameters
 * @param {object} options.json - POST JSON data
 * @param {object} options.files - Upload files paths
 * @param {number} options.timeout=5000 - Override request timeout in ms
 * @param {function} callback - Callback function
 * @returns {void}
 */

function talk (options, callback) {
  if (config.indexid) {
    options.path = options.path.replace (/^\/index/, '/indexes/' + config.indexid);

    if (options.json && options.json.type === 'PING') {
      return callback (new Error ('endpoint does not allow ping'));
    }
  }

  options.url = config.endpoint + options.path;
  options.method = options.method || 'GET';
  options.timeout = options.timeout || config.timeout;
  options.headers = options.headers || {};
  options.headers ['User-Agent'] = 'pastecapi node/' + process.versions.node;

  if (config.authkey) {
    options.headers.AuthKey = config.authkey;
  }

  function doResponse (err, res) {
    processResponse (err, res, callback);
  }

  httpreq.doRequest (options, doResponse);
}

/**
 * Test API access
 *
 * @callback callback
 * @param {function} callback - Process result
 * @returns {void}
 */

function ping (callback) {
  var options = {
    method: 'POST',
    path: '/',
    json: {
      type: 'PING'
    }
  };

  talk (options, callback);
}

/**
 * Load index from file on server
 *
 * @param {string} indexPath - Path to .dat file on server
 * @param {function} callback - Process result
 * @returns {void}
 */

function loadIndex (indexPath, callback) {
  var options = {
    method: 'POST',
    path: '/index/io',
    json: {
      type: 'LOAD',
      index_path: indexPath
    }
  };

  talk (options, callback);
}

/**
 * Write index to file on server
 *
 * @callback callback
 * @param {string} indexPath - Path of .dat file on server
 * @param {function} callback - Process result
 * @returns {void}
 */

function writeIndex (indexPath, callback) {
  var options = {
    method: 'POST',
    path: '/index/io',
    json: {
      type: 'WRITE',
      index_path: indexPath
    }
  };

  talk (options, callback);
}

/**
 * Clear index on server
 *
 * @callback callback
 * @param {function} callback - Process result
 * @returns {void}
 */

function clearIndex (callback) {
  var options = {
    method: 'POST',
    path: '/index/io',
    json: {
      type: 'CLEAR'
    }
  };

  talk (options, callback);
}

/**
 * Match image against index
 *
 * @callback callback
 * @param {string|buffer} image - Buffer or local path to image
 * @param {function} callback - Process result
 * @returns {void}
 */

function searchIndex (image, callback) {
  var options = {
    method: 'POST',
    path: '/index/searcher',
    binary: true
  };

  // Binary pass through
  if (image instanceof Buffer) {
    options.body = image;
    options.headers = {
      'Content-Length': image.length,
      'Content-Type': 'image/jpeg'
    };

    return talk (options, callback);
  }

  // Read file to buffer
  fs.readFile (image, function (err, data) {
    if (err) {
      return callback (err);
    }

    options.body = data;
    options.headers = {
      'Content-Length': data.length,
      'Content-Type': 'image/jpeg'
    };

    return talk (options, callback);
  });
}

/**
 * Remove image signature from index
 *
 * @callback callback
 * @param {number} imageId - Index ID of image signature
 * @param {function} callback - Process result
 * @returns {void}
 */

function deleteImage (imageId, callback) {
  var options = {
    method: 'DELETE',
    path: '/index/images/' + imageId
  };

  talk (options, callback);
}

/**
 * Add image signature to index
 *
 * @callback callback
 * @param {string|buffer} image - Buffer or local path to image
 * @param {number} imageId - Index ID for image
 * @param {function} callback - Process result
 * @returns {void}
 */

function addImage (image, imageId, callback) {
  var options = {
    method: 'PUT',
    path: '/index/images/' + imageId,
    binary: true
  };

  // Binary pass through
  if (image instanceof Buffer) {
    options.body = image;
    options.headers = {
      'Content-Length': image.length,
      'Content-Type': 'image/jpeg'
    };

    return talk (options, callback);
  }

  // Read file to buffer
  fs.readFile (image, function (err, data) {
    if (err) {
      return callback (err);
    }

    options.body = data;
    options.headers = {
      'Content-Length': data.length,
      'Content-Type': 'image/jpeg'
    };

    return talk (options, callback);
  });
}

/**
 * Module setup
 *
 * @param {string} endpoint - REST API base URL
 * @param (string} authkey - API auth key (--auth-key flag)
 * @param {number} timeout - Request wait timeout in ms
 * @returns {object} - Module methods
 */

module.exports = function (endpoint, authkey, timeout) {
  if (typeof endpoint === 'object') {
    config.endpoint = endpoint.endpoint || config.endpoint;
    config.indexid = endpoint.indexid || null;
    config.authkey = endpoint.authkey || null;
    config.timeout = endpoint.timeout || config.timeout;
  } else {
    if (typeof authkey === 'number') {
      timeout = authkey;
      authkey = null;
    }

    config.endpoint = endpoint || config.endpoint;
    config.timeout = timeout || config.timeout;
    config.authkey = authkey || null;
  }

  return {
    ping,
    loadIndex,
    writeIndex,
    clearIndex,
    searchIndex,
    addImage,
    deleteImage
  };
};
<commit_msg>Refactor(style): Restore cleaner returns<commit_after>/*
Name:         pastecapi
Description:  Lightweight module for Pastec image recognition API
Author:       Franklin van de Meent (https://frankl.in)
Source code:  https://github.com/fvdm/nodejs-pastecapi
Feedback:     https://github.com/fvdm/nodejs-pastecapi/issues
License:      Unlicense (Public Domain, see LICENSE file)
*/

var httpreq = require ('httpreq');
var fs = require ('fs');

var config = {
  endpoint: 'http://localhost:4121',
  timeout: 5000
};

/**
 * Process response error
 *
 * @callback callback
 * @param message {string} - error.message
 * @param err {Error, string} - error.error
 * @param res {object} - httpreq response details
 * @param callback {function} - `function (err) {}`
 * @returns {void}
 */

function processError (message, err, res, callback) {
  var error = new Error (message);

  error.statusCode = res && res.statusCode;
  error.error = err;
  callback (error);
}

/**
 * Process HTTP response
 *
 * @callback callback
 * @param err {Error, null} - httpreq error
 * @param res {object} - httpreq response details
 * @param callback {function} - `function (err, data) {}`
 * @returns {void}
 */

function processResponse (err, res, callback) {
  var data = res && res.body || '';

  if (err) {
    processError ('request failed', err, res, callback);
    return;
  }

  try {
    data = JSON.parse (data);
  } catch (e) {
    processError ('request failed', e, res, callback);
    return;
  }

  if (data.type && data.type.match (/(AUTHENTIFICATION_ERROR|NO_AUTH_KEY)/)) {
    processError ('invalid authkey', data.type, res, callback);
    return;
  }

  callback (null, data);
}

/**
 * Communicate with API
 *
 * @callback callback
 * @param {object} options
 * @param {string} options.method=GET - HTTP method
 * @param {string} options.path=/ - Request path, i.e. /index/searcher
 * @param {object} options.parameters - Request query parameters
 * @param {object} options.json - POST JSON data
 * @param {object} options.files - Upload files paths
 * @param {number} options.timeout=5000 - Override request timeout in ms
 * @param {function} callback - Callback function
 * @returns {void}
 */

function talk (options, callback) {
  if (config.indexid) {
    options.path = options.path.replace (/^\/index/, '/indexes/' + config.indexid);

    if (options.json && options.json.type === 'PING') {
      callback (new Error ('endpoint does not allow ping'));
      return;
    }
  }

  options.url = config.endpoint + options.path;
  options.method = options.method || 'GET';
  options.timeout = options.timeout || config.timeout;
  options.headers = options.headers || {};
  options.headers ['User-Agent'] = 'pastecapi node/' + process.versions.node;

  if (config.authkey) {
    options.headers.AuthKey = config.authkey;
  }

  function doResponse (err, res) {
    processResponse (err, res, callback);
  }

  httpreq.doRequest (options, doResponse);
}

/**
 * Test API access
 *
 * @callback callback
 * @param {function} callback - Process result
 * @returns {void}
 */

function ping (callback) {
  var options = {
    method: 'POST',
    path: '/',
    json: {
      type: 'PING'
    }
  };

  talk (options, callback);
}

/**
 * Load index from file on server
 *
 * @param {string} indexPath - Path to .dat file on server
 * @param {function} callback - Process result
 * @returns {void}
 */

function loadIndex (indexPath, callback) {
  var options = {
    method: 'POST',
    path: '/index/io',
    json: {
      type: 'LOAD',
      index_path: indexPath
    }
  };

  talk (options, callback);
}

/**
 * Write index to file on server
 *
 * @callback callback
 * @param {string} indexPath - Path of .dat file on server
 * @param {function} callback - Process result
 * @returns {void}
 */

function writeIndex (indexPath, callback) {
  var options = {
    method: 'POST',
    path: '/index/io',
    json: {
      type: 'WRITE',
      index_path: indexPath
    }
  };

  talk (options, callback);
}

/**
 * Clear index on server
 *
 * @callback callback
 * @param {function} callback - Process result
 * @returns {void}
 */

function clearIndex (callback) {
  var options = {
    method: 'POST',
    path: '/index/io',
    json: {
      type: 'CLEAR'
    }
  };

  talk (options, callback);
}

/**
 * Match image against index
 *
 * @callback callback
 * @param {string|buffer} image - Buffer or local path to image
 * @param {function} callback - Process result
 * @returns {void}
 */

function searchIndex (image, callback) {
  var options = {
    method: 'POST',
    path: '/index/searcher',
    binary: true
  };

  // Binary pass through
  if (image instanceof Buffer) {
    options.body = image;
    options.headers = {
      'Content-Length': image.length,
      'Content-Type': 'image/jpeg'
    };

    talk (options, callback);
    return;
  }

  // Read file to buffer
  fs.readFile (image, function (err, data) {
    if (err) {
      callback (err);
      return;
    }

    options.body = data;
    options.headers = {
      'Content-Length': data.length,
      'Content-Type': 'image/jpeg'
    };

    talk (options, callback);
  });
}

/**
 * Remove image signature from index
 *
 * @callback callback
 * @param {number} imageId - Index ID of image signature
 * @param {function} callback - Process result
 * @returns {void}
 */

function deleteImage (imageId, callback) {
  var options = {
    method: 'DELETE',
    path: '/index/images/' + imageId
  };

  talk (options, callback);
}

/**
 * Add image signature to index
 *
 * @callback callback
 * @param {string|buffer} image - Buffer or local path to image
 * @param {number} imageId - Index ID for image
 * @param {function} callback - Process result
 * @returns {void}
 */

function addImage (image, imageId, callback) {
  var options = {
    method: 'PUT',
    path: '/index/images/' + imageId,
    binary: true
  };

  // Binary pass through
  if (image instanceof Buffer) {
    options.body = image;
    options.headers = {
      'Content-Length': image.length,
      'Content-Type': 'image/jpeg'
    };

    talk (options, callback);
    return;
  }

  // Read file to buffer
  fs.readFile (image, function (err, data) {
    if (err) {
      callback (err);
      return;
    }

    options.body = data;
    options.headers = {
      'Content-Length': data.length,
      'Content-Type': 'image/jpeg'
    };

    talk (options, callback);
  });
}

/**
 * Module setup
 *
 * @param {string} endpoint - REST API base URL
 * @param (string} authkey - API auth key (--auth-key flag)
 * @param {number} timeout - Request wait timeout in ms
 * @returns {object} - Module methods
 */

module.exports = function (endpoint, authkey, timeout) {
  if (typeof endpoint === 'object') {
    config.endpoint = endpoint.endpoint || config.endpoint;
    config.indexid = endpoint.indexid || null;
    config.authkey = endpoint.authkey || null;
    config.timeout = endpoint.timeout || config.timeout;
  } else {
    if (typeof authkey === 'number') {
      timeout = authkey;
      authkey = null;
    }

    config.endpoint = endpoint || config.endpoint;
    config.timeout = timeout || config.timeout;
    config.authkey = authkey || null;
  }

  return {
    ping,
    loadIndex,
    writeIndex,
    clearIndex,
    searchIndex,
    addImage,
    deleteImage
  };
};
<|endoftext|>


<commit_before>var util = require('util')
var child = require('child_process')
var path = require('path')
var debug = require('debug')('regedit')
var errors = require('./errors.js')
var os = require('os')

/*
 * 	Access the registry without using a specific os architecture, this means that on a 32bit process on a 64bit machine
 * 	when we access hklm\software we will actually be accessing hklm\software\wow6432node.
 */
var OS_ARCH_AGNOSTIC = 'A'

/*
 * 	Access the registry using a specific os architecture, but determine what the architecture is automatically
 * 	This means that accessing in order to access the 32bit software registry on a 64bit machine we will need to
 * 	use the key hklm\software\wow6432node
 */
var OS_ARCH_SPECIFIC = 'S'

/*
 * 	Access the registry using 32bit os architecture
 */
var OS_ARCH_32BIT = '32'

/*
 * 	Access the registry using 64bit os architecture, this will have no effect on 32bit process/machines
 */
var OS_ARCH_64BIT = '64'

module.exports.list = function (keys, callback) {
	executeCommand(prepareCommand('regList.wsf', keys, OS_ARCH_AGNOSTIC), callback)
}

module.exports.createKey = function (keys, callback) {
	executeCommand(prepareCommand('regCreateKey.wsf', keys, OS_ARCH_AGNOSTIC), callback)
}

module.exports.deleteKey = function (keys, callback) {
	executeCommand(prepareCommand('regDeleteKey.wsf', keys, OS_ARCH_AGNOSTIC), callback)
}

module.exports.putValue = function(map, callback) {

	var cmd = 'regPutValue.wsf ' + OS_ARCH_AGNOSTIC

	for (var key in map) {
		var values = map[key]
		for (var valueName in values) {
			var entry = values[valueName]
			var line = wrapDoubleQuotes(key) + ' ' + wrapDoubleQuotes(valueName) + ' '
						+ wrapDoubleQuotes(renderValueByType(entry.value, entry.type)) + ' '
						+ entry.type

			cmd += ' ' + line
		}
	}

	executeCommand(cmd, callback)
}

module.exports.arch = {}

module.exports.arch.list = function(keys, callback) {
	executeCommand(prepareCommand('regList.wsf', keys, OS_ARCH_SPECIFIC), callback)
}

module.exports.arch.list32 = function (keys, callback) {
	executeCommand(prepareCommand('regList.wsf', keys, OS_ARCH_32BIT), callback)
}

module.exports.arch.list64 = function (keys, callback) {
	executeCommand(prepareCommand('regList.wsf', keys, OS_ARCH_64BIT), callback)
}

function executeCommand(cmd, callback) {

	if (typeof callback !== 'function') {
		throw new Error('missing callback')
	}

	execChildProcess(cmd, callback)
}

function execChildProcess(cmd, callback) {
	cmd = 'cscript.exe //Nologo ' + path.join(__dirname, 'vbs', cmd)

	debug(cmd)

	child.exec(cmd, function (err, stdout, stderr) {

		if (err) {
			if (stdout) {
				console.log(stdout)
			}

			if (stderr) {
				console.error(stderr)
			}

			if (err.code in errors) {
				return callback(errors[err.code])
			} else {
				return callback(err)
			}
		}

		// in case we have stuff in stderr but no real error
		if (stderr) return callback(new Error(stderr))

		debug(stdout, stderr)

		var result
		try {
			result = JSON.parse(stdout)
		} catch (e) {
			result = stdout
		}

		callback(null, result)
	})
}

function renderValueByType(value, type) {
	type = type.toUpperCase()

	switch (type) {
		case 'REG_BINARY':
			if (!util.isArray(value)) {
				throw new Error('invalid value type ' + typeof (value) + ' for registry type REG_BINARY, please use an array of numbers')
			}
			return value.join(',')

		case 'REG_MULTI_SZ':
			if (!util.isArray(value)) {
				throw new Error('invalid value type ' + typeof (value) + ' for registry type REG_BINARY, please use an array of strings')
			}
			return value.join(',')

		default:
			return value
	}
}

function prepareCommand(cmd, args, architecture) {
	cmd += ' ' + architecture

	if (typeof args === 'string') {
		return cmd += ' ' + wrapDoubleQuotes(args)
	} else if (util.isArray(args)) {
		return cmd += ' ' + wrapItemsWithDoubleQuotes(args)
	} else {
		return cmd
	}
}

function wrapItemsWithDoubleQuotes(arr) {
	var result = ''

	for (var i = 0; i < arr.length; i++) {
		if (i > 0)
			result += ' '

		result += wrapDoubleQuotes(arr[i])
	}

	return result
}

/*
 * conditionally wrap items with double quotes if they aren't wrapped already
 */
function wrapDoubleQuotes(item) {
	if (item[0] !== '"' && item[item.length - 1] !== '"') {
		return '"' + item + '"'
	}

	return item
}<commit_msg>support running scripts from directories with spaces, migrate to execFile() instead of exec() - safer and saves the need to wrap everything in double quotes manually<commit_after>var util = require('util')
var child = require('child_process')
var path = require('path')
var debug = require('debug')('regedit')
var errors = require('./errors.js')
var os = require('os')

/*
 * 	Access the registry without using a specific os architecture, this means that on a 32bit process on a 64bit machine
 * 	when we access hklm\software we will actually be accessing hklm\software\wow6432node.
 */
var OS_ARCH_AGNOSTIC = 'A'

/*
 * 	Access the registry using a specific os architecture, but determine what the architecture is automatically
 * 	This means that accessing in order to access the 32bit software registry on a 64bit machine we will need to
 * 	use the key hklm\software\wow6432node
 */
var OS_ARCH_SPECIFIC = 'S'

/*
 * 	Access the registry using 32bit os architecture
 */
var OS_ARCH_32BIT = '32'

/*
 * 	Access the registry using 64bit os architecture, this will have no effect on 32bit process/machines
 */
var OS_ARCH_64BIT = '64'

module.exports.list = function (keys, architecture, callback) {
	if (typeof architecture === 'function') {
		callback = architecture
		architecture = OS_ARCH_AGNOSTIC
	}

	var args = toCommandArgs('regList.wsf', architecture, keys)
	execute(args, callback)
}

module.exports.createKey = function (keys, callback) {
	var args = toCommandArgs('regCreateKey.wsf', OS_ARCH_AGNOSTIC, keys)
	execute(args, callback)
}

module.exports.deleteKey = function (keys, callback) {
	var args = toCommandArgs('regDeleteKey.wsf', OS_ARCH_AGNOSTIC, keys)
	execute(args, callback)
}

module.exports.putValue = function(map, callback) {
	var args = baseCommand('regPutValue.wsf', OS_ARCH_AGNOSTIC)

	for (var key in map) {
		var values = map[key]
		for (var valueName in values) {
			var entry = values[valueName]
			args.push(key)
			args.push(valueName)
			args.push(renderValueByType(entry.value, entry.type))
			args.push(entry.type)
		}
	}

	execute(args, callback)
}

module.exports.arch = {}

module.exports.arch.list = function(keys, callback) {
	module.exports.list(keys, OS_ARCH_SPECIFIC, callback)
}

module.exports.arch.list32 = function (keys, callback) {
	module.exports.list(keys, OS_ARCH_32BIT, callback)
}

module.exports.arch.list64 = function (keys, callback) {
	module.exports.list(keys, OS_ARCH_64BIT, callback)
}

function execute(args, callback) {

	if (typeof callback !== 'function') {
		throw new Error('missing callback')
	}

	debug(args)

	child.execFile('cscript.exe', args, function (err, stdout, stderr) {

		if (err) {
			if (stdout) {
				console.log(stdout)
			}

			if (stderr) {
				console.error(stderr)
			}

			if (err.code in errors) {
				return callback(errors[err.code])
			} else {
				return callback(err)
			}
		}

		// in case we have stuff in stderr but no real error
		if (stderr) return callback(new Error(stderr))

		debug(stdout)

		var result
		try {
			result = JSON.parse(stdout)
		} catch (e) {
			result = stdout
		}

		callback(null, result)
	})
}

function renderValueByType(value, type) {
	type = type.toUpperCase()

	switch (type) {
		case 'REG_BINARY':
			if (!util.isArray(value)) {
				throw new Error('invalid value type ' + typeof (value) + ' for registry type REG_BINARY, please use an array of numbers')
			}
			return value.join(',')

		case 'REG_MULTI_SZ':
			if (!util.isArray(value)) {
				throw new Error('invalid value type ' + typeof (value) + ' for registry type REG_BINARY, please use an array of strings')
			}
			return value.join(',')

		default:
			return value
	}
}

function toCommandArgs(cmd, arch, keys) {
	var result = baseCommand(cmd, arch)
	if (typeof keys === 'string') {
		result.push(keys)
	} else if (util.isArray(keys)) {
		result = result.concat(keys)
	} else {
		debug('creating command without using keys %s', keys)
	}

	return result
}

function baseCommand(cmd, arch) {
	return ['/Nologo', path.join(__dirname, 'vbs', cmd), arch]
}<|endoftext|>


<commit_before>var Ractive = require('ractive');

var templateTable = "
" +
  "
" +
  "
{{#.headers:i}}{{.}}	{{/}}
" +
  "
" +
  "
" +
  "    {{#.rendered}}" +
  "      {{#.itemsView:i}}" +
  "
{{>tableRow}}
" +
  "      {{/}}" +
  "    {{/}}" +
  "
" +
  "
";

var templateDiv = "
" +
  "
{{#.headers:i}}
{{.}}
{{/}}
" +
  "
" +
  "    {{#.rendered}}" +
  "      {{#.itemsView:i}}" +
  "
{{>divRow}}
" +
  "      {{/}}" +
  "    {{/}}" +
  "
" +
  "
";

(function() {
  var getProp = function getProp(obj, path) {
    var parts = path.split('.');
    for (var i = 0; i < parts.length; i++) {
      if (!!!obj) return obj;
      var p = parts[i];
      obj = obj[p];
    }
    return obj;
  };

  var Grid;
  Grid = Ractive.extend({
    template: '{{#.table}}{{>table}}{{/}}{{^.table}}{{>div}}{{/}}',
    beforeInit: function(opts) {
      var transfer = ['class', 'style', 'id'];
      var str = '';
      for (var k in opts.data) if (transfer.indexOf(k) >= 0) str += ' ' + k + '="' + opts.data[k] + '"';
      opts.partials.table = templateTable.replace(/\|/, str);
      opts.partials.div = templateDiv.replace(/\|/, str);
      opts.data.sorts = { order: [] };
    },
    init: function() {
      var grid = this;
      grid.on('rowDoubleClicked', function(e, i) {
        grid.fire('rowSelected', grid.get('itemsView.' + i), i, e);
      });
      grid.on('rowClicked', function(e, i) {
        grid.set('currentIndex', i);
      });
      grid.on('selection', function(e) {
        var current = grid.get('currentIndex');
        if (typeof current !== 'number') return;
        var items = grid.get('itemsView');
        if (e.original.keyCode === 38 && current > 0) current--;
        else if (e.original.keyCode === 40) current++;
        else if (e.original.keyCode === 13) {
          grid.fire('rowSelected', grid.get('current'), current, e);
          e.original.preventDefault();
          return;
        }
        else return;
        e.original.preventDefault();
        grid.current(current);
      });
      grid.on('colDoubleClicked', function(e, arr) {
        grid.fire('colSelected', e, grid.get('itemsView.' + arr[0]), arr[1], arr[0]);
      });
      grid.on('headerClicked', function(e, i) {
        var sorts = grid.get('sorts');
        var state = sorts[i];
        var nstate;
        if (e.original.ctrlKey && grid.get('multisortable')) {
          nstate = 3;
          if (!!!state || state > 2) nstate = 1;
          else if (state === 1) nstate = 2;
          if (nstate > 0 && nstate < 3) {
            if (sorts.order.indexOf(i) < 0) sorts.order.push(i);
          }
          grid.set('sorts.' + i, nstate);
        } else if (grid.get('sortable')) {
          for (var s in sorts) {
            if (s !== 'order') grid.set('sorts.' + s, 3);
          }
          nstate = 3;
          if (!!!state || state > 2) nstate = 1;
          else if (state === 1) nstate = 2;
          else {
            grid.set('sorts.order', []);
          }
          if (nstate > 0 && nstate < 3) grid.set('sorts.order', [i]);
          grid.set('sorts.' + i, nstate);
        }
      });
    },
    data: {
      rendered: false,
      editingRow: function(idx) {
        return (this.get('editingRows') || []).indexOf(idx) >= 0;
      },
      sortDir: function(idx) {
        return this.get('sorts')[idx];
      },
      columnClass: function(idx) {
        return ((this.get('_columns') || [])[idx] || {}).class;
      },
      isCurrentRow: function(idx) {
        return this.get('currentIndex') === idx;
      },
      editingRows: [],
      _columns: undefined,
      _filter: undefined,
      items: undefined,
      currentIndex: undefined,
      sorts: { order: [] },
      sortable: true,
      multisortable: true,
      table: true
    },
    computed: {
      itemsView: function() {
        var grd = this;
        this.get('sorts');
        var filter = this.get('_filter');
        var sorts = this.get('sorting') || [];
        var items = (this.get('items') || []).slice(0);
        var columns = this.get('_columns' + '') || [];
        var flted, i;
        if (typeof filter === 'function') {
          flted = [];
          for (i = 0; i < items.length; i++) {
            if (filter(items[i])) flted.push(items[i]);
          }
          items = flted;
        }
        if (this.get('currentIndex' + '') >= items.length) this.set('currentIndex', items.length - 1);
        items.sort(function(a, b) {
          for (var i in sorts) {
            var s = sorts[i];
            var dir = 1;
            var ai = getProp(a, columns[s.field].path);
            var bi = getProp(b, columns[s.field].path);

            if (s.dir === 2) dir = -1;
            else if (s.dir > 2) continue;

            if (ai < bi) return -1 * dir;
            else if (ai > bi) return 1 * dir;
            else continue;
          }
          return 0;
        });
        return items;
      },
      current: function() {
        var items = this.get('itemsView');
        var idx = this.get('currentIndex');
        if (typeof idx !== 'number') return null;
        return (items || [])[idx];
      },
      sorting: function() {
        var res = [];
        var sorts = this.get('sorts');
        var orders = sorts.order || [];
        for (var i in orders) {
          res.push({ field: orders[i], dir: sorts[orders[i]] });
        }
        return res;
      },
      headers: function() {
        var cols = this.get('columns');
        var res = [];
        for (var i in cols) {
          var c = cols[i];
          if (!!c.label) res.push(c.label);
          else if (!!!c.compute) {
            var nm = c.path || 'Column ' + i;
            res.push(nm.substring(nm.lastIndexOf('.') + 1));
          } else res.push('Column ' + i);
        }
        return res;
      },
      columns: function() {
        var cols = this.get('_columns');
        if (cols === undefined) cols = this.columns();
        if (cols === undefined) return [];
        cols = cols.slice(0);
        cols.sort(function(a, b) {
          var ai = a.order || 0;
          var bi = b.order || 0;
          if (ai < bi) return -1;
          else if (ai > bi) return 1;
          else return 0;
        });
        return cols;
      }
    },
    partials: {
      tableRow: '',
      divRow: '',
      table: '',
      div: ''
    },
    columns: function(arr) {
      var i;
      if (!!!arr) { // init columns from items
        var items = this.get('items');
        if (!!!items) return [];
        var item = items.slice(0).pop();
        var res = [];
        if (!!item) {
          var okTypes = ['String', 'Number', 'Boolean', 'Date'];
          for (i in item) {
            if (item.hasOwnProperty(i) && !!item[i]) {
              var t = item[i].constructor.name;
              if (okTypes.indexOf(t) >= 0) res.push({ label: i, path: i });
            }
          }
        }
        return this.columns(res);
      } else {
        for (i = 0; i < arr.length; i++) if (!arr[i].hasOwnProperty('order')) arr[i].order = i;
        arr.sort(function(a, b) {
          var ai = a.order;
          var bi = b.order;
          if (ai < bi) return -1;
          else if (ai > bi) return 1;
          else return 0;
        });
        this.set('_columns', arr);
        var tstr = '', dstr = '';
        for (var c = 0; c < arr.length; c++) {
          tstr += '{{.' + arr[c].path + '}}	';
          dstr += '
{{.' + arr[c].path + '}}
';
        }
        this.partials.tableRow = tstr;
        this.partials.divRow = dstr;
        this.set('rendered', false);
        this.set('rendered', true);
        return arr;
      }
    },
    current: function(idx) {
      if (arguments.length === 0) return this.get('current')();
      var items = this.get('itemsView');
      if (idx < 0 || idx >= items.length) return;
      this.set('currentIndex', idx);
      var tbl = this.get('table');

      var n, p;
      if (tbl) {
        n = this.el.querySelector('tbody').children[idx];
        p = this.el;
      } else {
        n = this.el.querySelector('.rg-body').children[idx];
        if (n.style.overflow !== '') p = n;
        else p = this.el;
      }

      if (n.offsetTop
 p.scrollTop + p.clientHeight) p.scrollTop = (n.offsetTop - p.clientHeight / 2);
    },
    filter: function(fn) {
      this.set('_filter', fn);
      this.update('itemsView');
    }
  });

  module.exports = Grid;
})();
<commit_msg>Adds a helper for controling grid movement from an external filter input box.<commit_after>var Ractive = require('ractive');

var templateTable = "
" +
  "
" +
  "
{{#.headers:i}}{{.}}	{{/}}
" +
  "
" +
  "
" +
  "    {{#.rendered}}" +
  "      {{#.itemsView:i}}" +
  "
{{>tableRow}}
" +
  "      {{/}}" +
  "    {{/}}" +
  "
" +
  "
";

var templateDiv = "
" +
  "
{{#.headers:i}}
{{.}}
{{/}}
" +
  "
" +
  "    {{#.rendered}}" +
  "      {{#.itemsView:i}}" +
  "
{{>divRow}}
" +
  "      {{/}}" +
  "    {{/}}" +
  "
" +
  "
";

(function() {
  var getProp = function getProp(obj, path) {
    var parts = path.split('.');
    for (var i = 0; i < parts.length; i++) {
      if (!!!obj) return obj;
      var p = parts[i];
      obj = obj[p];
    }
    return obj;
  };

  var Grid;
  Grid = Ractive.extend({
    template: '{{#.table}}{{>table}}{{/}}{{^.table}}{{>div}}{{/}}',
    beforeInit: function(opts) {
      var transfer = ['class', 'style', 'id'];
      var str = '';
      for (var k in opts.data) if (transfer.indexOf(k) >= 0) str += ' ' + k + '="' + opts.data[k] + '"';
      opts.partials.table = templateTable.replace(/\|/, str);
      opts.partials.div = templateDiv.replace(/\|/, str);
      opts.data.sorts = { order: [] };
    },
    init: function() {
      var grid = this;
      grid.on('rowDoubleClicked', function(e, i) {
        grid.fire('rowSelected', grid.get('itemsView.' + i), i, e);
      });
      grid.on('rowClicked', function(e, i) {
        grid.set('currentIndex', i);
      });
      grid.on('selection', function(e) {
        var current = grid.get('currentIndex');
        if (typeof current !== 'number') return;
        var items = grid.get('itemsView');
        if (e.original.keyCode === 38 && current > 0) current--;
        else if (e.original.keyCode === 40) current++;
        else if (e.original.keyCode === 13) {
          grid.fire('rowSelected', grid.get('current'), current, e);
          e.original.preventDefault();
          return;
        }
        else return;
        e.original.preventDefault();
        grid.current(current);
      });
      grid.on('colDoubleClicked', function(e, arr) {
        grid.fire('colSelected', e, grid.get('itemsView.' + arr[0]), arr[1], arr[0]);
      });
      grid.on('headerClicked', function(e, i) {
        var sorts = grid.get('sorts');
        var state = sorts[i];
        var nstate;
        if (e.original.ctrlKey && grid.get('multisortable')) {
          nstate = 3;
          if (!!!state || state > 2) nstate = 1;
          else if (state === 1) nstate = 2;
          if (nstate > 0 && nstate < 3) {
            if (sorts.order.indexOf(i) < 0) sorts.order.push(i);
          }
          grid.set('sorts.' + i, nstate);
        } else if (grid.get('sortable')) {
          for (var s in sorts) {
            if (s !== 'order') grid.set('sorts.' + s, 3);
          }
          nstate = 3;
          if (!!!state || state > 2) nstate = 1;
          else if (state === 1) nstate = 2;
          else {
            grid.set('sorts.order', []);
          }
          if (nstate > 0 && nstate < 3) grid.set('sorts.order', [i]);
          grid.set('sorts.' + i, nstate);
        }
      });
    },
    data: {
      rendered: false,
      editingRow: function(idx) {
        return (this.get('editingRows') || []).indexOf(idx) >= 0;
      },
      sortDir: function(idx) {
        return this.get('sorts')[idx];
      },
      columnClass: function(idx) {
        return ((this.get('_columns') || [])[idx] || {}).class;
      },
      isCurrentRow: function(idx) {
        return this.get('currentIndex') === idx;
      },
      editingRows: [],
      _columns: undefined,
      _filter: undefined,
      items: undefined,
      currentIndex: undefined,
      sorts: { order: [] },
      sortable: true,
      multisortable: true,
      table: true
    },
    computed: {
      itemsView: function() {
        var grd = this;
        this.get('sorts');
        var filter = this.get('_filter');
        var sorts = this.get('sorting') || [];
        var items = (this.get('items') || []).slice(0);
        var columns = this.get('_columns' + '') || [];
        var flted, i;
        if (typeof filter === 'function') {
          flted = [];
          for (i = 0; i < items.length; i++) {
            if (filter(items[i])) flted.push(items[i]);
          }
          items = flted;
        }
        if (this.get('currentIndex' + '') >= items.length) this.set('currentIndex', items.length - 1);
        items.sort(function(a, b) {
          for (var i in sorts) {
            var s = sorts[i];
            var dir = 1;
            var ai = getProp(a, columns[s.field].path);
            var bi = getProp(b, columns[s.field].path);

            if (s.dir === 2) dir = -1;
            else if (s.dir > 2) continue;

            if (ai < bi) return -1 * dir;
            else if (ai > bi) return 1 * dir;
            else continue;
          }
          return 0;
        });
        return items;
      },
      current: function() {
        var items = this.get('itemsView');
        var idx = this.get('currentIndex');
        if (typeof idx !== 'number') return null;
        return (items || [])[idx];
      },
      sorting: function() {
        var res = [];
        var sorts = this.get('sorts');
        var orders = sorts.order || [];
        for (var i in orders) {
          res.push({ field: orders[i], dir: sorts[orders[i]] });
        }
        return res;
      },
      headers: function() {
        var cols = this.get('columns');
        var res = [];
        for (var i in cols) {
          var c = cols[i];
          if (!!c.label) res.push(c.label);
          else if (!!!c.compute) {
            var nm = c.path || 'Column ' + i;
            res.push(nm.substring(nm.lastIndexOf('.') + 1));
          } else res.push('Column ' + i);
        }
        return res;
      },
      columns: function() {
        var cols = this.get('_columns');
        if (cols === undefined) cols = this.columns();
        if (cols === undefined) return [];
        cols = cols.slice(0);
        cols.sort(function(a, b) {
          var ai = a.order || 0;
          var bi = b.order || 0;
          if (ai < bi) return -1;
          else if (ai > bi) return 1;
          else return 0;
        });
        return cols;
      }
    },
    partials: {
      tableRow: '',
      divRow: '',
      table: '',
      div: ''
    },
    columns: function(arr) {
      var i;
      if (!!!arr) { // init columns from items
        var items = this.get('items');
        if (!!!items) return [];
        var item = items.slice(0).pop();
        var res = [];
        if (!!item) {
          var okTypes = ['String', 'Number', 'Boolean', 'Date'];
          for (i in item) {
            if (item.hasOwnProperty(i) && !!item[i]) {
              var t = item[i].constructor.name;
              if (okTypes.indexOf(t) >= 0) res.push({ label: i, path: i });
            }
          }
        }
        return this.columns(res);
      } else {
        for (i = 0; i < arr.length; i++) if (!arr[i].hasOwnProperty('order')) arr[i].order = i;
        arr.sort(function(a, b) {
          var ai = a.order;
          var bi = b.order;
          if (ai < bi) return -1;
          else if (ai > bi) return 1;
          else return 0;
        });
        this.set('_columns', arr);
        var tstr = '', dstr = '';
        for (var c = 0; c < arr.length; c++) {
          tstr += '{{.' + arr[c].path + '}}	';
          dstr += '
{{.' + arr[c].path + '}}
';
        }
        this.partials.tableRow = tstr;
        this.partials.divRow = dstr;
        this.set('rendered', false);
        this.set('rendered', true);
        return arr;
      }
    },
    current: function(idx) {
      if (arguments.length === 0) return this.get('current')();
      var items = this.get('itemsView');
      if (idx < 0 || idx >= items.length) return;
      this.set('currentIndex', idx);
      var tbl = this.get('table');

      var n, p;
      if (tbl) {
        n = this.el.querySelector('tbody').children[idx];
        p = this.el;
      } else {
        n = this.el.querySelector('.rg-body').children[idx];
        if (n.style.overflow !== '') p = n;
        else p = this.el;
      }

      if (n.offsetTop
 p.scrollTop + p.clientHeight) p.scrollTop = (n.offsetTop - p.clientHeight / 2);
    },
    filter: function(fn) {
      this.set('_filter', fn);
      this.update('itemsView');
    },
    filterInputControl: function() {
      var grid = this;
      return function(e) {
        var k = e.original.keyCode;
        if (k === 13) {
          var cur = grid.get('current');
          if (!!cur) {
            grid.fire('rowSelected', cur, null, null);
            e.original.preventDefault();
            return;
          }
        }
        if (k === 38 || k === 40) {
          e.original.preventDefault();
          var current = grid.get('currentIndex');
          if (typeof current !== 'number') current = k === 38 ? grid.get('itemsView').length - 1 : 0;
          else current = k == 38 ? current - 1 : current + 1;
          grid.current(current);
          return;
        }
      };
    }
  });

  module.exports = Grid;
})();
<|endoftext|>


<commit_before>var io = require('socket.io'),
    fs = require('fs'),
    path = require('path');

exports.debug = false;

exports.attach = function (server) {
    var oldListeners = server.listeners('request').splice(0);
    server.removeAllListeners('request');
    server.on('request', function (req, res) {
        if (req.url !== '/shotgun/shotgun.client.js') {
            for (var i = 0, l = oldListeners.length; i < l; i++) {
                oldListeners[i].call(server, req, res);
            }
        }
        else {
            try {
                fs.readFile(path.join(__dirname, '/node_modules/socket.io/node_modules/socket.io-client/dist/socket.io.js'), function (err, socketIoClient) {
                    fs.readFile(path.join(__dirname, '/client/shotgun.client.js'), function (err, shotgunClient) {
                        fs.readFile(path.join(__dirname, '/client/jquery.cooltype.js'), function (err, jqueryCoolType) {
                            fs.readFile(path.join(__dirname, '/client/jquery.shotgunConsole.js'), function (err, jqueryShotgunConsole) {
                                res.writeHead(200, { 'Content-Type': 'application/javascript' });
                                res.end(socketIoClient + '\n\n' + shotgunClient + '\n\n' + jqueryCoolType + '\n\n' + jqueryShotgunConsole);
                            });
                        });
                    });
                });
            }
            catch (e) {
            }
        }
    });
    var sio = io.listen(server, { log: false });
    var args = [].splice.call(arguments,1);
    args.forEach(function (shell) {
        shell.helpers.setCookie = function (name, value, days) {
            if (!this.cookies) this.cookies = [];
            this.cookies.push({
                name: name,
                value: value,
                days: days
            });
        };
        sio.of('/' + shell.namespace)
            .on('connection', function (socket) {
                socket.on('execute', function (cmdStr, context, options) {
                    if (exports.debug) console.log('%s: %s', shell.namespace, cmdStr);
                    var result = shell.execute(cmdStr, context, options);
                    socket.emit('result', result);
                });
            });
    });
};<commit_msg>attach() now returns an object that exposes internal functionality such as access to the socket.io instance.<commit_after>var io = require('socket.io'),
    fs = require('fs'),
    path = require('path');

exports.debug = false;

exports.attach = function (server) {
    var oldListeners = server.listeners('request').splice(0);
    server.removeAllListeners('request');
    server.on('request', function (req, res) {
        if (req.url !== '/shotgun/shotgun.client.js') {
            for (var i = 0, l = oldListeners.length; i < l; i++) {
                oldListeners[i].call(server, req, res);
            }
        }
        else {
            try {
                fs.readFile(path.join(__dirname, '/node_modules/socket.io/node_modules/socket.io-client/dist/socket.io.js'), function (err, socketIoClient) {
                    fs.readFile(path.join(__dirname, '/client/shotgun.client.js'), function (err, shotgunClient) {
                        fs.readFile(path.join(__dirname, '/client/jquery.cooltype.js'), function (err, jqueryCoolType) {
                            fs.readFile(path.join(__dirname, '/client/jquery.shotgunConsole.js'), function (err, jqueryShotgunConsole) {
                                res.writeHead(200, { 'Content-Type': 'application/javascript' });
                                res.end(socketIoClient + '\n\n' + shotgunClient + '\n\n' + jqueryCoolType + '\n\n' + jqueryShotgunConsole);
                            });
                        });
                    });
                });
            }
            catch (e) {
            }
        }
    });
    var sio = io.listen(server, { log: false });
    var args = [].splice.call(arguments,1);
    args.forEach(function (shell) {
        shell.helpers.setCookie = function (name, value, days) {
            if (!this.cookies) this.cookies = [];
            this.cookies.push({
                name: name,
                value: value,
                days: days
            });
        };
        sio.of('/' + shell.namespace)
            .on('connection', function (socket) {
                socket.on('execute', function (cmdStr, context, options) {
                    if (exports.debug) console.log('%s: %s', shell.namespace, cmdStr);
                    var result = shell.execute(cmdStr, context, options);
                    socket.emit('result', result);
                });
            });
    });
    return {
        socketIo: sio
    };
};<|endoftext|>