<commit_before>import {Utils} from "ag-grid/main";
import {ColDef} from "ag-grid/main";
import {ISetFilterParams} from "ag-grid/main";
import {TextFormatter} from "ag-grid/main";
import {TextFilter} from "ag-grid/main";
import {InMemoryRowModel, IRowModel, Constants} from 'ag-grid';
import {SetFilterValuesFunc, SetFilterValuesFuncParams} from "ag-grid/main";
import {ExternalPromise, Promise} from 'ag-grid';

// we cannot have 'null' as a key in a JavaScript map,
// it needs to be a string. so we use this string for
// storing null values.
const NULL_VALUE = '___NULL___';

export enum SetFilterModelValuesType {
    PROVIDED_LIST, PROVIDED_CB, NOT_PROVIDED
}

export class SetFilterModel {

    private colDef: ColDef;
    private filterParams: ISetFilterParams;

    private inMemoryRowModel: InMemoryRowModel;
    private valueGetter: any;
    private allUniqueValues: string[]; // all values in the table
    private availableUniqueValues: string[]; // all values not filtered by other rows
    private displayedValues: any[]; // all values we are rendering on screen (ie after mini filter)
    private miniFilter: string;
    private selectedValuesCount: number;
    private selectedValuesMap: {[value: string]: any};
    private suppressSorting: boolean;
    private formatter: TextFormatter;

    // to make code more readable, we work these out once, and
    // then refer to each time. both are derived from the filterParams
    private showingAvailableOnly: boolean;
    private valuesType: SetFilterModelValuesType;

    private doesRowPassOtherFilters: any;
    private modelUpdatedFunc: (values: string[], selected?:string[]) => void;
    private isLoadingFunc: (loading: boolean) => void;

    private filterValuesExternalPromise: ExternalPromise<string[]>;
    private filterValuesPromise: Promise<string[]>;

    constructor(
        colDef: ColDef,
        rowModel: IRowModel,
        valueGetter: any,
        doesRowPassOtherFilters: any,
        suppressSorting: boolean,
        modelUpdatedFunc: (values:string[], selected?:string[])=>void,
        isLoadingFunc: (loading:boolean)=>void
    ) {
        this.suppressSorting = suppressSorting;
        this.colDef = colDef;
        this.valueGetter = valueGetter;
        this.doesRowPassOtherFilters = doesRowPassOtherFilters;
        this.modelUpdatedFunc = modelUpdatedFunc;
        this.isLoadingFunc = isLoadingFunc;

        if (rowModel.getType()===Constants.ROW_MODEL_TYPE_IN_MEMORY) {
            this.inMemoryRowModel = <InMemoryRowModel> rowModel;
        }

        this.filterParams = this.colDef.filterParams ? <ISetFilterParams> this.colDef.filterParams : <ISetFilterParams>{};
        if (Utils.exists(this.filterParams) && Utils.exists(this.filterParams.values)) {
            this.valuesType =  Array.isArray(this.filterParams.values)?
                                        SetFilterModelValuesType.PROVIDED_LIST :
                                        SetFilterModelValuesType.PROVIDED_CB;
            this.showingAvailableOnly = this.filterParams.suppressRemoveEntries!==true;
        } else {
            this.valuesType = SetFilterModelValuesType.NOT_PROVIDED;
            this.showingAvailableOnly = true;
        }

        this.createAllUniqueValues();
        this.createAvailableUniqueValues();

        // by default, no filter, so we display everything
        this.displayedValues = this.availableUniqueValues;
        this.miniFilter = null;

        // we use a map rather than an array for the selected values as the lookup
        // for a map is much faster than the lookup for an array, especially when
        // the length of the array is thousands of records long
        this.selectedValuesMap = {};
        this.selectEverything();
        this.formatter = this.filterParams.textFormatter ? this.filterParams.textFormatter : TextFilter.DEFAULT_FORMATTER;
    }


    // if keepSelection not set will always select all filters
    // if keepSelection set will keep current state of selected filters
    //    unless selectAll chosen in which case will select all
    public refreshAfterNewRowsLoaded(keepSelection: any, isSelectAll: boolean) {
        this.createAllUniqueValues();
        this.refreshSelection(keepSelection, isSelectAll);
    }

    // if keepSelection not set will always select all filters
    // if keepSelection set will keep current state of selected filters
    //    unless selectAll chosen in which case will select all
    public refreshValues(valuesToUse:string[], keepSelection: any, isSelectAll: boolean) {
        this.setValues(valuesToUse);
        this.refreshSelection(keepSelection, isSelectAll);
    }

