<commit_before>/* eslint-disable react/forbid-prop-types */
import React from 'react';
import PropTypes from 'prop-types';
import { TextInput, View, StyleSheet } from 'react-native';

import { APP_FONT_FAMILY } from '../globalStyles/index';
import { CircleButton } from './CircleButton';
import { AddIcon, MinusIcon } from './icons';

/**
 * Input component which will update a numerical value. The `value` passed will be
 * cast, so should return a valid number when cast to `Number`.
 *
 * Has increment and decrement buttons which increase the passed value by one.
 * The buttons can also be held, increase by one every 50ms. The increment amount
 * then grows exponentially every 500ms, reseting to one after releasing.
 *
 * @prop {String|Number} value          The current numerical value.
 * @prop {Func}          onChangeText   Callback for updating the value.
 * @prop {Number}        lowerLimit     The lower limit of the numerical range.
 * @prop {Number}        upperLimit     The upper limit of the numerical range.
 * @prop {Object}        textInputStyle Style object for the underlying TextInput.
 *
 */
export const StepperInput = ({ value, onChangeText, lowerLimit, upperLimit, textInputStyle }) => {
  const currentValue = React.useRef(Number(value));
  const currentAdjustmentAmount = React.useRef(1);
  const valueAdjustmentInterval = React.useRef();
  const adjustmentIncreaseInterval = React.useRef();

  const onUpdate = newValue => {
    currentValue.current = newValue;
    onChangeText(newValue);
  };

  const decrementValue = () => {
    if (lowerLimit >= currentValue.current) return;
    currentValue.current -= currentAdjustmentAmount.current;
    onChangeText(currentValue.current);
  };

  const incrementValue = () => {
    if (currentValue.current > upperLimit) return;
    currentValue.current += currentAdjustmentAmount.current;
    onChangeText(currentValue.current);
  };

  const inreaseIncrement = () => {
    currentAdjustmentAmount.current *= 2;
  };

  const onStartingLongPress = isIncrement => {
    if (!valueAdjustmentInterval.current) {
      valueAdjustmentInterval.current = setInterval(
        isIncrement ? incrementValue : decrementValue,
        50
      );
    }

    if (!adjustmentIncreaseInterval.current) {
      adjustmentIncreaseInterval.current = setInterval(inreaseIncrement, 500);
    }
  };

  const onEndLongPress = () => {
    clearInterval(valueAdjustmentInterval.current);
    clearInterval(adjustmentIncreaseInterval.current);

    valueAdjustmentInterval.current = null;
    adjustmentIncreaseInterval.current = null;
    currentAdjustmentAmount.current = 1;
  };

  const onStartDecrementPress = () => onStartingLongPress(false);
  const onStartIncrementPress = () => onStartingLongPress(true);

  return (
    <View style={localStyles.row}>
      <CircleButton
        IconComponent={MinusIcon}
        onPressIn={onStartDecrementPress}
        onPressOut={onEndLongPress}
      />
      <TextInput onChangeText={onUpdate} value={String(value)} style={textInputStyle} />
      <CircleButton
        IconComponent={AddIcon}
        onPressIn={onStartIncrementPress}
        onPressOut={onEndLongPress}
      />
    </View>
  );
};

const localStyles = StyleSheet.create({
  row: { flexDirection: 'row', alignItems: 'center', justifyContent: 'center' },
  textInput: {
    marginHorizontal: 25,
    width: 100,
    textAlign: 'center',
    fontSize: 24,

    fontFamily: APP_FONT_FAMILY,
  },
});

StepperInput.defaultProps = {
  lowerLimit: 0,
  upperLimit: 99999,
  textInputStyle: localStyles.textInput,
};

StepperInput.propTypes = {
  lowerLimit: PropTypes.number,
  upperLimit: PropTypes.number,
  value: PropTypes.oneOfType([PropTypes.string, PropTypes.number]).isRequired,
  onChangeText: PropTypes.func.isRequired,
  textInputStyle: PropTypes.object,
};
<commit_msg>Add adjustments to StepperInput<commit_after>/* eslint-disable react/forbid-prop-types */
import React from 'react';
import PropTypes from 'prop-types';
import { TextInput, View, StyleSheet } from 'react-native';

import { APP_FONT_FAMILY } from '../globalStyles/index';
import { CircleButton } from './CircleButton';
import { AddIcon, MinusIcon } from './icons';

/**
 * Input component which will update a numerical value. The `value` passed will be
 * cast, so should return a valid number when cast to `Number`.
 *
 * Has increment and decrement buttons which increase the passed value by one.
 * The buttons can also be held, increase by one every 50ms. The increment amount
 * then grows exponentially every 500ms, reseting to one after releasing.
 *
 * Component uses state to update itself, and a ref for storing the current value. This
 * is a bit of a hack, but is much more responsive to the rapid updates of the input.
 *
 * @prop {String|Number} value          The current numerical value.
 * @prop {Func}          onChangeText   Callback for updating the value.
 * @prop {Number}        lowerLimit     The lower limit of the numerical range.
 * @prop {Number}        upperLimit     The upper limit of the numerical range.
 * @prop {Object}        textInputStyle Style object for the underlying TextInput.
 *
 */
export const StepperInput = ({ value, onChangeText, lowerLimit, upperLimit, textInputStyle }) => {
  const [, setCurrentValueState] = React.useState(Number(value));
  const currentValue = React.useRef(Number(value));
  const currentAdjustmentAmount = React.useRef(1);
  const valueAdjustmentInterval = React.useRef();
  const adjustmentIncreaseInterval = React.useRef();

  const parseNumber = number => Math.max(Math.min(number, upperLimit), lowerLimit);

  const onUpdate = newValue => {
    const updateValue = parseNumber(newValue);
    currentValue.current = updateValue;
    setCurrentValueState(updateValue);
    onChangeText(updateValue);
  };

  const decrementValue = () => {
    const updateValue = parseNumber(currentValue.current - currentAdjustmentAmount.current);
    currentValue.current = updateValue;
    setCurrentValueState(updateValue);
    onChangeText(updateValue);
  };

  const incrementValue = () => {
    const updateValue = parseNumber(currentValue.current + currentAdjustmentAmount.current);
    currentValue.current = updateValue;
    setCurrentValueState(updateValue);
    onChangeText(updateValue);
  };

  const inreaseIncrement = () => {
    currentAdjustmentAmount.current *= 2;
  };

  const onStartingLongPress = isIncrement => {
    if (!valueAdjustmentInterval.current) {
      valueAdjustmentInterval.current = setInterval(
        isIncrement ? incrementValue : decrementValue,
        50
      );
    }

    if (!adjustmentIncreaseInterval.current) {
      adjustmentIncreaseInterval.current = setInterval(inreaseIncrement, 500);
    }
  };

  const onEndLongPress = () => {
    clearInterval(valueAdjustmentInterval.current);
    clearInterval(adjustmentIncreaseInterval.current);

    valueAdjustmentInterval.current = null;
    adjustmentIncreaseInterval.current = null;
    currentAdjustmentAmount.current = 1;
  };

  const onStartDecrementPress = () => onStartingLongPress(false);
  const onStartIncrementPress = () => onStartingLongPress(true);

  return (
    <View style={localStyles.row}>
      <CircleButton
        IconComponent={MinusIcon}
        onPressIn={onStartDecrementPress}
        onPressOut={onEndLongPress}
      />
      <TextInput
        onChangeText={onUpdate}
        value={String(currentValue.current)}
        style={textInputStyle}
      />
      <CircleButton
        IconComponent={AddIcon}
        onPressIn={onStartIncrementPress}
        onPressOut={onEndLongPress}
      />
    </View>
  );
};

const localStyles = StyleSheet.create({
  row: { flexDirection: 'row', alignItems: 'center', justifyContent: 'center' },
  textInput: {
    marginHorizontal: 25,
    width: 100,
    textAlign: 'center',
    fontSize: 24,

    fontFamily: APP_FONT_FAMILY,
  },
});

StepperInput.defaultProps = {
  lowerLimit: 0,
  upperLimit: 99999,
  textInputStyle: localStyles.textInput,
};

StepperInput.propTypes = {
  lowerLimit: PropTypes.number,
  upperLimit: PropTypes.number,
  value: PropTypes.oneOfType([PropTypes.string, PropTypes.number]).isRequired,
  onChangeText: PropTypes.func.isRequired,
  textInputStyle: PropTypes.object,
};
<|endoftext|>


