base_js.html.twig 19 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531
  1. {# This file is partially duplicated in TwigBundle/Resources/views/base_js.html.twig. If you
  2. make any change in this file, verify the same change is needed in the other file. #}
  3. <script{% if csp_script_nonce is defined and csp_script_nonce %} nonce="{{ csp_script_nonce }}"{% endif %}>/*<![CDATA[*/
  4. {# Caution: the contents of this file are processed by Twig before loading
  5. them as JavaScript source code. Always use '/*' comments instead
  6. of '//' comments to avoid impossible-to-debug side-effects #}
  7. console.log('This uses the overridden profiler template');
  8. Sfjs = (function() {
  9. "use strict";
  10. if ('classList' in document.documentElement) {
  11. var hasClass = function (el, cssClass) { return el.classList.contains(cssClass); };
  12. var removeClass = function(el, cssClass) { el.classList.remove(cssClass); };
  13. var addClass = function(el, cssClass) { el.classList.add(cssClass); };
  14. var toggleClass = function(el, cssClass) { el.classList.toggle(cssClass); };
  15. } else {
  16. var hasClass = function (el, cssClass) { return el.className.match(new RegExp('\\b' + cssClass + '\\b')); };
  17. var removeClass = function(el, cssClass) { el.className = el.className.replace(new RegExp('\\b' + cssClass + '\\b'), ' '); };
  18. var addClass = function(el, cssClass) { if (!hasClass(el, cssClass)) { el.className += " " + cssClass; } };
  19. var toggleClass = function(el, cssClass) { hasClass(el, cssClass) ? removeClass(el, cssClass) : addClass(el, cssClass); };
  20. }
  21. var noop = function() {};
  22. var profilerStorageKey = 'symfony/profiler/';
  23. var request = function(url, onSuccess, onError, payload, options) {
  24. var xhr = window.XMLHttpRequest ? new XMLHttpRequest() : new ActiveXObject('Microsoft.XMLHTTP');
  25. options = options || {};
  26. options.maxTries = options.maxTries || 0;
  27. xhr.open(options.method || 'GET', url, true);
  28. xhr.setRequestHeader('X-Requested-With', 'XMLHttpRequest');
  29. xhr.onreadystatechange = function(state) {
  30. if (4 !== xhr.readyState) {
  31. return null;
  32. }
  33. if (xhr.status == 404 && options.maxTries > 1) {
  34. setTimeout(function(){
  35. options.maxTries--;
  36. request(url, onSuccess, onError, payload, options);
  37. }, 500);
  38. return null;
  39. }
  40. if (200 === xhr.status) {
  41. (onSuccess || noop)(xhr);
  42. } else {
  43. (onError || noop)(xhr);
  44. }
  45. };
  46. xhr.send(payload || '');
  47. };
  48. var getPreference = function(name) {
  49. if (!window.localStorage) {
  50. return null;
  51. }
  52. return localStorage.getItem(profilerStorageKey + name);
  53. };
  54. var setPreference = function(name, value) {
  55. if (!window.localStorage) {
  56. return null;
  57. }
  58. localStorage.setItem(profilerStorageKey + name, value);
  59. };
  60. var requestStack = [];
  61. var extractHeaders = function(xhr, stackElement) {
  62. /* Here we avoid to call xhr.getResponseHeader in order to */
  63. /* prevent polluting the console with CORS security errors */
  64. var allHeaders = xhr.getAllResponseHeaders();
  65. var ret;
  66. if (ret = allHeaders.match(/^x-debug-token:\s+(.*)$/im)) {
  67. stackElement.profile = ret[1];
  68. }
  69. if (ret = allHeaders.match(/^x-debug-token-link:\s+(.*)$/im)) {
  70. stackElement.profilerUrl = ret[1];
  71. }
  72. };
  73. var successStreak = 4;
  74. var pendingRequests = 0;
  75. var renderAjaxRequests = function() {
  76. var requestCounter = document.querySelector('.sf-toolbar-ajax-request-counter');
  77. if (!requestCounter) {
  78. return;
  79. }
  80. requestCounter.textContent = requestStack.length;
  81. var infoSpan = document.querySelector(".sf-toolbar-ajax-info");
  82. if (infoSpan) {
  83. infoSpan.textContent = requestStack.length + ' AJAX request' + (requestStack.length !== 1 ? 's' : '');
  84. }
  85. var ajaxToolbarPanel = document.querySelector('.sf-toolbar-block-ajax');
  86. if (requestStack.length) {
  87. ajaxToolbarPanel.style.display = 'block';
  88. } else {
  89. ajaxToolbarPanel.style.display = 'none';
  90. }
  91. if (pendingRequests > 0) {
  92. addClass(ajaxToolbarPanel, 'sf-ajax-request-loading');
  93. } else if (successStreak < 4) {
  94. addClass(ajaxToolbarPanel, 'sf-toolbar-status-red');
  95. removeClass(ajaxToolbarPanel, 'sf-ajax-request-loading');
  96. } else {
  97. removeClass(ajaxToolbarPanel, 'sf-ajax-request-loading');
  98. removeClass(ajaxToolbarPanel, 'sf-toolbar-status-red');
  99. }
  100. };
  101. var startAjaxRequest = function(index) {
  102. var tbody = document.querySelector('.sf-toolbar-ajax-request-list');
  103. if (!tbody) {
  104. return;
  105. }
  106. var request = requestStack[index];
  107. pendingRequests++;
  108. var row = document.createElement('tr');
  109. request.DOMNode = row;
  110. var methodCell = document.createElement('td');
  111. methodCell.textContent = request.method;
  112. row.appendChild(methodCell);
  113. var typeCell = document.createElement('td');
  114. typeCell.textContent = request.type;
  115. row.appendChild(typeCell);
  116. var statusCodeCell = document.createElement('td');
  117. var statusCode = document.createElement('span');
  118. statusCode.textContent = 'n/a';
  119. statusCodeCell.appendChild(statusCode);
  120. row.appendChild(statusCodeCell);
  121. var pathCell = document.createElement('td');
  122. pathCell.className = 'sf-ajax-request-url';
  123. if ('GET' === request.method) {
  124. var pathLink = document.createElement('a');
  125. pathLink.setAttribute('href', request.url);
  126. pathLink.textContent = request.url;
  127. pathCell.appendChild(pathLink);
  128. } else {
  129. pathCell.textContent = request.url;
  130. }
  131. pathCell.setAttribute('title', request.url);
  132. row.appendChild(pathCell);
  133. var durationCell = document.createElement('td');
  134. durationCell.className = 'sf-ajax-request-duration';
  135. durationCell.textContent = 'n/a';
  136. row.appendChild(durationCell);
  137. var profilerCell = document.createElement('td');
  138. profilerCell.textContent = 'n/a';
  139. row.appendChild(profilerCell);
  140. row.className = 'sf-ajax-request sf-ajax-request-loading';
  141. tbody.insertBefore(row, tbody.firstChild);
  142. renderAjaxRequests();
  143. };
  144. var finishAjaxRequest = function(index) {
  145. var request = requestStack[index];
  146. if (!request.DOMNode) {
  147. return;
  148. }
  149. pendingRequests--;
  150. var row = request.DOMNode;
  151. /* Unpack the children from the row */
  152. var methodCell = row.children[0];
  153. var statusCodeCell = row.children[2];
  154. var statusCodeElem = statusCodeCell.children[0];
  155. var durationCell = row.children[4];
  156. var profilerCell = row.children[5];
  157. if (request.error) {
  158. row.className = 'sf-ajax-request sf-ajax-request-error';
  159. methodCell.className = 'sf-ajax-request-error';
  160. successStreak = 0;
  161. } else {
  162. row.className = 'sf-ajax-request sf-ajax-request-ok';
  163. successStreak++;
  164. }
  165. if (request.statusCode) {
  166. if (request.statusCode < 300) {
  167. statusCodeElem.setAttribute('class', 'sf-toolbar-status');
  168. } else if (request.statusCode < 400) {
  169. statusCodeElem.setAttribute('class', 'sf-toolbar-status sf-toolbar-status-yellow');
  170. } else {
  171. statusCodeElem.setAttribute('class', 'sf-toolbar-status sf-toolbar-status-red');
  172. }
  173. statusCodeElem.textContent = request.statusCode;
  174. } else {
  175. statusCodeElem.setAttribute('class', 'sf-toolbar-status sf-toolbar-status-red');
  176. }
  177. if (request.duration) {
  178. durationCell.textContent = request.duration + 'ms';
  179. }
  180. if (request.profilerUrl) {
  181. profilerCell.textContent = '';
  182. var profilerLink = document.createElement('a');
  183. profilerLink.setAttribute('href', request.profilerUrl);
  184. profilerLink.textContent = request.profile;
  185. profilerCell.appendChild(profilerLink);
  186. }
  187. renderAjaxRequests();
  188. };
  189. var addEventListener;
  190. var el = document.createElement('div');
  191. if (!('addEventListener' in el)) {
  192. addEventListener = function (element, eventName, callback) {
  193. element.attachEvent('on' + eventName, callback);
  194. };
  195. } else {
  196. addEventListener = function (element, eventName, callback) {
  197. element.addEventListener(eventName, callback, false);
  198. };
  199. }
  200. {% if excluded_ajax_paths is defined %}
  201. if (window.fetch && window.fetch.polyfill === undefined) {
  202. var oldFetch = window.fetch;
  203. window.fetch = function () {
  204. var promise = oldFetch.apply(this, arguments);
  205. var url = arguments[0];
  206. var params = arguments[1];
  207. var paramType = Object.prototype.toString.call(arguments[0]);
  208. if (paramType === '[object Request]') {
  209. url = arguments[0].url;
  210. params = {
  211. method: arguments[0].method,
  212. credentials: arguments[0].credentials,
  213. headers: arguments[0].headers,
  214. mode: arguments[0].mode,
  215. redirect: arguments[0].redirect
  216. };
  217. }
  218. if (!url.match(new RegExp({{ excluded_ajax_paths|json_encode|raw }}))) {
  219. var method = 'GET';
  220. if (params && params.method !== undefined) {
  221. method = params.method;
  222. }
  223. var stackElement = {
  224. error: false,
  225. url: url,
  226. method: method,
  227. type: 'fetch',
  228. start: new Date()
  229. };
  230. var idx = requestStack.push(stackElement) - 1;
  231. promise.then(function (r) {
  232. stackElement.duration = new Date() - stackElement.start;
  233. stackElement.error = r.status < 200 || r.status >= 400;
  234. stackElement.statusCode = r.status;
  235. stackElement.profile = r.headers.get('x-debug-token');
  236. stackElement.profilerUrl = r.headers.get('x-debug-token-link');
  237. finishAjaxRequest(idx);
  238. }, function (e){
  239. stackElement.error = true;
  240. finishAjaxRequest(idx);
  241. });
  242. startAjaxRequest(idx);
  243. }
  244. return promise;
  245. };
  246. }
  247. if (window.XMLHttpRequest && XMLHttpRequest.prototype.addEventListener) {
  248. var proxied = XMLHttpRequest.prototype.open;
  249. XMLHttpRequest.prototype.open = function(method, url, async, user, pass) {
  250. var self = this;
  251. /* prevent logging AJAX calls to static and inline files, like templates */
  252. var path = url;
  253. if (url.substr(0, 1) === '/') {
  254. if (0 === url.indexOf('{{ request.basePath|e('js') }}')) {
  255. path = url.substr({{ request.basePath|length }});
  256. }
  257. }
  258. else if (0 === url.indexOf('{{ (request.schemeAndHttpHost ~ request.basePath)|e('js') }}')) {
  259. path = url.substr({{ (request.schemeAndHttpHost ~ request.basePath)|length }});
  260. }
  261. if (!path.match(new RegExp({{ excluded_ajax_paths|json_encode|raw }}))) {
  262. var stackElement = {
  263. error: false,
  264. url: url,
  265. method: method,
  266. type: 'xhr',
  267. start: new Date()
  268. };
  269. var idx = requestStack.push(stackElement) - 1;
  270. this.addEventListener('readystatechange', function() {
  271. if (self.readyState == 4) {
  272. stackElement.duration = new Date() - stackElement.start;
  273. stackElement.error = self.status < 200 || self.status >= 400;
  274. stackElement.statusCode = self.status;
  275. extractHeaders(self, stackElement);
  276. finishAjaxRequest(idx);
  277. }
  278. }, false);
  279. startAjaxRequest(idx);
  280. }
  281. proxied.apply(this, Array.prototype.slice.call(arguments));
  282. };
  283. }
  284. {% endif %}
  285. return {
  286. hasClass: hasClass,
  287. removeClass: removeClass,
  288. addClass: addClass,
  289. toggleClass: toggleClass,
  290. getPreference: getPreference,
  291. setPreference: setPreference,
  292. addEventListener: addEventListener,
  293. request: request,
  294. renderAjaxRequests: renderAjaxRequests,
  295. load: function(selector, url, onSuccess, onError, options) {
  296. var el = document.getElementById(selector);
  297. if (el && el.getAttribute('data-sfurl') !== url) {
  298. request(
  299. url,
  300. function(xhr) {
  301. el.innerHTML = xhr.responseText;
  302. el.setAttribute('data-sfurl', url);
  303. removeClass(el, 'loading');
  304. for (var i = 0; i < requestStack.length; i++) {
  305. startAjaxRequest(i);
  306. if (requestStack[i].duration) {
  307. finishAjaxRequest(i);
  308. }
  309. }
  310. (onSuccess || noop)(xhr, el);
  311. },
  312. function(xhr) { (onError || noop)(xhr, el); },
  313. '',
  314. options
  315. );
  316. }
  317. return this;
  318. },
  319. toggle: function(selector, elOn, elOff) {
  320. var tmp = elOn.style.display,
  321. el = document.getElementById(selector);
  322. elOn.style.display = elOff.style.display;
  323. elOff.style.display = tmp;
  324. if (el) {
  325. el.style.display = 'none' === tmp ? 'none' : 'block';
  326. }
  327. return this;
  328. },
  329. createTabs: function() {
  330. var tabGroups = document.querySelectorAll('.sf-tabs');
  331. /* create the tab navigation for each group of tabs */
  332. for (var i = 0; i < tabGroups.length; i++) {
  333. var tabs = tabGroups[i].querySelectorAll('.tab');
  334. var tabNavigation = document.createElement('ul');
  335. tabNavigation.className = 'tab-navigation';
  336. for (var j = 0; j < tabs.length; j++) {
  337. var tabId = 'tab-' + i + '-' + j;
  338. var tabTitle = tabs[j].querySelector('.tab-title').innerHTML;
  339. var tabNavigationItem = document.createElement('li');
  340. tabNavigationItem.setAttribute('data-tab-id', tabId);
  341. if (j == 0) { addClass(tabNavigationItem, 'active'); }
  342. if (hasClass(tabs[j], 'disabled')) { addClass(tabNavigationItem, 'disabled'); }
  343. tabNavigationItem.innerHTML = tabTitle;
  344. tabNavigation.appendChild(tabNavigationItem);
  345. var tabContent = tabs[j].querySelector('.tab-content');
  346. tabContent.parentElement.setAttribute('id', tabId);
  347. }
  348. tabGroups[i].insertBefore(tabNavigation, tabGroups[i].firstChild);
  349. }
  350. /* display the active tab and add the 'click' event listeners */
  351. for (i = 0; i < tabGroups.length; i++) {
  352. tabNavigation = tabGroups[i].querySelectorAll('.tab-navigation li');
  353. for (j = 0; j < tabNavigation.length; j++) {
  354. tabId = tabNavigation[j].getAttribute('data-tab-id');
  355. document.getElementById(tabId).querySelector('.tab-title').className = 'hidden';
  356. if (hasClass(tabNavigation[j], 'active')) {
  357. document.getElementById(tabId).className = 'block';
  358. } else {
  359. document.getElementById(tabId).className = 'hidden';
  360. }
  361. tabNavigation[j].addEventListener('click', function(e) {
  362. var activeTab = e.target || e.srcElement;
  363. /* needed because when the tab contains HTML contents, user can click */
  364. /* on any of those elements instead of their parent '<li>' element */
  365. while (activeTab.tagName.toLowerCase() !== 'li') {
  366. activeTab = activeTab.parentNode;
  367. }
  368. /* get the full list of tabs through the parent of the active tab element */
  369. var tabNavigation = activeTab.parentNode.children;
  370. for (var k = 0; k < tabNavigation.length; k++) {
  371. var tabId = tabNavigation[k].getAttribute('data-tab-id');
  372. document.getElementById(tabId).className = 'hidden';
  373. removeClass(tabNavigation[k], 'active');
  374. }
  375. addClass(activeTab, 'active');
  376. var activeTabId = activeTab.getAttribute('data-tab-id');
  377. document.getElementById(activeTabId).className = 'block';
  378. });
  379. }
  380. }
  381. },
  382. createToggles: function() {
  383. var toggles = document.querySelectorAll('.sf-toggle');
  384. for (var i = 0; i < toggles.length; i++) {
  385. var elementSelector = toggles[i].getAttribute('data-toggle-selector');
  386. var element = document.querySelector(elementSelector);
  387. addClass(element, 'sf-toggle-content');
  388. if (toggles[i].hasAttribute('data-toggle-initial') && toggles[i].getAttribute('data-toggle-initial') == 'display') {
  389. addClass(toggles[i], 'sf-toggle-on');
  390. addClass(element, 'sf-toggle-visible');
  391. } else {
  392. addClass(toggles[i], 'sf-toggle-off');
  393. addClass(element, 'sf-toggle-hidden');
  394. }
  395. addEventListener(toggles[i], 'click', function(e) {
  396. e.preventDefault();
  397. if ('' !== window.getSelection().toString()) {
  398. /* Don't do anything on text selection */
  399. return;
  400. }
  401. var toggle = e.target || e.srcElement;
  402. /* needed because when the toggle contains HTML contents, user can click */
  403. /* on any of those elements instead of their parent '.sf-toggle' element */
  404. while (!hasClass(toggle, 'sf-toggle')) {
  405. toggle = toggle.parentNode;
  406. }
  407. var element = document.querySelector(toggle.getAttribute('data-toggle-selector'));
  408. toggleClass(toggle, 'sf-toggle-on');
  409. toggleClass(toggle, 'sf-toggle-off');
  410. toggleClass(element, 'sf-toggle-hidden');
  411. toggleClass(element, 'sf-toggle-visible');
  412. /* the toggle doesn't change its contents when clicking on it */
  413. if (!toggle.hasAttribute('data-toggle-alt-content')) {
  414. return;
  415. }
  416. if (!toggle.hasAttribute('data-toggle-original-content')) {
  417. toggle.setAttribute('data-toggle-original-content', toggle.innerHTML);
  418. }
  419. var currentContent = toggle.innerHTML;
  420. var originalContent = toggle.getAttribute('data-toggle-original-content');
  421. var altContent = toggle.getAttribute('data-toggle-alt-content');
  422. toggle.innerHTML = currentContent !== altContent ? altContent : originalContent;
  423. });
  424. }
  425. /* Prevents from disallowing clicks on links inside toggles */
  426. var toggleLinks = document.querySelectorAll('.sf-toggle a');
  427. for (var i = 0; i < toggleLinks.length; i++) {
  428. addEventListener(toggleLinks[i], 'click', function(e) {
  429. e.stopPropagation();
  430. });
  431. }
  432. }
  433. };
  434. })();
  435. Sfjs.addEventListener(document, 'DOMContentLoaded', function() {
  436. Sfjs.createTabs();
  437. Sfjs.createToggles();
  438. });
  439. /*]]>*/</script>