<commit_before>/*global DOMPurify, Marionette, moment */

hqDefine("cloudcare/js/formplayer/menus/views/query", function () {
    // 'hqwebapp/js/hq.helpers' is a dependency. It needs to be added
    // explicitly when webapps is migrated to requirejs
    var kissmetrics = hqImport("analytix/js/kissmetrix");
    var FormplayerFrontend = hqImport("cloudcare/js/formplayer/app");
    var separator = " to ",
        dateFormat = "YYYY-MM-DD";
    var selectDelimiter = "#,#"; // Formplayer also uses this
    var Const = hqImport("cloudcare/js/form_entry/const"),
        Utils = hqImport("cloudcare/js/form_entry/utils"),
        initialPageData = hqImport("hqwebapp/js/initial_page_data");

    // special format handled by CaseSearch API
    var encodeValue = function (model, value, searchForBlank) {
            if (value && model.get("input") === "daterange") {
                value = "__range__" + value.replace(separator, "__");
            } else if (model.get('input') === 'select') {
                value = value.join(selectDelimiter);
            }

            var queryProvided = !(value === '' || (_.isArray(value) && _.isEmpty(value)));
            if (searchForBlank && queryProvided) {
                return selectDelimiter + value;
            } else if (queryProvided) {
                return value;
            } else if (searchForBlank) {
                return "";
            }
        },
        decodeValue = function (model, value) {
            if (!_.isString(value)) {
                return [false, undefined];
            }
            var values = value.split(selectDelimiter),
                searchForBlank = _.contains(values, ""),
                values = _.without(values, "");

            if (model.get('input') === 'select') {
                value = values;
            } else if (values.length === 1) {
                value = values[0];
                if (model.get("input") === "daterange") {
                    value = value.replace("__range__", "").replace("__", separator);
                }
            } else {
                value = undefined;
            }
            return [searchForBlank, value];
        },
        geocoderItemCallback = function (addressTopic, model) {
            return function (item) {
                kissmetrics.track.event("Accessibility Tracking - Geocoder Interaction in Case Search");
                model.set('value', item.place_name);
                initMapboxWidget(model);
                var broadcastObj = Utils.getBroadcastObject(item);
                $.publish(addressTopic, broadcastObj);
                return item.place_name;
            };
        },
        geocoderOnClearCallback = function (addressTopic) {
            return function () {
                kissmetrics.track.event("Accessibility Tracking - Geocoder Interaction in Case Search");
                $.publish(addressTopic, Const.NO_ANSWER);
            };
        },
        updateReceiver = function (element) {
            return function (_event, broadcastObj) {
                // e.g. format is home-state, home-zipcode, home-us_state||country
                var receiveExpression = element.data().receive;
                var receiveField = receiveExpression.split("-")[1];
                var value = null;
                if (broadcastObj === undefined || broadcastObj === Const.NO_ANSWER) {
                    value = Const.NO_ANSWER;
                } else if (broadcastObj[receiveField]) {
                    value = broadcastObj[receiveField];
                } else {
                    // match home-us_state||country style
                    var fields = receiveField.split('||');
                    $.each(fields, function (i, field) {
                        if (broadcastObj[field] !== undefined) {
                            value = broadcastObj[field];
                            return false;
                        }
                    });
                }
                // lookup DOM element again, in case the element got rerendered
                var domElement = $('*[data-receive="' + receiveExpression + '"]');
                if (domElement.is('input')) {
                    domElement.val(value);
                    domElement.change();
                }
                else {
                    // Set lookup table option by label
                    var matchingOption = function (el) {
                        return el.find("option").filter(function (_) {
                            return $(this).text().trim() === value;
                        });
                    }
                    var option = matchingOption(domElement);
                    if (domElement[0].multiple === true) {
                        var val = option.val();
                        if (option.length === 1 && domElement.val().indexOf(val) === -1) {
                            domElement.val(
                                domElement.val().concat(val)
                            ).trigger("change");
                        }
                    }
                    else {
                        if (option.length === 1) {
                            domElement.val(String(option.index() - 1)).trigger("change");
                        } else {
                            domElement.val("-1").trigger('change');
                        }
                    }
                }
            };
        },
        initMapboxWidget = function (model) {
            var id = model.get('id'),
                inputId = id + "_mapbox",
                $field = $("#" + inputId);
            $(function () {
                kissmetrics.track.event("Accessibility Tracking - Geocoder Seen in Case Search");
            });
            if ($field.find('.mapboxgl-ctrl-geocoder--input').length === 0) {
                if (!initialPageData.get("has_geocoder_privs")) {
                    $("#" + inputId).addClass('unsupported alert alert-warning');
                    $("#" + inputId).text(gettext(
                        "Sorry, this input is not supported because your project doesn't have a Geocoder privilege")
                    );
                    return true;
                }
                Utils.renderMapboxInput(
                    inputId,
                    geocoderItemCallback(id, model),
                    geocoderOnClearCallback(id),
                    initialPageData
                );
                var divEl = $field.find('.mapboxgl-ctrl-geocoder');
                divEl.css("max-width", "none");
                divEl.css("width", "100%");
            }

            if (model.get('value')) {
                $field.find('.mapboxgl-ctrl-geocoder--input').val(model.get('value'));
            }
        };

    var QueryView = Marionette.View.extend({
        tagName: "tr",
        className: "formplayer-request",
        template: _.template($("#query-view-item-template").html() || ""),

        templateContext: function () {
            var imageUri = this.options.model.get('imageUri'),
                audioUri = this.options.model.get('audioUri'),
                appId = this.model.collection.appId,
                value = this.options.model.get('value');

            return {
                imageUrl: imageUri ? FormplayerFrontend.getChannel().request('resourceMap', imageUri, appId) : "",
                audioUrl: audioUri ? FormplayerFrontend.getChannel().request('resourceMap', audioUri, appId) : "",
                value: value,
                hasError: this.hasError,
            };
        },

        initialize: function () {
            this.parentView = this.options.parentView;
            this.model = this.options.model;
            this.hasError = false;

            var allStickyValues = hqImport("cloudcare/js/formplayer/utils/util").getStickyQueryInputs(),
                stickyValue = allStickyValues[this.model.get('id')],
                [searchForBlank, value] = decodeValue(this.model, stickyValue);
            if (value && !this.model.get('value')) {
                // Set the value and blank search checkbox from the sticky
                // values if available and no default is present
                this.model.set('value', value);
            }
            this.model.set('searchForBlank', searchForBlank);
        },

        ui: {
            valueDropdown: 'select.query-field',
            hqHelp: '.hq-help',
            dateRange: 'input.daterange',
            date: 'input.date',
            queryField: '.query-field',
            searchForBlank: '.search-for-blank',
        },

        events: {
            'dp.change @ui.queryField': 'changeQueryField',
            'change @ui.queryField': 'changeQueryField',
            'click @ui.searchForBlank': 'toggleBlankSearch',
        },

        modelEvents: {
            'change': 'render',
        },

        _isValid: function () {
            if (!this.model.get('required')) {
                return true;
            }

            var answer = this.getEncodedValue();
            return answer !== undefined && (answer === "" || answer.replace(/\s+/, "") !== "");
        },

        isValid: function () {
            var hasError = !this._isValid();
            if (hasError !== this.hasError) {
                this.hasError = hasError;
                this.render();
            }
            return !this.hasError;
        },

        getEncodedValue: function () {
            if (this.model.get('input') === 'address') {
                return;  // skip geocoder address
            }
            var queryValue = $(this.ui.queryField).val(),
                searchForBlank = $(this.ui.searchForBlank).prop('checked');
            return encodeValue(this.model, queryValue, searchForBlank);
        },

        changeQueryField: function (e) {
            if (this.model.get('input') === 'select1' || this.model.get('input') === 'select') {
                this.parentView.changeDropdown(e);
            } else if (this.model.get('input') === 'address') {
                // geocoderItemCallback sets the value on the model
            } else {
                this.model.set('value', $(e.currentTarget).val());
            }
            this.parentView.setStickyQueryInputs();
        },

        toggleBlankSearch: function (e) {
            var self = this,
                searchForBlank = $(e.currentTarget).prop('checked');
            self.model.set('searchForBlank', searchForBlank);

            // When checking the blank search box for a geocoder field, toggle all its receiver fields
            if (self.model.get('input') === 'address') {
                _.each(self.model.collection.models, function (relatedModel) {
                    if (relatedModel.get('receive') && relatedModel.get('receive').split("-")[0] === self.model.get('id')) {
                        relatedModel.set('searchForBlank', searchForBlank);
                    }
                });
                initMapboxWidget(this.model);
            }
            self.parentView.setStickyQueryInputs();
        },

        onRender: function () {
            this.ui.valueDropdown.select2({
                allowClear: true,
                placeholder: " ",   // required for allowClear to work
                escapeMarkup: function (m) { return DOMPurify.sanitize(m); },
            });
            this.ui.hqHelp.hqHelp();
            this.ui.date.datetimepicker({
                format: dateFormat,
            });
            this.ui.dateRange.daterangepicker({
                locale: {
                    format: dateFormat,
                    separator: separator,
                },
                autoUpdateInput: false,
                "autoApply": true,
            });
            this.ui.dateRange.on('cancel.daterangepicker', function () {
                $(this).val('').trigger('change');
            });
            this.ui.dateRange.on('apply.daterangepicker', function(ev, picker) {
                $(this).val(picker.startDate.format(dateFormat) + separator + picker.endDate.format(dateFormat)).trigger('change');
            });
            this.ui.dateRange.on('change', function () {
                // Validate free-text input. Accept anything moment can recognize as a date, reformatting for ES.
                var $input = $(this),
                    oldValue = $input.val(),
                    parts = _.map(oldValue.split(separator), function (v) { return moment(v); }),
                    newValue = '';

                if (_.every(parts, function (part) { return part.isValid(); }))  {
                    if (parts.length === 1) { // condition where only one valid date is typed in rather than a range
                        $input.val(oldValue + separator + oldValue).trigger('change');
                    } else if (parts.length === 2) {
                        newValue = parts[0].format(dateFormat) + separator + parts[1].format(dateFormat);
                    }
                }
                if (oldValue !== newValue) {
                    $input.val(newValue).trigger('change');
                }
            });
            if (this.model.get('hidden') === 'true') {
                this.$el.hide();
            }
        },

    });

    var QueryListView = Marionette.CollectionView.extend({
        tagName: "div",
        template: _.template($("#query-view-list-template").html() || ""),
        childView: QueryView,
        childViewContainer: "tbody",
        childViewOptions: function () { return {parentView: this}; },

        initialize: function (options) {
            this.parentModel = options.collection.models;
        },

        templateContext: function () {
            return {
                title: this.options.title,
            };
        },

        ui: {
            clearButton: '#query-clear-button',
            submitButton: '#query-submit-button',
            valueInput: 'input.query-field',
        },

        events: {
            'click @ui.clearButton': 'clearAction',
            'click @ui.submitButton': 'submitAction',
        },

        getAnswers: function () {
            var answers = {};
            this.children.each(function (childView) {
                var encodedValue = childView.getEncodedValue();
                if (encodedValue !== undefined) {
                    answers[childView.model.get('id')] = encodedValue;
                }
            });
            return answers;
        },

        changeDropdown: function (e) {
            e.preventDefault();
            var self = this;
            var $fields = $(".query-field");

            // If there aren't at least two dropdowns, there are no dependencies
            if ($fields.filter("select").length < 2) {
                return;
            }

            var Util = hqImport("cloudcare/js/formplayer/utils/util");
            var urlObject = Util.currentUrlToObject();
            urlObject.setQueryData(this.getAnswers(), false);
            var fetchingPrompts = FormplayerFrontend.getChannel().request("app:select:menus", urlObject);
            $.when(fetchingPrompts).done(function (response) {
                for (var i = 0; i < response.models.length; i++) {
                    var choices = response.models[i].get('itemsetChoices');
                    if (choices) {
                        var $field = $($fields.get(i)),
                            value = response.models[i].get('value');
                        if ($field.data('select2')) {
                            // force close dropdown, the set below can interfere with this when clearing selection
                            $field.select2('close');
                        }
                        if (value !== null) {
                            value = value.split(selectDelimiter);
                            value = _.filter(value, function (val) { return val !== ''; });
                            if (!$field.attr('multiple')) {
                                value = _.isEmpty(value) ? null : value[0];
                            }
                        }
                        self.collection.models[i].set({
                            itemsetChoices: choices,
                            value: value,
                        });
                        $field.trigger('change.select2');
                    }
                }
            });
        },

        clearAction: function () {
            var self = this;
            _.each(self.collection.models, function (model) {
                model.set('value', '');
                model.set('searchForBlank', false);
                if (model.get('input') === 'address') {
                    initMapboxWidget(model);
                }
            });
            self.setStickyQueryInputs();
        },

        submitAction: function (e) {
            e.preventDefault();
            FormplayerFrontend.trigger('clearNotifications', errorHTML, true);

            var invalidFields = [];
            this.children.each(function (childView) {
                if (!childView.isValid()) {
                    invalidFields.push(childView.model.get('text'));
                }
            });

            if (invalidFields.length) {
                var errorHTML = "Please enter values for the following fields:";
                errorHTML += "
" + _.map(invalidFields, function (f) { return "
" + f + "
"; }).join("") + "
";
                FormplayerFrontend.trigger('showError', errorHTML, true);
                return;
            }

            FormplayerFrontend.trigger("menu:query", this.getAnswers());
        },

        setStickyQueryInputs: function () {
            var Util = hqImport("cloudcare/js/formplayer/utils/util");
            Util.setStickyQueryInputs(this.getAnswers());
        },

        onAttach: function () {
            this.initGeocoders();
        },

        initGeocoders: function () {
            var self = this;
            _.each(self.collection.models, function (model, i) {
                var $field = $($(".query-field")[i]);

                // Set geocoder receivers to subscribe
                if (model.get('receive')) {
                    var topic = model.get('receive').split("-")[0];
                    $.subscribe(topic, updateReceiver($field));
                }

                // Set geocoder address publish
                if (model.get('input') === 'address') {
                    initMapboxWidget(model);
                }
            });
        },

    });

    return function (data) {
        return new QueryListView(data);
    };
});
<commit_msg>Updated dp.change callback in query view<commit_after>/*global DOMPurify, Marionette, moment */

