diff --git a/src/Symfony/Bundle/WebProfilerBundle/Resources/views/Profiler/base_js.html.twig b/src/Symfony/Bundle/WebProfilerBundle/Resources/views/Profiler/base_js.html.twig index 6320b7fa69..9f823ad228 100644 --- a/src/Symfony/Bundle/WebProfilerBundle/Resources/views/Profiler/base_js.html.twig +++ b/src/Symfony/Bundle/WebProfilerBundle/Resources/views/Profiler/base_js.html.twig @@ -96,131 +96,135 @@ } }; + var successStreak = 4; + var pendingRequests = 0; var renderAjaxRequests = function() { - var requestCounter = document.querySelectorAll('.sf-toolbar-ajax-requests'); - if (!requestCounter.length) { + 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'); - var tbodies = document.querySelectorAll('.sf-toolbar-ajax-request-list'); - var state = 'ok'; - if (tbodies.length) { - var tbody = tbodies[0]; - - var rows = document.createDocumentFragment(); - - if (requestStack.length) { - for (var i = 0; i < requestStack.length; i++) { - var request = requestStack[i]; - - var row = document.createElement('tr'); - rows.insertBefore(row, rows.firstChild); - - var methodCell = document.createElement('td'); - if (request.error) { - methodCell.className = 'sf-ajax-request-error'; - } - methodCell.textContent = request.method; - row.appendChild(methodCell); - - var typeCell = document.createElement('td'); - typeCell.textContent = request.type; - row.appendChild(typeCell); - - var statusCodeCell = document.createElement('td'); - var statusCode = document.createElement('span'); - if (request.statusCode < 300) { - statusCode.setAttribute('class', 'sf-toolbar-status'); - } else if (request.statusCode < 400) { - statusCode.setAttribute('class', 'sf-toolbar-status sf-toolbar-status-yellow'); - } else { - statusCode.setAttribute('class', 'sf-toolbar-status sf-toolbar-status-red'); - } - statusCode.textContent = request.statusCode || '-'; - 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'; - - if (request.duration) { - durationCell.textContent = request.duration + "ms"; - } else { - durationCell.textContent = '-'; - } - row.appendChild(durationCell); - - row.appendChild(document.createTextNode(' ')); - var profilerCell = document.createElement('td'); - - if (request.profilerUrl) { - var profilerLink = document.createElement('a'); - profilerLink.setAttribute('href', request.profilerUrl); - profilerLink.textContent = request.profile; - profilerCell.appendChild(profilerLink); - } else { - profilerCell.textContent = 'n/a'; - } - - row.appendChild(profilerCell); - - var requestState = 'ok'; - if (request.error) { - requestState = 'error'; - if (state != "loading" && i > requestStack.length - 4) { - state = 'error'; - } - } else if (request.loading) { - requestState = 'loading'; - state = 'loading'; - } - row.className = 'sf-ajax-request sf-ajax-request-' + requestState; - } - - var infoSpan = document.querySelectorAll(".sf-toolbar-ajax-info")[0]; - var children = collectionToArray(tbody.children); - for (var i = 0; i < children.length; i++) { - tbody.removeChild(children[i]); - } - tbody.appendChild(rows); - - if (infoSpan) { - infoSpan.textContent = requestStack.length + ' AJAX request' + (requestStack.length > 1 ? 's' : ''); - } - - ajaxToolbarPanel.style.display = 'block'; - } else { - ajaxToolbarPanel.style.display = 'none'; - } - } - - requestCounter[0].textContent = requestStack.length; - - requestCounter[0].className = 'sf-toolbar-ajax-requests sf-toolbar-value'; - - if (state == 'ok') { - Sfjs.removeClass(ajaxToolbarPanel, 'sf-ajax-request-loading'); - Sfjs.removeClass(ajaxToolbarPanel, 'sf-toolbar-status-red'); - } else if (state == 'error') { - Sfjs.addClass(ajaxToolbarPanel, 'sf-toolbar-status-red'); - Sfjs.removeClass(ajaxToolbarPanel, 'sf-ajax-request-loading'); + if (requestStack.length) { + ajaxToolbarPanel.style.display = 'block'; } else { - Sfjs.addClass(ajaxToolbarPanel, 'sf-ajax-request-loading'); + 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 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 typeCell = document.createElement('td'); + typeCell.textContent = request.type; + row.appendChild(typeCell); + + 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[2]; + var statusCodeElem = statusCodeCell.children[0]; + var durationCell = row.children[4]; + var profilerCell = row.children[5]; + + 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 addEventListener; @@ -237,91 +241,86 @@ } {% 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; - } + 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 stackElement = { + error: false, + url: arguments[0], + method: method, + type: 'fetch', + start: new Date() + }; - requestStack.push(stackElement); - promise.then(function (r) { + var idx = requestStack.push(stackElement) - 1; + promise.then(function (r) { + stackElement.duration = new Date() - stackElement.start; + 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.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 = { + 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 = 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'); - renderAjaxRequests(); - }, function (e){ - stackElement.loading = false; - stackElement.error = true; - }); - renderAjaxRequests(); - } + stackElement.error = self.status < 200 || self.status >= 400; + stackElement.statusCode = self.status; + extractHeaders(self, stackElement); - 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 }}); + finishAjaxRequest(idx); } - } - else if (0 === url.indexOf('{{ (request.schemeAndHttpHost ~ request.basePath)|e('js') }}')) { - path = url.substr({{ (request.schemeAndHttpHost ~ request.basePath)|length }}); - } + }, false); - 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() - }; + startAjaxRequest(idx); + } - requestStack.push(stackElement); - - 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); - - renderAjaxRequests(); - } - }, false); - - renderAjaxRequests(); - } - - proxied.apply(this, Array.prototype.slice.call(arguments)); - }; - } + proxied.apply(this, Array.prototype.slice.call(arguments)); + }; + } {% endif %} return { @@ -497,6 +496,7 @@ Sfjs.addEventListener(window, 'load', function() { Sfjs.createTabs(); Sfjs.createToggles(); + Sfjs.renderAjaxRequests(); }); /*]]>*/