/** * @copyright (C) 2013 Open Source Matters, Inc. * @license GNU General Public License version 2 or later; see LICENSE.txt */ /** * JavaScript behavior to allow selected tab to be remained after save or page reload * keeping state in sessionStorage with better handling of multiple tab widgets per page * and not saving the state if there is no id in the url (like on the CREATE page of content) */ jQuery(function ($) { /** * Tiny jQuery extension to allow getting of url params * @use jQuery.urlParam('param') or $.urlParam('myRegex|anotherRegex') * If no trailing equals sign in name, add one, allows for general reuse */ $.urlParam = function (name) { if (!new RegExp("=$").exec(name)) { name = name + '='; } var results = new RegExp("[\\?&](" + name + ")([^&#]*)").exec(window.location.href); return results ? results[1] : null; }; // jQuery extension to get the XPATH of a DOM element $.getXpath = function (el) { if (typeof el == "string") { return document.evaluate(el, document, null, 0, null); } if (!el || el.nodeType != 1) { return ""; } if (el.id) { return "//*[@id='" + el.id + "']"; } var a = []; var sames = a.filter.call(el.parentNode.children, function (x) { return x.tagName == el.tagName; }); var b = []; return $.getXpath(el.parentNode) + "/" + el.tagName.toLowerCase() + (sames.length > 1 ? "[" + (b.indexOf.call(sames, el) + 1) + "]" : ""); }; // jQuery extension to get the DOM element from an XPATH $.findXpath = function (exp, ctxt) { var item; var coll = []; var result = document.evaluate(exp, ctxt || document, null, 5, null); while (item = result.iterateNext()) { coll.push(item); } return $(coll); }; var loadTabs = function () { /** * Remove an item from an array */ function remove_item(activeTabsHrefs, tabCollection) { for (var i = 0; i < activeTabsHrefs.length; i++) { if (activeTabsHrefs[i].indexOf(tabCollection) > -1) { activeTabsHrefs.splice(i, 1); } } return activeTabsHrefs; } /** * Generate the sessionStorage key we will use * This is the URL minus some cleanup */ function getStorageKey() { return window.location.href.toString().split(window.location.host)[1].replace(/&return=[a-zA-Z0-9%]+/, "").split('#')[0]; } /** * Save this tab to the storage in the form of a pseudo keyed array */ function saveActiveTab(event) { if (!window.sessionStorage) { return; } // Get a new storage key, normally the full url we are on with some cleanup var storageKey = getStorageKey(); // get this tabs own href var href = $(event.target).attr("href"); // find the collection of tabs this tab belongs to, and calculate the unique xpath to it var tabCollection = $.getXpath($(event.target).closest(".nav-tabs").first().get(0)); // error handling if (!tabCollection || typeof href == "undefined") { return; } // Create a dummy keyed array as js doesnt allow keyed arrays var storageValue = tabCollection + "|" + href; // Get the current array from the storage var activeTabsHrefs = JSON.parse(sessionStorage.getItem(storageKey)); // If none start a new array if (!activeTabsHrefs) { var activeTabsHrefs = []; } else { // Avoid Duplicates in the storage remove_item(activeTabsHrefs, tabCollection); } // Save clicked tab, with relationship to tabCollection to the array activeTabsHrefs.push(storageValue); // Store the selected tabs as an array in sessionStorage sessionStorage.setItem(storageKey, JSON.stringify(activeTabsHrefs)); } // Array with active tabs hrefs var activeTabsHrefs = JSON.parse(sessionStorage.getItem(getStorageKey())); // jQuery object with all tabs links var alltabs = $("a[data-toggle='tab']"); // When a tab is clicked, save its state! alltabs.on("click", function (e) { saveActiveTab(e); }); // Clean default tabs alltabs.parent(".active").removeClass("active"); // If we cannot find a tab storage for this url, see if we are coming from a save of a new item if (!activeTabsHrefs) { var unSavedStateUrl = getStorageKey().replace(/\&id=[0-9]*|[a-z]\&{1}_id=[0-9]*/, ''); activeTabsHrefs = JSON.parse(sessionStorage.getItem(unSavedStateUrl)); sessionStorage.removeItem(unSavedStateUrl); } // we have some tab states to restore, if we see a hash then let that trump the saved state if (activeTabsHrefs !== null && !window.location.hash) { // When moving from tab area to a different view $.each(activeTabsHrefs, function (index, tabFakexPath) { // Click the tab var parts = tabFakexPath.split("|"); $.findXpath(parts[0]).find("a[data-toggle='tab'][href='" + parts[1] + "']").click(); }); } else { // clean slate start // a list of tabs to click var tabsToClick = []; // If we are passing a hash then this trumps everything if (window.location.hash) { // for each set of tabs on the page alltabs.parents("ul").each(function (index, ul) { // If no tabs is saved, activate first tab from each tab set and save it var tabToClick = $(ul).find("a[href='" + window.location.hash + "']"); // If we found some|one if (tabToClick.length) { // if we managed to locate its selector directly if (tabToClick.selector) { // highlight tab of the tabs if the hash matches tabsToClick.push(tabToClick); } else { // highlight first tab of the tabs tabsToClick.push(tabToClick.first()); } var parentPane = tabToClick.closest('.tab-pane'); // bubble up for nested tabs (like permissions tabs in the permissions pane) if (parentPane) { var id = parentPane.attr('id'); if (id) { var parentTabToClick = $(parentPane).find("a[href='#" + id + "']"); if (parentTabToClick) { tabsToClick.push(parentTabToClick); } } } } // cleanup for another loop parentTabToClick = null; tabToClick = null; parentPane = null; id = null; }); // run in the right order bubbling up tabsToClick.reverse(); // for all queued tabs for (var i = 0; i < tabsToClick.length; i++) { // click the tabs, thus storing them jQuery(tabsToClick[i].selector).click(); } // Remove the #hash in the url - with support for older browsers with no flicker var scrollV, scrollH, loc = window.location; if ("pushState" in history) history.pushState("", document.title, loc.pathname + loc.search); else { // Prevent scrolling by storing the page's current scroll offset scrollV = document.body.scrollTop; scrollH = document.body.scrollLeft; loc.hash = ""; // Restore the scroll offset, should be flicker free document.body.scrollTop = scrollV; document.body.scrollLeft = scrollH; } } else { alltabs.parents("ul").each(function (index, ul) { // If no tabs is saved, activate first tab from each tab set and save it $(ul).find("a").first().click(); }); } } }; setTimeout(loadTabs, 100); });