]> src.twobees.de Git - tampermonkeyscripts.git/blob - AzureDevOpsChangesetCommentSearch.js
0.3: filter results by committer name
[tampermonkeyscripts.git] / AzureDevOpsChangesetCommentSearch.js
1 // ==UserScript==
2 // @name         ADS changeset comment search
3 // @version      0.3
4 // @description  Places a searchbox somewhere to search commit messages
5 // @author       Tobias Sachs
6 // @match        https://ads/*
7 // @grant        GM_addStyle
8 // @updateURL    https://src.twobees.de/?p=tampermonkeyscripts.git;a=blob_plain;f=AzureDevOpsChangesetCommentSearch.js;hb=HEAD
9 // @downloadURL  https://src.twobees.de/?p=tampermonkeyscripts.git;a=blob_plain;f=AzureDevOpsChangesetCommentSearch.js;hb=HEAD
10 // ==/UserScript==
11
12 // 0.3: - fix abort search
13 //      - filter reuslts by committers name
14 //      - no need for an observer
15 //      - use GM_addStyle to improve readability
16 //      - "Escape" stops search and removes resultstab
17
18 /* jshint esversion:6 */
19 (function() {
20     'use strict';
21
22     let searchDelayTimerId;
23     let pollUrl = 'https://ads/HeBa/Entwicklung/_apis/tfvc/changesets?maxCommentLength=500?$top=1000?searchCriteria.itemPath=$/Entwicklung/HEAD';
24     let skipParm = '&$skip=';
25     let changeSetUrl = `/HeBa/Entwicklung/_versionControl/changeset/`;
26
27     let totalReceived = 0;
28     let matchesFound = 0;
29     let searchTerm = "";
30
31     GM_addStyle(`#ts_cs_search {
32                      display: block;
33                      background: white;
34                      position: absolute;
35                      left: 450px;
36                      top: 0px;
37                      height: auto;
38                      z-index: 9999;
39                  }`);
40     GM_addStyle(`.ts_match {
41                      background: orange;
42                  }`);
43     GM_addStyle(`.ts_found {
44                      padding: 2px;
45                  }`);
46     GM_addStyle(`.ts_found a {
47                      text-decoration: none;
48                  }`);
49     GM_addStyle(`#ts_searchResults{
50                      max-height: 500px;
51                      background:#CCCCCC;
52                      overflow: auto;
53                  }`);
54
55     let searchDelayed = function(){
56         if (searchDelayTimerId) {
57             clearTimeout(searchDelayTimerId);
58         }
59
60         searchDelayTimerId = setTimeout(
61             () => {
62                 searchDelayTimerId = undefined;
63                 startSearching();
64             },
65             200);
66     };
67
68     let pressEscapeToAbort = function(e) {
69         if (e.key == "Escape"){
70             document.getElementById('ts_searchBox').value = "";
71             startSearching(); // will abort search and clean results
72         }
73     };
74
75     let requestNext = function(skipCnt, currentSearch){
76         let xhr = new XMLHttpRequest();
77         xhr.onload = () => searchItems(xhr, currentSearch);
78         xhr.onerror = function (e){
79             console.warning(`poll error: ${e.type}: ${e.loaded} bytes transferred\n` + JSON.stringify(e));
80         };
81         xhr.open('GET', pollUrl + skipParm + skipCnt, true);
82         xhr.send();
83     };
84
85     let searchItems = function(xhr, searchStr){
86         if (xhr.status !== 200){
87             console.info("poll failed: " + xhr.statusText);
88             return;
89         }
90
91         if (searchStr !== searchTerm)
92         {
93             console.info("Searchterm changed while searching to " + searchTerm);
94             return;
95         }
96
97         let sp = searchStr.split(/(von:|from:)/i);
98         let searchName = undefined;
99         let search = searchStr;
100         if (sp.length == 3){
101             search = sp[0].trim();
102             searchName = sp[2].trim();
103         }
104
105         let resultDiv = document.getElementById('ts_searchResults');
106         let statsSpan = document.getElementById('ts_searchStats');
107
108         var d = JSON.parse(xhr.responseText);
109         let regex = undefined;
110         let regexAuthor = undefined;
111         if (search){
112             regex = new RegExp("("+search+")", 'gi');
113         }
114         if (searchName){
115             regexAuthor = new RegExp("("+searchName+")", 'i');
116         }
117         totalReceived += d.value.length;
118         for (let e in d.value){
119             let cs = d.value[e];
120             if (regex !== undefined && regexAuthor !== undefined)
121             {
122                 if (!(cs.comment && regex.test(cs.comment) && regexAuthor.test(cs.author.displayName)))
123                 {
124                     continue;
125                 }
126             }
127             else
128             {
129                 if ((
130                        (cs.comment && regex !== undefined && regex.test(cs.comment))
131                     || (regexAuthor !== undefined && regexAuthor.test(cs.author.displayName))
132                     ) == false){
133                     continue;
134                 }
135             }
136             matchesFound++;
137             let comment;
138             let author;
139             if (regex){
140                 comment = cs.comment.replace(regex, `<span class="ts_match">$1</span>`);
141             }
142             else
143             {
144                 comment = cs.comment;
145             }
146             if (regexAuthor){
147                 author = cs.author.displayName.replace(regexAuthor, `<span class="ts_match">$1</span>`);
148             }
149             else{
150                 author = cs.author.displayName;
151             }
152
153
154             let item = `<div class='ts_found'>
155                                <a href='${changeSetUrl}${cs.changesetId}'>
156                                   ${cs.changesetId}: ${comment}  (${author})
157                                </a>
158                             </div>`;
159             resultDiv.innerHTML += item;
160     }
161     statsSpan.innerHTML = "matches: " + matchesFound +" searched comments: " + totalReceived;
162     if (d.value.length <= 0)
163     {
164         statsSpan.innerHTML += " -- search done.";
165         return;
166     }
167
168     requestNext(totalReceived, searchStr);
169 };
170
171  let startSearching = function (){
172     searchTerm = document.getElementById('ts_searchBox').value;
173     let results = document.getElementById('ts_searchResults');
174     totalReceived = 0;
175     matchesFound = 0;
176     results.innerHTML = "";
177     document.getElementById('ts_searchStats').innerHTML = "";
178
179     if (searchTerm.length < 3){ return; }
180
181     console.debug("new search term: '" + searchTerm+"'");
182     requestNext(0, searchTerm);
183 };
184
185 let addSearch = () => {
186     if (window.location.href.includes("_apis")){ return; }
187
188     let search = document.getElementById('ts_cs_search');
189     if (search){ return; }
190
191     console.debug("adding searchbox");
192     let html = `<div id="ts_cs_search">
193                         <input id="ts_searchBox" width="120" placeholder="searchstring [von:Name]" />
194                         <span id="ts_searchStats"></span>
195                         <div id="ts_searchResults"></div>
196                     </div>`;
197
198         document.body.insertAdjacentHTML('afterbegin', html);
199         let s = document.getElementById('ts_searchBox');
200         s.addEventListener("keyup", searchDelayed);
201         document.body.addEventListener("keyup", pressEscapeToAbort);
202
203     };
204
205 addSearch();
206 })();