Code coverage report for src/operator/bufferToggle.ts

Statements: 98.91% (91 / 92)      Branches: 75% (3 / 4)      Functions: 100% (21 / 21)      Lines: 98.82% (84 / 85)      Ignored: none     

All files » src/operator/ » bufferToggle.ts
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149  1   1 1 1                   1   16     1   16 16     1 16   1             1 16   1 16 16 16 16     1 67 67 67 59       1 6 6 5 5 5 5   6 6     1 8 8 6 6 6 6 6   8 8     1 30 30   30 30 1   29       29 29 29 29 29       1 17 17     17 17 17 17 17   1   1 16 16     1 30     1 1     1     1   1 29 29 29     1 15     1 2     1 2   1  
import {Operator} from '../Operator';
import {Subscriber} from '../Subscriber';
import {Observable} from '../Observable';
import {Subscription} from '../Subscription';
import {tryCatch} from '../util/tryCatch';
import {errorObject} from '../util/errorObject';
 
/**
 * buffers values from the source by opening the buffer via signals from an observable provided to `openings`, and closing
 * and sending the buffers when an observable returned by the `closingSelector` emits.
 * @param {Observable<O>} openings An observable of notifications to start new buffers
 * @param {Function} an function, that takes the value emitted by the `openings` observable and returns an Observable, which,
 *  when it emits, signals that the associated buffer should be emitted and cleared.
 * @returns {Observable<T[]>} an observable of arrays of buffered values.
 */
export function bufferToggle<T, O>(openings: Observable<O>,
                                   closingSelector: (openValue: O) => Observable<any>): Observable<T[]> {
  return this.lift(new BufferToggleOperator<T, T, O>(openings, closingSelector));
}
 
class BufferToggleOperator<T, R, O> implements Operator<T, R> {
 
  constructor(private openings: Observable<O>,
              private closingSelector: (openValue: O) => Observable<any>) {
  }
 
  call(subscriber: Subscriber<T>): Subscriber<T> {
    return new BufferToggleSubscriber<T, O>(subscriber, this.openings, this.closingSelector);
  }
}
 
interface BufferContext<T> {
  buffer: T[];
  subscription: Subscription<T>;
}
 
class BufferToggleSubscriber<T, O> extends Subscriber<T> {
  private contexts: Array<BufferContext<T>> = [];
 
  constructor(destination: Subscriber<T>,
              private openings: Observable<O>,
              private closingSelector: (openValue: O) => Observable<any>) {
    super(destination);
    this.add(this.openings._subscribe(new BufferToggleOpeningsSubscriber(this)));
  }
 
  _next(value: T) {
    const contexts = this.contexts;
    const len = contexts.length;
    for (let i = 0; i < len; i++) {
      contexts[i].buffer.push(value);
    }
  }
 
  _error(err: any) {
    const contexts = this.contexts;
    while (contexts.length > 0) {
      const context = contexts.shift();
      context.subscription.unsubscribe();
      context.buffer = null;
      context.subscription = null;
    }
    this.contexts = null;
    this.destination.error(err);
  }
 
  _complete() {
    const contexts = this.contexts;
    while (contexts.length > 0) {
      const context = contexts.shift();
      this.destination.next(context.buffer);
      context.subscription.unsubscribe();
      context.buffer = null;
      context.subscription = null;
    }
    this.contexts = null;
    this.destination.complete();
  }
 
  openBuffer(value: O) {
    const closingSelector = this.closingSelector;
    const contexts = this.contexts;
 
    let closingNotifier = tryCatch(closingSelector)(value);
    if (closingNotifier === errorObject) {
      this._error(closingNotifier.e);
    } else {
      let context = {
        buffer: [],
        subscription: new Subscription()
      };
      contexts.push(context);
      const subscriber = new BufferToggleClosingsSubscriber(this, context);
      const subscription = closingNotifier._subscribe(subscriber);
      context.subscription.add(subscription);
      this.add(subscription);
    }
  }
 
  closeBuffer(context: BufferContext<T>) {
    const contexts = this.contexts;
    Iif (contexts === null) {
      return;
    }
    const { buffer, subscription } = context;
    this.destination.next(buffer);
    contexts.splice(contexts.indexOf(context), 1);
    this.remove(subscription);
    subscription.unsubscribe();
  }
}
 
class BufferToggleOpeningsSubscriber<T> extends Subscriber<T> {
  constructor(private parent: BufferToggleSubscriber<any, T>) {
    super(null);
  }
 
  _next(value: T) {
    this.parent.openBuffer(value);
  }
 
  _error(err) {
    this.parent.error(err);
  }
 
  _complete() {
    // noop
  }
}
 
class BufferToggleClosingsSubscriber<T> extends Subscriber<T> {
  constructor(private parent: BufferToggleSubscriber<any, T>,
              private context: { subscription: any, buffer: T[] }) {
    super(null);
  }
 
  _next() {
    this.parent.closeBuffer(this.context);
  }
 
  _error(err) {
    this.parent.error(err);
  }
 
  _complete() {
    this.parent.closeBuffer(this.context);
  }
}