hqDefine("cloudcare/js/formplayer/menus/views/query", function () {
    // 'hqwebapp/js/hq.helpers' is a dependency. It needs to be added
    // explicitly when webapps is migrated to requirejs
    var kissmetrics = hqImport("analytix/js/kissmetrix");
    var FormplayerFrontend = hqImport("cloudcare/js/formplayer/app");
    var separator = " to ",
        dateFormat = "YYYY-MM-DD";
    var selectDelimiter = "#,#"; // Formplayer also uses this
    var Const = hqImport("cloudcare/js/form_entry/const"),
        Utils = hqImport("cloudcare/js/form_entry/utils"),
        initialPageData = hqImport("hqwebapp/js/initial_page_data");

    // special format handled by CaseSearch API
    var encodeValue = function (model, value, searchForBlank) {
            if (value && model.get("input") === "daterange") {
                value = "__range__" + value.replace(separator, "__");
            } else if (model.get('input') === 'select') {
                value = value.join(selectDelimiter);
            }

            var queryProvided = !(value === '' || (_.isArray(value) && _.isEmpty(value)));
            if (searchForBlank && queryProvided) {
                return selectDelimiter + value;
            } else if (queryProvided) {
                return value;
            } else if (searchForBlank) {
                return "";
            }
        },
        decodeValue = function (model, value) {
            if (!_.isString(value)) {
                return [false, undefined];
            }
            var values = value.split(selectDelimiter),
                searchForBlank = _.contains(values, ""),
                values = _.without(values, "");

            if (model.get('input') === 'select') {
                value = values;
            } else if (values.length === 1) {
                value = values[0];
                if (model.get("input") === "daterange") {
                    value = value.replace("__range__", "").replace("__", separator);
                }
            } else {
                value = undefined;
            }
            return [searchForBlank, value];
        },
        geocoderItemCallback = function (addressTopic, model) {
            return function (item) {
                kissmetrics.track.event("Accessibility Tracking - Geocoder Interaction in Case Search");
                model.set('value', item.place_name);
                initMapboxWidget(model);
                var broadcastObj = Utils.getBroadcastObject(item);
                $.publish(addressTopic, broadcastObj);
                return item.place_name;
            };
        },
        geocoderOnClearCallback = function (addressTopic) {
            return function () {
                kissmetrics.track.event("Accessibility Tracking - Geocoder Interaction in Case Search");
                $.publish(addressTopic, Const.NO_ANSWER);
            };
        },
        updateReceiver = function (element) {
            return function (_event, broadcastObj) {
                // e.g. format is home-state, home-zipcode, home-us_state||country
                var receiveExpression = element.data().receive;
                var receiveField = receiveExpression.split("-")[1];
                var value = null;
                if (broadcastObj === undefined || broadcastObj === Const.NO_ANSWER) {
                    value = Const.NO_ANSWER;
                } else if (broadcastObj[receiveField]) {
                    value = broadcastObj[receiveField];
                } else {
                    // match home-us_state||country style
                    var fields = receiveField.split('||');
                    $.each(fields, function (i, field) {
                        if (broadcastObj[field] !== undefined) {
                            value = broadcastObj[field];
                            return false;
                        }
                    });
                }
                // lookup DOM element again, in case the element got rerendered
                var domElement = $('*[data-receive="' + receiveExpression + '"]');
                if (domElement.is('input')) {
                    domElement.val(value);
                    domElement.change();
                }
                else {
                    // Set lookup table option by label
                    var matchingOption = function (el) {
                        return el.find("option").filter(function (_) {
                            return $(this).text().trim() === value;
                        });
                    }
                    var option = matchingOption(domElement);
                    if (domElement[0].multiple === true) {
                        var val = option.val();
                        if (option.length === 1 && domElement.val().indexOf(val) === -1) {
                            domElement.val(
                                domElement.val().concat(val)
                            ).trigger("change");
                        }
                    }
                    else {
                        if (option.length === 1) {
                            domElement.val(String(option.index() - 1)).trigger("change");
                        } else {
                            domElement.val("-1").trigger('change');
                        }
                    }
                }
            };
        },
        initMapboxWidget = function (model) {
            var id = model.get('id'),
                inputId = id + "_mapbox",
                $field = $("#" + inputId);
            $(function () {
                kissmetrics.track.event("Accessibility Tracking - Geocoder Seen in Case Search");
            });
            if ($field.find('.mapboxgl-ctrl-geocoder--input').length === 0) {
                if (!initialPageData.get("has_geocoder_privs")) {
                    $("#" + inputId).addClass('unsupported alert alert-warning');
                    $("#" + inputId).text(gettext(
                        "Sorry, this input is not supported because your project doesn't have a Geocoder privilege")
                    );
                    return true;
                }
                Utils.renderMapboxInput(
                    inputId,
                    geocoderItemCallback(id, model),
                    geocoderOnClearCallback(id),
                    initialPageData
                );
                var divEl = $field.find('.mapboxgl-ctrl-geocoder');
                divEl.css("max-width", "none");
                divEl.css("width", "100%");
            }

            if (model.get('value')) {
                $field.find('.mapboxgl-ctrl-geocoder--input').val(model.get('value'));
            }
        };

    var QueryView = Marionette.View.extend({
        tagName: "tr",
        className: "formplayer-request",
        template: _.template($("#query-view-item-template").html() || ""),

        templateContext: function () {
            var imageUri = this.options.model.get('imageUri'),
                audioUri = this.options.model.get('audioUri'),
                appId = this.model.collection.appId,
                value = this.options.model.get('value');

            return {
                imageUrl: imageUri ? FormplayerFrontend.getChannel().request('resourceMap', imageUri, appId) : "",
                audioUrl: audioUri ? FormplayerFrontend.getChannel().request('resourceMap', audioUri, appId) : "",
                value: value,
                hasError: this.hasError,
            };
        },

        initialize: function () {
            this.parentView = this.options.parentView;
            this.model = this.options.model;
            this.hasError = false;

            var allStickyValues = hqImport("cloudcare/js/formplayer/utils/util").getStickyQueryInputs(),
                stickyValue = allStickyValues[this.model.get('id')],
                [searchForBlank, value] = decodeValue(this.model, stickyValue);
            if (value && !this.model.get('value')) {
                // Set the value and blank search checkbox from the sticky
                // values if available and no default is present
                this.model.set('value', value);
            }
            this.model.set('searchForBlank', searchForBlank);
        },

        ui: {
            valueDropdown: 'select.query-field',
            hqHelp: '.hq-help',
            dateRange: 'input.daterange',
            date: 'input.date',
            queryField: '.query-field',
            searchForBlank: '.search-for-blank',
        },

        events: {
            'dp.change @ui.queryField': function (e) {
                // Without this, the datepicker popup won't show, because in the datepicker's
                // `show` function, the dp.change event is emitted before the popup is shown.
                // The delay makes sure this code doesn't run until the popup is shown.
                _.delay(function () {
                    changeQueryField(e);
                });
            },
            'change @ui.queryField': 'changeQueryField',
            'click @ui.searchForBlank': 'toggleBlankSearch',
        },

        modelEvents: {
            'change': 'render',
        },

        _isValid: function () {
            if (!this.model.get('required')) {
                return true;
            }

            var answer = this.getEncodedValue();
            return answer !== undefined && (answer === "" || answer.replace(/\s+/, "") !== "");
        },

        isValid: function () {
            var hasError = !this._isValid();
            if (hasError !== this.hasError) {
                this.hasError = hasError;
                this.render();
            }
            return !this.hasError;
        },

        getEncodedValue: function () {
            if (this.model.get('input') === 'address') {
                return;  // skip geocoder address
            }
            var queryValue = $(this.ui.queryField).val(),
                searchForBlank = $(this.ui.searchForBlank).prop('checked');
            return encodeValue(this.model, queryValue, searchForBlank);
        },

        changeQueryField: function (e) {
            if (this.model.get('input') === 'select1' || this.model.get('input') === 'select') {
                this.parentView.changeDropdown(e);
            } else if (this.model.get('input') === 'address') {
                // geocoderItemCallback sets the value on the model
            } else {
                this.model.set('value', $(e.currentTarget).val());
            }
            this.parentView.setStickyQueryInputs();
        },

        toggleBlankSearch: function (e) {
            var self = this,
                searchForBlank = $(e.currentTarget).prop('checked');
            self.model.set('searchForBlank', searchForBlank);

            // When checking the blank search box for a geocoder field, toggle all its receiver fields
            if (self.model.get('input') === 'address') {
                _.each(self.model.collection.models, function (relatedModel) {
                    if (relatedModel.get('receive') && relatedModel.get('receive').split("-")[0] === self.model.get('id')) {
                        relatedModel.set('searchForBlank', searchForBlank);
                    }
                });
                initMapboxWidget(this.model);
            }
            self.parentView.setStickyQueryInputs();
        },

        onRender: function () {
            this.ui.valueDropdown.select2({
                allowClear: true,
                placeholder: " ",   // required for allowClear to work
                escapeMarkup: function (m) { return DOMPurify.sanitize(m); },
            });
            this.ui.hqHelp.hqHelp();
            this.ui.date.datetimepicker({
                format: dateFormat,
            });
            this.ui.dateRange.daterangepicker({
                locale: {
                    format: dateFormat,
                    separator: separator,
                },
                autoUpdateInput: false,
                "autoApply": true,
            });
            this.ui.dateRange.on('cancel.daterangepicker', function () {
                $(this).val('').trigger('change');
            });
            this.ui.dateRange.on('apply.daterangepicker', function(ev, picker) {
                $(this).val(picker.startDate.format(dateFormat) + separator + picker.endDate.format(dateFormat)).trigger('change');
            });
            this.ui.dateRange.on('change', function () {
                // Validate free-text input. Accept anything moment can recognize as a date, reformatting for ES.
                var $input = $(this),
                    oldValue = $input.val(),
                    parts = _.map(oldValue.split(separator), function (v) { return moment(v); }),
                    newValue = '';

                if (_.every(parts, function (part) { return part.isValid(); }))  {
                    if (parts.length === 1) { // condition where only one valid date is typed in rather than a range
                        $input.val(oldValue + separator + oldValue).trigger('change');
                    } else if (parts.length === 2) {
                        newValue = parts[0].format(dateFormat) + separator + parts[1].format(dateFormat);
                    }
                }
                if (oldValue !== newValue) {
                    $input.val(newValue).trigger('change');
                }
            });
            if (this.model.get('hidden') === 'true') {
                this.$el.hide();
            }
        },

    });

    var QueryListView = Marionette.CollectionView.extend({
        tagName: "div",
        template: _.template($("#query-view-list-template").html() || ""),
        childView: QueryView,
        childViewContainer: "tbody",
        childViewOptions: function () { return {parentView: this}; },

        initialize: function (options) {
            this.parentModel = options.collection.models;
        },

        templateContext: function () {
            return {
                title: this.options.title,
            };
        },

        ui: {
            clearButton: '#query-clear-button',
            submitButton: '#query-submit-button',
            valueInput: 'input.query-field',
        },

        events: {
            'click @ui.clearButton': 'clearAction',
            'click @ui.submitButton': 'submitAction',
        },

        getAnswers: function () {
            var answers = {};
            this.children.each(function (childView) {
                var encodedValue = childView.getEncodedValue();
                if (encodedValue !== undefined) {
                    answers[childView.model.get('id')] = encodedValue;
                }
            });
            return answers;
        },

        changeDropdown: function (e) {
            e.preventDefault();
            var self = this;
            var $fields = $(".query-field");

            // If there aren't at least two dropdowns, there are no dependencies
            if ($fields.filter("select").length < 2) {
                return;
            }

            var Util = hqImport("cloudcare/js/formplayer/utils/util");
            var urlObject = Util.currentUrlToObject();
            urlObject.setQueryData(this.getAnswers(), false);
            var fetchingPrompts = FormplayerFrontend.getChannel().request("app:select:menus", urlObject);
            $.when(fetchingPrompts).done(function (response) {
                for (var i = 0; i < response.models.length; i++) {
                    var choices = response.models[i].get('itemsetChoices');
                    if (choices) {
                        var $field = $($fields.get(i)),
                            value = response.models[i].get('value');
                        if ($field.data('select2')) {
                            // force close dropdown, the set below can interfere with this when clearing selection
                            $field.select2('close');
                        }
                        if (value !== null) {
                            value = value.split(selectDelimiter);
                            value = _.filter(value, function (val) { return val !== ''; });
                            if (!$field.attr('multiple')) {
                                value = _.isEmpty(value) ? null : value[0];
                            }
                        }
                        self.collection.models[i].set({
                            itemsetChoices: choices,
                            value: value,
                        });
                        $field.trigger('change.select2');
                    }
                }
            });
        },

        clearAction: function () {
            var self = this;
            _.each(self.collection.models, function (model) {
                model.set('value', '');
                model.set('searchForBlank', false);
                if (model.get('input') === 'address') {
                    initMapboxWidget(model);
                }
            });
            self.setStickyQueryInputs();
        },

        submitAction: function (e) {
            e.preventDefault();
            FormplayerFrontend.trigger('clearNotifications', errorHTML, true);

            var invalidFields = [];
            this.children.each(function (childView) {
                if (!childView.isValid()) {
                    invalidFields.push(childView.model.get('text'));
                }
            });

            if (invalidFields.length) {
                var errorHTML = "Please enter values for the following fields:";
                errorHTML += "
" + _.map(invalidFields, function (f) { return "
" + f + "
"; }).join("") + "
";
                FormplayerFrontend.trigger('showError', errorHTML, true);
                return;
            }

            FormplayerFrontend.trigger("menu:query", this.getAnswers());
        },

        setStickyQueryInputs: function () {
            var Util = hqImport("cloudcare/js/formplayer/utils/util");
            Util.setStickyQueryInputs(this.getAnswers());
        },

        onAttach: function () {
            this.initGeocoders();
        },

        initGeocoders: function () {
            var self = this;
            _.each(self.collection.models, function (model, i) {
                var $field = $($(".query-field")[i]);

                // Set geocoder receivers to subscribe
                if (model.get('receive')) {
                    var topic = model.get('receive').split("-")[0];
                    $.subscribe(topic, updateReceiver($field));
                }

                // Set geocoder address publish
                if (model.get('input') === 'address') {
                    initMapboxWidget(model);
                }
            });
        },

    });

    return function (data) {
        return new QueryListView(data);
    };
});
<|endoftext|>


