Source: music21/stream/filters.js

import { Music21Exception } from '../exceptions21.js';

export class FilterException extends Music21Exception {

}

class _StopIteration {}

export const StopIterationSingleton = new _StopIteration();

/**
 * @memberof music21.stream.filters
 */
export class StreamFilter {
    static get derivationStr() {
        return 'streamFilter';
    }

    reset() {}

    call(item, iterator) {
        return true;
    }
}

/**
 * @extends music21.stream.filters.StreamFilter
 */
export class IsFilter extends StreamFilter {
    static get derivationStr() {
        return 'is';
    }

    constructor(target=[]) {
        super();
        if (!Array.isArray(target)) {
            target = [target];
        }
        this.target = target;
        this.numToFind = target.length;
    }

    reset() {
        this.numToFind = this.target.length;
    }

    call(item, iterator) {
        if (this.numToFind === 0) {
            return StopIterationSingleton;
        }
        if (this.target.includes(item)) {
            this.numToFind -= 1;
            return true;
        } else {
            return false;
        }
    }
}

/**
 * @extends music21.stream.filters.StreamFilter
 */
export class IsNotFilter extends IsFilter {
    static get derivationStr() {
        return 'IsNot';
    }

    constructor(target) {
        super(target);
        this.numToFind = Number.MAX_SAFE_INTEGER;
    }

    reset() {}

    call(item, iterator) {
        const ret = super.call(item, iterator);
        if (ret === StopIterationSingleton) {
            return ret;
        } else {
            return !ret;
        }
    }
}

// TODO(msc): IdFilter

/**
 * @extends music21.stream.filters.StreamFilter
 */
export class ClassFilter extends StreamFilter {
    static get derivationStr() {
        return 'getElementsByClass';
    }

    constructor(classList=[]) {
        super();
        if (!Array.isArray(classList)) {
            classList = [classList];
        }
        this.classList = classList;
    }
    // TODO: __eq__

    call(item, iterator) {
        return item.isClassOrSubclass(this.classList);
    }
}

/**
 * @extends music21.stream.filters.StreamFilter
 */
export class ClassNotFilter extends ClassFilter {
    static get derivationStr() {
        return 'getElementsNotOfClass';
    }

    call(item, iterator) {
        return !(super.call(item, iterator));
    }
}

// TODO: GroupFilter

/**
 * @extends music21.stream.filters.StreamFilter
 */
export class OffsetFilter extends StreamFilter {
    static get derivationStr() {
        return 'getElementsByOffset';
    }

    constructor(
        offsetStart,
        offsetEnd,
        {
            includeEndBoundary=true,
            mustFinishInSpan=false,
            mustBeginInSpan=true,
            includeElementsThatEndAtStart=true,
        }={}
    ) {
        super();
        this.offsetStart = offsetStart;
        this.offsetEnd = offsetEnd;
        this.includeEndBoundary = includeEndBoundary;
        this.mustFinishInSpan = mustFinishInSpan;
        this.mustBeginInSpan = mustBeginInSpan;
        this.includeElementsThatEndAtStart = includeElementsThatEndAtStart;


        this.zeroLengthSearch = false;
        if (offsetEnd === undefined) {
            this.offsetEnd = offsetStart;
            this.zeroLengthSearch = true;
        } else if (offsetEnd <= offsetStart) {
            this.zeroLengthSearch = true;
        }


    }

    call(item, iterator) {
        return this.isElementOffsetInRange(item, item.offset);
    }

    isElementOffsetInRange(e, offset) {
        if (offset > this.offsetEnd) {
            // anything that begins after the span is definitely out.
            return false;
        }
        const dur = e.duration;
        const elementEnd = offset + dur.quarterLength;
        if (elementEnd < this.offsetStart) {
            // anything that finishes before the span ends is definitely out
            return false;
        }

        // some part of the element is at least touching some part of span.

        let elementIsZeroLength = false;
        if (dur.quarterLength === 0) {
            elementIsZeroLength = true;
        }
        if (this.zeroLengthSearch && elementIsZeroLength) {
            return true;
        }

        if (this.mustFinishInSpan) {
            if (elementEnd > this.offsetEnd) {
                return false;
            }
            if (!this.includeEndBoundary && offset === this.offsetEnd) {
                return false;
            }
        }

        if (this.mustBeginInSpan) {
            if (offset < this.offsetStart) {
                return false;
            }
            if (!this.includeEndBoundary && offset === this.offsetEnd) {
                return false;
            }
        } else if (!elementIsZeroLength && elementEnd === this.offsetEnd && this.zeroLengthSearch) {
            return false;
        }

        if (!this.includeEndBoundary && offset === this.offsetEnd) {
            return false;
        }
        if (!this.includeElementsThatEndAtStart && elementEnd === this.offsetStart) {
            return false;
        }
        return true;
    }
}

// TODO: OffsetFilter (high priority)
// TODO: OffsetHierarchyFilter
Music21j, Copyright © 2013-2021 Michael Scott Asato Cuthbert.
Documentation generated by JSDoc 3.6.3 on Wed Jul 31st 2019 using the DocStrap template.