VirtualBox

Ignore:
Timestamp:
May 29, 2020 1:12:32 AM (5 years ago)
Author:
vboxsync
Message:

TestManager: Reworking the changelog tooltip and adding a way to get commits for bugs for the bug trackers. Work in progress

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  
    11551155}
    11561156
     1157.tmvcstooltipnew {
     1158    padding:    0px;
     1159    min-width:  50em;
     1160    overflow:   hidden;
     1161    border:     0px none;
     1162    background-color: #f9f9f9;
     1163}
     1164
    11571165
    11581166/*
  • trunk/src/VBox/ValidationKit/testmanager/htdocs/css/tooltip.css

    r83440 r84599  
    2828 * Form the main divs in template-tooltip.html.
    2929 */
    30 #tooltip {
     30.tooltip-main {
    3131    width:          100%;
    3232}
    3333
    34 #tooltip-inner {
     34.tooltip-inner {
    3535    clear:          both;
    3636    border:         2px solid black;
  • trunk/src/VBox/ValidationKit/testmanager/htdocs/js/common.js

    r83459 r84599  
    144144            return true;
    145145    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 */
     157function 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 */
     245function escapeElem(sText)
     246{
     247    sText = sText.replace(/&/g, '&amp;');
     248    sText = sText.replace(/>/g, '&lt;');
     249    return  sText.replace(/</g, '&gt;');
     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 */
     259function escapeAttr(sText)
     260{
     261    sText = sText.replace(/&/g, '&amp;');
     262    sText = sText.replace(/</g, '&lt;');
     263    sText = sText.replace(/>/g, '&gt;');
     264    return  sText.replace(/"/g, '&quot;');
    146265}
    147266
     
    692811 */
    693812
     813/** Enables non-iframe tooltip code. */
     814var g_fNewTooltips       = true;
     815
    694816/** Where we keep tooltip elements when not displayed. */
    695817var g_dTooltips          = {};
     
    9021024function tooltipElementOnMouseEnter()
    9031025{
    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);
    9061028    tooltipResetShowTimer();
    9071029    tooltipResetHideTimer();
     
    9191041function tooltipElementOnMouseOut()
    9201042{
    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
    9231064    tooltipHide();
    9241065    return true;
     
    9311072 * work around various browser differences too.
    9321073 */
    933 function svnHistoryTooltipOnLoad()
    934 {
    935     //console.log('svnHistoryTooltipOnLoad');
     1074function svnHistoryTooltipOldOnLoad()
     1075{
     1076    //console.log('svnHistoryTooltipOldOnLoad');
    9361077
    9371078    /*
     
    9531094        if (cx >= cxNeeded)
    9541095        {
    955             //console.log('svnHistoryTooltipOnLoad: overflowX -> hidden');
     1096            //console.log('svnHistoryTooltipOldOnLoad: overflowX -> hidden');
    9561097            oIFrameElement.style.overflowX = 'hidden';
    9571098        }
     
    9751116        if (cy >= cyNeeded)
    9761117        {
    977             //console.log('svnHistoryTooltipOnLoad: overflowY -> hidden');
     1118            //console.log('svnHistoryTooltipOldOnLoad: overflowY -> hidden');
    9781119            oIFrameElement.style.overflowY = 'hidden';
    9791120        }
     
    9851126        //console.log('cyNeeded='+cyNeeded+' cyMax='+g_oCurrentTooltip.cyMax+' cySpace='+cySpace+' cy='+cy);
    9861127        //console.log('oIFrameElement.offsetTop='+oIFrameElement.offsetTop);
    987         //console.log('svnHistoryTooltipOnLoad: 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);
    9881129
    9891130        tooltipRepositionOnLoad();
    9901131    }
    9911132    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 */
     1141function 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
     1203function 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();
    9921286}
    9931287
     
    10381332    //console.log('svnHistoryTooltipShow ' + sRepository);
    10391333
    1040     function svnHistoryTooltipDelayedShow()
     1334    function svnHistoryTooltipOldDelayedShow()
    10411335    {
    10421336        var sSrc;
    10431337
    10441338        var oTooltip = g_dTooltips[sKey];
    1045         //console.log('svnHistoryTooltipDelayedShow ' + sRepository + ' ' + oTooltip);
     1339        //console.log('svnHistoryTooltipOldDelayedShow ' + sRepository + ' ' + oTooltip);
    10461340        if (!oTooltip)
    10471341        {
     
    10711365            oIFrameElement.style.position = 'relative';
    10721366            oIFrameElement.onmouseenter   = tooltipElementOnMouseEnter;
    1073             oIFrameElement.onmouseout     = tooltipElementOnMouseOut;
     1367            //oIFrameElement.onmouseout     = tooltipElementOnMouseOut;
    10741368            oTooltip.oElm.appendChild(oIFrameElement);
    10751369            oTooltip.oIFrame = oIFrameElement;
     
    10821376                                /*console.log('iframe/onload');*/
    10831377                                tooltipReallyShow(oTooltip, oParent);
    1084                                 svnHistoryTooltipOnLoad();
     1378                                svnHistoryTooltipOldOnLoad();
    10851379                           }, isBrowserInternetExplorer() ? 256 : 128);
    10861380            };
     
    11051399                    setTimeout(function() { /* Slight delay to make sure it scrolls before it's shown. */
    11061400                                   tooltipReallyShow(oTooltip, oParent);
    1107                                    svnHistoryTooltipOnLoad();
     1401                                   svnHistoryTooltipOldOnLoad();
    11081402                               }, isBrowserInternetExplorer() ? 256 : 64);
    11091403                }
     
    11141408            {
    11151409                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
    11201505
    11211506    /*
     
    11231508     */
    11241509    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);
    11261514}
    11271515
     
    11371525function svnHistoryTooltipShow(oEvt, sRepository, iRevision)
    11381526{
    1139     return svnHistoryTooltipShowEx(oEvt, sRepository, iRevision, '')
     1527    return svnHistoryTooltipShowEx(oEvt, sRepository, iRevision, '');
    11401528}
    11411529
Note: See TracChangeset for help on using the changeset viewer.

© 2024 Oracle Support Privacy / Do Not Sell My Info Terms of Use Trademark Policy Automated Access Etiquette