<commit_before>var express = require('express');
var http = require('http');
var fs = require('fs');
var formidable = require('formidable');
var path = require('path');
var browserify = require('browserify-middleware');
var babelify = require('express-babelify-middleware');
var bodyParser = require('body-parser');
var spawn = require('child_process').spawn;
var mkdirp = require('mkdirp');

var app = express();
app.server = http.createServer(app);

app.use(bodyParser.urlencoded({
  extended: true
}));

// load parameter
var config = require('./config.json');
var config_dir = config.config_dir;
var upload_dir = config.upload_dir;

// initiate websockets
require('./websocket')(app);

// load gadgetron stuff
require('./gadgetron')(app, config);

// load User API
require('./api/userApi')(app, config);

// browserify
app.get('/js/gadgetronBundleApp.js', babelify('./public/js/main.js', {debug: true}));

// defines routes
// access to public folder
app.use(express.static('public'));

// load gadgetronStreamConfiguration API
require('./api/gadgetronStreamConfigurationApi')(app, config);

// route for getting content of a folder
app.get('/api/getFolder', function(req, res){
  var path = req.query.folderPath;
  mkdirp(path, function(error){
    if(error){
      app.broadcast(error);
      res.json(error);
    }
    else{
      fs.readdir(path, function(error, files) {
        res.json(files);
      });
    }
  })
});

// load fileToTrash API
require('./api/fileToTrashApi')(app, config);

// load database API
require('./api/databaseApi')(app, config);

// load upload API
require('./api/uploadApi')(app, config);

// load gadgetronIsmrmrdClient API
require('./api/gadgetronIsmrmrdClientApi')(app, config);

// start hdfview on server
app.get('/api/startHdfView', function(req, res) {
    var filePath = req.query.filePath;
    var success = true;
    var hdfViewer = spawn('hdfview',[filePath]);
    hdfViewer.stdout.on('data', function(data){
      if(data){
        app.broadcast(data.toString());
      }
    });
    hdfViewer.on('close', function(code){
      if(success){
        app.broadcast('hdfview opened ' + filePath, 'SUCCESS');
      }
      if(!res.headersSent){
        res.json({status: 'true'});
      }
    });
    hdfViewer.stderr.on('data', function(data){
      success = false;
      app.broadcast(data.toString(),'ERROR');
      if(!res.headersSent){
        res.json({status: 'false'});
      }
    });
    hdfViewer.on('error', function(error){
      app.broadcast('probabply hdfview is not installed', 'ERROR');
    })
});

app.server.listen(config.port);

// methods
// create folder
app.mkdir = function mkdir(pathDir){
  try{
    pathDir.split('/').forEach(function(dir, index, splits){
      const parent = splits.slice(0, index).join('/');
      const dirPath = path.resolve(parent, dir);
      if (!fs.existsSync(dirPath)) {
        fs.mkdirSync(dirPath);
      }
    });
  }
  catch(exception){
    if(exception.code != 'EEXIST'){
      throw exception;
    }
  }
}
<commit_msg>changed tab size<commit_after>var express = require('express');
var http = require('http');
var fs = require('fs');
var formidable = require('formidable');
var path = require('path');
var browserify = require('browserify-middleware');
var babelify = require('express-babelify-middleware');
var bodyParser = require('body-parser');
var spawn = require('child_process').spawn;
var mkdirp = require('mkdirp');

var app = express();
app.server = http.createServer(app);

app.use(bodyParser.urlencoded({
    extended: true
}));

// load parameter
var config = require('./config.json');

// initiate websockets
require('./websocket')(app);

// load gadgetron stuff
require('./gadgetron')(app, config);

// load User API
require('./api/userApi')(app, config);

// browserify
app.get('/js/gadgetronBundleApp.js', babelify('./public/js/main.js', {debug: true}));

// defines routes
// access to public folder
app.use(express.static('public'));

// load gadgetronStreamConfiguration API
require('./api/gadgetronStreamConfigurationApi')(app, config);

// route for getting content of a folder
app.get('/api/getFolder', function(req, res){
    var path = req.query.folderPath;
    mkdirp(path, function(error){
        if(error){
            app.broadcast(error);
            res.json(error);
        }
        else{
            fs.readdir(path, function(error, files) {
                res.json(files);
            });
        }
    })
});

// load fileToTrash API
require('./api/fileToTrashApi')(app, config);

// load database API
require('./api/databaseApi')(app, config);

// load upload API
require('./api/uploadApi')(app, config);

// load gadgetronIsmrmrdClient API
require('./api/gadgetronIsmrmrdClientApi')(app, config);

// start hdfview on server
app.get('/api/startHdfView', function(req, res) {
    var filePath = req.query.filePath;
    var success = true;
    var hdfViewer = spawn('hdfview',[filePath]);
    hdfViewer.stdout.on('data', function(data){
        if(data){
            app.broadcast(data.toString());
        }
    });
    hdfViewer.on('close', function(code){
        if(success){
            app.broadcast('hdfview opened ' + filePath, 'SUCCESS');
        }
        if(!res.headersSent){
            res.json({status: 'true'});
        }
    });
    hdfViewer.stderr.on('data', function(data){
        success = false;
        app.broadcast(data.toString(),'ERROR');
        if(!res.headersSent){
            res.json({status: 'false'});
        }
    });
    hdfViewer.on('error', function(error){
        app.broadcast('probabply hdfview is not installed', 'ERROR');
    })
});

app.server.listen(config.port);

// methods
// create folder
app.mkdir = function mkdir(pathDir){
    try{
    pathDir.split('/').forEach(function(dir, index, splits){
        const parent = splits.slice(0, index).join('/');
        const dirPath = path.resolve(parent, dir);
        if (!fs.existsSync(dirPath)) {
            fs.mkdirSync(dirPath);
        }
    });
    }
    catch(exception){
        if(exception.code != 'EEXIST'){
            throw exception;
        }
    }
}
<|endoftext|>


<commit_before>'use strict';

/**
 * An interface for a list of items.
 *
 * @constructor
 * @param {Array.<*>} values
 */

function DatalistInterface(values) {
    this.values = [];
    this.add.apply(this, values);
}

/**
 * Add all arguments.
 *
 * @this DatalistInterface
 * @param {...*} values
 * @return DatalistInterface Self.
 */

function add(/* values... */) {
    var self;

    self = this;

    self.values.push.apply(self.values, arguments);

    return self;
}

/**
 * Remove all arguments.
 *
 * @this DatalistInterface
 * @param {...*} values
 * @return DatalistInterface Self.
 */

function remove(/* values... */) {
    var values,
        index,
        position;

    values = this.values;
    index = arguments.length;

    while (index--) {
        position = values.indexOf(arguments[index]);

        if (position !== -1) {
            values.splice(position, 1);
        }
    }

    return this;
}

/**
 * Whether or not `value` is in context.
 *
 * @this DatalistInterface
 * @param {*} value
 * @return {boolean}
 */

function is(value) {
    return this.values.indexOf(value) !== -1;
}

/**
 * Get all values.
 *
 * @this DatalistInterface
 * @return {Array.<*>}
 */

function all() {
    return this.values.concat();
}

/**
 * Stringify all values.
 *
 * @this DatalistInterface
 * @return {string}
 */

function toString() {
    return this.values.toString();
}

/**
 * Expose methods on prototype.
 */

var datalistInterfacePrototype;

datalistInterfacePrototype = DatalistInterface.prototype;

datalistInterfacePrototype.add = add;
datalistInterfacePrototype.remove = remove;
datalistInterfacePrototype.is = is;
datalistInterfacePrototype.all = all;
datalistInterfacePrototype.valueOf = all;
datalistInterfacePrototype.toString = toString;

/**
 * Expose `DatalistInterface`.
 */

module.exports = DatalistInterface;
<commit_msg>Add `has()` method (same as `is()`)<commit_after>'use strict';

/**
 * An interface for a list of items.
 *
 * @constructor
 * @param {Array.<*>} values
 */

function DatalistInterface(values) {
    this.values = [];
    this.add.apply(this, values);
}

/**
 * Add all arguments.
 *
 * @this DatalistInterface
 * @param {...*} values
 * @return DatalistInterface Self.
 */

function add(/* values... */) {
    var self;

    self = this;

    self.values.push.apply(self.values, arguments);

    return self;
}

/**
 * Remove all arguments.
 *
 * @this DatalistInterface
 * @param {...*} values
 * @return DatalistInterface Self.
 */

function remove(/* values... */) {
    var values,
        index,
        position;

    values = this.values;
    index = arguments.length;

    while (index--) {
        position = values.indexOf(arguments[index]);

        if (position !== -1) {
            values.splice(position, 1);
        }
    }

    return this;
}

/**
 * Whether or not `value` is in context.
 *
 * @this DatalistInterface
 * @param {*} value
 * @return {boolean}
 */

function is(value) {
    return this.values.indexOf(value) !== -1;
}

/**
 * Get all values.
 *
 * @this DatalistInterface
 * @return {Array.<*>}
 */

function all() {
    return this.values.concat();
}

/**
 * Stringify all values.
 *
 * @this DatalistInterface
 * @return {string}
 */

function toString() {
    return this.values.toString();
}

/**
 * Expose methods on prototype.
 */

var datalistInterfacePrototype;

datalistInterfacePrototype = DatalistInterface.prototype;

datalistInterfacePrototype.add = add;
datalistInterfacePrototype.remove = remove;
datalistInterfacePrototype.is = is;
datalistInterfacePrototype.has = is;
datalistInterfacePrototype.all = all;
datalistInterfacePrototype.valueOf = all;
datalistInterfacePrototype.toString = toString;

/**
 * Expose `DatalistInterface`.
 */

module.exports = DatalistInterface;
<|endoftext|>


<commit_before>var tabbable = require('tabbable');

var listeningFocusTrap = null;