<commit_before>var HQExportDownloader = function (options) {
    var self = this;
    self.downloadLink = options.dowloadLink || '.export-action-download';
    self.modal = options.modal || "#export-download-status";
    self.loadingIndicator = options.loadingIndicator || ".loading-indicator";
    self.loadedData = options.loadedData || ".loaded-data";

    self.init = function () {
        $(function () {
            $(self.downloadLink).click(function () {
                var $modal = $(self.modal);
                $modal.find(self.loadingIndicator).removeClass('hide');
                $modal.find(self.loadedData).empty();
                $modal.find('.modal-header h3 span').text($(this).data("formname"));

                $.getJSON($(this).data('dlocation'), function (d) {
                    var autoRefresh = '';
                    var pollDownloader = function () {
                        if ($('#ready_'+d.download_id).length == 0)
                        {
                            $.get(d.download_url, function(data) {
                                $modal.find(self.loadedData).html(data);
                            });
                        } else {
                            $modal.find(self.loadingIndicator).addClass('hide');
                            clearInterval(autoRefresh);
                        }
                    };
                    $(self.modal).on('hide', function () {
                        clearInterval(autoRefresh);
                    });
                    autoRefresh = setInterval(pollDownloader, 2000);
                });

            });
        });
    };

//    $('.export-action-download').click(function() {
//        var $modalBody = $("#export-download-status .modal-body");
//        $modalBody.find('.loading-indicator').removeClass('hide');
//        $modalBody.find('.loaded-data').empty();
//        $("#export-download-status .modal-header h3 span").text($(this).data("formname"));
//        $.ajax({
//            type: "GET",
//            dataType: "json",
//            url: $(this).data('dlocation'),
//            success: function(d) {
//                var autoRefresh = '';
//                var pollDownloader = function () {
//                    console.log($('#ready_'+d.download_id).length);
//                    if ($('#ready_'+d.download_id).length == 0)
//                    {
//                        $.get(d.download_url, function(data) {
//                            $modalBody.find('.loaded-data').html(data);
//                        });
//                    } else {
//                        $modalBody.find('.loading-indicator').addClass('hide');
//                        clearInterval(autoRefresh);
//                    }
//                };
//                $("#export-download-status").on('hide', function () {
//                    clearInterval(autoRefresh);
//                });
//
//                autoRefresh = setInterval(pollDownloader, 2000);
//            }
//        });
//    });
};<commit_msg>delete commented out js<commit_after>var HQExportDownloader = function (options) {
    var self = this;
    self.downloadLink = options.dowloadLink || '.export-action-download';
    self.modal = options.modal || "#export-download-status";
    self.loadingIndicator = options.loadingIndicator || ".loading-indicator";
    self.loadedData = options.loadedData || ".loaded-data";

    self.init = function () {
        $(function () {
            $(self.downloadLink).click(function () {
                var $modal = $(self.modal);
                $modal.find(self.loadingIndicator).removeClass('hide');
                $modal.find(self.loadedData).empty();
                $modal.find('.modal-header h3 span').text($(this).data("formname"));

                $.getJSON($(this).data('dlocation'), function (d) {
                    var autoRefresh = '';
                    var pollDownloader = function () {
                        if ($('#ready_'+d.download_id).length == 0)
                        {
                            $.get(d.download_url, function(data) {
                                $modal.find(self.loadedData).html(data);
                            });
                        } else {
                            $modal.find(self.loadingIndicator).addClass('hide');
                            clearInterval(autoRefresh);
                        }
                    };
                    $(self.modal).on('hide', function () {
                        clearInterval(autoRefresh);
                    });
                    autoRefresh = setInterval(pollDownloader, 2000);
                });

            });
        });
    };
};
<|endoftext|>