<commit_before>"use strict";
var __extends = (this && this.__extends) || function (d, b) {
    for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p];
    function __() { this.constructor = d; }
    d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __());
};
var stream = require("stream");
var parser2 = require("htmlparser2");
var Transformer = (function (_super) {
    __extends(Transformer, _super);
    function Transformer(option) {
        var _this = _super.call(this, option) || this;
        _this.buffer = "";
        _this.tagModifier = {};
        _this.textTransforms = [];
        _this.parser = new parser2.Parser({
            ontext: function (text) { return _this._onText(text); },
            onopentag: function (name, attrib) { return _this._onOpenTag(name, attrib); }
        }, {
            recognizeSelfClosing: false
        });
        return _this;
    }
    Transformer.prototype.onTag = function (tagname, attribute, pattern, transformer) {
        if (this.tagModifier[tagname] == null) {
            this.tagModifier[tagname] = {};
        }
        if (this.tagModifier[tagname][attribute] == null) {
            this.tagModifier[tagname][attribute] = [];
        }
        this.tagModifier[tagname][attribute].push([pattern, transformer]);
        return this;
    };
    Transformer.prototype.onText = function (pattern, transformer) {
        this.textTransforms.push([pattern, transformer]);
        return this;
    };
    Transformer.prototype._onText = function (text) {
        this.push(Transformer._transformString(text, this.textTransforms));
    };
    Transformer._transformString = function (text, transforms) {
        for (var _i = 0, transforms_1 = transforms; _i < transforms_1.length; _i++) {
            var transform = transforms_1[_i];
            if (transform[0].test(text)) {
                return transform[1](text);
            }
        }
        return text;
    };
    Transformer.prototype._onOpenTag = function (name, attrib) {
        var lowerName = name.toLowerCase();
        var attribModifiers = this.tagModifier[lowerName];
        if (attribModifiers != null) {
            for (var _i = 0, _a = Object.keys(attrib); _i < _a.length; _i++) {
                var attribKey = _a[_i];
                var valueModifiers = attribModifiers[attribKey.toLowerCase()];
                if (valueModifiers != null) {
                    attrib[attribKey] = Transformer._transformString(attrib[attribKey], valueModifiers);
                }
            }
        }
        var attribsStr = Object.keys(attrib).map(function (k) { return " " + k + "=\"" + attrib[k] + "\""; }).join("");
        var trailling = lowerName === "br" || lowerName === "img" ? "/" : "";
        this.push("<" + name + attribsStr + trailling + ">");
    };
    Transformer.prototype._onCloseTag = function (name) {
        this.push("</" + name + ">");
    };
    Transformer.prototype._transform = function (chunk, encoding, callback) {
        var chunkStr = typeof chunk === "string" ? chunk : chunk.toString('utf-8');
        this.parser.write(chunkStr);
        callback();
    };
    Transformer.prototype._flush = function (callback) {
        this.parser.end();
    };
    return Transformer;
}(stream.Transform));
exports.Transformer = Transformer;
<commit_msg>build<commit_after>"use strict";
var __extends = (this && this.__extends) || function (d, b) {
    for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p];
    function __() { this.constructor = d; }
    d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __());
};
var stream = require("stream");
var parser2 = require("htmlparser2");
function isSelfClosedTag(tagname) {
    return tagname === "br" || tagname === "img";
}
var Transformer = (function (_super) {
    __extends(Transformer, _super);
    function Transformer(option) {
        var _this = _super.call(this, option) || this;
        _this.buffer = "";
        _this.tagModifier = {};
        _this.beforeCloseTags = {};
        _this.textTransforms = [];
        _this.parser = new parser2.Parser({
            ontext: function (text) { return _this._onText(text); },
            onopentag: function (name, attrib) { return _this._onOpenTag(name, attrib); },
            onclosetag: function (name) { return _this._onCloseTag(name); }
        }, {
            recognizeSelfClosing: false
        });
        return _this;
    }
    Transformer.prototype.onTag = function (tagname, attribute, pattern, transformer) {
        tagname = tagname.toLowerCase();
        if (this.tagModifier[tagname] == null) {
            this.tagModifier[tagname] = {};
        }
        attribute = attribute.toLowerCase();
        if (this.tagModifier[tagname][attribute] == null) {
            this.tagModifier[tagname][attribute] = [];
        }
        this.tagModifier[tagname][attribute].push([pattern, transformer]);
        return this;
    };
    Transformer.prototype.onBeforeClosingTag = function (tagname, transformer) {
        tagname = tagname.toLowerCase();
        if (this.beforeCloseTags[tagname] == null) {
            this.beforeCloseTags[tagname] = [];
        }
        this.beforeCloseTags[tagname].push(transformer);
        return this;
    };
    Transformer.prototype.onText = function (pattern, transformer) {
        this.textTransforms.push([pattern, transformer]);
        return this;
    };
    Transformer.prototype._onText = function (text) {
        this.push(Transformer._transformString(text, this.textTransforms));
    };
    Transformer._transformString = function (text, transforms) {
        for (var _i = 0, transforms_1 = transforms; _i < transforms_1.length; _i++) {
            var transform = transforms_1[_i];
            var execRes = transform[0].exec(text);
            if (execRes) {
                return transform[1](text, execRes);
            }
        }
        return text;
    };
    Transformer.prototype._onOpenTag = function (name, attrib) {
        var lowerName = name.toLowerCase();
        for (var _i = 0, _a = Object.keys(this.tagModifier)
            .filter(function (v) { return v === lowerName || v === "*"; }); _i < _a.length; _i++) {
            var tagKey = _a[_i];
            var attribModifiers = this.tagModifier[tagKey];
            var _loop_1 = function (attribKey) {
                for (var _i = 0, _a = Object.keys(attribModifiers)
                    .filter(function (v) { return v === attribKey.toLowerCase() || v === "*"; }); _i < _a.length; _i++) {
                    var attribTarget = _a[_i];
                    var valueModifiers = attribModifiers[attribTarget];
                    attrib[attribKey] = Transformer._transformString(attrib[attribKey], valueModifiers);
                }
            };
            for (var _b = 0, _c = Object.keys(attrib); _b < _c.length; _b++) {
                var attribKey = _c[_b];
                _loop_1(attribKey);
            }
        }
        var attribsStr = Object.keys(attrib).map(function (k) { return " " + k + "=\"" + attrib[k] + "\""; }).join("");
        var trailling = isSelfClosedTag(name) ? "/" : "";
        if (isSelfClosedTag(name)) {
            this._onProcessCloseTag(name);
        }
        this.push("<" + name + attribsStr + trailling + ">");
    };
    Transformer.prototype._onProcessCloseTag = function (name) {
        if (this.beforeCloseTags[name]) {
            for (var _i = 0, _a = this.beforeCloseTags[name]; _i < _a.length; _i++) {
                var transformer = _a[_i];
                this.push(transformer());
            }
        }
    };
    Transformer.prototype._onCloseTag = function (name) {
        if (!isSelfClosedTag(name)) {
            this._onProcessCloseTag(name);
            this.push("</" + name + ">");
        }
    };
    Transformer.prototype._transform = function (chunk, encoding, callback) {
        var chunkStr = typeof chunk === "string" ? chunk : chunk.toString('utf-8');
        this.parser.write(chunkStr);
        callback();
    };
    Transformer.prototype._flush = function (callback) {
        this.parser.end();
    };
    return Transformer;
}(stream.Transform));
exports.Transformer = Transformer;
<|endoftext|>


<commit_before>/*jshint globalstrict:false, strict:false */
/*global describe, beforeEach, afterEach, it, expect*/

////////////////////////////////////////////////////////////////////////////////
/// @brief Spec for foxx manager
///
/// @file
///
/// DISCLAIMER
///
/// Copyright 2014 ArangoDB GmbH, Cologne, Germany
///
/// Licensed under the Apache License, Version 2.0 (the "License");
/// you may not use this file except in compliance with the License.
/// You may obtain a copy of the License at
///
///     http://www.apache.org/licenses/LICENSE-2.0
///
/// Unless required by applicable law or agreed to in writing, software
/// distributed under the License is distributed on an "AS IS" BASIS,
/// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
/// See the License for the specific language governing permissions and
/// limitations under the License.
///
/// Copyright holder is ArangoDB GmbH, Cologne, Germany
///
/// @author Michael Hackstein
/// @author Copyright 2014, ArangoDB GmbH, Cologne, Germany
////////////////////////////////////////////////////////////////////////////////

'use strict';

var FoxxManager = require("org/arangodb/foxx/manager");
var arangodb = require("org/arangodb");
var ArangoError = arangodb.ArangoError;
var fs = require("fs");
var errors = require("internal").errors;
var basePath = fs.makeAbsolute(fs.join(module.startupPath(), "common", "test-data", "apps"));

function validateError(type, error) {
  expect(error instanceof ArangoError).toBeTruthy();
  expect(error.errorNum).toEqual(
    type.code,
    "Invalid code returned: expected " + type.code + " got " + error.errorNum
    + " Message: " + error
    + " Trace: " + error.stack
  );
}

