component-search-facet-managed-range.js

/**
 * Managed Range Facet component functionality
 * @module managedrange
 * @param  {jQuery} $ Instance of jQuery
 * @param  {Document} document dom document object
 * @return {Object} list of methods for working with managed range component
*/
XA.component.search.facet.managedrange = (function ($, document) {
    /**
    * This object stores all public api methods
    * @type {Object.<Methods>}
    * @memberOf module:managedrange
    */
    var api = {},
        urlHelperModel,
        queryModel,
        apiModel,
        initialized = false;
    /**
     * @name module:managedrange.FacetManagedRangeModel
     * @constructor
     * @augments Backbone.Model
     */
    var FacetManagedRangeModel = Backbone.Model.extend(
        /** @lends module:managedrange.FacetManagedRangeModel.prototype **/

        {
            /**
            * Default model options
            * @default
            */
            defaults: {
                dataProperties: {},
                sig: []
            }
        });
    /**
    * @name module:managedrange.FacetManagedRangeView
    * @constructor
    * @augments Backbone.View
    */
    var FacetManagedRangeView = XA.component.search.baseView.extend(
        /** @lends module:managedrange.FacetManagedRangeView.prototype **/

        {
            /**
            * Initially sets data to model and watches events on which
            * view should be updated
            * @listens module:managedrange.FacetManagedRangeView~event:change
            * @listens module:XA.component.search.vent~event:hashChanged
            * @memberof module:managedrange.FacetManagedRangeView
            */
            initialize: function () {
                this.properties = this.$el.data().properties;

                if (this.model) {
                    this.model.set({ dataProperties: this.properties });
                    this.model.set("sig", this.translateSignatures(this.properties.searchResultsSignature, this.properties.f));
                }

                if (this.$el.find(".filterButton").length === 0) {
                    //If there is no filter button, we still have to have the possibility to use manual ranges
                    this.$el.find('.manualRangeMin').on('blur', this.textBoxChange.bind(this, []));
                    this.$el.find('.manualRangeMax').on('blur', this.textBoxChange.bind(this, []));
                }
                this.model.on("change", this.render, this);

                XA.component.search.vent.on("hashChanged", this.updateComponent.bind(this));
            },
            /**
           * list of events for Backbone View
           * @memberof module:managedrange.FacetManagedRangeView
           * @alias module:managedrange.FacetManagedRangeView#events
           */
            events: {
                'click .faceLink': 'linkClick',
                'click .facetRadio': 'radioClick',
                'click .facetCheckbox': 'checkBoxClick',
                'click .filterButton': 'filter',
                'click .bottom-remove-filter, .clear-filter': 'clearFilter',
                'keyup .manualRangeMin, .manualRangeMax': 'configureKeyCodes'
            },
            /**
             * On enter click calls 
             * ["filter"]{@link module:managedrange.FacetManagedRangeView.filter} method
             * @param {Event} e Event object
             * @memberof module:managedrange.FacetManagedRangeView
             * @alias module:managedrange.FacetManagedRangeView#configureKeyCodes
             */
            configureKeyCodes: function (e) {
                if (e.keyCode == 13) {
                    this.filter();
                }
            },
            /**
            * Helper function that will take all ranges selected on a page and 
            * update hash parameters according to this value
            * @param {Array<DomElement>} foundRangeControls List of range controls on a page
            * @memberof module:managedrange.FacetManagedRangeView
            * @alias module:managedrange.FacetManagedRangeView#updateHash
            */
            updateHash: function (foundRangeControls) {
                var sig = this.model.get('sig'),
                    ranges = [];

                _.each(foundRangeControls, function (range) {
                    var $range = $(range);
                    ranges.push($range.data().minvalue + "|" + $range.data().maxvalue);
                });

                this.$el.find('.manualRangeMin').val('');
                this.$el.find('.manualRangeMax').val('');

                queryModel.updateHash(this.updateSignaturesHash(sig, ranges.join(','), {}));
            },
            /**
             * Render view
             * @memberof module:managedrange.FacetManagedRangeView
             * @alias module:managedrange.FacetManagedRangeView#render
             */
            render: function () {
                var hashObj = queryModel.parseHashParameters(window.location.hash),
                    facetClose = this.$el.find(".facet-heading > span"),
                    sig = this.model.get('sig'),
                    inst = this,
                    ranges,
                    i;

                for (i = 0; i < sig.length; i++) {
                    if (hashObj.hasOwnProperty(sig[i]) && hashObj[sig[i]] !== '') {
                        ranges = hashObj[sig[i]].split(",");

                        facetClose.addClass('has-active-facet');
                        _.each(ranges, function (range) {
                            var r = range.split('|'),
                                minValue = r[0],
                                maxValue = r[1],
                                selector,
                                $component;

                            if (minValue === "" && maxValue === "") {
                                return;
                            } else if (minValue !== "" && maxValue !== "") {
                                selector = ".facetCheckbox[data-minvalue='" + minValue + "'][data-maxvalue='" + maxValue + "'], .facetRadio[data-minvalue='" + minValue + "'][data-maxvalue='" + maxValue + "']";
                            } else if (minValue !== "" && maxValue === "") {
                                selector = ".facetCheckbox[data-minvalue='" + minValue + "'], .facetRadio[data-minvalue='" + minValue + "']";
                            } else if (minValue === "" && maxValue !== "") {
                                selector = ".facetCheckbox[data-maxvalue='" + maxValue + "'], .facetRadio[data-maxvalue='" + maxValue + "']";
                            }

                            $component = $(selector);

                            if ($component.length > 0) {
                                $component.attr("checked", "checked");
                            } else {
                                inst.$el.find('.manualRangeMin').val(r[0]);
                                inst.$el.find('.manualRangeMax').val(r[1]);
                            }
                        });
                    }
                }
            },
            /**
             * Updates Hash base on selected element by calling 
             * ["updateHash"]{@link module:managedrange.FacetManagedRangeView.updateHash}
             * @param {Event} param Event that contains
             * currentTarget element
             * @memberof module:managedrange.FacetManagedRangeView
             * @alias module:managedrange.FacetManagedRangeView#radioClick
             */
            radioClick: function (param) {
                //fix for radio buttons groups in the ASP.NET repeater control
                var radio = $(param.currentTarget);
                $('.facetRadio').attr("name", radio.attr("name"));
                this.updateHash(radio);
            },
            /**
             * Clears values for manualRangeMin and manualRangeMax elements
             * @param {Event} param Event that contains
             * currentTarget element
             * @memberof module:managedrange.FacetManagedRangeView
             * @alias module:managedrange.FacetManagedRangeView#checkBoxClick
             */
            checkBoxClick: function (param) {
                var checkbox = $(param.currentTarget);
                if (checkbox.is(":checked")) {
                    this.$el.find('.manualRangeMin').val("");
                    this.$el.find('.manualRangeMax').val("");
                }
            },
            /**
             * Manages checked status for facetCheckbox 
             * Clears values for manualRangeMin and manualRangeMax elements
             * and updates hash value
             * @param {Event} param Event that contains
             * currentTarget element
             * @memberof module:managedrange.FacetManagedRangeView
             * @alias module:managedrange.FacetManagedRangeView#linkClick
             */
            linkClick: function (param) {
                var $link = $(param.currentTarget),
                    hashValue = $link.data().minvalue + "|" + $link.data().maxvalue;

                this.$el.find(".facetCheckbox[data-shortid!=" + $link.data().shortid + "]").removeAttr("checked");
                this.$el.find(".facetCheckbox[data-shortid=" + $link.data().shortid + "]").attr("checked", "checked");
                this.$el.find('.manualRangeMin').val("");
                this.$el.find('.manualRangeMax').val("");

                queryModel.updateHash(this.updateSignaturesHash(sig, hashValue, {}));
            },
            /**
             * If minRange or maxRange value set 
             * ["queryModel updateHash"]{@link module:searchQuery.updateHash} method
             * If checked CheckBox or RadioButton called
             * ["managedrange updateHash"]{@link module:managedrange.FacetManagedRangeView.updateHash}
             * If multiply selection disabled call 
             * ["radioClick"]{@link module:managedrange.FacetManagedRangeView.radioClick}
             * @memberof module:managedrange.FacetManagedRangeView
             * @alias module:managedrange.FacetManagedRangeView.filter
             */
            filter: function () {
                var multipleSelection = this.model.get('dataProperties').multipleSelection,
                    checkedCheckBoxes = this.$el.find('.facetCheckbox:checked'),
                    checkedRadioButton = this.$el.find('.facetRadio:checked'),
                    minRange = this.$el.find('.manualRangeMin'),
                    maxRange = this.$el.find('.manualRangeMax'),
                    sig = this.model.get('sig'),
                    maxRange, minRange, minValue, maxValue;

                //Gets values from text boxes
                minValue = minRange.length > 0 && minRange.val() !== "" ? minRange.val() : "";
                maxValue = maxRange.length > 0 && maxRange.val() !== "" ? maxRange.val() : "";

                if (minValue !== "" || maxValue !== "") {
                    //Clears all selected radio buttons and checkboxes
                    this.$el.find(".facetRadio").removeAttr("checked");
                    this.$el.find(".facetCheckbox").removeAttr("checked");

                    //Auto update hash after text box change when there is no filter button
                    queryModel.updateHash(this.updateSignaturesHash(sig, minValue + "|" + maxValue, {}));
                } else if (checkedCheckBoxes.length > 0 || checkedRadioButton.length > 0) {
                    if (multipleSelection) {
                        this.updateHash(checkedCheckBoxes);
                    } else {
                        this.radioClick({ currentTarget: checkedRadioButton });
                    }
                }
            },
            /**
            * Clears selected filter and updates hash
            * @memberof module:managedrange.FacetManagedRangeView
            * @alias module:managedrange.FacetManagedRangeView#clearFilter
            * 
            */
            clearFilter: function () {
                var minRange = this.$el.find('.manualRangeMin'),
                    maxRange = this.$el.find('.manualRangeMax'),
                    properties = this.model.get('dataProperties'),
                    facetClose = this.$el.find('.facet-heading > span'),
                    hash = queryModel.parseHashParameters(window.location.hash),
                    sig = this.model.get('sig'),
                    shouldClear = false,
                    facetData,
                    i;

                for (i = 0; i < sig.length; i++) {
                    facetData = sig[i];
                    if (typeof hash[facetData] !== "undefined" && hash[facetData] !== "") {
                        delete properties[facetData];
                        hash[facetData] = "";
                        shouldClear = true;
                    }
                }

                if (!shouldClear) {
                    return;
                }

                queryModel.updateHash(hash);

                this.model.set({ dataProperties: properties });

                this.$el.find('.facetCheckbox').removeAttr("checked");
                this.$el.find('.facetRadio').removeAttr("checked");

                facetClose.removeClass('has-active-facet');

                if (minRange.length > 0) {
                    minRange.val(minRange.data().defaultvalue);
                }
                if (maxRange.length > 0) {
                    maxRange.val(maxRange.data().defaultvalue);
                }
            },
            /**
             * Updates hash on checkbox or radio button changes
             * @memberof module:managedrange.FacetManagedRangeView
             * @alias module:managedrange.FacetManagedRangeView#clearFilter
             * 
             */
            textBoxChange: function () {
                var minRange = this.$el.find('.manualRangeMin'),
                    maxRange = this.$el.find('.manualRangeMax'),
                    minValue = minRange.length > 0 && minRange.val() !== "" ? minRange.val() : "",
                    maxValue = maxRange.length > 0 && maxRange.val() !== "" ? maxRange.val() : "",
                    sig = this.model.get('sig');

                //Clears all selected radio buttons and checkboxes
                this.$el.find(".facetRadio").removeAttr("checked");
                this.$el.find(".facetCheckbox").removeAttr("checked");

                if (minValue !== "" || maxValue !== "") {
                    //Auto updates hash after text box change when there is no filter button
                    queryModel.updateHash(this.updateSignaturesHash(sig, minValue + "|" + maxValue, {}));
                } else {
                    //Clears all radio buttons and checkbox hash parameters
                    queryModel.updateHash(this.updateSignaturesHash(sig, "", {}));
                }
            },
            /**
             * Updates component
             * @memberof module:managedrange.FacetManagedRangeView
             * @alias module:managedrange.FacetManagedRangeView#clearFilter
             * 
             */
            updateComponent: function (hash) {
                var sig = this.model.get('sig'),
                    facetPart,
                    i;
                for (i = 0; i < sig.length; i++) {
                    facetPart = sig[i].toLowerCase();
                    if (hash.hasOwnProperty(facetPart)) {
                        this.render();
                    } else {
                        this.clearFilter();
                    }
                }
            }
        });
    /**
    * For each search managed range component on a page creates instance of 
    * ["FacetManagedRangeModel"]{@link module:managedrange.FacetManagedRangeModel} and 
    * ["FacetManagedRangeView"]{@link module:managedrange.FacetManagedRangeView} 
    * @memberOf module:managedrange
    * @alias module:managedrange.init
    */
    api.init = function () {
        if ($("body").hasClass("on-page-editor") || initialized) {
            return;
        }

        queryModel = XA.component.search.query;
        apiModel = XA.component.search.ajax;
        urlHelperModel = XA.component.search.url;

        var facetManagedRangesList = $(".facet-managed-range");
        _.each(facetManagedRangesList, function (elem) {
            var model = new FacetManagedRangeModel(),
                view = new FacetManagedRangeView({ el: $(elem), model: model });
            view.render();
        });

        initialzied = true;
    };

    return api;

}(jQuery, document));

XA.register('managedrange', XA.component.search.facet.managedrange);