function focusTrap(element, userOptions) {
  var tabbableNodes = [];
  var nodeFocusedBeforeActivation = null;
  var active = false;

  var container = (typeof element === 'string')
    ? document.querySelector(element)
    : element;

  var config = userOptions || {};
  config.returnFocusOnDeactivate = (userOptions && userOptions.returnFocusOnDeactivate != undefined)
    ? userOptions.returnFocusOnDeactivate
    : true;
  config.escapeDeactivates = (userOptions && userOptions.escapeDeactivates != undefined)
    ? userOptions.escapeDeactivates
    : true;

  var trap = {
    activate: activate,
    deactivate: deactivate,
    pause: removeListeners,
    unpause: addListeners,
  };

  return trap;

  function activate(activateOptions) {
    var defaultedActivateOptions = {
      onActivate: (activateOptions && activateOptions.onActivate !== undefined)
        ? activateOptions.onActivate
        : config.onActivate,
    };

    active = true;
    nodeFocusedBeforeActivation = document.activeElement;

    if (defaultedActivateOptions.onActivate) {
      defaultedActivateOptions.onActivate();
    }

    addListeners();
    return trap;
  }

  function deactivate(deactivateOptions) {
    var defaultedDeactivateOptions = {
      returnFocus: (deactivateOptions && deactivateOptions.returnFocus != undefined)
        ? deactivateOptions.returnFocus
        : config.returnFocusOnDeactivate,
      onDeactivate: (deactivateOptions && deactivateOptions.onDeactivate !== undefined)
        ? deactivateOptions.onDeactivate
        : config.onDeactivate,
    };

    removeListeners();

    if (defaultedDeactivateOptions.onDeactivate) {
      defaultedDeactivateOptions.onDeactivate();
    }

    if (defaultedDeactivateOptions.returnFocus) {
      setTimeout(function() {
        tryFocus(nodeFocusedBeforeActivation);
      }, 0);
    }

    active = false;
    return this;
  }

  function addListeners() {
    if (!active) return;

    // There can be only one listening focus trap at a time
    if (listeningFocusTrap) {
      listeningFocusTrap.pause();
    }
    listeningFocusTrap = trap;

    updateTabbableNodes();
    tryFocus(firstFocusNode());
    document.addEventListener('focus', checkFocus, true);
    document.addEventListener('click', checkClick, true);
    document.addEventListener('mousedown', checkPointerDown, true);
    document.addEventListener('touchstart', checkPointerDown, true);
    document.addEventListener('keydown', checkKey, true);

    return trap;
  }

  function removeListeners() {
    if (!active || !listeningFocusTrap) return;

    document.removeEventListener('focus', checkFocus, true);
    document.removeEventListener('click', checkClick, true);
    document.removeEventListener('mousedown', checkPointerDown, true);
    document.removeEventListener('touchstart', checkPointerDown, true);
    document.removeEventListener('keydown', checkKey, true);

    listeningFocusTrap = null;

    return trap;
  }

  function firstFocusNode() {
    var node;

    if (!config.initialFocus) {
      node = tabbableNodes[0];
      if (!node) {
        throw new Error('You can\'t have a focus-trap without at least one focusable element');
      }
      return node;
    }

    node = (typeof config.initialFocus === 'string')
      ? document.querySelector(config.initialFocus)
      : config.initialFocus;
    if (!node) {
      throw new Error('`initialFocus` refers to no known node');
    }

    return node;
  }

  // This needs to be done on mousedown and touchstart instead of click
  // so that it precedes the focus event
  function checkPointerDown(e) {
    if (config.clickOutsideDeactivates) {
      deactivate({ returnFocus: false });
    }
  }

  function checkClick(e) {
    if (config.clickOutsideDeactivates) return;
    if (container.contains(e.target)) return;
    e.preventDefault();
    e.stopImmediatePropagation();
  }

  function checkFocus(e) {
    if (container.contains(e.target)) return;
    e.preventDefault();
    e.stopImmediatePropagation();
    e.target.blur();
  }

  function checkKey(e) {
    if (e.key === 'Tab' || e.keyCode === 9) {
      handleTab(e);
    }

    if (config.escapeDeactivates !== false && isEscapeEvent(e)) {
      deactivate();
    }
  }

  function handleTab(e) {
    e.preventDefault();
    updateTabbableNodes();
    var currentFocusIndex = tabbableNodes.indexOf(e.target);
    var lastTabbableNode = tabbableNodes[tabbableNodes.length - 1];
    var firstTabbableNode = tabbableNodes[0];

    if (e.shiftKey) {
      if (e.target === firstTabbableNode || tabbableNodes.indexOf(e.target) === -1) {
        return tryFocus(lastTabbableNode);
      }
      return tryFocus(tabbableNodes[currentFocusIndex - 1]);
    }

    if (e.target === lastTabbableNode) return tryFocus(firstTabbableNode);

    tryFocus(tabbableNodes[currentFocusIndex + 1]);
  }

  function updateTabbableNodes() {
    tabbableNodes = tabbable(container);
  }
}

function isEscapeEvent(e) {
  return e.key === 'Escape' || e.key === 'Esc' || e.keyCode === 27;
}

function tryFocus(node) {
  if (!node || !node.focus) return;
  node.focus();
  if (node.tagName.toLowerCase() === 'input') {
    node.select();
  }
}

module.exports = focusTrap;
<commit_msg>Prevent focus-trap from attempting to call 'blur' on 'document' (e.g., in Firefox)<commit_after>var tabbable = require('tabbable');

var listeningFocusTrap = null;

function focusTrap(element, userOptions) {
  var tabbableNodes = [];
  var nodeFocusedBeforeActivation = null;
  var active = false;

  var container = (typeof element === 'string')
    ? document.querySelector(element)
    : element;

  var config = userOptions || {};
  config.returnFocusOnDeactivate = (userOptions && userOptions.returnFocusOnDeactivate != undefined)
    ? userOptions.returnFocusOnDeactivate
    : true;
  config.escapeDeactivates = (userOptions && userOptions.escapeDeactivates != undefined)
    ? userOptions.escapeDeactivates
    : true;

  var trap = {
    activate: activate,
    deactivate: deactivate,
    pause: removeListeners,
    unpause: addListeners,
  };

  return trap;

  function activate(activateOptions) {
    var defaultedActivateOptions = {
      onActivate: (activateOptions && activateOptions.onActivate !== undefined)
        ? activateOptions.onActivate
        : config.onActivate,
    };

    active = true;
    nodeFocusedBeforeActivation = document.activeElement;

    if (defaultedActivateOptions.onActivate) {
      defaultedActivateOptions.onActivate();
    }

    addListeners();
    return trap;
  }

  function deactivate(deactivateOptions) {
    var defaultedDeactivateOptions = {
      returnFocus: (deactivateOptions && deactivateOptions.returnFocus != undefined)
        ? deactivateOptions.returnFocus
        : config.returnFocusOnDeactivate,
      onDeactivate: (deactivateOptions && deactivateOptions.onDeactivate !== undefined)
        ? deactivateOptions.onDeactivate
        : config.onDeactivate,
    };

    removeListeners();

    if (defaultedDeactivateOptions.onDeactivate) {
      defaultedDeactivateOptions.onDeactivate();
    }

    if (defaultedDeactivateOptions.returnFocus) {
      setTimeout(function() {
        tryFocus(nodeFocusedBeforeActivation);
      }, 0);
    }

    active = false;
    return this;
  }

  function addListeners() {
    if (!active) return;

    // There can be only one listening focus trap at a time
    if (listeningFocusTrap) {
      listeningFocusTrap.pause();
    }
    listeningFocusTrap = trap;

    updateTabbableNodes();
    tryFocus(firstFocusNode());
    document.addEventListener('focus', checkFocus, true);
    document.addEventListener('click', checkClick, true);
    document.addEventListener('mousedown', checkPointerDown, true);
    document.addEventListener('touchstart', checkPointerDown, true);
    document.addEventListener('keydown', checkKey, true);

    return trap;
  }

  function removeListeners() {
    if (!active || !listeningFocusTrap) return;

    document.removeEventListener('focus', checkFocus, true);
    document.removeEventListener('click', checkClick, true);
    document.removeEventListener('mousedown', checkPointerDown, true);
    document.removeEventListener('touchstart', checkPointerDown, true);
    document.removeEventListener('keydown', checkKey, true);

    listeningFocusTrap = null;

    return trap;
  }

  function firstFocusNode() {
    var node;

    if (!config.initialFocus) {
      node = tabbableNodes[0];
      if (!node) {
        throw new Error('You can\'t have a focus-trap without at least one focusable element');
      }
      return node;
    }

    node = (typeof config.initialFocus === 'string')
      ? document.querySelector(config.initialFocus)
      : config.initialFocus;
    if (!node) {
      throw new Error('`initialFocus` refers to no known node');
    }

    return node;
  }

  // This needs to be done on mousedown and touchstart instead of click
  // so that it precedes the focus event
  function checkPointerDown(e) {
    if (config.clickOutsideDeactivates) {
      deactivate({ returnFocus: false });
    }
  }

  function checkClick(e) {
    if (config.clickOutsideDeactivates) return;
    if (container.contains(e.target)) return;
    e.preventDefault();
    e.stopImmediatePropagation();
  }

  function checkFocus(e) {
    if (container.contains(e.target) || e.target === document) return;
    e.preventDefault();
    e.stopImmediatePropagation();
    e.target.blur();
  }

  function checkKey(e) {
    if (e.key === 'Tab' || e.keyCode === 9) {
      handleTab(e);
    }

    if (config.escapeDeactivates !== false && isEscapeEvent(e)) {
      deactivate();
    }
  }

  function handleTab(e) {
    e.preventDefault();
    updateTabbableNodes();
    var currentFocusIndex = tabbableNodes.indexOf(e.target);
    var lastTabbableNode = tabbableNodes[tabbableNodes.length - 1];
    var firstTabbableNode = tabbableNodes[0];

    if (e.shiftKey) {
      if (e.target === firstTabbableNode || tabbableNodes.indexOf(e.target) === -1) {
        return tryFocus(lastTabbableNode);
      }
      return tryFocus(tabbableNodes[currentFocusIndex - 1]);
    }

    if (e.target === lastTabbableNode) return tryFocus(firstTabbableNode);

    tryFocus(tabbableNodes[currentFocusIndex + 1]);
  }

  function updateTabbableNodes() {
    tabbableNodes = tabbable(container);
  }
}

function isEscapeEvent(e) {
  return e.key === 'Escape' || e.key === 'Esc' || e.keyCode === 27;
}

function tryFocus(node) {
  if (!node || !node.focus) return;
  node.focus();
  if (node.tagName.toLowerCase() === 'input') {
    node.select();
  }
}

module.exports = focusTrap;
<|endoftext|>


<commit_before>/**
 * @flow
 * */
import React, {Component} from 'react';
import {View, TextInput} from 'react-native';
import Immutable from 'immutable';
type P ={
    keyboardType?: string,
    placeholder?: string,
    autoFocus: boolean,
    value?: string,
    onPinCompleted: (string, Array<string>) => void,
}
type S={

}
export default class PinInput extends Component<void,P,S> {
    pinItemStyle: any;
    onPinCompleted: Function;
    pinItemProps: {};

    constructor(props) {
        super(props);
        this.props.autoFocus = props.autoFocus || true;
        this.props.placeholder = props.placeholder || props.placeHolder || '_';
        this.pinLength = this.props.pinLength || 4;
        this.state = {
            pins: Array(this.pinLength).fill(null)
        };
        let value = this.props.value;
        if (value) {
            if (value.length !== this.pinLength) {
                throw new Error(`pin length is not equal ${this.pinLength}`)
            }
            this.state.pins = Array.from(value)
        }
        this.pinInputItems = new Array(this.pinLength);
    }