describe("Foxx Manager install", function() {
  beforeEach(function() {
    try {
      FoxxManager.uninstall("/unittest/broken");
    } catch(e) {
      try {
        // Make sure that the files are physically removed
        var appPath = fs.makeAbsolute(module.appPath());
        appPath = fs.join(appPath, "unittest", "broken");
        fs.removeDirectoryRecursive(appPath, true);
      } catch(err) {
      }
    }
  });

  describe("failing for an invalid app", function() {
    beforeEach(function() {
      try {
        FoxxManager.uninstall("/unittest/broken", {force: true});
      } catch(e) {}
    });

    afterEach(function() {
      try {
        FoxxManager.uninstall("/unittest/broken");
      } catch(e) {}
    });

    it("without manifest", function() {
      try {
        FoxxManager.install(fs.join(basePath, "no-manifest"), "/unittest/broken");
        expect(true).toBeFalsy("Managed to install broken application");
      } catch(e) {
        validateError(errors.ERROR_FILE_NOT_FOUND, e);
      }
    });

    it("with malformed manifest", function() {
      try {
        FoxxManager.install(fs.join(basePath, "malformed-manifest"), "/unittest/broken");
        expect(true).toBeFalsy("Managed to install broken application");
      } catch(e) {
        validateError(errors.ERROR_MALFORMED_MANIFEST_FILE, e);
      }
    });

    it("with incomplete manifest", function() {
      try {
        FoxxManager.install(fs.join(basePath, "incomplete-manifest"), "/unittest/broken");
        expect(true).toBeFalsy("Managed to install broken application");
      } catch(e) {
        validateError(errors.ERROR_INVALID_APPLICATION_MANIFEST, e);
      }
    });

    it("with malformed name", function() {
      try {
        FoxxManager.install(fs.join(basePath, "malformed-name"), "/unittest/broken");
        expect(true).toBeFalsy("Managed to install broken application");
      } catch(e) {
        validateError(errors.ERROR_INVALID_APPLICATION_MANIFEST, e);
      }
    });

    it("with malformed controller file", function() {
      try {
        FoxxManager.install(fs.join(basePath, "malformed-controller-file"), "/unittest/broken");
        expect(true).toBeFalsy("Managed to install broken application");
      } catch(e) {
        validateError(errors.ERROR_FAILED_TO_EXECUTE_SCRIPT, e);
      }
    });

    it("with malformed controller path", function() {
      try {
        FoxxManager.install(fs.join(basePath, "malformed-controller-name"), "/unittest/broken");
        expect(true).toBeFalsy("Managed to install broken application");
      } catch(e) {
        validateError(errors.ERROR_INVALID_APPLICATION_MANIFEST, e);
      }
    });

    it("with malformed controller path", function() {
      try {
        FoxxManager.install(fs.join(basePath, "malformed-controller-path"), "/unittest/broken");
        expect(true).toBeFalsy("Managed to install broken application");
      } catch(e) {
        validateError(errors.ERROR_FILE_NOT_FOUND, e);
      }
    });

    it("with broken controller file", function() {
      try {
        FoxxManager.install(fs.join(basePath, "broken-controller-file"), "/unittest/broken");
        expect(true).toBeFalsy("Managed to install broken application");
      } catch(e) {
        validateError(errors.ERROR_FAILED_TO_EXECUTE_SCRIPT, e);
      }
    });

    it("with broken exports file", function() {
      try {
        FoxxManager.install(fs.join(basePath, "broken-exports-file"), "/unittest/broken");
        expect(true).toBeFalsy("Managed to install broken application");
      } catch(e) {
        validateError(errors.ERROR_FAILED_TO_EXECUTE_SCRIPT, e);
      }
    });

    it("with broken setup file", function() {
      try {
        FoxxManager.install(fs.join(basePath, "broken-setup-file"), "/unittest/broken");
        expect(true).toBeFalsy("Managed to install broken application");
      } catch(e) {
        validateError(errors.ERROR_FAILED_TO_EXECUTE_SCRIPT, e);
      }
    });

    it("with malformed exports file", function() {
      try {
        FoxxManager.install(fs.join(basePath, "malformed-exports-file"), "/unittest/broken");
        expect(true).toBeFalsy("Managed to install broken application");
      } catch(e) {
        validateError(errors.ERROR_FAILED_TO_EXECUTE_SCRIPT, e);
      }
    });

    it("with malformed exports path", function() {
      try {
        FoxxManager.install(fs.join(basePath, "malformed-exports-path"), "/unittest/broken");
        expect(true).toBeFalsy("Managed to install broken application");
      } catch(e) {
        validateError(errors.ERROR_FILE_NOT_FOUND, e);
      }
    });

    it("with malformed setup file", function() {
      try {
        FoxxManager.install(fs.join(basePath, "malformed-setup-file"), "/unittest/broken");
        expect(true).toBeFalsy("Managed to install broken application");
      } catch(e) {
        validateError(errors.ERROR_FAILED_TO_EXECUTE_SCRIPT, e);
      }
    });

    it("with malformed setup path", function() {
      try {
        FoxxManager.install(fs.join(basePath, "malformed-setup-path"), "/unittest/broken");
        expect(true).toBeFalsy("Managed to install broken application");
      } catch(e) {
        validateError(errors.ERROR_FILE_NOT_FOUND, e);
      }
    });

    it("with missing controller file", function() {
      try {
        FoxxManager.install(fs.join(basePath, "missing-controller-file"), "/unittest/broken");
        expect(true).toBeFalsy("Managed to install broken application");
      } catch(e) {
        validateError(errors.ERROR_FILE_NOT_FOUND, e);
      }
    });

    it("with missing exports file", function() {
      try {
        FoxxManager.install(fs.join(basePath, "missing-exports-file"), "/unittest/broken");
        expect(true).toBeFalsy("Managed to install broken application");
      } catch(e) {
        validateError(errors.ERROR_FILE_NOT_FOUND, e);
      }
    });

    it("with missing setup file", function() {
      try {
        FoxxManager.install(fs.join(basePath, "missing-setup-file"), "/unittest/broken");
        expect(true).toBeFalsy("Managed to install broken application");
      } catch(e) {
        validateError(errors.ERROR_FILE_NOT_FOUND, e);
      }
    });
  });

  describe("success with", function() {
    it("a minimal app", function() {
      try {
        FoxxManager.install(fs.join(basePath, "minimal-working-manifest"), "/unittest/broken");
      } catch(e) {
        expect(true).toBeFalsy("Could not install a minimal valid app.");
      }
      FoxxManager.uninstall("/unittest/broken", {force: true});
    });

    it("an app containing a sub-folder 'app'", function() {
      try {
        FoxxManager.install(fs.join(basePath, "interior-app-path"), "/unittest/broken");
      } catch(e) {
        expect(true).toBeFalsy("Could not install an app with sub-folder 'app'.");
      }
      FoxxManager.uninstall("/unittest/broken", {force: true});
    });
  });

  describe("should not install on invalid mountpoint", function() {
    it("starting with _", function() {
      var mount = "/_disallowed";
      try {
        FoxxManager.install(fs.join(basePath, "minimal-working-manifest"), mount);
        expect(true).toBeFalsy("Installed app at invalid mountpoint.");
        FoxxManager.uninstall(mount);
      } catch(e) {
        validateError(errors.ERROR_INVALID_MOUNTPOINT, e);
      }
    });

    it("starting with %", function() {
      var mount = "/%disallowed";
      try {
        FoxxManager.install(fs.join(basePath, "minimal-working-manifest"), mount);
        expect(true).toBeFalsy("Installed app at invalid mountpoint.");
        FoxxManager.uninstall(mount);
      } catch(e) {
        validateError(errors.ERROR_INVALID_MOUNTPOINT, e);
      }
    });

    it("starting with a number", function() {
      var mount = "/3disallowed";
      try {
        FoxxManager.install(fs.join(basePath, "minimal-working-manifest"), mount);
        expect(true).toBeFalsy("Installed app at invalid mountpoint.");
        FoxxManager.uninstall(mount);
      } catch(e) {
        validateError(errors.ERROR_INVALID_MOUNTPOINT, e);
      }
    });

    it("starting with app/", function() {
      var mount = "/app";
      try {
        FoxxManager.install(fs.join(basePath, "minimal-working-manifest"), mount);
        expect(true).toBeFalsy("Installed app at invalid mountpoint.");
        FoxxManager.uninstall(mount);
      } catch(e) {
        validateError(errors.ERROR_INVALID_MOUNTPOINT, e);
      }
    });

    it("containing /app/", function() {
      var mount = "/unittest/app/test";
      try {
        FoxxManager.install(fs.join(basePath, "minimal-working-manifest"), mount);
        expect(true).toBeFalsy("Installed app at invalid mountpoint.");
        FoxxManager.uninstall(mount);
      } catch(e) {
        validateError(errors.ERROR_INVALID_MOUNTPOINT, e);
      }
    });

    it("containing a .", function() {
      var mount = "/dis.allowed";
      try {
        FoxxManager.install(fs.join(basePath, "minimal-working-manifest"), mount);
        expect(true).toBeFalsy("Installed app at invalid mountpoint.");
        FoxxManager.uninstall(mount);
      } catch(e) {
        validateError(errors.ERROR_INVALID_MOUNTPOINT, e);
      }
    });

    it("containing a whitespace", function() {
      var mount = "/disal lowed";
      try {
        FoxxManager.install(fs.join(basePath, "minimal-working-manifest"), mount);
        expect(true).toBeFalsy("Installed app at invalid mountpoint.");
        FoxxManager.uninstall(mount);
      } catch(e) {
        validateError(errors.ERROR_INVALID_MOUNTPOINT, e);
      }
    });

    it("starting with ?", function() {
      var mount = "/disal?lowed";
      try {
        FoxxManager.install(fs.join(basePath, "minimal-working-manifest"), mount);
        expect(true).toBeFalsy("Installed app at invalid mountpoint.");
        FoxxManager.uninstall(mount);
      } catch(e) {
        validateError(errors.ERROR_INVALID_MOUNTPOINT, e);
      }
    });

    it("starting with :", function() {
      var mount = "/disa:llowed";
      try {
        FoxxManager.install(fs.join(basePath, "minimal-working-manifest"), mount);
        expect(true).toBeFalsy("Installed app at invalid mountpoint.");
        FoxxManager.uninstall(mount);
      } catch(e) {
        validateError(errors.ERROR_INVALID_MOUNTPOINT, e);
      }
    });
  });

  it("checking marvolous comments", function() {
    var mount = "/unittest/comments";
    try {
      FoxxManager.uninstall(mount, {force: true});
    } catch (e) {
      expect(e).toBeUndefined();
    }
    try {
      FoxxManager.install(fs.join(basePath, "fanciful-comments"), mount);
      expect(true).toBeTruthy();
    } catch (e) {
      expect(true).toBeFalsy("Failed to install app with comments.");
      expect(e).toBeUndefined();
    }
    try {
      FoxxManager.uninstall(mount, {force: true});
    } catch (e) {
      expect(e).toBeUndefined();
    }
  });
});
<commit_msg>fix spelling<commit_after>/*jshint globalstrict:false, strict:false */
/*global describe, beforeEach, afterEach, it, expect*/

////////////////////////////////////////////////////////////////////////////////
/// @brief Spec for foxx manager
///
/// @file
///
/// DISCLAIMER
///
/// Copyright 2014 ArangoDB GmbH, Cologne, Germany
///
/// Licensed under the Apache License, Version 2.0 (the "License");
/// you may not use this file except in compliance with the License.
/// You may obtain a copy of the License at
///
///     http://www.apache.org/licenses/LICENSE-2.0
///
/// Unless required by applicable law or agreed to in writing, software
/// distributed under the License is distributed on an "AS IS" BASIS,
/// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
/// See the License for the specific language governing permissions and
/// limitations under the License.
///
/// Copyright holder is ArangoDB GmbH, Cologne, Germany
///
/// @author Michael Hackstein
/// @author Copyright 2014, ArangoDB GmbH, Cologne, Germany
////////////////////////////////////////////////////////////////////////////////

'use strict';

var FoxxManager = require("org/arangodb/foxx/manager");
var arangodb = require("org/arangodb");
var ArangoError = arangodb.ArangoError;
var fs = require("fs");
var errors = require("internal").errors;
var basePath = fs.makeAbsolute(fs.join(module.startupPath(), "common", "test-data", "apps"));

function validateError(type, error) {
  expect(error instanceof ArangoError).toBeTruthy();
  expect(error.errorNum).toEqual(
    type.code,
    "Invalid code returned: expected " + type.code + " got " + error.errorNum
    + " Message: " + error
    + " Trace: " + error.stack
  );
}

