Source: display-collection.js

/*
 * Copyright 2015-2017 Hewlett Packard Enterprise Development Company, L.P.
 * Licensed under the MIT License (the "License"); you may not use this file except in compliance with the License.
 */

/**
 * @module display-collection
 */
define([
    'underscore',
    'backbone'
], function(_, Backbone) {
    'use strict';

    // When we don't know the count for a value (because it is in the selected values collection but not the parametric
    // collection) use null instead
    var UNKNOWN_COUNT = null;

    // Transforms an array of selected values into an array of values collection model attributes
    function attributesForUnknownCountValues(values) {
        return _.map(values, function(value) {
            return {id: value, count: UNKNOWN_COUNT, selected: true};
        });
    }

    /**
     * The attributes set on each model in a [ValuesCollection]{@link module:display-collection~ValuesCollection}.
     * @typedef module:display-collection~ValuesCollection.ValueModelAttributes
     * @property {string} id The value name
     * @property {number} count The number of documents with the current search or null if we don't have that information
     */
    /**
     * Collection for storing the parametric values associated with a single parametric field.
     * @constructor
     * @name module:display-collection~ValuesCollection
     * @extends {Backbone.Collection}
     */
    var ValuesCollection = Backbone.Collection.extend({
        comparator: function(model) {
            // If count is null this evaluates to -0
            return -model.get('count');
        }
    });

    /**
     * The attributes set on each [DisplayModel]{@link module:display-collection~DisplayModel}.
     * @typedef module:display-collection~DisplayModel.DisplayModelAttributes
     * @property {string} id The field name
     * @property {string} displayName A "prettified" friendly name for the field
     * @property {boolean} selected Whether the user has selected the value
     * @property {boolean} selected Whether the user has selected the value
     */
    /**
     * The model for the [DisplayCollection]{@link module:display-collection}, representing a parametric field. The values
     * for the field are stored in the fieldValues property.
     * @constructor
     * @name module:display-collection~DisplayModel
     * @property {module:display-collection~ValuesCollection} fieldValues
     * @extends {Backbone.Model}
     */
    var DisplayModel = Backbone.Model.extend({
        initialize: function(attributes, options) {
            this.fieldValues = new ValuesCollection(options.initialValues);
        }
    });

    /**
     * The attributes expected on models in the parametricCollection.
     * @typedef module:display-collection~ParametricCollectionAttributes
     * @property {string} name The field name
     * @property {Array.<{value: string, count: number}>} values The values and associated document counts for the field
     */
    /**
     * @typedef module:display-collection~DisplayCollection.DisplayCollectionOptions
     * @property {Backbone.Collection} parametricCollection The collection which fetches the parametric fields and values
     * which match the current search
     * @property {module:selected-values-collection~SelectedValuesCollection} selectedParametricValues The collection which tracks which parametric
     * values have been selected by the user
     */
    /**
     * Combines the parametric values selected by the user with the fields and values returned by the server in order
     * to back a view of parametric values relevant to a search. Once a parametric value has been selected (and added to the
     * selected parametric values collection), it should not be removed until the user deselects it, even if they search for
     * something else.
     * <p>This is designed to be a read only view on the underlying collections and should not be modified externally.
     * @name module:display-collection~DisplayCollection
     * @param {Array} models Must be the empty array due to restrictions in Backbone Collection
     * @param {module:display-collection~DisplayCollectionOptions} options
     * @constructor
     * @extends Backbone.View
     */
    return Backbone.Collection.extend(/** @lends module:display-collection~DisplayCollection.prototype */{
        comparator: 'displayName',
        model: DisplayModel,

        initialize: function(models, options) {
            this.parametricCollection = options.parametricCollection;
            this.selectedParametricValues = options.selectedParametricValues;

            this.createModels = this.getResolvedDisplayModels;

            this.listenTo(this.selectedParametricValues, 'add', this.onSelectedValueAdd);
            this.listenTo(this.selectedParametricValues, 'remove', this.onSelectedValueRemove);
            this.listenTo(this.selectedParametricValues, 'reset', this.onReset);
            this.listenTo(this.parametricCollection, 'reset', this.onReset);

            Array.prototype.push.apply(models, this.createModels());
        },

        onSelectedValueAdd: function(selectedModel) {
            var field = selectedModel.get('field');
            var value = selectedModel.get('value');
            if(value) {
                var valueModelAttributes = {
                    id: value,
                    displayValue: selectedModel.get('displayValue'),
                    count: UNKNOWN_COUNT,
                    selected: true
                };
                var fieldModel = this.get(field);

                if(fieldModel) {
                    var valueModel = fieldModel.fieldValues.get(value);

                    if(valueModel) {
                        valueModel.set('selected', true);
                    } else {
                        fieldModel.fieldValues.add(valueModelAttributes);
                    }
                } else {
                    var attributes = {
                        id: field,
                        displayName: selectedModel.get('displayName'),
                        totalValues: 0,
                        type: 'Parametric'
                    };

                    this.add(new DisplayModel(attributes, {initialValues: [valueModelAttributes]}));
                }
            }
        },

        onSelectedValueRemove: function(selectedModel) {
            var field = selectedModel.get('field');
            var value = selectedModel.get('value');
            if(value) {
                var parametricModel = this.parametricCollection.get(field);

                var inParametricCollection = parametricModel && _.any(parametricModel.get('values'), function(item) {
                        return value === item.value;
                    });

                var model = this.get(field);

                if(inParametricCollection) {
                    model.fieldValues.get(value).set('selected', false);
                } else {
                    // Only remove the value model if the value is not in the parametric collection
                    if(model.fieldValues.length <= 1) {
                        this.remove(model);
                    } else {
                        model.fieldValues.remove(value);
                    }
                }
            }
        },

        onReset: function() {
            this.reset(this.createModels());
        },

        /**
         * Returns an array of display models based on the current state of the parametric and selected values collections.
         * @private
         * @return {module:display-collection~DisplayModel[]}
         */
        getResolvedDisplayModels: function() {
            var selectedFields = this.selectedParametricValues.toFieldsAndValues();

            var newModels = this.parametricCollection.map(function(parametricModel) {
                var field = parametricModel.get('id');

                // Track the selected values for this field so we don't consider them twice
                var selectedField = selectedFields[field];
                var selectedValues = selectedField ? selectedField.values : [];

                var initialValues = _.map(parametricModel.get('values'), function(item) {
                    var value = item.value;
                    var oldSelectedValuesLength = selectedValues.length;
                    selectedValues = _.without(selectedValues, value);
                    // If the length has changed after calling _.without, the value must have been selected
                    var isSelected = oldSelectedValuesLength !== selectedValues.length;
                    return {id: item.value, displayValue: item.displayValue, count: item.count, selected: isSelected};
                })
                // Handle any selected values which are not in the parametric collection
                    .concat(attributesForUnknownCountValues(selectedValues));

                // Delete the selected field from the map so we don't consider it twice
                delete selectedFields[field];

                var attributes = {
                    id: parametricModel.get('id'),
                    displayName: parametricModel.get('displayName'),
                    type: parametricModel.get('type'),
                    totalValues: parametricModel.get('totalValues')
                };

                return new DisplayModel(attributes, {initialValues: initialValues});
            }, this);

            // Handle any selected fields which were not present in the parametric collection
            newModels = newModels.concat(_.chain(selectedFields)
                .map(function(data, field) {
                    return data.range
                        ? null
                        : new DisplayModel(
                            {
                                id: field,
                                displayName: data.displayName,
                                type: data.type,
                                totalValues: null
                            },
                            {
                                initialValues: attributesForUnknownCountValues(data.values)
                            }
                        );
                })
                .compact()
                .value()
            );

            return newModels;
        }
    });
});
Copyright 2015-2017 Hewlett Packard Enterprise Development Company, L.P. Licensed under the MIT License (the "License"); you may not use this file except in compliance with the License.
Documentation generated by JSDoc 3.4.0 on Thu May 25th 16:25 2017 using the DocStrap template.