Changeset 84599 in vbox for trunk/src/VBox/ValidationKit/testmanager/htdocs
- Timestamp:
- May 29, 2020 1:12:32 AM (5 years ago)
- Location:
- trunk/src/VBox/ValidationKit/testmanager/htdocs
- Files:
-
- 3 edited
Legend:
- Unmodified
- Added
- Removed
-
trunk/src/VBox/ValidationKit/testmanager/htdocs/css/common.css
r83440 r84599 1155 1155 } 1156 1156 1157 .tmvcstooltipnew { 1158 padding: 0px; 1159 min-width: 50em; 1160 overflow: hidden; 1161 border: 0px none; 1162 background-color: #f9f9f9; 1163 } 1164 1157 1165 1158 1166 /* -
trunk/src/VBox/ValidationKit/testmanager/htdocs/css/tooltip.css
r83440 r84599 28 28 * Form the main divs in template-tooltip.html. 29 29 */ 30 #tooltip{30 .tooltip-main { 31 31 width: 100%; 32 32 } 33 33 34 #tooltip-inner {34 .tooltip-inner { 35 35 clear: both; 36 36 border: 2px solid black; -
trunk/src/VBox/ValidationKit/testmanager/htdocs/js/common.js
r83459 r84599 144 144 return true; 145 145 return false; 146 } 147 148 /** 149 * Parses a typical ISO timestamp, returing a Date object, reasonably 150 * forgiving, but will throw weird indexing/conversion errors if the input 151 * is malformed. 152 * 153 * @returns Date object. 154 * @param sTs The timestamp to parse. 155 * @sa parseIsoTimestamp() in utils.py. 156 */ 157 function parseIsoTimestamp(sTs) 158 { 159 /* YYYY-MM-DD */ 160 var iYear = parseInt(sTs.substring(0, 4), 10); 161 console.assert(sTs.charAt(4) == '-'); 162 var iMonth = parseInt(sTs.substring(5, 7), 10); 163 console.assert(sTs.charAt(7) == '-'); 164 var iDay = parseInt(sTs.substring(8, 10), 10); 165 166 /* Skip separator */ 167 var sTime = sTs.substring(10); 168 while ('Tt \t\n\r'.includes(sTime.charAt(0))) { 169 sTime = sTime.substring(1); 170 } 171 172 /* HH:MM:SS */ 173 var iHour = parseInt(sTime.substring(0, 2), 10); 174 console.assert(sTime.charAt(2) == ':'); 175 var iMin = parseInt(sTime.substring(3, 5), 10); 176 console.assert(sTime.charAt(5) == ':'); 177 var iSec = parseInt(sTime.substring(6, 8), 10); 178 179 /* Fraction? */ 180 var offTime = 8; 181 var iMicroseconds = 0; 182 if (offTime < sTime.length && '.,'.includes(sTime.charAt(offTime))) 183 { 184 offTime += 1; 185 var cchFraction = 0; 186 while (offTime + cchFraction < sTime.length && '0123456789'.includes(sTime.charAt(offTime + cchFraction))) 187 cchFraction += 1; 188 if (cchFraction > 0) 189 { 190 iMicroseconds = parseInt(sTime.substring(offTime, offTime + cchFraction), 10); 191 offTime += cchFraction; 192 while (cchFraction < 6) 193 { 194 iMicroseconds *= 10; 195 cchFraction += 1; 196 } 197 while (cchFraction > 6) 198 { 199 iMicroseconds = iMicroseconds / 10; 200 cchFraction -= 1; 201 } 202 } 203 } 204 var iMilliseconds = (iMicroseconds + 499) / 1000; 205 206 /* Naive? */ 207 var oDate = new Date(Date.UTC(iYear, iMonth, iDay, iHour, iMin, iSec, iMilliseconds)); 208 if (offTime >= sTime.length) 209 return oDate; 210 211 /* Zulu? */ 212 if (offTime >= sTime.length || 'Zz'.includes(sTime.charAt(offTime))) 213 return oDate; 214 215 /* Some kind of offset afterwards. */ 216 var chSign = sTime.charAt(offTime); 217 if ('+-'.includes(chSign)) 218 { 219 offTime += 1; 220 var cMinTz = parseInt(sTime.substring(offTime, offTime + 2), 10) * 60; 221 offTime += 2; 222 if (offTime < sTime.length && sTime.charAt(offTime) == ':') 223 offTime += 1; 224 if (offTime + 2 <= sTime.length) 225 { 226 cMinTz += parseInt(sTime.substring(offTime, offTime + 2), 10); 227 offTime += 2; 228 } 229 console.assert(offTime == sTime.length); 230 if (chSign == '-') 231 cMinTz = -cMinTz; 232 233 return new Date(oDate.getTime() + cMinTz * 60000); 234 } 235 console.assert(false); 236 return oDate; 237 } 238 239 /** 240 * Escapes special characters to HTML-safe sequences, for element use. 241 * 242 * @returns Escaped string suitable for HTML. 243 * @param sText Plain text to escape. 244 */ 245 function escapeElem(sText) 246 { 247 sText = sText.replace(/&/g, '&'); 248 sText = sText.replace(/>/g, '<'); 249 return sText.replace(/</g, '>'); 250 } 251 252 /** 253 * Escapes special characters to HTML-safe sequences, for double quoted 254 * attribute use. 255 * 256 * @returns Escaped string suitable for HTML. 257 * @param sText Plain text to escape. 258 */ 259 function escapeAttr(sText) 260 { 261 sText = sText.replace(/&/g, '&'); 262 sText = sText.replace(/</g, '<'); 263 sText = sText.replace(/>/g, '>'); 264 return sText.replace(/"/g, '"'); 146 265 } 147 266 … … 692 811 */ 693 812 813 /** Enables non-iframe tooltip code. */ 814 var g_fNewTooltips = true; 815 694 816 /** Where we keep tooltip elements when not displayed. */ 695 817 var g_dTooltips = {}; … … 902 1024 function tooltipElementOnMouseEnter() 903 1025 { 904 //console.log('tooltipElementOnMouseEnter: arguments.length='+arguments.length+' [0]='+arguments[0]);905 //console.log('ENT: currentTarget='+arguments[0].currentTarget);1026 console.log('tooltipElementOnMouseEnter: arguments.length='+arguments.length+' [0]='+arguments[0]); 1027 console.log('ENT: currentTarget='+arguments[0].currentTarget+' id='+arguments[0].currentTarget.id+' class='+arguments[0].currentTarget.className); 906 1028 tooltipResetShowTimer(); 907 1029 tooltipResetHideTimer(); … … 919 1041 function tooltipElementOnMouseOut() 920 1042 { 921 //console.log('tooltipElementOnMouseOut: arguments.length='+arguments.length+' [0]='+arguments[0]); 922 //console.log('OUT: currentTarget='+arguments[0].currentTarget); 1043 var oEvt = arguments[0]; 1044 console.log('tooltipElementOnMouseOut: arguments.length='+arguments.length+' [0]='+oEvt); 1045 console.log('OUT: currentTarget='+oEvt.currentTarget+' id='+oEvt.currentTarget.id+' class='+oEvt.currentTarget.className); 1046 1047 /* Ignore the event if leaving to a child element. */ 1048 var oElm = oEvt.toElement || oEvt.relatedTarget; 1049 if (oElm != this && oElm) 1050 { 1051 for (;;) 1052 { 1053 oElm = oElm.parentNode; 1054 if (!oElm || oElm == window) 1055 break; 1056 if (oElm == this) 1057 { 1058 console.log('OUT: was to child! - ignore'); 1059 return false; 1060 } 1061 } 1062 } 1063 923 1064 tooltipHide(); 924 1065 return true; … … 931 1072 * work around various browser differences too. 932 1073 */ 933 function svnHistoryTooltipO nLoad()934 { 935 //console.log('svnHistoryTooltipO nLoad');1074 function svnHistoryTooltipOldOnLoad() 1075 { 1076 //console.log('svnHistoryTooltipOldOnLoad'); 936 1077 937 1078 /* … … 953 1094 if (cx >= cxNeeded) 954 1095 { 955 //console.log('svnHistoryTooltipO nLoad: overflowX -> hidden');1096 //console.log('svnHistoryTooltipOldOnLoad: overflowX -> hidden'); 956 1097 oIFrameElement.style.overflowX = 'hidden'; 957 1098 } … … 975 1116 if (cy >= cyNeeded) 976 1117 { 977 //console.log('svnHistoryTooltipO nLoad: overflowY -> hidden');1118 //console.log('svnHistoryTooltipOldOnLoad: overflowY -> hidden'); 978 1119 oIFrameElement.style.overflowY = 'hidden'; 979 1120 } … … 985 1126 //console.log('cyNeeded='+cyNeeded+' cyMax='+g_oCurrentTooltip.cyMax+' cySpace='+cySpace+' cy='+cy); 986 1127 //console.log('oIFrameElement.offsetTop='+oIFrameElement.offsetTop); 987 //console.log('svnHistoryTooltipO nLoad: cx='+cx+'cxMax='+g_oCurrentTooltip.cxMax+' cxNeeded='+cxNeeded+' cy='+cy+' cyMax='+g_oCurrentTooltip.cyMax);1128 //console.log('svnHistoryTooltipOldOnLoad: cx='+cx+'cxMax='+g_oCurrentTooltip.cxMax+' cxNeeded='+cxNeeded+' cy='+cy+' cyMax='+g_oCurrentTooltip.cyMax); 988 1129 989 1130 tooltipRepositionOnLoad(); 990 1131 } 991 1132 return true; 1133 } 1134 1135 /** 1136 * iframe.onload hook that repositions and resizes the tooltip. 1137 * 1138 * This is a little hacky and we're calling it one or three times too many to 1139 * work around various browser differences too. 1140 */ 1141 function svnHistoryTooltipNewOnLoad() 1142 { 1143 //console.log('svnHistoryTooltipNewOnLoad'); 1144 1145 /* 1146 * Resize the tooltip to better fit the content. 1147 */ 1148 tooltipRepositionOnLoad(); /* Sets cxMax and cyMax. */ 1149 oTooltip = g_oCurrentTooltip; 1150 if (oTooltip) 1151 { 1152 var oElmInner = oTooltip.oInnerElm; 1153 var cxSpace = Math.max(oElmInner.offsetLeft * 2, 0); /* simplified */ 1154 var cySpace = Math.max(oElmInner.offsetTop * 2, 0); /* simplified */ 1155 var cxNeeded = oElmInner.scrollWidth + cxSpace; 1156 var cyNeeded = oElmInner.scrollHeight + cySpace; 1157 var cx = Math.min(cxNeeded, oTooltip.cxMax); 1158 1159 oTooltip.oElm.width = cx + 'px'; 1160 oElmInner.width = (cx - cxSpace) + 'px'; 1161 if (cx >= cxNeeded) 1162 { 1163 //console.log('svnHistoryTooltipNewOnLoad: overflowX -> hidden'); 1164 oElmInner.style.overflowX = 'hidden'; 1165 } 1166 else 1167 { 1168 oElmInner.style.overflowX = 'scroll'; 1169 } 1170 1171 var cy = Math.min(cyNeeded, oTooltip.cyMax); 1172 if (cyNeeded > oTooltip.cyMax && oTooltip.cyMaxUp > 0) 1173 { 1174 var cyMove = Math.min(cyNeeded - oTooltip.cyMax, oTooltip.cyMaxUp); 1175 oTooltip.cyMax += cyMove; 1176 oTooltip.yPos -= cyMove; 1177 oTooltip.oElm.style.top = oTooltip.yPos + 'px'; 1178 cy = Math.min(cyNeeded, oTooltip.cyMax); 1179 } 1180 1181 oTooltip.oElm.height = cy + 'px'; 1182 oElmInner.height = (cy - cySpace) + 'px'; 1183 if (cy >= cyNeeded) 1184 { 1185 //console.log('svnHistoryTooltipNewOnLoad: overflowY -> hidden'); 1186 oElmInner.style.overflowY = 'hidden'; 1187 } 1188 else 1189 { 1190 oElmInner.style.overflowY = 'scroll'; 1191 } 1192 1193 //console.log('cyNeeded='+cyNeeded+' cyMax='+oTooltip.cyMax+' cySpace='+cySpace+' cy='+cy); 1194 //console.log('oElmInner.offsetTop='+oElmInner.offsetTop); 1195 //console.log('svnHistoryTooltipNewOnLoad: cx='+cx+'cxMax='+oTooltip.cxMax+' cxNeeded='+cxNeeded+' cy='+cy+' cyMax='+oTooltip.cyMax); 1196 1197 tooltipRepositionOnLoad(); 1198 } 1199 return true; 1200 } 1201 1202 1203 function svnHistoryTooltipNewOnReadState(oTooltip, oRestReq, oParent) 1204 { 1205 console.log('svnHistoryTooltipNewOnReadState'); 1206 console.log('svnHistoryTooltipNewOnReadState'); 1207 1208 /* 1209 * Check the result and translate it to a javascript object (oResp). 1210 */ 1211 var oResp = null; 1212 var sHtml; 1213 if (oRestReq.status != 200) 1214 { 1215 console.log('svnHistoryTooltipNewOnReadState: status=' + oRestReq.status); 1216 sHtml = '<p>error: status=' + oRestReq.status + '</p>'; 1217 } 1218 else 1219 { 1220 try 1221 { 1222 oResp = JSON.parse(oRestReq.responseText); 1223 } 1224 catch (oEx) 1225 { 1226 console.log('JSON.parse threw: ' + oEx.toString()); 1227 console.log(oRestReq.responseText); 1228 sHtml = '<p>error: JSON.parse threw: ' + oEx.toString() + '</p>'; 1229 } 1230 } 1231 1232 /* 1233 * Generate the HTML. 1234 * 1235 * Note! Make sure the highlighting code in svnHistoryTooltipNewDelayedShow 1236 * continues to work after modifying this code. 1237 */ 1238 if (oResp) 1239 { 1240 var asDaysOfTheWeek = [ 'Sunday', 'Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday' ]; 1241 sHtml = '<div class="tmvcstimeline tmvcstimelinetooltip">\n'; 1242 1243 var aoCommits = oResp.aoCommits; 1244 var cCommits = oResp.aoCommits.length; 1245 var iCurDay = null; 1246 var i; 1247 for (i = 0; i < cCommits; i++) 1248 { 1249 var oCommit = aoCommits[i]; 1250 var tsCreated = parseIsoTimestamp(oCommit.tsCreated); 1251 var iCommitDay = Math.floor((tsCreated.getTime() + tsCreated.getTimezoneOffset()) / (24 * 60 * 60 * 1000)); 1252 if (iCurDay === null || iCurDay != iCommitDay) 1253 { 1254 if (iCurDay !== null) 1255 sHtml += ' </dl>\n'; 1256 iCurDay = iCommitDay; 1257 sHtml += ' <h2>' + tsCreated.toISOString().split('T')[0] + ' ' + asDaysOfTheWeek[tsCreated.getDay()] + '</h2>\n'; 1258 sHtml += ' <dl>\n'; 1259 } 1260 Date 1261 1262 var sHighligh = ''; 1263 if (oCommit.iRevision == oTooltip.iRevision) 1264 sHighligh += ' class="tmvcstimeline-highlight"'; 1265 1266 sHtml += ' <dt id="r' + oCommit.iRevision + '"' + sHighligh + '>'; 1267 sHtml += '<a href="' + oResp.sTracChangesetUrlFmt.replace('%(iRevision)s', oCommit.iRevision.toString()); 1268 sHtml += '" target="_blank">'; 1269 sHtml += '<span class="tmvcstimeline-time">' + escapeElem(tsCreated.toLocaleTimeString()) + '</span>' 1270 sHtml += ' Changeset <span class="tmvcstimeline-rev">[' + oCommit.iRevision + ']</span>'; 1271 sHtml += ' by <span class="tmvcstimeline-author">' + escapeElem(oCommit.sAuthor) + '</span>'; 1272 sHtml += '</a></dt>\n'; 1273 sHtml += ' <dd' + sHighligh + '>' + escapeElem(oCommit.sMessage) + '</dd>\n'; 1274 } 1275 1276 if (iCurDay !== null) 1277 sHtml += ' </dl>\n'; 1278 sHtml += '</div>'; 1279 } 1280 1281 console.log('svnHistoryTooltipNewOnReadState: sHtml=' + sHtml); 1282 oTooltip.oInnerElm.innerHTML = sHtml; 1283 1284 tooltipReallyShow(oTooltip, oParent); 1285 svnHistoryTooltipNewOnLoad(); 992 1286 } 993 1287 … … 1038 1332 //console.log('svnHistoryTooltipShow ' + sRepository); 1039 1333 1040 function svnHistoryTooltip DelayedShow()1334 function svnHistoryTooltipOldDelayedShow() 1041 1335 { 1042 1336 var sSrc; 1043 1337 1044 1338 var oTooltip = g_dTooltips[sKey]; 1045 //console.log('svnHistoryTooltip DelayedShow ' + sRepository + ' ' + oTooltip);1339 //console.log('svnHistoryTooltipOldDelayedShow ' + sRepository + ' ' + oTooltip); 1046 1340 if (!oTooltip) 1047 1341 { … … 1071 1365 oIFrameElement.style.position = 'relative'; 1072 1366 oIFrameElement.onmouseenter = tooltipElementOnMouseEnter; 1073 oIFrameElement.onmouseout = tooltipElementOnMouseOut;1367 //oIFrameElement.onmouseout = tooltipElementOnMouseOut; 1074 1368 oTooltip.oElm.appendChild(oIFrameElement); 1075 1369 oTooltip.oIFrame = oIFrameElement; … … 1082 1376 /*console.log('iframe/onload');*/ 1083 1377 tooltipReallyShow(oTooltip, oParent); 1084 svnHistoryTooltipO nLoad();1378 svnHistoryTooltipOldOnLoad(); 1085 1379 }, isBrowserInternetExplorer() ? 256 : 128); 1086 1380 }; … … 1105 1399 setTimeout(function() { /* Slight delay to make sure it scrolls before it's shown. */ 1106 1400 tooltipReallyShow(oTooltip, oParent); 1107 svnHistoryTooltipO nLoad();1401 svnHistoryTooltipOldOnLoad(); 1108 1402 }, isBrowserInternetExplorer() ? 256 : 64); 1109 1403 } … … 1114 1408 { 1115 1409 tooltipReallyShow(oTooltip, oParent); 1116 svnHistoryTooltipOnLoad(); 1117 } 1118 } 1119 } 1410 svnHistoryTooltipOldOnLoad(); 1411 } 1412 } 1413 } 1414 1415 function svnHistoryTooltipNewDelayedShow() 1416 { 1417 var sSrc; 1418 1419 var oTooltip = g_dTooltips[sKey]; 1420 console.log('svnHistoryTooltipNewDelayedShow: ' + sRepository + ' ' + oTooltip); 1421 if (!oTooltip) 1422 { 1423 /* 1424 * Create a new tooltip element. 1425 */ 1426 console.log('creating ' + sKey); 1427 1428 var oElm = document.createElement('div'); 1429 oElm.setAttribute('id', sKey); 1430 oElm.className = 'tmvcstooltipnew'; 1431 //oElm.setAttribute('style', 'display:none; position: absolute;'); 1432 oElm.style.display = 'none'; /* Note! Must stay hidden till loaded, or parent jumps with #rXXXX.*/ 1433 oElm.style.position = 'absolute'; 1434 oElm.style.zIndex = 6001; 1435 oElm.onmouseenter = tooltipElementOnMouseEnter; 1436 oElm.onmouseout = tooltipElementOnMouseOut; 1437 1438 var oInnerElm = document.createElement('div'); 1439 oInnerElm.className = 'tooltip-inner'; 1440 oElm.appendChild(oInnerElm); 1441 1442 oTooltip = {}; 1443 oTooltip.oElm = oElm; 1444 oTooltip.oInnerElm = oInnerElm; 1445 oTooltip.xPos = 0; 1446 oTooltip.yPos = 0; 1447 oTooltip.cxMax = 0; 1448 oTooltip.cyMax = 0; 1449 oTooltip.cyMaxUp = 0; 1450 oTooltip.xScroll = 0; 1451 oTooltip.yScroll = 0; 1452 oTooltip.iRevision = iRevision; /**< For :target/highlighting */ 1453 1454 oRestReq = new XMLHttpRequest(); 1455 oRestReq.onreadystatechange = function() { svnHistoryTooltipNewOnReadState(oTooltip, this, oParent); } 1456 oRestReq.open('GET', sUrlPrefix + 'rest.py?sPath=vcs/changelog/' + sRepository 1457 + '/' + svnHistoryTooltipCalcLastRevision(iRevision) + '/' + g_cTooltipSvnRevisions); 1458 oRestReq.setRequestHeader('Content-type', 'application/json'); 1459 1460 document.body.appendChild(oTooltip.oElm); 1461 g_dTooltips[sKey] = oTooltip; 1462 1463 oRestReq.send(''); 1464 } 1465 else 1466 { 1467 /* 1468 * Show the existing one, possibly with different highlighting. 1469 * Note! Update this code when changing svnHistoryTooltipNewOnReadState. 1470 */ 1471 if (oTooltip.iRevision != iRevision) 1472 { 1473 //console.log('Changing revision ' + oTooltip.iRevision + ' -> ' + iRevision); 1474 var oElmTimelineDiv = oTooltip.oInnerElm.firstElementChild; 1475 var i; 1476 for (i = 0; i < oElmTimelineDiv.children.length; i++) 1477 { 1478 var oElm = oElmTimelineDiv.children[i]; 1479 //console.log('oElm='+oElm+' id='+oElm.id+' nodeName='+oElm.nodeName); 1480 if (oElm.nodeName == 'DL') 1481 { 1482 var iCurRev = iRevision - 64; 1483 var j; 1484 for (j = 0; i < oElm.children.length; i++) 1485 { 1486 var oDlSubElm = oElm.children[i]; 1487 //console.log(' oDlSubElm='+oDlSubElm+' id='+oDlSubElm.id+' nodeName='+oDlSubElm.nodeName+' className='+oDlSubElm.className); 1488 if (oDlSubElm.id.length > 2) 1489 iCurRev = parseInt(oDlSubElm.id.substring(1), 10); 1490 if (iCurRev == iRevision) 1491 oDlSubElm.className = 'tmvcstimeline-highlight'; 1492 else 1493 oDlSubElm.className = ''; 1494 } 1495 } 1496 } 1497 oTooltip.iRevision = iRevision; 1498 } 1499 1500 tooltipReallyShow(oTooltip, oParent); 1501 svnHistoryTooltipNewOnLoad(); 1502 } 1503 } 1504 1120 1505 1121 1506 /* … … 1123 1508 */ 1124 1509 tooltipResetShowTimer(); 1125 g_idTooltipShowTimer = setTimeout(svnHistoryTooltipDelayedShow, 512); 1510 if (g_fNewTooltips) 1511 g_idTooltipShowTimer = setTimeout(svnHistoryTooltipNewDelayedShow, 512); 1512 else 1513 g_idTooltipShowTimer = setTimeout(svnHistoryTooltipOldDelayedShow, 512); 1126 1514 } 1127 1515 … … 1137 1525 function svnHistoryTooltipShow(oEvt, sRepository, iRevision) 1138 1526 { 1139 return svnHistoryTooltipShowEx(oEvt, sRepository, iRevision, '') 1527 return svnHistoryTooltipShowEx(oEvt, sRepository, iRevision, ''); 1140 1528 } 1141 1529
Note:
See TracChangeset
for help on using the changeset viewer.