describe("Foxx Manager install", function() {
  beforeEach(function() {
    try {
      FoxxManager.uninstall("/unittest/broken");
    } catch(e) {
      try {
        // Make sure that the files are physically removed
        var appPath = fs.makeAbsolute(module.appPath());
        appPath = fs.join(appPath, "unittest", "broken");
        fs.removeDirectoryRecursive(appPath, true);
      } catch(err) {
      }
    }
  });

  describe("failing for an invalid app", function() {
    beforeEach(function() {
      try {
        FoxxManager.uninstall("/unittest/broken", {force: true});
      } catch(e) {}
    });

    afterEach(function() {
      try {
        FoxxManager.uninstall("/unittest/broken");
      } catch(e) {}
    });

    it("without manifest", function() {
      try {
        FoxxManager.install(fs.join(basePath, "no-manifest"), "/unittest/broken");
        expect(true).toBeFalsy("Managed to install broken application");
      } catch(e) {
        validateError(errors.ERROR_FILE_NOT_FOUND, e);
      }
    });

    it("with malformed manifest", function() {
      try {
        FoxxManager.install(fs.join(basePath, "malformed-manifest"), "/unittest/broken");
        expect(true).toBeFalsy("Managed to install broken application");
      } catch(e) {
        validateError(errors.ERROR_MALFORMED_MANIFEST_FILE, e);
      }
    });

    it("with incomplete manifest", function() {
      try {
        FoxxManager.install(fs.join(basePath, "incomplete-manifest"), "/unittest/broken");
        expect(true).toBeFalsy("Managed to install broken application");
      } catch(e) {
        validateError(errors.ERROR_INVALID_APPLICATION_MANIFEST, e);
      }
    });

    it("with malformed name", function() {
      try {
        FoxxManager.install(fs.join(basePath, "malformed-name"), "/unittest/broken");
        expect(true).toBeFalsy("Managed to install broken application");
      } catch(e) {
        validateError(errors.ERROR_INVALID_APPLICATION_MANIFEST, e);
      }
    });

    it("with malformed controller file", function() {
      try {
        FoxxManager.install(fs.join(basePath, "malformed-controller-file"), "/unittest/broken");
        expect(true).toBeFalsy("Managed to install broken application");
      } catch(e) {
        validateError(errors.ERROR_FAILED_TO_EXECUTE_SCRIPT, e);
      }
    });

    it("with malformed controller path", function() {
      try {
        FoxxManager.install(fs.join(basePath, "malformed-controller-name"), "/unittest/broken");
        expect(true).toBeFalsy("Managed to install broken application");
      } catch(e) {
        validateError(errors.ERROR_INVALID_APPLICATION_MANIFEST, e);
      }
    });

    it("with malformed controller path", function() {
      try {
        FoxxManager.install(fs.join(basePath, "malformed-controller-path"), "/unittest/broken");
        expect(true).toBeFalsy("Managed to install broken application");
      } catch(e) {
        validateError(errors.ERROR_FILE_NOT_FOUND, e);
      }
    });

    it("with broken controller file", function() {
      try {
        FoxxManager.install(fs.join(basePath, "broken-controller-file"), "/unittest/broken");
        expect(true).toBeFalsy("Managed to install broken application");
      } catch(e) {
        validateError(errors.ERROR_FAILED_TO_EXECUTE_SCRIPT, e);
      }
    });

    it("with broken exports file", function() {
      try {
        FoxxManager.install(fs.join(basePath, "broken-exports-file"), "/unittest/broken");
        expect(true).toBeFalsy("Managed to install broken application");
      } catch(e) {
        validateError(errors.ERROR_FAILED_TO_EXECUTE_SCRIPT, e);
      }
    });

    it("with broken setup file", function() {
      try {
        FoxxManager.install(fs.join(basePath, "broken-setup-file"), "/unittest/broken");
        expect(true).toBeFalsy("Managed to install broken application");
      } catch(e) {
        validateError(errors.ERROR_FAILED_TO_EXECUTE_SCRIPT, e);
      }
    });

    it("with malformed exports file", function() {
      try {
        FoxxManager.install(fs.join(basePath, "malformed-exports-file"), "/unittest/broken");
        expect(true).toBeFalsy("Managed to install broken application");
      } catch(e) {
        validateError(errors.ERROR_FAILED_TO_EXECUTE_SCRIPT, e);
      }
    });

    it("with malformed exports path", function() {
      try {
        FoxxManager.install(fs.join(basePath, "malformed-exports-path"), "/unittest/broken");
        expect(true).toBeFalsy("Managed to install broken application");
      } catch(e) {
        validateError(errors.ERROR_FILE_NOT_FOUND, e);
      }
    });

    it("with malformed setup file", function() {
      try {
        FoxxManager.install(fs.join(basePath, "malformed-setup-file"), "/unittest/broken");
        expect(true).toBeFalsy("Managed to install broken application");
      } catch(e) {
        validateError(errors.ERROR_FAILED_TO_EXECUTE_SCRIPT, e);
      }
    });

    it("with malformed setup path", function() {
      try {
        FoxxManager.install(fs.join(basePath, "malformed-setup-path"), "/unittest/broken");
        expect(true).toBeFalsy("Managed to install broken application");
      } catch(e) {
        validateError(errors.ERROR_FILE_NOT_FOUND, e);
      }
    });

    it("with missing controller file", function() {
      try {
        FoxxManager.install(fs.join(basePath, "missing-controller-file"), "/unittest/broken");
        expect(true).toBeFalsy("Managed to install broken application");
      } catch(e) {
        validateError(errors.ERROR_FILE_NOT_FOUND, e);
      }
    });

    it("with missing exports file", function() {
      try {
        FoxxManager.install(fs.join(basePath, "missing-exports-file"), "/unittest/broken");
        expect(true).toBeFalsy("Managed to install broken application");
      } catch(e) {
        validateError(errors.ERROR_FILE_NOT_FOUND, e);
      }
    });

    it("with missing setup file", function() {
      try {
        FoxxManager.install(fs.join(basePath, "missing-setup-file"), "/unittest/broken");
        expect(true).toBeFalsy("Managed to install broken application");
      } catch(e) {
        validateError(errors.ERROR_FILE_NOT_FOUND, e);
      }
    });
  });

  describe("success with", function() {
    it("a minimal app", function() {
      try {
        FoxxManager.install(fs.join(basePath, "minimal-working-manifest"), "/unittest/broken");
      } catch(e) {
        expect(true).toBeFalsy("Could not install a minimal valid app.");
      }
      FoxxManager.uninstall("/unittest/broken", {force: true});
    });

    it("an app containing a sub-folder 'app'", function() {
      try {
        FoxxManager.install(fs.join(basePath, "interior-app-path"), "/unittest/broken");
      } catch(e) {
        expect(true).toBeFalsy("Could not install an app with sub-folder 'app'.");
      }
      FoxxManager.uninstall("/unittest/broken", {force: true});
    });
  });

  describe("should not install on invalid mountpoint", function() {
    it("starting with _", function() {
      var mount = "/_disallowed";
      try {
        FoxxManager.install(fs.join(basePath, "minimal-working-manifest"), mount);
        expect(true).toBeFalsy("Installed app at invalid mountpoint.");
        FoxxManager.uninstall(mount);
      } catch(e) {
        validateError(errors.ERROR_INVALID_MOUNTPOINT, e);
      }
    });

    it("starting with %", function() {
      var mount = "/%disallowed";
      try {
        FoxxManager.install(fs.join(basePath, "minimal-working-manifest"), mount);
        expect(true).toBeFalsy("Installed app at invalid mountpoint.");
        FoxxManager.uninstall(mount);
      } catch(e) {
        validateError(errors.ERROR_INVALID_MOUNTPOINT, e);
      }
    });

    it("starting with a number", function() {
      var mount = "/3disallowed";
      try {
        FoxxManager.install(fs.join(basePath, "minimal-working-manifest"), mount);
        expect(true).toBeFalsy("Installed app at invalid mountpoint.");
        FoxxManager.uninstall(mount);
      } catch(e) {
        validateError(errors.ERROR_INVALID_MOUNTPOINT, e);
      }
    });

    it("starting with app/", function() {
      var mount = "/app";
      try {
        FoxxManager.install(fs.join(basePath, "minimal-working-manifest"), mount);
        expect(true).toBeFalsy("Installed app at invalid mountpoint.");
        FoxxManager.uninstall(mount);
      } catch(e) {
        validateError(errors.ERROR_INVALID_MOUNTPOINT, e);
      }
    });

    it("containing /app/", function() {
      var mount = "/unittest/app/test";
      try {
        FoxxManager.install(fs.join(basePath, "minimal-working-manifest"), mount);
        expect(true).toBeFalsy("Installed app at invalid mountpoint.");
        FoxxManager.uninstall(mount);
      } catch(e) {
        validateError(errors.ERROR_INVALID_MOUNTPOINT, e);
      }
    });

    it("containing a .", function() {
      var mount = "/dis.allowed";
      try {
        FoxxManager.install(fs.join(basePath, "minimal-working-manifest"), mount);
        expect(true).toBeFalsy("Installed app at invalid mountpoint.");
        FoxxManager.uninstall(mount);
      } catch(e) {
        validateError(errors.ERROR_INVALID_MOUNTPOINT, e);
      }
    });

    it("containing a whitespace", function() {
      var mount = "/disal lowed";
      try {
        FoxxManager.install(fs.join(basePath, "minimal-working-manifest"), mount);
        expect(true).toBeFalsy("Installed app at invalid mountpoint.");
        FoxxManager.uninstall(mount);
      } catch(e) {
        validateError(errors.ERROR_INVALID_MOUNTPOINT, e);
      }
    });

    it("starting with ?", function() {
      var mount = "/disal?lowed";
      try {
        FoxxManager.install(fs.join(basePath, "minimal-working-manifest"), mount);
        expect(true).toBeFalsy("Installed app at invalid mountpoint.");
        FoxxManager.uninstall(mount);
      } catch(e) {
        validateError(errors.ERROR_INVALID_MOUNTPOINT, e);
      }
    });

    it("starting with :", function() {
      var mount = "/disa:llowed";
      try {
        FoxxManager.install(fs.join(basePath, "minimal-working-manifest"), mount);
        expect(true).toBeFalsy("Installed app at invalid mountpoint.");
        FoxxManager.uninstall(mount);
      } catch(e) {
        validateError(errors.ERROR_INVALID_MOUNTPOINT, e);
      }
    });
  });

  it("checking marvelous comments", function() {
    var mount = "/unittest/comments";
    try {
      FoxxManager.uninstall(mount, {force: true});
    } catch (e) {
      expect(e).toBeUndefined();
    }
    try {
      FoxxManager.install(fs.join(basePath, "fanciful-comments"), mount);
      expect(true).toBeTruthy();
    } catch (e) {
      expect(true).toBeFalsy("Failed to install app with comments.");
      expect(e).toBeUndefined();
    }
    try {
      FoxxManager.uninstall(mount, {force: true});
    } catch (e) {
      expect(e).toBeUndefined();
    }
  });
});
<|endoftext|>


<commit_before>'use strict'

var Buffer = require('safe-buffer').Buffer
var mqtt = require('mqtt-packet')
var EE = require('events').EventEmitter
var util = require('util')
var eos = require('end-of-stream')
var empty = Buffer.allocUnsafe(0)
var Packet = require('aedes-packet')
var write = require('./write')
var QoSPacket = require('./qos-packet')
var handleSubscribe = require('./handlers/subscribe')
var handleUnsubscribe = require('./handlers/unsubscribe')
var handle = require('./handlers')

module.exports = Client

