1<%--
2  ~ Copyright (c) 2016 Google Inc. All Rights Reserved.
3  ~
4  ~ Licensed under the Apache License, Version 2.0 (the "License"); you
5  ~ may not use this file except in compliance with the License. You may
6  ~ obtain a copy of the License at
7  ~
8  ~     http://www.apache.org/licenses/LICENSE-2.0
9  ~
10  ~ Unless required by applicable law or agreed to in writing, software
11  ~ distributed under the License is distributed on an "AS IS" BASIS,
12  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
13  ~ implied. See the License for the specific language governing
14  ~ permissions and limitations under the License.
15  --%>
16<%@ page contentType='text/html;charset=UTF-8' language='java' %>
17<%@ taglib prefix='fn' uri='http://java.sun.com/jsp/jstl/functions' %>
18<%@ taglib prefix='c' uri='http://java.sun.com/jsp/jstl/core'%>
19
20<html>
21  <%@ include file="header.jsp" %>
22  <link rel="stylesheet" href="/css/show_coverage.css">
23  <script async defer src="https://apis.google.com/js/api.js"
24          onload="this.onload=function(){};handleClientLoad()"
25          onreadystatechange="if (this.readyState === 'complete') this.onload()">
26  </script>
27  <body>
28    <script type="text/javascript">
29        $(document).ready(function() {
30            // Initialize AJAX for CORS
31            $.ajaxSetup({
32                xhrFields : {
33                    withCredentials: true
34                }
35            });
36
37            $('.collapsible.popout').collapsible({
38              accordion : true
39            }).find('.collapsible-header').click(onClick);
40
41
42            $("div.collapsible-header > span.indicator.waves-effect").click(function(evt) {
43              evt.preventDefault();
44
45              $("#loader-indicator").show();
46
47              var cmd = $(evt.target).text();
48              var testRunId = $(evt.target).data("id");
49              var postData = { coverageId: testRunId, testName: "${testName}", testRunId: "${startTime}", cmd: cmd};
50              $.post("/api/coverage/data", postData, function() {
51                // success
52                console.log("success");
53                var detachedLi = $(evt.target).parent().parent().detach();
54                if (cmd == "enable") {
55                  $(evt.target).text("disable");
56                  $(evt.target).parent().removeClass("grey");
57                  $('ul.collapsible.popout').prepend(detachedLi);
58                } else {
59                  $(evt.target).text("enable");
60                  $(evt.target).parent().addClass("grey");
61                  $('ul.collapsible.popout').append(detachedLi);
62                }
63              })
64              .done(function() {
65                // Done
66                $("#loader-indicator").fadeOut("slow");
67              })
68              .fail(function() {
69                alert( "Error occurred during changing the status" );
70              });
71            });
72        });
73
74        function handleClientLoad() {
75          // Load the API client and auth2 library
76          gapi.load('client:auth2', initClient);
77        }
78
79        function initClient() {
80          gapi.client.init({
81            client_id: ${clientId},
82            scope: ${gerritScope}
83          }).then(function () {
84            // displayEntries();
85          });
86        }
87
88        /* Open a window to Gerrit so that user can login.
89           Minimize the previously clicked entry.
90        */
91        var gerritLogin = function(element) {
92            window.open(${gerritURI}, "Ratting", "toolbar=0,status=0");
93            element.click();
94        }
95
96        /* Loads source code for a particular entry and displays it with
97           coverage information as the accordion entry expands.
98        */
99        var onClick = function() {
100            // Remove source code from the accordion entry that was open before
101            var self = $(this);
102            var prev = self.parent().siblings('li.active');
103            if (prev.length > 0) {
104                prev.find('.table-container').empty();
105            }
106            var url = self.parent().data('url');
107            var container = self.parent().find('.table-container');
108            container.html('<div class="center-align">Loading...</div>');
109            var coverageVectors = self.parent().data('coverage');
110            if (self.parent().hasClass('active')) {
111                // Remove the code from display
112                container.empty();
113            } else {
114                /* Fetch and display the code.
115                   Note: a coverageVector may be shorter than sourceContents due
116                   to non-executable (i.e. comments or language-specific syntax)
117                   lines in the code. Trailing source lines that have no
118                   coverage information are assumed to be non-executable.
119                */
120                $.ajax({
121                    url: url,
122                    dataType: 'text'
123                }).promise().done(function(src) {
124                    src = atob(src);
125                    if (!src) return;
126                    srcLines = src.split('\n');
127                    covered = 0;
128                    total = 0;
129                    var table = $('<table class="table"></table>');
130                    var rows = srcLines.forEach(function(line, j) {
131                        var count = coverageVectors[j];
132                        var row = $('<tr></tr>');
133                        if (typeof count == 'undefined' || count < 0) {
134                            count = "--";
135                        } else if (count == 0) {
136                            row.addClass('uncovered');
137                            total += 1;
138                        } else {
139                            row.addClass('covered');
140                            total += 1;
141                        }
142                        row.append('<td class="count">' + String(count) + '</td>');
143                        row.append('<td class="line_no">' + String(j+1) + '</td>');
144                        code = $('<td class="code"></td>');
145                        code.text(String(line));
146                        code.appendTo(row);
147                        row.appendTo(table);
148                    });
149                    container.empty();
150                    container.append(table);
151                }).fail(function(error) {
152                    if (error.status == 0) {  // origin error, refresh cookie
153                        container.empty();
154                        container.html('<div class="center-align">' +
155                                       '<span class="login-button">' +
156                                       'Click to authorize Gerrit access' +
157                                       '</span></div>');
158                        container.find('.login-button').click(function() {
159                            gerritLogin(self);
160                        });
161                    } else {
162                        container.html('<div class="center-align">' +
163                                       'Not found.</div>');
164                    }
165                });
166            }
167        }
168    </script>
169    <div id='coverage-container' class='wide container'>
170      <h4 class="section-title"><b>Coverage:</b> </h4>
171      <ul class="collapsible popout" data-collapsible="accordion">
172        <c:forEach var="coverageEntity" items="${coverageEntityList}" varStatus="loop">
173          <li data-url="<c:url value="${coverageEntity.gerritUrl}"/>" data-index="${loop.index}" data-coverage="${coverageEntity.lineCoverage}">
174          <div class="collapsible-header <c:out value='${coverageEntity.isIgnored ? "grey" : ""}'/>">
175            <i class="material-icons">library_books</i>
176            <div class="truncate"><b>${coverageEntity.projectName}</b>/${coverageEntity.filePath}</div>
177            <div class="right total-count">${coverageEntity.coveredCount}/${coverageEntity.totalCount}</div>
178            <div class="indicator ${coverageEntity.percentage >= 70 ? "green" : "red"}">${coverageEntity.percentage}%</div>
179            <c:if test="${isModerator}">
180              <span data-id="${coverageEntity.id}" class="indicator waves-effect blue lighten-1" style="margin-left: 5px;"><c:out value='${coverageEntity.isIgnored ? "enable" : "disable"}'/></span>
181            </c:if>
182          </div>
183          <div class="collapsible-body row">
184            <div class="html-container">
185              <div class="table-container"></div>
186            </div>
187          </div>
188          </li>
189        </c:forEach>
190      </ul>
191    </div>
192
193      <div id="loader-indicator" class="loader-background" style="display: none">
194          <div class="preloader-wrapper big active">
195              <div class="spinner-layer spinner-blue-only">
196                  <div class="circle-clipper left">
197                      <div class="circle"></div>
198                  </div>
199                  <div class="gap-patch">
200                      <div class="circle"></div>
201                  </div>
202                  <div class="circle-clipper right">
203                      <div class="circle"></div>
204                  </div>
205              </div>
206          </div>
207      </div>
208    <%@ include file="footer.jsp" %>
209  </body>
210</html>
211