<commit_before>/* global d3, _, Datamap, STATES_TOPOJSON, DISTRICT_TOPOJSON, BLOCK_TOPOJSON */

function IndieMapController($scope, $compile, $location, $filter, storageService, locationsService, isMobile) {
    var vm = this;

    $scope.$watch(function () {
        return vm.data;
    }, function (newValue, oldValue) {
        if (newValue === oldValue) {
            return;
        }
        if (newValue.data) {
            vm.map.data = getData(newValue);
        }
        vm.map.fills = newValue.fills;
        vm.map.rightLegend = newValue.rightLegend;
        vm.indicator = newValue.slug;
    }, true);

    $scope.$watch(function () {
        return vm.bubbles;
    }, function (newValue, oldValue) {
        if (newValue === oldValue) {
            return;
        }
        vm.mapPluginData.bubbles = newValue;
    }, true);

    $scope.$watch(function () {
        return vm.rightLegend;
    }, function (newValue, oldValue) {
        if (newValue === oldValue) {
            return;
        }
        vm.map.rightLegend = newValue;
    }, true);

    if (Object.keys($location.search()).length === 0) {
        $location.search(storageService.getKey('search'));
    } else {
        storageService.setKey('search', $location.search());
    }

    var location_id = $location.search().location_id;
    vm.type = '';
    vm.mapHeight = 0;
    vm.scalingFactor = 1;

    vm.initTopoJson = function (location_level, location) {
        if (location_level === void(0) || isNaN(location_level) || location_level === -1 || location_level === 4) {
            vm.scope = "ind";
            vm.type = vm.scope + "Topo";
            Datamap.prototype[vm.type] = STATES_TOPOJSON;
        } else if (location_level === 0) {
            vm.scope = location.map_location_name;
            vm.type = vm.scope + "Topo";
            Datamap.prototype[vm.type] = DISTRICT_TOPOJSON;
        } else if (location_level === 1) {
            vm.scope = location.map_location_name;
            vm.type = vm.scope + "Topo";
            Datamap.prototype[vm.type] = BLOCK_TOPOJSON;
        }
        if (Datamap.prototype[vm.type].objects[vm.scope] !== void(0)) {
            if ($location.$$path.indexOf('wasting') !== -1 && location.location_type === 'district') {
                vm.mapHeight = 750;
            } else {
                vm.mapHeight = Datamap.prototype[vm.type].objects[vm.scope].height;
            }
            if (isMobile) {
                // scale maps based on space available on device.
                var headerHeight = $('#map-chart-header').height();
                // take window height and subtract height of the header plus 44px of additional padding / margins
                var availableHeight = window.innerHeight - headerHeight - 44;
                if (availableHeight < vm.mapHeight) {
                    // how much we have downscaled so we can also scale the map itself
                    vm.scalingFactor = availableHeight / vm.mapHeight;
                    // this approximates additional scaling based on the width of the device.
                    // the factor was determined from a few samples and is not perfect.
                    var factor = 1.1;
                    var aspectRatio = window.innerHeight / window.innerWidth;
                    if (aspectRatio > factor) {
                        vm.scalingFactor = vm.scalingFactor / (aspectRatio / factor);
                    }
                    vm.mapHeight = availableHeight;
                }
            }
        }
    };

    vm.getScale = function (options) {
        // apply additional scaling if necessary (used by mobile maps)
        return vm.scalingFactor * options.scale;
    };

    var mapConfiguration = function (location) {

        var location_level = -1;
        if (location.location_type === 'state') location_level = 0;
        else if (location.location_type === 'district') location_level = 1;
        else if (location.location_type === 'block') location_level = 2;
        else location_level = -1;

        vm.initTopoJson(location_level, location);

        vm.map = {
            scope: vm.scope,
            rightLegend: vm.data && vm.data !== void(0) ? vm.data.rightLegend : null,
            label: vm.data && vm.data !== void(0) ? vm.data.label : null,
            data: getData(vm.data),
            fills: vm.data && vm.data !== void(0) ? vm.data.fills : null,
            height: vm.mapHeight,
            geographyConfig: {
                highlightFillColor: '#00f8ff',
                highlightBorderColor: '#000000',
                highlightBorderWidth: 1,
                highlightBorderOpacity: 1,
                popupTemplate: function (geography, data) {
                    return vm.templatePopup({
                        loc: {
                            loc: geography,
                            row: vm.map.data[geography.id],
                        },
                    });
                },
            },
            setProjection: function (element) {
                var div = vm.scope === "ind" ? 3 : 4;
                var options = Datamap.prototype[vm.type].objects[vm.scope];
                var projection = d3.geo.equirectangular()
                    .center(options.center)
                    .scale(vm.getScale(options))
                    .translate([element.offsetWidth / 2, element.offsetHeight / div]);
                var path = d3.geo.path()
                    .projection(projection);

                return {path: path, projection: projection};
            },
        };
        if (isMobile) {
            // this is used to add padding in datamap that we don't want on mobile.
            // can't set to 0 because then it will default to 50
            vm.map.options = {
                legendHeight: 1,
            };
        }

        vm.mapPlugins = {
            bubbles: null,
        };
        vm.mapPluginData = {
            bubbles: [],
        };

        if (vm.map.data && !isMobile) {
            // this chunk of code adds the legend to web maps
            _.extend(vm.mapPlugins, {
                customTable: function () {
                    if (this.options.rightLegend !== null &&
                        d3.select(this.options.element)[0][0].lastChild.className !== 'map-kpi-outer') {
                        var html = [
                            '
',
                            '
',
                            '
' + this.options.label + '
',
                        ];
                        for (var fillKey in this.options.fills) {
                            if (fillKey === 'defaultFill') continue;
                            html.push(
                                '
',
                                '

',
                                '
',
                                '' + fillKey + '',
                                '
',
                                '
'
                            );
                        }
                        html.push('

');

                        var locName = 'National';
                        if (storageService.getKey('selectedLocation') !== void(0)) {
                            locName = storageService.getKey('selectedLocation')['name'];
                        }
                        if (this.options.rightLegend['average'] !== void(0)) {
                            html.push('
');
                            if (this.options.rightLegend['average_format'] === 'number') {
                                html.push('' + locName + ' aggregate (in Month): ' + $filter('indiaNumbers')(this.options.rightLegend['average']));
                            } else {
                                html.push('' + locName + ' aggregate (in Month): ' + d3.format('.2f')(this.options.rightLegend['average']) + '%');
                            }
                            html.push('
',
                                '
',
                                '
',
                                this.options.rightLegend['info'],
                                '
'
                            );
                        } else {
                            html.push(
                                '
',
                                this.options.rightLegend['info'],
                                '
'
                            );
                        }
                        if (this.options.rightLegend.extended_info && this.options.rightLegend.extended_info.length > 0) {
                            html.push('

');
                            window.angular.forEach(this.options.rightLegend.extended_info, function (info) {
                                html.push(
                                    '
' + info.indicator + ' ' + info.value + '
'
                                );
                            });
                            html.push('
');
                        }

                        html.push('
');
                        d3.select(this.options.element).append('div')
                            .attr('class', 'map-kpi-outer')
                            .attr('style', 'position: absolute; top: 15px; left: 0; z-index: -1')
                            .html(html.join(''));
                        var mapHeight = d3.select(this.options.element)[0][0].offsetHeight;
                        var legendHeight = d3.select(this.options.element)[0][0].lastElementChild.offsetHeight;
                        if (mapHeight < legendHeight + 15) {
                            d3.select(this.options.element)[0][0].style.height = legendHeight + 15 + "px";
                        }
                    }
                },
            });
        }
    };

    locationsService.getLocation(location_id).then(function (location) {
        mapConfiguration(location);
    });

    vm.indicator = vm.data && vm.data !== void(0) ? vm.data.slug : null;

    vm.changeIndicator = function (value) {
        window.angular.forEach(vm.data, function (row) {
            if (row.slug === value) {
                vm.map.data = row.data;
                vm.map.fills = row.fills;
                vm.map.rightLegend = row.rightLegend;
            }
        });
        vm.indicator = value;
    };

    var getData = function (data) {
        var mapData = data && data !== void(0) ? data.data : null;
        if (!mapData) {
            return null;
        }
        return mapData;
    };

    vm.getHtmlContent = function (geography) {
        var html = "";
        html += "
";
        html += '<button type="button" class="close" ng-click="$ctrl.closePopup()" ' +
                'aria-label="Close">×</button>';
        html += "
";
        html +="
";
        window.angular.forEach(vm.data.data[geography.id].original_name, function (value) {
            html += '<button class="btn btn-xs btn-default" ng-click="$ctrl.updateMap(\'' + value + '\')">' + value + '</button>';
        });
        html += "
";
        return html;
    };

    vm.closePopup = function () {
        var popup = d3.select("#locPopup");
        popup.classed("hidden", true);
    };

    vm.updateMap = function (geography) {
        if (geography.id !== void(0) && vm.data.data[geography.id] && vm.data.data[geography.id].original_name.length > 1) {
            var html = vm.getHtmlContent(geography);
            var css = 'display: block; left: ' + event.layerX + 'px; top: ' + event.layerY + 'px;';

            var popup = d3.select('#locPopup');
            popup.classed("hidden", false);
            popup.attr('style', css)
                .html(html);

            $compile(popup[0])($scope);
        } else {
            var location = geography.id || geography;
            if (geography.id !== void(0) && vm.data.data[geography.id] && vm.data.data[geography.id].original_name.length === 1) {
                location = vm.data.data[geography.id].original_name[0];
            }
            locationsService.getLocationByNameAndParent(location, location_id).then(function (locations) {
                var location = locations[0];
                if (!location) {
                    return;
                }
                $location.search('location_name', (geography.id || geography));
                $location.search('location_id', location.location_id);
                storageService.setKey('search', $location.search());
            });
        }

    };

}

IndieMapController.$inject = [
    '$scope', '$compile', '$location', '$filter', 'storageService', 'locationsService', 'isMobile',
];

window.angular.module('icdsApp').directive('indieMap', function () {
    return {
        restrict: 'E',
        scope: {
            data: '=?',
            legendTitle: '@?',
            bubbles: '=?',
            templatePopup: '&',
        },
        template: '

<datamap on-click="$ctrl.updateMap" map="$ctrl.map" plugins="$ctrl.mapPlugins" plugin-data="$ctrl.mapPluginData"></datamap>
',
        bindToController: true,
        controller: IndieMapController,
        controllerAs: '$ctrl',
    };
});
<commit_msg>add very hacky centering of map based on a few data points<commit_after>/* global d3, _, Datamap, STATES_TOPOJSON, DISTRICT_TOPOJSON, BLOCK_TOPOJSON */

function IndieMapController($scope, $compile, $location, $filter, storageService, locationsService, isMobile) {
    var vm = this;

    $scope.$watch(function () {
        return vm.data;
    }, function (newValue, oldValue) {
        if (newValue === oldValue) {
            return;
        }
        if (newValue.data) {
            vm.map.data = getData(newValue);
        }
        vm.map.fills = newValue.fills;
        vm.map.rightLegend = newValue.rightLegend;
        vm.indicator = newValue.slug;
    }, true);

    $scope.$watch(function () {
        return vm.bubbles;
    }, function (newValue, oldValue) {
        if (newValue === oldValue) {
            return;
        }
        vm.mapPluginData.bubbles = newValue;
    }, true);

    $scope.$watch(function () {
        return vm.rightLegend;
    }, function (newValue, oldValue) {
        if (newValue === oldValue) {
            return;
        }
        vm.map.rightLegend = newValue;
    }, true);

    if (Object.keys($location.search()).length === 0) {
        $location.search(storageService.getKey('search'));
    } else {
        storageService.setKey('search', $location.search());
    }

    var location_id = $location.search().location_id;
    vm.type = '';
    vm.mapHeight = 0;
    vm.scalingFactor = 1;

    vm.initTopoJson = function (location_level, location) {
        if (location_level === void(0) || isNaN(location_level) || location_level === -1 || location_level === 4) {
            vm.scope = "ind";
            vm.type = vm.scope + "Topo";
            Datamap.prototype[vm.type] = STATES_TOPOJSON;
        } else if (location_level === 0) {
            vm.scope = location.map_location_name;
            vm.type = vm.scope + "Topo";
            Datamap.prototype[vm.type] = DISTRICT_TOPOJSON;
        } else if (location_level === 1) {
            vm.scope = location.map_location_name;
            vm.type = vm.scope + "Topo";
            Datamap.prototype[vm.type] = BLOCK_TOPOJSON;
        }
        if (Datamap.prototype[vm.type].objects[vm.scope] !== void(0)) {
            if ($location.$$path.indexOf('wasting') !== -1 && location.location_type === 'district') {
                vm.mapHeight = 750;
            } else {
                vm.mapHeight = Datamap.prototype[vm.type].objects[vm.scope].height;
            }
            if (isMobile) {
                // scale maps based on space available on device.
                var headerHeight = $('#map-chart-header').height();
                // take window height and subtract height of the header plus 44px of additional padding / margins
                var availableHeight = window.innerHeight - headerHeight - 44;
                if (availableHeight < vm.mapHeight) {
                    // how much we have downscaled so we can also scale the map itself
                    vm.scalingFactor = availableHeight / vm.mapHeight;
                    // this approximates additional scaling based on the width of the device.
                    // the factor was determined from a few samples and is not perfect.
                    var factor = 1.1;
                    var aspectRatio = window.innerHeight / window.innerWidth;
                    if (aspectRatio > factor) {
                        vm.scalingFactor = vm.scalingFactor / (aspectRatio / factor);
                    }
                    vm.mapHeight = availableHeight;
                }
            }
        }
    };

    vm.getCenter = function (options) {
        if (isMobile) {
            // adjust center for mobile maps, because web maps are intentionally offset to leave
            // room for the legend.
            function getLatAdjustmentForScale(scale) {
                // this is a very rough approximation made with a few data points for a first pass
                // basically the further zoomed in we are the less we should adjust
                if (scale < 5000) {
                    return 2;
                } else if (scale < 8000) {
                    return 1.5;
                } else if (scale < 12000) {
                    return 1;
                } else {
                    return .5;
                }
            }
            return [options.center[0] + getLatAdjustmentForScale(options.scale), options.center[1]];
        } else {
            return options.center;
        }
    };

    vm.getScale = function (options) {
        // apply additional scaling if necessary (used by mobile maps)
        return vm.scalingFactor * options.scale;
    };

    var mapConfiguration = function (location) {

        var location_level = -1;
        if (location.location_type === 'state') location_level = 0;
        else if (location.location_type === 'district') location_level = 1;
        else if (location.location_type === 'block') location_level = 2;
        else location_level = -1;

        vm.initTopoJson(location_level, location);

        vm.map = {
            scope: vm.scope,
            rightLegend: vm.data && vm.data !== void(0) ? vm.data.rightLegend : null,
            label: vm.data && vm.data !== void(0) ? vm.data.label : null,
            data: getData(vm.data),
            fills: vm.data && vm.data !== void(0) ? vm.data.fills : null,
            height: vm.mapHeight,
            geographyConfig: {
                highlightFillColor: '#00f8ff',
                highlightBorderColor: '#000000',
                highlightBorderWidth: 1,
                highlightBorderOpacity: 1,
                popupTemplate: function (geography, data) {
                    return vm.templatePopup({
                        loc: {
                            loc: geography,
                            row: vm.map.data[geography.id],
                        },
                    });
                },
            },
            setProjection: function (element) {
                var div = vm.scope === "ind" ? 3 : 4;
                var options = Datamap.prototype[vm.type].objects[vm.scope];
                var projection = d3.geo.equirectangular()
                    .center(vm.getCenter(options))
                    .scale(vm.getScale(options))
                    .translate([element.offsetWidth / 2, element.offsetHeight / div]);
                var path = d3.geo.path()
                    .projection(projection);

                return {path: path, projection: projection};
            },
        };
        if (isMobile) {
            // this is used to add padding in datamap that we don't want on mobile.
            // can't set to 0 because then it will default to 50
            vm.map.options = {
                legendHeight: 1,
            };
        }

        vm.mapPlugins = {
            bubbles: null,
        };
        vm.mapPluginData = {
            bubbles: [],
        };

        if (vm.map.data && !isMobile) {
            // this chunk of code adds the legend to web maps
            _.extend(vm.mapPlugins, {
                customTable: function () {
                    if (this.options.rightLegend !== null &&
                        d3.select(this.options.element)[0][0].lastChild.className !== 'map-kpi-outer') {
                        var html = [
                            '
',
                            '
',
                            '
' + this.options.label + '
',
                        ];
                        for (var fillKey in this.options.fills) {
                            if (fillKey === 'defaultFill') continue;
                            html.push(
                                '
',
                                '

',
                                '
',
                                '' + fillKey + '',
                                '
',
                                '
'
                            );
                        }
                        html.push('

');

                        var locName = 'National';
                        if (storageService.getKey('selectedLocation') !== void(0)) {
                            locName = storageService.getKey('selectedLocation')['name'];
                        }
                        if (this.options.rightLegend['average'] !== void(0)) {
                            html.push('
');
                            if (this.options.rightLegend['average_format'] === 'number') {
                                html.push('' + locName + ' aggregate (in Month): ' + $filter('indiaNumbers')(this.options.rightLegend['average']));
                            } else {
                                html.push('' + locName + ' aggregate (in Month): ' + d3.format('.2f')(this.options.rightLegend['average']) + '%');
                            }
                            html.push('
',
                                '
',
                                '
',
                                this.options.rightLegend['info'],
                                '
'
                            );
                        } else {
                            html.push(
                                '
',
                                this.options.rightLegend['info'],
                                '
'
                            );
                        }
                        if (this.options.rightLegend.extended_info && this.options.rightLegend.extended_info.length > 0) {
                            html.push('

');
                            window.angular.forEach(this.options.rightLegend.extended_info, function (info) {
                                html.push(
                                    '
' + info.indicator + ' ' + info.value + '
'
                                );
                            });
                            html.push('
');
                        }

                        html.push('
');
                        d3.select(this.options.element).append('div')
                            .attr('class', 'map-kpi-outer')
                            .attr('style', 'position: absolute; top: 15px; left: 0; z-index: -1')
                            .html(html.join(''));
                        var mapHeight = d3.select(this.options.element)[0][0].offsetHeight;
                        var legendHeight = d3.select(this.options.element)[0][0].lastElementChild.offsetHeight;
                        if (mapHeight < legendHeight + 15) {
                            d3.select(this.options.element)[0][0].style.height = legendHeight + 15 + "px";
                        }
                    }
                },
            });
        }
    };

    locationsService.getLocation(location_id).then(function (location) {
        mapConfiguration(location);
    });

    vm.indicator = vm.data && vm.data !== void(0) ? vm.data.slug : null;

    vm.changeIndicator = function (value) {
        window.angular.forEach(vm.data, function (row) {
            if (row.slug === value) {
                vm.map.data = row.data;
                vm.map.fills = row.fills;
                vm.map.rightLegend = row.rightLegend;
            }
        });
        vm.indicator = value;
    };

    var getData = function (data) {
        var mapData = data && data !== void(0) ? data.data : null;
        if (!mapData) {
            return null;
        }
        return mapData;
    };

    vm.getHtmlContent = function (geography) {
        var html = "";
        html += "
";
        html += '<button type="button" class="close" ng-click="$ctrl.closePopup()" ' +
                'aria-label="Close">×</button>';
        html += "
";
        html +="
";
        window.angular.forEach(vm.data.data[geography.id].original_name, function (value) {
            html += '<button class="btn btn-xs btn-default" ng-click="$ctrl.updateMap(\'' + value + '\')">' + value + '</button>';
        });
        html += "
";
        return html;
    };

    vm.closePopup = function () {
        var popup = d3.select("#locPopup");
        popup.classed("hidden", true);
    };

    vm.updateMap = function (geography) {
        if (geography.id !== void(0) && vm.data.data[geography.id] && vm.data.data[geography.id].original_name.length > 1) {
            var html = vm.getHtmlContent(geography);
            var css = 'display: block; left: ' + event.layerX + 'px; top: ' + event.layerY + 'px;';

            var popup = d3.select('#locPopup');
            popup.classed("hidden", false);
            popup.attr('style', css)
                .html(html);

            $compile(popup[0])($scope);
        } else {
            var location = geography.id || geography;
            if (geography.id !== void(0) && vm.data.data[geography.id] && vm.data.data[geography.id].original_name.length === 1) {
                location = vm.data.data[geography.id].original_name[0];
            }
            locationsService.getLocationByNameAndParent(location, location_id).then(function (locations) {
                var location = locations[0];
                if (!location) {
                    return;
                }
                $location.search('location_name', (geography.id || geography));
                $location.search('location_id', location.location_id);
                storageService.setKey('search', $location.search());
            });
        }

    };

}

IndieMapController.$inject = [
    '$scope', '$compile', '$location', '$filter', 'storageService', 'locationsService', 'isMobile',
];

window.angular.module('icdsApp').directive('indieMap', function () {
    return {
        restrict: 'E',
        scope: {
            data: '=?',
            legendTitle: '@?',
            bubbles: '=?',
            templatePopup: '&',
        },
        template: '

<datamap on-click="$ctrl.updateMap" map="$ctrl.map" plugins="$ctrl.mapPlugins" plugin-data="$ctrl.mapPluginData"></datamap>
',
        bindToController: true,
        controller: IndieMapController,
        controllerAs: '$ctrl',
    };
});
<|endoftext|>


<commit_before>import React from 'react'
import shallowEqual from 'shallowequal'

export const withDomain = ({
  domainClass,
  mapPropsToArg,
  mapPropsToArgsArray = props => mapPropsToArg ? [mapPropsToArg(props)] : [],
  createDomain = props => new domainClass(...mapPropsToArgsArray(props)),  // eslint-disable-line new-cap
  propName = 'domain',
  shouldRecreateDomain = (currentProps, nextProps) =>
    ! shallowEqual(mapPropsToArgsArray(currentProps), mapPropsToArgsArray(nextProps)),
}) =>
  Component =>
    class DomainProvider extends React.Component {

      domain

      componentWillMount() {
        this.domain = createDomain(this.props)
      }

      componentWillUpdate(nextProps) {
        if (shouldRecreateDomain(this.props, nextProps)) {
          this.stop()
          this.domain = createDomain(nextProps)
        }
      }

      componentWillUnmount() {
        this.stop()
        this.domain = null
      }

      stop() {
        if (this.domain && typeof this.domain.stop === 'function') {
          this.domain.stop()
        }
      }

      render() {
        return React.createElement(Component, { ...this.props, [propName]: this.domain })
      }
    }
<commit_msg>Support updating rather than recreate and lifeCycleMethods for domains<commit_after>import React from 'react'
import shallowEqual from 'shallowequal'

export const withDomain = ({
  domainClass,
  mapPropsToArg,
  mapPropsToArgsArray = props => mapPropsToArg ? [mapPropsToArg(props)] : [],
  createDomain = props => new domainClass(...mapPropsToArgsArray(props)),  // eslint-disable-line new-cap
  propName = 'domain',
  shouldRecreateDomain = (currentProps, nextProps) =>
    ! shallowEqual(mapPropsToArgsArray(currentProps), mapPropsToArgsArray(nextProps)),
}) =>
  Component =>
    class DomainProvider extends React.Component {

      domain

      componentWillMount() {
        this.domain = createDomain(this.props)
      }

      componentDidMount() {
        if (this.domain && typeof this.domain.componentDidMount === 'function') {
          this.domain.componentDidMount()
        }
      }

      componentWillUpdate(nextProps) {
        if (shouldRecreateDomain(this.props, nextProps)) {
          this.stop()
          this.domain = createDomain(nextProps)
        } else if (this.domain && typeof this.domain.update === 'function') {
          this.domain.update(nextProps)
        }
      }

      componentWillUnmount() {
        this.stop()
        this.domain = null
      }

      stop() {
        if (this.domain && typeof this.domain.stop === 'function') {
          this.domain.stop()
        }
      }

      render() {
        return React.createElement(Component, { ...this.props, [propName]: this.domain })
      }
    }
<|endoftext|>