function Client (broker, conn) {
  var that = this

  this.broker = broker
  this.conn = conn
  this.parser = mqtt.parser()
  this.connected = false
  this.errored = false
  this.clean = true
  this._handling = 0
  this.subscriptions = {}
  this.id = null

  // we use two variables for the will
  // because we store in _will while
  // we are authenticating
  this.will = null
  this._will = null

  conn.client = this

  this.parser.client = this

  this.duplicates = {}
  this._parsingBatch = 1
  this._nextId = Math.ceil(Math.random() * 65535)

  this.disconnected = false

  this.parser.on('packet', enqueue)

  function nextBatch (err) {
    if (err) {
      return that.emit('error', err)
    }

    var buf = empty
    var client = that

    if (client._paused) {
      return
    }

    that._parsingBatch--
    if (that._parsingBatch <= 0) {
      that._parsingBatch = 0
      buf = client.conn.read(null)

      if (buf) {
        client.parser.parse(buf)
      }
    }
  }
  this._nextBatch = nextBatch

  this.on('error', onError)

  nextBatch()

  conn.on('readable', nextBatch)
  conn.on('error', this.emit.bind(this, 'error'))
  this.parser.on('error', this.emit.bind(this, 'error'))

  this._eos = eos(this.conn, this.close.bind(this))

  function dedupe (packet) {
    var duplicates = that.duplicates
    var id = packet.brokerId
    if (!id) {
      return true
    }
    var counter = packet.brokerCounter
    var result = (duplicates[id] || 0) < counter
    if (result) {
      duplicates[id] = counter
    }
    return result
  }

  this.deliver0 = function deliverQoS0 (_packet, cb) {
    var toForward = dedupe(_packet) &&
      that.broker.authorizeForward(that, _packet)
    if (toForward) {
      var packet = new Packet(toForward, broker)
      packet.qos = 0
      write(that, packet, cb)
    } else {
      cb()
    }
  }

  this.deliverQoS = function deliverQoS (_packet, cb) {
    // downgrade to qos0 if requested by publish
    if (_packet.qos === 0) {
      that.deliver0(_packet, cb)
    } else {
      var toForward = dedupe(_packet) &&
        that.broker.authorizeForward(that, _packet)
      if (toForward) {
        var packet = new QoSPacket(toForward, that)
        // Downgrading to client subscription qos if needed
        var clientSub = that.subscriptions[packet.topic]
        if (clientSub && clientSub.qos && clientSub.qos < packet.qos) {
          packet.qos = clientSub.qos
        }
        packet.writeCallback = cb
        if (that.clean) {
          writeQoS(null, that, packet)
        } else {
          broker.persistence.outgoingUpdate(that, packet, writeQoS)
        }
      } else if (that.clean === false) {
        that.broker.persistence.outgoingClearMessageId(that, _packet, nop)
        // we consider this to be an error, since the packet is undefined
        // so there's nothing to send
        cb()
      } else {
        cb()
      }
    }
  }

  this._keepaliveTimer = null
  this._keepaliveInterval = -1

  this._connectTimer = setTimeout(function () {
    that.emit('error', new Error('connect did not arrive in time'))
  }, broker.connectTimeout)
}

function writeQoS (err, client, packet) {
  if (err) {
    // is this right, or we should ignore thins?
    client.emit('error', err)
  } else {
    write(client, packet, packet.writeCallback)
  }
}

function onError (err) {
  this.errored = true
  this.conn.removeAllListeners('error')
  this.conn.on('error', nop)
  if (this.id) {
    this.broker.emit('clientError', this, err)
  } else {
    this.broker.emit('connectionError', this, err)
  }
  this.close()
}

util.inherits(Client, EE)

Client.prototype._onError = onError

Client.prototype.publish = function (message, done) {
  var packet = new Packet(message, this.broker)
  var that = this
  if (packet.qos === 0) {
    // skip offline and send it as it is
    this.deliver0(packet, done || nop)
  } else if (!this.clean && this.id) {
    this.broker.persistence.outgoingEnqueue({
      clientId: this.id
    }, packet, function deliver (err) {
      if (err) {
        return done(err)
      }
      that.deliverQoS(packet, done)
    })
  } else {
    that.deliverQoS(packet, done)
  }
}

Client.prototype.subscribe = function (packet, done) {
  if (!packet.subscriptions) {
    if (!Array.isArray(packet)) {
      packet = [packet]
    }
    packet = {
      subscriptions: packet
    }
  }
  handleSubscribe(this, packet, done)
}

Client.prototype.unsubscribe = function (packet, done) {
  if (!packet.unsubscriptions) {
    if (!Array.isArray(packet)) {
      packet = [packet]
    }
    packet = {
      unsubscriptions: packet
    }
  }
  handleUnsubscribe(this, packet, done)
}

Client.prototype.close = function (done) {
  var that = this
  var conn = this.conn

  if (this.connected) {
    handleUnsubscribe(
      this,
      {
        close: true,
        unsubscriptions: Object.keys(this.subscriptions)
      },
      finish)
  } else {
    finish()
  }

  this.parser.removeAllListeners('packet')
  conn.removeAllListeners('readable')

  if (this._keepaliveTimer) {
    this._keepaliveTimer.clear()
    this._keepaliveInterval = -1
    this._keepaliveTimer = null
  }

  if (this._connectTimer) {
    clearTimeout(this._connectTimer)
    this._connectTimer = null
  }

  this._eos()
  this._eos = nop

  function finish () {
    if (!that.disconnected && that.will) {
      var will = Object.assign({}, that.will)
      that.broker.authorizePublish(that, will, function (err) {
        if (!err) {
          that.broker.publish(will, that, done)
        } else {
          done()
        }

        function done (err) {
          if (!err) {
            that.broker.persistence.delWill({
              id: that.id,
              brokerId: that.broker.id
            }, nop)
          }
        }
      })
      that.will = null // this function might be called twice
    }

    conn.removeAllListeners('error')
    conn.on('error', nop)

    if (that.broker.clients[that.id]) {
      that.broker.unregisterClient(that)
    }

    // clear up the drain event listeners
    that.conn.emit('drain')
    that.conn.removeAllListeners('drain')

    if (conn.destroySoon) {
      conn.destroySoon()
    } if (conn.destroy) {
      conn.destroy()
    } else {
      conn.end()
    }

    if (typeof done === 'function') {
      done()
    }
  }
}

Client.prototype.pause = function () {
  this._paused = true
}

Client.prototype.resume = function () {
  this._paused = false
  this._nextBatch()
}

function enqueue (packet) {
  this.client._parsingBatch++
  handle(this.client, packet, this.client._nextBatch)
}

function nop () { }
<commit_msg>added missing else<commit_after>'use strict'

var Buffer = require('safe-buffer').Buffer
var mqtt = require('mqtt-packet')
var EE = require('events').EventEmitter
var util = require('util')
var eos = require('end-of-stream')
var empty = Buffer.allocUnsafe(0)
var Packet = require('aedes-packet')
var write = require('./write')
var QoSPacket = require('./qos-packet')
var handleSubscribe = require('./handlers/subscribe')
var handleUnsubscribe = require('./handlers/unsubscribe')
var handle = require('./handlers')

module.exports = Client

function Client (broker, conn) {
  var that = this

  this.broker = broker
  this.conn = conn
  this.parser = mqtt.parser()
  this.connected = false
  this.errored = false
  this.clean = true
  this._handling = 0
  this.subscriptions = {}
  this.id = null

  // we use two variables for the will
  // because we store in _will while
  // we are authenticating
  this.will = null
  this._will = null

  conn.client = this

  this.parser.client = this

  this.duplicates = {}
  this._parsingBatch = 1
  this._nextId = Math.ceil(Math.random() * 65535)

  this.disconnected = false

  this.parser.on('packet', enqueue)

  function nextBatch (err) {
    if (err) {
      return that.emit('error', err)
    }

    var buf = empty
    var client = that

    if (client._paused) {
      return
    }

    that._parsingBatch--
    if (that._parsingBatch <= 0) {
      that._parsingBatch = 0
      buf = client.conn.read(null)

      if (buf) {
        client.parser.parse(buf)
      }
    }
  }
  this._nextBatch = nextBatch

  this.on('error', onError)

  nextBatch()

  conn.on('readable', nextBatch)
  conn.on('error', this.emit.bind(this, 'error'))
  this.parser.on('error', this.emit.bind(this, 'error'))

  this._eos = eos(this.conn, this.close.bind(this))

  function dedupe (packet) {
    var duplicates = that.duplicates
    var id = packet.brokerId
    if (!id) {
      return true
    }
    var counter = packet.brokerCounter
    var result = (duplicates[id] || 0) < counter
    if (result) {
      duplicates[id] = counter
    }
    return result
  }

  this.deliver0 = function deliverQoS0 (_packet, cb) {
    var toForward = dedupe(_packet) &&
      that.broker.authorizeForward(that, _packet)
    if (toForward) {
      var packet = new Packet(toForward, broker)
      packet.qos = 0
      write(that, packet, cb)
    } else {
      cb()
    }
  }

  this.deliverQoS = function deliverQoS (_packet, cb) {
    // downgrade to qos0 if requested by publish
    if (_packet.qos === 0) {
      that.deliver0(_packet, cb)
    } else {
      var toForward = dedupe(_packet) &&
        that.broker.authorizeForward(that, _packet)
      if (toForward) {
        var packet = new QoSPacket(toForward, that)
        // Downgrading to client subscription qos if needed
        var clientSub = that.subscriptions[packet.topic]
        if (clientSub && clientSub.qos && clientSub.qos < packet.qos) {
          packet.qos = clientSub.qos
        }
        packet.writeCallback = cb
        if (that.clean) {
          writeQoS(null, that, packet)
        } else {
          broker.persistence.outgoingUpdate(that, packet, writeQoS)
        }
      } else if (that.clean === false) {
        that.broker.persistence.outgoingClearMessageId(that, _packet, nop)
        // we consider this to be an error, since the packet is undefined
        // so there's nothing to send
        cb()
      } else {
        cb()
      }
    }
  }

  this._keepaliveTimer = null
  this._keepaliveInterval = -1

  this._connectTimer = setTimeout(function () {
    that.emit('error', new Error('connect did not arrive in time'))
  }, broker.connectTimeout)
}

function writeQoS (err, client, packet) {
  if (err) {
    // is this right, or we should ignore thins?
    client.emit('error', err)
  } else {
    write(client, packet, packet.writeCallback)
  }
}

function onError (err) {
  this.errored = true
  this.conn.removeAllListeners('error')
  this.conn.on('error', nop)
  if (this.id) {
    this.broker.emit('clientError', this, err)
  } else {
    this.broker.emit('connectionError', this, err)
  }
  this.close()
}

util.inherits(Client, EE)

Client.prototype._onError = onError

Client.prototype.publish = function (message, done) {
  var packet = new Packet(message, this.broker)
  var that = this
  if (packet.qos === 0) {
    // skip offline and send it as it is
    this.deliver0(packet, done || nop)
  } else if (!this.clean && this.id) {
    this.broker.persistence.outgoingEnqueue({
      clientId: this.id
    }, packet, function deliver (err) {
      if (err) {
        return done(err)
      }
      that.deliverQoS(packet, done)
    })
  } else {
    that.deliverQoS(packet, done)
  }
}

Client.prototype.subscribe = function (packet, done) {
  if (!packet.subscriptions) {
    if (!Array.isArray(packet)) {
      packet = [packet]
    }
    packet = {
      subscriptions: packet
    }
  }
  handleSubscribe(this, packet, done)
}

Client.prototype.unsubscribe = function (packet, done) {
  if (!packet.unsubscriptions) {
    if (!Array.isArray(packet)) {
      packet = [packet]
    }
    packet = {
      unsubscriptions: packet
    }
  }
  handleUnsubscribe(this, packet, done)
}

