<!doctype html>
<html ng-app="app">

<head>
    <style>
        .trace {
            min-width: 900px;
            width: 100%;
        }

        .trace tr:hover {
            background-color: #D9EDF7 !important;
        }

        .trace tr td {
            width: 14%;
            white-space: nowrap;
            padding: 2px;
            border-right: 1px solid #EEE;
        }

        .trace tr td.details {
            width: 10%;
            padding-right: 20px;
        }

        .trace.cursor_pointer_on_hover {
            cursor: pointer;
        }

        .trace .level {
            width: 10%;
            font-weight: bold;
        }

        .bold {
            font-weight: bold;
        }

        .duration {
            width: 25px;
            margin: 0px;
            padding: 0px;
            background-color: #C6EFF3;
            border-radius: 4px;
            font-size: 10px;
        }

        .duration div {
            padding-top: 4px;
            padding-bottom: 4px;
            text-align: center;
        }

        dl {
            margin: 5px 0px;
        }

        .hljs {
            white-space: pre;
            word-wrap: normal;
        }

        .hljs.wrapped {
            white-space: pre-wrap;
        }

        .toggle-button {
            margin-top: -7px;
        }

    </style>
    <script>
        var static_files = $LOCAL;
        if (static_files){
            document.write('<link rel="stylesheet" href="/libs/bootstrap.min.css">');
            document.write('<link rel="stylesheet" href="/libs/github.min.css">');
            document.write('<script type="text/javascript" src="/libs/angular.min.js"><\/script>');
            document.write('<script type="text/javascript" src="/libs/ui-bootstrap-tpls-2.3.1.min.js"><\/script>');
            document.write('<script type="text/javascript" src="/libs/highlight.min.js"><\/script>');
            document.write('<script type="text/javascript" src="/libs/angular-highlightjs.min.js"><\/script>');
            document.write('<script type="text/javascript" src="/libs/angular-ui-tree.min.js"><\/script>');
            document.write('<script type="text/javascript" src="/libs/angular-ui-tree-filter.js"><\/script>');
        }
        else{
            document.write('<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap.min.css">');
            document.write('<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/9.8.0/styles/github.min.css">');
            document.write('<script type="text/javascript" src="https://ajax.googleapis.com/ajax/libs/angularjs/1.6.0/angular.min.js"><\/script>');
            document.write('<script type="text/javascript" src="https://angular-ui.github.io/bootstrap/ui-bootstrap-tpls-2.3.1.min.js"><\/script>');
            document.write('<script type="text/javascript" src="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/9.8.0/highlight.min.js"><\/script>');
            document.write('<script type="text/javascript" src="https://pc035860.github.io/angular-highlightjs/angular-highlightjs.min.js"><\/script>');
            document.write('<script type="text/javascript" src="https://cdn.rawgit.com/angular-ui-tree/angular-ui-tree/master/dist/angular-ui-tree.min.js"><\/script>');
            document.write('<script type="text/javascript" src="https://cdn.rawgit.com/EE/angular-ui-tree-filter/master/dist/angular-ui-tree-filter.js"><\/script>');
        }
    </script>
</head>

