System.register(['./view.js', './utils.js', 'active-expression', 'active-expression-rewriting', 'src/client/ContextJS/src/contextjs.js', 'utils'], function (_export, _context) {
  "use strict";

  var View, pushIfMissing, removeIfExisting, Stack, isPrimitive, identity, BaseActiveExpression, aexpr, cop, PausableLoop;
  return {
    setters: [function (_viewJs) {
      View = _viewJs.default;
      var _exportObj = {};
      _exportObj.View = _viewJs.default;

      _export(_exportObj);
    }, function (_utilsJs) {
      pushIfMissing = _utilsJs.pushIfMissing;
      removeIfExisting = _utilsJs.removeIfExisting;
      Stack = _utilsJs.Stack;
      isPrimitive = _utilsJs.isPrimitive;
      identity = _utilsJs.identity;
    }, function (_activeExpression) {
      BaseActiveExpression = _activeExpression.BaseActiveExpression;
    }, function (_activeExpressionRewriting) {
      aexpr = _activeExpressionRewriting.default;
    }, function (_srcClientContextJSSrcContextjsJs) {
      cop = _srcClientContextJSSrcContextjsJs;
    }, function (_utils) {
      PausableLoop = _utils.PausableLoop;
    }],
    execute: function () {
      _recorder_._src_client_reactive_active_group_select_js = _recorder_._src_client_reactive_active_group_select_js || {};
      _recorder_._src_client_reactive_active_group_select_js.select = select;
      _recorder_._src_client_reactive_active_group_select_js.__unload__ = __unload__;
      _recorder_._src_client_reactive_active_group_select_js.trackSelector = trackSelector;
      _recorder_._src_client_reactive_active_group_select_js.removeObsoleteListeners = removeObsoleteListeners;
      _recorder_._src_client_reactive_active_group_select_js.addSelectorListener = addSelectorListener;
      _recorder_._src_client_reactive_active_group_select_js.trackInitializeAndDestroy = trackInitializeAndDestroy;
      _recorder_._src_client_reactive_active_group_select_js.untrackInstance = untrackInstance;
      _recorder_._src_client_reactive_active_group_select_js.trackInstance = trackInstance;
      _recorder_._src_client_reactive_active_group_select_js.baseViewForClass = baseViewForClass;
      _recorder_._src_client_reactive_active_group_select_js.ensureBaseViewForClass = ensureBaseViewForClass;
      _recorder_._src_client_reactive_active_group_select_js.View = View;
      _recorder_._src_client_reactive_active_group_select_js.identity = identity;
      _recorder_._src_client_reactive_active_group_select_js.isPrimitive = isPrimitive;
      _recorder_._src_client_reactive_active_group_select_js.Stack = Stack;
      _recorder_._src_client_reactive_active_group_select_js.removeIfExisting = removeIfExisting;
      _recorder_._src_client_reactive_active_group_select_js.pushIfMissing = pushIfMissing;
      _recorder_._src_client_reactive_active_group_select_js.BaseActiveExpression = BaseActiveExpression;
      _recorder_._src_client_reactive_active_group_select_js.aexpr = aexpr;
      _recorder_._src_client_reactive_active_group_select_js.cop = cop;
      _recorder_._src_client_reactive_active_group_select_js.PausableLoop = PausableLoop;


      // TODO: this is use to keep SystemJS from messing up scoping
      // (FilterOperation would not be defined in select)
      const HACK = {};

      /**
       * #TODO: this is from withlogging.js
       */
      // #TODO: can we make this easier, e.g. automatically identifying the class to adapt from the very instance? What about superclasses?
      _recorder_._src_client_reactive_active_group_select_js.HACK = HACK;
      const baseViewByClass = new WeakMap();

      _export('baseViewByClass', baseViewByClass);

      _recorder_._src_client_reactive_active_group_select_js.baseViewByClass = baseViewByClass;
      function ensureBaseViewForClass(Class) {
        if (!_recorder_._src_client_reactive_active_group_select_js.baseViewByClass.has(Class)) {
          _recorder_._src_client_reactive_active_group_select_js.baseViewByClass.set(Class, new _recorder_._src_client_reactive_active_group_select_js.View());
        }
        return _recorder_._src_client_reactive_active_group_select_js.baseViewByClass.get(Class);
      }

      function baseViewForClass(Class) {
        return _recorder_._src_client_reactive_active_group_select_js.ensureBaseViewForClass(Class);
      }

      _export('baseViewForClass', baseViewForClass);

      function trackInstance(instance) {
        _recorder_._src_client_reactive_active_group_select_js.baseViewForClass(this).safeAdd(instance);
      }

      _export('trackInstance', trackInstance);

      function untrackInstance(instance) {
        _recorder_._src_client_reactive_active_group_select_js.baseViewForClass(this).safeRemove(instance);
      }

      // #TODO: ideally, we would track the constructor, maybe with **decorators**?
      // #TODO: unused, maybe use cop instead of a functional mixin
      // #TODO: use cop here

      _export('untrackInstance', untrackInstance);

      function trackInitializeAndDestroy(Class) {
        _recorder_._src_client_reactive_active_group_select_js.cop.layer().refineClass(Class, {
          initialize(...args) {
            const result = _recorder_._src_client_reactive_active_group_select_js.cop.proceed(...args);
            _recorder_._src_client_reactive_active_group_select_js.trackInstance.call(Class, this);
            return result;
          },
          destroy(...args) {
            _recorder_._src_client_reactive_active_group_select_js.untrackInstance.call(Class, this);
            return _recorder_._src_client_reactive_active_group_select_js.cop.proceed(...args);
          }
        }).beGlobal();
      }

      _export('trackInitializeAndDestroy', trackInitializeAndDestroy);

      class Operator {}
      _recorder_._src_client_reactive_active_group_select_js.Operator = Operator;
      class IdentityOperator extends _recorder_._src_client_reactive_active_group_select_js.Operator {
        // constructor(upstream, downstream) {
        //     super();
        //     this.downstream = downstream;
        //     upstream.downstream.push(this);
        //     upstream.now().forEach(function(item) {
        //         downstream.safeAdd(item);
        //     });
        // }
        newItemFromUpstream(item) {
          this.downstream.safeAdd(item);
        }
        destroyItemFromUpstream(item) {
          this.downstream.safeRemove(item);
        }
      }

      _recorder_._src_client_reactive_active_group_select_js.IdentityOperator = IdentityOperator;
      class FilterOperator extends _recorder_._src_client_reactive_active_group_select_js.IdentityOperator {
        constructor(upstream, downstream, expression) {
          super();
          this.expression = expression;

          this.selectionItems = [];

          this.upstream = upstream;
          this.downstream = downstream;
          upstream.downstream.push(this);
          upstream.now().forEach(item => this.newItemFromUpstream(item));
        }
        newItemFromUpstream(item) {
          this.onNewInstance(item);
        }
        onNewInstance(item) {
          _recorder_._src_client_reactive_active_group_select_js.aexpr(this.expression, { params: [item] }).onBecomeTrue(() => this.add(item)).onBecomeFalse(() => this.remove(item));
        }
        add(item) {
          if (this.upstream.now().indexOf(item) >= 0) {
            this.addDueToFilterExpression(item);
          }
        }
        remove(item) {
          this.removeDueToFilterExpression(item);
        }
        addDueToFilterExpression(item) {
          this.downstream.safeAdd(item);
        }
        removeDueToFilterExpression(item) {
          this.downstream.safeRemove(item);
        }
        destroyItemFromUpstream(item) {
          this.remove(item);
        }
      }

      _recorder_._src_client_reactive_active_group_select_js.FilterOperator = FilterOperator;
      class MapOperator extends _recorder_._src_client_reactive_active_group_select_js.IdentityOperator {
        constructor(upstream, downstream, mapFunction) {
          super();
          this.mapFunction = mapFunction || _recorder_._src_client_reactive_active_group_select_js.identity;
          this.items = [];
          this.outputItemsByItems = new Map();

          this.downstream = downstream;
          upstream.downstream.push(this);
          upstream.now().forEach(item => this.newItemFromUpstream(item));
        }
        newItemFromUpstream(item) {
          var wasNewItem = _recorder_._src_client_reactive_active_group_select_js.pushIfMissing(this.items, item);
          if (wasNewItem) {
            var outputItem = this.mapFunction(item);
            this.outputItemsByItems.set(item, outputItem);
            this.downstream.safeAdd(outputItem);
          }
        }
        destroyItemFromUpstream(item) {
          var gotRemoved = _recorder_._src_client_reactive_active_group_select_js.removeIfExisting(this.items, item);
          if (gotRemoved) {
            var outputItem = this.outputItemsByItems.get(item);
            this.outputItemsByItems.delete(item);
            this.downstream.safeRemove(outputItem);
          }
        }
      }

      // #TODO: allow for more than 2 upstreams to merge
      _recorder_._src_client_reactive_active_group_select_js.MapOperator = MapOperator;
      class UnionOperator extends _recorder_._src_client_reactive_active_group_select_js.IdentityOperator {
        constructor(downstream, upstream1, upstream2) {
          super();
          this.upstream1 = upstream1;
          this.upstream2 = upstream2;
          this.downstream = downstream;
          upstream1.downstream.push(this);
          upstream2.downstream.push(this);

          upstream1.now().concat(upstream2.now()).forEach(function (item) {
            this.newItemFromUpstream(item);
          }, this);
        }
        newItemFromUpstream(item) {
          var itemAlreadyExists = this.downstream.now().includes(item);
          if (!itemAlreadyExists) {
            this.downstream.safeAdd(item);
          }
        }
        destroyItemFromUpstream(item) {
          var itemStillExists = this.upstream1.now().includes(item) || this.upstream2.now().includes(item);
          if (!itemStillExists) {
            this.downstream.safeRemove(item);
          }
        }
      }

      // TODO: make this reusable
      _recorder_._src_client_reactive_active_group_select_js.UnionOperator = UnionOperator;
      class FlowToFunction {
        constructor(upstream, create, destroy) {
          this.create = create;
          this.destroy = destroy;
          upstream.downstream.push(this);
        }
        newItemFromUpstream(item) {
          this.create(item);
        }
        destroyItemFromUpstream(item) {
          this.destroy(item);
        }
      }

      /**
       *
       * @class Pair
       * @classdesc This is used by the {@link View#cross} operator.
       * @property {Object} first
       * @property {Object} second
       */
      _recorder_._src_client_reactive_active_group_select_js.FlowToFunction = FlowToFunction;
      class Pair {
        constructor(first, second) {
          this.first = first;
          this.second = second;
        }
      }

      _recorder_._src_client_reactive_active_group_select_js.Pair = Pair;
      class CrossOperator {
        constructor(upstream1, upstream2, downstream) {
          this.upstream1 = upstream1;
          this.upstream2 = upstream2;
          this.downstream = downstream;

          this.trackedItems = [[], []];
          this.pairs = new Map();

          new _recorder_._src_client_reactive_active_group_select_js.FlowToFunction(upstream1, this.newItemFromUpstream.bind(this, 0), this.destroyItemFromUpstream.bind(this, 0));
          new _recorder_._src_client_reactive_active_group_select_js.FlowToFunction(upstream2, this.newItemFromUpstream.bind(this, 1), this.destroyItemFromUpstream.bind(this, 1));
          upstream1.now().forEach(this.newItemFromUpstream.bind(this, 0));
          upstream2.now().forEach(this.newItemFromUpstream.bind(this, 1));
        }
        newItemFromUpstream(index, item) {
          var wasNewItem = _recorder_._src_client_reactive_active_group_select_js.pushIfMissing(this.trackedItems[index], item);
          if (wasNewItem) {
            this.forEachPairWithDo(index, item, function (pair) {
              this.downstream.safeAdd(pair);
            });
          }
        }
        destroyItemFromUpstream(index, item) {
          var gotRemoved = _recorder_._src_client_reactive_active_group_select_js.removeIfExisting(this.trackedItems[index], item);
          if (gotRemoved) {
            this.forEachPairWithDo(index, item, function (pair) {
              this.downstream.safeRemove(pair);
            });
          }
        }
        forEachPairWithDo(index, item, callback) {
          var zeroes = index === 0 ? [item] : this.trackedItems[0];
          var ones = index === 1 ? [item] : this.trackedItems[1];

          zeroes.forEach(function (zeroElement) {
            ones.forEach(function (oneElement) {
              var pair = this.getOrCreatePairForCombination(zeroElement, oneElement);
              callback.call(this, pair);
            }, this);
          }, this);
        }
        getOrCreatePairForCombination(zero, one) {
          if (!this.pairs.has(zero)) {
            this.pairs.set(zero, new Map());
          }
          var map = this.pairs.get(zero);
          if (!map.has(one)) {
            map.set(one, new _recorder_._src_client_reactive_active_group_select_js.Pair(zero, one));
          }
          return map.get(one);
        }
      }

      _recorder_._src_client_reactive_active_group_select_js.CrossOperator = CrossOperator;
      class DelayOperator extends _recorder_._src_client_reactive_active_group_select_js.IdentityOperator {
        constructor(upstream, downstream, delayTime) {
          super(upstream, downstream);
          this.upstream = upstream;
          this.downstream = downstream;
          this.delayTime = delayTime;
          upstream.downstream.push(this);

          this.delays = new Map();

          upstream.now().forEach(function (item) {
            this.newItemFromUpstream(item);
          }, this);
        }
        newItemFromUpstream(item) {
          if (!this.delays.has(item)) {
            this.delays.set(item, setInterval(() => this.downstream.safeAdd(item), this.delayTime));
          }
        }
        destroyItemFromUpstream(item) {
          this.downstream.safeRemove(item);
          if (this.delays.has(item)) {
            clearTimeout(this.delays.get(item));
            this.delays.delete(item);
          }
        }
      }

      _recorder_._src_client_reactive_active_group_select_js.DelayOperator = DelayOperator;
      class ReduceOperator {
        constructor(upstream, reducer, initialValue) {
          this.upstream = upstream;
          upstream.downstream.push(this);
          this.aexpr = new _recorder_._src_client_reactive_active_group_select_js.BaseActiveExpression(() => this.upstream.now().reduce(reducer, initialValue));
        }
        newItemFromUpstream() {
          this.aexpr.checkAndNotify();
        }
        destroyItemFromUpstream() {
          this.newItemFromUpstream();
        }
      }

      _recorder_._src_client_reactive_active_group_select_js.ReduceOperator = ReduceOperator;
      Object.assign(_recorder_._src_client_reactive_active_group_select_js.View.prototype, {
        /**
         * Takes an additional filter function and returns a reactive object set. That set only contains the objects of the original set that also match the given filter function.
         * @function View#filter
         * @param {View~filterIterator} iterator
         * @return {View} The callee of this method.
         */
        filter(iterator, context) {
          var newSelection = new _recorder_._src_client_reactive_active_group_select_js.View();

          new _recorder_._src_client_reactive_active_group_select_js.FilterOperator(this, newSelection, iterator, context);

          return newSelection;
        },
        /**
         * Takes a mapping function and returns another reactive object set. That set always contains the mapped objects corresponding to the objects in the original set.
         * @function View#map
         * @param {View~mapIterator} iterator
         * @return {View} The callee of this method.
         */
        map(iterator) {
          var newSelection = new _recorder_._src_client_reactive_active_group_select_js.View();

          new _recorder_._src_client_reactive_active_group_select_js.MapOperator(this, newSelection, iterator);

          return newSelection;
        },
        /**
         * Create a new {@link View} containing all elements of the callee and the argument.
         * @function View#union
         * @param {View} otherView {@link View}
         * @return {View} Contains every object of both input Views.
         */
        union(otherView) {
          var newSelection = new _recorder_._src_client_reactive_active_group_select_js.View();

          new _recorder_._src_client_reactive_active_group_select_js.UnionOperator(newSelection, this, otherView);

          return newSelection;
        },
        /**
         * Create a new {@link View} containing all elements of the cartesian product of the callee and the argument as {@link Pair}.
         * @function View#cross
         * @param {View} otherView {@link View}
         * @return {View} Contains every combination of both input Views as two-element Array.
         */
        cross(otherView) {
          var newSelection = new _recorder_._src_client_reactive_active_group_select_js.View();

          new _recorder_._src_client_reactive_active_group_select_js.CrossOperator(this, otherView, newSelection);

          return newSelection;
        },

        /**
         * Delays the propagation of items of the callee.
         * Items are propagated to the returned {@link View} in {@link View#delay.delayTime} milliseconds,
         * if they are not removed from the callee before the timeout.
         * @function View#delay
         * @param {Number} delayTime - the time to delay given in milliSeconds.
         * @returns {View}
         */
        delay(delayTime) {
          var newSelection = new _recorder_._src_client_reactive_active_group_select_js.View();

          new _recorder_._src_client_reactive_active_group_select_js.DelayOperator(this, newSelection, delayTime);

          return newSelection;
        },

        /**
         * Whenever the callee is modified, the returned Active Expression gets notified.
         * @function View#reduce
         * @param {View~reducer} reducer
         * @param initialValue - the initial value passed to the {@View~reducer}.
         * @returns {ActiveExpression} changing with the callee
         */
        reduce(reducer, initialValue) {
          const reduce = new _recorder_._src_client_reactive_active_group_select_js.ReduceOperator(this, reducer, initialValue);

          return reduce.aexpr;
        }
      });

      /**
       * The callback function to determine whether an Object should be part of the resulting derived {@link View}.
       * @callback View~filterIterator
       * @param {Object} item - item from the original {@link View}.
       * @return {Boolean}
       */

      /**
       * The callback that computes the item to be added to the mapped {@link View}.
       * @callback View~mapIterator
       * @param {Object} item - item from the original {@link View}.
       * @return {Object} mapped item
       */

      /**
       * The callback that computes the aggregation of the modified {@link View}.
       * @callback View~reducer
       * @param {Object} accumulator
       * @param {Object} item
       * @return {Object}
       */

      _recorder_._src_client_reactive_active_group_select_js.View.stack = new _recorder_._src_client_reactive_active_group_select_js.Stack();
      _recorder_._src_client_reactive_active_group_select_js.View.current = function () {
        return _recorder_._src_client_reactive_active_group_select_js.View.stack.top();
      };
      _recorder_._src_client_reactive_active_group_select_js.View.withOnStack = function (el, callback, context) {
        _recorder_._src_client_reactive_active_group_select_js.View.stack.push(el);
        try {
          callback.call(context);
        } finally {
          _recorder_._src_client_reactive_active_group_select_js.View.stack.pop();
        }
      };

      /**
       * ################## CSS SELECTORS ##################
       */
      const selectorByAnimationName = new Map();
      _recorder_._src_client_reactive_active_group_select_js.selectorByAnimationName = selectorByAnimationName;
      const selectors = {};
      _recorder_._src_client_reactive_active_group_select_js.selectors = selectors;
      let animationCount = 0;
      _recorder_._src_client_reactive_active_group_select_js.animationCount = animationCount;
      const styles = document.createElement('style');
      _recorder_._src_client_reactive_active_group_select_js.styles = styles;
      const keyframes = document.createElement('style');
      _recorder_._src_client_reactive_active_group_select_js.keyframes = keyframes;
      const head = document.getElementsByTagName('head')[0];
      _recorder_._src_client_reactive_active_group_select_js.head = head;
      const startName = 'animationstart';
      _recorder_._src_client_reactive_active_group_select_js.startName = startName;
      const startEvent = function (event) {
        event.selector = _recorder_._src_client_reactive_active_group_select_js.selectorByAnimationName.get(event.animationName);
        ((_recorder_._src_client_reactive_active_group_select_js.startListenerByRoot.get(this) || {})[event.animationName] || []).forEach(fn => {
          fn.call(this, event);
        });
      };
      _recorder_._src_client_reactive_active_group_select_js.startEvent = startEvent;
      const roots = new Set();
      _recorder_._src_client_reactive_active_group_select_js.roots = roots;
      const startListenerByRoot = new Map();

      _recorder_._src_client_reactive_active_group_select_js.startListenerByRoot = startListenerByRoot;
      _recorder_._src_client_reactive_active_group_select_js.styles.type = _recorder_._src_client_reactive_active_group_select_js.keyframes.type = 'text/css';
      _recorder_._src_client_reactive_active_group_select_js.head.appendChild(_recorder_._src_client_reactive_active_group_select_js.styles);
      _recorder_._src_client_reactive_active_group_select_js.head.appendChild(_recorder_._src_client_reactive_active_group_select_js.keyframes);

      function addSelectorListener(root, selector, fn) {
        var key = _recorder_._src_client_reactive_active_group_select_js.selectors[selector];
        if (!_recorder_._src_client_reactive_active_group_select_js.startListenerByRoot.has(root)) {
          _recorder_._src_client_reactive_active_group_select_js.startListenerByRoot.set(root, {});
        }
        var listeners = _recorder_._src_client_reactive_active_group_select_js.startListenerByRoot.get(root);

        if (!key) {
          key = _recorder_._src_client_reactive_active_group_select_js.selectors[selector] = 'SelectorListener-' + _recorder_._src_client_reactive_active_group_select_js.animationCount++ + '-' + selector.replace(/[^0-9a-zA-Z]/gi, '');
          let node = document.createTextNode(`@keyframes ${key} {
  from { outline-color: #fff; }
  to { outline-color: #000; }
}`);
          _recorder_._src_client_reactive_active_group_select_js.keyframes.appendChild(node);
          _recorder_._src_client_reactive_active_group_select_js.styles.sheet.insertRule(`${selector} {
  animation-duration: 0.001s;
  animation-name: ${key} !important;
}`, 0);
          _recorder_._src_client_reactive_active_group_select_js.selectorByAnimationName.set(key, selector);
        }

        if (!_recorder_._src_client_reactive_active_group_select_js.roots.has(root)) {
          _recorder_._src_client_reactive_active_group_select_js.roots.add(root);
          root.addEventListener(_recorder_._src_client_reactive_active_group_select_js.startName, _recorder_._src_client_reactive_active_group_select_js.startEvent, false);
        }

        (listeners[key] = listeners[key] || []).push(fn);
      }

      /**
       * chrome does not support the animationcancel event, so we have to resort back to other means, namely polling
       */
      const stopMatchingDetectors = new Set();

      _recorder_._src_client_reactive_active_group_select_js.stopMatchingDetectors = stopMatchingDetectors;
      function removeObsoleteListeners() {
        Array.from(_recorder_._src_client_reactive_active_group_select_js.stopMatchingDetectors).forEach(detector => {
          if (!detector.matchesSelector()) {
            detector.removeElement();
            _recorder_._src_client_reactive_active_group_select_js.stopMatchingDetectors.delete(detector);
          }
        });
        if (_recorder_._src_client_reactive_active_group_select_js.stopMatchingDetectors.size === 0) {
          _recorder_._src_client_reactive_active_group_select_js.stopMatchingLoop.pause();
        }
      }

      const stopMatchingLoop = new _recorder_._src_client_reactive_active_group_select_js.PausableLoop(() => {
        // lively.warn('check stop matching');
        _recorder_._src_client_reactive_active_group_select_js.removeObsoleteListeners();
      });

      _recorder_._src_client_reactive_active_group_select_js.stopMatchingLoop = stopMatchingLoop;
      function trackSelector(selector, { root = document }) {
        const view = new _recorder_._src_client_reactive_active_group_select_js.View();
        _recorder_._src_client_reactive_active_group_select_js.addSelectorListener(root, selector, event => {
          const element = event.target;
          view.safeAdd(element);

          _recorder_._src_client_reactive_active_group_select_js.stopMatchingDetectors.add({
            matchesSelector() {
              const inDOM = element.getRootNode({ composed: true }) === document;
              return inDOM && element.matches(selector);
            },
            removeElement() {
              view.safeRemove(element);
            }
          });
          _recorder_._src_client_reactive_active_group_select_js.stopMatchingLoop.ensureRunning();
        });
        return view;
      }

      function __unload__() {
        _recorder_._src_client_reactive_active_group_select_js.styles.remove();
        _recorder_._src_client_reactive_active_group_select_js.keyframes.remove();
        _recorder_._src_client_reactive_active_group_select_js.roots.forEach(root => root.removeEventListener(_recorder_._src_client_reactive_active_group_select_js.startName, _recorder_._src_client_reactive_active_group_select_js.startEvent, false));

        _recorder_._src_client_reactive_active_group_select_js.stopMatchingLoop.pause();
      }

      /**
       * @function select
       * @param {Class} Class
       * @return {View}
       */

      _export('__unload__', __unload__);

      function select(Class, options) {
        // css selector given?
        if (typeof Class === 'string') {
          return _recorder_._src_client_reactive_active_group_select_js.trackSelector(Class, options || {});
        }

        // fall back to track all instances of a class
        return _recorder_._src_client_reactive_active_group_select_js.baseViewForClass(Class);
      }

      _export('default', select);
    }
  };
});