platform-content-handler.js 10.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290
  1. filteringContext = {
  2. dependencies: {},
  3. restrictedDependencies: [],
  4. activeFilters: []
  5. }
  6. let highlightedAnchor;
  7. let topNavbarOffset;
  8. window.addEventListener('load', () => {
  9. document.querySelectorAll("div[data-platform-hinted]")
  10. .forEach(elem => elem.addEventListener('click', (event) => togglePlatformDependent(event,elem)))
  11. document.querySelectorAll("div[tabs-section]")
  12. .forEach(elem => elem.addEventListener('click', (event) => toggleSectionsEventHandler(event)))
  13. const filterSection = document.getElementById('filter-section')
  14. if (filterSection) {
  15. filterSection.addEventListener('click', (event) => filterButtonHandler(event))
  16. initializeFiltering()
  17. }
  18. initTabs()
  19. handleAnchor()
  20. initHidingLeftNavigation()
  21. document.getElementById('main').addEventListener("scroll", (e) => {
  22. document.getElementsByClassName("navigation-wrapper")[0].classList.toggle("sticky-navigation", e.target.scrollTop > 0)
  23. })
  24. topNavbarOffset = document.getElementById('navigation-wrapper')
  25. })
  26. const initHidingLeftNavigation = () => {
  27. document.getElementById("leftToggler").onclick = function(event) {
  28. //Events need to be prevented from bubbling since they will trigger next handler
  29. event.preventDefault();
  30. event.stopPropagation();
  31. event.stopImmediatePropagation();
  32. document.getElementById("leftColumn").classList.toggle("open");
  33. }
  34. document.getElementById("main").onclick = () => {
  35. document.getElementById("leftColumn").classList.remove("open");
  36. }
  37. }
  38. // Hash change is needed in order to allow for linking inside the same page with anchors
  39. // If this is not present user is forced to refresh the site in order to use an anchor
  40. window.onhashchange = handleAnchor
  41. function scrollToElementInContent(element){
  42. const scrollToElement = () => document.getElementById('main').scrollTo({ top: element.offsetTop - topNavbarOffset.offsetHeight, behavior: "smooth"})
  43. const waitAndScroll = () => {
  44. setTimeout(() => {
  45. if(topNavbarOffset){
  46. scrollToElement()
  47. } else {
  48. waitForScroll()
  49. }
  50. }, 50)
  51. }
  52. if(topNavbarOffset){
  53. scrollToElement()
  54. } else {
  55. waitAndScroll()
  56. }
  57. }
  58. function handleAnchor() {
  59. if(highlightedAnchor){
  60. highlightedAnchor.classList.remove('anchor-highlight')
  61. highlightedAnchor = null;
  62. }
  63. let searchForTab = function(element) {
  64. if(element && element.hasAttribute) {
  65. if(element.hasAttribute("data-togglable")) return element;
  66. else return searchForTab(element.parentNode)
  67. } else return null
  68. }
  69. let anchor = window.location.hash
  70. if (anchor != "") {
  71. anchor = anchor.substring(1)
  72. let element = document.querySelector('a[data-name="' + anchor+'"]')
  73. if (element) {
  74. let tab = searchForTab(element)
  75. if (tab) {
  76. toggleSections(tab)
  77. }
  78. const content = element.nextElementSibling
  79. if(content){
  80. content.classList.add('anchor-highlight')
  81. highlightedAnchor = content
  82. }
  83. scrollToElementInContent(element)
  84. }
  85. }
  86. }
  87. function initTabs(){
  88. document.querySelectorAll("div[tabs-section]")
  89. .forEach(element => {
  90. showCorrespondingTabBody(element)
  91. element.addEventListener('click', (event) => toggleSectionsEventHandler(event))
  92. })
  93. let cached = localStorage.getItem("active-tab")
  94. if (cached) {
  95. let parsed = JSON.parse(cached)
  96. let tab = document.querySelector('div[tabs-section] > button[data-togglable="' + parsed + '"]')
  97. if(tab) {
  98. toggleSections(tab)
  99. }
  100. }
  101. }
  102. function showCorrespondingTabBody(element){
  103. const key = element.querySelector("button[data-active]").getAttribute("data-togglable")
  104. document.querySelector(".tabs-section-body")
  105. .querySelector("div[data-togglable='" + key + "']")
  106. .setAttribute("data-active", "")
  107. }
  108. function filterButtonHandler(event) {
  109. if(event.target.tagName == "BUTTON" && event.target.hasAttribute("data-filter")) {
  110. let sourceset = event.target.getAttribute("data-filter")
  111. if(filteringContext.activeFilters.indexOf(sourceset) != -1) {
  112. filterSourceset(sourceset)
  113. } else {
  114. unfilterSourceset(sourceset)
  115. }
  116. }
  117. }
  118. function initializeFiltering() {
  119. filteringContext.dependencies = JSON.parse(sourceset_dependencies)
  120. document.querySelectorAll("#filter-section > button")
  121. .forEach(p => filteringContext.restrictedDependencies.push(p.getAttribute("data-filter")))
  122. Object.keys(filteringContext.dependencies).forEach(p => {
  123. filteringContext.dependencies[p] = filteringContext.dependencies[p]
  124. .filter(q => -1 !== filteringContext.restrictedDependencies.indexOf(q))
  125. })
  126. let cached = window.localStorage.getItem('inactive-filters')
  127. if (cached) {
  128. let parsed = JSON.parse(cached)
  129. //Events are used by react to get values in 'on this page'
  130. const event = new CustomEvent('sourceset-filter-change', { detail: parsed });
  131. window.dispatchEvent(event);
  132. filteringContext.activeFilters = filteringContext.restrictedDependencies
  133. .filter(q => parsed.indexOf(q) == -1 )
  134. } else {
  135. filteringContext.activeFilters = filteringContext.restrictedDependencies
  136. }
  137. refreshFiltering()
  138. }
  139. function filterSourceset(sourceset) {
  140. filteringContext.activeFilters = filteringContext.activeFilters.filter(p => p != sourceset)
  141. refreshFiltering()
  142. addSourcesetFilterToCache(sourceset)
  143. }
  144. function unfilterSourceset(sourceset) {
  145. if(filteringContext.activeFilters.length == 0) {
  146. filteringContext.activeFilters = filteringContext.dependencies[sourceset].concat([sourceset])
  147. refreshFiltering()
  148. filteringContext.dependencies[sourceset].concat([sourceset]).forEach(p => removeSourcesetFilterFromCache(p))
  149. } else {
  150. filteringContext.activeFilters.push(sourceset)
  151. refreshFiltering()
  152. removeSourcesetFilterFromCache(sourceset)
  153. }
  154. }
  155. function addSourcesetFilterToCache(sourceset) {
  156. let cached = localStorage.getItem('inactive-filters')
  157. if (cached) {
  158. let parsed = JSON.parse(cached)
  159. localStorage.setItem('inactive-filters', JSON.stringify(parsed.concat([sourceset])))
  160. } else {
  161. localStorage.setItem('inactive-filters', JSON.stringify([sourceset]))
  162. }
  163. }
  164. function removeSourcesetFilterFromCache(sourceset) {
  165. let cached = localStorage.getItem('inactive-filters')
  166. if (cached) {
  167. let parsed = JSON.parse(cached)
  168. localStorage.setItem('inactive-filters', JSON.stringify(parsed.filter(p => p != sourceset)))
  169. }
  170. }
  171. function toggleSections(target) {
  172. localStorage.setItem('active-tab', JSON.stringify(target.getAttribute("data-togglable")))
  173. const activateTabs = (containerClass) => {
  174. for(const element of document.getElementsByClassName(containerClass)){
  175. for(const child of element.children){
  176. if(child.getAttribute("data-togglable") === target.getAttribute("data-togglable")){
  177. child.setAttribute("data-active", "")
  178. } else {
  179. child.removeAttribute("data-active")
  180. }
  181. }
  182. }
  183. }
  184. activateTabs("tabs-section")
  185. activateTabs("tabs-section-body")
  186. }
  187. function toggleSectionsEventHandler(evt){
  188. if(!evt.target.getAttribute("data-togglable")) return
  189. toggleSections(evt.target)
  190. }
  191. function togglePlatformDependent(e, container) {
  192. let target = e.target
  193. if (target.tagName != 'BUTTON') return;
  194. let index = target.getAttribute('data-toggle')
  195. for(let child of container.children){
  196. if(child.hasAttribute('data-toggle-list')){
  197. for(let bm of child.children){
  198. if(bm == target){
  199. bm.setAttribute('data-active',"")
  200. } else if(bm != target) {
  201. bm.removeAttribute('data-active')
  202. }
  203. }
  204. }
  205. else if(child.getAttribute('data-togglable') == index) {
  206. child.setAttribute('data-active',"")
  207. }
  208. else {
  209. child.removeAttribute('data-active')
  210. }
  211. }
  212. }
  213. function refreshFiltering() {
  214. let sourcesetList = filteringContext.activeFilters
  215. document.querySelectorAll("[data-filterable-set]")
  216. .forEach(
  217. elem => {
  218. let platformList = elem.getAttribute("data-filterable-set").split(' ').filter(v => -1 !== sourcesetList.indexOf(v))
  219. elem.setAttribute("data-filterable-current", platformList.join(' '))
  220. }
  221. )
  222. const event = new CustomEvent('sourceset-filter-change', { detail: sourcesetList });
  223. window.dispatchEvent(event);
  224. refreshFilterButtons()
  225. refreshPlatformTabs()
  226. }
  227. function refreshPlatformTabs() {
  228. document.querySelectorAll(".platform-hinted > .platform-bookmarks-row").forEach(
  229. p => {
  230. let active = false;
  231. let firstAvailable = null
  232. p.childNodes.forEach(
  233. element => {
  234. if(element.getAttribute("data-filterable-current") != ''){
  235. if( firstAvailable == null) {
  236. firstAvailable = element
  237. }
  238. if(element.hasAttribute("data-active")) {
  239. active = true;
  240. }
  241. }
  242. }
  243. )
  244. if( active == false && firstAvailable) {
  245. firstAvailable.click()
  246. }
  247. }
  248. )
  249. }
  250. function refreshFilterButtons() {
  251. document.querySelectorAll("#filter-section > button")
  252. .forEach(f => {
  253. if(filteringContext.activeFilters.indexOf(f.getAttribute("data-filter")) != -1){
  254. f.setAttribute("data-active","")
  255. } else {
  256. f.removeAttribute("data-active")
  257. }
  258. })
  259. }