github.com/yrj2011/jx-test-infra@v0.0.0-20190529031832-7a2065ee98eb/gubernator/static/build.js (about) 1 // Given a DOM node, attempt to select it. 2 function select(node) { 3 var sel = window.getSelection(); 4 if (sel.toString() !== "") { 5 // User is already trying to do a drag-selection, don't prevent it. 6 return; 7 } 8 // Works in Chrome/Safari/FF/IE10+ 9 var range = document.createRange(); 10 range.selectNode(node); 11 sel.removeAllRanges(); 12 sel.addRange(range); 13 } 14 15 // Rewrite timestamps to respect the current locale. 16 function fix_timestamps() { 17 function replace(className, fmt) { 18 var tz = moment.tz.guess(); 19 var els = document.getElementsByClassName(className); 20 for (var i = 0; i < els.length; i++) { 21 var el = els[i]; 22 var epoch = el.getAttribute('data-epoch'); 23 if (epoch) { 24 var time = moment(1000 * epoch).tz(tz); 25 if (typeof fmt === 'function') { 26 el.innerText = fmt(time); 27 } else { 28 el.innerText = time.format(fmt); 29 } 30 } 31 } 32 } 33 replace('timestamp', 'YYYY-MM-DD HH:mm z') 34 replace('shorttimestamp', 'DD HH:mm') 35 replace('humantimestamp', function(t) { 36 var fmt = 'MMM D, Y'; 37 if (t.isAfter(moment().startOf('day'))) { 38 fmt = 'h:mm A'; 39 } else if (t.isAfter(moment().startOf('year'))) { 40 fmt = 'MMM D'; 41 } 42 return t.format(fmt); 43 }) 44 } 45 46 var get_cache = {}; 47 48 // Download a file from GCS or elsewhere, and run "callback" with its contents. 49 function get(uri, callback) { 50 if (get_cache[uri]) { 51 callback(get_cache[uri]); 52 return; 53 } 54 if (uri[0] === '/') { 55 // Matches gs://bucket/file/path -> [..., "bucket", "file/path"] 56 // /bucket/file/path -> [..., "bucket", "file/path"] 57 var groups = uri.match(/([^/:]+)\/(.*)/); 58 var bucket = groups[1], path = groups[2]; 59 var url = 'https://www.googleapis.com/storage/v1/b/' + bucket + '/o/' + 60 encodeURIComponent(path) + '?alt=media'; 61 } else { 62 var url = uri; 63 } 64 var req = new XMLHttpRequest(); 65 req.open('GET', url); 66 req.onload = function(resp) { 67 get_cache[uri] = {status: req.status, response: req.response}; 68 callback(req); 69 } 70 req.send(); 71 } 72 73 function expand_lines(els, data) { 74 var lines = data.split('\n'); 75 var parent = els[0].parentElement; 76 for (var i = 0; i < els.length; i++) { 77 var el = els[i]; 78 var range = el.dataset['range'].split('-'); 79 var chunk = lines.slice(range[0], range[1]); 80 var chunk = chunk.join('\n'); 81 if (el.previousSibling) { 82 el.previousSibling.appendData(chunk); 83 el.remove(); 84 } else if (el.nextSibling) { 85 el.nextSibling.data = chunk + el.nextSibling.data; 86 el.remove(); 87 } 88 } 89 parent.normalize(); // merge adjacent text nodes 90 fix_escape_codes(); // colorize new segments 91 } 92 93 function expand_skipped(els) { 94 var src = els[0].parentElement.dataset['src']; 95 get(src, function(req) { 96 if (req.status == 401) { // unauthorized 97 // try proxying through Gubernator 98 var proxyUrl = document.location.origin + '/gcsproxy?path=' + escape(src); 99 document.getElementById('rawloglink').href = proxyUrl 100 get(proxyUrl, function (req) { expand_lines(els, req.response); }) 101 } else { 102 expand_lines(els, req.response); 103 } 104 }); 105 document.querySelector('h2#log').innerHTML = 'Build Log'; 106 } 107 108 function expand_all(btn) { 109 var logs = document.querySelectorAll('pre[data-src]'); 110 for (var i = 0; i < logs.length; i++) { 111 var skips = logs[i].querySelectorAll('span.skip'); 112 if (skips.length > 0) { 113 expand_skipped(skips); 114 } 115 } 116 btn.remove(); 117 } 118 119 function expand_element(els) { 120 var parent = els[0].parentElement; 121 var hidden = parent.querySelectorAll(".hidden"); 122 for (var i = 0; i < hidden.length; i++) { 123 hidden[i].classList.toggle("hidden"); 124 } 125 els[0].classList.add("hidden"); 126 } 127 128 /* given a string containing ansi formatting directives, return a new one 129 with designated regions of text marked with the appropriate color directives, 130 and with all unknown directives stripped */ 131 function ansi_to_html(orig) { 132 // Given a cmd (like "32" or "0;97"), some enclosed body text, and the original string, 133 // either return the body wrapped in an element to achieve the desired result, or the 134 // original string if nothing works. 135 function annotate(cmd, body, orig) { 136 var code = +(cmd.replace('0;', '')); 137 if (code === 0) // reset 138 return body; 139 else if (code === 1) // bold 140 return '<em>' + body + '</em>'; 141 else if (30 <= code && code <= 37) // foreground color 142 return '<span class="ansi-' + (code - 30) + '">' + body + '</span>' 143 else if (90 <= code && code <= 97) // foreground color, bright 144 return '<span class="ansi-' + (code - 90 + 8) + '">' + body + '</span>' 145 return orig; // fallback: don't change anything 146 } 147 // Find commands, optionally followed by a bold command, with some content, then a reset command. 148 // Unpaired commands are *not* handled here, but they're very uncommon. 149 var filtered = orig.replace(/\033\[([0-9;]*)\w(\033\[1m)?([^\033]*?)\033\[0m/g, function(match, code, bold, body, offset, string) { 150 if (bold !== undefined) // normal code + bold 151 return '<em>' + annotate(code, body, string) + '</em>'; 152 return annotate(code, body, string); 153 }) 154 // Strip out anything left over. 155 return filtered.replace(/\033\[([0-9;]*\w)/g, function(match, cmd, offset, string) { 156 console.log('unhandled ansi code: ', cmd, "context:", JSON.stringify(filtered.slice(offset-50,offset+50))); 157 return ''; 158 }); 159 } 160 161 function fix_escape_codes() { 162 var logs = document.querySelectorAll('pre[data-src]'); 163 for (var i = 0; i < logs.length; i++) { 164 var orig = logs[i].innerHTML; 165 var newer = ansi_to_html(orig); 166 if (orig !== newer) { 167 logs[i].innerHTML = newer; 168 } 169 } 170 } 171 172 /* Remove unicode sequences caused by colorized output in junit.xml */ 173 function remove_unicode_escape_codes() { 174 var errors = document.querySelectorAll('pre.error') 175 for (var i = 0; i < errors.length; i++) { 176 var orig = errors[i].innerHTML 177 var newer = orig.replace(/\ufffd\[\d+m/g, "") 178 if (orig !== newer) { 179 errors[i].innerHTML = newer; 180 } 181 } 182 } 183 184 function init() { 185 fix_timestamps(); 186 fix_escape_codes(); 187 remove_unicode_escape_codes(); 188 document.body.onclick = function(evt) { 189 var target = evt.target; 190 if (target.nodeName === 'SPAN' && target.classList.contains('skip')) { 191 expand_skipped([target]); 192 evt.preventDefault(); 193 } 194 if (target.nodeName === 'SPAN' && target.classList.contains('expand')) { 195 expand_element([target]); 196 evt.preventDefault(); 197 } 198 } 199 } 200 201 if (typeof module !== 'undefined' && module.exports) { 202 // enable node.js `require('./build')` to work for testing 203 module.exports = { 204 ansi_to_html: ansi_to_html 205 } 206 } 207 208 // Acknowledge a PR to suppress it. If repo is "CLEAR", clear acks instead. 209 function ack(event, repo, number, latest) { 210 event.stopPropagation(); 211 var req = new XMLHttpRequest(); 212 req.open('POST', '/pr'); 213 req.onload = function(resp) { 214 if (req.status != 200) return; 215 var row = document.getElementById('needs-attention ' + repo + ' ' + number); 216 if (row) { 217 row.remove(); 218 } 219 } 220 req.send(JSON.stringify({ 221 command: 'ack', 222 repo: repo, 223 number: number, 224 latest: latest, 225 })); 226 } 227 228 // Reset the acknowledged PRs 229 function ack_clear() { 230 var req = new XMLHttpRequest(); 231 req.open('POST', '/pr'); 232 req.onload = function(resp) { 233 if (req.status != 200) return; 234 document.location = document.location; // refresh 235 } 236 req.send(JSON.stringify({ 237 command: 'ack-clear', 238 })); 239 }