Client.prototype.close = function (done) {
  var that = this
  var conn = this.conn

  if (this.connected) {
    handleUnsubscribe(
      this,
      {
        close: true,
        unsubscriptions: Object.keys(this.subscriptions)
      },
      finish)
  } else {
    finish()
  }

  this.parser.removeAllListeners('packet')
  conn.removeAllListeners('readable')

  if (this._keepaliveTimer) {
    this._keepaliveTimer.clear()
    this._keepaliveInterval = -1
    this._keepaliveTimer = null
  }

  if (this._connectTimer) {
    clearTimeout(this._connectTimer)
    this._connectTimer = null
  }

  this._eos()
  this._eos = nop

  function finish () {
    if (!that.disconnected && that.will) {
      var will = Object.assign({}, that.will)
      that.broker.authorizePublish(that, will, function (err) {
        if (!err) {
          that.broker.publish(will, that, done)
        } else {
          done()
        }

        function done (err) {
          if (!err) {
            that.broker.persistence.delWill({
              id: that.id,
              brokerId: that.broker.id
            }, nop)
          }
        }
      })
      that.will = null // this function might be called twice
    }

    conn.removeAllListeners('error')
    conn.on('error', nop)

    if (that.broker.clients[that.id]) {
      that.broker.unregisterClient(that)
    }

    // clear up the drain event listeners
    that.conn.emit('drain')
    that.conn.removeAllListeners('drain')

    if (conn.destroySoon) {
      conn.destroySoon()
    } else if (conn.destroy) {
      conn.destroy()
    } else {
      conn.end()
    }

    if (typeof done === 'function') {
      done()
    }
  }
}

Client.prototype.pause = function () {
  this._paused = true
}

Client.prototype.resume = function () {
  this._paused = false
  this._nextBatch()
}

function enqueue (packet) {
  this.client._parsingBatch++
  handle(this.client, packet, this.client._nextBatch)
}

function nop () { }
<|endoftext|>


<commit_before>'use strict'

const Game = require('./game')

$(document).ready( () => {
  keyboardMoves()
  startLeftPlayer()
})

let canvas = document.getElementById('game')
let context = canvas.getContext('2d')
let game = new Game

var drawOne = function () {
  context.fillStyle = "#FF0000"
  context.fillRect(game.bikeOne.x, game.bikeOne.y, game.bikeOne.width, game.bikeOne.height)
  return this
}

var drawTwo = function () {
  context.fillStyle = "#FFFF00"
  context.fillRect(game.bikeTwo.x, game.bikeTwo.y, game.bikeTwo.width, game.bikeTwo.height)
  return this
}

var startRightPlayer = () => {
  $('#start').on('click', function(event) {
    rightTwo()
  })
}

var startLeftPlayer = () => {
  $('#start').on('click', function(event) {
    rightOne()
  })
}

function keyboardMoves () {
  $('body').on('keydown', function(event) {
    switch(event.which) {
        case 65:
        return leftOne()

        case 87:
        return upOne()

        case 68:
        return rightOne()

        case 83:
        return downOne()

        case 74:
        return leftTwo()

        case 73:
        return upTwo()

        case 76:
        return rightTwo()

        case 75:
        return downTwo()

        default: return
    }
    event.preventDefault()
  })
}

var rightOne = () => {
  requestAnimationFrame( function gameLoop() {
    drawOne()
    game.bikeOne.moveRight
  })
}

var downOne = () => {
  requestAnimationFrame( function gameLoop() {
    drawOne()
    game.bikeOne.moveDown
  })
}

var upOne = () => {
  requestAnimationFrame( function gameLoop() {
    drawOne()
    game.bikeOne.moveUp
  })
}

var leftOne = () => {
  requestAnimationFrame( function gameLoop() {
    drawOne()
    game.bikeOne.moveLeft
  })
}

var rightTwo = () => {
  requestAnimationFrame( function gameLoop() {
    drawTwo()
    game.bikeTwo.moveRight
  })
}

var downTwo = () => {
  requestAnimationFrame( function gameLoop() {
    drawTwo()
    game.bikeTwo.moveDown
  })
}

var upTwo = () => {
  requestAnimationFrame( function gameLoop() {
    drawTwo()
    game.bikeTwo.moveUp
  })
}

var leftTwo = () => {
  requestAnimationFrame( function gameLoop() {
    drawTwo()
    game.bikeTwo.moveLeft
  })
}
<commit_msg>down to 111 lines on match.js<commit_after>'use strict'

const Game = require('./game')

$(document).ready( () => {
  keyboardMoves()
  startPlayers()
})

let canvas = document.getElementById('game')
let context = canvas.getContext('2d')
let game = new Game

var drawOne = function () {
  context.fillStyle = "#FF0000"
  context.fillRect(game.bikeOne.x, game.bikeOne.y, game.bikeOne.width, game.bikeOne.height)
  return this
}

var drawTwo = function () {
  context.fillStyle = "#FFFF00"
  context.fillRect(game.bikeTwo.x, game.bikeTwo.y, game.bikeTwo.width, game.bikeTwo.height)
  return this
}

var startPlayers = () => {
  $('#start').on('click', function(event) {
    rightOne(); rightTwo()
  })
}

function keyboardMoves () {
  $('body').on('keydown', function(event) {
    switch(event.which) {
        case 65:
        return leftOne()

        case 87:
        return upOne()

        case 68:
        return rightOne()

        case 83:
        return downOne()

        case 74:
        return leftTwo()

        case 73:
        return upTwo()

        case 76:
        return rightTwo()

        case 75:
        return downTwo()

        default: return
    }
    event.preventDefault()
  })
}

var rightOne = () => {
  requestAnimationFrame( function gameLoop() {
    drawOne(); game.bikeOne.moveRight
  })
}

var downOne = () => {
  requestAnimationFrame( function gameLoop() {
    drawOne(); game.bikeOne.moveDown
  })
}

var upOne = () => {
  requestAnimationFrame( function gameLoop() {
    drawOne(); game.bikeOne.moveUp
  })
}

var leftOne = () => {
  requestAnimationFrame( function gameLoop() {
    drawOne(); game.bikeOne.moveLeft
  })
}

var rightTwo = () => {
  requestAnimationFrame( function gameLoop() {
    drawTwo(); game.bikeTwo.moveRight
  })
}

var downTwo = () => {
  requestAnimationFrame( function gameLoop() {
    drawTwo(); game.bikeTwo.moveDown
  })
}

var upTwo = () => {
  requestAnimationFrame( function gameLoop() {
    drawTwo(); game.bikeTwo.moveUp
  })
}

var leftTwo = () => {
  requestAnimationFrame( function gameLoop() {
    drawTwo(); game.bikeTwo.moveLeft
  })
}
<|endoftext|>


<commit_before>const request = require('request');
const debug = require('debug')('rachio.DataStore');

const http = opts => new Promise((resolve, reject) => {
  debug('http.Request %s %s', opts.method, opts.uri);
  return request(opts, (err, { statusCode }, body) => {
    if (err || statusCode >= 400) {
      const errorObj = { status: statusCode, errors: (err ? [err] : [body.error] || body.errors) };
      debug('http.Error: [%d] %s %s\n  %j', statusCode, opts.method, opts.uri, errorObj);
      return reject(new Error(`${errorObj.errors} [${statusCode}] ${opts.method} ${opts.uri}`));
    }
    debug('http.Response [%d] %s %s', statusCode, opts.method, opts.uri);
    return resolve(body);
  });
});

class DataStore {
  constructor(baseUrl, headers = {}) {
    this.baseUrl = baseUrl;
    this.headers = headers;
  }

  get(url, qs = {}, headers = {}) {
    return http({ method: 'GET',
      uri: `${this.baseUrl}/${url}`,
      headers: Object.assign({}, this.headers, headers),
      qs,
      json: true });
  }

  put(url, json = {}, qs = {}, headers = {}) {
    return http({ method: 'PUT',
      uri: `${this.baseUrl}/${url}`,
      headers: Object.assign({}, this.headers, headers),
      qs,
      json });
  }
}

module.exports = DataStore;
<commit_msg>Revised error handling; Non-error responses without body from RachioApi (i.e., 204s) will now return boolean 'true'<commit_after>const request = require('request');
const debug = require('debug')('rachio.DataStore');

const getErrorArr = (err, body) => {
  const errors = [];
  if (err) {
    return [err];
  }
  if (body && body.error) {
    return [body.error];
  }
  if (body && body.errors) {
    return body.errors.map(e => e.message || e);
  }
  return errors;
};

const http = opts => new Promise((resolve, reject) => {
  debug('http.Request %s %s', opts.method, opts.uri);
  return request(opts, (err, { statusCode }, body) => {
    if (err || statusCode >= 400) {
      const errorObj = { status: statusCode, errors: getErrorArr(err, body) };
      debug('http.Error: [%d] %s %s\n  %j', statusCode, opts.method, opts.uri, errorObj);
      return reject(new Error(`${errorObj.errors} [${statusCode}] ${opts.method} ${opts.uri}`));
    }
    debug('http.Response [%d] %s %s', statusCode, opts.method, opts.uri);
    return resolve(body || true);
  });
});

class DataStore {
  constructor(baseUrl, headers = {}) {
    this.baseUrl = baseUrl;
    this.headers = headers;
  }

  get(url, qs = {}, headers = {}) {
    return http({ method: 'GET',
      uri: `${this.baseUrl}/${url}`,
      headers: Object.assign({}, this.headers, headers),
      qs,
      json: true });
  }

  put(url, json = {}, qs = {}, headers = {}) {
    return http({ method: 'PUT',
      uri: `${this.baseUrl}/${url}`,
      headers: Object.assign({}, this.headers, headers),
      qs,
      json });
  }
}

module.exports = DataStore;
<|endoftext|>


<commit_before>
/*

  data-store.js

*/

var fs    = require("fs"),
    low   = require("lowdb"),
    Utils = require("./utils"),
    _     = require("lodash/core"),
    pair  = require("lodash/fromPairs");

/*

  function extendDatabase

  Adds a "get" function so that lowdb can handle key/values.

*/

function extendDatabase(db) {

  // Gets a database key
  db.get = function(key) {
    var val = db.object[key];
    return (Array.isArray(val)) ? db(key) : _(val);
  }

  // Runs a database query
  db.query = function(query, context, next) {
    query.run(db.get(query.query.table).value(), context, next);
  }

  // Executes multiple queries and combines into a single object
  db.queries = function(queries, context, next) {

    var results = {},
        remaining;

    // Ensure that queries is an array
    queries = queries || [];
    if (!Array.isArray(queries)) queries = [queries];

    remaining = queries.length;
    if (!remaining) return next(null, {});

    _.each(queries, function(query) {
      db.query(query.query, context, function(err, result) {
        if ("undefined" !== typeof query.idx) result = result[query.idx];
        results = _.extend(results, (query.key) ? pair([[query.key, result]]) : result);
        if (--remaining === 0) next(err, results);
      });
    });

  }

  return db;

}

