UNPKG

altinn-designsystem

Version:

Altinn Design system based on Pattern Lab.

729 lines (651 loc) 24.3 kB
/* globals $, Foundation */ var colnavCustom = function() { var levels = ['a-colnav-firstLevel', 'a-colnav-secondLevel', 'a-colnav-thirdLevel']; // Classnames var open = []; // Array to hold open levels var isSmall = $('.a-contentOverview').width() < 900; // Boolean for determining screen size var movedDuringTouch = false; // Boolean to determine whether there was movement during touch var shifted; // Boolean to determine if shift key was pressed var savedResults = {}; // Object to hold saved results var currentData = {}; var pluginInstance; // Variable to hold Foundation plugin instance var endPointUrl = ''; var currentCategory = ''; var menuHandlersAttached = false; var populateSublevel; // We need to declare this func. before defining it because of lint warnings var keys = { category: 'category', checked: ':checked', colnavSelector: '.a-colnav', colnavItemSelector: '.a-colnav-item', dataId: 'data-id', dataIndex: 'data-index', disabled: 'disabled', colnavWrapper: '.a-colnav-wrapper', dataLevel: 'data-level', loaderClass: '.a-js-drilldownLoader', radioClassSelector: '.radio', switchurl: 'switchurl', toggleInput: '[name="js-switchForm"]' }; var elements = { }; var category = { category: 'category', provider: 'provider' }; function time(label) { // Comment this out before committing, used to measure performance in dev // console.time(label); } function timeEnd(label) { // Comment this out before committing, used to measure performance in dev // console.timeEnd(label); } function createElement(name) { return document.createElement(name); } function hideLoader() { $(keys.loaderClass).hide(); } function showLoader() { $(keys.loaderClass).show(); } function maxDepth() { var depth = 3; if (currentCategory === category.provider) { depth = 2; } return depth; } function getListProperty(item) { if (item.SubCategory) { return item.SubCategory; } else if (item.List) { return item.List; } else if (item.SchemaList) { return item.SchemaList; } return null; } function setHistoryState(position) { var urlQueryString = '?category='; var newurl = window.location.pathname; currentCategory = $(keys.toggleInput + keys.checked).data(keys.switchurl).replace('get', ''); if (history.replaceState) { urlQueryString += currentCategory; if (position !== null && position !== '') { urlQueryString = urlQueryString + '&position=' + position; } newurl += urlQueryString; window.history.replaceState({ path: newurl }, '', newurl); } } function stopEvent(e) { if (typeof e.cancelable !== 'boolean' || e.cancelable) { e.preventDefault(); } e.stopPropagation(); e.stopImmediatePropagation(); } function urlQuery(query) { // Parse current URL for query value var _query = query.replace(/[[]/, '[').replace(/[\]]/, '\\]'); var expr = '[\\?&]' + _query + '=([^&#]*)'; var regex = new RegExp(expr); var results = regex.exec(window.location.href); if (results !== null) { return results[1]; } return false; } function createSubMenuForClickedItem($li) { var $item = $li.closest('li'); var level = $item.data('level'); var dataIndex = $item.data('index'); var data = null; var parentDataIndex = null; var parentData = null; if (level === 1) { data = getListProperty(currentData[dataIndex]); } else if (level === 2) { parentDataIndex = $item.parent().closest('li').data('index'); parentData = currentData[parentDataIndex]; data = getListProperty(parentData)[dataIndex]; data = getListProperty(data); } populateSublevel(data, $item.find('ul'), level + 1); } // This code awful. It can return a number or an element (!), // the argument names don't convey any information and there // a few magical numbers. // Should be the first target of a refactoring in the future. // Perform various calculations to determine placements and widths function calc(x, y, z) { var returnValue = ''; var left = null; var contentOverviewWith = $('.a-contentOverview').width(); if (isSmall) { if (isNaN(x)) { returnValue = x.css('left', '50px'); } else { returnValue = ((contentOverviewWith - ((z + 1) * 50)) - (1.5 * (z + 1))) + 'px'; } } else if (isNaN(x)) { left = parseInt(x.css('left'), 10); if ((currentCategory === category.provider && x.hasClass('a-colnav-secondLevel') && left < 250) || (currentCategory === category.category && x.hasClass('a-colnav-thirdLevel') && left < 200)) { returnValue = x; } else { returnValue = x.css('left', parseInt(contentOverviewWith / y / (z || 1), 10) + 'px'); } } else { returnValue = parseInt(contentOverviewWith / x / (y || 1), 10); } return returnValue; } function whenClick(eventOrElement, alt) { // Logic for clicks on items var index = null; var level = null; var wasStacked = null; var str = null; var el = null; var li = null; var text = null; var position = null; if (eventOrElement.which === 2 || eventOrElement.which === 3) { // Middle-click or right-click, stop processing the event // Ref.: http://api.jquery.com/event.which/ return; } if (eventOrElement.target && eventOrElement.target.tagName === 'UL') { return; } if (eventOrElement.stopPropagation) { stopEvent(eventOrElement); } // Determine element el = alt === undefined ? $(eventOrElement.target) : eventOrElement; li = el.closest('li'); if (!li.hasClass('is-dropdown-submenu-parent') && !li.hasClass('is-dropdown-submenu-parent')) { li = el; } if (li.is('h4')) { li = li.parents().eq(1); } // If item holds an actual link, redirect if (li.children('a').prop('href')) { window.location = li.children('a').prop('href'); return; } text = li.find('h2').length > 0 ? li.find('h2').text() : li.find('h3').text(); position = li.find('h2').length > 0 ? li.find('h2').attr(keys.dataId) : li.find('h3').attr(keys.dataId); createSubMenuForClickedItem(li); level = li.data('level'); // Iterate through levels for (index = 0; index < level; index += 1) { // levels.forEach(function(str, index) { str = levels[index]; // var wasStacked; if (el.closest('ul').hasClass(str)) { // Check if element exists // Check if item is already open if (el.closest('a').hasClass('open') || el.find('a').hasClass('open') || el.hasClass('open')) { position = el.closest('ul').prev().find('h2').attr(keys.dataId) || ''; setHistoryState(position); open = []; // Clear array for open levels // Hide lower levels: $('.' + levels[index + 1]).removeClass('noTrans').css('left', '250%'); $('.' + levels[2]).removeClass('noTrans').css('left', '250%'); calc(index > 0 ? el.closest('ul') : 0, 3 / index); // Calculate left position for parent // Reset markup: el.closest('ul').removeClass('stacked').find('.open').removeClass('open'); el.closest('ul').find('.dim').removeClass('dim'); if (isSmall) { el.closest('ul').css('width', calc(1.5, null, index - 1)); } } else { // If item is not open: setHistoryState(position); if (index === 0) { // If on first level, reset markup and hide lower levels el.closest('ul').find('.dim').removeClass('dim'); $('.' + levels[1]).removeClass('stacked').removeClass('noTrans').css('left', '250%'); $('.' + levels[2]).removeClass('stacked').removeClass('noTrans').css('left', '250%'); } wasStacked = el.closest('ul').hasClass('stacked'); // Check if parent was stacked el.closest('ul').children('li').children('a').addClass('dim'); // Dim other items // Stack parent and reset any open items: el.closest('ul').addClass('stacked').find('.open').removeClass('open'); $('.' + levels[index + 1]).hide().addClass('noTrans'); // Adjust markup of lower level // Adjust item markup: el.addClass('open').closest('a').addClass('open'); el.find('a').eq(0).addClass('open'); if (open.indexOf(levels[index + 1]) === -1) { // If lower level is not open // Prepare lower level and add it to array: li.find('.' + levels[index + 1]).removeClass(wasStacked ? '' : 'noTrans') .css('left', '250%').show(); open.push(levels[index + 1]); } if (index > 0) { // If level is second or lower, calculate left position and width calc(el.closest('ul'), 3, index + 1).removeClass('noTrans') .css('width', calc(1.5, null, index)).show(); } // Calculate left position and width for lower level calc(li.find('.' + levels[index + 1]), 3, index + 1).css('width', calc(1.5, null, index)) .show(); } } } // Perform markup adjustments to stacked view: if ($('.a-colnav-firstLevel').hasClass('stacked')) { $('.a-js-backButton').show(); if (isSmall) { $('.switch-container').hide(); $('.a-containerColnav-top').css('padding-bottom', '0px'); $('.a-js-backButton').css('margin-top', '-4px'); $('.a-js-colnavTitleBold').text(''); $('.a-js-colnavTitleRegular').text(text); } // Perform markup adjustments to unstacked view: } else { $('.a-js-backButton').hide(); if (isSmall) { $('.switch-container').show(); $('.a-containerColnav-top').css('padding-bottom', '24px'); $('.a-js-backButton').css('margin-top', '0px'); $('.a-js-colnavTitleBold').text(''); $('.a-js-colnavTitleRegular').text('Alle skjemaer'); } } // Adjust the height of container: $('.a-colnav-firstLevel').css('height', 'auto'); if ( parseInt($('.a-colnav-thirdLevel:visible').height(), 10) > parseInt($('.a-colnav-firstLevel').height(), 10) || parseInt($('.a-colnav-secondLevel:visible').height(), 10) > parseInt($('.a-colnav-firstLevel').height(), 10)) { $('.a-colnav-firstLevel') .css('height', parseInt($('.a-colnav-thirdLevel:visible').height(), 10) > parseInt($('.a-colnav-secondLevel:visible').height(), 10) ? (parseInt($('.a-colnav-thirdLevel:visible').height(), 10) - 2) : (parseInt($('.a-colnav-secondLevel:visible').height(), 10) - 2) + 'px'); } if (!$('.a-colnav-firstLevel').hasClass('stacked')) { $('.a-colnav-firstLevel').css('height', 'auto'); } } function attachEventHandlers() { $(document).on('keyup keydown', function(e) { // Detect shift key shifted = e.shiftKey; }); $('.a-js-backButton').on('touchend click', function(event) { if (event.type !== 'touchend' || movedDuringTouch === false) { whenClick($('a.open').last(), true); } return false; }); $('.a-js-backButton').on('touchstart', function(event) { movedDuringTouch = false; }); $('.a-js-backButton').on('touchmove', function(event) { movedDuringTouch = true; }); } function disableToggles() { $(keys.toggleInput).closest(keys.radioClassSelector).addClass(keys.disabled); $(keys.toggleInput).attr(keys.disabled, true); } function enableToggles() { $(keys.toggleInput).closest(keys.radioClassSelector).removeClass(keys.disabled); $(keys.toggleInput).attr(keys.disabled, false); } function whenKey(e, classToQuery) { // Logic for keypresses on items var code = e.keyCode || e.which; if (code === 27 || code === 37 || code === 38 || code === 39 || code === 40) { stopEvent(e); } if (code === 13 || code === 32) { if (classToQuery !== '.a-colnav-item-third') { stopEvent(e); $(e.target).trigger('mouseup').trigger('focus'); } } else if (code === 9 && !$(e.target).hasClass('open')) { if (shifted) { if ($(e.target).blur().parent().prev().length !== 0) { stopEvent(e); $(e.target).blur().parent().prev() .find(classToQuery) .trigger('focus'); } } else if ($(e.target).blur().parent().next().length !== 0) { stopEvent(e); $(e.target).blur().parent().next() .find(classToQuery) .trigger('focus'); } } else if (code === 9 && $(e.target).hasClass('open')) { if (shifted) { stopEvent(e); $(e.target).blur().parent().parent() .parent() .children('a') .trigger('focus'); } else { stopEvent(e); $(e.target).blur().next().children('li:eq(0)') .children('a') .trigger('focus'); } } } function createDropDown() { time('createDropDown'); if (elements.$rootColnav.prop('data-dropdown-menu')) { pluginInstance.destroy(); } pluginInstance = new Foundation.DropdownMenu(elements.$rootColnav); timeEnd('createDropDown'); } function attachSubMenuEventHandlers($parentItem) { time('attachSubMenuEventHandlers'); $parentItem.find('.a-colnav-item-second').on('keydown', function(event) { whenKey(event, '.a-colnav-item-second'); }); $parentItem.find('.a-colnav-item-third').on('keydown', function(event) { whenKey(event, '.a-colnav-item-third'); }); timeEnd('attachSubMenuEventHandlers'); } function attachMenuEventHandlers() { var queryHit = false; var positionUrlParameterValue = null; time('attachMenuEventHandlers'); if (!menuHandlersAttached) { $(keys.colnavWrapper).on('mouseup touchend', function(event) { if (event.type !== 'touchend' || movedDuringTouch === false) { whenClick(event); } return false; }); $(keys.colnavWrapper).on('touchstart', function(event) { movedDuringTouch = false; }); $(keys.colnavWrapper).on('touchmove', function(event) { movedDuringTouch = true; }); menuHandlersAttached = true; } $(keys.colnavItemSelector).on('keydown', function(event) { whenKey(event, keys.colnavItemSelector); }); $(keys.colnavItemSelector).on('click', function(event) { if ($(window).scrollTop() > $(keys.colnavWrapper).offset().top) { $('html,body').animate({ scrollTop: $(keys.colnavWrapper).offset().top }, 300); } }); $(keys.colnavItemSelector).on('focus', function() { if ($('.a-colnav-secondLevel.submenu.is-active').length === 1) { $(this).off('keydown.zf.drilldown').parent().find('.a-colnav-item-second') .eq(0) .focus(); } }); // Check if position is included in URL, and navigate to it positionUrlParameterValue = urlQuery('position'); if (positionUrlParameterValue) { $(keys.colnavSelector).find('a.a-colnav-item').each(function() { if ($(this).find('h2').attr(keys.dataId) === positionUrlParameterValue) { queryHit = true; whenClick($(this), true); } }); if (currentCategory === category.category) { $(keys.colnavSelector).find('a.a-colnav-item-second').each(function() { if ($(this).find('h3').attr(keys.dataId) === positionUrlParameterValue) { queryHit = true; whenClick($(this).closest('ul').prev(), true); setTimeout(function() { whenClick($(this), true); }.bind(this), 250); } }); } } timeEnd('attachMenuEventHandlers'); } function createMenuNodeFragment(item, level, index) { var liClasses = ['', 'is-dropdown-submenu-parent is-submenu-item is-dropdown-submenu-item opens-right', 'is-submenu-item is-dropdown-submenu-item']; var aClasses = ['a-colnav-item', 'a-colnav-item-second', 'a-colnav-item-third']; var ulClasses = ['a-colnav a-colnav-vertical a-colnav-secondLevel', 'a-colnav a-colnav-vertical a-colnav-thirdLevel is-dropdown-submenu', '']; var span; var node = { li: createElement('li'), a: createElement('a'), h: createElement('h' + (level + 1)), p: createElement('p'), ul: createElement('ul') }; if (level === 2 && maxDepth() === 2) { node.h = createElement('h4'); } node.li.dataset.level = level; node.li.dataset.index = index; node.h.dataset.id = item.Id; node.h.textContent = item.Heading || item.Title; node.li.appendChild(node.a); node.a.appendChild(node.h); node.a.tabIndex = 0; node.li.className = liClasses[level - 1]; node.a.className = aClasses[level - 1]; node.p.className = 'a-leadText'; node.ul.className = ulClasses[level - 1]; if (level === 3) { span = createElement('span'); span.className = 'a-colnav-rightText'; span.textContent = item.Provider || 'Skatteetaten'; node.a.appendChild(span); } else if (level === 1) { node.a.appendChild(node.p); } if (level < maxDepth()) { node.li.appendChild(node.ul); node.a.className += ' a-js-colnavLink'; } else { node.a.className += ' a-js-colnavLinkAlt'; } return node; } populateSublevel = function(items, $ul, level) { var i; var item = null; var fragmmentNode = null; var fragment = document.createDocumentFragment(); if ($ul.children().length > 0 || items == null) { return; } time('populateSublevel'); for (i = 0; i < items.length; i += 1) { item = items[i]; fragmmentNode = createMenuNodeFragment(item, level, i); if (item.Url) { fragmmentNode.a.href = item.Url; } if (item.Description) { fragmmentNode.p.textContent = item.Description; } fragment.appendChild(fragmmentNode.li); } $ul[0].appendChild(fragment); $('.a-colnav-wrapper > .a-colnav').show(); timeEnd('populateSublevel'); if (level === 1) { createDropDown(); attachMenuEventHandlers(); } else { attachSubMenuEventHandlers($ul); } }; function populateDataObject(data, level) { var i = null; for (i = 0; i < data.length; i += 1) { currentData[i] = data[i]; } } function populateNavigation(str, data) { time('populateNavigation'); currentData = []; populateDataObject(data, 1); populateSublevel(currentData, elements.$rootColnav, 1); hideLoader(); enableToggles(); timeEnd('populateNavigation'); } function afterRequest(str, data) { timeEnd('getDrilldownSource'); time('afterRequest'); elements.$rootColnav = $('.a-colnav-wrapper > .a-colnav'); // empty() would be a safer way to remove the elements, but it's // slower // elements.$rootColnav.empty(); elements.$rootColnav.html(''); timeEnd('afterRequest'); populateNavigation(str, data); } function getDrilldownSource(str) { var url = endPointUrl + str; time('getDrilldownSource'); showLoader(); disableToggles(); if (savedResults[str]) { // Get stored results if present afterRequest(str, savedResults[str]); } else { // These hardcoded paths and IPs need to be fixed probably if (window.location.pathname.indexOf('DesignSystem') === 1 || window.location.origin.indexOf('localhost') !== -1 || window.location.origin.indexOf('192.168.') !== -1) { url += '.json'; } $.ajax({ type: 'GET', url: url, success: function(data) { savedResults[str] = data; afterRequest(str, data); // Perform populating logic } }); } } function resizedWindow() { var wasSmall = isSmall; // Redefine boolean for determining screen size isSmall = $('.a-contentOverview').width() < 900; // No change required if the window is still big/small if (isSmall === wasSmall) { return; } getDrilldownSource($(keys.toggleInput + keys.checked).data(keys.switchurl)); if (!isSmall) { // Ensure reset of markup $('.switch-container').show(); $('.a-js-colnavTitleRegular').text('Alle skjemaer'); } if (isSmall) { // Small screen specific style (can be moved to stylesheet) $('.a-contentOverview').css('overflow-x', 'hidden'); } } function onBodyClick(e) { var arr = []; if (!isSmall) { if ($(e.target).closest('.a-colnav-firstLevel').length === 0) { $('a.open').each(function() { arr.push($(this)); }); arr.reverse(); arr.forEach(function(item) { item.parent().trigger('mouseup'); }); } } } function performResizeLogicAfterResizeEvents() { var resizeTimeout; window.onresize = function() { clearTimeout(resizeTimeout); resizeTimeout = setTimeout(resizedWindow, 100); }; } function setSwitchUrlAttribute() { $(keys.toggleInput).each(function(index) { $(this).data(keys.switchurl, $(this).parents().eq(2).data(keys.switchurl + (index + 1)) ); $(this).prop('data-' + keys.switchurl, $(this).parents().eq(2).data(keys.switchurl + (index + 1)) ); }); } function onToggleChange() { if ($(this).is(keys.checked)) { setHistoryState(null); getDrilldownSource($(this).data(keys.switchurl)); } } function detectSelectedSourceChange() { $(keys.toggleInput).change(onToggleChange); } function loadDrillDownSource() { var selectedIndex = 1; var dataSource = 'get'; var urlCategory = urlQuery(keys.category); // Chose category/kategori by default currentCategory = category.category; // and see if the URL contains the selected category already if (urlCategory !== null && urlCategory !== false) { currentCategory = urlCategory; } if (currentCategory === category.provider) { selectedIndex = 2; } dataSource += currentCategory; $(keys.toggleInput).prop('checked', false); // Maybe not so robust to use the id, but it's the only way we have to // identify each checkbox at this point $('#atom-switch-cb' + selectedIndex).prop('checked', true); getDrilldownSource(dataSource); } function getElements() { elements.$rootColnav = $('.a-colnav-wrapper > .a-colnav'); } $(document).ready(function() { endPointUrl = $(keys.toggleInput).parents().eq(3).data('switchendpoint'); getElements(); if (elements.$rootColnav.length > 0) { // Check if drilldown markup is present if (isSmall) { // Small screen specific style (can be moved to stylesheet) $('.a-contentOverview').css('overflow-x', 'hidden'); } attachEventHandlers(); loadDrillDownSource(); performResizeLogicAfterResizeEvents(); setSwitchUrlAttribute(); detectSelectedSourceChange(); $('body').on('click', onBodyClick); } }); };