瀏覽代碼

Replace angular templating by homemade for speed purpose

Gaël Métais 10 年之前
父節點
當前提交
0947724245
共有 4 個文件被更改,包括 563 次插入441 次删除
  1. 7 0
      front/src/css/timeline.css
  2. 547 386
      front/src/js/directives/offendersDirectives.js
  3. 8 0
      front/src/less/timeline.less
  4. 1 55
      front/src/views/timeline.html

+ 7 - 0
front/src/css/timeline.css

@@ -212,6 +212,9 @@
 .table .child span:hover {
 .table .child span:hover {
   background: #EBD8E2;
   background: #EBD8E2;
 }
 }
+.table .child span:hover div {
+  display: inline-block;
+}
 .table .child span:hover .childArgs {
 .table .child span:hover .childArgs {
   display: block;
   display: block;
   position: absolute;
   position: absolute;
@@ -245,6 +248,7 @@
   width: 0.8em;
   width: 0.8em;
 }
 }
 .detailsOverlay {
 .detailsOverlay {
+  display: none;
   position: absolute;
   position: absolute;
   right: 3em;
   right: 3em;
   top: -3em;
   top: -3em;
@@ -261,6 +265,9 @@
     width: 25em;
     width: 25em;
   }
   }
 }
 }
+.showDetails .detailsOverlay {
+  display: block;
+}
 .detailsOverlay .closeBtn {
 .detailsOverlay .closeBtn {
   position: absolute;
   position: absolute;
   top: 0.5em;
   top: 0.5em;

+ 547 - 386
front/src/js/directives/offendersDirectives.js

@@ -41,15 +41,15 @@
         };
         };
     });
     });
 
 
-    function getDomElementButtonHTML(obj) {
-        if (obj.tree) {
-            return '<div class="offenderButton opens">' + getDomElementButtonInnerHTML(obj) + '</div>';
+    function getDomElementButtonHTML(obj, onASingleLine) {
+        if (obj.tree && !onASingleLine) {
+            return '<div class="offenderButton opens">' + getDomElementButtonInnerHTML(obj, onASingleLine) + '</div>';
+        } else {
+            return '<div class="offenderButton">' + getDomElementButtonInnerHTML(obj, onASingleLine) + '</div>';
         }
         }
-
-        return '<div class="offenderButton">' + getDomElementButtonInnerHTML(obj) + '</div>';
     }
     }
 
 
-    function getDomElementButtonInnerHTML(obj) {
+    function getDomElementButtonInnerHTML(obj, onASingleLine) {
         if (obj.type === 'html' ||
         if (obj.type === 'html' ||
             obj.type === 'body' ||
             obj.type === 'body' ||
             obj.type === 'head' ||
             obj.type === 'head' ||
@@ -72,7 +72,7 @@
             html = 'Created element <b>' + obj.element + '</b>';
             html = 'Created element <b>' + obj.element + '</b>';
         }
         }
 
 
-        if (obj.tree) {
+        if (obj.tree && !onASingleLine) {
             html += getdomTreeHTML(obj.tree);
             html += getdomTreeHTML(obj.tree);
         }
         }
 
 
@@ -94,480 +94,641 @@
     });
     });
 
 
 
 
-    function getJQueryContextButtonHTML(context) {
+    function getJQueryContextButtonHTML(context, onASingleLine) {
         if (context.length === 0) {
         if (context.length === 0) {
             return '<span class="offenderButton">Empty jQuery object</span>';
             return '<span class="offenderButton">Empty jQuery object</span>';
         }
         }
 
 
         if (context.length === 1) {
         if (context.length === 1) {
-            return getDomElementButtonHTML(context.elements[0]);
+            return getDomElementButtonHTML(context.elements[0], onASingleLine);
         }
         }
 
 
-        var html = context.length + ' elements (' + getDomElementButtonHTML(context.elements[0]) + ', ' + getDomElementButtonHTML(context.elements[1]);
+        var html = context.length + ' elements (' + getDomElementButtonHTML(context.elements[0], onASingleLine) + ', ' + getDomElementButtonHTML(context.elements[1], onASingleLine);
         if (context.length === 3) {
         if (context.length === 3) {
-            html += ', ' + getDomElementButtonHTML(context.elements[0]);
+            html += ', ' + getDomElementButtonHTML(context.elements[0], onASingleLine);
         } else if (context.length > 3) {
         } else if (context.length > 3) {
             html += ' and ' + (context.length - 2) + ' more...';
             html += ' and ' + (context.length - 2) + ' more...';
         }
         }
         return html + '}';
         return html + '}';
     }
     }
 
 
-    offendersDirectives.directive('timelineParams', function() {
-        
-        function isJQuery(node) {
-            return node.data.type.indexOf('jQuery ') === 0;
-        }
+    function isJQuery(node) {
+        return node.data.type.indexOf('jQuery ') === 0;
+    }
 
 
-        function getNonJQueryHTML(node) {
-            var type = node.data.type;
+    function getNonJQueryHTML(node, onASingleLine) {
+        var type = node.data.type;
 
 
-            if (!node.data.callDetails) {
-                return '';
-            }
+        if (!node.data.callDetails) {
+            return '';
+        }
 
 
-            var args = node.data.callDetails.arguments;
-            var ctxt = node.data.callDetails.context;
+        var args = node.data.callDetails.arguments;
+        var ctxt = node.data.callDetails.context;
 
 
 
 
-            switch (type) {
-                case 'getElementById':
-                case 'createElement':
-                    return '<b>' + args[0] + '</b>';
+        switch (type) {
+            case 'getElementById':
+            case 'createElement':
+                return '<b>' + args[0] + '</b>';
 
 
-                case 'getElementsByClassName':
-                case 'getElementsByTagName':
-                case 'querySelector':
-                case 'querySelectorAll':
-                    return '<b>' + args[0] + '</b> on ' + getDomElementButtonHTML(ctxt.elements[0]);
+            case 'getElementsByClassName':
+            case 'getElementsByTagName':
+            case 'querySelector':
+            case 'querySelectorAll':
+                return '<b>' + args[0] + '</b> on ' + getDomElementButtonHTML(ctxt.elements[0], onASingleLine);
 
 
-                case 'appendChild':
-                    return 'append ' + getDomElementButtonHTML(args[0]) + ' to ' + getDomElementButtonHTML(ctxt.elements[0]);
+            case 'appendChild':
+                return 'append ' + getDomElementButtonHTML(args[0], onASingleLine) + ' to ' + getDomElementButtonHTML(ctxt.elements[0], onASingleLine);
 
 
-                case 'insertBefore':
-                    return 'insert' + getDomElementButtonHTML(args[0]) + ' into ' + getDomElementButtonHTML(ctxt.elements[0]) + ' before ' + getDomElementButtonHTML(args[1]);
+            case 'insertBefore':
+                return 'insert' + getDomElementButtonHTML(args[0], onASingleLine) + ' into ' + getDomElementButtonHTML(ctxt.elements[0], onASingleLine) + ' before ' + getDomElementButtonHTML(args[1], onASingleLine);
 
 
-                case 'addEventListener':
-                    return 'bind <b>' + args[0] + '</b> to ' + getDomElementButtonHTML(ctxt.elements[0]);
+            case 'addEventListener':
+                return 'bind <b>' + args[0] + '</b> to ' + getDomElementButtonHTML(ctxt.elements[0], onASingleLine);
 
 
-                case 'error':
-                    return args[0];
+            case 'error':
+                return args[0];
 
 
-                default:
-                    return '';
-            }
+            default:
+                return '';
         }
         }
+    }
 
 
-        function getJQueryHTML(node) {
-            var type = node.data.type;
-            var args = node.data.callDetails.arguments;
-            var ctxt = node.data.callDetails.context;
-            
-            // escape HTML in args
-            for (var i = 0 ; i < 4 ; i ++) {
-                if (args[i]) {
-                    args[i] = escapeHTML(args[i]);
-                }
+    function getJQueryHTML(node, onASingleLine) {
+        var type = node.data.type;
+        var args = node.data.callDetails.arguments;
+        var ctxt = node.data.callDetails.context;
+        
+        // escape HTML in args
+        for (var i = 0 ; i < 4 ; i ++) {
+            if (args[i]) {
+                args[i] = escapeHTML(args[i]);
             }
             }
+        }
 
 
-            if (type === 'jQuery loaded' || type === 'jQuery version change') {
-                return args[0];
-            }
+        if (type === 'jQuery loaded' || type === 'jQuery version change') {
+            return args[0];
+        }
 
 
-            switch (type) {
-                case 'jQuery - onDOMReady':
-                case 'jQuery - windowOnLoad':
-                    return '(function)';
+        switch (type) {
+            case 'jQuery - onDOMReady':
+            case 'jQuery - windowOnLoad':
+                return '(function)';
 
 
-                case 'jQuery - Sizzle call':
-                    return '<b>' + args[0] + '</b> on ' + getDomElementButtonHTML(ctxt.elements[0]);
+            case 'jQuery - Sizzle call':
+                return '<b>' + args[0] + '</b> on ' + getDomElementButtonHTML(ctxt.elements[0], onASingleLine);
 
 
-                case 'jQuery - find':
-                    if (ctxt && ctxt.length === 1 && ctxt.elements[0].type !== 'document') {
-                        return '<b>' + args[0] + '</b> on ' + getJQueryContextButtonHTML(ctxt);
-                    } else {
-                        return '<b>' + args[0] + '</b>';
-                    }
-                    break;
+            case 'jQuery - find':
+                if (ctxt && ctxt.length === 1 && ctxt.elements[0].type !== 'document') {
+                    return '<b>' + args[0] + '</b> on ' + getJQueryContextButtonHTML(ctxt, onASingleLine);
+                } else {
+                    return '<b>' + args[0] + '</b>';
+                }
+                break;
 
 
-                case 'jQuery - html':
-                    if (args[0] !== undefined) {
-                        return 'set content "<b>' + args[0] + '</b>" to ' + getJQueryContextButtonHTML(ctxt);
-                    } else {
-                        return 'get content from ' + getJQueryContextButtonHTML(ctxt);
-                    }
-                    break;
+            case 'jQuery - html':
+                if (args[0] !== undefined) {
+                    return 'set content "<b>' + args[0] + '</b>" to ' + getJQueryContextButtonHTML(ctxt, onASingleLine);
+                } else {
+                    return 'get content from ' + getJQueryContextButtonHTML(ctxt, onASingleLine);
+                }
+                break;
 
 
-                case 'jQuery - append':
-                    return 'append ' + joinArgs(args) + ' to ' + getJQueryContextButtonHTML(ctxt);
+            case 'jQuery - append':
+                return 'append ' + joinArgs(args) + ' to ' + getJQueryContextButtonHTML(ctxt, onASingleLine);
 
 
-                case 'jQuery - appendTo':
-                    return 'append' + getJQueryContextButtonHTML(ctxt) + ' to <b>' + args[0] + '</b>';
+            case 'jQuery - appendTo':
+                return 'append' + getJQueryContextButtonHTML(ctxt, onASingleLine) + ' to <b>' + args[0] + '</b>';
 
 
-                case 'jQuery - prepend':
-                    return 'prepend ' + joinArgs(args) + ' to ' + getJQueryContextButtonHTML(ctxt);
+            case 'jQuery - prepend':
+                return 'prepend ' + joinArgs(args) + ' to ' + getJQueryContextButtonHTML(ctxt, onASingleLine);
 
 
-                case 'jQuery - prependTo':
-                    return 'prepend ' + getJQueryContextButtonHTML(ctxt) + ' to <b>' + args[0] + '</b>';
+            case 'jQuery - prependTo':
+                return 'prepend ' + getJQueryContextButtonHTML(ctxt, onASingleLine) + ' to <b>' + args[0] + '</b>';
 
 
-                case 'jQuery - before':
-                    return 'insert ' + joinArgs(args) + ' before ' + getJQueryContextButtonHTML(ctxt);
+            case 'jQuery - before':
+                return 'insert ' + joinArgs(args) + ' before ' + getJQueryContextButtonHTML(ctxt, onASingleLine);
 
 
-                case 'jQuery - insertBefore':
-                    return 'insert ' + getJQueryContextButtonHTML(ctxt) + ' before <b>' + args[0] + '</b>';
+            case 'jQuery - insertBefore':
+                return 'insert ' + getJQueryContextButtonHTML(ctxt, onASingleLine) + ' before <b>' + args[0] + '</b>';
 
 
-                case 'jQuery - after':
-                    return 'insert ' + joinArgs(args) + ' after ' + getJQueryContextButtonHTML(ctxt);
+            case 'jQuery - after':
+                return 'insert ' + joinArgs(args) + ' after ' + getJQueryContextButtonHTML(ctxt, onASingleLine);
 
 
-                case 'jQuery - insertAfter':
-                    return 'insert ' + getJQueryContextButtonHTML(ctxt) + ' after <b>' + args[0] + '</b>';
+            case 'jQuery - insertAfter':
+                return 'insert ' + getJQueryContextButtonHTML(ctxt, onASingleLine) + ' after <b>' + args[0] + '</b>';
 
 
-                case 'jQuery - remove':
-                case 'jQuery - detach':
-                    if (args[0]) {
-                        return getJQueryContextButtonHTML(ctxt) + ' filtered by <b>' + args[0] + '</b>';
-                    } else {
-                        return getJQueryContextButtonHTML(ctxt);
-                    }
-                    break;
-
-                case 'jQuery - empty':
-                case 'jQuery - clone':
-                case 'jQuery - unwrap':
-                case 'jQuery - show':
-                case 'jQuery - hide':
-                case 'jQuery - animate':
-                case 'jQuery - fadeIn':
-                case 'jQuery - fadeOut':
-                case 'jQuery - fadeTo':
-                case 'jQuery - fadeToggle':
-                case 'jQuery - slideDown':
-                case 'jQuery - slideUp':
-                case 'jQuery - slideToggle':
-                    return getJQueryContextButtonHTML(ctxt);
-
-                case 'jQuery - replaceWith':
-                    return 'replace ' + getJQueryContextButtonHTML(ctxt) + ' with <b>' + args[0] + '</b>';
-
-                case 'jQuery - replaceAll':
-                    return 'replace <b>' + args[0] + '</b> with ' + getJQueryContextButtonHTML(ctxt);
-
-                case 'jQuery - text':
-                    if (args[0]) {
-                        return 'set text "<b>' + args[0] + '</b>" to ' + getJQueryContextButtonHTML(ctxt);
-                    } else {
-                        return 'get text from ' + getJQueryContextButtonHTML(ctxt);
-                    }
-                    break;
-
-                case 'jQuery - wrap':
-                case 'jQuery - wrapAll':
-                    return 'wrap ' + getJQueryContextButtonHTML(ctxt) + ' within <b>' + args[0] + '</b>';
-
-                case 'jQuery - wrapInner':
-                    return 'wrap the content of ' + getJQueryContextButtonHTML(ctxt) + ' within <b>' + args[0] + '</b>';
-
-                case 'jQuery - css':
-                case 'jQuery - attr':
-                case 'jQuery - prop':
-                    if (isStringOfObject(args[0])) {
-                        return 'set <b>' + args[0] + '</b> on ' + getJQueryContextButtonHTML(ctxt);
-                    } else if (args[1]) {
-                        return 'set <b>' + args[0] + '</b> : <b>' + args[1] + '</b> on ' + getJQueryContextButtonHTML(ctxt);
-                    } else {
-                        return 'get <b>' + args[0] + '</b> from ' + getJQueryContextButtonHTML(ctxt);
-                    }
-                    break;
-
-                case 'jQuery - offset':
-                case 'jQuery - height':
-                case 'jQuery - innerHeight':
-                case 'jQuery - outerHeight':
-                case 'jQuery - width':
-                case 'jQuery - innerWidth':
-                case 'jQuery - outerWidth':
-                case 'jQuery - scrollLeft':
-                case 'jQuery - scrollTop':
-                case 'jQuery - position':
-                    if (args[0]) {
-                        return 'set <b>' + args[0] + '</b> on ' + getJQueryContextButtonHTML(ctxt);
-                    } else {
-                        return 'get from ' + getJQueryContextButtonHTML(ctxt);
-                    }
-                    break;
+            case 'jQuery - remove':
+            case 'jQuery - detach':
+                if (args[0]) {
+                    return getJQueryContextButtonHTML(ctxt, onASingleLine) + ' filtered by <b>' + args[0] + '</b>';
+                } else {
+                    return getJQueryContextButtonHTML(ctxt, onASingleLine);
+                }
+                break;
+
+            case 'jQuery - empty':
+            case 'jQuery - clone':
+            case 'jQuery - unwrap':
+            case 'jQuery - show':
+            case 'jQuery - hide':
+            case 'jQuery - animate':
+            case 'jQuery - fadeIn':
+            case 'jQuery - fadeOut':
+            case 'jQuery - fadeTo':
+            case 'jQuery - fadeToggle':
+            case 'jQuery - slideDown':
+            case 'jQuery - slideUp':
+            case 'jQuery - slideToggle':
+                return getJQueryContextButtonHTML(ctxt, onASingleLine);
+
+            case 'jQuery - replaceWith':
+                return 'replace ' + getJQueryContextButtonHTML(ctxt, onASingleLine) + ' with <b>' + args[0] + '</b>';
+
+            case 'jQuery - replaceAll':
+                return 'replace <b>' + args[0] + '</b> with ' + getJQueryContextButtonHTML(ctxt, onASingleLine);
+
+            case 'jQuery - text':
+                if (args[0]) {
+                    return 'set text "<b>' + args[0] + '</b>" to ' + getJQueryContextButtonHTML(ctxt, onASingleLine);
+                } else {
+                    return 'get text from ' + getJQueryContextButtonHTML(ctxt, onASingleLine);
+                }
+                break;
+
+            case 'jQuery - wrap':
+            case 'jQuery - wrapAll':
+                return 'wrap ' + getJQueryContextButtonHTML(ctxt, onASingleLine) + ' within <b>' + args[0] + '</b>';
+
+            case 'jQuery - wrapInner':
+                return 'wrap the content of ' + getJQueryContextButtonHTML(ctxt, onASingleLine) + ' within <b>' + args[0] + '</b>';
+
+            case 'jQuery - css':
+            case 'jQuery - attr':
+            case 'jQuery - prop':
+                if (isStringOfObject(args[0])) {
+                    return 'set <b>' + args[0] + '</b> on ' + getJQueryContextButtonHTML(ctxt, onASingleLine);
+                } else if (args[1]) {
+                    return 'set <b>' + args[0] + '</b> : <b>' + args[1] + '</b> on ' + getJQueryContextButtonHTML(ctxt, onASingleLine);
+                } else {
+                    return 'get <b>' + args[0] + '</b> from ' + getJQueryContextButtonHTML(ctxt, onASingleLine);
+                }
+                break;
+
+            case 'jQuery - offset':
+            case 'jQuery - height':
+            case 'jQuery - innerHeight':
+            case 'jQuery - outerHeight':
+            case 'jQuery - width':
+            case 'jQuery - innerWidth':
+            case 'jQuery - outerWidth':
+            case 'jQuery - scrollLeft':
+            case 'jQuery - scrollTop':
+            case 'jQuery - position':
+                if (args[0]) {
+                    return 'set <b>' + args[0] + '</b> on ' + getJQueryContextButtonHTML(ctxt, onASingleLine);
+                } else {
+                    return 'get from ' + getJQueryContextButtonHTML(ctxt, onASingleLine);
+                }
+                break;
+
+            case 'jQuery - toggle':
+                if (args[0] === 'true') {
+                    return getJQueryContextButtonHTML(ctxt, onASingleLine) + ' to visible';
+                } else if (args[0] === 'false') {
+                    return getJQueryContextButtonHTML(ctxt, onASingleLine) + ' to hidden';
+                } else {
+                    return getJQueryContextButtonHTML(ctxt, onASingleLine);
+                }
+                break;
+
+            case 'jQuery - on':
+            case 'jQuery - one':
+                if (args[1]) {
+                    return 'bind <b>' + args[0] + '</b> on ' + getJQueryContextButtonHTML(ctxt, onASingleLine) + '\'s children filtered by <b>' + args[1] + '</b>';
+                } else {
+                    return 'bind <b>' + args[0] + '</b> on ' + getJQueryContextButtonHTML(ctxt, onASingleLine);
+                }
+                break;
 
 
-                case 'jQuery - toggle':
-                    if (args[0] === 'true') {
-                        return getJQueryContextButtonHTML(ctxt) + ' to visible';
-                    } else if (args[0] === 'false') {
-                        return getJQueryContextButtonHTML(ctxt) + ' to hidden';
+            case 'jQuery - off':
+                if (args[0]) {
+                    if (args[1]) {
+                        return 'unbind <b>' + args[0] + '</b> from ' + getJQueryContextButtonHTML(ctxt, onASingleLine) + '\'s children filtered by <b>' + args[1] + '</b>';
                     } else {
                     } else {
-                        return getJQueryContextButtonHTML(ctxt);
+                        return 'unbind <b>' + args[0] + '</b> from ' + getJQueryContextButtonHTML(ctxt, onASingleLine);
                     }
                     }
-                    break;
+                } else {
+                    return 'unbind all events';
+                }
+                break;
+
+            case 'jQuery - live':
+            case 'jQuery - bind':
+                return 'bind <b>' + args[0] + '</b> on ' + getJQueryContextButtonHTML(ctxt, onASingleLine);
+
+            case 'jQuery - die':
+            case 'jQuery - unbind':
+                if (args[0]) {
+                    return 'unbind <b>' + args[0] + '</b> from ' + getJQueryContextButtonHTML(ctxt, onASingleLine);
+                } else {
+                    return 'unbind all events';
+                }
+                break;
+
+            case 'jQuery - delegate':
+                return 'bind <b>' + args[1] + '</b> on ' + getJQueryContextButtonHTML(ctxt, onASingleLine) + '\'s children filtered by <b>' + args[0] + '</b>';
 
 
-                case 'jQuery - on':
-                case 'jQuery - one':
+            case 'jQuery - undelegate':
+                if (args[0]) {
                     if (args[1]) {
                     if (args[1]) {
-                        return 'bind <b>' + args[0] + '</b> on ' + getJQueryContextButtonHTML(ctxt) + '\'s children filtered by <b>' + args[1] + '</b>';
+                        return 'unbind <b>' + args[1] + '</b> from ' + getJQueryContextButtonHTML(ctxt, onASingleLine) + '\'s children filtered by <b>' + args[0] + '</b>';
                     } else {
                     } else {
-                        return 'bind <b>' + args[0] + '</b> on ' + getJQueryContextButtonHTML(ctxt);
+                        return 'unbind namespace <b>' + args[0] + '</b>';
                     }
                     }
-                    break;
-
-                case 'jQuery - off':
-                    if (args[0]) {
-                        if (args[1]) {
-                            return 'unbind <b>' + args[0] + '</b> from ' + getJQueryContextButtonHTML(ctxt) + '\'s children filtered by <b>' + args[1] + '</b>';
-                        } else {
-                            return 'unbind <b>' + args[0] + '</b> from ' + getJQueryContextButtonHTML(ctxt);
-                        }
-                    } else {
-                        return 'unbind all events';
-                    }
-                    break;
+                } else {
+                    return 'unbind all events';
+                }
+                break;
+
+            case 'jQuery - blur':
+            case 'jQuery - change':
+            case 'jQuery - click':
+            case 'jQuery - dblclick':
+            case 'jQuery - error':
+            case 'jQuery - focus':
+            case 'jQuery - focusin':
+            case 'jQuery - focusout':
+            case 'jQuery - hover':
+            case 'jQuery - keydown':
+            case 'jQuery - keypress':
+            case 'jQuery - keyup':
+            case 'jQuery - load':
+            case 'jQuery - mousedown':
+            case 'jQuery - mouseenter':
+            case 'jQuery - mouseleave':
+            case 'jQuery - mousemove':
+            case 'jQuery - mouseout':
+            case 'jQuery - mouseover':
+            case 'jQuery - mouseup':
+            case 'jQuery - resize':
+            case 'jQuery - scroll':
+            case 'jQuery - select':
+            case 'jQuery - submit':
+            case 'jQuery - unload':
+                return 'bind on ' + getJQueryContextButtonHTML(ctxt, onASingleLine);
+
+            case 'jQuery - removeAttr':
+            case 'jQuery - removeProp':
+                return 'remove <b>' + args[0] + '</b> from ' + getJQueryContextButtonHTML(ctxt, onASingleLine);
+
+            case 'jQuery - val':
+                if (args[0]) {
+                    return 'set value <b>' + args[0] + '</b> to ' + getJQueryContextButtonHTML(ctxt, onASingleLine);
+                } else {
+                    return 'get value from ' + getJQueryContextButtonHTML(ctxt, onASingleLine);
+                }
+                break;
 
 
-                case 'jQuery - live':
-                case 'jQuery - bind':
-                    return 'bind <b>' + args[0] + '</b> on ' + getJQueryContextButtonHTML(ctxt);
+            case 'jQuery - hasClass':
+            case 'jQuery - addClass':
+            case 'jQuery - removeClass':
+                return '<b>' + args[0] + '</b> on ' + getJQueryContextButtonHTML(ctxt, onASingleLine);
 
 
-                case 'jQuery - die':
-                case 'jQuery - unbind':
-                    if (args[0]) {
-                        return 'unbind <b>' + args[0] + '</b> from ' + getJQueryContextButtonHTML(ctxt);
-                    } else {
-                        return 'unbind all events';
-                    }
-                    break;
-
-                case 'jQuery - delegate':
-                    return 'bind <b>' + args[1] + '</b> on ' + getJQueryContextButtonHTML(ctxt) + '\'s children filtered by <b>' + args[0] + '</b>';
-
-                case 'jQuery - undelegate':
-                    if (args[0]) {
-                        if (args[1]) {
-                            return 'unbind <b>' + args[1] + '</b> from ' + getJQueryContextButtonHTML(ctxt) + '\'s children filtered by <b>' + args[0] + '</b>';
-                        } else {
-                            return 'unbind namespace <b>' + args[0] + '</b>';
-                        }
-                    } else {
-                        return 'unbind all events';
-                    }
-                    break;
-
-                case 'jQuery - blur':
-                case 'jQuery - change':
-                case 'jQuery - click':
-                case 'jQuery - dblclick':
-                case 'jQuery - error':
-                case 'jQuery - focus':
-                case 'jQuery - focusin':
-                case 'jQuery - focusout':
-                case 'jQuery - hover':
-                case 'jQuery - keydown':
-                case 'jQuery - keypress':
-                case 'jQuery - keyup':
-                case 'jQuery - load':
-                case 'jQuery - mousedown':
-                case 'jQuery - mouseenter':
-                case 'jQuery - mouseleave':
-                case 'jQuery - mousemove':
-                case 'jQuery - mouseout':
-                case 'jQuery - mouseover':
-                case 'jQuery - mouseup':
-                case 'jQuery - resize':
-                case 'jQuery - scroll':
-                case 'jQuery - select':
-                case 'jQuery - submit':
-                case 'jQuery - unload':
-                    return 'bind on ' + getJQueryContextButtonHTML(ctxt);
-
-                case 'jQuery - removeAttr':
-                case 'jQuery - removeProp':
-                    return 'remove <b>' + args[0] + '</b> from ' + getJQueryContextButtonHTML(ctxt);
-
-                case 'jQuery - val':
-                    if (args[0]) {
-                        return 'set value <b>' + args[0] + '</b> to ' + getJQueryContextButtonHTML(ctxt);
-                    } else {
-                        return 'get value from ' + getJQueryContextButtonHTML(ctxt);
-                    }
-                    break;
-
-                case 'jQuery - hasClass':
-                case 'jQuery - addClass':
-                case 'jQuery - removeClass':
-                    return '<b>' + args[0] + '</b> on ' + getJQueryContextButtonHTML(ctxt);
-
-                case 'jQuery - toggleClass':
-                    if (args[0]) {
-                        if (args[1]) {
-                            return 'toggle <b>' + args[0] + '</b> on ' + getJQueryContextButtonHTML(ctxt) + ' to <b>' + args[1] + '</b>';
-                        } else {
-                            return 'toggle <b>' + args[0] + '</b> on ' + getJQueryContextButtonHTML(ctxt);
-                        }
+            case 'jQuery - toggleClass':
+                if (args[0]) {
+                    if (args[1]) {
+                        return 'toggle <b>' + args[0] + '</b> on ' + getJQueryContextButtonHTML(ctxt, onASingleLine) + ' to <b>' + args[1] + '</b>';
                     } else {
                     } else {
-                        return 'magic no-argument toggleClass';
+                        return 'toggle <b>' + args[0] + '</b> on ' + getJQueryContextButtonHTML(ctxt, onASingleLine);
                     }
                     }
-                    break;
+                } else {
+                    return 'magic no-argument toggleClass';
+                }
+                break;
 
 
-                case 'jQuery - children':
-                    if (args[0]) {
-                        return 'of ' + getJQueryContextButtonHTML(ctxt) + ' filtered by <b>' + args[0] + '</b>';
-                    } else {
-                        return 'of ' + getJQueryContextButtonHTML(ctxt);
-                    }
-                    break;
+            case 'jQuery - children':
+                if (args[0]) {
+                    return 'of ' + getJQueryContextButtonHTML(ctxt, onASingleLine) + ' filtered by <b>' + args[0] + '</b>';
+                } else {
+                    return 'of ' + getJQueryContextButtonHTML(ctxt, onASingleLine);
+                }
+                break;
 
 
-                case 'jQuery - closest':
+            case 'jQuery - closest':
+                if (args[1]) {
+                    return 'closest <b>' + args[0] + '</b> from ' + getJQueryContextButtonHTML(ctxt, onASingleLine) + ' in context <b>' + args[1] + '</b>';
+                } else {
+                    return 'closest <b>' + args[0] + '</b> from ' + getJQueryContextButtonHTML(ctxt, onASingleLine);
+                }
+                break;
+
+            case 'jQuery - next':
+            case 'jQuery - nextAll':
+                if (args[0]) {
+                    return 'after ' + getJQueryContextButtonHTML(ctxt, onASingleLine) + ' matching <b>' + args[0] + '</b>';
+                } else {
+                    return 'after ' + getJQueryContextButtonHTML(ctxt, onASingleLine);
+                }
+                break;
+
+            case 'jQuery - nextUntil':
+                if (args[0]) {
                     if (args[1]) {
                     if (args[1]) {
-                        return 'closest <b>' + args[0] + '</b> from ' + getJQueryContextButtonHTML(ctxt) + ' in context <b>' + args[1] + '</b>';
+                        return 'after ' + getJQueryContextButtonHTML(ctxt, onASingleLine) + ' until <b>' + args[0] + '</b> and matching <b>' + args[1] + '</b>';
                     } else {
                     } else {
-                        return 'closest <b>' + args[0] + '</b> from ' + getJQueryContextButtonHTML(ctxt);
+                        return 'after ' + getJQueryContextButtonHTML(ctxt, onASingleLine) + ' until <b>' + args[0] + '</b>';
                     }
                     }
-                    break;
+                } else {
+                    return 'after ' + getJQueryContextButtonHTML(ctxt, onASingleLine);
+                }
+                break;
 
 
-                case 'jQuery - next':
-                case 'jQuery - nextAll':
-                    if (args[0]) {
-                        return 'after ' + getJQueryContextButtonHTML(ctxt) + ' matching <b>' + args[0] + '</b>';
-                    } else {
-                        return 'after ' + getJQueryContextButtonHTML(ctxt);
-                    }
-                    break;
-
-                case 'jQuery - nextUntil':
-                    if (args[0]) {
-                        if (args[1]) {
-                            return 'after ' + getJQueryContextButtonHTML(ctxt) + ' until <b>' + args[0] + '</b> and matching <b>' + args[1] + '</b>';
-                        } else {
-                            return 'after ' + getJQueryContextButtonHTML(ctxt) + ' until <b>' + args[0] + '</b>';
-                        }
-                    } else {
-                        return 'after ' + getJQueryContextButtonHTML(ctxt);
-                    }
-                    break;
+            case 'jQuery - offsetParent':
+                return 'of ' + getJQueryContextButtonHTML(ctxt, onASingleLine);
 
 
-                case 'jQuery - offsetParent':
-                    return 'of ' + getJQueryContextButtonHTML(ctxt);
+            case 'jQuery - prev':
+            case 'jQuery - prevAll':
+                if (args[0]) {
+                    return 'before ' + getJQueryContextButtonHTML(ctxt, onASingleLine) + ' matching <b>' + args[0] + '</b>';
+                } else {
+                    return 'before ' + getJQueryContextButtonHTML(ctxt, onASingleLine);
+                }
+                break;
 
 
-                case 'jQuery - prev':
-                case 'jQuery - prevAll':
-                    if (args[0]) {
-                        return 'before ' + getJQueryContextButtonHTML(ctxt) + ' matching <b>' + args[0] + '</b>';
-                    } else {
-                        return 'before ' + getJQueryContextButtonHTML(ctxt);
-                    }
-                    break;
-
-                case 'jQuery - prevUntil':
-                    if (args[0]) {
-                        if (args[1]) {
-                            return 'before ' + getJQueryContextButtonHTML(ctxt) + ' until <b>' + args[0] + '</b> and matching <b>' + args[1] + '</b>';
-                        } else {
-                            return 'before ' + getJQueryContextButtonHTML(ctxt) + ' until <b>' + args[0] + '</b>';
-                        }
+            case 'jQuery - prevUntil':
+                if (args[0]) {
+                    if (args[1]) {
+                        return 'before ' + getJQueryContextButtonHTML(ctxt, onASingleLine) + ' until <b>' + args[0] + '</b> and matching <b>' + args[1] + '</b>';
                     } else {
                     } else {
-                        return 'before ' + getJQueryContextButtonHTML(ctxt);
+                        return 'before ' + getJQueryContextButtonHTML(ctxt, onASingleLine) + ' until <b>' + args[0] + '</b>';
                     }
                     }
-                    break;
+                } else {
+                    return 'before ' + getJQueryContextButtonHTML(ctxt, onASingleLine);
+                }
+                break;
+
+            case 'jQuery - parent':
+            case 'jQuery - parents':
+                if (args[0]) {
+                    return 'of ' + getJQueryContextButtonHTML(ctxt, onASingleLine) + ' matching <b>' + args[0] + '</b>';
+                } else {
+                    return 'of ' + getJQueryContextButtonHTML(ctxt, onASingleLine);
+                }
+                break;
 
 
-                case 'jQuery - parent':
-                case 'jQuery - parents':
-                    if (args[0]) {
-                        return 'of ' + getJQueryContextButtonHTML(ctxt) + ' matching <b>' + args[0] + '</b>';
-                    } else {
-                        return 'of ' + getJQueryContextButtonHTML(ctxt);
-                    }
-                    break;
-
-                case 'jQuery - parentsUntil':
-                    if (args[0]) {
-                        if (args[1]) {
-                            return 'of ' + getJQueryContextButtonHTML(ctxt) + ' until <b>' + args[0] + '</b> and matching <b>' + args[1] + '</b>';
-                        } else {
-                            return 'of ' + getJQueryContextButtonHTML(ctxt) + ' until <b>' + args[0] + '</b>';
-                        }
+            case 'jQuery - parentsUntil':
+                if (args[0]) {
+                    if (args[1]) {
+                        return 'of ' + getJQueryContextButtonHTML(ctxt, onASingleLine) + ' until <b>' + args[0] + '</b> and matching <b>' + args[1] + '</b>';
                     } else {
                     } else {
-                        return 'of ' + getJQueryContextButtonHTML(ctxt);
+                        return 'of ' + getJQueryContextButtonHTML(ctxt, onASingleLine) + ' until <b>' + args[0] + '</b>';
                     }
                     }
-                    break;
+                } else {
+                    return 'of ' + getJQueryContextButtonHTML(ctxt, onASingleLine);
+                }
+                break;
 
 
-                case 'jQuery - siblings':
-                    if (args[0]) {
-                        return 'near ' + getJQueryContextButtonHTML(ctxt) + ' matching <b>' + args[0] + '</b>';
-                    } else {
-                        return 'near ' + getJQueryContextButtonHTML(ctxt);
-                    }
-                    break;
+            case 'jQuery - siblings':
+                if (args[0]) {
+                    return 'near ' + getJQueryContextButtonHTML(ctxt, onASingleLine) + ' matching <b>' + args[0] + '</b>';
+                } else {
+                    return 'near ' + getJQueryContextButtonHTML(ctxt, onASingleLine);
+                }
+                break;
+
+            case 'jQuery - onDOMReady':
+                return '(function)';
+
+            default:
+                return '';
+        }
+    }
 
 
-                case 'jQuery - onDOMReady':
-                    return '(function)';
+    function escapeHTML(html) {
+        var entityMap = {
+            "&": "&amp;",
+            "<": "&lt;",
+            ">": "&gt;",
+            '"': '&quot;',
+            "'": '&#39;',
+            "/": '&#x2F;'
+        };
 
 
-                default:
-                    return '';
+        return String(html).replace(/[&<>"'\/]/g, function (s) {
+            return entityMap[s];
+        });
+    }
+
+    function joinArgs(args) {
+        var html = '<b>' + args[0] + '</b>';
+        if (args[1]) {
+            html += ', <b>' + args[1] + '</b>';
+            if (args[2]) {
+                html += ', <b>' + args[2] + '</b>';
+                if (args[3]) {
+                    html += ', and more...';
+                }
             }
             }
         }
         }
+        return html;
+    }
 
 
-        function escapeHTML(html) {
-            var entityMap = {
-                "&": "&amp;",
-                "<": "&lt;",
-                ">": "&gt;",
-                '"': '&quot;',
-                "'": '&#39;',
-                "/": '&#x2F;'
-            };
-
-            return String(html).replace(/[&<>"'\/]/g, function (s) {
-                return entityMap[s];
-            });
+   function isStringOfObject(str) {
+        return typeof str === 'string' && str[0] === '{' && str[str.length - 1] === '}';
+    }
+
+    function isPureString(str) {
+        return typeof str === 'string' && str[0] !== '{' && str !== '(function)' && str !== '[Object]' && str !== '[Array]' && str !== 'true' && str !== 'false' && str !== 'undefined' && str !== 'unknown';
+    }
+
+    function getTimelineParamsHTML(node, onASingleLine) {
+        if (isJQuery(node)) {
+            return getJQueryHTML(node, onASingleLine);
+        } else {
+            return getNonJQueryHTML(node, onASingleLine);
         }
         }
+    }
 
 
-        function joinArgs(args) {
-            var html = '<b>' + args[0] + '</b>';
-            if (args[1]) {
-                html += ', <b>' + args[1] + '</b>';
-                if (args[2]) {
-                    html += ', <b>' + args[2] + '</b>';
-                    if (args[3]) {
-                        html += ', and more...';
-                    }
-                }
+    function getBacktraceHTML(backtrace) {
+        var html = '';
+        var parsedBacktrace = parseBacktrace(backtrace);
+
+        if (!parsedBacktrace || parsedBacktrace.length === 0) {
+            html += '<div><div>can\'t find any backtrace :/</div></div>';
+        } else {
+            for (var i = 0 ; i < parsedBacktrace.length ; i++) {
+                html += '<div>';
+                html += '<div>' + (parsedBacktrace[i].fnName || '(anonymous)') + '</div>';
+                html += '<div class="trace"><url-link url="trace.filePath" max-length="40"></url-link>:' + parsedBacktrace[i].line + '</div>';
+                html += '</div>';
             }
             }
-            return html;
         }
         }
 
 
-        function getHTML(node) {
-            if (isJQuery(node)) {
-                return getJQueryHTML(node);
+        return html;
+    }
+
+    function parseBacktrace(str) {
+        if (!str) {
+            return null;
+        }
+
+        var out = [];
+        var splited = str.split(' / ');
+        
+        try {
+
+            splited.forEach(function(trace) {
+                var fnName = null, fileAndLine;
+
+                var withFnResult = /^([^\s\(]+) \((.+:\d+)\)$/.exec(trace);
+                if (withFnResult === null) {
+                    fileAndLine = trace;
+                } else {
+                    fnName = withFnResult[1];
+                    fileAndLine = withFnResult[2];
+                }
+
+                var fileAndLineSplit = /^(.*):(\d+)$/.exec(fileAndLine);
+                var filePath = fileAndLineSplit[1];
+                var line = fileAndLineSplit[2];
+
+                out.push({
+                    fnName: fnName,
+                    filePath: filePath,
+                    line: line
+                });
+            });
+
+        } catch(e) {
+            return null;
+        }
+
+        return out;
+    }
+
+    function getTimelineDetailsHTML(node) {
+        var html = '';
+
+        if (node.data.type != 'jQuery loaded' && node.data.type != 'jQuery version change' && !node.windowPerformance) {
+            if (node.warning || node.error) {
+                html += '<div class="icon-warning"></div>';
             } else {
             } else {
-                return getNonJQueryHTML(node);
+                html += '<div class="icon-question"></div>';
+            }
+
+            html += '<div class="detailsOverlay">';
+            html += '<div class="closeBtn">✖</div>';
+
+            if (node.data.callDetails.context && node.data.callDetails.context.length === 0) {
+                html += '<h4>Called on 0 jQuery element</h4><p class="advice">Useless function call, as the jQuery object is empty.</p>';
+            } else if (node.data.type === 'jQuery - bind' && node.data.callDetails.context.length > 5) {
+                html += '<p class="advice">The .bind() method attaches the event listener to each jQuery element one by one. Using the .on() method is preferable if available (from v1.7).</p>';
+            }
+
+            if (node.data.resultsNumber === 0) {
+                html += '<p class="advice">The query returned 0 results. Could it be unused or dead code?</p>';
+            } else if (node.data.resultsNumber > 0) {
+                html += '<p>The query returned ' + node.data.resultsNumber + ' ' + (node.data.resultsNumber > 1 ? 'results' : 'result') + '.</p>';
+            }
+
+            if (node.data.backtrace) {
+                html += '<h4>Backtrace</h4>';
+                html += '<div class="table">';
+                html += getBacktraceHTML(node.data.backtrace);
+                html += '</div>';
+            }
+
+            html += '</div>';
+        }
+        
+        return html;
+    }
+
+    
+    offendersDirectives.directive('profilerLine', ['$filter', function($filter) {
+        
+        var numberWithCommas = $filter('number');
+
+        function getProfilerLineHTML(index, node) {
+            return  '<div class="index">' + (index + 1) + '</div>' +
+                    '<div class="type">' + node.data.type + (node.children ? '<div class="children">' + recursiveChildrenHTML(node) + '</div>' : '') + '</div>' +
+                    '<div class="value offenders">' + getTimelineParamsHTML(node, false) + '</div>' +
+                    '<div class="details">' + getTimelineDetailsHTML(node) + '</div>' +
+                    '<div class="startTime ' + node.data.loadingStep + '">' + numberWithCommas(node.data.timestamp, 0) + ' ms</div>';
+        }
+
+        function recursiveChildrenHTML(node) {
+            var html = '';
+            
+            if (node.children) {
+                node.children.forEach(function(child) {
+                    html += '<div class="child"><span>' + child.data.type + '<div class="childArgs">' + getTimelineParamsHTML(child, true) + '</div></span>' + recursiveChildrenHTML(child) + '</div>';
+                });
             }
             }
+
+            return html;
         }
         }
 
 
-       function isStringOfObject(str) {
-            return typeof str === 'string' && str[0] === '{' && str[str.length - 1] === '}';
+        function onDetailsClick(row) {
+            // Close if it's alreay open
+            if (row.classList.contains('showDetails')) {
+                closeDetails(row);
+                return;
+            }
+
+            // Close any other open details overlay
+            var openOnes = document.getElementsByClassName('showDetails');
+            if (openOnes.length > 0) {
+                openOnes[0].classList.remove('showDetails');
+            }
+
+            // Make it appear
+            row.classList.add('showDetails');
+
+            // Bind the close button
+            row.querySelector('.closeBtn').addEventListener('click', function() {
+                closeDetails(row);
+            });
         }
         }
 
 
-        function isPureString(str) {
-            return typeof str === 'string' && str[0] !== '{' && str !== '(function)' && str !== '[Object]' && str !== '[Array]' && str !== 'true' && str !== 'false' && str !== 'undefined' && str !== 'unknown';
+        function closeDetails(row) {
+            row.classList.remove('showDetails');
+
+            // Unbind the close button
+            row.querySelector('.closeBtn').removeEventListener('click', closeDetails);   
         }
         }
 
 
         return {
         return {
             restrict: 'E',
             restrict: 'E',
             scope: {
             scope: {
+                index: '=',
                 node: '='
                 node: '='
             },
             },
-            template: '<div class="value offenders"></div>',
+            template: '<div id="line_{{index}}"></div>',
             replace: true,
             replace: true,
             link: function(scope, element) {
             link: function(scope, element) {
-                var html = getHTML(scope.node);
-                if (html) {
-                    element.append(html);
+                
+                if (scope.node.error) {
+                    element.addClass('jsError');
+                } else if (scope.node.windowPerformance) {
+                    element.addClass('windowPerformance');
+                }
+
+                element.append(getProfilerLineHTML(scope.index, scope.node));
+
+                // Bind click on the details icon
+                var detailsIcon = element[0].querySelector('.details div');
+                if (detailsIcon) {
+                    detailsIcon.addEventListener('click', function() {
+                        onDetailsClick(this.parentNode.parentNode);
+                    });
                 }
                 }
             }
             }
         };
         };
-    });
+    }]);
+
 
 
     offendersDirectives.filter('shortenUrl', function() {
     offendersDirectives.filter('shortenUrl', function() {
         return function(url, maxLength) {
         return function(url, maxLength) {

+ 8 - 0
front/src/less/timeline.less

@@ -244,6 +244,10 @@
     span:hover {
     span:hover {
         background: #EBD8E2;
         background: #EBD8E2;
 
 
+        div {
+            display: inline-block;
+        }
+
         .childArgs {
         .childArgs {
             display: block;
             display: block;
             position: absolute;
             position: absolute;
@@ -284,6 +288,7 @@
 }
 }
 
 
 .detailsOverlay {
 .detailsOverlay {
+    display: none;
     position: absolute;
     position: absolute;
     right: 3em;
     right: 3em;
     top: -3em;
     top: -3em;
@@ -300,6 +305,9 @@
         width: 25em;
         width: 25em;
     }
     }
 }
 }
+.showDetails .detailsOverlay {
+    display: block;
+}
 .detailsOverlay .closeBtn {
 .detailsOverlay .closeBtn {
     position: absolute;
     position: absolute;
     top: 0.5em;
     top: 0.5em;

+ 1 - 55
front/src/views/timeline.html

@@ -65,8 +65,6 @@
         </div>
         </div>
     </div>
     </div>
     <div class="table">
     <div class="table">
-
-        <toto data-info="mavariable"></toto>
         <div class="headers">
         <div class="headers">
             <div><!-- index --></div>
             <div><!-- index --></div>
             <div>Type</div>
             <div>Type</div>
@@ -74,61 +72,9 @@
             <div><!-- details --></div>
             <div><!-- details --></div>
             <div>Timestamp</div>
             <div>Timestamp</div>
         </div>
         </div>
-        <div ng-if="(!warningsFilterOn || node.warning || node.error)"
-             ng-repeat="node in profilerData" ng-class="{
-                showingDetails: node.showDetails,
-                jsError: node.error,
-                windowPerformance: node.windowPerformance
-             }" id="line_{{$index}}">
-
-            <div class="index">{{$index + 1}}</div>
-            <div class="type">
-                {{node.data.type}}
-                <js-children node="node"></js-children>
-            </div>
-
-            <timeline-params node="node"></timeline-params>
-
-            <div class="details">
-                <div ng-class="{'icon-question': !node.warning && !node.error, 'icon-warning': node.warning || node.error}"
-                     ng-click="onNodeDetailsClick(node)"
-                     ng-if="node.data.type != 'jQuery loaded' && node.data.type != 'jQuery version change' && !node.windowPerformance"></div>
-                
-                <div class="detailsOverlay" ng-if="node.showDetails">
-                    <div class="closeBtn" ng-click="onNodeDetailsClick(node)">✖</div>
-
-                    <div ng-if="node.data.callDetails.context.length === 0">
-                        <h4>Called on 0 jQuery element</h4>
-                        <p class="advice">Useless function call, as the jQuery object is empty.</p>
-                    </div>
 
 
-                    <p class="advice" ng-if="node.data.type == 'jQuery - bind' && node.data.callDetails.context.length > 5">
-                        The .bind() method attaches the event listener to each jQuery element one by one. Using the .on() method is preferable if available (from v1.7).
-                    </p>
+        <profiler-line ng-repeat="node in profilerData" data-index="$index" node="node"></profiler-line>
 
 
-                    <p class="advice" ng-if="node.data.resultsNumber === 0">
-                        The query returned 0 results. Could it be unused or dead code?
-                    </p>
-                    <p ng-if="node.data.resultsNumber > 0">
-                        The query returned <ng-pluralize count="node.data.resultsNumber" when="{'one': '1 result', 'other': '{} results'}"></ng-pluralize>.
-                    </p>
-
-                    <div ng-if="node.parsedBacktrace">
-                        <h4>Backtrace</h4>
-                        <div class="table">
-                            <div ng-repeat="trace in node.parsedBacktrace track by $index">
-                                <div>{{trace.fnName || '(anonymous)'}}</div>
-                                <div class="trace"><url-link url="trace.filePath" max-length="40"></url-link>:{{trace.line}}</div>
-                            </div>
-                            <div ng-if="node.parsedBacktrace.length == 0 && node.data.type != 'script loaded'">
-                                <div>can't find any backtrace :/</div>
-                            </div>
-                        </div>
-                    </div>
-                </div>
-            </div>
-            <div class="startTime" ng-class="node.data.loadingStep">{{node.data.timestamp | number: 0}} ms</div>
-        </div>
     </div>
     </div>
 
 
     <div class="backToDashboard"><a href="#" ng-click="backToDashboard()">Back to dashboard</a></div>
     <div class="backToDashboard"><a href="#" ng-click="backToDashboard()">Back to dashboard</a></div>