    private refreshSelection(keepSelection: any, isSelectAll: boolean) {
        this.createAvailableUniqueValues();

        let oldModel = Object.keys(this.selectedValuesMap);

        this.selectedValuesMap = {};
        this.processMiniFilter();

        if (keepSelection) {
            this.setModel(oldModel, isSelectAll);
        } else {
            this.selectEverything();
        }
    }

    public refreshAfterAnyFilterChanged() {
        if (this.showingAvailableOnly) {
            this.createAvailableUniqueValues();
            this.processMiniFilter();
        }
    }

    private createAllUniqueValues() {
        if (this.areValuesSync()) {
            let valuesToUse: string[] = this.extractSyncValuesToUse();
            this.setValues(valuesToUse);
            this.filterValuesPromise = Promise.resolve(null);
        } else {
            this.filterValuesExternalPromise = Promise.external<string[]>();
            this.filterValuesPromise = this.filterValuesExternalPromise.promise;
            this.isLoadingFunc(true);
            this.setValues([]);
            let callback = <SetFilterValuesFunc> this.filterParams.values;
            let params: SetFilterValuesFuncParams = {
                success:this.onAsyncValuesLoaded.bind(this)
            };
            callback(params);
        }
    }

    private onAsyncValuesLoaded(values:string[]): void {
        this.modelUpdatedFunc(values);
        this.isLoadingFunc(false);
        this.filterValuesExternalPromise.resolve(values);
    }

    private areValuesSync() {
        return this.valuesType == SetFilterModelValuesType.PROVIDED_LIST || this.valuesType == SetFilterModelValuesType.NOT_PROVIDED;
    }

    public setValuesType(value:SetFilterModelValuesType){
        this.valuesType = value;
    }

    private setValues(valuesToUse: string[]) {
        this.allUniqueValues = valuesToUse;
        if (!this.suppressSorting) {
            this.sortValues(this.allUniqueValues);
        }
    }<commit_msg>AG-1455: Added colDef to the async values callback for setFilters<commit_after>import {
    ColDef,
    ISetFilterParams,
    SetFilterValuesFunc,
    SetFilterValuesFuncParams,
    TextFilter,
    TextFormatter,
    Utils
} from "ag-grid/main";
import {Constants, ExternalPromise, InMemoryRowModel, IRowModel, Promise} from 'ag-grid';

// we cannot have 'null' as a key in a JavaScript map,
// it needs to be a string. so we use this string for
// storing null values.
const NULL_VALUE = '___NULL___';

export enum SetFilterModelValuesType {
    PROVIDED_LIST, PROVIDED_CB, NOT_PROVIDED
}

export class SetFilterModel {

    private colDef: ColDef;
    private filterParams: ISetFilterParams;

    private inMemoryRowModel: InMemoryRowModel;
    private valueGetter: any;
    private allUniqueValues: string[]; // all values in the table
    private availableUniqueValues: string[]; // all values not filtered by other rows
    private displayedValues: any[]; // all values we are rendering on screen (ie after mini filter)
    private miniFilter: string;
    private selectedValuesCount: number;
    private selectedValuesMap: {[value: string]: any};
    private suppressSorting: boolean;
    private formatter: TextFormatter;

    // to make code more readable, we work these out once, and
    // then refer to each time. both are derived from the filterParams
    private showingAvailableOnly: boolean;
    private valuesType: SetFilterModelValuesType;

    private doesRowPassOtherFilters: any;
    private modelUpdatedFunc: (values: string[], selected?:string[]) => void;
    private isLoadingFunc: (loading: boolean) => void;

    private filterValuesExternalPromise: ExternalPromise<string[]>;
    private filterValuesPromise: Promise<string[]>;

