Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Customize selection for candidate needles + full diff view #1622

1 change: 0 additions & 1 deletion assets/assetpack.def
Expand Up @@ -17,7 +17,6 @@
< stylesheets/openqa.scss
< https://raw.githubusercontent.com/bootstrapthemesco/bootstrap-4-multi-dropdown-navbar/beta2.0/css/bootstrap-4-navbar.css
< https://cdnjs.cloudflare.com/ajax/libs/chosen/1.7.0/chosen.css
< stylesheets/result_preview.scss
<< https://raw.githubusercontent.com/twbs/bootstrap/v4.0.0/scss/bootstrap.scss
<< https://raw.githubusercontent.com/twbs/bootstrap/v4.0.0/scss/_functions.scss
<< https://raw.githubusercontent.com/twbs/bootstrap/v4.0.0/scss/_variables.scss
Expand Down
150 changes: 109 additions & 41 deletions assets/javascripts/needlediff.js
Expand Up @@ -99,6 +99,11 @@ NeedleDiff.prototype.draw = function() {
split = 1;
}

// Show full diff
if (this.fullNeedleImg) {
this.ctx.drawImage(this.fullNeedleImg, 0, 0, split, this.height, 0, 0, split, this.height);
}

// Draw all match boxes
this.matches.forEach(function(a, idx) {

Expand All @@ -118,39 +123,53 @@ NeedleDiff.prototype.draw = function() {
y_end = this.height;
}

if (split > x) {

// ...fill that left part with the original needle's area

if (split - x < width) {
width = split - x;
// draw match/area frames
orig = this.areas[idx];
this.ctx.lineWidth = lineWidth;
if (split > x && !this.fullNeedleImg) {
// fill left part with original needle's area
var usedWith = width;
if (split - x < usedWith) {
usedWith = split - x;
} else {
width += 1;
usedWith += 1;
}
if (!this.fullNeedleImg) {
// draw matching part of needle image
this.ctx.strokeStyle = NeedleDiff.strokecolor(a.type);
this.ctx.drawImage(this.needleImg, orig.xpos, orig.ypos,
usedWith, a.height, x, a.ypos, usedWith, a.height);
// draw frame of match area
this.ctx.lineWidth = lineWidth;
this.ctx.beginPath();
this.ctx.moveTo(x + usedWith, y_start);
this.ctx.lineTo(x, y_start);
this.ctx.lineTo(x, y_end);
this.ctx.lineTo(x + usedWith, y_end);
this.ctx.lineTo(x + usedWith, y_start);
this.ctx.stroke();
}
}

this.ctx.strokeStyle = NeedleDiff.strokecolor(a.type);

orig = this.areas[idx];
this.ctx.drawImage(this.needleImg, orig['xpos'], orig['ypos'],
width, a['height'], x, a['ypos'], width, a['height']);

this.ctx.lineWidth = lineWidth;
if (split > orig.xpos && this.fullNeedleImg) {
// draw frame of original area
this.ctx.strokeStyle = NeedleDiff.strokecolor('originalArea');
this.ctx.beginPath();
this.ctx.moveTo(x + width, y_start);
this.ctx.lineTo(x, y_start);
this.ctx.lineTo(x, y_end);
this.ctx.lineTo(x + width, y_end);
this.ctx.lineTo(x + width, y_start);
var origX = orig.xpos;
var origY = orig.ypos - lineWidth;
var origYEnd = orig.ypos + orig.height + lineWidth;
var origWidth = split - origX < a.width ? split - origX : a.width + 1;
this.ctx.moveTo(origX + origWidth, origY);
this.ctx.lineTo(origX, origY);
this.ctx.lineTo(origX, origYEnd);
this.ctx.lineTo(origX + origWidth, origYEnd);
this.ctx.lineTo(origX + origWidth, origY);
this.ctx.stroke();
}

width = a['width'];

if (split < x + width) {

// ...fill the right part with the new screenshot (not gray)

this.ctx.strokeStyle = NeedleDiff.shapecolor(a['type']);
// fill the right part with the new screenshot (not gray)
this.ctx.strokeStyle = NeedleDiff.shapecolor(a.type);

var start = split;
if (split < a['xpos'])
Expand Down Expand Up @@ -210,15 +229,16 @@ NeedleDiff.prototype.draw = function() {
this.ctx.fillText(text, tx, ty);
}

text = "Needle";
textSize = this.ctx.measureText(text);
if (a['xpos'] + textSize.width < split) {
tx = a['xpos'] + 1;
this.ctx.strokeText(text, tx, ty);
this.ctx.fillStyle = "rgb(64,224,208)";
this.ctx.fillText(text, tx, ty);
if (!this.fullNeedleImg) {
text = "Needle";
textSize = this.ctx.measureText(text);
if (a['xpos'] + textSize.width < split) {
tx = a['xpos'] + 1;
this.ctx.strokeText(text, tx, ty);
this.ctx.fillStyle = "rgb(64,224,208)";
this.ctx.fillText(text, tx, ty);
}
}

}

}.bind(this));
Expand Down Expand Up @@ -265,9 +285,10 @@ NeedleDiff.prototype.mouseup = function(event) {
}

NeedleDiff.strokecolors = {
ok: 'rgb( 64, 224, 208)',
fail: 'rgb( 64, 224, 208)',
exclude: 'rgb(100, 100, 100)',
ok: 'rgb( 64, 224, 208)',
fail: 'rgb( 64, 224, 208)',
exclude: 'rgb(100, 100, 100)',
originalArea: 'rgb(200, 200, 200)',
};

NeedleDiff.strokecolor = function(type) {
Expand Down Expand Up @@ -325,22 +346,69 @@ function setDiffScreenshot(differ, screenshotSrc) {
});
}

function setNeedle() {
var sel = $('#needlediff_selector').find('option:selected');
function setNeedle(sel, kind) {
// default to the same mode which was used previously
if (!kind) {
kind = window.differ.fullNeedleImg ? 'full-diff' : 'area-only-diff';
}
// set parameter according to the selected kind of diff
if (kind === 'area-only-diff') {
var assignFullNeedleImg = false;
} else if (kind === 'full-diff') {
var assignFullNeedleImg = true;
} else {
window.alert(kind + ' is not available (yet)!');
return;
}

var currentSelection = $('#needlediff_selector tbody tr.selected');
if (sel) {
// set needle for newly selected item
currentSelection.removeClass('selected');
sel.addClass('selected');
// update label/button text
var label = sel.data('label');
if (!label) {
label = 'Screenshot';
}
$('#current_needle_label').text(label);
} else {
// set needle for current selection
sel = currentSelection;
}

window.differ.areas = sel.data('areas');
window.differ.matches = sel.data('matches');
// set areas/matches
if (sel.length) {
// show actual candidate
window.differ.areas = sel.data('areas');
window.differ.matches = sel.data('matches');
$('#screenshot_button').prop('disabled', false);
} else {
// show only a screenshot
window.differ.areas = window.differ.matches = [];
$('#screenshot_button').prop('disabled', true);
}

// set image
var src = sel.data('image');

if (src) {
$('<img src="' + src + '">').on('load', function() {
var image = $(this).get(0);
window.differ.needleImg = image;
window.differ.fullNeedleImg = assignFullNeedleImg ? image : null;
window.differ.draw();
});
} else {
window.differ.needleImg = null;
window.differ.fullNeedleImg = null;
window.differ.draw();
}

// close menu again, except user is selecting text to copy
var needleDiffSelector = document.getElementById('needlediff_selector');
var selection = window.getSelection();
var userSelectedText = !selection.isCollapsed && $.contains(needleDiffSelector, selection.anchorNode);
if (!userSelectedText && $(needleDiffSelector).is(":visible")) {
$('#candidatesMenu').dropdown('toggle');
}
}
97 changes: 77 additions & 20 deletions assets/javascripts/test_result.js
Expand Up @@ -51,6 +51,21 @@ function previewSuccess(data, force) {
}
});
$('[data-toggle="popover"]').popover({html: true});
// make persistent dropdowns persistent by preventing click-event propagation
$('.dropdown-persistent').on('click', function (event) {
event.stopPropagation();
});
// ensure keydown event happening when button has focus is propagated to the right handler
$('.candidates-selection .dropdown-toggle').on('keydown', function (event) {
event.stopPropagation();
handleKeyDownOnTestDetails(event);
});
// handle click on the diff selection
$('.trigger-diff').on('click', function (event) {
var trigger = $(this);
setNeedle(trigger.parents('tr'), trigger.data('diff'));
event.stopPropagation();
});
}