module.exports = function(options) {

  var db = extendDatabase(low()),
      val;

  // Set defaults
  options = _.extend({ path : "./data", route : "/" }, options);

  Utils.mapFiles(options.path).forEach(function(file) {

    var json = JSON.parse(fs.readFileSync(file.path, "utf8")),
        slug = Utils.makeRoutes(options.route, { folder : file.folder, name : "" })[0]

    // If array, use file name as key. Otherwise, parse keys
    if (Array.isArray(json)) {

      key = (slug + "/" + file.name).replace(/\/+/g, "/").replace(/^\/+/, "");
      db.object[key] = json;

    } else {

      // Create path-like keys using directories (and remove leading "/")
      for (key in json) {

        // Remove leading "/" and make db key-friendly
        val = json[key];
        key = (slug + "/" + key).replace(/\/+/g, "/").replace(/^\/+/, "");
        db.object[key] = val;

      }
    }

  });

  return db;

}
<commit_msg>data-store: removes defaults (moving to config)<commit_after>
/*

  data-store.js

*/

var fs    = require("fs"),
    low   = require("lowdb"),
    Utils = require("./utils"),
    _     = require("lodash/core"),
    pair  = require("lodash/fromPairs");

/*

  function extendDatabase

  Adds a "get" function so that lowdb can handle key/values.

*/

function extendDatabase(db) {

  // Gets a database key
  db.get = function(key) {
    var val = db.object[key];
    return (Array.isArray(val)) ? db(key) : _(val);
  }

  // Runs a database query
  db.query = function(query, context, next) {
    query.run(db.get(query.query.table).value(), context, next);
  }

  // Executes multiple queries and combines into a single object
  db.queries = function(queries, context, next) {

    var results = {},
        remaining;

    // Ensure that queries is an array
    queries = queries || [];
    if (!Array.isArray(queries)) queries = [queries];

    remaining = queries.length;
    if (!remaining) return next(null, {});

    _.each(queries, function(query) {
      db.query(query.query, context, function(err, result) {
        if ("undefined" !== typeof query.idx) result = result[query.idx];
        results = _.extend(results, (query.key) ? pair([[query.key, result]]) : result);
        if (--remaining === 0) next(err, results);
      });
    });

  }

  return db;

}

module.exports = function(options) {

  var db = extendDatabase(low()),
      val;

  Utils.mapFiles(options.path).forEach(function(file) {

    var json = JSON.parse(fs.readFileSync(file.path, "utf8")),
        slug = Utils.makeRoutes(options.route, { folder : file.folder, name : "" })[0]

    // If array, use file name as key. Otherwise, parse keys
    if (Array.isArray(json)) {

      key = (slug + "/" + file.name).replace(/\/+/g, "/").replace(/^\/+/, "");
      db.object[key] = json;

    } else {

      // Create path-like keys using directories (and remove leading "/")
      for (key in json) {

        // Remove leading "/" and make db key-friendly
        val = json[key];
        key = (slug + "/" + key).replace(/\/+/g, "/").replace(/^\/+/, "");
        db.object[key] = val;

      }
    }

  });

  return db;

}
<|endoftext|>


<commit_before>import React from 'react';
import autoBind from 'react-autobind';
import alertify from 'alertifyjs';
import Select from 'react-select';
import ToggleSwitch from 'js/components/toggleSwitch';
import TextBox from 'js/components/textBox';
import {actions} from '../../actions';
import {bem} from 'js/bem';

/*
 * Modal for connecting project data
 */
class ConnectProjects extends React.Component {
  constructor(props) {
    super(props);
    this.state = {
      isVirgin: true,
      isLoading: false,
      // `data_sharing` is an empty object if never enabled before
      isShared: props.asset.data_sharing?.enabled || false,
      attachedParents: [],
      sharingEnabledAssets: null,
      newParent: null,
      newFilename: '',
      fieldsErrors: {},
    };

    autoBind(this);
  }

  /*
   * Setup
   */

  componentDidMount() {
    this.refreshAttachmentList();
    actions.dataShare.getSharingEnabledAssets();

    actions.dataShare.attachToParent.completed.listen(
      this.refreshAttachmentList
    );
    actions.dataShare.attachToParent.failed.listen(
      this.onAttachToParentFailed
    );
    actions.dataShare.detachParent.completed.listen(
      this.refreshAttachmentList
    );
    actions.dataShare.getSharingEnabledAssets.completed.listen(
      this.onGetSharingEnabledAssetsCompleted
    );
    actions.dataShare.getAttachedParents.completed.listen(
      this.onGetAttachedParentsCompleted
    );
    actions.dataShare.toggleDataSharing.completed.listen(
      this.onToggleDataSharingCompleted
    );
  }

  /*
   * `actions` Listeners
   */

  onAttachToParentFailed(response) {
    this.setState({
      isLoading: false,
      fieldsErrors: response.responseJSON || {},
    });
  }
  onGetAttachedParentsCompleted(response) {
    this.setState({
      isVirgin: false,
      isLoading: false,
      attachedParents: response,
    });
  }
  onGetSharingEnabledAssetsCompleted(response) {
    this.setState({sharingEnabledAssets: response});
  }
  onToggleDataSharingCompleted() {
    this.setState({isShared: !this.state.isShared});
  }
  refreshAttachmentList() {
    this.setState({
      newParent: null,
      newFilename: '',
    });
    actions.dataShare.getAttachedParents(this.props.asset.uid);
  }

  /*
   * UI Listeners
   */

  confirmAttachment() {

    let parentUrl = this.state.newParent?.url;
    let filename = this.state.newFilename;
    if (filename !== '' && parentUrl) {
      this.setState({
        isLoading: true,
        fieldsErrors: {},
      });

      var data = JSON.stringify({
        parent: parentUrl,
        fields: [],
        filename: filename,
      });
      actions.dataShare.attachToParent(this.props.asset.uid, data);
    } else {
      if (!parentUrl) {
        this.setState({
          fieldsErrors: Object.assign(
            this.state.fieldsErrors, {emptyParent: 'No project selected'}
          )
        });
      }
      if (filename === '') {
        this.setState({
          fieldsErrors: Object.assign(
            this.state.fieldsErrors, {emptyFilename: 'Field is empty'}
          )
        });
      }
    }
  }
  onParentChange(newVal) {
    this.setState({
      newParent: newVal,
      fieldsErrors: {},
    });
    this.generateAutoname(newVal);
  }
  onFilenameChange(newVal) {
    this.setState({
      newFilename: newVal,
      fieldsErrors: {},
    });
  }
  removeAttachment(newVal) {
    this.setState({isLoading: true})
    actions.dataShare.detachParent(newVal);
  }
  toggleSharingData() {
    var data = JSON.stringify({
      data_sharing: {
        enabled: !this.state.isShared
      }
    });

    if (!this.state.isShared) {
      let dialog = alertify.dialog('confirm');
      let opts = {
        title: `${t('Privacy Notice')}`,
        message: t('This will attach the full dataset from \"##ASSET_NAME##\" as a background XML file to this form. While not easily visible, it is technically possible for anyone entering data to your form to retrieve and view this dataset. Do not use this feature if \"##ASSET_NAME##\" includes sensitive data.').replaceAll('##ASSET_NAME##', this.props.asset.name),
        labels: {ok: t('Acknowledge and continue'), cancel: t('Cancel')},
        onok: (evt, value) => {
          actions.dataShare.toggleDataSharing(this.props.asset.uid, data);
          dialog.destroy();
        },
        oncancel: () => {
          dialog.destroy();
        }
      };
      dialog.set(opts).show();
    } else {
      actions.dataShare.toggleDataSharing(this.props.asset.uid, data);
    }
  }

  /*
   * Utilities
   */

  generateAutoname(newParent) {
    if (newParent) {
      let autoname = newParent.name;
      autoname = autoname.toLowerCase().substring(0, 30).replace(/(\ |\.)/g, '_');
      this.setState({newFilename: autoname});
    }
  }
  generateTruncatedDisplayName(name) {
    return name.length > 30 ? `${name.substring(0, 30)}...` : name;
  }
  generateFilteredAssetList() {
    let attachedParentUids = [];
    this.state.attachedParents.forEach((item) => {
      attachedParentUids.push(item.parentUid)
    });

    // Filter displayed asset list based on unattached projects
    return (
      this.state.sharingEnabledAssets.results.filter(
        item => !attachedParentUids.includes(item.uid)
      )
    );
  }

 /*
  * May be useful later for replacing autoname with existing name
  *
  * getExteralFilename() {
  *   let filename = '';
  *   this.props.asset.content.survey.some((element) => {
  *     if (element.type === XML_EXTERNAL) {
  *       filename = element.name;
  *     }
  *   });
  *   return filename;
  * }
  */

  /*
   * Rendering
   */

  renderLoading(message = t('loading…')) {
    return (
      <bem.Loading>
        <bem.Loading__inner>

          {message}
        </bem.Loading__inner>
      </bem.Loading>
    );
  }
  renderSwitchLabel() {
    if (this.state.isShared) {
      return (
        <ToggleSwitch
          onChange={this.toggleSharingData.bind(this)}
          label={t('Data sharing enabled')}
          checked={this.state.isShared}
        />
      );
    } else {
      return (
        <ToggleSwitch
          onChange={this.toggleSharingData.bind(this)}
          label={t('Data sharing disabled')}
          checked={this.state.isShared}
        />
      );
    }
  }
  renderSelect(sharingEnabledAssets) {
    const selectClassNames = ['kobo-select__wrapper'];
    if (this.state.fieldsErrors.emptyParent || this.state.fieldsErrors.parent) {
      selectClassNames.push('kobo-select__wrapper--error');
    }
    return(

        <Select
          placeholder={t('Select a different project to import data from')}
          options={sharingEnabledAssets}
          value={this.state.newParent}
          isLoading={(this.state.isVirgin || this.state.isLoading || !sharingEnabledAssets)}
          getOptionLabel={option => option.name}
          getOptionValue={option => option.url}
          noOptionsMessage={() => {return t('No projects to connect')}}
          onChange={this.onParentChange}
          className='kobo-select'
          classNamePrefix='kobo-select'
        />

          {this.state.fieldsErrors.emptyParent || this.state.fieldsErrors.parent}

    );
  }

