<commit_before>(function ($) {
    // extend jQuery --------------------------------------------------------------------
    $.fn.butterCombobox = function () {
        return this.each(function () {
            new FilterableCombobox(this);
        });
    };

    // define objects --------------------------------------------------------------------

    var FilterableCombobox = Class.extend({
        init: function (rootElement) {
            this.$select = $(rootElement).find("select");
            this.$ghostInput = null;
            this.$resultContainer = null;
            this.$resultListContainer = null;
            this.$options = this.$select.find("option");
            this.optionResultList = [];
            this.$selectedOption = null;
            this._hasFocus = false;

            this._keyCodes = {
                //backspace: 8,
                tab: 9,
                enter: 13,
                shift: 16,
                ctrl: 17,
                alt: 18,
                pause: 19,
                caps_lock: 20,
                escape: 27,
                page_up: 33,
                page_down: 34,
                end: 35,
                home: 36,
                arrow_left: 37,
                arrow_up: 38,
                arrow_right: 39,
                arrow_down: 40,
                insert: 45,
                // 'delete' is a reserved key word
                delete_key: 46,
                left_window_key: 91,
                right_window_key: 92,
                select_key: 93,
                num_lock: 144,
                scroll_lock: 145
            };

            this._initializeGhostElement();
            this._initializeDropDownButton();
        },

        _initializeGhostElement: function () {
            var self = this;
            self.$ghostInput = self.$select.parent().find(".butter-component-combobox-ghost")
                //.addClass($select.attr("class"))
                .val(self.$select.find("option:selected").text())
                .on("focus", function () {
                    self._hasFocus = true;
                    // automatically select whole text on focus
                    this.setSelectionRange(0, $(this).val().length)
                })
                .on("mouseup", function (event) {
                    // in safari the mouseup event unselects the text, so we have to prevent this
                    self._stopEvent(event);
                })
                .on("blur", function () {
                    self._hasFocus = false;
                    window.setTimeout(function () {
                        if (!self._hasFocus) {
                            self._hideOptionResultList();
                            self._resetDisplayValue();
                        }
                    }, 200);
                })
                .on("keyup", function (event) {
                    // don't handle other keys than character keys
                    for (keyName in self._keyCodes) {
                        if (self._keyCodes[keyName] === event.which) {
                            self._stopEvent(event);
                            return;
                        }
                    }

                    var searchText = $(this).val();
                    self._createOptionResultList(searchText);
                    self._showOptionResultList(searchText);
                })
                .on("keydown", function (event) {
                    if (event.which === self._keyCodes.enter) {
                        self._stopEvent(event);
                        if (self.$selectedOption !== null) {
                            self._setSelectedValue();
                        }
                    } else if (event.which === self._keyCodes.arrow_up || event.which === self._keyCodes.arrow_down) {
                        self._stopEvent(event);
                        if (self.optionResultList.length === 0) {
                            self._createOptionResultList();
                        }
                        if (self.$resultContainer === null) {
                            self._showOptionResultList();
                        }
                        if (self.$selectedOption === null) {
                            self._selectResultOptionElement(self.$resultListContainer.children()[0]);
                        } else {
                            self._moveResultOptionElementSelectionCursor(event.which === self._keyCodes.arrow_up ? -1 : 1);
                        }
                    } else if (event.which === self._keyCodes.escape) {
                        if (self.$resultContainer !== null) {
                            self._hideOptionResultList();
                            self._resetDisplayValue();
                            this.setSelectionRange(0, $(this).val().length);
                        } else {
                            self.$ghostInput.blur();
                        }
                    }
                });

        },

        _initializeDropDownButton: function () {
            var self = this;
            self.$ghostInput.next()
                .on("click", function (event) {
                    self._stopEvent(event);
                    self._createOptionResultList();
                    self._showOptionResultList();
                    self.$ghostInput.focus();
                });
        },

        _createOptionResultList: function (searchText) {
            var self = this;
            self.optionResultList = [];
            if (!searchText || searchText === "") {
                self.$options.each(function () {
                    var $option = $(this);
                    self.optionResultList.push($option);
                });
            } else {
                self.$options.each(function () {
                    var $option = $(this);
                    if ($option.text().toLowerCase().indexOf(searchText.toLowerCase()) >= 0) {
                        self.optionResultList.push($option);
                    }
                });
            }
        },

        _showOptionResultList: function (searchText) {
            // create container elements if necessary
            if (this.$resultContainer === null) {
                var $inputGroup = this.$ghostInput.parent();
                var inputGroupOffset = $inputGroup.offset();
                this.$resultContainer = $("
")
                    .addClass("butter-component-combobox-resultContainer")
                    .css({
                        position: "absolute",
                        left: inputGroupOffset.left,
                        top: inputGroupOffset.top + $inputGroup.outerHeight(),
                        width: $inputGroup.innerWidth(),
                        zIndex: 1000
                    });
                this.$resultListContainer = $("
")
                    .addClass("butter-component-combobox-resultListContainer")
                    .appendTo(this.$resultContainer);
                $("body").append(this.$resultContainer);
            }

            this.$resultListContainer.empty();
            this.$selectedOption = null;

            if (this.optionResultList.length > 0) {
                for (var i = 0; i < this.optionResultList.length; i++) {
                    var resultItemHtml;
                    var resultItemLabel;
                    var resultItemText = this.optionResultList[i].text();
                    var separatorIndex = resultItemText.indexOf(" - ");
                    if (separatorIndex > 0) {
                        resultItemLabel = resultItemText.substring(0, separatorIndex);
                        resultItemHtml = "" + resultItemLabel + "";
                        resultItemHtml += "
" + resultItemText.substr(separatorIndex + 3);
                    } else {
                        resultItemLabel = resultItemText;
                        resultItemHtml = "" + resultItemText + "";
                    }

                    var self = this;
                    $("
")
                        .html(resultItemHtml)
                        .attr("data-select-value", this.optionResultList[i].val())
                        .attr("data-select-label", resultItemLabel)
                        .addClass("butter-component-combobox-resultItem")
                        .on("click", function () {
                            self._setSelectedValue();
                        })
                        .on("mouseenter", function () {
                            self._selectResultOptionElement(this);
                        })
                        .appendTo(self.$resultListContainer)
                        .highlight(searchText);
                }
            } else {
                $("
")
                    .text("Keine Einträge vorhanden!")
                    .addClass("butter-component-combobox-noResultItems")
                    .appendTo(this.$resultListContainer);
            }
        },

        _hideOptionResultList: function () {
            var self = this;
            if (self.$resultContainer !== null) {
                self.$resultContainer.remove();
                self.$resultContainer = null;
                self.$resultListContainer = null;
            }
        },

        _selectResultOptionElement: function (optionElement) {
            var selectedOptionElement = $(optionElement);
            selectedOptionElement.addClass("butter-component-combobox-resultItem-selected")
                .siblings().removeClass("butter-component-combobox-resultItem-selected");
            this.$selectedOption = selectedOptionElement;
        },

        _moveResultOptionElementSelectionCursor: function (direction) {
            if (direction > 0) {
                var $next = this.$selectedOption.next();
                if ($next.length > 0) {
                    this._selectResultOptionElement($next[0]);
                } else {
                    //there is no next
                    this._selectResultOptionElement(this.$resultListContainer.children()[0]);
                }
            } else {
                var $prev = this.$selectedOption.prev();
                if ($prev.length > 0) {
                    this._selectResultOptionElement($prev[0]);
                } else {
                    //there is no previous
                    var resultListOptions = this.$resultListContainer.children();
                    this._selectResultOptionElement(resultListOptions[resultListOptions.length - 1]);
                }
            }
        },

        _setSelectedValue: function () {
            var $selectedOption = this.$selectedOption;
            if ($selectedOption !== null) {
                var valueData = $selectedOption.attr("data-select-value");
                if(valueData !== undefined) {
                    this.$select
                        .val(valueData)
                        .change();
                    this.$ghostInput.val($selectedOption.attr("data-select-label"));
                    this._hideOptionResultList();
                }
            }
        },

        _resetDisplayValue: function () {
            // set old value
            this.$ghostInput.val(this.$select.find("option:selected").text());
        },

        _stopEvent: function (event) {
            event.preventDefault();
            //event.preventBubble();
        }
    });
}
(jQuery)
)
;<commit_msg>BUT-301 Fixed: Click on chevron-down does open select but does not close it<commit_after>(function ($) {
    // extend jQuery --------------------------------------------------------------------
    $.fn.butterCombobox = function () {
        return this.each(function () {
            new FilterableCombobox(this);
        });
    };

    // define objects --------------------------------------------------------------------

    var FilterableCombobox = Class.extend({
        init: function (rootElement) {
            this.$select = $(rootElement).find("select");
            this.$ghostInput = null;
            this.$resultContainer = null;
            this.$resultListContainer = null;
            this.$options = this.$select.find("option");
            this.optionResultList = [];
            this.$selectedOption = null;
            this._hasFocus = false;
            this.isMouseClickeBlocked = false;

            this._keyCodes = {
                //backspace: 8,
                tab: 9,
                enter: 13,
                shift: 16,
                ctrl: 17,
                alt: 18,
                pause: 19,
                caps_lock: 20,
                escape: 27,
                page_up: 33,
                page_down: 34,
                end: 35,
                home: 36,
                arrow_left: 37,
                arrow_up: 38,
                arrow_right: 39,
                arrow_down: 40,
                insert: 45,
                // 'delete' is a reserved key word
                delete_key: 46,
                left_window_key: 91,
                right_window_key: 92,
                select_key: 93,
                num_lock: 144,
                scroll_lock: 145
            };

            this._initializeGhostElement();
            this._initializeDropDownButton();
        },

        _initializeGhostElement: function () {
            var self = this;
            self.$ghostInput = self.$select.parent().find(".butter-component-combobox-ghost")
                //.addClass($select.attr("class"))
                .val(self.$select.find("option:selected").text())
                .on("focus", function () {
                    self._hasFocus = true;
                    // automatically select whole text on focus
                    this.setSelectionRange(0, $(this).val().length)
                })
                .on("mouseup", function (event) {
                    // in safari the mouseup event unselects the text, so we have to prevent this
                    self._stopEvent(event);
                })
                .on("blur", function () {
                    self._hasFocus = false;
                    self.isMouseClickeBlocked = true;

                    window.setTimeout(function () {
                        if (!self._hasFocus) {
                            self._hideOptionResultList();
                            self._resetDisplayValue();
                            self.isMouseClickeBlocked = false;
                        }
                    }, 200);
                })
                .on("keyup", function (event) {
                    // don't handle other keys than character keys
                    for (keyName in self._keyCodes) {
                        if (self._keyCodes[keyName] === event.which) {
                            self._stopEvent(event);
                            return;
                        }
                    }

                    var searchText = $(this).val();
                    self._createOptionResultList(searchText);
                    self._showOptionResultList(searchText);
                })
                .on("keydown", function (event) {
                    if (event.which === self._keyCodes.enter) {
                        self._stopEvent(event);
                        if (self.$selectedOption !== null) {
                            self._setSelectedValue();
                        }
                    } else if (event.which === self._keyCodes.arrow_up || event.which === self._keyCodes.arrow_down) {
                        self._stopEvent(event);
                        if (self.optionResultList.length === 0) {
                            self._createOptionResultList();
                        }
                        if (self.$resultContainer === null) {
                            self._showOptionResultList();
                        }
                        if (self.$selectedOption === null) {
                            self._selectResultOptionElement(self.$resultListContainer.children()[0]);
                        } else {
                            self._moveResultOptionElementSelectionCursor(event.which === self._keyCodes.arrow_up ? -1 : 1);
                        }
                    } else if (event.which === self._keyCodes.escape) {
                        if (self.$resultContainer !== null) {
                            self._hideOptionResultList();
                            self._resetDisplayValue();
                            this.setSelectionRange(0, $(this).val().length);
                        } else {
                            self.$ghostInput.blur();
                        }
                    }
                });

        },

        _initializeDropDownButton: function () {
            var self = this;
            self.$ghostInput.next()
                .on("click", function (event) {
                    self._stopEvent(event);

                    if(self.isMouseClickeBlocked){
                        return;
                    }

                    self._createOptionResultList();
                    self._showOptionResultList();
                    self.$ghostInput.focus();
                });
        },

        _createOptionResultList: function (searchText) {
            var self = this;
            self.optionResultList = [];
            if (!searchText || searchText === "") {
                self.$options.each(function () {
                    var $option = $(this);
                    self.optionResultList.push($option);
                });
            } else {
                self.$options.each(function () {
                    var $option = $(this);
                    if ($option.text().toLowerCase().indexOf(searchText.toLowerCase()) >= 0) {
                        self.optionResultList.push($option);
                    }
                });
            }
        },

        _showOptionResultList: function (searchText) {
            // create container elements if necessary
            if (this.$resultContainer === null) {
                var $inputGroup = this.$ghostInput.parent();
                var inputGroupOffset = $inputGroup.offset();
                this.$resultContainer = $("
")
                    .addClass("butter-component-combobox-resultContainer")
                    .css({
                        position: "absolute",
                        left: inputGroupOffset.left,
                        top: inputGroupOffset.top + $inputGroup.outerHeight(),
                        width: $inputGroup.innerWidth(),
                        zIndex: 1000
                    });
                this.$resultListContainer = $("
")
                    .addClass("butter-component-combobox-resultListContainer")
                    .appendTo(this.$resultContainer);
                $("body").append(this.$resultContainer);
            }

            this.$resultListContainer.empty();
            this.$selectedOption = null;

            if (this.optionResultList.length > 0) {
                for (var i = 0; i < this.optionResultList.length; i++) {
                    var resultItemHtml;
                    var resultItemLabel;
                    var resultItemText = this.optionResultList[i].text();
                    var separatorIndex = resultItemText.indexOf(" - ");
                    if (separatorIndex > 0) {
                        resultItemLabel = resultItemText.substring(0, separatorIndex);
                        resultItemHtml = "" + resultItemLabel + "";
                        resultItemHtml += "
" + resultItemText.substr(separatorIndex + 3);
                    } else {
                        resultItemLabel = resultItemText;
                        resultItemHtml = "" + resultItemText + "";
                    }

                    var self = this;
                    $("
")
                        .html(resultItemHtml)
                        .attr("data-select-value", this.optionResultList[i].val())
                        .attr("data-select-label", resultItemLabel)
                        .addClass("butter-component-combobox-resultItem")
                        .on("click", function () {
                            self._setSelectedValue();
                        })
                        .on("mouseenter", function () {
                            self._selectResultOptionElement(this);
                        })
                        .appendTo(self.$resultListContainer)
                        .highlight(searchText);
                }
            } else {
                $("
")
                    .text("Keine Einträge vorhanden!")
                    .addClass("butter-component-combobox-noResultItems")
                    .appendTo(this.$resultListContainer);
            }
        },

        _hideOptionResultList: function () {
            var self = this;
            if (self.$resultContainer !== null) {
                self.$resultContainer.remove();
                self.$resultContainer = null;
                self.$resultListContainer = null;
            }
        },

        _selectResultOptionElement: function (optionElement) {
            var selectedOptionElement = $(optionElement);
            selectedOptionElement.addClass("butter-component-combobox-resultItem-selected")
                .siblings().removeClass("butter-component-combobox-resultItem-selected");
            this.$selectedOption = selectedOptionElement;
        },

        _moveResultOptionElementSelectionCursor: function (direction) {
            if (direction > 0) {
                var $next = this.$selectedOption.next();
                if ($next.length > 0) {
                    this._selectResultOptionElement($next[0]);
                } else {
                    //there is no next
                    this._selectResultOptionElement(this.$resultListContainer.children()[0]);
                }
            } else {
                var $prev = this.$selectedOption.prev();
                if ($prev.length > 0) {
                    this._selectResultOptionElement($prev[0]);
                } else {
                    //there is no previous
                    var resultListOptions = this.$resultListContainer.children();
                    this._selectResultOptionElement(resultListOptions[resultListOptions.length - 1]);
                }
            }
        },

        _setSelectedValue: function () {
            var $selectedOption = this.$selectedOption;
            if ($selectedOption !== null) {
                var valueData = $selectedOption.attr("data-select-value");
                if(valueData !== undefined) {
                    this.$select
                        .val(valueData)
                        .change();
                    this.$ghostInput.val($selectedOption.attr("data-select-label"));
                    this._hideOptionResultList();
                }
            }
        },

        _resetDisplayValue: function () {
            // set old value
            this.$ghostInput.val(this.$select.find("option:selected").text());
        },

        _stopEvent: function (event) {
            event.preventDefault();
            //event.preventBubble();
        }
    });
}
(jQuery)
)
;<|endoftext|>


<commit_before>const readFile = require('./fs').readFile;
const writeFile = require('./fs').writeFile;
const compose = require('lodash').flowRight;
const getReactVersion = require('../getReactNativeVersion');
const getPrefix = require('./getPrefix');

const applyPatch = (filePath, patch) =>
  compose(writeFile(filePath), patch, readFile(filePath));

module.exports = function registerNativeAndroidModule(name, androidConfig, params, projectConfig) {
  const prefix = getPrefix(getReactVersion(projectConfig.folder));
  const makeSettingsPatch = require(`./patches/makeSettingsPatch`);
  const makeBuildPatch = require(`./patches/makeBuildPatch`);
  const makeMainActivityPatch = require(`./${prefix}/makeMainActivityPatch`);

  const performSettingsGradlePatch = applyPatch(
    projectConfig.settingsGradlePath,
    makeSettingsPatch.apply(null, arguments)
  );

  const performBuildGradlePatch = applyPatch(
    projectConfig.buildGradlePath,
    makeBuildPatch(name)
  );

  const performMainActivityPatch = applyPatch(
    projectConfig.mainActivityPath,
    makeMainActivityPatch(androidConfig, params)
  );

  /**
   * Check if module has been installed already
   */
  const isInstalled = compose(
    (content) => ~content.indexOf(`:${name}`),
    readFile(projectConfig.buildGradlePath)
  );

  if (isInstalled(name)) {
    return false;
  }

  compose(
    performSettingsGradlePatch,
    performBuildGradlePatch,
    performMainActivityPatch
  )();

  return true;
};
<commit_msg>Eslint please be green<commit_after>const readFile = require('./fs').readFile;
const writeFile = require('./fs').writeFile;
const compose = require('lodash').flowRight;
const getReactVersion = require('../getReactNativeVersion');
const getPrefix = require('./getPrefix');

const applyPatch = (filePath, patch) =>
  compose(writeFile(filePath), patch, readFile(filePath));

module.exports = function registerNativeAndroidModule(name, androidConfig, params, projectConfig) {
  const prefix = getPrefix(getReactVersion(projectConfig.folder));
  const makeSettingsPatch = require(`./patches/makeSettingsPatch`);
  const makeBuildPatch = require(`./patches/makeBuildPatch`);
  const makeMainActivityPatch = require(`./${prefix}/makeMainActivityPatch`);

  const performSettingsGradlePatch = applyPatch(
    projectConfig.settingsGradlePath,
    makeSettingsPatch.apply(null, arguments)
  );

  const performBuildGradlePatch = applyPatch(
    projectConfig.buildGradlePath,
    makeBuildPatch(name)
  );

  const performMainActivityPatch = applyPatch(
    projectConfig.mainActivityPath,
    makeMainActivityPatch(androidConfig, params)
  );

  /**
   * Check if module has been installed already
   */
  const isInstalled = compose(
    (content) => ~content.indexOf(`:${name}`),
    readFile(projectConfig.buildGradlePath)
  );

  if (isInstalled(name)) {
    return false;
  }

  compose(
    performSettingsGradlePatch,
    performBuildGradlePatch,
    performMainActivityPatch
  )();

  return true;
};
<|endoftext|>


<commit_before>"use strict";
angular.module("typeaheadDropdown.tpl", [])
        .run(["$templateCache",
            function (a) {
                a.put("templates/typeaheadDropdown.tpl.html",
                    "
" +
                    //"
Loading options...
" +
                    "
" +
                    "
" +
                    "<input class=form-control placeholder=\"Select or type...\" ng-model=model[config.modelLabel] typeahead=\"op[config.optionLabel] for op in options | filter:$viewValue\" typeahead-editable=false typeahead-on-select=\"onSelect($item, $model, $label)\" ng-required=\"required\"> " +
                    "" +
                    "<button class=\"btn btn-default dropdown-toggle\" dropdown-toggle>" +
                    "" +
                    "" +
                    "</button>" +
                    "" +
                    "
" +
                    "
" +
                    "
" +
                    "{{op[config.optionLabel]}}" +
                    "
" +
                    "
" +
                    "
" +
                    "
");
            }
        ]),
    angular.module("apg.typeaheadDropdown", ["typeaheadDropdown.tpl", "ui.bootstrap"])
        .directive("typeaheadDropdown", function() {
            return {
                templateUrl: "templates/typeaheadDropdown.tpl.html",
                scope: { model: "=ngModel", options: "=",  config: "=?", events: "=", required: "=?ngRequired" },
                require: "ngModel",
                replace: true,
                link: function($scope, $element, $attrs) {
                    $scope.externalEvents = {
                        onItemSelect: angular.noop
                    };

                    angular.extend($scope.externalEvents, $scope.events || []);
                },

                controller: ["$scope",
                    function (a) {
                        a.config = angular.extend({ modelLabel: "name", optionLabel: "name" }, a.config),

                        a.onSelect = function (i) {
                            if (a.model === undefined) {
                                a.model = [];
                            }
                            angular.extend(a.model, i);
                            a.model[a.config.modelLabel] = i[a.config.optionLabel];
                            if (a.events !== undefined) {
                                a.events.onItemSelect(i);
                            }
                        }
                    }
                ]
            }
        });<commit_msg>like http://jsfiddle.net/mThrT/22<commit_after>"use strict";
angular.module("typeaheadDropdown.tpl", [])
        .run(["$templateCache",
            function (a) {
                a.put("templates/typeaheadDropdown.tpl.html",
                    "
" +
                    //"
Loading options...
" +
                    "
" +
                    "
" +
                    "<input class=form-control placeholder=\"Select or type...\" ng-model=mdl typeahead=\"op[config.optionLabel] for op in options | filter:$viewValue\" typeahead-editable=false typeahead-on-select=\"onSelect($item, $model, $label)\" ng-required=\"required\"> " +
                    "" +
                    "<button class=\"btn btn-default dropdown-toggle\" dropdown-toggle>" +
                    "" +
                    "" +
                    "</button>" +
                    "" +
                    "
" +
                    "
" +
                    "
" +
                    "{{op[config.optionLabel]}}" +
                    "
" +
                    "
" +
                    "
" +
                    "
");
            }
        ]),
    angular.module("apg.typeaheadDropdown", ["typeaheadDropdown.tpl", "ui.bootstrap"])
        .directive("typeaheadDropdown", function() {
            return {
                templateUrl: "templates/typeaheadDropdown.tpl.html",
                scope: { mdl: "=ngModel", options: "=",  config: "=?", events: "=", required: "=?ngRequired" },
                require: "ngModel",
                replace: true,
                link: function($scope, $element, $attrs) {
                    $scope.externalEvents = {
                        onItemSelect: angular.noop
                    };

                    angular.extend($scope.externalEvents, $scope.events || []);
                },

                controller: ["$scope",
                    function (a) {
                        a.config = angular.extend({ modelLabel: "name", optionLabel: "name" }, a.config),

                        a.onSelect = function (i) {
                            if (a.mdl === undefined) {
                                a.mdl = [];
                            }
                            angular.extend(a.mdl, i);
                            a.mdl[a.config.modelLabel] = i[a.config.optionLabel];
                            if (a.events !== undefined) {
                                a.events.onItemSelect(i);
                            }
                        }
                    }
                ]
            }
        });<|endoftext|>


<commit_before>import React from 'react';
import PropTypes from 'prop-types';
import { FormattedMessage } from 'react-intl';
import FormGroup from '@material-ui/core/FormGroup';
import FormControlLabel from '@material-ui/core/FormControlLabel';
import CheckboxNext from '@material-ui/core/Checkbox';
import styled from 'styled-components';
import difference from 'lodash.difference';
import intersection from 'lodash.intersection';
import CheckContext from '../../CheckContext';
import { black54, black87, caption, units, opaqueBlack02, opaqueBlack05 } from '../../styles/js/shared';
import { createTag } from '../../relay/mutations/CreateTagMutation';
import { deleteTag } from '../../relay/mutations/DeleteTagMutation';

const StyledHeadingFirst = styled.div`
  color: ${black87};
  font: ${caption};
`;

const StyledHeading = styled(StyledHeadingFirst)`
  padding-top: ${units(3)};
`;

const StyledNone = styled.div`
  color: ${black54};
  padding-top: ${units(1)};
`;

const StyledNotFound = styled.div`
  color: ${black54};
  padding-top: ${units(14)};
  display: flex;
  justify-content: center;
`;

const StyledTagPickerArea = styled.div`
  padding: ${units(2)};
  height: ${units(32)};
  overflow-y: auto;
  border: 1px solid ${opaqueBlack05};
  background-color: ${opaqueBlack02};
`;

class TagPicker extends React.Component {
  handleCreateTag(value) {
    const { media } = this.props;

    const context = new CheckContext(this).getContextStore();

    const onSuccess = () => {};
    const onFailure = () => {};

    createTag(
      {
        media,
        value,
        annotator: context.currentUser,
      },
      onSuccess, onFailure,
    );
  }

  handleRemoveTag = (value) => {
    const { media } = this.props;

    const removedTag = this.props.tags.find(tag => tag.node.tag_text === value);

    const onSuccess = () => {};
    const onFailure = () => {};

    deleteTag(
      {
        media,
        tagId: removedTag.node.id,
      },
      onSuccess, onFailure,
    );
  };

  handleSelectCheckbox = (e, inputChecked) => {
    if (inputChecked) {
      this.handleCreateTag(e.target.id);
    } else {
      this.handleRemoveTag(e.target.id);
    }
  }

  renderNotFound(shownTagsCount, totalTagsCount) {
    if (totalTagsCount === 0) {
      return (
        <StyledNotFound>
          <FormattedMessage
            id="tagPicker.emptyTags"
            defaultMessage="There are currently no tags for this team."
          />
        </StyledNotFound>
      );
    }

    return (
      <StyledNotFound>
        <FormattedMessage
          id="tagPicker.tagNotFound"
          defaultMessage="Tag #{tag} not found."
          values={{ tag: this.props.value }}
        />
      </StyledNotFound>
    );
  }

  render() {
    const { media, tags, value } = this.props;

    const compareString = (tag, val) => {
      if (!tag) {
        return false;
      }
      return tag.toLowerCase().includes(val.toLowerCase());
    };

    const plainMediaTags = tags.map(tag => tag.node.tag_text);

    const suggestedTags = media.team && media.team.get_suggested_tags
      ? media.team.get_suggested_tags.split(',').filter(tag => compareString(tag, value))
      : [];

    const nonRepeatedUsedTags =
      difference(media.team.used_tags, suggestedTags).filter(tag => compareString(tag, value));

    const checkedSuggestedTags = intersection(suggestedTags, plainMediaTags);
    const uncheckedSuggestedTags = difference(suggestedTags, plainMediaTags);

    const checkedUsedTags =
      difference(plainMediaTags, suggestedTags).filter(tag => compareString(tag, value));
    const uncheckedUsedTags = difference(nonRepeatedUsedTags, plainMediaTags);

    const shownSuggestedCount = checkedSuggestedTags.length + uncheckedSuggestedTags.length;
    const shownUsedCount = checkedUsedTags.length + uncheckedUsedTags.length;

    const shownTagsCount = shownSuggestedCount + shownUsedCount;
    const totalTagsCount = suggestedTags.length + tags.length + media.team.used_tags.length;

    return (shownTagsCount === 0 ?
      <StyledTagPickerArea>
        { this.renderNotFound(shownTagsCount, totalTagsCount) }
      </StyledTagPickerArea>
      :
      <StyledTagPickerArea>
        <FormGroup>
          <StyledHeadingFirst>
            <FormattedMessage
              id="tagPicker.teamTags"
              defaultMessage="{team} team tags"
              values={{ team: media.team.name }}
            />
          </StyledHeadingFirst>
          {
            shownSuggestedCount === 0 ?
              <StyledNone>
                <FormattedMessage id="tagPicker.none" defaultMessage="None" />
              </StyledNone>
              :
              checkedSuggestedTags.concat(uncheckedSuggestedTags).map((tag, index) => (
                <FormControlLabel
                  key={`team-suggested-tag-${index.toString()}`}
                  control={
                    <CheckboxNext
                      checked={plainMediaTags.includes(tag)}
                      onChange={this.handleSelectCheckbox}
                      id={tag}
                    />
                  }
                  label={tag}
                />
              ))
          }
          <StyledHeading>
            <FormattedMessage id="tagPicker.teamOtherTags" defaultMessage="Custom tags" />
          </StyledHeading>
          {
            shownUsedCount === 0 ?
              <StyledNone>
                <FormattedMessage id="tagPicker.none" defaultMessage="None" />
              </StyledNone>
              :
              checkedUsedTags.concat(uncheckedUsedTags).map((tag, index) => (
                <FormControlLabel
                  key={`team-used-tag-${index.toString()}`}
                  control={
                    <CheckboxNext
                      checked={plainMediaTags.includes(tag)}
                      onChange={this.handleSelectCheckbox}
                      id={tag}
                    />
                  }
                  label={tag}
                />
              ))
          }
        </FormGroup>
      </StyledTagPickerArea>
    );
  }
}

TagPicker.contextTypes = {
  store: PropTypes.object,
};

export default TagPicker;
<commit_msg>Fix tag checkbox alignment for rtl #7149<commit_after>import React from 'react';
import PropTypes from 'prop-types';
import { FormattedMessage, injectIntl, intlShape } from 'react-intl';
import FormGroup from '@material-ui/core/FormGroup';
import FormControlLabel from '@material-ui/core/FormControlLabel';
import CheckboxNext from '@material-ui/core/Checkbox';
import rtlDetect from 'rtl-detect';
import styled from 'styled-components';
import difference from 'lodash.difference';
import intersection from 'lodash.intersection';
import CheckContext from '../../CheckContext';
import { black54, black87, caption, units, opaqueBlack02, opaqueBlack05 } from '../../styles/js/shared';
import { createTag } from '../../relay/mutations/CreateTagMutation';
import { deleteTag } from '../../relay/mutations/DeleteTagMutation';

const StyledHeadingFirst = styled.div`
  color: ${black87};
  font: ${caption};
`;

const StyledHeading = styled(StyledHeadingFirst)`
  padding-top: ${units(3)};
`;

const StyledNone = styled.div`
  color: ${black54};
  padding-top: ${units(1)};
`;

const StyledNotFound = styled.div`
  color: ${black54};
  padding-top: ${units(14)};
  display: flex;
  justify-content: center;
`;

const StyledTagPickerArea = styled.div`
  padding: ${units(2)};
  height: ${units(32)};
  overflow-y: auto;
  border: 1px solid ${opaqueBlack05};
  background-color: ${opaqueBlack02};
`;

const StyledFormControlLabel = styled(FormControlLabel)`
  margin-${props => props.direction.from}: -14px !important;
  margin-${props => props.direction.to}: 16px !important;
`;

class TagPicker extends React.Component {
  handleCreateTag(value) {
    const { media } = this.props;

    const context = new CheckContext(this).getContextStore();

    const onSuccess = () => {};
    const onFailure = () => {};

    createTag(
      {
        media,
        value,
        annotator: context.currentUser,
      },
      onSuccess, onFailure,
    );
  }

  handleRemoveTag = (value) => {
    const { media } = this.props;

    const removedTag = this.props.tags.find(tag => tag.node.tag_text === value);

    const onSuccess = () => {};
    const onFailure = () => {};

    deleteTag(
      {
        media,
        tagId: removedTag.node.id,
      },
      onSuccess, onFailure,
    );
  };

  handleSelectCheckbox = (e, inputChecked) => {
    if (inputChecked) {
      this.handleCreateTag(e.target.id);
    } else {
      this.handleRemoveTag(e.target.id);
    }
  }

  renderNotFound(shownTagsCount, totalTagsCount) {
    if (totalTagsCount === 0) {
      return (
        <StyledNotFound>
          <FormattedMessage
            id="tagPicker.emptyTags"
            defaultMessage="There are currently no tags for this team."
          />
        </StyledNotFound>
      );
    }

    return (
      <StyledNotFound>
        <FormattedMessage
          id="tagPicker.tagNotFound"
          defaultMessage="Tag #{tag} not found."
          values={{ tag: this.props.value }}
        />
      </StyledNotFound>
    );
  }

  render() {
    const { media, tags, value } = this.props;

    const compareString = (tag, val) => {
      if (!tag) {
        return false;
      }
      return tag.toLowerCase().includes(val.toLowerCase());
    };

    const plainMediaTags = tags.map(tag => tag.node.tag_text);

    const suggestedTags = media.team && media.team.get_suggested_tags
      ? media.team.get_suggested_tags.split(',').filter(tag => compareString(tag, value))
      : [];

    const nonRepeatedUsedTags =
      difference(media.team.used_tags, suggestedTags).filter(tag => compareString(tag, value));

    const checkedSuggestedTags = intersection(suggestedTags, plainMediaTags);
    const uncheckedSuggestedTags = difference(suggestedTags, plainMediaTags);

    const checkedUsedTags =
      difference(plainMediaTags, suggestedTags).filter(tag => compareString(tag, value));
    const uncheckedUsedTags = difference(nonRepeatedUsedTags, plainMediaTags);

    const shownSuggestedCount = checkedSuggestedTags.length + uncheckedSuggestedTags.length;
    const shownUsedCount = checkedUsedTags.length + uncheckedUsedTags.length;

    const shownTagsCount = shownSuggestedCount + shownUsedCount;
    const totalTagsCount = suggestedTags.length + tags.length + media.team.used_tags.length;

    const isRtl = rtlDetect.isRtlLang(this.props.intl.locale);
    const direction = {
      from: isRtl ? 'right' : 'left',
      to: isRtl ? 'left' : 'right',
    };

    return (shownTagsCount === 0 ?
      <StyledTagPickerArea>
        { this.renderNotFound(shownTagsCount, totalTagsCount) }
      </StyledTagPickerArea>
      :
      <StyledTagPickerArea>
        <FormGroup>
          <StyledHeadingFirst>
            <FormattedMessage
              id="tagPicker.teamTags"
              defaultMessage="{team} team tags"
              values={{ team: media.team.name }}
            />
          </StyledHeadingFirst>
          {
            shownSuggestedCount === 0 ?
              <StyledNone>
                <FormattedMessage id="tagPicker.none" defaultMessage="None" />
              </StyledNone>
              :
              checkedSuggestedTags.concat(uncheckedSuggestedTags).map((tag, index) => (
                <StyledFormControlLabel
                  direction={direction}
                  key={`team-suggested-tag-${index.toString()}`}
                  control={
                    <CheckboxNext
                      checked={plainMediaTags.includes(tag)}
                      onChange={this.handleSelectCheckbox}
                      id={tag}
                    />
                  }
                  label={tag}
                />
              ))
          }
          <StyledHeading>
            <FormattedMessage id="tagPicker.teamOtherTags" defaultMessage="Custom tags" />
          </StyledHeading>
          {
            shownUsedCount === 0 ?
              <StyledNone>
                <FormattedMessage id="tagPicker.none" defaultMessage="None" />
              </StyledNone>
              :
              checkedUsedTags.concat(uncheckedUsedTags).map((tag, index) => (
                <StyledFormControlLabel
                  direction={direction}
                  key={`team-used-tag-${index.toString()}`}
                  control={
                    <CheckboxNext
                      checked={plainMediaTags.includes(tag)}
                      onChange={this.handleSelectCheckbox}
                      id={tag}
                    />
                  }
                  label={tag}
                />
              ))
          }
        </FormGroup>
      </StyledTagPickerArea>
    );
  }
}

TagPicker.propTypes = {
  // https://github.com/yannickcr/eslint-plugin-react/issues/1389
  // eslint-disable-next-line react/no-typos
  intl: intlShape.isRequired,
};

TagPicker.contextTypes = {
  store: PropTypes.object,
};

export default injectIntl(TagPicker);
<|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/.
 */

/*
 * Copyright (c) 2014, Joyent, Inc.
 */

/*
 * Syncs the rule and remote VM state with FWAPI and VMAPI
 */

var assert = require('assert-plus');
var async = require('async');
var fw = require('../fw');
var mod_fwapi = require('../fwapi');
var mod_vm = require('../vm');
var mod_vmapi = require('../vmapi');
var prim = require('jsprim');
var vasync = require('vasync');
var VMAPI = require('sdc-clients').VMAPI;

// --- Internal

/**
 * List all rules and store in opts.allRules
 */
function optsListRules(opts, callback) {
    var listPayload = {
        log: opts.log,
        req_id: opts.req_id,
        payload: {}
    };

    fw.list(listPayload, function (err, rules) {
        if (rules) {
            opts.allRules = rules;
        }

        return callback(err);
    });
}

/**
 * List all remote VMs and store in opts.allRVMs
 */
function optsListRVMs(opts, callback) {
    var listPayload = {
        log: opts.log,
        req_id: opts.req_id,
        payload: {}
    };

    fw.listRVMs(listPayload, function (err, rvms) {
        if (rvms) {
            opts.allRVMs = rvms;
        }

        return callback(err);
    });
}

/**
 * List VMs and store in opts.allVMs
 */
function optsListVMs(opts, callback) {
    mod_vm.list(opts.log, function (err, vms) {
        if (vms) {
            opts.allVMs = vms;
        }

        return callback(err);
    });
}

/**
 * Rules that are present on the local system but weren't resolved during
 * the sync process no longer apply, so should be removed.
 */
function removeDeletedRules(opts, callback) {
    opts.log.debug('Removing local rules and RVMs no longer in FWAPI');
    var local = [];
    var toDel = {
        rvmUUIDs: [],
        uuids: []
    };

    opts.allRules.forEach(function (rule) {
        // Omit local-only rules
        if (rule.created_by != 'fwapi') {
            local.push(rule);
            return;
        }

        if (!opts.resolved || !opts.resolved.rules.hasOwnProperty(rule.uuid)) {
            toDel.uuids.push(rule.uuid);
        }
    });

    opts.allRVMs.forEach(function (rvm) {
        if (!opts.resolved || !opts.resolved.vms.hasOwnProperty(rvm.uuid)) {
            toDel.rvmUUIDs.push(rvm.uuid);
        }
    });

    if (local.length !== 0) {
        opts.log.info({ localRules: local }, 'Local-only rules: not removing');
    }

    if (toDel.rvmUUIDs.length === 0 && toDel.uuids.length === 0) {
        opts.log.info('No removed rules or RVMs to delete');
        return callback();
    }

    opts.log.info(toDel, 'deleting rules / RVMs');
    toDel.vms = opts.allVMs;
    var delPayload = {
        log: opts.log,
        payload: toDel,
        req_id: opts.req_id
    };

    fw.del(delPayload, function (err) {
        if (err) {
            opts.log.info('Error deleting rules / RVMs');
        }

        return callback(err);
    });
}

/**
 * Sync rules from FWAPI and remote VMs from VMAPI
 */
function syncFromAPIs(opts, callback) {
    var payloads = mod_fwapi.buildResolvePayload({
        log: opts.log,
        vms: opts.allVMs
    });

    opts.log.debug({ payloads: payloads },
        'sync: resolve payloads');
    opts.resolve = payloads;
    opts.vms = opts.allVMs;

    return updateOwnerRules(opts, callback);
}

/**
 * Adds rules that only exist locally to FWAPI
 */
function syncToFWAPI(opts, callback) {
    opts.log.debug('Syncing rules to FWAPI');
    /*jsl:ignore*/
    var hitError = false;
    /*jsl:end*/
    var toSync = [];

    for (var r in opts.allRules) {
        var rule = opts.allRules[r];
        if (!rule.hasOwnProperty('created_by') ||
            rule.created_by !== 'fwapi') {
            toSync.push(rule);
        }
    }

    if (toSync.length === 0) {
        opts.log.info('No local rules to sync to FWAPI');
        return callback();
    }

    opts.log.debug({ rules: toSync }, 'rules to sync');

    async.forEachSeries(toSync, function _update(syncRule, cb) {
        opts.fwapi.createRule(syncRule, function (cErr, newRule) {
            if (cErr) {
                opts.log.error({ err: cErr, rule: syncRule },
                    'Error creating rule in FWAPI');
                hitError = true;
                return cb();
            }

            // Need to re-add the rule so that it gets created_by=fwapi
            // (otherwise we'd hit this code path next time we sync)
            var toAdd = {
                log: opts.log,
                payload: { vms: opts.allVMs, rules: [ newRule ] }
            };

            fw.add(toAdd, function (err2, res2) {
                if (err2) {
                    opts.log.error({ err: err2, rule: newRule },
                        'Error adding rule');
                    hitError = true;
                    return cb();
                }

                // Need to add this to the resolved property in opts
                // so that it doesn't get deleted by
                // removeDeletedRules() later in the sync process:
                mod_fwapi.addResolvedToOpts(opts, null, [ newRule ]);

                opts.log.info(res2, 'Updated local rule');
                return cb();
            });
        });
    }, function _doneFWAPI(finalErr) {
        opts.log.debug('Done syncing rules to FWAPI');
        return callback(finalErr);
    });
}

/**
 * Resolve payloads in opts.resolve, which syncs VMs and rules from VMAPI
 * and FWAPI.
 *
 * We don't want an error syncing a rule from FWAPI to prevent applying
 * any other rules. Once we've applied FWAPI rules, we'll return an error
 * and avoid syncing back local rules that aren't in FWAPI.
 *
 */
function updateOwnerRules(opts, callback) {
    var hitError = false;
    async.eachSeries(opts.resolve, function _update(payload, cb) {
        mod_fwapi.resolve(opts, payload, function (err) {
            if (err) {
                opts.log.error(err, 'partial failure while syncing firewall rules');
                hitError = true;
            }
            return cb();
        });
    }, function _done(err) {
        opts.log.debug(opts.cache.state, 'cache state after resolve');
        if (err) {
            return callback(err);
        } else if (hitError) {
            return callback(new Error('failed to sync all firewall rules'));
        } else {
            return callback();
        }
    });
}

// --- Exports

/**
 * Syncs firewall state to / from FWAPI and VMAPI
 */
function sync(opts, callback) {
    assert.object(opts, 'opts');
    assert.object(opts.cache, 'opts.cache');
    assert.object(opts.config, 'opts.config');
    assert.object(opts.config.vmapi, 'opts.config.vmapi');
    assert.string(opts.config.vmapi.host, 'opts.config.vmapi.host');
    assert.string(opts.config.serverUUID, 'opts.config.serverUUID');

    opts.fwapi = mod_fwapi.createClient(opts);
    opts.vmapi = mod_vmapi.createClient(opts);

    vasync.pipeline({
        arg: opts,
        funcs: [
            optsListVMs,
            syncFromAPIs,
            optsListRules,
            syncToFWAPI,
            optsListRules,
            optsListRVMs,
            removeDeletedRules
        ]
    }, function (err) {
        opts.fwapi.client.close();
        opts.vmapi.client.close();

        return callback(err);
    });
}

module.exports = {
    run: sync
};
<commit_msg>FWAPI-220 Firewaller retries indefinitely even when there is no chance of succeeding, blocking subsequent provisioning requests (fix `make check')<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/.
 */

/*
 * Copyright (c) 2014, Joyent, Inc.
 */

/*
 * Syncs the rule and remote VM state with FWAPI and VMAPI
 */

var assert = require('assert-plus');
var async = require('async');
var fw = require('../fw');
var mod_fwapi = require('../fwapi');
var mod_vm = require('../vm');
var mod_vmapi = require('../vmapi');
var prim = require('jsprim');
var vasync = require('vasync');
var VMAPI = require('sdc-clients').VMAPI;

// --- Internal

/**
 * List all rules and store in opts.allRules
 */
function optsListRules(opts, callback) {
    var listPayload = {
        log: opts.log,
        req_id: opts.req_id,
        payload: {}
    };

    fw.list(listPayload, function (err, rules) {
        if (rules) {
            opts.allRules = rules;
        }

        return callback(err);
    });
}

/**
 * List all remote VMs and store in opts.allRVMs
 */
function optsListRVMs(opts, callback) {
    var listPayload = {
        log: opts.log,
        req_id: opts.req_id,
        payload: {}
    };

    fw.listRVMs(listPayload, function (err, rvms) {
        if (rvms) {
            opts.allRVMs = rvms;
        }

        return callback(err);
    });
}

/**
 * List VMs and store in opts.allVMs
 */
function optsListVMs(opts, callback) {
    mod_vm.list(opts.log, function (err, vms) {
        if (vms) {
            opts.allVMs = vms;
        }

        return callback(err);
    });
}

/**
 * Rules that are present on the local system but weren't resolved during
 * the sync process no longer apply, so should be removed.
 */
function removeDeletedRules(opts, callback) {
    opts.log.debug('Removing local rules and RVMs no longer in FWAPI');
    var local = [];
    var toDel = {
        rvmUUIDs: [],
        uuids: []
    };

    opts.allRules.forEach(function (rule) {
        // Omit local-only rules
        if (rule.created_by != 'fwapi') {
            local.push(rule);
            return;
        }

        if (!opts.resolved || !opts.resolved.rules.hasOwnProperty(rule.uuid)) {
            toDel.uuids.push(rule.uuid);
        }
    });

    opts.allRVMs.forEach(function (rvm) {
        if (!opts.resolved || !opts.resolved.vms.hasOwnProperty(rvm.uuid)) {
            toDel.rvmUUIDs.push(rvm.uuid);
        }
    });

    if (local.length !== 0) {
        opts.log.info({ localRules: local }, 'Local-only rules: not removing');
    }

    if (toDel.rvmUUIDs.length === 0 && toDel.uuids.length === 0) {
        opts.log.info('No removed rules or RVMs to delete');
        return callback();
    }

    opts.log.info(toDel, 'deleting rules / RVMs');
    toDel.vms = opts.allVMs;
    var delPayload = {
        log: opts.log,
        payload: toDel,
        req_id: opts.req_id
    };

    fw.del(delPayload, function (err) {
        if (err) {
            opts.log.info('Error deleting rules / RVMs');
        }

        return callback(err);
    });
}

/**
 * Sync rules from FWAPI and remote VMs from VMAPI
 */
function syncFromAPIs(opts, callback) {
    var payloads = mod_fwapi.buildResolvePayload({
        log: opts.log,
        vms: opts.allVMs
    });

    opts.log.debug({ payloads: payloads },
        'sync: resolve payloads');
    opts.resolve = payloads;
    opts.vms = opts.allVMs;

    return updateOwnerRules(opts, callback);
}

/**
 * Adds rules that only exist locally to FWAPI
 */
function syncToFWAPI(opts, callback) {
    opts.log.debug('Syncing rules to FWAPI');
    /*jsl:ignore*/
    var hitError = false;
    /*jsl:end*/
    var toSync = [];

    for (var r in opts.allRules) {
        var rule = opts.allRules[r];
        if (!rule.hasOwnProperty('created_by') ||
            rule.created_by !== 'fwapi') {
            toSync.push(rule);
        }
    }

    if (toSync.length === 0) {
        opts.log.info('No local rules to sync to FWAPI');
        return callback();
    }

    opts.log.debug({ rules: toSync }, 'rules to sync');

    async.forEachSeries(toSync, function _update(syncRule, cb) {
        opts.fwapi.createRule(syncRule, function (cErr, newRule) {
            if (cErr) {
                opts.log.error({ err: cErr, rule: syncRule },
                    'Error creating rule in FWAPI');
                hitError = true;
                return cb();
            }

            // Need to re-add the rule so that it gets created_by=fwapi
            // (otherwise we'd hit this code path next time we sync)
            var toAdd = {
                log: opts.log,
                payload: { vms: opts.allVMs, rules: [ newRule ] }
            };

            fw.add(toAdd, function (err2, res2) {
                if (err2) {
                    opts.log.error({ err: err2, rule: newRule },
                        'Error adding rule');
                    hitError = true;
                    return cb();
                }

                // Need to add this to the resolved property in opts
                // so that it doesn't get deleted by
                // removeDeletedRules() later in the sync process:
                mod_fwapi.addResolvedToOpts(opts, null, [ newRule ]);

                opts.log.info(res2, 'Updated local rule');
                return cb();
            });
        });
    }, function _doneFWAPI(finalErr) {
        opts.log.debug('Done syncing rules to FWAPI');
        return callback(finalErr);
    });
}

/**
 * Resolve payloads in opts.resolve, which syncs VMs and rules from VMAPI
 * and FWAPI.
 *
 * We don't want an error syncing a rule from FWAPI to prevent applying
 * any other rules. Once we've applied FWAPI rules, we'll return an error
 * and avoid syncing back local rules that aren't in FWAPI.
 *
 */
function updateOwnerRules(opts, callback) {
    var hitError = false;
    async.eachSeries(opts.resolve, function _update(payload, cb) {
        mod_fwapi.resolve(opts, payload, function (err) {
            if (err) {
                opts.log.error(err,
                    'partial failure while syncing firewall rules');
                hitError = true;
            }
            return cb();
        });
    }, function _done(err) {
        opts.log.debug(opts.cache.state, 'cache state after resolve');
        if (err) {
            return callback(err);
        } else if (hitError) {
            return callback(new Error('failed to sync all firewall rules'));
        } else {
            return callback();
        }
    });
}

// --- Exports

/**
 * Syncs firewall state to / from FWAPI and VMAPI
 */
function sync(opts, callback) {
    assert.object(opts, 'opts');
    assert.object(opts.cache, 'opts.cache');
    assert.object(opts.config, 'opts.config');
    assert.object(opts.config.vmapi, 'opts.config.vmapi');
    assert.string(opts.config.vmapi.host, 'opts.config.vmapi.host');
    assert.string(opts.config.serverUUID, 'opts.config.serverUUID');

    opts.fwapi = mod_fwapi.createClient(opts);
    opts.vmapi = mod_vmapi.createClient(opts);

    vasync.pipeline({
        arg: opts,
        funcs: [
            optsListVMs,
            syncFromAPIs,
            optsListRules,
            syncToFWAPI,
            optsListRules,
            optsListRVMs,
            removeDeletedRules
        ]
    }, function (err) {
        opts.fwapi.client.close();
        opts.vmapi.client.close();

        return callback(err);
    });
}

module.exports = {
    run: sync
};
<|endoftext|>


<commit_before>/* LIB */

var authorize = require('./authorize');
var sendRequest = require('./sendRequest');

/* MODULES */

var moment = require('moment');

/* CONSTRUCTOR */

(function() {

  var TeleBureau = {};

  /* PRIVATE VARIABLES */

  var params;

  /* PUBLIC FUNCTIONS */

  TeleBureau.setup = function(setupParams) {
    params = setupParams;
    sendRequest.setup(setupParams);
  };

  TeleBureau.getEvent = function(options, callback) {
    if (!options || typeof options !== 'object') {
      throw new Error('Error calling TeleBureau Get - no params object provided.');
    } else if (!options.referenceId) {
      throw new Error('Error calling TeleBureau Get - "referenceId" not provided in the request params.');
    } else if (!callback || typeof callback !== 'function') {
      throw new Error('Error calling TeleBureau Get - no callback function provided.');
    }

    sendRequest.request({
      method: 'GET',
      resource: '/telebureau/event/' + options.referenceId,
      qs: null
    }, function(err, data) {
      return callback(err, data);
    });
  };

  TeleBureau.submitEvent = function(options, callback) {
    if (!options || typeof options !== 'object') {
      throw new Error('Error calling TeleBureau Submit - no params object provided.');
    } else if (!options.phoneNumber) {
      throw new Error('Error calling TeleBureau Submit - "phoneNumber" not provided in the request params.');
    } else if (!options.fraudType) {
      throw new Error('Error calling TeleBureau Submit - "fraudType" not provided in the request params.');
    } else if (!options.occurredAt) {
      throw new Error('Error calling TeleBureau Submit - "occurredAt" not provided in the request params.');
    } else if (!callback || typeof callback !== 'function') {
      throw new Error('Error calling TeleBureau Submit - no callback function provided.');
    }

    var dateFormat = 'YYYY-MM-DDTHH-mm-SSZZ';

    options.occurredAt = moment(options.occurredAt).format(dateFormat);
    if (options.discoveredAt) options.discoveredAt = moment(options.discoveredAt).format(dateFormat);
    if (options.verifiedAt) options.verifiedAt = moment(options.verifiedAt).format(dateFormat);

    var fields = {
      phone_number: options.phoneNumber,
      fraud_type: options.fraudType,
      occurred_at: options.occurredAt,
      discovered_at: options.discoveredAt,
      fraud_ip: options.fraudIp,
      impact_type: options.impactType,
      impact: options.impact,
      verified_by: options.verifiedBy,
      verified_at: options.verifiedAt
    };

    sendRequest.request({
      method: 'POST',
      resource: '/telebureau/event',
      qs: null,
      fields: fields
    }, function(err, data) {
      return callback(err, data);
    });
  };

  TeleBureau.deleteEvent = function(options, callback) {
    if (!options || typeof options !== 'object') {
      throw new Error('Error calling TeleBureau Delete - no params object provided.');
    } else if (!options.referenceId) {
      throw new Error('Error calling TeleBureau Delete - "referenceId" not provided in the request params.');
    } else if (!callback || typeof callback !== 'function') {
      throw new Error('Error calling TeleBureau Delete - no callback function provided.');
    }

    sendRequest.request({
      method: 'DELETE',
      resource: '/telebureau/event/' + options.referenceId,
      qs: null
    }, function(err, data) {
      return callback(err, data);
    });
  };

  /* NPM EXPORT */

  if (typeof module === 'object' && module.exports) {
    module.exports = TeleBureau;
  } else {
    throw new Error('This module only works with NPM in NodeJS/IO.JS environments.');
  }

}());
<commit_msg>Debug log.<commit_after>/* LIB */

var authorize = require('./authorize');
var sendRequest = require('./sendRequest');

/* MODULES */

var moment = require('moment');

/* CONSTRUCTOR */

(function() {

  var TeleBureau = {};

  /* PRIVATE VARIABLES */

  var params;

  /* PUBLIC FUNCTIONS */

  TeleBureau.setup = function(setupParams) {
    params = setupParams;
    sendRequest.setup(setupParams);
  };

  TeleBureau.getEvent = function(options, callback) {
    if (!options || typeof options !== 'object') {
      throw new Error('Error calling TeleBureau Get - no params object provided.');
    } else if (!options.referenceId) {
      throw new Error('Error calling TeleBureau Get - "referenceId" not provided in the request params.');
    } else if (!callback || typeof callback !== 'function') {
      throw new Error('Error calling TeleBureau Get - no callback function provided.');
    }

    sendRequest.request({
      method: 'GET',
      resource: '/telebureau/event/' + options.referenceId,
      qs: null
    }, function(err, data) {
      return callback(err, data);
    });
  };

  TeleBureau.submitEvent = function(options, callback) {
    if (!options || typeof options !== 'object') {
      throw new Error('Error calling TeleBureau Submit - no params object provided.');
    } else if (!options.phoneNumber) {
      throw new Error('Error calling TeleBureau Submit - "phoneNumber" not provided in the request params.');
    } else if (!options.fraudType) {
      throw new Error('Error calling TeleBureau Submit - "fraudType" not provided in the request params.');
    } else if (!options.occurredAt) {
      throw new Error('Error calling TeleBureau Submit - "occurredAt" not provided in the request params.');
    } else if (!callback || typeof callback !== 'function') {
      throw new Error('Error calling TeleBureau Submit - no callback function provided.');
    }

    var dateFormat = 'YYYY-MM-DDTHH-mm-SSZZ';

    options.occurredAt = moment(options.occurredAt).format(dateFormat);
    if (options.discoveredAt) options.discoveredAt = moment(options.discoveredAt).format(dateFormat);
    if (options.verifiedAt) options.verifiedAt = moment(options.verifiedAt).format(dateFormat);

    console.log(options.verifiedAt, options.discoveredAt);

    var fields = {
      phone_number: options.phoneNumber,
      fraud_type: options.fraudType,
      occurred_at: options.occurredAt,
      discovered_at: options.discoveredAt,
      fraud_ip: options.fraudIp,
      impact_type: options.impactType,
      impact: options.impact,
      verified_by: options.verifiedBy,
      verified_at: options.verifiedAt
    };

    sendRequest.request({
      method: 'POST',
      resource: '/telebureau/event',
      qs: null,
      fields: fields
    }, function(err, data) {
      return callback(err, data);
    });
  };

  TeleBureau.deleteEvent = function(options, callback) {
    if (!options || typeof options !== 'object') {
      throw new Error('Error calling TeleBureau Delete - no params object provided.');
    } else if (!options.referenceId) {
      throw new Error('Error calling TeleBureau Delete - "referenceId" not provided in the request params.');
    } else if (!callback || typeof callback !== 'function') {
      throw new Error('Error calling TeleBureau Delete - no callback function provided.');
    }

    sendRequest.request({
      method: 'DELETE',
      resource: '/telebureau/event/' + options.referenceId,
      qs: null
    }, function(err, data) {
      return callback(err, data);
    });
  };

  /* NPM EXPORT */

  if (typeof module === 'object' && module.exports) {
    module.exports = TeleBureau;
  } else {
    throw new Error('This module only works with NPM in NodeJS/IO.JS environments.');
  }

}());
<|endoftext|>


<commit_before>import chainPromiseData from './index';

const user = { id: 1, name: 'Chainy McChainFace'};

function createUser() {
  return Promise.resolve(user);
}

describe('chainPromiseData', () => {
  it('Should return a function', () => {
    expect(typeof chainPromiseData(createUser)).toBe('function');
  });
  it('Should apply a mapper function if provided', () => {
    chainPromiseData(
      createUser,
      ({ id, name }) => ({ userId: id, userName: name })
    )()
      .then(data => {
        expect(data.userId).toBe(user.id);
        expect(data.userName).toBe(user.name);
      });
  });
  it('Should apply a filter array if provided', () => {
    chainPromiseData(
      createUser,
      ['name']
    )()
      .then(data => {
        expect(data.id).toBeUndefined();
        expect(data.name).toBe(user.name);
      });
  })
});<commit_msg>Added test for actually chaining the data<commit_after>import chainPromiseData from './index';

const user = { id: 1, name: 'Chainy McChainFace'};
const food = { food: 'Burger' };

function createUser() {
  return Promise.resolve(user);
}

function makeFood() {
  return Promise.resolve(food);
}

function returnsNothing() {
  return Promise.resolve();
}

describe('chainPromiseData', () => {
  it('Should return a function', () => {
    expect(typeof chainPromiseData(createUser)).toBe('function');
  });
  it('Should apply a mapper function if provided', () => {
    chainPromiseData(
      createUser,
      ({ id, name }) => ({ userId: id, userName: name })
    )()
      .then(data => {
        expect(data.userId).toBe(user.id);
        expect(data.userName).toBe(user.name);
      });
  });
  it('Should apply a filter array if provided', () => {
    chainPromiseData(
      createUser,
      ['name']
    )()
      .then(data => {
        expect(data.id).toBeUndefined();
        expect(data.name).toBe(user.name);
      });
  });
  it('Should hold on to data through longer chains', () => {
    createUser()
      .then(chainPromiseData(makeFood))
      .then(chainPromiseData(returnsNothing))
      .then(data => {
        expect(data.id).toBe(user.id);
        expect(data.name).toBe(user.name);
        expect(data.food).toBe(food.food);
      })
  });
});<|endoftext|>