exception.js 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304
  1. /* This file is based on WebProfilerBundle/Resources/views/Profiler/base_js.html.twig.
  2. If you make any change in this file, verify the same change is needed in the other file. */
  3. /*<![CDATA[*/
  4. (function() {
  5. "use strict";
  6. if ('classList' in document.documentElement) {
  7. var hasClass = function (el, cssClass) { return el.classList.contains(cssClass); };
  8. var removeClass = function(el, cssClass) { el.classList.remove(cssClass); };
  9. var addClass = function(el, cssClass) { el.classList.add(cssClass); };
  10. var toggleClass = function(el, cssClass) { el.classList.toggle(cssClass); };
  11. } else {
  12. var hasClass = function (el, cssClass) { return el.className.match(new RegExp('\\b' + cssClass + '\\b')); };
  13. var removeClass = function(el, cssClass) { el.className = el.className.replace(new RegExp('\\b' + cssClass + '\\b'), ' '); };
  14. var addClass = function(el, cssClass) { if (!hasClass(el, cssClass)) { el.className += " " + cssClass; } };
  15. var toggleClass = function(el, cssClass) { hasClass(el, cssClass) ? removeClass(el, cssClass) : addClass(el, cssClass); };
  16. }
  17. var addEventListener;
  18. var el = document.createElement('div');
  19. if (!('addEventListener' in el)) {
  20. addEventListener = function (element, eventName, callback) {
  21. element.attachEvent('on' + eventName, callback);
  22. };
  23. } else {
  24. addEventListener = function (element, eventName, callback) {
  25. element.addEventListener(eventName, callback, false);
  26. };
  27. }
  28. if (navigator.clipboard) {
  29. document.querySelectorAll('[data-clipboard-text]').forEach(function(element) {
  30. removeClass(element, 'hidden');
  31. element.addEventListener('click', function() {
  32. navigator.clipboard.writeText(element.getAttribute('data-clipboard-text'));
  33. })
  34. });
  35. }
  36. (function createTabs() {
  37. /* the accessibility options of this component have been defined according to: */
  38. /* www.w3.org/WAI/ARIA/apg/example-index/tabs/tabs-manual.html */
  39. var tabGroups = document.querySelectorAll('.sf-tabs:not([data-processed=true])');
  40. /* create the tab navigation for each group of tabs */
  41. for (var i = 0; i < tabGroups.length; i++) {
  42. var tabs = tabGroups[i].querySelectorAll(':scope > .tab');
  43. var tabNavigation = document.createElement('div');
  44. tabNavigation.className = 'tab-navigation';
  45. tabNavigation.setAttribute('role', 'tablist');
  46. var selectedTabId = 'tab-' + i + '-0'; /* select the first tab by default */
  47. for (var j = 0; j < tabs.length; j++) {
  48. var tabId = 'tab-' + i + '-' + j;
  49. var tabTitle = tabs[j].querySelector('.tab-title').innerHTML;
  50. var tabNavigationItem = document.createElement('button');
  51. addClass(tabNavigationItem, 'tab-control');
  52. tabNavigationItem.setAttribute('data-tab-id', tabId);
  53. tabNavigationItem.setAttribute('role', 'tab');
  54. tabNavigationItem.setAttribute('aria-controls', tabId);
  55. if (hasClass(tabs[j], 'active')) { selectedTabId = tabId; }
  56. if (hasClass(tabs[j], 'disabled')) {
  57. addClass(tabNavigationItem, 'disabled');
  58. }
  59. tabNavigationItem.innerHTML = tabTitle;
  60. tabNavigation.appendChild(tabNavigationItem);
  61. var tabContent = tabs[j].querySelector('.tab-content');
  62. tabContent.parentElement.setAttribute('id', tabId);
  63. }
  64. tabGroups[i].insertBefore(tabNavigation, tabGroups[i].firstChild);
  65. addClass(document.querySelector('[data-tab-id="' + selectedTabId + '"]'), 'active');
  66. }
  67. /* display the active tab and add the 'click' event listeners */
  68. for (i = 0; i < tabGroups.length; i++) {
  69. tabNavigation = tabGroups[i].querySelectorAll(':scope > .tab-navigation .tab-control');
  70. for (j = 0; j < tabNavigation.length; j++) {
  71. tabId = tabNavigation[j].getAttribute('data-tab-id');
  72. var tabPanel = document.getElementById(tabId);
  73. tabPanel.setAttribute('role', 'tabpanel');
  74. tabPanel.setAttribute('aria-labelledby', tabId);
  75. tabPanel.querySelector('.tab-title').className = 'hidden';
  76. if (hasClass(tabNavigation[j], 'active')) {
  77. tabPanel.className = 'block';
  78. tabNavigation[j].setAttribute('aria-selected', 'true');
  79. tabNavigation[j].removeAttribute('tabindex');
  80. } else {
  81. tabPanel.className = 'hidden';
  82. tabNavigation[j].removeAttribute('aria-selected');
  83. tabNavigation[j].setAttribute('tabindex', '-1');
  84. }
  85. tabNavigation[j].addEventListener('click', function(e) {
  86. var activeTab = e.target || e.srcElement;
  87. /* needed because when the tab contains HTML contents, user can click */
  88. /* on any of those elements instead of their parent '<button>' element */
  89. while (activeTab.tagName.toLowerCase() !== 'button') {
  90. activeTab = activeTab.parentNode;
  91. }
  92. /* get the full list of tabs through the parent of the active tab element */
  93. var tabNavigation = activeTab.parentNode.children;
  94. for (var k = 0; k < tabNavigation.length; k++) {
  95. var tabId = tabNavigation[k].getAttribute('data-tab-id');
  96. document.getElementById(tabId).className = 'hidden';
  97. removeClass(tabNavigation[k], 'active');
  98. tabNavigation[k].removeAttribute('aria-selected');
  99. tabNavigation[k].setAttribute('tabindex', '-1');
  100. }
  101. addClass(activeTab, 'active');
  102. activeTab.setAttribute('aria-selected', 'true');
  103. activeTab.removeAttribute('tabindex');
  104. var activeTabId = activeTab.getAttribute('data-tab-id');
  105. document.getElementById(activeTabId).className = 'block';
  106. });
  107. }
  108. tabGroups[i].setAttribute('data-processed', 'true');
  109. }
  110. })();
  111. (function createToggles() {
  112. var toggles = document.querySelectorAll('.sf-toggle:not([data-processed=true])');
  113. for (var i = 0; i < toggles.length; i++) {
  114. var elementSelector = toggles[i].getAttribute('data-toggle-selector');
  115. var element = document.querySelector(elementSelector);
  116. addClass(element, 'sf-toggle-content');
  117. if (toggles[i].hasAttribute('data-toggle-initial') && toggles[i].getAttribute('data-toggle-initial') == 'display') {
  118. addClass(toggles[i], 'sf-toggle-on');
  119. addClass(element, 'sf-toggle-visible');
  120. } else {
  121. addClass(toggles[i], 'sf-toggle-off');
  122. addClass(element, 'sf-toggle-hidden');
  123. }
  124. addEventListener(toggles[i], 'click', function(e) {
  125. e.preventDefault();
  126. if ('' !== window.getSelection().toString()) {
  127. /* Don't do anything on text selection */
  128. return;
  129. }
  130. var toggle = e.target || e.srcElement;
  131. /* needed because when the toggle contains HTML contents, user can click */
  132. /* on any of those elements instead of their parent '.sf-toggle' element */
  133. while (!hasClass(toggle, 'sf-toggle')) {
  134. toggle = toggle.parentNode;
  135. }
  136. var element = document.querySelector(toggle.getAttribute('data-toggle-selector'));
  137. toggleClass(toggle, 'sf-toggle-on');
  138. toggleClass(toggle, 'sf-toggle-off');
  139. toggleClass(element, 'sf-toggle-hidden');
  140. toggleClass(element, 'sf-toggle-visible');
  141. /* the toggle doesn't change its contents when clicking on it */
  142. if (!toggle.hasAttribute('data-toggle-alt-content')) {
  143. return;
  144. }
  145. if (!toggle.hasAttribute('data-toggle-original-content')) {
  146. toggle.setAttribute('data-toggle-original-content', toggle.innerHTML);
  147. }
  148. var currentContent = toggle.innerHTML;
  149. var originalContent = toggle.getAttribute('data-toggle-original-content');
  150. var altContent = toggle.getAttribute('data-toggle-alt-content');
  151. toggle.innerHTML = currentContent !== altContent ? altContent : originalContent;
  152. });
  153. /* Prevents from disallowing clicks on links inside toggles */
  154. var toggleLinks = toggles[i].querySelectorAll('a');
  155. for (var j = 0; j < toggleLinks.length; j++) {
  156. addEventListener(toggleLinks[j], 'click', function(e) {
  157. e.stopPropagation();
  158. });
  159. }
  160. /* Prevents from disallowing clicks on "copy to clipboard" elements inside toggles */
  161. var copyToClipboardElements = toggles[i].querySelectorAll('span[data-clipboard-text]');
  162. for (var k = 0; k < copyToClipboardElements.length; k++) {
  163. addEventListener(copyToClipboardElements[k], 'click', function(e) {
  164. e.stopPropagation();
  165. });
  166. }
  167. toggles[i].setAttribute('data-processed', 'true');
  168. }
  169. })();
  170. (function createFilters() {
  171. document.querySelectorAll('[data-filters] [data-filter]').forEach(function (filter) {
  172. var filters = filter.closest('[data-filters]'),
  173. type = 'choice',
  174. name = filter.dataset.filter,
  175. ucName = name.charAt(0).toUpperCase()+name.slice(1),
  176. list = document.createElement('ul'),
  177. values = filters.dataset['filter'+ucName] || filters.querySelectorAll('[data-filter-'+name+']'),
  178. labels = {},
  179. defaults = null,
  180. indexed = {},
  181. processed = {};
  182. if (typeof values === 'string') {
  183. type = 'level';
  184. labels = values.split(',');
  185. values = values.toLowerCase().split(',');
  186. defaults = values.length - 1;
  187. }
  188. addClass(list, 'filter-list');
  189. addClass(list, 'filter-list-'+type);
  190. values.forEach(function (value, i) {
  191. if (value instanceof HTMLElement) {
  192. value = value.dataset['filter'+ucName];
  193. }
  194. if (value in processed) {
  195. return;
  196. }
  197. var option = document.createElement('li'),
  198. label = i in labels ? labels[i] : value,
  199. active = false,
  200. matches;
  201. if ('' === label) {
  202. option.innerHTML = '<em>(none)</em>';
  203. } else {
  204. option.innerText = label;
  205. }
  206. option.dataset.filter = value;
  207. option.setAttribute('title', 1 === (matches = filters.querySelectorAll('[data-filter-'+name+'="'+value+'"]').length) ? 'Matches 1 row' : 'Matches '+matches+' rows');
  208. indexed[value] = i;
  209. list.appendChild(option);
  210. addEventListener(option, 'click', function () {
  211. if ('choice' === type) {
  212. filters.querySelectorAll('[data-filter-'+name+']').forEach(function (row) {
  213. if (option.dataset.filter === row.dataset['filter'+ucName]) {
  214. toggleClass(row, 'filter-hidden-'+name);
  215. }
  216. });
  217. toggleClass(option, 'active');
  218. } else if ('level' === type) {
  219. if (i === this.parentNode.querySelectorAll('.active').length - 1) {
  220. return;
  221. }
  222. this.parentNode.querySelectorAll('li').forEach(function (currentOption, j) {
  223. if (j <= i) {
  224. addClass(currentOption, 'active');
  225. if (i === j) {
  226. addClass(currentOption, 'last-active');
  227. } else {
  228. removeClass(currentOption, 'last-active');
  229. }
  230. } else {
  231. removeClass(currentOption, 'active');
  232. removeClass(currentOption, 'last-active');
  233. }
  234. });
  235. filters.querySelectorAll('[data-filter-'+name+']').forEach(function (row) {
  236. if (i < indexed[row.dataset['filter'+ucName]]) {
  237. addClass(row, 'filter-hidden-'+name);
  238. } else {
  239. removeClass(row, 'filter-hidden-'+name);
  240. }
  241. });
  242. }
  243. });
  244. if ('choice' === type) {
  245. active = null === defaults || 0 <= defaults.indexOf(value);
  246. } else if ('level' === type) {
  247. active = i <= defaults;
  248. if (active && i === defaults) {
  249. addClass(option, 'last-active');
  250. }
  251. }
  252. if (active) {
  253. addClass(option, 'active');
  254. } else {
  255. filters.querySelectorAll('[data-filter-'+name+'="'+value+'"]').forEach(function (row) {
  256. toggleClass(row, 'filter-hidden-'+name);
  257. });
  258. }
  259. processed[value] = true;
  260. });
  261. if (1 < list.childNodes.length) {
  262. filter.appendChild(list);
  263. filter.dataset.filtered = '';
  264. }
  265. });
  266. })();
  267. })();
  268. /*]]>*/