Over a million developers have joined DZone.
{{announcement.body}}
{{announcement.title}}

Angular Commit Bubbles

DZone's Guide to

Angular Commit Bubbles

· Java Zone
Free Resource

Learn how to troubleshoot and diagnose some of the most common performance issues in Java today. Brought to you in partnership with AppDynamics.

I’m obsessed with the pseudo-declarative side of Angular. Making HTML Turing-complete was a stroke of genius. To test the theory, and towards something that would be genuinely useful, I thought I’d blow the dust off something else that Miško had made in the past…

Commit Bubbles!

Miško Hevery wrote an ActionScript app that showed the enthusiasm for testing of the developers within a team. He’d previously worked at Adobe and was quite skilled with the Flash eco-system. I’m sure he’s forgotten much of that now, given how significant and time consuming Angular has become. Former ThoughtWorker Jon Wolter blogged about it. I think Jon even helped Miško develop it a couple of years before. We only have screen-shots of the original app to go with:

Anyway, we recreated it in Angular as a team-effort after-work activity. To be fair it’s also an Angular teaching tool for people new to it, with me switching between “product owner” and “Angular coder” as needed. It served as a tutorial for SVG too, as it happens.

If you have IE 10 or better, you can play with it here (mobile too): http://paul-hammant.github.io/angular-commit-bubbles/commits.html

The checkboxes and buttons work. They remove (or add back) committers so that you can hone in on best performer (or worst, or both). You can also click on bubbles for an overlay showing more info about the commit. That’s only a jpeg above though, so don’t get too upset if you’ve been clicking it. The real deal is here

In the implementation, we has a deliberate non-functional goal: What would the solution look like with least possible lines of JavaScript. The Source is here, but for your convenience inline here too:

<!DOCTYPE html>
<html>
<head>
    <meta content="text/html;charset=utf-8" http-equiv="Content-Type">
    <meta content="utf-8" http-equiv="encoding">
    <title>Commit Bubbles</title>
    <script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.13/angular.min.js"></script>
    <script src="http://underscorejs.org/underscore-min.js"></script>
    <script src="http://vitalets.github.io/checklist-model/checklist-model.js"></script>
    <script src="http://momentjs.com/downloads/moment.min.js"></script>
    <script>
        var app = angular.module("app", ["checklist-model"]);
        app.controller('CommitBubbles', function ($scope) {
            $scope.c = {
                chartHeight: 680,
                topMargin: 100,
                colors: ["#FFA07A","#20B2AA","#778899","#B0C4DE","#F5F5DC","#00FF00","#000000","#32CD32","#FAF0E6","#0000FF","#FF00FF","#8A2BE2","#800000","#A52A2A","#66CDAA","#DEB887","#0000CD","#5F9EA0","#7FFF00","#9370DB","#D2691E","#FF7F50","#7B68EE","#6495ED","#48D1CC","#DC143C","#C71585","#00FFFF","#191970","#00008B","#F5FFFA","#008B8B","#FFE4E1","#B8860B","#A9A9A9","#006400","#000080","#BDB76B","#8B008B","#808000","#6B8E23","#FF8C00","#FFA500","#9932CC","#FF4500","#8B0000","#DA70D6","#E9967A","#EEE8AA","#8FBC8F","#98FB98","#483D8B","#AFEEEE","#2F4F4F","#DB7093","#00CED1","#9400D3","#FFDAB9","#FF1493","#CD853F","#00BFFF","#FFC0CB","#696969","#DDA0DD","#1E90FF","#B0E0E6","#B22222","#800080","#FFFAF0","#FF0000","#228B22","#BC8F8F","#FF00FF","#4169E1","#DCDCDC","#FA8072","#FFD700","#FAA460","#DAA520","#2E8B57","#808080","#008000","#A0522D","#ADFF2F","#C0C0C0","#87CEEB","#FF69B4","#6A5ACD","#CD5C5C","#708090","#4B0082","#00FF7F","#F0E68C","#4682B4","#E6E6FA","#D2B48C","#008080","#7CFC00","#D8BFD8","#FFFACD","#FF6347","#ADD8E6","#40E0D0","#F08080","#EE82EE","#F5DEB3","#FAFAD2","#90EE90","#D3D3D3","#FFFF00","#FFB6C1"]
            };
            $scope.commits = [
                {
                    ts:1288202623006,
                    hash:"1234567890",
                    testPercentage:100,
                    size:55,
                    author:"John Doe"
                },
                {
                    ts:1288121623006,
                    hash:"13432435645",
                    testPercentage:30,
                    size:81,
                    author:"John Doe"
                },
                {
                    ts:1288128023006,
                    hash:"12232435645",
                    testPercentage:50,
                    size:50,
                    author:"Hector"
                },
                {
                    ts:1288202823006,
                    hash:"12332343455",
                    testPercentage:55,
                    size:50,
                    author:"Hector"
                },
                {
                    ts:1288323623006,
                    hash:"2342342344",
                    testPercentage:15,
                    size:22,
                    author:"Mildred"
                }
            ];
            $scope.authors = _.uniq(_.pluck($scope.commits, 'author'));
            $scope.selected = { authors: $scope.authors.slice(0) };
            $scope.from = _.min($scope.commits, "ts").ts;
            $scope.to = _.max($scope.commits, "ts").ts;
            $scope.timeSpan = moment.duration(moment($scope.from).diff(moment($scope.to))).humanize();
            $scope.checkAllAuthors = function() {
              $scope.selected.authors = angular.copy($scope.authors);
            };
        });
    </script>