<body>
    <script>
        (function(angular) {
            'use strict';

            var OSProfilerData = $DATA;

            angular
                .module('app', ['ui.bootstrap', 'hljs', 'ui.tree', 'ui.tree-filter'])
                .config(['$rootScopeProvider', function ($rootScopeProvider) {
                    $rootScopeProvider.digestTtl(50);
                }])
                .config(['hljsServiceProvider', function (hljsServiceProvider) {
                    hljsServiceProvider.setOptions({
                        // replace tab with 4 spaces
                        tabReplace: '    '
                    });
                }])
                .config(['uiTreeFilterSettingsProvider', function (uiTreeFilterSettingsProvider) {
                    uiTreeFilterSettingsProvider.addresses = [
                        'info.name',
                        'info.project',
                        'info.service',
                        'info.host'
                    ];
                    uiTreeFilterSettingsProvider.descendantCollection = 'children';
                }])
                .controller('ProfilerController', ProfilerController)
                .controller('ModalInstanceController', ModalInstanceController);

            // Inject services
            ProfilerController.$inject = ['$filter', '$uibModal'];
            ModalInstanceController.$inject = ['$uibModalInstance', 'info'];

            function ProfilerController($filter, $uibModal) {
                // NOTE(tovin07): Bind this to vm. This is controller as and vm syntax.
                // This style is mainstream now. It replaces $scope style.
                // Ref: https://johnpapa.net/angularjss-controller-as-and-the-vm-variable/
                var vm = this;

                vm.filter = $filter('uiTreeFilter');

                var converInput = function(input, level) {
                    level = (level) ? level : 0;
                    input.level = level;
                    input.is_leaf = !input.children.length;

                    for (var i = 0; i < input.children.length; i++) {
                        converInput(input.children[i], level + 1);
                    }
                    return input;
                };

                vm.isLastTrace = function(started) {
                    if (started >=0 && started == vm.tree[0].info.last_trace_started) {
                        return true;
                    }
                    return false;
                };

                vm.getWidth = function(data) {
                    var fullDuration = vm.tree[0].info.finished;
                    var duration = (data.info.finished - data.info.started) * 100.0 / fullDuration;
                    return (duration >= 0.5) ? duration : 0.5;
                };

                vm.getStarted = function(data) {
                    var fullDuration = vm.tree[0].info.finished;
                    return data.info.started * 100.0 / fullDuration;
                };

                vm.isImportance = function(data) {
                    return ['total', 'wsgi', 'rpc'].indexOf(data.info.name) != -1;
                };

                vm.display = function(data) {
                    $uibModal.open({
                        animation: true,
                        templateUrl: 'modal_renderer.html',
                        controller: 'ModalInstanceController',
                        controllerAs: 'modal',
                        size: 'lg',
                        resolve: {
                            info: function() {
                                return angular.copy(data.info);
                            }
                        }
                    });
                };

                vm.tree = [converInput(OSProfilerData)];
            }

            function ModalInstanceController($uibModalInstance, info){
                var modal = this;
                var metadata = {};
                angular.forEach(info, function(value, key) {
                    var parts = key.split('.');
                    var metaText = 'meta';
                    if (parts[0] == metaText) {
                        if (parts.length == 2) {
                            this[parts[1]] = value;
                        } else {
                            var groupName = parts[1];
                            if (!(groupName in this)) {
                                this[groupName] = {};
                            }
                            // Plus 2 for 2 dots such as: meta.raw_payload.heat.wsgi-start
                            var index = metaText.length + parts[1].length + 2;
                            this[groupName][key.slice(index)] = value;
                        }
                    };
                }, metadata);

                info.duration = info.finished - info.started;
                // Escape single-quotes to prevent angular parse lexerr
                info.metadata = JSON.stringify(metadata, null, 4).replace(/'/g, "\\'");

                // Bind to view model
                modal.info = info;

                modal.columns = [
                    ['name', 'project', 'service', 'host'],
                    ['started', 'finished', 'duration', 'exception']
                ];

                modal.toggleWrap = function() {
                    var element = angular.element(document.querySelector('code.hljs'));

                    var wrappedClass = 'wrapped';
                    var isWrapped = element.hasClass(wrappedClass);
                    if (isWrapped) {
                        element.removeClass(wrappedClass);
                    } else {
                        element.addClass(wrappedClass);
                    }
                };

                modal.close = function() {
                    $uibModalInstance.dismiss('close');
                };
            }
        })(window.angular);
    </script>
    <!--Tree item template-->
    <script type="text/ng-template" id="tree_item_renderer.html">
        <div ng-init="hide_children=false">
            <table class="trace cursor_pointer_on_hover">
                <tr ng-class="{'bg-success': vm.isLastTrace(data.info.started)}">
                    <td class="level" style="padding-left: {{::data.level * 5}}px;">
                        <button type="button" class="btn btn-default btn-xs" ng-disabled="data.is_leaf" ng-click="hide_children=!hide_children">
                            <span class="glyphicon glyphicon-{{(data.is_leaf) ? 'cloud' : ((hide_children) ? 'plus': 'minus')}}"></span>
                            {{::data.level || 0}}
                        </button>
                    </td>
                    <td ng-click="vm.display(data)" class="text-center">
                        <div class="duration" style="width: {{vm.getWidth(data)}}%; margin-left: {{vm.getStarted(data)}}%;">
                            <div>{{data.info.finished - data.info.started}} ms</div>
                        </div>
                    </td>
                    <td ng-click="vm.display(data)" class="{{vm.isImportance(data) ? 'bold' : ''}} text-right">{{::data.info.name}}</td>
                    <td ng-click="vm.display(data)">{{::data.info.project || "n/a"}}</td>
                    <td ng-click="vm.display(data)">{{::data.info.service || "n/a"}}</td>
                    <td ng-click="vm.display(data)">{{::data.info.host || "n/a"}}</td>
                    <td class="details">
                        <a href="#" ng-click="vm.display(data)">Details</a>
                    </td>
                </tr>
            </table>
            <div ui-tree-nodes ng-model="data.children" ng-hide="hide_children">
                <div ui-tree-node ng-repeat="data in data.children" ng-include="'tree_item_renderer.html'" ng-hide="!vm.filter(data, vm.filterPattern)"></div>
            </div>
        </div>
    </script>
    <!--Modal template-->
    <script type="text/ng-template" id="modal_renderer.html">
        <div class="modal-header">
            <h3 class="text-center">
                <strong>Trace Point Details</strong>
                <span class="btn btn-default pull-right toggle-button" ng-click="modal.toggleWrap()">Toggle wrap-text</span>
            </h3>
        </div>
        <div class="modal-body">
            <div class="row">
                <div class="col-md-6" ng-repeat="cols in modal.columns">
                    <dl class="dl-horizontal" ng-repeat="column in cols">
                        <dt class="text-capitalize">{{::column}}</dt>
                        <dd>{{::modal.info[column]}}</dd>
                    </dl>
                </div>
                <div class="col-md-12">
                    <!--For metadata only-->
                    <dl class="dl-horizontal">
                        <dt class="text-capitalize">metadata</dt>
                        <dd hljs hljs-language="json" hljs-source="'{{::modal.info.metadata}}'"></dd>
                    </dl>
                </div>
            </div>
        </div>
        <div class="modal-footer">
            <span class="btn btn-default" ng-click="modal.close()">Close</span>
        </div>
    </script>
    <!--Body-->
    <div ng-controller="ProfilerController as vm">
        <form class="form-horizontal">
            <div class="form-group">
                <label for="filterPattern" class="col-md-4 control-label">Span filtering:</label>
                <div class="col-md-6">
                    <input type="text" class="form-control" id="filterPattern"
                        placeholder="Input your regular expression pattern here"
                        ng-model="vm.filterPattern">
                </div>
            </div>
        </form>
        <table class="trace">
            <tr class="bold text-left" style="border-bottom: solid 1px gray;">
                <td class="level">Levels</td>
                <td>Duration</td>
                <td class="text-right">Type</td>
                <td>Project</td>
                <td>Service</td>
                <td>Host</td>
                <td class="details">Details</td>
            </tr>
        </table>
        <div ui-tree>
            <div ui-tree-nodes ng-model="vm.tree">
                <div ui-tree-node ng-repeat="data in vm.tree" ng-include="'tree_item_renderer.html'" ng-hide="!vm.filter(data, vm.filterPattern)"></div>
            </div>
        </div>
    </div>
</body>

</html>