  render() {
    let sharingEnabledAssets = [];
    if (this.state.sharingEnabledAssets !== null) {
      sharingEnabledAssets = this.generateFilteredAssetList();
    }

    return (
      <bem.FormModal__form
        className='project-settings project-settings--upload-file connect-projects'
        onSubmit={this.confirmAttachment}
      >
        {/* Enable data sharing */}
        <bem.FormModal__item m='data-sharing'>

{t('Share data with other project forms')}

            {t('Enable data sharing to allow other forms to import and use dynamic data from this project. Learn more about dynamic data attachments')}
            {t(' ' + 'here')}

          {this.renderSwitchLabel()}
        </bem.FormModal__item>

        {/* Attach other projects data */}
        <bem.FormModal__item m='import-data'>

{t('Import other project data')}

            {t('Connect with other project(s) to import dynamic data from them into this project. Learn more about dynamic data attachments')}
            {t(' ' + 'here')}

          {/* Selecting project form*/}
          {sharingEnabledAssets &&

              {this.renderSelect(sharingEnabledAssets)}
              <TextBox
                placeholder={t('Give a unique name to the import')}
                value={this.state.newFilename}
                onChange={this.onFilenameChange}
                errors={this.state.fieldsErrors.emptyFilename ||
                        this.state.fieldsErrors.filename}
              />
              <bem.KoboButton m='blue'>
                {t('Import')}
              </bem.KoboButton>

          }

          {/* Display attached projects */}

            {t('Imported')}
            {(this.state.isVirgin || this.state.isLoading) &&

                {this.renderLoading(t('Loading imported projects'))}

            }
            {!this.state.isLoading && this.state.attachedParents.length == 0 &&

                {t('No data imported')}

            }
            {!this.state.isLoading && this.state.attachedParents.length > 0 &&
                this.state.attachedParents.map((item, n) => {
                  return (

                          {item.filename}

                          {this.generateTruncatedDisplayName(item.parentName)}

                       this.removeAttachment(item.attachmentUrl)}
                      />

                  );
                })
            }

        </bem.FormModal__item>

      </bem.FormModal__form>
    );
  }
}

export default ConnectProjects;
<commit_msg>added missing refactored toggleSwitch component<commit_after>import React from 'react';
import autoBind from 'react-autobind';
import alertify from 'alertifyjs';
import Select from 'react-select';
import ToggleSwitch from 'js/components/common/toggleSwitch';
import TextBox from 'js/components/common/textBox';
import {actions} from '../../actions';
import {bem} from 'js/bem';

/*
 * Modal for connecting project data
 */
class ConnectProjects extends React.Component {
  constructor(props) {
    super(props);
    this.state = {
      isVirgin: true,
      isLoading: false,
      // `data_sharing` is an empty object if never enabled before
      isShared: props.asset.data_sharing?.enabled || false,
      attachedParents: [],
      sharingEnabledAssets: null,
      newParent: null,
      newFilename: '',
      fieldsErrors: {},
    };

    autoBind(this);
  }

  /*
   * Setup
   */

  componentDidMount() {
    this.refreshAttachmentList();
    actions.dataShare.getSharingEnabledAssets();

    actions.dataShare.attachToParent.completed.listen(
      this.refreshAttachmentList
    );
    actions.dataShare.attachToParent.failed.listen(
      this.onAttachToParentFailed
    );
    actions.dataShare.detachParent.completed.listen(
      this.refreshAttachmentList
    );
    actions.dataShare.getSharingEnabledAssets.completed.listen(
      this.onGetSharingEnabledAssetsCompleted
    );
    actions.dataShare.getAttachedParents.completed.listen(
      this.onGetAttachedParentsCompleted
    );
    actions.dataShare.toggleDataSharing.completed.listen(
      this.onToggleDataSharingCompleted
    );
  }

  /*
   * `actions` Listeners
   */

  onAttachToParentFailed(response) {
    this.setState({
      isLoading: false,
      fieldsErrors: response.responseJSON || {},
    });
  }
  onGetAttachedParentsCompleted(response) {
    this.setState({
      isVirgin: false,
      isLoading: false,
      attachedParents: response,
    });
  }
  onGetSharingEnabledAssetsCompleted(response) {
    this.setState({sharingEnabledAssets: response});
  }
  onToggleDataSharingCompleted() {
    this.setState({isShared: !this.state.isShared});
  }
  refreshAttachmentList() {
    this.setState({
      newParent: null,
      newFilename: '',
    });
    actions.dataShare.getAttachedParents(this.props.asset.uid);
  }

  /*
   * UI Listeners
   */

  confirmAttachment() {

    let parentUrl = this.state.newParent?.url;
    let filename = this.state.newFilename;
    if (filename !== '' && parentUrl) {
      this.setState({
        isLoading: true,
        fieldsErrors: {},
      });

      var data = JSON.stringify({
        parent: parentUrl,
        fields: [],
        filename: filename,
      });
      actions.dataShare.attachToParent(this.props.asset.uid, data);
    } else {
      if (!parentUrl) {
        this.setState({
          fieldsErrors: Object.assign(
            this.state.fieldsErrors, {emptyParent: 'No project selected'}
          )
        });
      }
      if (filename === '') {
        this.setState({
          fieldsErrors: Object.assign(
            this.state.fieldsErrors, {emptyFilename: 'Field is empty'}
          )
        });
      }
    }
  }
  onParentChange(newVal) {
    this.setState({
      newParent: newVal,
      fieldsErrors: {},
    });
    this.generateAutoname(newVal);
  }
  onFilenameChange(newVal) {
    this.setState({
      newFilename: newVal,
      fieldsErrors: {},
    });
  }
  removeAttachment(newVal) {
    this.setState({isLoading: true})
    actions.dataShare.detachParent(newVal);
  }
  toggleSharingData() {
    var data = JSON.stringify({
      data_sharing: {
        enabled: !this.state.isShared
      }
    });

    if (!this.state.isShared) {
      let dialog = alertify.dialog('confirm');
      let opts = {
        title: `${t('Privacy Notice')}`,
        message: t('This will attach the full dataset from \"##ASSET_NAME##\" as a background XML file to this form. While not easily visible, it is technically possible for anyone entering data to your form to retrieve and view this dataset. Do not use this feature if \"##ASSET_NAME##\" includes sensitive data.').replaceAll('##ASSET_NAME##', this.props.asset.name),
        labels: {ok: t('Acknowledge and continue'), cancel: t('Cancel')},
        onok: (evt, value) => {
          actions.dataShare.toggleDataSharing(this.props.asset.uid, data);
          dialog.destroy();
        },
        oncancel: () => {
          dialog.destroy();
        }
      };
      dialog.set(opts).show();
    } else {
      actions.dataShare.toggleDataSharing(this.props.asset.uid, data);
    }
  }

  /*
   * Utilities
   */

  generateAutoname(newParent) {
    if (newParent) {
      let autoname = newParent.name;
      autoname = autoname.toLowerCase().substring(0, 30).replace(/(\ |\.)/g, '_');
      this.setState({newFilename: autoname});
    }
  }
  generateTruncatedDisplayName(name) {
    return name.length > 30 ? `${name.substring(0, 30)}...` : name;
  }
  generateFilteredAssetList() {
    let attachedParentUids = [];
    this.state.attachedParents.forEach((item) => {
      attachedParentUids.push(item.parentUid)
    });

    // Filter displayed asset list based on unattached projects
    return (
      this.state.sharingEnabledAssets.results.filter(
        item => !attachedParentUids.includes(item.uid)
      )
    );
  }

 /*
  * May be useful later for replacing autoname with existing name
  *
  * getExteralFilename() {
  *   let filename = '';
  *   this.props.asset.content.survey.some((element) => {
  *     if (element.type === XML_EXTERNAL) {
  *       filename = element.name;
  *     }
  *   });
  *   return filename;
  * }
  */

  /*
   * Rendering
   */

  renderLoading(message = t('loading…')) {
    return (
      <bem.Loading>
        <bem.Loading__inner>

          {message}
        </bem.Loading__inner>
      </bem.Loading>
    );
  }
  renderSwitchLabel() {
    if (this.state.isShared) {
      return (
        <ToggleSwitch
          onChange={this.toggleSharingData.bind(this)}
          label={t('Data sharing enabled')}
          checked={this.state.isShared}
        />
      );
    } else {
      return (
        <ToggleSwitch
          onChange={this.toggleSharingData.bind(this)}
          label={t('Data sharing disabled')}
          checked={this.state.isShared}
        />
      );
    }
  }
  renderSelect(sharingEnabledAssets) {
    const selectClassNames = ['kobo-select__wrapper'];
    if (this.state.fieldsErrors.emptyParent || this.state.fieldsErrors.parent) {
      selectClassNames.push('kobo-select__wrapper--error');
    }
    return(

        <Select
          placeholder={t('Select a different project to import data from')}
          options={sharingEnabledAssets}
          value={this.state.newParent}
          isLoading={(this.state.isVirgin || this.state.isLoading || !sharingEnabledAssets)}
          getOptionLabel={option => option.name}
          getOptionValue={option => option.url}
          noOptionsMessage={() => {return t('No projects to connect')}}
          onChange={this.onParentChange}
          className='kobo-select'
          classNamePrefix='kobo-select'
        />

          {this.state.fieldsErrors.emptyParent || this.state.fieldsErrors.parent}

    );
  }

  render() {
    let sharingEnabledAssets = [];
    if (this.state.sharingEnabledAssets !== null) {
      sharingEnabledAssets = this.generateFilteredAssetList();
    }

    return (
      <bem.FormModal__form
        className='project-settings project-settings--upload-file connect-projects'
        onSubmit={this.confirmAttachment}
      >
        {/* Enable data sharing */}
        <bem.FormModal__item m='data-sharing'>

{t('Share data with other project forms')}

            {t('Enable data sharing to allow other forms to import and use dynamic data from this project. Learn more about dynamic data attachments')}
            {t(' ' + 'here')}

          {this.renderSwitchLabel()}
        </bem.FormModal__item>

        {/* Attach other projects data */}
        <bem.FormModal__item m='import-data'>

{t('Import other project data')}

            {t('Connect with other project(s) to import dynamic data from them into this project. Learn more about dynamic data attachments')}
            {t(' ' + 'here')}

          {/* Selecting project form*/}
          {sharingEnabledAssets &&

              {this.renderSelect(sharingEnabledAssets)}
              <TextBox
                placeholder={t('Give a unique name to the import')}
                value={this.state.newFilename}
                onChange={this.onFilenameChange}
                errors={this.state.fieldsErrors.emptyFilename ||
                        this.state.fieldsErrors.filename}
              />
              <bem.KoboButton m='blue'>
                {t('Import')}
              </bem.KoboButton>

          }

          {/* Display attached projects */}

            {t('Imported')}
            {(this.state.isVirgin || this.state.isLoading) &&

                {this.renderLoading(t('Loading imported projects'))}

            }
            {!this.state.isLoading && this.state.attachedParents.length == 0 &&

                {t('No data imported')}

            }
            {!this.state.isLoading && this.state.attachedParents.length > 0 &&
                this.state.attachedParents.map((item, n) => {
                  return (

                          {item.filename}

                          {this.generateTruncatedDisplayName(item.parentName)}

                       this.removeAttachment(item.attachmentUrl)}
                      />

                  );
                })
            }

        </bem.FormModal__item>

      </bem.FormModal__form>
    );
  }
}

export default ConnectProjects;
<|endoftext|>