Вы не можете выбрать более 25 тем Темы должны начинаться с буквы или цифры, могут содержать дефисы(-) и должны содержать не более 35 символов.
 
 
 
 

462 строки
13 KiB

  1. import $ from 'jquery';
  2. require('jquery.easing');
  3. import {StudioPreviewUtil} from '../../_global/scripts/utils/StudioPreviewUtil';
  4. class IHKHeader {
  5. constructor(header) {
  6. this.header = header.addClass('initiated');
  7. this.window = $(window);
  8. this.body = $('body');
  9. this.overlayOpen = false;
  10. this.showOpenedNav = this.header.find('.primary').data("show-opened-menu");
  11. const t = this;
  12. this.initLogo();
  13. this.initTopLink();
  14. this.initScroll();
  15. this.initSearch();
  16. this.initPrimaryNav();
  17. this.initSecondaryNav();
  18. this.initToggling();
  19. this.initLanguage();
  20. if (this.showOpenedNav) {
  21. t.header.find('.toggle-nav').click();
  22. }
  23. this.body.on('open-navigation', function (event, data) {
  24. if (data.openMenu) {
  25. t.header.find('.toggle-nav').click();
  26. }
  27. t.initPrimaryNav(data.rootUrl);
  28. });
  29. }
  30. initLogo() {
  31. const t = this;
  32. const logo = t.header.find('.logo');
  33. const img = logo.find('img');
  34. const overlay = $('<span class="logo-overlay" />').appendTo(logo);
  35. const logoObserver = new ResizeObserver(() => {
  36. if (img.height() < 44) {
  37. overlay.css('left', img.height() * 2.1);
  38. } else {
  39. overlay.removeAttr('style');
  40. }
  41. })
  42. logoObserver.observe(img.get(0));
  43. };
  44. initTopLink() {
  45. const t = this;
  46. const toplink = $('<a href="#" class="toplink" tabindex="-1" />').text('Top').prependTo(t.header);
  47. toplink.on('click', function (e) {
  48. e.preventDefault();
  49. $('html, body').animate({'scrollTop': 0}, Math.round($(window).scrollTop() / 12 + 300), 'easeOutQuad');
  50. })
  51. }
  52. initSearch() {
  53. const t = this;
  54. const formNav = $('<div class="form-nav" />').appendTo(t.header.find('.search form'));
  55. t.header.find('nav .secondary').clone().appendTo(formNav);
  56. t.header.find('nav .meta').clone().appendTo(formNav);
  57. t.header.find('.open-search, .close-search').on('click', function (e) {
  58. t.header.toggleClass('search-open').removeClass('nav-open');
  59. if (t.header.hasClass('search-open')) {
  60. setTimeout(function () {
  61. t.header.find('.search-field').focus();
  62. }, 200)
  63. }
  64. t.toggleContentScroll();
  65. })
  66. };
  67. initScroll() {
  68. const t = this;
  69. // State Machine für Scroll-Verhalten
  70. t.scrollState = {
  71. current: 'top', // 'top', 'scrolling-down', 'scrolled', 'scrolling-up'
  72. lastPosition: 0,
  73. transitioning: false
  74. };
  75. window.addEventListener("scroll", function () {
  76. if (t.scrollState.transitioning) return;
  77. window.requestAnimationFrame(function () {
  78. t.checkScrollWithStateMachine();
  79. });
  80. }, {passive: true});
  81. }
  82. checkScrollWithStateMachine() {
  83. const t = this;
  84. const st = this.window.scrollTop();
  85. const state = t.scrollState;
  86. if (t.overlayOpen || $('.gallery-popup.open').length) {
  87. return false;
  88. }
  89. const delta = st - state.lastPosition;
  90. const isScrollingDown = delta > 0;
  91. const isScrollingUp = delta < 0;
  92. switch (state.current) {
  93. case 'top':
  94. if (st > 100 && isScrollingDown) {
  95. t.transitionTo('scrolled');
  96. }
  97. break;
  98. case 'scrolled':
  99. if (st < 30 && isScrollingUp) {
  100. t.transitionTo('top');
  101. }
  102. break;
  103. }
  104. state.lastPosition = st;
  105. // Toplink logic
  106. if (st > this.window.height() * 1.2) {
  107. this.header.addClass('show-toplink');
  108. } else {
  109. this.header.removeClass('show-toplink');
  110. }
  111. }
  112. transitionTo(newState) {
  113. const t = this;
  114. t.scrollState.transitioning = true;
  115. if (newState === 'scrolled') {
  116. this.header.addClass('scrolled');
  117. this.body.addClass('header-scrolled');
  118. } else if (newState === 'top') {
  119. this.header.removeClass('scrolled');
  120. this.body.removeClass('header-scrolled');
  121. }
  122. t.scrollState.current = newState;
  123. // Transition Lock
  124. setTimeout(() => {
  125. t.scrollState.transitioning = false;
  126. }, 150);
  127. }
  128. initToggling() {
  129. this.header.find('.overlay-holder').on('click touch', this.toggleNavigation.bind(this));
  130. this.header.find('.toggle-nav').on('click touch', this.toggleNavigation.bind(this));
  131. };
  132. toggleNavigation() {
  133. const t = this;
  134. if (StudioPreviewUtil && StudioPreviewUtil.isStudioPreview()) {
  135. $("html").stop().animate({'scrollTop': 0}, 300, 'swing', function () {
  136. t.header.toggleClass('nav-open');
  137. t.toggleContentScroll();
  138. });
  139. } else {
  140. t.header.toggleClass('nav-open');
  141. t.toggleContentScroll();
  142. }
  143. };
  144. initPrimaryNav(optionalRootUrl) {
  145. const t = this;
  146. const primary = t.header.find('.primary');
  147. $(primary).empty();
  148. t.baseUrl = primary.attr('data-base-url');
  149. t.rootUrl = primary.attr('data-root-url');
  150. if (optionalRootUrl) {
  151. t.rootUrl = optionalRootUrl;
  152. primary.off('click', 'a');
  153. }
  154. primary.append($('<ul class="current" />').attr('data-json', t.baseUrl + t.rootUrl));
  155. primary.on('click', 'a', function (e) {
  156. const a = $(this);
  157. const li = a.parent();
  158. const ul = li.parent('ul');
  159. if (li.hasClass('deep')) {
  160. e.preventDefault();
  161. if (a.siblings('ul').length) {
  162. ul.removeClass('current');
  163. t.toTop(ul);
  164. a.siblings('ul').addClass('current');
  165. li.addClass('open');
  166. t.manageTabIndex();
  167. } else {
  168. const deepUl = $('<ul />').attr('data-json', a.attr('href')).appendTo(li);
  169. t.loadNav(deepUl);
  170. }
  171. } else if (li.hasClass('back')) {
  172. e.preventDefault();
  173. t.toTop(ul);
  174. if (ul.closest('li').length) {
  175. ul.removeClass('current')
  176. .closest('li').removeClass('open')
  177. .parent('ul').addClass('current');
  178. t.manageTabIndex();
  179. } else {
  180. const backUl = $('<ul />').attr('data-json', a.attr('href')).insertBefore(ul);
  181. t.loadNav(backUl);
  182. }
  183. }
  184. });
  185. t.loadNav(t.header.find('.primary ul'));
  186. }
  187. openMenuByUrlParam(anchor, t) {
  188. const li = anchor.parent();
  189. const ul = li.parent('ul');
  190. if (li.hasClass('deep')) {
  191. if (anchor.siblings('ul').length) {
  192. ul.removeClass('current');
  193. t.toTop(ul);
  194. anchor.siblings('ul').addClass('current');
  195. li.addClass('open');
  196. t.manageTabIndex();
  197. } else {
  198. const deepUl = $('<ul />').attr('data-json', anchor.attr('href')).appendTo(li);
  199. t.loadNav(deepUl);
  200. }
  201. }
  202. }
  203. initSecondaryNav() {
  204. // secondary-nav-item
  205. const t = this;
  206. const $secondary = t.header.find("nav").find(".secondary");
  207. const $secondaryItems = $secondary.find(".secondary-nav-item");
  208. $.each($secondaryItems, function (index, element) {
  209. $(element).on("click", function (e) {
  210. if ($(element).data("is-channel")) {
  211. e.preventDefault();
  212. const elementRootUrl = $(this).data("root-url");
  213. t.initPrimaryNav(elementRootUrl);
  214. } else {
  215. return true;
  216. }
  217. });
  218. });
  219. }
  220. loadNav(ul) {
  221. const t = this;
  222. const currentPageId = t.header.find('.primary').data("page-content-id");
  223. if (ul.parent('li').length) {
  224. ul.addClass('current');
  225. setTimeout(function () {
  226. const parentUl = ul.addClass('loading').parent('li').addClass('open')
  227. .closest('.current').removeClass('current');
  228. t.toTop(parentUl);
  229. }, 20)
  230. }
  231. $.ajax({
  232. url: ul.attr('data-json'),
  233. type: "GET",
  234. dataType: "json",
  235. xhrFields: {withCredentials: true}
  236. }).done(function (data) {
  237. ul.attr('data-cm-metadata', '[{"_":{"$Ref":"content/' + data.tocListId + '"}}]');
  238. if (data.parentId) {
  239. $('<li class="back" />').appendTo(ul)
  240. .append($('<a />').attr('href', t.baseUrl + data.parentId).text(data.title));
  241. }
  242. if (!data.hideOverview) {
  243. if (data.showOverview) {
  244. const overviewElement = $('<li class="overview" />');
  245. overviewElement.appendTo(ul)
  246. .append($('<a />').attr('href', data.url).text(window.ihk.translations.overview));
  247. if (data.contentId === currentPageId.toString()) {
  248. overviewElement.find("a").addClass("active");
  249. }
  250. }
  251. }
  252. const parentElement = data;
  253. $.each(data.items, function () {
  254. const itemsBaseUrl = ihk.settings.navigationItemsUrl;
  255. let itemUrl = this.url;
  256. if (itemsBaseUrl) {
  257. itemUrl = itemsBaseUrl + itemUrl;
  258. }
  259. let li = $('<li />').attr('data-id', this.contentId).attr('data-cm-metadata', '[{"_":{"$Ref":"content/' + this.contentId + '"}}]').appendTo(ul),
  260. a = $('<a />').html(this.title).appendTo(li);
  261. switch (this.type) {
  262. case "CMChannel" :
  263. li.addClass('deep');
  264. a.attr('href', t.baseUrl + this.contentId);
  265. break;
  266. case "CMArticle" :
  267. li.addClass('link');
  268. a.attr('href', itemUrl);
  269. break;
  270. default :
  271. li.addClass('miscellaneous');
  272. li.attr('data-type', this.type);
  273. a.attr('href', itemUrl);
  274. }
  275. if (this.linktype && this.linktype === 'external') {
  276. li.addClass('external');
  277. $(li).find("a").attr("target", "_blank")
  278. }
  279. if (this.viewType === 'onlinemagazinstart') {
  280. // todo als link
  281. li.removeClass('deep');
  282. li.addClass('magazine-nav');
  283. $(li).find("a").attr("href", itemUrl);
  284. if (this.titleImage) {
  285. a.text('').append($('<img src="' + this.titleImage + '" alt="' + this.title + '" />'))
  286. }
  287. }
  288. if ((this.viewType === 'themenseite' || this.viewType === 'ihk-wahl-startseite') && (!parentElement.root || this.skipEmptyMenu)) {
  289. li.addClass('overview');
  290. li.removeClass('deep');
  291. a.attr('href', itemUrl);
  292. }
  293. if (this.trackCode && this.trackCode.length > 0) {
  294. $(li).find("a").attr("onmousedown", this.trackCode);
  295. }
  296. if (this.restrictedTo && this.restrictedTo.indexOf('extranet') > -1) {
  297. li.addClass('extranet');
  298. } else if (this.restrictedTo && this.restrictedTo.indexOf('intranet') > -1) {
  299. li.addClass('intranet');
  300. }
  301. if (this.linktype && this.linktype === 'document') {
  302. li.addClass('download');
  303. }
  304. if (this.contentId === currentPageId.toString()) {
  305. $(li).find("a").addClass("active");
  306. }
  307. });
  308. ul.addClass('current').attr('data-id', data.contentId);
  309. if (ul.parent('li').length) {
  310. setTimeout(function () {
  311. const parentUl = ul.removeClass('loading').parent('li').addClass('open')
  312. .closest('.current').removeClass('current');
  313. t.toTop(parentUl);
  314. t.manageTabIndex();
  315. }, 20)
  316. } else if (ul.next('ul').length) {
  317. const innerUl = ul.next('ul');
  318. let wrapperLi = ul.find('li[data-id="' + innerUl.attr('data-id') + '"]').addClass('open');
  319. if (!wrapperLi.length) {
  320. wrapperLi = $('<li />').addClass('open').appendTo(ul);
  321. }
  322. wrapperLi.append(innerUl);
  323. setTimeout(function () {
  324. innerUl.removeClass('current');
  325. wrapperLi.removeClass('open');
  326. t.manageTabIndex();
  327. }, 20)
  328. }
  329. let hash = window.location.hash;
  330. if (hash) {
  331. let menuId = hash.substring(1);
  332. if (!menuId.startsWith('/')) {
  333. let li = ul.find('li[data-id=' + menuId + ']');
  334. if (li.length > 0) {
  335. t.toggleNavigation();
  336. let anchor = li.find('a');
  337. if (anchor.length > 0) {
  338. t.openMenuByUrlParam(anchor, t);
  339. }
  340. }
  341. }
  342. }
  343. })
  344. }
  345. toggleContentScroll() {
  346. const t = this;
  347. if (t.header.hasClass('nav-open') || t.header.hasClass('search-open')) {
  348. t.overlayOpen = true;
  349. this.body.trigger('overlay-open');
  350. t.body.css('top', (t.window.scrollTop() * -1) + 'px').addClass('nav-open');
  351. if (t.header.hasClass('search-open')) {
  352. t.body.addClass('search-open');
  353. }
  354. } else {
  355. t.overlayOpen = false;
  356. this.body.trigger('overlay-close');
  357. const top = Math.abs(parseInt(t.body.css('top')));
  358. t.body.removeClass('nav-open').removeClass('search-open').removeAttr('style');
  359. t.window.scrollTop(top);
  360. }
  361. }
  362. initLanguage() {
  363. const lang = this.header.find('.language');
  364. lang.find('button').on('click', function (e) {
  365. lang.toggleClass('open');
  366. })
  367. lang.on('click', function (e) {
  368. e.stopPropagation();
  369. })
  370. this.body.on('click', function () {
  371. lang.removeClass('open');
  372. })
  373. lang.siblings().find('a').on('focus', function () {
  374. lang.removeClass('open');
  375. })
  376. }
  377. manageTabIndex() {
  378. this.header.find('.primary a').attr('tabindex', '-1');
  379. this.header.find('.primary .current > li > a').removeAttr('tabindex');
  380. }
  381. toTop(ul) {
  382. if (ul.scrollTop() > 0) {
  383. ul.animate({'scrollTop': 0}, 300, 'swing');
  384. }
  385. }
  386. }
  387. export default IHKHeader;
  388. $('body').on('ihk-init', function () {
  389. $('.page-header:not(.initiated)').each((i, el) => {
  390. new IHKHeader($(el));
  391. });
  392. });