550-visual-selector
dgtlmoon 3 years ago
parent eef98c6adc
commit ea4a8ed580

@ -1119,12 +1119,11 @@ def changedetection_app(config=None, datastore_o=None):
with sync_playwright() as p:
browser = p.chromium.connect_over_cdp("ws://127.0.0.1:3000")
page = browser.new_page()
page.set_viewport_size({"width": 1220, "height": 800})
page.goto(watch['url'])
# time.sleep(3)
#time.sleep(3)
# https://github.com/microsoft/playwright/issues/620
screenshot = page.screenshot(type='jpeg', clip={'x': 1.0, 'y': 1.0, 'width': 1280, 'height': 1024})
screenshot = page.screenshot(type='jpeg', full_page=True)
# Could be made a lot faster
# https://toruskit.com/blog/how-to-get-element-bounds-without-reflow/
@ -1141,13 +1140,13 @@ def changedetection_app(config=None, datastore_o=None):
var bbox;
for (var i = 0; i < elements.length; i++) {
bbox = elements[i].getBoundingClientRect();
if (! bbox['width'] || !bbox['height'] ) {
if ( bbox['width'] == window.innerWidth ) {
continue;
}
if (bbox['width'] >500 && bbox['height'] >500 ) {
if (bbox['width'] <10 || bbox['height'] <10 ) {
continue;
}
if(! 'textContent' in elements[i] || elements[i].textContent.length < 2 ) {
continue;
}

@ -0,0 +1,56 @@
/**
* debounce
* @param {integer} milliseconds This param indicates the number of milliseconds
* to wait after the last call before calling the original function.
* @param {object} What "this" refers to in the returned function.
* @return {function} This returns a function that when called will wait the
* indicated number of milliseconds after the last call before
* calling the original function.
*/
Function.prototype.debounce = function (milliseconds, context) {
var baseFunction = this,
timer = null,
wait = milliseconds;
return function () {
var self = context || this,
args = arguments;
function complete() {
baseFunction.apply(self, args);
timer = null;
}
if (timer) {
clearTimeout(timer);
}
timer = setTimeout(complete, wait);
};
};
/**
* throttle
* @param {integer} milliseconds This param indicates the number of milliseconds
* to wait between calls before calling the original function.
* @param {object} What "this" refers to in the returned function.
* @return {function} This returns a function that when called will wait the
* indicated number of milliseconds between calls before
* calling the original function.
*/
Function.prototype.throttle = function (milliseconds, context) {
var baseFunction = this,
lastEventTimestamp = null,
limit = milliseconds;
return function () {
var self = context || this,
args = arguments,
now = Date.now();
if (!lastEventTimestamp || now - lastEventTimestamp >= limit) {
lastEventTimestamp = now;
baseFunction.apply(self, args);
}
};
};

@ -1,4 +1,6 @@
$("img#selector-background").bind('load', function () {
var current_selected_i;
function fetch_data() {
// Image is ready
$('.fetching-update-notice').html("Fetching element data..");
@ -9,62 +11,51 @@ $("img#selector-background").bind('load', function () {
$('.fetching-update-notice').html("Rendering..");
reflow_selector(data);
});
};
});
function reflow_selector(selector_data) {
$('#selector-canvas').attr('width', $("img#selector-background").width());
$('#selector-canvas').attr('height', $("img#selector-background").height());
//.attr('height', $("img#selector-background").height());
// could trim it according to the lowest/furtheret item in the dataset
stage = new createjs.Stage("selector-canvas");
// to get onMouseOver & onMouseOut events, we need to enable them on the
// stage:
stage.enableMouseOver();
output = new createjs.Text("Test press, click, doubleclick, mouseover, and mouseout", "14px Arial");
output.x = output.y = 10;
stage.addChild(output);
var squares = [];
for (var i = 0; i < selector_data.length; i++) {
squares[i] = new createjs.Shape();
squares[i].graphics.beginFill("rgba(215,0,0,0.2)").drawRect(
selector_data[i]['left'],
selector_data[i]['top'],
selector_data[i]['width'],
selector_data[i]['height']);
squares[i].name = selector_data[i]['xpath'];
stage.addChild(squares[i]);
squares[i].on("click", handleMouseEvent);
squares[i].on("dblclick", handleMouseEvent);
squares[i].on("mouseover", handleMouseEvent);
squares[i].on("mouseout", handleMouseEvent);
squares.push(squares[i]);
// $('#selector-canvas').attr('width',
// $("img#selector-background")[0].getBoundingClientRect().width);
var c = document.getElementById("selector-canvas");
var selector_image = document.getElementById("selector-background");
var selector_image_rect = selector_image.getBoundingClientRect();
$('#selector-canvas').attr('height', selector_image_rect.height);
$('#selector-canvas').attr('width', selector_image_rect.width);
var ctx = c.getContext("2d");
ctx.strokeStyle = 'rgba(255,0,0,5)';
// set this on resize too
var x_scale = selector_image_rect.width / selector_image.naturalWidth;
var y_scale = selector_image_rect.height / selector_image.naturalHeight;
console.log(selector_data.length + " selectors found");
$('#selector-canvas').bind('mousemove', function (e) {
ctx.clearRect(0, 0, c.width, c.height);
current_selected_i=null;
// Reverse order - the most specific one should be deeper/"laster"
for (var i = selector_data.length; i!=0; i--) {
// draw all of them? let them choose somehow?
var sel = selector_data[i-1];
if (e.offsetY > sel.top * y_scale && e.offsetY < sel.top * y_scale + sel.height * y_scale
&&
e.offsetX > sel.left * y_scale && e.offsetX < sel.left * y_scale + sel.width * y_scale
) {
ctx.strokeRect(sel.left * x_scale, sel.top * y_scale, sel.width * x_scale, sel.height * y_scale);
// no need to keep digging
current_selected_i=i;
break;
}
stage.update();
$('.fetching-update-notice').hide();
}
function handleMouseEvent(evt) {
output.text = "evt.target: " + evt.target + ", evt.type: " + evt.type;
if(evt.type == 'mouseover') {
evt.target.graphics.beginFill("rgba(225,220,220,0.9)");
}
if(evt.type == 'mouseout') {
evt.target.graphics.beginFill("rgba(1,1,1,0.4)");
}
}.debounce(5));
// to save CPU, we're only updating when we need to, instead of on a tick:1
stage.update();
$('#selector-canvas').bind('mousedown', function (e) {
alert(selector_data[current_selected_i].xpath);
});
}

@ -450,7 +450,11 @@ ul {
position: relative; }
#selector-wrapper > img {
position: absolute;
z-index: 4; }
z-index: 4;
max-width: 100%; }
#selector-wrapper > canvas {
position: absolute;
z-index: 5; }
position: relative;
z-index: 5;
max-width: 100%; }
#selector-wrapper > canvas:hover {
cursor: pointer; }

