region-updater.js 9.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248
  1. /**
  2. * Copyright © Magento, Inc. All rights reserved.
  3. * See COPYING.txt for license details.
  4. */
  5. define([
  6. 'jquery',
  7. 'mage/template',
  8. 'underscore',
  9. 'jquery/ui',
  10. 'mage/validation'
  11. ], function ($, mageTemplate, _) {
  12. 'use strict';
  13. $.widget('mage.regionUpdater', {
  14. options: {
  15. regionTemplate:
  16. '<option value="<%- data.value %>" <% if (data.isSelected) { %>selected="selected"<% } %>>' +
  17. '<%- data.title %>' +
  18. '</option>',
  19. isRegionRequired: true,
  20. isZipRequired: true,
  21. isCountryRequired: true,
  22. currentRegion: null,
  23. isMultipleCountriesAllowed: true
  24. },
  25. /**
  26. *
  27. * @private
  28. */
  29. _create: function () {
  30. this._initCountryElement();
  31. this.currentRegionOption = this.options.currentRegion;
  32. this.regionTmpl = mageTemplate(this.options.regionTemplate);
  33. this._updateRegion(this.element.find('option:selected').val());
  34. $(this.options.regionListId).on('change', $.proxy(function (e) {
  35. this.setOption = false;
  36. this.currentRegionOption = $(e.target).val();
  37. }, this));
  38. $(this.options.regionInputId).on('focusout', $.proxy(function () {
  39. this.setOption = true;
  40. }, this));
  41. },
  42. /**
  43. *
  44. * @private
  45. */
  46. _initCountryElement: function () {
  47. if (this.options.isMultipleCountriesAllowed) {
  48. this.element.parents('div.field').show();
  49. this.element.on('change', $.proxy(function (e) {
  50. this._updateRegion($(e.target).val());
  51. }, this));
  52. if (this.options.isCountryRequired) {
  53. this.element.addClass('required-entry');
  54. this.element.parents('div.field').addClass('required');
  55. }
  56. } else {
  57. this.element.parents('div.field').hide();
  58. }
  59. },
  60. /**
  61. * Remove options from dropdown list
  62. *
  63. * @param {Object} selectElement - jQuery object for dropdown list
  64. * @private
  65. */
  66. _removeSelectOptions: function (selectElement) {
  67. selectElement.find('option').each(function (index) {
  68. if (index) {
  69. $(this).remove();
  70. }
  71. });
  72. },
  73. /**
  74. * Render dropdown list
  75. * @param {Object} selectElement - jQuery object for dropdown list
  76. * @param {String} key - region code
  77. * @param {Object} value - region object
  78. * @private
  79. */
  80. _renderSelectOption: function (selectElement, key, value) {
  81. selectElement.append($.proxy(function () {
  82. var name = value.name.replace(/[!"#$%&'()*+,.\/:;<=>?@[\\\]^`{|}~]/g, '\\$&'),
  83. tmplData,
  84. tmpl;
  85. if (value.code && $(name).is('span')) {
  86. key = value.code;
  87. value.name = $(name).text();
  88. }
  89. tmplData = {
  90. value: key,
  91. title: value.name,
  92. isSelected: false
  93. };
  94. if (this.options.defaultRegion === key) {
  95. tmplData.isSelected = true;
  96. }
  97. tmpl = this.regionTmpl({
  98. data: tmplData
  99. });
  100. return $(tmpl);
  101. }, this));
  102. },
  103. /**
  104. * Takes clearError callback function as first option
  105. * If no form is passed as option, look up the closest form and call clearError method.
  106. * @private
  107. */
  108. _clearError: function () {
  109. var args = ['clearError', this.options.regionListId, this.options.regionInputId, this.options.postcodeId];
  110. if (this.options.clearError && typeof this.options.clearError === 'function') {
  111. this.options.clearError.call(this);
  112. } else {
  113. if (!this.options.form) {
  114. this.options.form = this.element.closest('form').length ? $(this.element.closest('form')[0]) : null;
  115. }
  116. this.options.form = $(this.options.form);
  117. this.options.form && this.options.form.data('validator') &&
  118. this.options.form.validation.apply(this.options.form, _.compact(args));
  119. // Clean up errors on region & zip fix
  120. $(this.options.regionInputId).removeClass('mage-error').parent().find('[generated]').remove();
  121. $(this.options.regionListId).removeClass('mage-error').parent().find('[generated]').remove();
  122. $(this.options.postcodeId).removeClass('mage-error').parent().find('[generated]').remove();
  123. }
  124. },
  125. /**
  126. * Update dropdown list based on the country selected
  127. *
  128. * @param {String} country - 2 uppercase letter for country code
  129. * @private
  130. */
  131. _updateRegion: function (country) {
  132. // Clear validation error messages
  133. var regionList = $(this.options.regionListId),
  134. regionInput = $(this.options.regionInputId),
  135. postcode = $(this.options.postcodeId),
  136. label = regionList.parent().siblings('label'),
  137. requiredLabel = regionList.parents('div.field');
  138. this._clearError();
  139. this._checkRegionRequired(country);
  140. // Populate state/province dropdown list if available or use input box
  141. if (this.options.regionJson[country]) {
  142. this._removeSelectOptions(regionList);
  143. $.each(this.options.regionJson[country], $.proxy(function (key, value) {
  144. this._renderSelectOption(regionList, key, value);
  145. }, this));
  146. if (this.currentRegionOption) {
  147. regionList.val(this.currentRegionOption);
  148. }
  149. if (this.setOption) {
  150. regionList.find('option').filter(function () {
  151. return this.text === regionInput.val();
  152. }).attr('selected', true);
  153. }
  154. if (this.options.isRegionRequired) {
  155. regionList.addClass('required-entry').removeAttr('disabled');
  156. requiredLabel.addClass('required');
  157. } else {
  158. regionList.removeClass('required-entry validate-select').removeAttr('data-validate');
  159. requiredLabel.removeClass('required');
  160. if (!this.options.optionalRegionAllowed) { //eslint-disable-line max-depth
  161. regionList.attr('disabled', 'disabled');
  162. } else {
  163. regionList.removeAttr('disabled');
  164. }
  165. }
  166. regionList.show();
  167. regionInput.hide();
  168. label.attr('for', regionList.attr('id'));
  169. } else {
  170. this._removeSelectOptions(regionList);
  171. if (this.options.isRegionRequired) {
  172. regionInput.addClass('required-entry').removeAttr('disabled');
  173. requiredLabel.addClass('required');
  174. } else {
  175. if (!this.options.optionalRegionAllowed) { //eslint-disable-line max-depth
  176. regionInput.attr('disabled', 'disabled');
  177. }
  178. requiredLabel.removeClass('required');
  179. regionInput.removeClass('required-entry');
  180. }
  181. regionList.removeClass('required-entry').prop('disabled', 'disabled').hide();
  182. regionInput.show();
  183. label.attr('for', regionInput.attr('id'));
  184. }
  185. // If country is in optionalzip list, make postcode input not required
  186. if (this.options.isZipRequired) {
  187. $.inArray(country, this.options.countriesWithOptionalZip) >= 0 ?
  188. postcode.removeClass('required-entry').closest('.field').removeClass('required') :
  189. postcode.addClass('required-entry').closest('.field').addClass('required');
  190. }
  191. // Add defaultvalue attribute to state/province select element
  192. regionList.attr('defaultvalue', this.options.defaultRegion);
  193. },
  194. /**
  195. * Check if the selected country has a mandatory region selection
  196. *
  197. * @param {String} country - Code of the country - 2 uppercase letter for country code
  198. * @private
  199. */
  200. _checkRegionRequired: function (country) {
  201. var self = this;
  202. this.options.isRegionRequired = false;
  203. $.each(this.options.regionJson.config['regions_required'], function (index, elem) {
  204. if (elem === country) {
  205. self.options.isRegionRequired = true;
  206. }
  207. });
  208. }
  209. });
  210. return $.mage.regionUpdater;
  211. });