]> src.twobees.de Git - tampermonkeyscripts.git/blob - AzureDevOpsChangesetCommentSearch.js
0.4: - improve position of search box
[tampermonkeyscripts.git] / AzureDevOpsChangesetCommentSearch.js
1 // ==UserScript==
2 // @name         ADS changeset comment search
3 // @version      0.4
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.4: - improve position of search box
13 // 0.3: - fix abort search
14 //      - filter reuslts by committers name
15 //      - no need for an observer
16 //      - use GM_addStyle to improve readability
17 //      - "Escape" stops search and removes resultstab
18
19 /* jshint esversion:6 */
20 (function() {
21     'use strict';
22
23     let searchDelayTimerId;
24     let pollUrl = 'https://ads/HeBa/Entwicklung/_apis/tfvc/changesets?maxCommentLength=500?$top=1000?searchCriteria.itemPath=$/Entwicklung/HEAD';
25     let skipParm = '&$skip=';
26     let changeSetUrl = `/HeBa/Entwicklung/_versionControl/changeset/`;
27
28     let totalReceived = 0;
29     let matchesFound = 0;
30     let searchTerm = "";
31
32     GM_addStyle(`#ts_cs_search {
33                     position: absolute;
34                     left: 50%;
35                     top: 0px;
36                     height: auto;
37                     z-index: 9999;
38                     transform: translateX(-50%);
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" 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 })();