    componentWillReceiveProps(props) {
        this.props.autoFocus = props.autoFocus || true;
    }
    setPin(pin: string) {
        if (pin) {
            if (pin.length !== this.pinLength) {
                throw new Error(`pin length is not equal ${this.pinLength}`)
            }
            let p = (this.props.placeholder || ' ');
            this.setState({pins: Array.from(pin).map(v => v && v !== p ? v : null)});
            if (this.props.autoFocus) {
                this.focusPin(this.pinLength - 1)
            }
        }
    }

    getPin(): string {
        let pinText = this.state.pins.map(v => {
            let p = this.props.placeholder || ' ';
            return v && v !== p ? v : p;
        }).join('');
        return pinText;
    }

    clearPin() {
        for (let i = 0; i < this.pinLength; i++) {
            this.blurPin(i)
        }
        let pins = Array(this.pinLength).fill(null);
        this.setState({pins: Immutable.List(pins).toArray()});
        if (this.props.autoFocus) {
            this.setState({pins: Immutable.List(pins).set(0, '').toArray()});
            this.focusPin(0)
        }
    }

    async onPinItemChanged(i, t) {
        let pins = Immutable.List(this.state.pins).set(i, t).toArray();
        await this.setStateAsync({pins: pins});
        if (!t) {
            return
        }
        if (this.isCompleted()) {
            //end
            if (this.props.onPinCompleted) {
                let pinText = this.state.pins.map(v => {
                    let p = this.props.placeholder || ' ';
                    return v && v !== p ? v : p;
                }).join('');
                this.props.onPinCompleted(pinText);
            }
            return
        }
        if (i + 1 < this.pinLength) {
            this.focusPin(i + 1);
        }
    }

    isCompleted() {
        return this.state.pins.filter(e => !e).length === 0;
    }

    focusPin(i) {
        this.refs[`pin_${(i)}`].focus();
    }

    blurPin(i) {
        this.refs[`pin_${i}`].blur();
    }

    render() {
        return (
            <View style={{flexDirection: 'row', justifyContent: 'space-around', alignItems: 'center'}}>
                {
                    this.state.pins.map((v, i) => {
                        return (
                            <TextInput
                                key={"pin_" + i}
                                ref={`pin_${i}`}
                                autoFocus={this.props.autoFocus && i === 0}
                                style={{
                                    padding: 2,
                                    margin: 2,
                                    borderColor: 'gray',
                                    borderWidth: 1,
                                    width: 30,
                                    height: 30,
                                    textAlign: 'center',
                                    ...this.props.pinItemStyle
                                }}
                                placeholder={this.props.placeholder || '_'}
                                enablesReturnKeyAutomatically={true}
                                keyboardType={(this.props.pinItemProps||{}).keyboardType || 'default'}
                                returnKeyType={(this.props.pinItemProps || {}).returnKeyType || 'default'}
                                secureTextEntry={(v && (this.props.pinItemProps || {}).secureTextEntry ) || false}
                                maxLength={1}
                                onFocus={async (e) => await this.onPinFocus(i)}
                                onChangeText={async (t) => {
                                    await this.onPinItemChanged(i, t);
                                }}
                                onKeyPress={(e) => this.onPinKeyPress(e, i)}
                                value={this.state.pins[i]}/>
                        )
                    })
                }
            </View>
        )

    }

    async onPinFocus(i) {
        await this.setStateAsync({pins: Immutable.List(this.state.pins).set(i, null).toArray()})
    }

    onPinKeyPress(e, i) {
        let key = e.nativeEvent.key;
        if (key === 'Backspace') {
                this.focusPin(Math.max(i - 1, 0))
        }
    }

    async setStateAsync(partialState) {
        return await new Promise(resolve => this.setState(partialState, () => resolve()))
    }
}<commit_msg>add setPins getPins onPinsCompleted<commit_after>/**
 * @flow
 * */
import React, {Component} from 'react';
import {View, TextInput} from 'react-native';
import Immutable from 'immutable';
type P ={
    keyboardType?: string,
    placeholder?: string,
    autoFocus: boolean,
    value?: string,
    onPinCompleted: (string) => void,
    onPinsCompleted: (Array<string>) => void,
}
type S={

}
export default class PinInput extends Component<void,P,S> {
    pinItemStyle: any;
    onPinCompleted: Function;
    pinItemProps: {};

    constructor(props) {
        super(props);
        this.props.autoFocus = props.autoFocus || true;
        this.props.placeholder = props.placeholder || props.placeHolder || '_';
        this.pinLength = this.props.pinLength || 4;
        this.state = {
            pins: Array(this.pinLength).fill(null)
        };
        let value = this.props.value;
        if (value) {
            if (value.length !== this.pinLength) {
                throw new Error(`pin length is not equal ${this.pinLength}`)
            }
            this.state.pins = Array.from(value)
        }
        this.pinInputItems = new Array(this.pinLength);
    }

    componentWillReceiveProps(props) {
        this.props.autoFocus = props.autoFocus || true;
    }

    /**
     * @deprecated use setPins
     * */
    setPin(pin: string) {
        if (pin) {
            if (pin.length !== this.pinLength) {
                throw new Error(`pin length is not equal ${this.pinLength}`)
            }
            let p = (this.props.placeholder || ' ');
            this.setState({pins: Array.from(pin).map(v => v && v !== p ? v : null)});
            if (this.props.autoFocus) {
                this.focusPin(this.pinLength - 1)
            }
        }
    }

    setPins(pins: Array<string>) {
        pins = pins || Array(this.pinLength).fill(null);
        if (pins.length !== this.pinLength) {
            throw new Error(`pin length is not equal ${this.pinLength}`)
        }
        this.setState({pins: pins});
        if (this.props.autoFocus) {
            this.focusPin(this.pinLength - 1)
        }
    }

    /**
     * @deprecated use getPins
     * */
    getPin(): string {
        let pinText = this.state.pins.map(v => {
            let p = this.props.placeholder || ' ';
            return v && v !== p ? v : p;
        }).join('');
        return pinText;
    }

    getPins(): Array<string> {
        return this.state.pins;
    }
    clearPin() {
        for (let i = 0; i < this.pinLength; i++) {
            this.blurPin(i)
        }
        let pins = Array(this.pinLength).fill(null);
        this.setState({pins: Immutable.List(pins).toArray()});
        if (this.props.autoFocus) {
            this.setState({pins: Immutable.List(pins).set(0, '').toArray()});
            this.focusPin(0)
        }
    }

    async onPinItemChanged(i, t) {
        let pins = Immutable.List(this.state.pins).set(i, t).toArray();
        await this.setStateAsync({pins: pins});
        if (!t) {
            return
        }
        if (this.isCompleted()) {
            if (this.props.onPinCompleted) {
                let pinText = this.state.pins.map(v => {
                    let p = this.props.placeholder || ' ';
                    return v && v !== p ? v : p;
                }).join('');
                this.props.onPinCompleted(pinText);
            }
            if (this.props.onPinsCompleted) {
                this.props.onPinsCompleted(this.state.pins)
            }
            return
        }
        if (i + 1 < this.pinLength) {
            this.focusPin(i + 1);
        }
    }

    isCompleted() {
        return this.state.pins.filter(e => !e).length === 0;
    }

    focusPin(i) {
        this.refs[`pin_${(i)}`].focus();
    }

    blurPin(i) {
        this.refs[`pin_${i}`].blur();
    }

    render() {
        return (
            <View style={{flexDirection: 'row', justifyContent: 'space-around', alignItems: 'center'}}>
                {
                    this.state.pins.map((v, i) => {
                        return (
                            <TextInput
                                key={"pin_" + i}
                                ref={`pin_${i}`}
                                autoFocus={this.props.autoFocus && i === 0}
                                style={{
                                    padding: 2,
                                    margin: 2,
                                    borderColor: 'gray',
                                    borderWidth: 1,
                                    width: 30,
                                    height: 30,
                                    textAlign: 'center',
                                    ...this.props.pinItemStyle
                                }}
                                placeholder={this.props.placeholder || '_'}
                                enablesReturnKeyAutomatically={true}
                                keyboardType={(this.props.pinItemProps||{}).keyboardType || 'default'}
                                returnKeyType={(this.props.pinItemProps || {}).returnKeyType || 'default'}
                                secureTextEntry={(v && (this.props.pinItemProps || {}).secureTextEntry ) || false}
                                maxLength={1}
                                onFocus={async (e) => await this.onPinFocus(i)}
                                onChangeText={async (t) => {
                                    await this.onPinItemChanged(i, t);
                                }}
                                onKeyPress={(e) => this.onPinKeyPress(e, i)}
                                value={this.state.pins[i]}/>
                        )
                    })
                }
            </View>
        )

    }

    async onPinFocus(i) {
        await this.setStateAsync({pins: Immutable.List(this.state.pins).set(i, null).toArray()})
    }

    onPinKeyPress(e, i) {
        let key = e.nativeEvent.key;
        if (key === 'Backspace') {
                this.focusPin(Math.max(i - 1, 0))
        }
    }

    async setStateAsync(partialState) {
        return await new Promise(resolve => this.setState(partialState, () => resolve()))
    }
}<|endoftext|>


<commit_before>/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

const path = require('path');
const mysql = require('mysql');
const buf = require('buf').hex;
const MysqlPatcher = require('mysql-patcher');

const AppError = require('../../error');
const config = require('../../config');
const logger = require('../../logging')('db.mysql');
const P = require('../../promise');
const patch = require('./patch');

function MysqlStore(options) {
  if (options.charset && options.charset !== 'UTF8_UNICODE_CI') {
    logger.warn('createDatabase', { charset: options.charset });
  } else {
    options.charset = 'UTF8_UNICODE_CI';
  }
  options.typeCast = function(field, next) {
    if (field.type === 'TINY' && field.length === 1) {
      return field.string() === '1';
    }
    return next();
  };
  this._pool = mysql.createPool(options);
}

// Apply patches up to the current patch level.
// This will also create the DB if it is missing.

function updateDbSchema(patcher) {
  logger.verbose('updateDbSchema', patcher.options);

  var d = P.defer();
  patcher.patch(function(err) {
    if (err) {
      logger.error('updateDbSchema', err);
      return d.reject(err);
    }
    d.resolve();
  });

  return d.promise;
}

// Sanity-check that we're working with a compatible patch level.

function checkDbPatchLevel(patcher) {
  logger.verbose('checkDbPatchLevel', patcher.options);

  var d = P.defer();

  patcher.readDbPatchLevel(function(err) {
    if (err) {
      logger.error('checkDbPatchLevel', err);
      return d.reject(err);
    }
    // We are only guaranteed to run correctly if we're at the current
    // patch level for this version of the code (the normal state of
    // affairs) or the one immediately above it (during a deployment).
    if (patcher.currentPatchLevel !== patch.level) {
      if (patcher.currentPatchLevel !== patch.level + 1) {
        err = 'unexpected db patch level: ' + patcher.currentPatchLevel;
        logger.error('checkDbPatchLevel', err);
        return d.reject(new Error(err));
      }
    }
    d.resolve();
  });

  return d.promise;
}

