loader.js 6.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237
  1. /**
  2. * Copyright © Magento, Inc. All rights reserved.
  3. * See COPYING.txt for license details.
  4. */
  5. define([
  6. 'jquery',
  7. 'mage/template',
  8. 'jquery/ui',
  9. 'mage/translate'
  10. ], function ($, mageTemplate) {
  11. 'use strict';
  12. $.widget('mage.loader', {
  13. loaderStarted: 0,
  14. options: {
  15. icon: '',
  16. texts: {
  17. loaderText: $.mage.__('Please wait...'),
  18. imgAlt: $.mage.__('Loading...')
  19. },
  20. template:
  21. '<div class="loading-mask" data-role="loader">' +
  22. '<div class="loader">' +
  23. '<img alt="<%- data.texts.imgAlt %>" src="<%- data.icon %>">' +
  24. '<p><%- data.texts.loaderText %></p>' +
  25. '</div>' +
  26. '</div>'
  27. },
  28. /**
  29. * Loader creation
  30. * @protected
  31. */
  32. _create: function () {
  33. this._bind();
  34. },
  35. /**
  36. * Bind on ajax events
  37. * @protected
  38. */
  39. _bind: function () {
  40. this._on({
  41. 'processStop': 'hide',
  42. 'processStart': 'show',
  43. 'show.loader': 'show',
  44. 'hide.loader': 'hide',
  45. 'contentUpdated.loader': '_contentUpdated'
  46. });
  47. },
  48. /**
  49. * Verify loader present after content updated
  50. *
  51. * This will be cleaned up by the task MAGETWO-11070
  52. *
  53. * @param {EventObject} e
  54. * @private
  55. */
  56. _contentUpdated: function (e) {
  57. this.show(e);
  58. },
  59. /**
  60. * Show loader
  61. */
  62. show: function (e, ctx) {
  63. this._render();
  64. this.loaderStarted++;
  65. this.spinner.show();
  66. if (ctx) {
  67. this.spinner
  68. .css({
  69. width: ctx.outerWidth(),
  70. height: ctx.outerHeight(),
  71. position: 'absolute'
  72. })
  73. .position({
  74. my: 'top left',
  75. at: 'top left',
  76. of: ctx
  77. });
  78. }
  79. return false;
  80. },
  81. /**
  82. * Hide loader
  83. */
  84. hide: function () {
  85. if (this.loaderStarted > 0) {
  86. this.loaderStarted--;
  87. if (this.loaderStarted === 0) {
  88. this.spinner.hide();
  89. }
  90. }
  91. return false;
  92. },
  93. /**
  94. * Render loader
  95. * @protected
  96. */
  97. _render: function () {
  98. var html;
  99. if (!this.spinnerTemplate) {
  100. this.spinnerTemplate = mageTemplate(this.options.template);
  101. html = $(this.spinnerTemplate({
  102. data: this.options
  103. }));
  104. html.prependTo(this.element);
  105. this.spinner = html;
  106. }
  107. },
  108. /**
  109. * Destroy loader
  110. */
  111. _destroy: function () {
  112. this.spinner.remove();
  113. }
  114. });
  115. /**
  116. * This widget takes care of registering the needed loader listeners on the body
  117. */
  118. $.widget('mage.loaderAjax', {
  119. options: {
  120. defaultContainer: '[data-container=body]',
  121. loadingClass: 'ajax-loading'
  122. },
  123. /**
  124. * @private
  125. */
  126. _create: function () {
  127. this._bind();
  128. // There should only be one instance of this widget, and it should be attached
  129. // to the body only. Having it on the page twice will trigger multiple processStarts.
  130. if (window.console && !this.element.is(this.options.defaultContainer) && $.mage.isDevMode(undefined)) {
  131. console.warn('This widget is intended to be attached to the body, not below.');
  132. }
  133. },
  134. /**
  135. * @private
  136. */
  137. _bind: function () {
  138. $(document).on({
  139. 'ajaxSend': this._onAjaxSend.bind(this),
  140. 'ajaxComplete': this._onAjaxComplete.bind(this)
  141. });
  142. },
  143. /**
  144. * @param {Object} loaderContext
  145. * @return {*}
  146. * @private
  147. */
  148. _getJqueryObj: function (loaderContext) {
  149. var ctx;
  150. // Check to see if context is jQuery object or not.
  151. if (loaderContext) {
  152. if (loaderContext.jquery) {
  153. ctx = loaderContext;
  154. } else {
  155. ctx = $(loaderContext);
  156. }
  157. } else {
  158. ctx = $('[data-container="body"]');
  159. }
  160. return ctx;
  161. },
  162. /**
  163. * @param {jQuery.Event} e
  164. * @param {Object} jqxhr
  165. * @param {Object} settings
  166. * @private
  167. */
  168. _onAjaxSend: function (e, jqxhr, settings) {
  169. var ctx;
  170. $(this.options.defaultContainer)
  171. .addClass(this.options.loadingClass)
  172. .attr({
  173. 'aria-busy': true
  174. });
  175. if (settings && settings.showLoader) {
  176. ctx = this._getJqueryObj(settings.loaderContext);
  177. ctx.trigger('processStart');
  178. // Check to make sure the loader is there on the page if not report it on the console.
  179. // NOTE that this check should be removed before going live. It is just an aid to help
  180. // in finding the uses of the loader that maybe broken.
  181. if (window.console && !ctx.parents('[data-role="loader"]').length) {
  182. console.warn('Expected to start loader but did not find one in the dom');
  183. }
  184. }
  185. },
  186. /**
  187. * @param {jQuery.Event} e
  188. * @param {Object} jqxhr
  189. * @param {Object} settings
  190. * @private
  191. */
  192. _onAjaxComplete: function (e, jqxhr, settings) {
  193. $(this.options.defaultContainer)
  194. .removeClass(this.options.loadingClass)
  195. .attr('aria-busy', false);
  196. if (settings && settings.showLoader) {
  197. this._getJqueryObj(settings.loaderContext).trigger('processStop');
  198. }
  199. }
  200. });
  201. return {
  202. loader: $.mage.loader,
  203. loaderAjax: $.mage.loaderAjax
  204. };
  205. });