@ -637,14 +637,19 @@ ul {
#selector-wrapper {
position: relative;
//width: 100%;
> img {
position: absolute;
z-index: 4;
max-width: 100%;
}
>canvas {
position: absolute;
position: relative;
z-index: 5;
max-width: 100%;
&:hover {
cursor: pointer;
}
}
}

@ -12,7 +12,7 @@
</script>
<script type="text/javascript" src="{{url_for('static_content', group='js', filename='notifications.js')}}" defer></script>
<script type="text/javascript" src="{{url_for('static_content', group='js', filename='visual-selector.js')}}" defer></script>
<script src="https://code.createjs.com/easeljs-0.8.2.min.js"></script>
<script type="text/javascript" src="{{url_for('static_content', group='js', filename='limit.js')}}" defer></script>
<div class="edit-form monospaced-textarea">
@ -187,7 +187,13 @@ nav
<div id="selector-wrapper">
<!-- request the screenshot and get the element offset info ready -->
<!-- use img src ready load to know everything is ready to map out -->
<img id="selector-background" src="{{ url_for('visualselector_request_current_screenshot_and_metadata', uuid=uuid) }}"/>
<img id="selector-background" />
<script>
$("img#selector-background").attr("src", "{{ url_for('visualselector_request_current_screenshot_and_metadata', uuid=uuid) }}");
$("img#selector-background").bind('load', function () {
fetch_data();
});
</script>
<canvas id="selector-canvas"></canvas>
</div>
</div>

Loading…
Cancel
Save