(function (context, $) { "use strict"; context.JK = context.JK || {}; context.JK.SelectLocation = Class.extend({ init: function ($countries, $regions, $cities, app, useEasyDropdown) { this.api = context.JK.Rest(); this.logger = context.JK.logger; this.loadingCitiesData = false; this.loadingRegionsData = false; this.loadingCountriesData = false; this.nilOptionStr = ''; this.nilOptionText = 'n/a'; this.countriesLoaded = false; this.$countries = $countries; this.$regions = $regions; this.$cities = $cities; this.$deferred = null; this.useEasyDropdown = useEasyDropdown === undefined ? true : useEasyDropdown; this.app = app; $countries.on('change', function (evt) { evt.stopPropagation(); this.handleCountryChanged(); return false; }.bind(this)); if($regions) { $regions.on('change', function (evt) { evt.stopPropagation(); this.handleRegionChanged(); return false; }.bind(this)); } }, selectCountry: function (country) { if(this.useEasyDropdown) { this.$countries.easyDropDown('select', country, true) } else { this.$countries.val(country) } }, ready: function() { return this.$deferred; }, load: function (country, region, city) { this.country = country; this.region = region; this.city = city; if (!country) { // this case shouldn't happen because sign up makes you pick a location. This is just 'in case', so that the UI is more error-resilient this.logger.debug("user has no specified country: " + country) country = 'US'; } // make the 3 slower requests, which only matter if the user wants to affect their ISP or location this.loadingCountriesData = true; this.$deferred = this.api.getCountries() .done(function (countriesx) { this.populateCountriesx(countriesx["countriesx"], country); }.bind(this)) .fail(this.app.ajaxError) .always(function () { this.loadingCountriesData = false; }.bind(this)) if (country && this.$regions) { this.loadingRegionsData = true; this.api.getRegions({ country: country }) .done(function (regions) { this.populateRegions(regions["regions"], region); }.bind(this)) .fail(this.regionListFailure.bind(this)) .always(function () { this.loadingRegionsData = false; }.bind(this)) if (region && this.$cities) { this.loadingCitiesData = true; this.api.getCities({ country: country, region: region }) .done(function (cities) { this.populateCities(cities["cities"], this.city) }.bind(this)) .fail(this.cityListFailure.bind(this)) .always(function () { this.loadingCitiesData = false; }.bind(this)) } } return this.$deferred; }, handleCountryChanged: function () { var selectedCountry = this.$countries.val() if(!this.$regions) { return; } var selectedRegion = this.$regions.val() var cityElement = this.$cities this.updateRegionList(selectedCountry, this.$regions); this.updateCityList(selectedCountry, null, cityElement); }, handleRegionChanged: function () { var selectedCountry = this.$countries.val() var selectedRegion = this.$regions.val() var cityElement = this.$cities; this.updateCityList(selectedCountry, selectedRegion, cityElement); }, updateRegionList: function (selectedCountry, regionElement) { // only update region if (selectedCountry) { // set city disabled while updating regionElement.attr('disabled', true).easyDropDown('disable'); this.loadingRegionsData = true; regionElement.children().remove() regionElement.append($(this.nilOptionStr).text('loading...')) this.api.getRegions({ country: selectedCountry }) .done(this.getRegionsDone.bind(this)) .error(function (err) { regionElement.children().remove() regionElement.append($(this.nilOptionStr).text(this.nilOptionText)) }.bind(this)) .always(function () { console.log("regions load: this.loadingRegionsData; " + this.loadingRegionsData) this.loadingRegionsData = false; }.bind(this)) } else { regionElement.children().remove() regionElement.append($(this.nilOptionStr).text(this.nilOptionText)) } }, updateCityList: function (selectedCountry, selectedRegion, cityElement) { // only update cities if (selectedCountry && selectedRegion) { // set city disabled while updating cityElement.attr('disabled', true).easyDropDown('disable'); this.loadingCitiesData = true; cityElement.children().remove() cityElement.append($(this.nilOptionStr).text('loading...')) this.api.getCities({ country: selectedCountry, region: selectedRegion }) .done(this.getCitiesDone.bind(this)) .error(function (err) { cityElement.children().remove() cityElement.append($(this.nilOptionStr).text(this.nilOptionText)) }.bind(this)) .always(function () { this.loadingCitiesData = false; }.bind(this)) } else { cityElement.children().remove(); cityElement.append($(this.nilOptionStr).text(this.nilOptionText)); if(this.useEasyDropdown) { context.JK.dropdown(cityElement); } } }, getCitiesDone: function (data) { this.populateCities(data['cities'], this.city); }, getRegionsDone: function (data) { this.populateRegions(data['regions'], this.region); this.updateCityList(this.$countries.val(), this.$regions.val(), this.$cities); }, writeCountry: function (index, countryx) { if (!countryx.countrycode) return; var option = $(this.nilOptionStr); option.text(countryx.countryname ? countryx.countryname : countryx.countrycode); option.attr("value", countryx.countrycode); if (countryx.countrycode == this.country) { this.foundCountry = true; } this.$countries.append(option); }, populateCountriesx: function (countriesx) { this.countriesLoaded = true; // countriesx has the format [{countrycode: "US", countryname: "United States"}, ...] this.foundCountry = false; this.$countries.children().remove(); var nilOption = $(this.nilOptionStr); nilOption.text(this.nilOptionText); this.$countries.append(nilOption); $.each(countriesx, this.writeCountry.bind(this)); if (!this.foundCountry) { this.logger.warn("user has no country in the database. user's country:" + this.country) // in this case, the user has a country that is not in the database // this can happen in a development/test scenario, but let's assume it can // happen in production too. var option = $(this.nilOptionStr); option.text(this.country); option.attr("value", this.country); this.$countries.append(option); } this.$countries.val(this.country); this.$countries.attr("disabled", null).easyDropDown('enable'); if(this.useEasyDropdown) { context.JK.dropdown(this.$countries); } }, writeRegion: function (index, region) { if (!region) return; var option = $(this.nilOptionStr) option.text(region['name']) option.attr("value", region['region']) this.$regions.append(option) }, populateRegions: function (regions, userRegion) { this.$regions.children().remove() var nilOption = $(this.nilOptionStr); nilOption.text(this.nilOptionText); this.$regions.append(nilOption); $.each(regions, this.writeRegion.bind(this)) this.$regions.val(userRegion) this.$regions.attr("disabled", null).easyDropDown('enable'); if(this.useEasyDropdown) { context.JK.dropdown(this.$regions); } }, writeCity: function (index, city) { if (!city) return; var option = $(this.nilOptionStr) option.text(city) option.attr("value", city) this.$cities.append(option) }, populateCities: function (cities, userCity) { this.$cities.children().remove(); var nilOption = $(this.nilOptionStr); nilOption.text(this.nilOptionText); this.$cities.append(nilOption); $.each(cities, this.writeCity.bind(this)) this.$cities.val(userCity) this.$cities.attr("disabled", null).easyDropDown('enable'); if(this.useEasyDropdown) { context.JK.dropdown(this.$cities); } }, regionListFailure: function (jqXHR, textStatus, errorThrown) { if (jqXHR.status == 422) { this.logger.debug("no regions found for country: " + this.country); } else { this.app.ajaxError(arguments); } }, cityListFailure: function (jqXHR, textStatus, errorThrown) { if (jqXHR.status == 422) { this.logger.debug("no cities found for country/region: " + this.country + "/" + this.region); } else { this.app.ajaxError(arguments); } } }); })(window, jQuery);