Last active
July 6, 2022 19:09
-
-
Save patrick-mcdougle/dcd5435aecd096fdc7c367641c7b08d1 to your computer and use it in GitHub Desktop.
Overwrite JS Template
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<script{% if csp_script_nonce is defined and csp_script_nonce %} nonce={{ csp_script_nonce }}{% endif %}>/*<![CDATA[*/ | |
{# Caution: the contents of this file are processed by Twig before loading | |
them as JavaScript source code. Always use '/*' comments instead | |
of '//' comments to avoid impossible-to-debug side-effects #} | |
Sfjs = (function() { | |
"use strict"; | |
var classListIsSupported = 'classList' in document.documentElement; | |
if (classListIsSupported) { | |
var hasClass = function (el, cssClass) { return el.classList.contains(cssClass); }; | |
var removeClass = function(el, cssClass) { el.classList.remove(cssClass); }; | |
var addClass = function(el, cssClass) { el.classList.add(cssClass); }; | |
var toggleClass = function(el, cssClass) { el.classList.toggle(cssClass); }; | |
} else { | |
var hasClass = function (el, cssClass) { return el.className.match(new RegExp('\\b' + cssClass + '\\b')); }; | |
var removeClass = function(el, cssClass) { el.className = el.className.replace(new RegExp('\\b' + cssClass + '\\b'), ' '); }; | |
var addClass = function(el, cssClass) { if (!hasClass(el, cssClass)) { el.className += " " + cssClass; } }; | |
var toggleClass = function(el, cssClass) { hasClass(el, cssClass) ? removeClass(el, cssClass) : addClass(el, cssClass); }; | |
} | |
var noop = function() {}; | |
var profilerStorageKey = 'sf2/profiler/'; | |
var request = function(url, onSuccess, onError, payload, options) { | |
var xhr = window.XMLHttpRequest ? new XMLHttpRequest() : new ActiveXObject('Microsoft.XMLHTTP'); | |
options = options || {}; | |
options.maxTries = options.maxTries || 0; | |
xhr.open(options.method || 'GET', url, true); | |
xhr.setRequestHeader('X-Requested-With', 'XMLHttpRequest'); | |
xhr.onreadystatechange = function(state) { | |
if (4 !== xhr.readyState) { | |
return null; | |
} | |
if (xhr.status == 404 && options.maxTries > 1) { | |
setTimeout(function(){ | |
options.maxTries--; | |
request(url, onSuccess, onError, payload, options); | |
}, 500); | |
return null; | |
} | |
if (200 === xhr.status) { | |
(onSuccess || noop)(xhr); | |
} else { | |
(onError || noop)(xhr); | |
} | |
}; | |
xhr.send(payload || ''); | |
}; | |
var getPreference = function(name) { | |
if (!window.localStorage) { | |
return null; | |
} | |
return localStorage.getItem(profilerStorageKey + name); | |
}; | |
var setPreference = function(name, value) { | |
if (!window.localStorage) { | |
return null; | |
} | |
localStorage.setItem(profilerStorageKey + name, value); | |
}; | |
var requestStack = []; | |
var extractHeaders = function(xhr, stackElement) { | |
/* Here we avoid to call xhr.getResponseHeader in order to */ | |
/* prevent polluting the console with CORS security errors */ | |
var allHeaders = xhr.getAllResponseHeaders(); | |
var ret; | |
if (ret = allHeaders.match(/^x-debug-token:\s+(.*)$/im)) { | |
stackElement.profile = ret[1]; | |
} | |
if (ret = allHeaders.match(/^x-debug-token-link:\s+(.*)$/im)) { | |
stackElement.profilerUrl = ret[1]; | |
} | |
}; | |
var successStreak = 4; | |
var pendingRequests = 0; | |
var startAjaxRequest = function(index) { | |
var request = requestStack[index]; | |
pendingRequests++; | |
var row = document.createElement('tr'); | |
request.DOMNode = row; | |
var tbody = document.querySelectorAll('.sf-toolbar-ajax-request-list')[0]; | |
if (!tbody) { | |
return; | |
} | |
var methodCell = document.createElement('td'); | |
methodCell.textContent = request.method; | |
row.appendChild(methodCell); | |
var statusCodeCell = document.createElement('td'); | |
var statusCode = document.createElement('span'); | |
statusCode.textContent = '-'; | |
statusCodeCell.appendChild(statusCode); | |
row.appendChild(statusCodeCell); | |
var pathCell = document.createElement('td'); | |
pathCell.className = 'sf-ajax-request-url'; | |
if ('GET' === request.method) { | |
var pathLink = document.createElement('a'); | |
pathLink.setAttribute('href', request.url); | |
pathLink.textContent = request.url; | |
pathCell.appendChild(pathLink); | |
} else { | |
pathCell.textContent = request.url; | |
} | |
pathCell.setAttribute('title', request.url); | |
row.appendChild(pathCell); | |
var durationCell = document.createElement('td'); | |
durationCell.className = 'sf-ajax-request-duration'; | |
durationCell.textContent = '-'; | |
row.appendChild(durationCell); | |
var profilerCell = document.createElement('td'); | |
profilerCell.textContent = 'n/a'; | |
row.appendChild(profilerCell); | |
row.className = 'sf-ajax-request sf-ajax-request-loading'; | |
tbody.insertBefore(row, tbody.firstChild); | |
renderAjaxRequests(); | |
}; | |
var finishAjaxRequest = function(index) { | |
var request = requestStack[index]; | |
pendingRequests--; | |
var row = request.DOMNode; | |
/* Unpack the children from the row */ | |
var methodCell = row.children[0]; | |
var statusCodeCell = row.children[1]; | |
var statusCodeElem = statusCodeCell.children[0]; | |
var durationCell = row.children[3]; | |
var profilerCell = row.children[4]; | |
if (request.error) { | |
row.className = 'sf-ajax-request sf-ajax-request-error'; | |
methodCell.className = 'sf-ajax-request-error'; | |
successStreak = 0; | |
} else { | |
row.className = 'sf-ajax-request sf-ajax-request-ok'; | |
successStreak++; | |
} | |
if (request.statusCode) { | |
if (request.statusCode < 300) { | |
statusCodeElem.setAttribute('class', 'sf-toolbar-status'); | |
} else if (request.statusCode < 400) { | |
statusCodeElem.setAttribute('class', 'sf-toolbar-status sf-toolbar-status-yellow'); | |
} else { | |
statusCodeElem.setAttribute('class', 'sf-toolbar-status sf-toolbar-status-red'); | |
} | |
statusCodeElem.textContent = request.statusCode; | |
} | |
if (request.duration) { | |
durationCell.textContent = request.duration + 'ms'; | |
} | |
if (request.profilerUrl) { | |
profilerCell.textContent = ''; | |
var profilerLink = document.createElement('a'); | |
profilerLink.setAttribute('href', request.profilerUrl); | |
profilerLink.textContent = request.profile; | |
profilerCell.appendChild(profilerLink); | |
} | |
renderAjaxRequests(); | |
}; | |
var renderAjaxRequests = function() { | |
var requestCounter = document.querySelectorAll('.sf-toolbar-ajax-requests')[0]; | |
if (!requestCounter) { | |
return; | |
} | |
requestCounter.textContent = requestStack.length; | |
requestCounter.className = 'sf-toolbar-ajax-requests sf-toolbar-value'; | |
var infoSpan = document.querySelectorAll(".sf-toolbar-ajax-info")[0]; | |
if (infoSpan) { | |
infoSpan.textContent = requestStack.length + ' AJAX request' + (requestStack.length > 1 ? 's' : ''); | |
} | |
var ajaxToolbarPanel = document.querySelector('.sf-toolbar-block-ajax'); | |
if (requestStack.length) { | |
ajaxToolbarPanel.style.display = 'block'; | |
} else { | |
ajaxToolbarPanel.style.display = 'none'; | |
} | |
if (pendingRequests > 0) { | |
addClass(ajaxToolbarPanel, 'sf-ajax-request-loading'); | |
} else if (successStreak < 4) { | |
addClass(ajaxToolbarPanel, 'sf-toolbar-status-red'); | |
removeClass(ajaxToolbarPanel, 'sf-ajax-request-loading'); | |
} else { | |
removeClass(ajaxToolbarPanel, 'sf-ajax-request-loading'); | |
removeClass(ajaxToolbarPanel, 'sf-toolbar-status-red'); | |
} | |
}; | |
var addEventListener; | |
var el = document.createElement('div'); | |
if (!('addEventListener' in el)) { | |
addEventListener = function (element, eventName, callback) { | |
element.attachEvent('on' + eventName, callback); | |
}; | |
} else { | |
addEventListener = function (element, eventName, callback) { | |
element.addEventListener(eventName, callback, false); | |
}; | |
} | |
{% if excluded_ajax_paths is defined %} | |
if (window.fetch && window.fetch.polyfill === undefined) { | |
var oldFetch = window.fetch; | |
window.fetch = function () { | |
var promise = oldFetch.apply(this, arguments); | |
if (!arguments[0].match(new RegExp({{ excluded_ajax_paths|json_encode|raw }}))) { | |
var method = 'GET'; | |
if (arguments[1] && arguments[1].method !== undefined) { | |
method = arguments[1].method; | |
} | |
var stackElement = { | |
loading: true, | |
error: false, | |
url: arguments[0], | |
method: method, | |
type: 'fetch', | |
start: new Date() | |
}; | |
var idx = requestStack.push(stackElement) - 1; | |
promise.then(function (r) { | |
stackElement.duration = new Date() - stackElement.start; | |
stackElement.loading = false; | |
stackElement.error = r.status < 200 || r.status >= 400; | |
stackElement.statusCode = r.status; | |
stackElement.profile = r.headers.get('x-debug-token'); | |
stackElement.profilerUrl = r.headers.get('x-debug-token-link'); | |
finishAjaxRequest(idx); | |
}, function (e){ | |
stackElement.loading = false; | |
stackElement.error = true; | |
}); | |
startAjaxRequest(idx); | |
} | |
return promise; | |
}; | |
} | |
if (window.XMLHttpRequest && XMLHttpRequest.prototype.addEventListener) { | |
var proxied = XMLHttpRequest.prototype.open; | |
XMLHttpRequest.prototype.open = function(method, url, async, user, pass) { | |
var self = this; | |
/* prevent logging AJAX calls to static and inline files, like templates */ | |
var path = url; | |
if (url.substr(0, 1) === '/') { | |
if (0 === url.indexOf('{{ request.basePath|e('js') }}')) { | |
path = url.substr({{ request.basePath|length }}); | |
} | |
} | |
else if (0 === url.indexOf('{{ (request.schemeAndHttpHost ~ request.basePath)|e('js') }}')) { | |
path = url.substr({{ (request.schemeAndHttpHost ~ request.basePath)|length }}); | |
} | |
if (!path.match(new RegExp({{ excluded_ajax_paths|json_encode|raw }}))) { | |
var stackElement = { | |
loading: true, | |
error: false, | |
url: url, | |
method: method, | |
type: 'xhr', | |
start: new Date() | |
}; | |
var idx = requestStack.push(stackElement) - 1; | |
this.addEventListener('readystatechange', function() { | |
if (self.readyState == 4) { | |
stackElement.duration = new Date() - stackElement.start; | |
stackElement.loading = false; | |
stackElement.error = self.status < 200 || self.status >= 400; | |
stackElement.statusCode = self.status; | |
extractHeaders(self, stackElement); | |
finishAjaxRequest(idx); | |
} | |
}, false); | |
startAjaxRequest(idx); | |
} | |
proxied.apply(this, Array.prototype.slice.call(arguments)); | |
}; | |
} | |
{% endif %} | |
return { | |
hasClass: hasClass, | |
removeClass: removeClass, | |
addClass: addClass, | |
toggleClass: toggleClass, | |
getPreference: getPreference, | |
setPreference: setPreference, | |
addEventListener: addEventListener, | |
request: request, | |
renderAjaxRequests: renderAjaxRequests, | |
load: function(selector, url, onSuccess, onError, options) { | |
var el = document.getElementById(selector); | |
if (el && el.getAttribute('data-sfurl') !== url) { | |
request( | |
url, | |
function(xhr) { | |
el.innerHTML = xhr.responseText; | |
el.setAttribute('data-sfurl', url); | |
removeClass(el, 'loading'); | |
(onSuccess || noop)(xhr, el); | |
}, | |
function(xhr) { (onError || noop)(xhr, el); }, | |
'', | |
options | |
); | |
} | |
return this; | |
}, | |
toggle: function(selector, elOn, elOff) { | |
var tmp = elOn.style.display, | |
el = document.getElementById(selector); | |
elOn.style.display = elOff.style.display; | |
elOff.style.display = tmp; | |
if (el) { | |
el.style.display = 'none' === tmp ? 'none' : 'block'; | |
} | |
return this; | |
}, | |
createTabs: function() { | |
var tabGroups = document.querySelectorAll('.sf-tabs'); | |
/* create the tab navigation for each group of tabs */ | |
for (var i = 0; i < tabGroups.length; i++) { | |
var tabs = tabGroups[i].querySelectorAll('.tab'); | |
var tabNavigation = document.createElement('ul'); | |
tabNavigation.className = 'tab-navigation'; | |
for (var j = 0; j < tabs.length; j++) { | |
var tabId = 'tab-' + i + '-' + j; | |
var tabTitle = tabs[j].querySelector('.tab-title').innerHTML; | |
var tabNavigationItem = document.createElement('li'); | |
tabNavigationItem.setAttribute('data-tab-id', tabId); | |
if (j == 0) { addClass(tabNavigationItem, 'active'); } | |
if (hasClass(tabs[j], 'disabled')) { addClass(tabNavigationItem, 'disabled'); } | |
tabNavigationItem.innerHTML = tabTitle; | |
tabNavigation.appendChild(tabNavigationItem); | |
var tabContent = tabs[j].querySelector('.tab-content'); | |
tabContent.parentElement.setAttribute('id', tabId); | |
} | |
tabGroups[i].insertBefore(tabNavigation, tabGroups[i].firstChild); | |
} | |
/* display the active tab and add the 'click' event listeners */ | |
for (i = 0; i < tabGroups.length; i++) { | |
tabNavigation = tabGroups[i].querySelectorAll('.tab-navigation li'); | |
for (j = 0; j < tabNavigation.length; j++) { | |
tabId = tabNavigation[j].getAttribute('data-tab-id'); | |
document.getElementById(tabId).querySelector('.tab-title').className = 'hidden'; | |
if (hasClass(tabNavigation[j], 'active')) { | |
document.getElementById(tabId).className = 'block'; | |
} else { | |
document.getElementById(tabId).className = 'hidden'; | |
} | |
tabNavigation[j].addEventListener('click', function(e) { | |
var activeTab = e.target || e.srcElement; | |
/* needed because when the tab contains HTML contents, user can click */ | |
/* on any of those elements instead of their parent '<li>' element */ | |
while (activeTab.tagName.toLowerCase() !== 'li') { | |
activeTab = activeTab.parentNode; | |
} | |
/* get the full list of tabs through the parent of the active tab element */ | |
var tabNavigation = activeTab.parentNode.children; | |
for (var k = 0; k < tabNavigation.length; k++) { | |
var tabId = tabNavigation[k].getAttribute('data-tab-id'); | |
document.getElementById(tabId).className = 'hidden'; | |
removeClass(tabNavigation[k], 'active'); | |
} | |
addClass(activeTab, 'active'); | |
var activeTabId = activeTab.getAttribute('data-tab-id'); | |
document.getElementById(activeTabId).className = 'block'; | |
}); | |
} | |
} | |
}, | |
createToggles: function() { | |
var toggles = document.querySelectorAll('.sf-toggle'); | |
for (var i = 0; i < toggles.length; i++) { | |
var elementSelector = toggles[i].getAttribute('data-toggle-selector'); | |
var element = document.querySelector(elementSelector); | |
addClass(element, 'sf-toggle-content'); | |
if (toggles[i].hasAttribute('data-toggle-initial') && toggles[i].getAttribute('data-toggle-initial') == 'display') { | |
addClass(element, 'sf-toggle-visible'); | |
} else { | |
addClass(element, 'sf-toggle-hidden'); | |
} | |
addEventListener(toggles[i], 'click', function(e) { | |
e.preventDefault(); | |
var toggle = e.target || e.srcElement; | |
/* needed because when the toggle contains HTML contents, user can click */ | |
/* on any of those elements instead of their parent '.sf-toggle' element */ | |
while (!hasClass(toggle, 'sf-toggle')) { | |
toggle = toggle.parentNode; | |
} | |
var element = document.querySelector(toggle.getAttribute('data-toggle-selector')); | |
toggleClass(element, 'sf-toggle-hidden'); | |
toggleClass(element, 'sf-toggle-visible'); | |
/* the toggle doesn't change its contents when clicking on it */ | |
if (!toggle.hasAttribute('data-toggle-alt-content')) { | |
return; | |
} | |
if (!toggle.hasAttribute('data-toggle-original-content')) { | |
toggle.setAttribute('data-toggle-original-content', toggle.innerHTML); | |
} | |
var currentContent = toggle.innerHTML; | |
var originalContent = toggle.getAttribute('data-toggle-original-content'); | |
var altContent = toggle.getAttribute('data-toggle-alt-content'); | |
toggle.innerHTML = currentContent !== altContent ? altContent : originalContent; | |
}); | |
} | |
} | |
}; | |
})(); | |
Sfjs.addEventListener(window, 'load', function() { | |
Sfjs.createTabs(); | |
Sfjs.createToggles(); | |
Sfjs.renderAjaxRequests(); | |
}); | |
/*]]>*/</script> |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment