chromeHAR

An HTTP Archive (HAR) viewer built on AngularJS


  +Eric J. Duran   {}ericduran


URL: http://goo.gl/rph7w

HTTP Archive (HAR)

  • Simple JSON file
  • Contains all HTTP request data
  • Allows you to replicate the network waterfall

"entries": [ {
  "time": 7,
  "request": {
    "method": "GET",
    "url": "http://localhost/github/har/index.html",
    "httpVersion": "HTTP/1.1",
    "headers": [{
      "name": "Accept-Encoding",
      "value": "gzip,deflate,sdch"
    }]
    ....
  }
}]
            

HAR Spec

Easy to export from Chrome dev tools

Save as har from dev tools

HAR Viewers

Software is hard har viewer

http://www.softwareishard.com/har/viewer/

chromeHAR

Sample chromeHAR

http://ericduran.github.com/chromeHAR/

Why AngularJS

Data binding example

Simple data binding

Super Simple Data binding


angular.module('net', ['net.dnd']).controller('NetworkCtrl',
  function ($scope) {
    'use strict';
    ...
    $scope.startedTime = 'Start Time';
    $scope.pageTimings = 'Timings';
    ...
  }
);
            

  <span>{{startedTime}}</span>

  Becomes

  <span>Start Time</span>

            

Demo

We'll be right back.

Lets look at the controller


angular.module('net', ['net.dnd']).controller('NetworkCtrl',
  function ($scope) {
    'use strict';
    // Empty entries array.
    $scope.entries = [];

    // Takes JSON data and sets up $scope.entries.
    $scope.updateHar = function(data) {
      var entries = data.log.entries;
      $.each(entries, function(i, entry) {
        entries[i] = new HAREntry(entry, i, data);
        $scope.transfer += entries[i].getRawContentSize();
      });
      $scope.entries = entries;
    }
  }
);
          

Templates

95% of the app is just a table row


<tr ng-repeat="entry in entries" ng-class="getSelectedRow($index)">
    <td ng-click="showDetails($index)">
      <div>{{entry.parsedURL.lastPathComponent}}
        <div>{{entry.folder}}</div>
      </div>
    </td>
    <td><div>{{entry.method}}</div></td>
    <td><div>{{entry.statusText}</div></td>
    <td><div>{{entry.mimeType}}</div></td>
    <td><div>{{entry.size}}</div></td>
    <td><div>{{entry.time}}<</div></td>
    <td class="timeline-column">
      // A bit more work ;-) but the same principles
    </td>
</tr>
          

Actual template is a bit more ;-)

Filtering the <tr>s


<tr ng-repeat="entry in entries |
  filter:query |
  filter:type |
  orderBy:predicate:reverse"
>
....
</tr>
          
<input type="search" ng-model="query">


<li ng-click="type.mimeType='text/html'">Docs</li>
<li ng-click="type.mimeType='text/css'">Styles</li>

Ordering the <tr>s


// Original version: Set my predicate to the nameSort key.
<th ng-click="predicate = 'nameSort'; reverse=false">Name</th>

// New version: set sort lets me flip the reverse value.
<th ng-click="setSort('method');">Method</th>
          

// ng-repeat also has an orderBy applied after all the filters
<tr ng-repeat="entry in entries | ... |
  orderBy:predicate:reverse">
....
</tr>
          

Toggling active states


<li ng-class="getClass('css')" ng-click="sI='css'">Stylesheets</li>

// JS Code:
$scope.getClass = function (type) {
   return ( (type == $scope.sI) ? 'selected' : '');
}
          

Returns "selected" class when the "sI" value is the same

Angular meet HTML5 Drag & Drop

My 1st attempt looked something like this


// Random script outside of the controller.
window.ondrop = function (e) {
  ...
  var scope = angular.element("body").scope();
  scope.$apply(function() {
    scope.updateHar(data);
  });
}
            

Worked great, just didn't feel right.

Directives!


<div dnd-dragover="foo()"> or <div dnd-drop="bar($event)">
            


var dndModule = angular.module('net.dnd', []);
['Dragstart', 'Drop', ...].forEach(function(name) {
    var directiveName = 'dnd' + name;
    dndModule.directive(directiveName, ['$parse',
    function($parse) {
      return function(scope, element, attr) {
        var fn = $parse(attr[directiveName]);
        element.bind(name.toLowerCase(), function(event) {
          scope.$apply(function() {
            fn(scope, {$event:event});
          });
        });
      };
    }]);
  }
);
            

Thanks




  +Eric J. Duran   {}ericduran