theme.js 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612
  1. /**
  2. * Copyright © Magento, Inc. All rights reserved.
  3. * See COPYING.txt for license details.
  4. */
  5. define('globalNavigationScroll', [
  6. 'jquery'
  7. ], function ($) {
  8. 'use strict';
  9. var win = $(window),
  10. subMenuClass = '.submenu',
  11. fixedClassName = '_fixed',
  12. menu = $('.menu-wrapper'),
  13. content = $('.page-wrapper'),
  14. menuItems = $('#nav').children('li'),
  15. winHeight,
  16. menuHeight = menu.height(),
  17. menuScrollMax = 0,
  18. submenuHeight = 0,
  19. contentHeight,
  20. winTop = 0,
  21. winTopLast = 0,
  22. scrollStep = 0,
  23. nextTop = 0;
  24. /**
  25. * Check if menu is fixed
  26. * @returns {Boolean}
  27. */
  28. function isMenuFixed() {
  29. return menuHeight < contentHeight && contentHeight > winHeight;
  30. }
  31. /**
  32. * Check if class exist than add or do nothing
  33. * @param {jQuery} el
  34. * @param {String} $class
  35. */
  36. function checkAddClass(el, $class) {
  37. if (!el.hasClass($class)) {
  38. el.addClass($class);
  39. }
  40. }
  41. /**
  42. * Check if class exist than remove or do nothing
  43. * @param {jQuery} el
  44. * @param {String} $class
  45. */
  46. function checkRemoveClass(el, $class) {
  47. if (el.hasClass($class)) {
  48. el.removeClass($class);
  49. }
  50. }
  51. /**
  52. * Calculate and apply menu position
  53. */
  54. function positionMenu() {
  55. // Spotting positions and heights
  56. winHeight = win.height();
  57. contentHeight = content.height();
  58. winTop = win.scrollTop();
  59. scrollStep = winTop - winTopLast;
  60. if (isMenuFixed()) { // fixed menu cases
  61. checkAddClass(menu, fixedClassName);
  62. if (menuHeight > winHeight) { // smart scroll cases
  63. if (winTop > winTopLast) { //eslint-disable-line max-depth
  64. menuScrollMax = menuHeight - winHeight;
  65. nextTop < menuScrollMax - scrollStep ?
  66. nextTop += scrollStep : nextTop = menuScrollMax;
  67. menu.css('top', -nextTop);
  68. } else if (winTop <= winTopLast) { // scroll up
  69. nextTop > -scrollStep ?
  70. nextTop += scrollStep : nextTop = 0;
  71. menu.css('top', -nextTop);
  72. }
  73. }
  74. } else { // static menu cases
  75. checkRemoveClass(menu, fixedClassName);
  76. }
  77. // Save previous window scrollTop
  78. winTopLast = winTop;
  79. }
  80. positionMenu(); // page start calculation
  81. // Change position on scroll
  82. win.on('scroll', function () {
  83. positionMenu();
  84. });
  85. win.on('resize', function () {
  86. winHeight = win.height();
  87. // Reset position if fixed and out of smart scroll
  88. if (menuHeight < contentHeight && menuHeight <= winHeight) {
  89. menu.removeAttr('style');
  90. menuItems.off();
  91. }
  92. });
  93. // Add event to menuItems to check submenu overlap
  94. menuItems.on('click', function () {
  95. var submenu = $(this).children(subMenuClass),
  96. delta,
  97. logo = $('.logo')[0].offsetHeight;
  98. submenuHeight = submenu.height();
  99. if (submenuHeight > menuHeight && menuHeight + logo > winHeight) {
  100. menu.height(submenuHeight - logo);
  101. delta = -menu.position().top;
  102. window.scrollTo(0, 0);
  103. positionMenu();
  104. window.scrollTo(0, delta);
  105. positionMenu();
  106. menuHeight = submenuHeight;
  107. }
  108. });
  109. });
  110. define('globalNavigation', [
  111. 'jquery',
  112. 'jquery/ui',
  113. 'globalNavigationScroll'
  114. ], function ($) {
  115. 'use strict';
  116. $.widget('mage.globalNavigation', {
  117. options: {
  118. selectors: {
  119. menu: '#nav',
  120. currentItem: '._current',
  121. topLevelItem: '.level-0',
  122. topLevelHref: '> a',
  123. subMenu: '> .submenu',
  124. closeSubmenuBtn: '[data-role="close-submenu"]'
  125. },
  126. overlayTmpl: '<div class="admin__menu-overlay"></div>'
  127. },
  128. /** @inheritdoc */
  129. _create: function () {
  130. var selectors = this.options.selectors;
  131. this.menu = this.element;
  132. this.menuLinks = $(selectors.topLevelHref, selectors.topLevelItem);
  133. this.closeActions = $(selectors.closeSubmenuBtn);
  134. this._initOverlay()
  135. ._bind();
  136. },
  137. /**
  138. * @return {Object}
  139. * @private
  140. */
  141. _initOverlay: function () {
  142. this.overlay = $(this.options.overlayTmpl).appendTo('body').hide(0);
  143. return this;
  144. },
  145. /**
  146. * @private
  147. */
  148. _bind: function () {
  149. var focus = this._focus.bind(this),
  150. open = this._open.bind(this),
  151. blur = this._blur.bind(this),
  152. keyboard = this._keyboard.bind(this);
  153. this.menuLinks
  154. .on('focus', focus)
  155. .on('click', open);
  156. this.menuLinks.last().on('blur', blur);
  157. this.closeActions.on('keydown', keyboard);
  158. },
  159. /**
  160. * Remove active class from current menu item
  161. * Turn back active class to current page menu item
  162. */
  163. _blur: function (e) {
  164. var selectors = this.options.selectors,
  165. menuItem = $(e.target).closest(selectors.topLevelItem),
  166. currentItem = $(selectors.menu).find(selectors.currentItem);
  167. menuItem.removeClass('_active');
  168. currentItem.addClass('_active');
  169. },
  170. /**
  171. * Add focus to active menu item
  172. */
  173. _keyboard: function (e) {
  174. var selectors = this.options.selectors,
  175. menuItem = $(e.target).closest(selectors.topLevelItem);
  176. if (e.which === 13) {
  177. this._close(e);
  178. $(selectors.topLevelHref, menuItem).focus();
  179. }
  180. },
  181. /**
  182. * Toggle active state on focus
  183. */
  184. _focus: function (e) {
  185. var selectors = this.options.selectors,
  186. menuItem = $(e.target).closest(selectors.topLevelItem);
  187. menuItem.addClass('_active')
  188. .siblings(selectors.topLevelItem)
  189. .removeClass('_active');
  190. },
  191. /**
  192. * @param {jQuery.Event} e
  193. * @private
  194. */
  195. _closeSubmenu: function (e) {
  196. var selectors = this.options.selectors,
  197. currentItem = $(selectors.menu).find(selectors.currentItem);
  198. this._close(e);
  199. currentItem.addClass('_active');
  200. },
  201. /**
  202. * @param {jQuery.Event} e
  203. * @private
  204. */
  205. _open: function (e) {
  206. var selectors = this.options.selectors,
  207. menuItemSelector = selectors.topLevelItem,
  208. menuItem = $(e.target).closest(menuItemSelector),
  209. subMenu = $(selectors.subMenu, menuItem),
  210. close = this._closeSubmenu.bind(this),
  211. closeBtn = subMenu.find(selectors.closeSubmenuBtn);
  212. if (subMenu.length) {
  213. e.preventDefault();
  214. }
  215. menuItem.addClass('_show')
  216. .siblings(menuItemSelector)
  217. .removeClass('_show');
  218. subMenu.attr('aria-expanded', 'true');
  219. closeBtn.on('click', close);
  220. this.overlay.show(0).on('click', close);
  221. this.menuLinks.last().off('blur');
  222. },
  223. /**
  224. * @param {jQuery.Event} e
  225. * @private
  226. */
  227. _close: function (e) {
  228. var selectors = this.options.selectors,
  229. menuItem = this.menu.find(selectors.topLevelItem + '._show'),
  230. subMenu = $(selectors.subMenu, menuItem),
  231. closeBtn = subMenu.find(selectors.closeSubmenuBtn),
  232. blur = this._blur.bind(this);
  233. e.preventDefault();
  234. this.overlay.hide(0).off('click');
  235. this.menuLinks.last().on('blur', blur);
  236. closeBtn.off('click');
  237. subMenu.attr('aria-expanded', 'false');
  238. menuItem.removeClass('_show _active');
  239. }
  240. });
  241. return $.mage.globalNavigation;
  242. });
  243. define('globalSearch', [
  244. 'jquery',
  245. 'jquery/ui'
  246. ], function ($) {
  247. 'use strict';
  248. $.widget('mage.globalSearch', {
  249. options: {
  250. field: '.search-global-field',
  251. fieldActiveClass: '_active',
  252. input: '#search-global'
  253. },
  254. /** @inheritdoc */
  255. _create: function () {
  256. this.field = $(this.options.field);
  257. this.input = $(this.options.input);
  258. this._events();
  259. },
  260. /**
  261. * @private
  262. */
  263. _events: function () {
  264. var self = this;
  265. this.input.on('blur.resetGlobalSearchForm', function () {
  266. if (!self.input.val()) {
  267. self.field.removeClass(self.options.fieldActiveClass);
  268. }
  269. });
  270. this.input.on('focus.activateGlobalSearchForm', function () {
  271. self.field.addClass(self.options.fieldActiveClass);
  272. });
  273. }
  274. });
  275. return $.mage.globalSearch;
  276. });
  277. define('modalPopup', [
  278. 'jquery',
  279. 'jquery/ui'
  280. ], function ($) {
  281. 'use strict';
  282. $.widget('mage.modalPopup', {
  283. options: {
  284. popup: '.popup',
  285. btnDismiss: '[data-dismiss="popup"]',
  286. btnHide: '[data-hide="popup"]'
  287. },
  288. /** @inheritdoc */
  289. _create: function () {
  290. this.fade = this.element;
  291. this.popup = $(this.options.popup, this.fade);
  292. this.btnDismiss = $(this.options.btnDismiss, this.popup);
  293. this.btnHide = $(this.options.btnHide, this.popup);
  294. this._events();
  295. },
  296. /**
  297. * @private
  298. */
  299. _events: function () {
  300. var self = this;
  301. this.btnDismiss
  302. .on('click.dismissModalPopup', function () {
  303. self.fade.remove();
  304. });
  305. this.btnHide
  306. .on('click.hideModalPopup', function () {
  307. self.fade.hide();
  308. });
  309. }
  310. });
  311. return $.mage.modalPopup;
  312. });
  313. define('useDefault', [
  314. 'jquery',
  315. 'jquery/ui'
  316. ], function ($) {
  317. 'use strict';
  318. $.widget('mage.useDefault', {
  319. options: {
  320. field: '.field',
  321. useDefault: '.use-default',
  322. checkbox: '.use-default-control',
  323. label: '.use-default-label'
  324. },
  325. /** @inheritdoc */
  326. _create: function () {
  327. this.el = this.element;
  328. this.field = $(this.el).closest(this.options.field);
  329. this.useDefault = $(this.options.useDefault, this.field);
  330. this.checkbox = $(this.options.checkbox, this.useDefault);
  331. this.label = $(this.options.label, this.useDefault);
  332. this.origValue = this.el.attr('data-store-label');
  333. this._events();
  334. },
  335. /**
  336. * @private
  337. */
  338. _events: function () {
  339. var self = this;
  340. this.el.on(
  341. 'change.toggleUseDefaultVisibility keyup.toggleUseDefaultVisibility',
  342. $.proxy(this._toggleUseDefaultVisibility, this)
  343. ).trigger('change.toggleUseDefaultVisibility');
  344. this.checkboxon('change.setOrigValue', function () {
  345. if ($(this).prop('checked')) {
  346. self.el
  347. .val(self.origValue)
  348. .trigger('change.toggleUseDefaultVisibility');
  349. $(this).prop('checked', false);
  350. }
  351. });
  352. },
  353. /**
  354. * @private
  355. */
  356. _toggleUseDefaultVisibility: function () {
  357. var curValue = this.el.val(),
  358. origValue = this.origValue;
  359. this[curValue != origValue ? '_show' : '_hide'](); //eslint-disable-line eqeqeq
  360. },
  361. /**
  362. * @private
  363. */
  364. _show: function () {
  365. this.useDefault.show();
  366. },
  367. /**
  368. * @private
  369. */
  370. _hide: function () {
  371. this.useDefault.hide();
  372. }
  373. });
  374. return $.mage.useDefault;
  375. });
  376. define('loadingPopup', [
  377. 'jquery',
  378. 'jquery/ui'
  379. ], function ($) {
  380. 'use strict';
  381. $.widget('mage.loadingPopup', {
  382. options: {
  383. message: 'Please wait...',
  384. timeout: 5000,
  385. timeoutId: null,
  386. callback: null,
  387. template: null
  388. },
  389. /** @inheritdoc */
  390. _create: function () {
  391. this.template =
  392. '<div class="popup popup-loading">' +
  393. '<div class="popup-inner">' + this.options.message + '</div>' +
  394. '</div>';
  395. this.popup = $(this.template);
  396. this._show();
  397. this._events();
  398. },
  399. /**
  400. * @private
  401. */
  402. _events: function () {
  403. var self = this;
  404. this.element
  405. .on('showLoadingPopup', function () {
  406. self._show();
  407. })
  408. .on('hideLoadingPopup', function () {
  409. self._hide();
  410. });
  411. },
  412. /**
  413. * @private
  414. */
  415. _show: function () {
  416. var options = this.options,
  417. timeout = options.timeout;
  418. $('body').trigger('processStart');
  419. if (timeout) {
  420. options.timeoutId = setTimeout(this._delayedHide.bind(this), timeout);
  421. }
  422. },
  423. /**
  424. * @private
  425. */
  426. _hide: function () {
  427. $('body').trigger('processStop');
  428. },
  429. /**
  430. * @private
  431. */
  432. _delayedHide: function () {
  433. this._hide();
  434. this.options.callback && this.options.callback();
  435. this.options.timeoutId && clearTimeout(this.options.timeoutId);
  436. }
  437. });
  438. return $.mage.loadingPopup;
  439. });
  440. define('collapsable', [
  441. 'jquery',
  442. 'jquery/ui',
  443. 'jquery/jquery.tabs'
  444. ], function ($) {
  445. 'use strict';
  446. $.widget('mage.collapsable', {
  447. options: {
  448. parent: null,
  449. openedClass: 'opened',
  450. wrapper: '.fieldset-wrapper'
  451. },
  452. /** @inheritdoc */
  453. _create: function () {
  454. this._events();
  455. },
  456. /** @inheritdoc */
  457. _events: function () {
  458. var self = this;
  459. this.element
  460. .on('show', function (e) {
  461. var fieldsetWrapper = $(this).closest(self.options.wrapper);
  462. fieldsetWrapper.addClass(self.options.openedClass);
  463. e.stopPropagation();
  464. })
  465. .on('hide', function (e) {
  466. var fieldsetWrapper = $(this).closest(self.options.wrapper);
  467. fieldsetWrapper.removeClass(self.options.openedClass);
  468. e.stopPropagation();
  469. });
  470. }
  471. });
  472. return $.mage.collapsable;
  473. });
  474. define('js/theme', [
  475. 'jquery',
  476. 'mage/smart-keyboard-handler',
  477. 'mage/ie-class-fixer',
  478. 'collapsable',
  479. 'domReady!'
  480. ], function ($, keyboardHandler) {
  481. 'use strict';
  482. /* @TODO refactor collapsible as widget and avoid logic binding with such a general selectors */
  483. $('.collapse').collapsable();
  484. $.each($('.entry-edit'), function (i, entry) {
  485. $('.collapse:first', entry).filter(function () {
  486. return $(this).data('collapsed') !== true;
  487. }).collapse('show');
  488. });
  489. keyboardHandler.apply();
  490. });