MysqlStore.connect = function mysqlConnect(options) {

  options.createDatabase = options.createSchema;
  options.dir = path.join(__dirname, 'patches');
  options.metaTable = 'dbMetadata';
  options.patchKey = 'schema-patch-level';
  options.patchLevel = patch.level;
  options.mysql = mysql;
  var patcher = new MysqlPatcher(options);

  return P.promisify(patcher.connect, patcher)().then(function() {
    if (options.createSchema) {
      return updateDbSchema(patcher);
    }
  }).then(function() {
    return checkDbPatchLevel(patcher);
  }).then(function() {
    return P.promisify(patcher.end, patcher)();
  }).then(function() {
    return new MysqlStore(options);
  });
};

const Q_AVATAR_INSERT = 'INSERT INTO avatars (id, url, userId, providerId) ' +
  'VALUES (?, ?, ?, ?)';
const Q_AVATAR_UPDATE = 'INSERT INTO avatar_selected (userId, avatarId) '
  + 'VALUES (?, ?) ON DUPLICATE KEY UPDATE avatarId = VALUES(avatarId)';
const Q_AVATAR_GET = 'SELECT * FROM avatars WHERE id=?';
const Q_SELECTED_AVATAR = 'SELECT avatars.* FROM avatars LEFT JOIN '
  + 'avatar_selected ON (avatars.id = avatar_selected.avatarId) WHERE '
  + 'avatars.userId=? AND avatar_selected.avatarId IS NOT NULL';
const Q_AVATAR_LIST = 'SELECT avatars.id, url, avatar_selected.avatarId '
  + 'IS NOT NULL AS selected FROM avatars LEFT JOIN avatar_selected ON '
  + '(avatars.id = avatar_selected.avatarId) WHERE avatars.userId=?';
const Q_AVATAR_DELETE = 'DELETE FROM avatars WHERE id=?';

const Q_PROVIDER_INSERT = 'INSERT INTO avatar_providers (name) VALUES (?)';
const Q_PROVIDER_GET_BY_NAME = 'SELECT * FROM avatar_providers WHERE name=?';
const Q_PROVIDER_GET_BY_ID = 'SELECT * FROM avatar_providers WHERE id=?';

const Q_PROFILE_DISPLAY_NAME_UPDATE = 'INSERT INTO profile ' +
  '(userId, displayName) VALUES (?, ?) ON DUPLICATE KEY UPDATE ' +
  'displayName = VALUES(displayName)';
const Q_PROFILE_DISPLAY_NAME_GET = 'SELECT displayName FROM profile ' +
  'WHERE userId=?';
const Q_PROFILE_DELETE = 'DELETE FROM profile WHERE userId=?';

function firstRow(rows) {
  return rows[0];
}

function releaseConn(connection) {
  connection.release();
}

MysqlStore.prototype = {

  ping: function ping() {
    logger.debug('ping');
    // see bluebird.using():
    // https://github.com/petkaantonov/bluebird/blob/master/API.md#resource-management
    return P.using(this._getConnection(), function(conn) {
      return new P(function(resolve, reject) {
        conn.ping(function(err) {
          if (err) {
            logger.error('ping', err);
            reject(err);
          } else {
            resolve();
          }
        });
      });
    });
  },

  addAvatar: function addAvatar(id, uid, url, provider, selected) {
    id = buf(id);
    uid = buf(uid);
    var store = this;
    return this.getProviderByName(provider).then(function(prov) {
      if (!prov) {
        throw AppError.unsupportedProvider(url);
      }
      var p = store._write(Q_AVATAR_INSERT, [id, url, uid, prov.id]);
      if (selected) {
        p = p.then(function() {
          return store._write(Q_AVATAR_UPDATE, [uid, id]);
        });
      }

      return p;
    });
  },

  getAvatar: function getAvatar(id) {
    return this._readOne(Q_AVATAR_GET, [buf(id)]);
  },

  getSelectedAvatar: function getSelectedAvatar(uid) {
    return this._readOne(Q_SELECTED_AVATAR, [buf(uid)]);
  },

  getAvatars: function getAvatars(uid) {
    return this._read(Q_AVATAR_LIST, [buf(uid)]);
  },

  deleteAvatar: function deleteAvatar(id) {
    return this._write(Q_AVATAR_DELETE, [buf(id)]);
  },

  addProvider: function addProvider(name) {
    return this._write(Q_PROVIDER_INSERT, [name]);
  },

  getProviderByName: function getProviderByName(name) {
    return this._readOne(Q_PROVIDER_GET_BY_NAME, [name]);
  },

  getProviderById: function getProviderById(id) {
    return this._readOne(Q_PROVIDER_GET_BY_ID, [id]);
  },

  setDisplayName: function setDisplayName(uid, displayName) {
    return this._write(Q_PROFILE_DISPLAY_NAME_UPDATE, [buf(uid), displayName]);
  },

  getDisplayName: function getDisplayName(uid) {
    return this._readOne(Q_PROFILE_DISPLAY_NAME_GET, [buf(uid)]);
  },

  removeProfile: function removeProfile(uid) {
    return this._write(Q_PROFILE_DELETE, [buf(uid)]);
  },

  _write: function _write(sql, params) {
    return this._query(this._pool, sql, params);
  },

  _read: function _read(sql, params) {
    return this._query(this._pool, sql, params);
  },

  _readOne: function _readOne(sql, params) {
    return this._read(sql, params).then(firstRow);
  },

  _getConnection: function _getConnection() {
    // see bluebird.using()/disposer():
    // https://github.com/petkaantonov/bluebird/blob/master/API.md#resource-management
    //
    // tl;dr: using() and disposer() ensures that the dispose method will
    // ALWAYS be called at the end of the promise stack, regardless of
    // various errors thrown. So this should ALWAYS release the connection.
    var pool = this._pool;
    return new P(function(resolve, reject) {
      pool.getConnection(function(err, conn) {
        if (err) {
          reject(err);
        } else {
          resolve(conn);
        }
      });
    }).disposer(releaseConn);
  },

  _query: function _query(connection, sql, params) {
    return new P(function(resolve, reject) {
      connection.query(sql, params || [], function(err, results) {
        if (err) {
          reject(err);
        } else {
          resolve(results);
        }
      });
    });
  }
};

if (config.get('env') === 'test') {
  MysqlStore.prototype._clear = function clear() {
    var store = this;
    return this._write('DELETE FROM avatar_selected;')
      .then(function() {
        return store._write('DELETE FROM avatars;');
      })
      .then(function() {
        return store._write('DELETE FROM avatar_providers;');
      });
  };
}

module.exports = MysqlStore;
<commit_msg>fix(mysql): fix patcher version check to enforce patch >= n<commit_after>/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

const path = require('path');
const mysql = require('mysql');
const buf = require('buf').hex;
const MysqlPatcher = require('mysql-patcher');

const AppError = require('../../error');
const config = require('../../config');
const logger = require('../../logging')('db.mysql');
const P = require('../../promise');
const patch = require('./patch');

function MysqlStore(options) {
  if (options.charset && options.charset !== 'UTF8_UNICODE_CI') {
    logger.warn('createDatabase', { charset: options.charset });
  } else {
    options.charset = 'UTF8_UNICODE_CI';
  }
  options.typeCast = function(field, next) {
    if (field.type === 'TINY' && field.length === 1) {
      return field.string() === '1';
    }
    return next();
  };
  this._pool = mysql.createPool(options);
}

// Apply patches up to the current patch level.
// This will also create the DB if it is missing.

function updateDbSchema(patcher) {
  logger.verbose('updateDbSchema', patcher.options);

  var d = P.defer();
  patcher.patch(function(err) {
    if (err) {
      logger.error('updateDbSchema', err);
      return d.reject(err);
    }
    d.resolve();
  });

  return d.promise;
}

// Sanity-check that we're working with a compatible patch level.

function checkDbPatchLevel(patcher) {
  logger.verbose('checkDbPatchLevel', patcher.options);

  var d = P.defer();

  patcher.readDbPatchLevel(function(err) {
    if (err) {
      logger.error('checkDbPatchLevel', err);
      return d.reject(err);
    }

    // We can run if we're at or above some patch level.  Should be
    // equal at initial deployment, and may be one or more higher
    // later on, due to database changes in preparation for the next
    // release.
    if (patcher.currentPatchLevel >= patch.level) {
      return d.resolve();
    }

    err = 'unexpected db patch level: ' + patcher.currentPatchLevel;
    return d.reject(new Error(err));
  });

  return d.promise;
}

MysqlStore.connect = function mysqlConnect(options) {

  options.createDatabase = options.createSchema;
  options.dir = path.join(__dirname, 'patches');
  options.metaTable = 'dbMetadata';
  options.patchKey = 'schema-patch-level';
  options.patchLevel = patch.level;
  options.mysql = mysql;
  var patcher = new MysqlPatcher(options);

  return P.promisify(patcher.connect, patcher)().then(function() {
    if (options.createSchema) {
      return updateDbSchema(patcher);
    }
  }).then(function() {
    return checkDbPatchLevel(patcher);
  }).then(function() {
    return P.promisify(patcher.end, patcher)();
  }).then(function() {
    return new MysqlStore(options);
  });
};

const Q_AVATAR_INSERT = 'INSERT INTO avatars (id, url, userId, providerId) ' +
  'VALUES (?, ?, ?, ?)';
const Q_AVATAR_UPDATE = 'INSERT INTO avatar_selected (userId, avatarId) '
  + 'VALUES (?, ?) ON DUPLICATE KEY UPDATE avatarId = VALUES(avatarId)';
const Q_AVATAR_GET = 'SELECT * FROM avatars WHERE id=?';
const Q_SELECTED_AVATAR = 'SELECT avatars.* FROM avatars LEFT JOIN '
  + 'avatar_selected ON (avatars.id = avatar_selected.avatarId) WHERE '
  + 'avatars.userId=? AND avatar_selected.avatarId IS NOT NULL';
const Q_AVATAR_LIST = 'SELECT avatars.id, url, avatar_selected.avatarId '
  + 'IS NOT NULL AS selected FROM avatars LEFT JOIN avatar_selected ON '
  + '(avatars.id = avatar_selected.avatarId) WHERE avatars.userId=?';
const Q_AVATAR_DELETE = 'DELETE FROM avatars WHERE id=?';

const Q_PROVIDER_INSERT = 'INSERT INTO avatar_providers (name) VALUES (?)';
const Q_PROVIDER_GET_BY_NAME = 'SELECT * FROM avatar_providers WHERE name=?';
const Q_PROVIDER_GET_BY_ID = 'SELECT * FROM avatar_providers WHERE id=?';

const Q_PROFILE_DISPLAY_NAME_UPDATE = 'INSERT INTO profile ' +
  '(userId, displayName) VALUES (?, ?) ON DUPLICATE KEY UPDATE ' +
  'displayName = VALUES(displayName)';
const Q_PROFILE_DISPLAY_NAME_GET = 'SELECT displayName FROM profile ' +
  'WHERE userId=?';
