192 lines
		
	
	
		
			4.3 KiB
		
	
	
	
		
			JavaScript
		
	
	
	
	
	
			
		
		
	
	
			192 lines
		
	
	
		
			4.3 KiB
		
	
	
	
		
			JavaScript
		
	
	
	
	
	
'use strict';
 | 
						|
 | 
						|
const Ref = require('./ref');
 | 
						|
 | 
						|
 | 
						|
const internals = {};
 | 
						|
 | 
						|
 | 
						|
internals.extendedCheckForValue = function (value, insensitive) {
 | 
						|
 | 
						|
    const valueType = typeof value;
 | 
						|
 | 
						|
    if (valueType === 'object') {
 | 
						|
        if (value instanceof Date) {
 | 
						|
            return (item) => {
 | 
						|
 | 
						|
                return item instanceof Date && value.getTime() === item.getTime();
 | 
						|
            };
 | 
						|
        }
 | 
						|
 | 
						|
        if (Buffer.isBuffer(value)) {
 | 
						|
            return (item) => {
 | 
						|
 | 
						|
                return Buffer.isBuffer(item) && value.length === item.length && value.toString('binary') === item.toString('binary');
 | 
						|
            };
 | 
						|
        }
 | 
						|
    }
 | 
						|
    else if (insensitive && valueType === 'string') {
 | 
						|
        const lowercaseValue = value.toLowerCase();
 | 
						|
        return (item) => {
 | 
						|
 | 
						|
            return typeof item === 'string' && lowercaseValue === item.toLowerCase();
 | 
						|
        };
 | 
						|
    }
 | 
						|
 | 
						|
    return null;
 | 
						|
};
 | 
						|
 | 
						|
 | 
						|
module.exports = class InternalSet {
 | 
						|
 | 
						|
    constructor(from) {
 | 
						|
 | 
						|
        this._set = new Set(from);
 | 
						|
        this._hasRef = false;
 | 
						|
    }
 | 
						|
 | 
						|
    add(value, refs) {
 | 
						|
 | 
						|
        const isRef = Ref.isRef(value);
 | 
						|
        if (!isRef && this.has(value, null, null, false)) {
 | 
						|
 | 
						|
            return this;
 | 
						|
        }
 | 
						|
 | 
						|
        if (refs !== undefined) { // If it's a merge, we don't have any refs
 | 
						|
            Ref.push(refs, value);
 | 
						|
        }
 | 
						|
 | 
						|
        this._set.add(value);
 | 
						|
 | 
						|
        this._hasRef |= isRef;
 | 
						|
 | 
						|
        return this;
 | 
						|
    }
 | 
						|
 | 
						|
    merge(add, remove) {
 | 
						|
 | 
						|
        for (const item of add._set) {
 | 
						|
            this.add(item);
 | 
						|
        }
 | 
						|
 | 
						|
        for (const item of remove._set) {
 | 
						|
            this.remove(item);
 | 
						|
        }
 | 
						|
 | 
						|
        return this;
 | 
						|
    }
 | 
						|
 | 
						|
    remove(value) {
 | 
						|
 | 
						|
        this._set.delete(value);
 | 
						|
        return this;
 | 
						|
    }
 | 
						|
 | 
						|
    has(value, state, options, insensitive) {
 | 
						|
 | 
						|
        return !!this.get(value, state, options, insensitive);
 | 
						|
    }
 | 
						|
 | 
						|
    get(value, state, options, insensitive) {
 | 
						|
 | 
						|
        if (!this._set.size) {
 | 
						|
            return false;
 | 
						|
        }
 | 
						|
 | 
						|
        const hasValue = this._set.has(value);
 | 
						|
        if (hasValue) {
 | 
						|
            return { value };
 | 
						|
        }
 | 
						|
 | 
						|
        const extendedCheck = internals.extendedCheckForValue(value, insensitive);
 | 
						|
        if (!extendedCheck) {
 | 
						|
            if (state && this._hasRef) {
 | 
						|
                for (let item of this._set) {
 | 
						|
                    if (Ref.isRef(item)) {
 | 
						|
                        item = [].concat(item(state.reference || state.parent, options));
 | 
						|
                        const found = item.indexOf(value);
 | 
						|
                        if (found >= 0) {
 | 
						|
                            return { value: item[found] };
 | 
						|
                        }
 | 
						|
                    }
 | 
						|
                }
 | 
						|
            }
 | 
						|
 | 
						|
            return false;
 | 
						|
        }
 | 
						|
 | 
						|
        return this._has(value, state, options, extendedCheck);
 | 
						|
    }
 | 
						|
 | 
						|
    _has(value, state, options, check) {
 | 
						|
 | 
						|
        const checkRef = !!(state && this._hasRef);
 | 
						|
 | 
						|
        const isReallyEqual = function (item) {
 | 
						|
 | 
						|
            if (value === item) {
 | 
						|
                return true;
 | 
						|
            }
 | 
						|
 | 
						|
            return check(item);
 | 
						|
        };
 | 
						|
 | 
						|
        for (let item of this._set) {
 | 
						|
            if (checkRef && Ref.isRef(item)) { // Only resolve references if there is a state, otherwise it's a merge
 | 
						|
                item = item(state.reference || state.parent, options);
 | 
						|
 | 
						|
                if (Array.isArray(item)) {
 | 
						|
                    const found = item.findIndex(isReallyEqual);
 | 
						|
                    if (found >= 0) {
 | 
						|
                        return {
 | 
						|
                            value: item[found]
 | 
						|
                        };
 | 
						|
                    }
 | 
						|
 | 
						|
                    continue;
 | 
						|
                }
 | 
						|
            }
 | 
						|
 | 
						|
            if (isReallyEqual(item)) {
 | 
						|
                return {
 | 
						|
                    value: item
 | 
						|
                };
 | 
						|
            }
 | 
						|
        }
 | 
						|
 | 
						|
        return false;
 | 
						|
    }
 | 
						|
 | 
						|
    values(options) {
 | 
						|
 | 
						|
        if (options && options.stripUndefined) {
 | 
						|
            const values = [];
 | 
						|
 | 
						|
            for (const item of this._set) {
 | 
						|
                if (item !== undefined) {
 | 
						|
                    values.push(item);
 | 
						|
                }
 | 
						|
            }
 | 
						|
 | 
						|
            return values;
 | 
						|
        }
 | 
						|
 | 
						|
        return Array.from(this._set);
 | 
						|
    }
 | 
						|
 | 
						|
    slice() {
 | 
						|
 | 
						|
        const set = new InternalSet(this._set);
 | 
						|
        set._hasRef = this._hasRef;
 | 
						|
        return set;
 | 
						|
    }
 | 
						|
 | 
						|
    concat(source) {
 | 
						|
 | 
						|
        const set = new InternalSet([...this._set, ...source._set]);
 | 
						|
        set._hasRef = !!(this._hasRef | source._hasRef);
 | 
						|
        return set;
 | 
						|
    }
 | 
						|
};
 |