validation.js 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310
  1. /**
  2. * Copyright © Magento, Inc. All rights reserved.
  3. * See COPYING.txt for license details.
  4. */
  5. /* global BASE_URL, alertAlreadyDisplayed */
  6. (function (factory) {
  7. 'use strict';
  8. if (typeof define === 'function' && define.amd) {
  9. define([
  10. 'jquery',
  11. 'underscore',
  12. 'Magento_Ui/js/modal/alert',
  13. 'jquery/ui',
  14. 'jquery/validate',
  15. 'mage/translate',
  16. 'mage/validation'
  17. ], factory);
  18. } else {
  19. factory(jQuery);
  20. }
  21. }(function ($, _, alert) {
  22. 'use strict';
  23. $.extend(true, $.validator.prototype, {
  24. /**
  25. * Focus invalid fields
  26. */
  27. focusInvalid: function () {
  28. if (this.settings.focusInvalid) {
  29. try {
  30. $(this.errorList.length && this.errorList[0].element || [])
  31. .focus()
  32. .trigger('focusin');
  33. } catch (e) {
  34. // ignore IE throwing errors when focusing hidden elements
  35. }
  36. }
  37. },
  38. /**
  39. * Elements.
  40. */
  41. elements: function () {
  42. var validator = this,
  43. rulesCache = {};
  44. // select all valid inputs inside the form (no submit or reset buttons)
  45. return $(this.currentForm)
  46. .find('input, select, textarea')
  47. .not(this.settings.forceIgnore)
  48. .not(':submit, :reset, :image, [disabled]')
  49. .not(this.settings.ignore)
  50. .filter(function () {
  51. if (!this.name && validator.settings.debug && window.console) {
  52. console.error('%o has no name assigned', this);
  53. }
  54. // select only the first element for each name, and only those with rules specified
  55. if (this.name in rulesCache || !validator.objectLength($(this).rules())) {
  56. return false;
  57. }
  58. rulesCache[this.name] = true;
  59. return true;
  60. });
  61. }
  62. });
  63. $.extend($.fn, {
  64. /**
  65. * ValidationDelegate overridden for those cases where the form is located in another form,
  66. * to avoid not correct working of validate plug-in
  67. * @override
  68. * @param {String} delegate - selector, if event target matched against this selector,
  69. * then event will be delegated
  70. * @param {String} type - event type
  71. * @param {Function} handler - event handler
  72. * @return {Element}
  73. */
  74. validateDelegate: function (delegate, type, handler) {
  75. return this.on(type, $.proxy(function (event) {
  76. var target = $(event.target),
  77. form = target[0].form;
  78. if (form && $(form).is(this) && $.data(form, 'validator') && target.is(delegate)) {
  79. return handler.apply(target, arguments);
  80. }
  81. }, this));
  82. }
  83. });
  84. $.widget('mage.validation', $.mage.validation, {
  85. options: {
  86. messagesId: 'messages',
  87. forceIgnore: '',
  88. ignore: ':disabled, .ignore-validate, .no-display.template, ' +
  89. ':disabled input, .ignore-validate input, .no-display.template input, ' +
  90. ':disabled select, .ignore-validate select, .no-display.template select, ' +
  91. ':disabled textarea, .ignore-validate textarea, .no-display.template textarea',
  92. errorElement: 'label',
  93. errorUrl: typeof BASE_URL !== 'undefined' ? BASE_URL : null,
  94. /**
  95. * @param {HTMLElement} element
  96. */
  97. highlight: function (element) {
  98. if ($.validator.defaults.highlight && $.isFunction($.validator.defaults.highlight)) {
  99. $.validator.defaults.highlight.apply(this, arguments);
  100. }
  101. $(element).trigger('highlight.validate');
  102. },
  103. /**
  104. * @param {HTMLElement} element
  105. */
  106. unhighlight: function (element) {
  107. if ($.validator.defaults.unhighlight && $.isFunction($.validator.defaults.unhighlight)) {
  108. $.validator.defaults.unhighlight.apply(this, arguments);
  109. }
  110. $(element).trigger('unhighlight.validate');
  111. }
  112. },
  113. /**
  114. * Validation creation
  115. * @protected
  116. */
  117. _create: function () {
  118. if (!this.options.submitHandler && $.type(this.options.submitHandler) !== 'function') {
  119. if (!this.options.frontendOnly && this.options.validationUrl) {
  120. this.options.submitHandler = $.proxy(this._ajaxValidate, this);
  121. } else {
  122. this.options.submitHandler = $.proxy(this._submit, this);
  123. }
  124. }
  125. this.element.on('resetElement', function (e) {
  126. $(e.target).rules('remove');
  127. });
  128. this._super('_create');
  129. },
  130. /**
  131. * ajax validation
  132. * @protected
  133. */
  134. _ajaxValidate: function () {
  135. $.ajax({
  136. url: this.options.validationUrl,
  137. type: 'POST',
  138. dataType: 'json',
  139. data: this.element.serialize(),
  140. context: $('body'),
  141. success: $.proxy(this._onSuccess, this),
  142. error: $.proxy(this._onError, this),
  143. showLoader: true,
  144. dontHide: false
  145. });
  146. },
  147. /**
  148. * Process ajax success.
  149. *
  150. * @protected
  151. * @param {Object} response
  152. */
  153. _onSuccess: function (response) {
  154. if (!response.error) {
  155. this._submit();
  156. } else {
  157. this._showErrors(response);
  158. $(this.element[0]).trigger('afterValidate.error');
  159. $('body').trigger('processStop');
  160. }
  161. },
  162. /**
  163. * Submitting a form.
  164. * @private
  165. */
  166. _submit: function () {
  167. $(this.element[0]).trigger('afterValidate.beforeSubmit');
  168. this.element[0].submit();
  169. },
  170. /**
  171. * Displays errors after backend validation.
  172. *
  173. * @param {Object} data - Data that came from backend.
  174. */
  175. _showErrors: function (data) {
  176. $('body').notification('clear')
  177. .notification('add', {
  178. error: data.error,
  179. message: data.message,
  180. /**
  181. * @param {*} message
  182. */
  183. insertMethod: function (message) {
  184. $('.messages:first').html(message);
  185. }
  186. });
  187. },
  188. /**
  189. * Tries to retrieve element either by id or by inputs' name property.
  190. * @param {String} code - String to search by.
  191. * @returns {jQuery} jQuery element.
  192. */
  193. _getByCode: function (code) {
  194. var parent = this.element[0],
  195. element;
  196. element = parent.querySelector('#' + code) || parent.querySelector('input[name=' + code + ']');
  197. return $(element);
  198. },
  199. /**
  200. * Process ajax error
  201. * @protected
  202. */
  203. _onError: function () {
  204. $(this.element[0]).trigger('afterValidate.error');
  205. $('body').trigger('processStop');
  206. if (this.options.errorUrl) {
  207. location.href = this.options.errorUrl;
  208. }
  209. }
  210. });
  211. _.each({
  212. 'validate-greater-zero-based-on-option': [
  213. function (v, el) {
  214. var optionType = $(el)
  215. .closest('.form-list')
  216. .prev('.fieldset-alt')
  217. .find('select.select-product-option-type'),
  218. optionTypeVal = optionType.val();
  219. v = Number(v) || 0;
  220. if (optionType && (optionTypeVal == 'checkbox' || optionTypeVal == 'multi') && v <= 0) { //eslint-disable-line
  221. return false;
  222. }
  223. return true;
  224. },
  225. $.mage.__('Please enter a number greater 0 in this field.')
  226. ],
  227. 'validate-rating': [
  228. function () {
  229. var ratings = $('#detailed_rating').find('.field-rating'),
  230. noError = true;
  231. ratings.each(function (index, rating) {
  232. noError = noError && $(rating).find('input:checked').length > 0;
  233. });
  234. return noError;
  235. },
  236. $.mage.__('Please select one of each ratings above.')
  237. ],
  238. 'validate-downloadable-file': [
  239. function (v, element) {
  240. var elmParent = $(element).parent(),
  241. linkType = elmParent.find('input[value="file"]'),
  242. newFileContainer;
  243. if (linkType.is(':checked') && (v === '' || v === '[]')) {
  244. newFileContainer = elmParent.find('.new-file');
  245. if (!alertAlreadyDisplayed && (newFileContainer.empty() || newFileContainer.is(':visible'))) {
  246. window.alertAlreadyDisplayed = true;
  247. alert({
  248. content: $.mage.__('There are files that were selected but not uploaded yet. ' +
  249. 'Please upload or remove them first')
  250. });
  251. }
  252. return false;
  253. }
  254. return true;
  255. },
  256. 'Please upload a file.'
  257. ],
  258. 'validate-downloadable-url': [
  259. function (v, element) {
  260. var linkType = $(element).parent().find('input[value="url"]');
  261. if (linkType.is(':checked') && v === '') {
  262. return false;
  263. }
  264. return true;
  265. },
  266. $.mage.__('Please specify Url.')
  267. ]
  268. }, function (rule, i) {
  269. rule.unshift(i);
  270. $.validator.addMethod.apply($.validator, rule);
  271. });
  272. return $.mage.validation;
  273. }));