    constructor(
        colDef: ColDef,
        rowModel: IRowModel,
        valueGetter: any,
        doesRowPassOtherFilters: any,
        suppressSorting: boolean,
        modelUpdatedFunc: (values:string[], selected?:string[])=>void,
        isLoadingFunc: (loading:boolean)=>void
    ) {
        this.suppressSorting = suppressSorting;
        this.colDef = colDef;
        this.valueGetter = valueGetter;
        this.doesRowPassOtherFilters = doesRowPassOtherFilters;
        this.modelUpdatedFunc = modelUpdatedFunc;
        this.isLoadingFunc = isLoadingFunc;

        if (rowModel.getType()===Constants.ROW_MODEL_TYPE_IN_MEMORY) {
            this.inMemoryRowModel = <InMemoryRowModel> rowModel;
        }

        this.filterParams = this.colDef.filterParams ? <ISetFilterParams> this.colDef.filterParams : <ISetFilterParams>{};
        if (Utils.exists(this.filterParams) && Utils.exists(this.filterParams.values)) {
            this.valuesType =  Array.isArray(this.filterParams.values)?
                                        SetFilterModelValuesType.PROVIDED_LIST :
                                        SetFilterModelValuesType.PROVIDED_CB;
            this.showingAvailableOnly = this.filterParams.suppressRemoveEntries!==true;
        } else {
            this.valuesType = SetFilterModelValuesType.NOT_PROVIDED;
            this.showingAvailableOnly = true;
        }

        this.createAllUniqueValues();
        this.createAvailableUniqueValues();

        // by default, no filter, so we display everything
        this.displayedValues = this.availableUniqueValues;
        this.miniFilter = null;

        // we use a map rather than an array for the selected values as the lookup
        // for a map is much faster than the lookup for an array, especially when
        // the length of the array is thousands of records long
        this.selectedValuesMap = {};
        this.selectEverything();
        this.formatter = this.filterParams.textFormatter ? this.filterParams.textFormatter : TextFilter.DEFAULT_FORMATTER;
    }


    // if keepSelection not set will always select all filters
    // if keepSelection set will keep current state of selected filters
    //    unless selectAll chosen in which case will select all
    public refreshAfterNewRowsLoaded(keepSelection: any, isSelectAll: boolean) {
        this.createAllUniqueValues();
        this.refreshSelection(keepSelection, isSelectAll);
    }

    // if keepSelection not set will always select all filters
    // if keepSelection set will keep current state of selected filters
    //    unless selectAll chosen in which case will select all
    public refreshValues(valuesToUse:string[], keepSelection: any, isSelectAll: boolean) {
        this.setValues(valuesToUse);
        this.refreshSelection(keepSelection, isSelectAll);
    }

    private refreshSelection(keepSelection: any, isSelectAll: boolean) {
        this.createAvailableUniqueValues();

        let oldModel = Object.keys(this.selectedValuesMap);

        this.selectedValuesMap = {};
        this.processMiniFilter();

        if (keepSelection) {
            this.setModel(oldModel, isSelectAll);
        } else {
            this.selectEverything();
        }
    }

    public refreshAfterAnyFilterChanged() {
        if (this.showingAvailableOnly) {
            this.createAvailableUniqueValues();
            this.processMiniFilter();
        }
    }

    private createAllUniqueValues() {
        if (this.areValuesSync()) {
            let valuesToUse: string[] = this.extractSyncValuesToUse();
            this.setValues(valuesToUse);
            this.filterValuesPromise = Promise.resolve(null);
        } else {
            this.filterValuesExternalPromise = Promise.external<string[]>();
            this.filterValuesPromise = this.filterValuesExternalPromise.promise;
            this.isLoadingFunc(true);
            this.setValues([]);
            let callback = <SetFilterValuesFunc> this.filterParams.values;
            let params: SetFilterValuesFuncParams = {
                success:this.onAsyncValuesLoaded.bind(this),
                colDef: this.colDef
            };
            callback(params);
        }
    }

    private onAsyncValuesLoaded(values:string[]): void {
        this.modelUpdatedFunc(values);
        this.isLoadingFunc(false);
        this.filterValuesExternalPromise.resolve(values);
    }

    private areValuesSync() {
        return this.valuesType == SetFilterModelValuesType.PROVIDED_LIST || this.valuesType == SetFilterModelValuesType.NOT_PROVIDED;
    }

    public setValuesType(value:SetFilterModelValuesType){
        this.valuesType = value;
    }

    private setValues(valuesToUse: string[]) {
        this.allUniqueValues = valuesToUse;
        if (!this.suppressSorting) {
            this.sortValues(this.allUniqueValues);
        }
    }
{'fraction_non_alphanumeric': 0.08641975308641975, 'fraction_numerical': 0.00030111412225233364, 'mean_word_length': 2.890190336749634, 'pattern_counts': {'":': 0, '<': 17, '<?xml version=': 0, '>': 25, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 4, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '4309734', 'n_tokens_mistral': 3441, 'n_tokens_neox': 3408, 'n_words': 879}