</head>
<body ng-app="app" ng-controller="CommitBubbles" bgcolor="#FFFFFF">
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xl="http://www.w3.org/1999/xlink" version="1.1" width="1220"
     height="{{c.chartHeight + c.topMargin + 5}}"
     xmlns:dc="http://purl.org/dc/elements/1.1/">
    <rect fill="#FEFCFF" x="100" y="{{c.topMargin}}" width="1100" height="{{c.chartHeight}}" ng-click="selected.commit = false"/>
    <g font-family="Verdana" font-size="16">
        <text x="0" y="{{c.topMargin + 8}}">All Test</text>
        <text x="0" y="{{c.chartHeight/2 + 5 + c.topMargin}}">Equal</text>
        <text x="0" y="{{c.chartHeight + c.topMargin}}">All Prod</text>
        <text x="1140" y="{{c.chartHeight/2 + 17 + c.topMargin}}">Time &#10148;</text>
    </g>
    <g style="stroke:rgb(0,0,0);stroke-width:2">
        <line x1="100" y1="{{c.chartHeight/2 + c.topMargin}}" x2="1200" y2="{{c.chartHeight/2 + c.topMargin}}" />
        <line x1="100" y1="{{c.topMargin +2}}" x2="100" y2="{{c.chartHeight + c.topMargin+2}}"/>
    </g>
    <g font-family="Verdana" font-size="8" ng-repeat="i in [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10]" style="stroke:rgb(0,0,0);stroke-width:0.5">
        <line ng-show="i != 5" x1="100" stroke-dasharray="10,35"
              y1="{{c.chartHeight + 2 - (c.chartHeight/10 * i) + c.topMargin}}" x2="1200"
              y2="{{c.chartHeight - (c.chartHeight/10 * i) + c.topMargin}}" />
        <text fill="white" x="70" y="{{c.chartHeight + 3  - (c.chartHeight/10 * i) + c.topMargin}}">{{100 - (10*i)}}:{{(10*i)}}</text>
    </g>
    <g stroke="black">
        <circle ng-repeat="commit in commits" ng-show="selected.authors.indexOf(commit.author) > -1"
                cx="{{100 + (commit.ts - from) * 1100 / (to - from) }}"
                cy="{{c.chartHeight - (commit.testPercentage * c.chartHeight/100) + c.topMargin}}"
                r="{{commit.size * (c.chartHeight/600)}}"
                stroke-opacity="{{(selected.commit === commit) ? 1 : 0}}"
                fill="{{c.colors}}" fill-opacity="0.5"
                ng-click="selected.commit = commit"/>
    </g>
    <foreignObject class="node" x="0" y="0" width="1200" height="{{c.topMargin}}">
        <body xmlns="http://www.w3.org/1999/xhtml">
        <h2>{{timeSpan}} shown between {{from | date:'MM/dd/yy HH:mm'}} and {{to | date:'MM/dd/yy HH:mm'}}</h2>
        <div>Show authors:
            <label ng-repeat="author in authors" style="{{'color: ' + c.colors[$index] }}">
                <input type="checkbox" checklist-model="selected.authors" checklist-value="author"> {{author}}
            </label>
            <button ng-click="checkAllAuthors()" style="margin: 0 0 5px 5px">All authors</button>
            <button ng-click="selected.authors.splice(0,selected.authors.length)" style="margin: 0 0 5px 5px">None</button>
        </div>
        </body>
    </foreignObject>
    <g font-family="Verdana" font-size="16" ng-show="selected.commit" ng-click="selected.commit = false">
        <rect style="stroke:rgb(0,0,0);stroke-width:2" x="300" y="300" height="300" width="400" fill="white" opacity="0.7"/>
        <text x="320" y="330">Selected Commit</text>
        <text x="320" y="380">Hash: {{selected.commit.hash}}</text>
        <text x="320" y="430">Author: {{selected.commit.author}}</text>
        <text x="320" y="480">Date/Time: {{selected.commit.ts | date:'yyyy-MM-dd HH:mm:ss Z'}}</text>
        <text x="320" y="530">Size: {{selected.commit.size}}</text>
        <text x="320" y="580">Test Percentage: {{selected.commit.testPercentage}}</text>
    </g>
</svg>
</body>
</html>

If we ignore the array of non-clashing colors, some constants, and the simulation of four commits that’s inlined rather than pulled via $resource, there’s only ten lines of JavaScript. Underscore has helped us reduce lines quite a bit. The downside is that the html is pretty angular-attribute heavy.

Next?

Colleagues [a href="http://www.grahambrooks.com"]Graham Brooks and Premanand Chandrasekaran are working to use it in a real (and larger system), and populate the commit info (at least for Subversion).

A D3 Alternative

Colleague Pawel Badenski forked my repo, and quickly made a D3 version of the same thing. I don’t want to give him too much publicity, so I’ll leave it at that

20% less lines of code, grumble grumble……

Understand the needs and benefits around implementing the right monitoring solution for a growing containerized market. Brought to you in partnership with AppDynamics.

Topics:

Published at DZone with permission of Paul Hammant, DZone MVB. See the original article here.

Opinions expressed by DZone contributors are their own.

{{ parent.title || parent.header.title}}

{{ parent.tldr }}

{{ parent.urlSource.name }}