function mapHash(hash) {
Expand Down Expand Up @@ -153,30 +168,73 @@ function checkResultHash() {
}
}

function setupResult(state, jobid, status_url, details_url) {
setupAsyncFailedResult();
$(".current_preview").removeClass("current_preview");
function prevNeedle() {
// select previous in current tag
var currentSelection = $('#needlediff_selector tbody tr.selected');
var newSelection = currentSelection.prev();
if (!newSelection.length) {
// select last in previous tag
newSelection = currentSelection.parents('li').prevAll().find('tbody tr').last();
}
setNeedle(newSelection);
}

$(window).keydown(function(e) {
var ftn = $(":focus").prop("tagName");
if (ftn == "INPUT" || ftn == "TEXTAREA") {
return;
function nextNeedle() {
var currentSelection = $('#needlediff_selector tbody tr.selected');
if (!currentSelection.length) {
// select first needle in first tag
var newSelection = $('#needlediff_selector tbody tr:first-child').first();
} else {
// select next in current tag
var newSelection = currentSelection.next();
if (!newSelection.length) {
// select first of next tag
newSelection = currentSelection.parents('li').nextAll().find('tbody tr').first();
}
}
if (e.shiftKey || e.metaKey || e.ctrlKey || e.altKey) {
return;
if (newSelection.length) {
setNeedle(newSelection);
}
if (e.which == KeyEvent.DOM_VK_LEFT) {
prevPreview();
e.preventDefault();
}

function handleKeyDownOnTestDetails(e) {
var ftn = $(':focus').prop('tagName');
if (ftn === 'INPUT' || ftn === 'TEXTAREA') {
return;
}
else if (e.which == KeyEvent.DOM_VK_RIGHT) {
nextPreview();
e.preventDefault();
} else if (e.which == KeyEvent.DOM_VK_ESCAPE) {
setCurrentPreview(null);
e.preventDefault();
if (e.shiftKey || e.metaKey || e.ctrlKey || e.altKey) {
return;
}
});

switch(e.which) {
case KeyEvent.DOM_VK_LEFT:
prevPreview();
e.preventDefault();
break;
case KeyEvent.DOM_VK_RIGHT:
nextPreview();
e.preventDefault();
break;
case KeyEvent.DOM_VK_ESCAPE:
setCurrentPreview(null);
e.preventDefault();
break;
case KeyEvent.DOM_VK_UP:
prevNeedle();
e.preventDefault();
break;
case KeyEvent.DOM_VK_DOWN:
nextNeedle();
e.preventDefault();
break;
}
}

function setupResult(state, jobid, status_url, details_url) {
setupAsyncFailedResult();
$(".current_preview").removeClass("current_preview");

$(window).keydown(handleKeyDownOnTestDetails);

$(window).resize(function() {
if ($(".current_preview")) {
Expand Down Expand Up @@ -214,7 +272,6 @@ function setupResult(state, jobid, status_url, details_url) {
$(window).on("hashchange", checkResultHash);
checkResultHash();

$(document).on("change", "#needlediff_selector", setNeedle);
$("a[data-toggle='tab']").on("show.bs.tab", function(e) {
var tabshown = $(e.target).attr("href");
// now this is very special
Expand Down
1 change: 1 addition & 0 deletions assets/stylesheets/openqa.scss
Expand Up @@ -13,6 +13,7 @@
@import "tables.scss";
@import "dashboard.scss";
@import "test-details.scss";
@import "result_preview.scss";
@import "admin-pages.scss";
@import "comments.scss";
@import "overview.scss";