const Q_PROFILE_DELETE = 'DELETE FROM profile WHERE userId=?';

function firstRow(rows) {
  return rows[0];
}

function releaseConn(connection) {
  connection.release();
}

MysqlStore.prototype = {

  ping: function ping() {
    logger.debug('ping');
    // see bluebird.using():
    // https://github.com/petkaantonov/bluebird/blob/master/API.md#resource-management
    return P.using(this._getConnection(), function(conn) {
      return new P(function(resolve, reject) {
        conn.ping(function(err) {
          if (err) {
            logger.error('ping', err);
            reject(err);
          } else {
            resolve();
          }
        });
      });
    });
  },

  addAvatar: function addAvatar(id, uid, url, provider, selected) {
    id = buf(id);
    uid = buf(uid);
    var store = this;
    return this.getProviderByName(provider).then(function(prov) {
      if (!prov) {
        throw AppError.unsupportedProvider(url);
      }
      var p = store._write(Q_AVATAR_INSERT, [id, url, uid, prov.id]);
      if (selected) {
        p = p.then(function() {
          return store._write(Q_AVATAR_UPDATE, [uid, id]);
        });
      }

      return p;
    });
  },

  getAvatar: function getAvatar(id) {
    return this._readOne(Q_AVATAR_GET, [buf(id)]);
  },

  getSelectedAvatar: function getSelectedAvatar(uid) {
    return this._readOne(Q_SELECTED_AVATAR, [buf(uid)]);
  },

  getAvatars: function getAvatars(uid) {
    return this._read(Q_AVATAR_LIST, [buf(uid)]);
  },

  deleteAvatar: function deleteAvatar(id) {
    return this._write(Q_AVATAR_DELETE, [buf(id)]);
  },

  addProvider: function addProvider(name) {
    return this._write(Q_PROVIDER_INSERT, [name]);
  },

  getProviderByName: function getProviderByName(name) {
    return this._readOne(Q_PROVIDER_GET_BY_NAME, [name]);
  },

  getProviderById: function getProviderById(id) {
    return this._readOne(Q_PROVIDER_GET_BY_ID, [id]);
  },

  setDisplayName: function setDisplayName(uid, displayName) {
    return this._write(Q_PROFILE_DISPLAY_NAME_UPDATE, [buf(uid), displayName]);
  },

  getDisplayName: function getDisplayName(uid) {
    return this._readOne(Q_PROFILE_DISPLAY_NAME_GET, [buf(uid)]);
  },

  removeProfile: function removeProfile(uid) {
    return this._write(Q_PROFILE_DELETE, [buf(uid)]);
  },

  _write: function _write(sql, params) {
    return this._query(this._pool, sql, params);
  },

  _read: function _read(sql, params) {
    return this._query(this._pool, sql, params);
  },

  _readOne: function _readOne(sql, params) {
    return this._read(sql, params).then(firstRow);
  },

  _getConnection: function _getConnection() {
    // see bluebird.using()/disposer():
    // https://github.com/petkaantonov/bluebird/blob/master/API.md#resource-management
    //
    // tl;dr: using() and disposer() ensures that the dispose method will
    // ALWAYS be called at the end of the promise stack, regardless of
    // various errors thrown. So this should ALWAYS release the connection.
    var pool = this._pool;
    return new P(function(resolve, reject) {
      pool.getConnection(function(err, conn) {
        if (err) {
          reject(err);
        } else {
          resolve(conn);
        }
      });
    }).disposer(releaseConn);
  },

  _query: function _query(connection, sql, params) {
    return new P(function(resolve, reject) {
      connection.query(sql, params || [], function(err, results) {
        if (err) {
          reject(err);
        } else {
          resolve(results);
        }
      });
    });
  }
};

if (config.get('env') === 'test') {
  MysqlStore.prototype._clear = function clear() {
    var store = this;
    return this._write('DELETE FROM avatar_selected;')
      .then(function() {
        return store._write('DELETE FROM avatars;');
      })
      .then(function() {
        return store._write('DELETE FROM avatar_providers;');
      });
  };
}

module.exports = MysqlStore;
<|endoftext|>


<commit_before>function argString(args) {
  var asStrings = [];

  for (i = 0; i < args.length; i++) {
    if (typeof args[i] === 'string') {
      asStrings.push('\'' + args[i] + '\'');
    } else {
      asStrings.push(args[i]);
    }
  }

  return asStrings.join(', ');
}

function unexpectedFunctionCall(mock, args) {
  return new Error('unexpected function call ' + mock._name + '(' + argString(args) + ')');
}

function defaultMockHandler() {
  throw unexpectedFunctionCall(this, Array.prototype.slice.call(arguments));
}

var mockHandler = defaultMockHandler;

function ExpectedCall(mock, args) {
  return expectedCall = {
    mock: mock,
    met: false,
    args: args || [],
    complete: function() {
      this.met = true;
    },
    isComplete: function() {
      return this.met;
    },
    matches: function(mock, args) {
      if (mock === expectedCall.mock) {
        if (args.length !== expectedCall.args.length) {
          return false;
        }

        for (i = 0; i < args.length; i++) {
          if (args[i] !== expectedCall.args[i]) {
            return false;
          }
        }

        return true;
      }
    }
  };
}

module.exports = {
  mockFunction: function mockFunction(name) {
    var expectedCalls = [];

    function when(thunk) {
      mockHandler = function mockHandler() {
        var mock = this;
        var matchedExpectation;
        var args = Array.prototype.slice.call(arguments);

        expectedCalls.some(function(expectedCall) {
          if (expectedCall.matches(mock, args)) {
            expectedCall.complete();
            matchedExpectation = expectedCall;
            return false;
          }

          return true;
        });

        if (!matchedExpectation) {
          throw unexpectedFunctionCall(mock, args);
        }

        return matchedExpectation.returnValue;
      }

      thunk();

      mockHandler = defaultMockHandler;

      expectedCalls.forEach(function(expectedCall) {
        if (expectedCall.isComplete() == false) {
          throw new Error('not all calls occurred');
        }
      });
    }

    function andWillReturn(returnValue) {
      this.returnValue = returnValue;

      return {
        when: when
      };
    }

    function andAlso() {
      return this;
    }

    var theMock = function theMock() {
      return mockHandler.apply(theMock, Array.prototype.slice.call(arguments));
    };

    theMock._name = name;

    theMock.shouldBeCalled = function shouldBeCalled() {
      var expectedCall = ExpectedCall(theMock);

      expectedCalls.push(expectedCall);

      return {
        when: when,
        andWillReturn: andWillReturn.bind(expectedCall),
        andAlso: andAlso.bind({
          when: when
        })
      };
    };

    theMock.shouldBeCalledWith = function shouldBeCalledWith() {
      var expectedCall = ExpectedCall(theMock, Array.prototype.slice.call(arguments));

      expectedCalls.push(expectedCall);

      return {
        when: when
      }
    }

    return theMock;
  }
};
<commit_msg>Extracted API to drop a few lines and DRY things out<commit_after>function argString(args) {
  var asStrings = [];

  for (i = 0; i < args.length; i++) {
    if (typeof args[i] === 'string') {
      asStrings.push('\'' + args[i] + '\'');
    } else {
      asStrings.push(args[i]);
    }
  }

  return asStrings.join(', ');
}

function unexpectedFunctionCall(mock, args) {
  return new Error('unexpected function call ' + mock._name + '(' + argString(args) + ')');
}

function defaultMockHandler() {
  throw unexpectedFunctionCall(this, Array.prototype.slice.call(arguments));
}

var mockHandler = defaultMockHandler;

function ExpectedCall(mock, args) {
  return expectedCall = {
    mock: mock,
    met: false,
    args: args || [],
    complete: function() {
      this.met = true;
    },
    isComplete: function() {
      return this.met;
    },
    matches: function(mock, args) {
      if (mock === expectedCall.mock) {
        if (args.length !== expectedCall.args.length) {
          return false;
        }

        for (i = 0; i < args.length; i++) {
          if (args[i] !== expectedCall.args[i]) {
            return false;
          }
        }

        return true;
      }
    }
  };
}

module.exports = {
  mockFunction: function mockFunction(name) {
    var expectedCalls = [];

    function when(thunk) {
      mockHandler = function mockHandler() {
        var mock = this;
        var matchedExpectation;
        var args = Array.prototype.slice.call(arguments);

        expectedCalls.some(function(expectedCall) {
          if (expectedCall.matches(mock, args)) {
            expectedCall.complete();
            matchedExpectation = expectedCall;
            return false;
          }

          return true;
        });

        if (!matchedExpectation) {
          throw unexpectedFunctionCall(mock, args);
        }

        return matchedExpectation.returnValue;
      }

      thunk();

      mockHandler = defaultMockHandler;

      expectedCalls.forEach(function(expectedCall) {
        if (expectedCall.isComplete() == false) {
          throw new Error('not all calls occurred');
        }
      });
    }

    var api = {
      when: when,
      andWillReturn: andWillReturn,
      andAlso: andAlso
    };

    function andWillReturn(returnValue) {
      expectedCalls[expectedCalls.length - 1].returnValue = returnValue;

      return api;
    }

    function andAlso() {
      return api;
    }

    var theMock = function theMock() {
      return mockHandler.apply(theMock, Array.prototype.slice.call(arguments));
    };

    theMock._name = name;

    theMock.shouldBeCalled = function shouldBeCalled() {
      expectedCalls.push(ExpectedCall(theMock));
      return api;
    };

    theMock.shouldBeCalledWith = function shouldBeCalledWith() {
      expectedCalls.push(ExpectedCall(theMock, Array.prototype.slice.call(arguments)));
      return api;
    }

    return theMock;
  }
};
<|endoftext|>


