Facet.js 10.3 KB
define(function(require) {
    'use strict';
    var _ = require('underscore');
    var Backbone = require('backbone');
    var Sort = require('./Sort');
    var Util = require('./Util');

    function wrapComparatorForAllBeforeAfter(comparator, doBefore, doAfter) {
        if (!doBefore && !doAfter) {
            return comparator;
        }
        return function beforeAfterComparator(a, b) {
            if (doBefore) {
                if (a === 'before') {
                    return -1;
                } else if (b === 'before') {
                    return 1;
                }
            }
            if (doAfter) {
                if (a === 'after') {
                    return 1;
                } else if (b === 'after') {
                    return -1;
                }
            }
            return comparator.call(this, a, b);
        };
    }
    var Facet = Backbone.Model.extend({
        defaults: function defaults() {
            return {
                checkedKeys: [],
                formName: null,
                query: null,
                suggestions: [],
            };
        },
        initialize: function(data, options) {
            this.set('all', true);
            this.on('change:queryMax change:queryMin', function() {
                this.set('hasQuery', this.get('queryMin') !== undefined && this.get('queryMax') !== undefined);
            }, this);
            (function(self) {
                var skipCallback;
                self.on('item-change change:queryMax change:queryMin', function() {
                    switch (skipCallback) {
                        case 'change:checkedKeys':
                        case 'change:all':
                            return;
                    }
                    var checkedKeys = [];
                    this.get('items').each(function(facetItem) {
                        if (facetItem.get('checked')) {
                            checkedKeys.push(facetItem.get('key'));
                        }
                    });
                    var checkedCount = checkedKeys.length;
                    if (this.get('queryMin') !== undefined && this.get('queryMax') !== undefined) {
                        checkedCount++;
                    }
                    skipCallback = 'item-change';
                    try {
                        this.set({
                            all: checkedCount === 0,
                            checkedKeys: checkedKeys,

                        });
                    } finally {
                        skipCallback = null;
                    }
                }, self);
                self.on('change:all', function() {
                    switch (skipCallback) {
                        case 'change:checkedKeys':
                        case 'item-change':
                            return;
                    }
                    skipCallback = 'change:all';
                    try {
                        this.get('items').invoke('set', {checked: false});
                        this.set({
                            checkedKeys: [],
                            queryMax: undefined,
                            queryMin: undefined,
                        });
                    } finally {
                        skipCallback = null;
                    }
                }, self);
                self.on('change:checkedKeys', function() {
                    switch (skipCallback) {
                        case 'item-change':
                        case 'change:all':
                            return;
                    }
                    var checkedKeys = {};
                    _.each(this.get('checkedKeys'), function(value, i) {
                        checkedKeys[value] = true;
                    });
                    skipCallback = 'change:checkedKeys';
                    var checkedCount = 0;
                    if (this.get('queryMin') !== undefined && this.get('queryMax') !== undefined) {
                        checkedCount++;
                    }
                    try {
                        this.get('items').each(function(facetItem) {
                            var newChecked = checkedKeys[facetItem.get('key')];
                            var currentChecked = facetItem.get('checked');
                            if (newChecked !== currentChecked) {
                                facetItem.set('checked', !!newChecked);
                            }
                            if (facetItem.get('checked')) {
                                checkedCount++;
                            }
                        });
                        this.set({
                            all: checkedCount === 0,
                        });
                    } finally {
                        skipCallback = null;
                    }
                }, self);
            })(this);
            var sortKeyExtractor = options.sortKeyExtractor;
            if (!sortKeyExtractor) {
                sortKeyExtractor = Util.getItemKeyAccessor;
            }
            var formatter = options.formatter;
            if (!formatter) {
                var labelMap = options.labelMap;
                if (labelMap) {
                    formatter = function(item) {
                        var value = item.get('key');
                        var label = options.labelMap[value.toUpperCase()];
                        return label ? label : value;
                    };
                } else {
                    formatter = Util.getItemKeyAccessor;
                }
            }
            this.formatter = formatter;
            this.on('change:comparator change:orderByDirection', function() {
                var comparator = this.get('comparator');
                if (!comparator) {
                    comparator = Sort.standard;
                } else if (typeof comparator === 'string') {
                    comparator = Sort[comparator];
                }
                switch (options.facetType) {
                    case 'year':
                    case 'range':
                    case 'interval':
                        comparator = wrapComparatorForAllBeforeAfter(comparator, ['all', 'before'].indexOf(options.other) !== -1, ['all', 'after'].indexOf(options.other) !== -1);
                        break;
                }
                var facet = this;
                comparator = (function(comparator) {
                    return function facetItemValue(a, b) {
                        var keyA = sortKeyExtractor(a);
                        var keyB = sortKeyExtractor(b);
                        switch (facet.get('orderByDirection')) {
                            case 'desc':
                                var tmp = keyA;
                                keyA = keyB;
                                keyB = tmp;
                                break;
                        }
                        return comparator.call(facet, keyA, keyB);
                    };
                })(comparator);
                var items = this.get('items');
                items.comparator = comparator;
                items.sort();
            }, this);

            this.set('items', new ItemCollection([]));
            this.set('comparator', options.comparator, {silent: true});
            this.trigger('change:comparator');
            this.get('items').on('item-change', function() {
                this.trigger('item-change');
            }, this);
            this.facetType = options.facetType;
            this.facetStats = options.facetStats;
            this.other = options.other;
            this.facetField = options.facetField;
            this.queryField = options.queryField;
            this.queryValue = options.queryValue;
            this.bins = options.bins;
            switch (this.facetType) {
                case 'year':
                    this.rangeStart = options.start;
                    this.rangeEnd = options.end;
                    this.rangeGap = '+' + (options.gap ? options.gap : 1) + 'YEAR';
                    break;
                case 'range':
                    this.rangeStart = options.start;
                    this.rangeEnd = options.end;
                    this.rangeGap = options.gap;
                    break;
                case 'field':
                case 'year-field':
                case 'interval':
                    break;
                default:
                    var e = new Error('Unsupported facet type: ' + this.facetType);
                    e.facet = this;
                    throw e;
            }
            return Facet.__super__.initialize.apply(this, arguments);
        },
        /*
        toJSON: function toJSON(options) {
            if (!!options.facetSuggestions) {
                return {
                    rows: 0,
                    facet: true,
                    wt: 'json',
                    q: '*:*',
                    'facet.contains': this.get('query'),
                    'facet.contains.ignoreCase': true,
                    'facet.field': this.queryField,
                    'facet.mincount': 1,
                };
            }
        },
        parse: function parse(data, options) {
            if (!!options.facetSuggestions) {
                var suggestions = [];
                var key;
                _.each(data.facet_counts.facet_fields[this.queryField], function(value, index) {
                    if (index % 2 === 0) {
                        key = value;
                    } else if (value > 0) {
                        suggestions.push({key: key, value: value});
                    }
                });
                return {
                    suggestions: suggestions,
                };
            }
            return null;
        },
        */
    });
    var ItemCollection = Backbone.Collection.extend({
        remove: function() {
        }
    });
    var Item = Facet.Item = Backbone.Model.extend({
        idAttribute: 'key',
        defaults: {
            checked: false,
            hidden: false,
        },
        initialize: function(data, options) {
            this.on('add', function(model, parent, options) {
                // added to a collection
                this.on('change:checked', function() {
                    parent.trigger('item-change');
                });
            }, this);
            return Item.__super__.initialize.apply(this, arguments);
        }
    });

    return Facet;
});