<commit_before>/* This file is part of Indico.
 * Copyright (C) 2002 - 2016 European Organization for Nuclear Research (CERN).
 *
 * Indico is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public License as
 * published by the Free Software Foundation; either version 3 of the
 * License, or (at your option) any later version.
 *
 * Indico is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with Indico; if not, see <http://www.gnu.org/licenses/>.
 */

 (function(global) {
    'use strict';

    global.initMenuSortable = function initMenuSortable() {
        // ui.sender seems broken (returns null) so we keep track of the sortable who sent the item manually...
        var _sender = null;
        /*
         *.sortable('cancel') is also broken thus we need to keep track of the validity of an update manually as well.
         * See 'beforeStop' for a more detailed explanation.
         */
        var _invalidUpdate = false;

        function _canDrop(evt, ui) {
            if (_sender[0] === ui.placeholder.parent()[0]) { // entry moved within the same list
                return true;
            }
            if (!ui.item.hasClass('nestable')) {
                return false;
            }

            var parent = ui.placeholder.closest('.menu-entry').parent();
            // valid cross-list drag if the target list is the root one or if the target list can have nested entries
            return parent[0] === undefined || parent.hasClass('allow-nested');
        }

        // delete existing sortable before recreating them
        $('.menu-entries.ui-sortable').sortable('destroy');

        $('.menu-entries').sortable({
            handle: '.drag-handle',
            placeholder: 'menu-entry-placeholder',
            axis: 'y',
            connectWith: '.connect-menu',
            start: function(evt, ui) {
                _sender = $(this);
                if (ui.item.hasClass('nestable')) {
                    // The list only has the placeholder and the entry being dragged. It should look like an empty list.
                    if (_sender.children().not('.menu-entry-placeholder').length === 1) {
                        _sender.addClass('empty');
                    }
                    // Show empty children list which accept children
                    $('.menu-entries > .allow-nested > .menu-entry > .menu-entries.empty').addClass('expand')
                        .parent('.menu-entry')
                            .addClass('with-children');
                    ui.item.find('> .menu-entry > .menu-entries.empty').removeClass('expand')
                        .parent('.menu-entry')
                            .removeClass('with-children');
                }
                ui.placeholder.height(ui.helper.outerHeight());
            },
            beforeStop: function(evt, ui) {
                /*
                 * In the case of an invalid update we need to cancel the move. But .sortable('cancel') fails internally
                 * when called from here so we keep track of whether the move is valid, check in 'update' if can update
                 * and then cancel the move in 'stop' if needed.
                 */
                _invalidUpdate = !_canDrop(evt, ui);
            },
            stop: function(evt, ui) {
                // mandatory clean-up
                $('.menu-entries').removeClass('hide-placeholder');
                if (ui.item.hasClass('nestable')) {
                    $('.menu-entries > .allow-nested > .menu-entry > .menu-entries.empty').removeClass('expand')
                        .parent('.menu-entry')
                            .removeClass('with-children');
                }

                // Check if move is valid
                if (_invalidUpdate) {
                    // This can only happen if a nested user-submitted entry is dropped on an invalid target list, which
                    // shouldn't happen as internal nested list are not connected with the other lists.
                    $(this).sortable('cancel');

                } else {
                    if (ui.item.hasClass('nestable')) {
                        // remove .empty from parent list and .nestable from parent entry
                        ui.item.closest('.menu-entries')
                            .removeClass('empty')
                            .parent('.menu-entry')
                                .addClass('with-children')
                                .parent()
                                    .removeClass('nestable');

                        var isRoot = ui.item.closest('.menu-entry')[0] === undefined;
                        // disallow adding child elements
                        if (!isRoot) {
                             ui.item.removeClass('allow-nested');
                        }
                        // allow adding children to root entries which have a list of children
                        if (isRoot && ui.item.find('> .menu-entry > .menu-entries')) {
                            ui.item.addClass('allow-nested');
                        }

                        // add empty class to sender list of children if the last entry was moved out
                        if (!_sender.children().length) {
                            _sender.addClass('empty')
                                .closest('.menu-entry')
                                    .parent()
                                        .addClass('nestable');
                        }
                    }
                }
                _sender = null;
            },
            over: function(evt, ui) {
                // hide place-holder of list which are invalid targets
                if (!_canDrop(evt, ui)) {
                    ui.placeholder.closest('.menu-entries').addClass('hide-placeholder');
                }
            },
            update: function (evt, ui) {
                // Prevent update from nested list which does not contain the element any more.
                if (this !== ui.item.parent()[0]) {
                    return;
                }
                // Don't update an invalid move.
                if (_invalidUpdate) {
                    return;
                }

                evt.stopPropagation();
                var data = {};

                var position = ui.item.prev().children('.menu-entry').data('position');
                if (position !== null) {
                    data.position = position;
                }

                var parentId = ui.item.closest('.menu-entry').data('entry-id');
                if (parentId !== null) {
                    data.parent_id = parentId; // snake case to follow PEP8 on the back-end
                }

                $.ajax({
                    url: ui.item.children('.menu-entry').data('position-url'),
                    method: 'POST',
                    dataType: 'json',
                    data: data,
                    complete: IndicoUI.Dialogs.Util.progress(),
                    error: handleAjaxError
                });
            }
        });
    };

    $(document).on('click', '.menu-entry > .i-label > .actions > .edit-entry', function(evt) {
        evt.preventDefault();
        evt.stopPropagation();
        ajaxDialog({
            trigger: this,
            url: $(this).data('href'),
            title: $T.gettext('Menu Entry Settings'),
            onClose: function(data) {
                if (data) {
                    $(this.trigger).closest('.menu-entry').replaceWith(data.entry);
                }
            }
        });
    }).on('indico:confirmed', '.menu-entry .enabled, .menu-entry .not-enabled', function(evt) {
        evt.stopPropagation();
        evt.preventDefault();

        var $this = $(this);
        $.ajax({
            url: $this.data('href'),
            method: $this.data('method'),
            complete: IndicoUI.Dialogs.Util.progress(),
            error: handleAjaxError,
            success: function(data) {
                var isEnabled = data.is_enabled;
                $this.toggleClass('enabled', isEnabled)
                    .toggleClass('not-enabled', !isEnabled)
                    .parent('.actions')
                        .parent('.i-label').toggleClass('striped', !isEnabled);
            }
        });
    }).on('indico:confirmed', '.menu-entry .default, .menu-entry .not-default', function(evt) {
        evt.stopPropagation();
        evt.preventDefault();

        var $this = $(this);
        $.ajax({
            url: $this.data('href'),
            method: $this.data('method'),
            complete: IndicoUI.Dialogs.Util.progress(),
            error: handleAjaxError,
            success: function(data) {
                var isDefault = data.is_default;
                $('.menu-entry .default').removeClass('default').addClass('not-default');
                $this.toggleClass('default', isDefault).toggleClass('not-default', !isDefault);
            }
        });
    }).on('indico:confirmed', '.menu-entry .delete-entry', function(evt) {
        evt.preventDefault();

        var $this = $(this);
        $.ajax({
            url: $this.data('href'),
            method: $this.data('method'),
            complete: IndicoUI.Dialogs.Util.progress(),
            error: handleAjaxError,
            success: function(data) {
                if (data) {
                    $('#menu-entries > .menu-entries').replaceWith(data.menu);
                    initMenuSortable();
                }
            }
        });
    });
 })(window);
<commit_msg>Fix indentation<commit_after>/* This file is part of Indico.
 * Copyright (C) 2002 - 2016 European Organization for Nuclear Research (CERN).
 *
 * Indico is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public License as
 * published by the Free Software Foundation; either version 3 of the
 * License, or (at your option) any later version.
 *
 * Indico is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with Indico; if not, see <http://www.gnu.org/licenses/>.
 */

(function(global) {
    'use strict';

    global.initMenuSortable = function initMenuSortable() {
        // ui.sender seems broken (returns null) so we keep track of the sortable who sent the item manually...
        var _sender = null;
        /*
         *.sortable('cancel') is also broken thus we need to keep track of the validity of an update manually as well.
         * See 'beforeStop' for a more detailed explanation.
         */
        var _invalidUpdate = false;

        function _canDrop(evt, ui) {
            if (_sender[0] === ui.placeholder.parent()[0]) { // entry moved within the same list
                return true;
            }
            if (!ui.item.hasClass('nestable')) {
                return false;
            }

            var parent = ui.placeholder.closest('.menu-entry').parent();
            // valid cross-list drag if the target list is the root one or if the target list can have nested entries
            return parent[0] === undefined || parent.hasClass('allow-nested');
        }

        // delete existing sortable before recreating them
        $('.menu-entries.ui-sortable').sortable('destroy');

        $('.menu-entries').sortable({
            handle: '.drag-handle',
            placeholder: 'menu-entry-placeholder',
            axis: 'y',
            connectWith: '.connect-menu',
            start: function(evt, ui) {
                _sender = $(this);
                if (ui.item.hasClass('nestable')) {
                    // The list only has the placeholder and the entry being dragged. It should look like an empty list.
                    if (_sender.children().not('.menu-entry-placeholder').length === 1) {
                        _sender.addClass('empty');
                    }
                    // Show empty children list which accept children
                    $('.menu-entries > .allow-nested > .menu-entry > .menu-entries.empty').addClass('expand')
                        .parent('.menu-entry')
                            .addClass('with-children');
                    ui.item.find('> .menu-entry > .menu-entries.empty').removeClass('expand')
                        .parent('.menu-entry')
                            .removeClass('with-children');
                }
                ui.placeholder.height(ui.helper.outerHeight());
            },
            beforeStop: function(evt, ui) {
                /*
                 * In the case of an invalid update we need to cancel the move. But .sortable('cancel') fails internally
                 * when called from here so we keep track of whether the move is valid, check in 'update' if can update
                 * and then cancel the move in 'stop' if needed.
                 */
                _invalidUpdate = !_canDrop(evt, ui);
            },
            stop: function(evt, ui) {
                // mandatory clean-up
                $('.menu-entries').removeClass('hide-placeholder');
                if (ui.item.hasClass('nestable')) {
                    $('.menu-entries > .allow-nested > .menu-entry > .menu-entries.empty').removeClass('expand')
                        .parent('.menu-entry')
                            .removeClass('with-children');
                }

                // Check if move is valid
                if (_invalidUpdate) {
                    // This can only happen if a nested user-submitted entry is dropped on an invalid target list, which
                    // shouldn't happen as internal nested list are not connected with the other lists.
                    $(this).sortable('cancel');

                } else {
                    if (ui.item.hasClass('nestable')) {
                        // remove .empty from parent list and .nestable from parent entry
                        ui.item.closest('.menu-entries')
                            .removeClass('empty')
                            .parent('.menu-entry')
                                .addClass('with-children')
                                .parent()
                                    .removeClass('nestable');

                        var isRoot = ui.item.closest('.menu-entry')[0] === undefined;
                        // disallow adding child elements
                        if (!isRoot) {
                             ui.item.removeClass('allow-nested');
                        }
                        // allow adding children to root entries which have a list of children
                        if (isRoot && ui.item.find('> .menu-entry > .menu-entries')) {
                            ui.item.addClass('allow-nested');
                        }

                        // add empty class to sender list of children if the last entry was moved out
                        if (!_sender.children().length) {
                            _sender.addClass('empty')
                                .closest('.menu-entry')
                                    .parent()
                                        .addClass('nestable');
                        }
                    }
                }
                _sender = null;
            },
            over: function(evt, ui) {
                // hide place-holder of list which are invalid targets
                if (!_canDrop(evt, ui)) {
                    ui.placeholder.closest('.menu-entries').addClass('hide-placeholder');
                }
            },
            update: function (evt, ui) {
                // Prevent update from nested list which does not contain the element any more.
                if (this !== ui.item.parent()[0]) {
                    return;
                }
                // Don't update an invalid move.
                if (_invalidUpdate) {
                    return;
                }

                evt.stopPropagation();
                var data = {};

                var position = ui.item.prev().children('.menu-entry').data('position');
                if (position !== null) {
                    data.position = position;
                }

                var parentId = ui.item.closest('.menu-entry').data('entry-id');
                if (parentId !== null) {
                    data.parent_id = parentId; // snake case to follow PEP8 on the back-end
                }

                $.ajax({
                    url: ui.item.children('.menu-entry').data('position-url'),
                    method: 'POST',
                    dataType: 'json',
                    data: data,
                    complete: IndicoUI.Dialogs.Util.progress(),
                    error: handleAjaxError
                });
            }
        });
    };

    $(document).on('click', '.menu-entry > .i-label > .actions > .edit-entry', function(evt) {
        evt.preventDefault();
        evt.stopPropagation();
        ajaxDialog({
            trigger: this,
            url: $(this).data('href'),
            title: $T.gettext('Menu Entry Settings'),
            onClose: function(data) {
                if (data) {
                    $(this.trigger).closest('.menu-entry').replaceWith(data.entry);
                }
            }
        });
    }).on('indico:confirmed', '.menu-entry .enabled, .menu-entry .not-enabled', function(evt) {
        evt.stopPropagation();
        evt.preventDefault();

        var $this = $(this);
        $.ajax({
            url: $this.data('href'),
            method: $this.data('method'),
            complete: IndicoUI.Dialogs.Util.progress(),
            error: handleAjaxError,
            success: function(data) {
                var isEnabled = data.is_enabled;
                $this.toggleClass('enabled', isEnabled)
                    .toggleClass('not-enabled', !isEnabled)
                    .parent('.actions')
                        .parent('.i-label').toggleClass('striped', !isEnabled);
            }
        });
    }).on('indico:confirmed', '.menu-entry .default, .menu-entry .not-default', function(evt) {
        evt.stopPropagation();
        evt.preventDefault();

        var $this = $(this);
        $.ajax({
            url: $this.data('href'),
            method: $this.data('method'),
            complete: IndicoUI.Dialogs.Util.progress(),
            error: handleAjaxError,
            success: function(data) {
                var isDefault = data.is_default;
                $('.menu-entry .default').removeClass('default').addClass('not-default');
                $this.toggleClass('default', isDefault).toggleClass('not-default', !isDefault);
            }
        });
    }).on('indico:confirmed', '.menu-entry .delete-entry', function(evt) {
        evt.preventDefault();

        var $this = $(this);
        $.ajax({
            url: $this.data('href'),
            method: $this.data('method'),
            complete: IndicoUI.Dialogs.Util.progress(),
            error: handleAjaxError,
            success: function(data) {
                if (data) {
                    $('#menu-entries > .menu-entries').replaceWith(data.menu);
                    initMenuSortable();
                }
            }
        });
    });
})(window);
<|endoftext|>


<commit_before>#! /usr/bin/env node

var opts = require('optimist').argv
var through = require('through2');

function indexhtmlify(opts) {
    opts = opts || {}

    var s = through(function onwrite(chunk, enc, cb) {
        s.push(chunk)
        cb()
    }, function onend(cb) {
        s.push('</script>\n')
        s.push('\n')
        cb()
    })

    s.push('<!DOCTYPE html>\n')
    if(opts.appcache) {
      var appcache = opts.appcache === true ? './manifest.appcache' : opts.appcache
      s.push('')
    }
    else
      s.push('\n')
    s.push('\n')
    s.push('<title>' + (opts.title || '---') + '</title>\n')
    s.push('<meta content="width=device-width, initial-scale=1.0, ' +
        'maximum-scale=1.0, user-scalable=0" name="viewport" />\n')
    s.push('<meta charset=utf-8>\n')

    if (opts.style) {
        s.push('\n')
    }

    s.push('\n')
    s.push('\n')
        s.push('\n')
        cb()
    })

    s.push('<!DOCTYPE html>\n')
    if(opts.appcache) {
      var appcache = opts.appcache === true ? './manifest.appcache' : opts.appcache
      s.push('')
    }
    else
      s.push('\n')
    s.push('\n')
    s.push('<title>' + (opts.title || '---') + '</title>\n')
    s.push('<meta content="width=device-width, initial-scale=1.0, ' +
        'maximum-scale=1.0, user-scalable=0" name="viewport" />\n')
    s.push('<meta charset="utf-8">\n')

    if (opts.style) {
        s.push('\n')
    }

    s.push('\n')
    s.push('


<commit_before>var pieces = {
  destroyer: {
    size: 2,
    code: 'D'
  },
  cruiser: {
    size: 2,
    code: 'Z'
  },
  submarine: {
    size: 3,
    code: 'S'
  },
  battleship: {
    size: 4,
    code: 'B'
  },
  carrier: {
    size: 5,
    code: 'C'
  }
};

function generateBoard() {
  var board = [],
      i = 0;
  for (; i < 10; ++i) {
    board[i] = new Array(10);
  }
  return board;
}

function Battlecat() {
  this.playerBoard = generateBoard();
  this.enemyBoard = generateBoard();
  this.id = '';
  this.playerName = '';

  return this;
}

Battlecat.prototype.placePiece = function (code, positions) {
};

Battlecat.prototype.tryPlace = function (piece, startPosition, direction) {
  var size = pieces[piece].size,
      i = 0,
      x = startPosition[0],
      y = startPosition[1],
      positions = [];
  if ((direction == 'v' && x + size > 9) || (direction == 'h' && y + size > 9)) return;
  for (; i < size; ++i) {
    if (this.playerBoard[x][y] !== undefined) return;
    positions.push([x, y]);
    if (direction == 'v') { x++; }
    else if (direction == 'h') { y++; }
  }
  this.placePiece(pieces[piece].code, positions);
};

Battlecat.prototype.tryBomb = function (position) {

};

Battlecat.prototype.removePiece = function (piece) {

};

module.exports.Battlecat = Battlecat;
module.exports.createPlayer = function () {
  return new Battlecat();
};
<commit_msg>lay down some pieces<commit_after>var pieces = {
  destroyer: {
    size: 2,
    code: 'D'
  },
  cruiser: {
    size: 2,
    code: 'Z'
  },
  submarine: {
    size: 3,
    code: 'S'
  },
  battleship: {
    size: 4,
    code: 'B'
  },
  carrier: {
    size: 5,
    code: 'C'
  }
};

function generateBoard() {
  var board = [],
      i = 0;
  for (; i < 10; ++i) {
    board[i] = new Array(10);
  }
  return board;
}

function Battlecat() {
  this.playerBoard = generateBoard();
  this.enemyBoard = generateBoard();
  this.id = '';
  this.playerName = '';

  return this;
}

Battlecat.prototype.placePiece = function (code, positions) {
  var i = 0,
      l = positions.length;
  for (; i < l; ++i) {
    var x = i[0],
        y = i[1];
    this.playerBoard[x][y] = code;
  }
};

Battlecat.prototype.tryPlace = function (piece, startPosition, direction) {
  var size = pieces[piece].size,
      i = 0,
      x = startPosition[0],
      y = startPosition[1],
      positions = [];
  if ((direction == 'v' && x + size > 9) || (direction == 'h' && y + size > 9)) return;
  for (; i < size; ++i) {
    if (this.playerBoard[x][y] !== undefined) return;
    positions.push([x, y]);
    if (direction == 'v') { x++; }
    else if (direction == 'h') { y++; }
  }
  this.placePiece(pieces[piece].code, positions);
};

Battlecat.prototype.tryBomb = function (position) {

};

Battlecat.prototype.removePiece = function (piece) {

};

module.exports.Battlecat = Battlecat;
module.exports.createPlayer = function () {
  return new Battlecat();
};
<|endoftext|>


<commit_before>/**
 * tozan
 * https://github.com/paazmaya/tozan
 * Index filesystem by creating metadata database
 *
 * Copyright (c) Juga Paazmaya <paazmaya@yahoo.com> (https://paazmaya.fi)
 * Licensed under the MIT license
 */

const fs = require('fs'),
  path = require('path'),
  execSync = require('child_process').execSync;

const Better3 = require('better-sqlite3'),
  Progress = require('progress');

const OPENSSL_VERSION = 'openssl version';
const EXEC_OPTIONS = {
  encoding: 'utf8',
  cwd: process.cwd()
};
const BEGIN_DOT = /^\./;
const DEFAULT_DATABASE = ':memory:';
const ITERATION_SIZE = 100;

// https://www.sqlite.org/withoutrowid.html
const CREATE_TABLE = `
  CREATE TABLE IF NOT EXISTS files (
    filepath TEXT PRIMARY KEY ON CONFLICT REPLACE,
    sha256 TEXT,
    filesize INTEGER,
    modified INTEGER
  ) WITHOUT ROWID
`;
const COLUMNS_IN_TABLE = 4;

// Database data insertation query phrase with placeholders
const questions = Array(COLUMNS_IN_TABLE).fill('?').join(', ');
const INSERT_DATA = `INSERT INTO files VALUES (${questions})`;

/**
 * Create and initialise SQLite database and tables, which by default is in memory.
 *
 * @param  {string} location Where shall the database be stored, defauts to ':memory:'
 * @returns {sqlite3.Database} Database instance
 * @see http://sqlite.org/lang_createtable.html
 */
const createDatabase = (location) => {
  location = location || DEFAULT_DATABASE;

  const opts = {
    memory: false
  };

  if (location === DEFAULT_DATABASE) {
    location = 'in-memory';
    opts.memory = true;
  }

  const db = new Better3(location, opts);

  // Create tables that are needed.
  db.exec(CREATE_TABLE);

  return db;
};

/**
 * Generate and store the metadata for all the files in the list
 *
 * @param {array} list List of file meta data objects
 * @param {sqlite3.Database} db Database instance
 * @returns {sqlite3.Database|boolean} Database instance or false
 */
const storeData = (list, db) => {
  if (!(list instanceof Array) || list.length === 0) {
    return false;
  }

  const statement = db.prepare(INSERT_DATA);

  list.forEach((item) => {
    const values = Object.keys(item).map((key) => item[key]);
    statement.run(values);
  });

  return db;
};

/**
 * List all files under the given directory.
 *
 * @param {string} directory  Root directory in which images should be
 * @param {object} options    Options that are all boolean values and false by default
 * @param {boolean} options.ignoreDotFiles Ignore files and directories that begin with a dot
 *
 * @returns {Array} List of files
 */
const findFiles = (directory, options) => {
  let items = fs.readdirSync(directory);

  if (options.ignoreDotFiles) {
    items = items.filter((item) => {
      return !BEGIN_DOT.test(item);
    });
  }

  items = items.map((item) => {
    return path.join(directory, item);
  }).filter((item) => {
    try {
      fs.accessSync(item);

      return true;
    }
    catch (error) {
      console.error(`File "${item}" could not be accessed`);

      return false;
    }
  });

  let files = [];

  items.forEach((item) => {
    const stat = fs.statSync(item);
    if (stat.isDirectory()) {
      files = files.concat(findFiles(item, options));
    }
    else if (stat.isFile()) {
      files.push(item);
    }
  });

  return files;
};

/**
 * Get the meta data for the given file.
 *
 * @param {string} filepath File path
 * @returns {object|boolean} Meta data of the given file or false when it could not
 * @see https://nodejs.org/docs/latest-v6.x/api/fs.html#fs_class_fs_stats
 */
const getMeta = (filepath) => {
  let stat;

  try {
    stat = fs.statSync(filepath);
  }
  catch (error) {
    console.error(`Could not access the file "${filepath}"`);
    console.error(error.message);

    return false;
  }

  const command = `openssl dgst -sha256 "${filepath}"`;
  const output = execSync(command, EXEC_OPTIONS);
  const sha256 = output.trim().split(' ').pop();

  return {
    filepath: filepath,
    sha256: sha256,
    filesize: stat.size, // File size in bytes
    modified: stat.mtime.getTime() // Milliseconds elapsed since 1 January 1970 00:00:00 UTC
  };
};

/**
 * @param {array} files List of file paths
 * @param {object} options Options that are all boolean values and false by default
 * @param {string} options.database Possible database file to be used with SQLite
 *
 * @returns {void}
 */
const processFiles = (files, options) => {
  const db = createDatabase(options.database);

  // Handle files in chunks.
  const iterations = Math.ceil(files.length / ITERATION_SIZE);

  // Show command line progress.
  const bar = new Progress(`Processing ${files.length} files [:bar] :percent`, {
    total: files.length,
    complete: '#',
    incomplete: '-'
  });

  for (let i = 1; i <= iterations; ++i) {
    const list = files.splice(0, ITERATION_SIZE);
    const data = list
      .map((item) => {
        bar.tick();
        bar.render();

        return getMeta(item);
      })
      .filter((item) => {
        // Take out those items that have returned false from getMeta
        return item;
      });

    storeData(data, db);
  }

  db.close();
};

/**
 * Get rid of duplicates in the list.
 *
 * @param {array} list List of things to get only unique ones
 * @returns {array} Unique items in the list
 */
const unique = (list) => {
  const filtered = [];

  list.forEach((item) => {
    if (filtered.indexOf(item) === -1) {
      filtered.push(item);
    }
  });

  return filtered;
};

/**
 * What is the version of OpenSSL binary?
 * Used for checking its existance.
 *
 * @param {string} command Command for checking version
 * @returns {string|boolean} Version or false
 */
const openSSLVersion = (command) => {
  let version;
  try {
    version = execSync(command, EXEC_OPTIONS);
  }
  catch (error) {
    console.error('Looks like "openssl" is not available, hence cannot continue.');

    return false;
  }

  return version;
};

/**
 * Checks that OpenSSL is available before getting a list of files and process them.
 *
 * @param {string} directory  Root directory in which images should be
 * @param {object} options    Options that are all boolean values and false by default
 * @param {string} options.database Possible database file to be used with SQLite
 * @param {boolean} options.ignoreDotFiles Ignore files and directories that begin with a dot
 *
 * @returns {boolean} Processed or not
 */
module.exports = function tozan(directory, options) {

  const version = openSSLVersion(OPENSSL_VERSION);
  if (!version) {
    return false;
  }

  console.log(`Using "${version.trim()}" for SHA-256 hashing`);

  const files = unique(findFiles(directory, options));
  processFiles(files, options);

  return true;
};

module.exports.DEFAULT_DATABASE = DEFAULT_DATABASE;
module.exports.OPENSSL_VERSION = OPENSSL_VERSION;

// For unit testing only.
module.exports._createDatabase = createDatabase;
module.exports._storeData = storeData;
module.exports._findFiles = findFiles;
module.exports._getMeta = getMeta;
module.exports._processFiles = processFiles;
module.exports._unique = unique;
module.exports._openSSLVersion = openSSLVersion;
<commit_msg>Add u modified to regular expressions<commit_after>/**
 * tozan
 * https://github.com/paazmaya/tozan
 * Index filesystem by creating metadata database
 *
 * Copyright (c) Juga Paazmaya <paazmaya@yahoo.com> (https://paazmaya.fi)
 * Licensed under the MIT license
 */

const fs = require('fs'),
  path = require('path'),
  execSync = require('child_process').execSync;

const Better3 = require('better-sqlite3'),
  Progress = require('progress');

const OPENSSL_VERSION = 'openssl version';
const EXEC_OPTIONS = {
  encoding: 'utf8',
  cwd: process.cwd()
};
const BEGIN_DOT = /^\./u;
const DEFAULT_DATABASE = ':memory:';
const ITERATION_SIZE = 100;

// https://www.sqlite.org/withoutrowid.html
const CREATE_TABLE = `
  CREATE TABLE IF NOT EXISTS files (
    filepath TEXT PRIMARY KEY ON CONFLICT REPLACE,
    sha256 TEXT,
    filesize INTEGER,
    modified INTEGER
  ) WITHOUT ROWID
`;
const COLUMNS_IN_TABLE = 4;

// Database data insertation query phrase with placeholders
const questions = Array(COLUMNS_IN_TABLE).fill('?').join(', ');
const INSERT_DATA = `INSERT INTO files VALUES (${questions})`;

/**
 * Create and initialise SQLite database and tables, which by default is in memory.
 *
 * @param  {string} location Where shall the database be stored, defauts to ':memory:'
 * @returns {sqlite3.Database} Database instance
 * @see http://sqlite.org/lang_createtable.html
 */
const createDatabase = (location) => {
  location = location || DEFAULT_DATABASE;

  const opts = {
    memory: false
  };

  if (location === DEFAULT_DATABASE) {
    location = 'in-memory';
    opts.memory = true;
  }

  const db = new Better3(location, opts);

  // Create tables that are needed.
  db.exec(CREATE_TABLE);

  return db;
};

/**
 * Generate and store the metadata for all the files in the list
 *
 * @param {array} list List of file meta data objects
 * @param {sqlite3.Database} db Database instance
 * @returns {sqlite3.Database|boolean} Database instance or false
 */
const storeData = (list, db) => {
  if (!(list instanceof Array) || list.length === 0) {
    return false;
  }

  const statement = db.prepare(INSERT_DATA);

  list.forEach((item) => {
    const values = Object.keys(item).map((key) => item[key]);
    statement.run(values);
  });

  return db;
};

/**
 * List all files under the given directory.
 *
 * @param {string} directory  Root directory in which images should be
 * @param {object} options    Options that are all boolean values and false by default
 * @param {boolean} options.ignoreDotFiles Ignore files and directories that begin with a dot
 *
 * @returns {Array} List of files
 */
const findFiles = (directory, options) => {
  let items = fs.readdirSync(directory);

  if (options.ignoreDotFiles) {
    items = items.filter((item) => {
      return !BEGIN_DOT.test(item);
    });
  }

  items = items.map((item) => {
    return path.join(directory, item);
  }).filter((item) => {
    try {
      fs.accessSync(item);

      return true;
    }
    catch (error) {
      console.error(`File "${item}" could not be accessed`);

      return false;
    }
  });

  let files = [];

  items.forEach((item) => {
    const stat = fs.statSync(item);
    if (stat.isDirectory()) {
      files = files.concat(findFiles(item, options));
    }
    else if (stat.isFile()) {
      files.push(item);
    }
  });

  return files;
};

/**
 * Get the meta data for the given file.
 *
 * @param {string} filepath File path
 * @returns {object|boolean} Meta data of the given file or false when it could not
 * @see https://nodejs.org/docs/latest-v6.x/api/fs.html#fs_class_fs_stats
 */
const getMeta = (filepath) => {
  let stat;

  try {
    stat = fs.statSync(filepath);
  }
  catch (error) {
    console.error(`Could not access the file "${filepath}"`);
    console.error(error.message);

    return false;
  }

  const command = `openssl dgst -sha256 "${filepath}"`;
  const output = execSync(command, EXEC_OPTIONS);
  const sha256 = output.trim().split(' ').pop();

  return {
    filepath: filepath,
    sha256: sha256,
    filesize: stat.size, // File size in bytes
    modified: stat.mtime.getTime() // Milliseconds elapsed since 1 January 1970 00:00:00 UTC
  };
};

/**
 * @param {array} files List of file paths
 * @param {object} options Options that are all boolean values and false by default
 * @param {string} options.database Possible database file to be used with SQLite
 *
 * @returns {void}
 */
const processFiles = (files, options) => {
  const db = createDatabase(options.database);

  // Handle files in chunks.
  const iterations = Math.ceil(files.length / ITERATION_SIZE);

  // Show command line progress.
  const bar = new Progress(`Processing ${files.length} files [:bar] :percent`, {
    total: files.length,
    complete: '#',
    incomplete: '-'
  });

  for (let i = 1; i <= iterations; ++i) {
    const list = files.splice(0, ITERATION_SIZE);
    const data = list
      .map((item) => {
        bar.tick();
        bar.render();

        return getMeta(item);
      })
      .filter((item) => {
        // Take out those items that have returned false from getMeta
        return item;
      });

    storeData(data, db);
  }

  db.close();
};

/**
 * Get rid of duplicates in the list.
 *
 * @param {array} list List of things to get only unique ones
 * @returns {array} Unique items in the list
 */
const unique = (list) => {
  const filtered = [];

  list.forEach((item) => {
    if (filtered.indexOf(item) === -1) {
      filtered.push(item);
    }
  });

  return filtered;
};

/**
 * What is the version of OpenSSL binary?
 * Used for checking its existance.
 *
 * @param {string} command Command for checking version
 * @returns {string|boolean} Version or false
 */
const openSSLVersion = (command) => {
  let version;
  try {
    version = execSync(command, EXEC_OPTIONS);
  }
  catch (error) {
    console.error('Looks like "openssl" is not available, hence cannot continue.');

    return false;
  }

  return version;
};

/**
 * Checks that OpenSSL is available before getting a list of files and process them.
 *
 * @param {string} directory  Root directory in which images should be
 * @param {object} options    Options that are all boolean values and false by default
 * @param {string} options.database Possible database file to be used with SQLite
 * @param {boolean} options.ignoreDotFiles Ignore files and directories that begin with a dot
 *
 * @returns {boolean} Processed or not
 */
module.exports = function tozan(directory, options) {

  const version = openSSLVersion(OPENSSL_VERSION);
  if (!version) {
    return false;
  }

  console.log(`Using "${version.trim()}" for SHA-256 hashing`);

  const files = unique(findFiles(directory, options));
  processFiles(files, options);

  return true;
};

module.exports.DEFAULT_DATABASE = DEFAULT_DATABASE;
module.exports.OPENSSL_VERSION = OPENSSL_VERSION;

// For unit testing only.
module.exports._createDatabase = createDatabase;
module.exports._storeData = storeData;
module.exports._findFiles = findFiles;
module.exports._getMeta = getMeta;
module.exports._processFiles = processFiles;
module.exports._unique = unique;
module.exports._openSSLVersion = openSSLVersion;
<|endoftext|>