// Copyright (C) 2021 Leigh Morresi (dgtlmoon@gmail.com)
// All rights reserved.
// yes - this is really a hack, if you are a front-ender and want to help, please get in touch!
$ ( document ) . ready ( function ( ) {
var current _selected _i ;
var state _clicked = false ;
var c ;
// greyed out fill context
var xctx ;
// redline highlight context
var ctx ;
var current _default _xpath = [ ] ;
var x _scale = 1 ;
var y _scale = 1 ;
var selector _image ;
var selector _image _rect ;
var selector _data ;
$ ( '#visualselector-tab' ) . click ( function ( ) {
$ ( "img#selector-background" ) . off ( 'load' ) ;
state _clicked = false ;
current _selected _i = false ;
bootstrap _visualselector ( ) ;
} ) ;
$ ( document ) . on ( 'keydown' , function ( event ) {
if ( $ ( "img#selector-background" ) . is ( ":visible" ) ) {
if ( event . key == "Escape" ) {
state _clicked = false ;
ctx . clearRect ( 0 , 0 , c . width , c . height ) ;
}
}
} ) ;
// For when the page loads
if ( ! window . location . hash || window . location . hash != '#visualselector' ) {
$ ( "img#selector-background" ) . attr ( 'src' , '' ) ;
return ;
}
// Handle clearing button/link
$ ( '#clear-selector' ) . on ( 'click' , function ( event ) {
if ( ! state _clicked ) {
alert ( 'Oops, Nothing selected!' ) ;
}
state _clicked = false ;
ctx . clearRect ( 0 , 0 , c . width , c . height ) ;
xctx . clearRect ( 0 , 0 , c . width , c . height ) ;
$ ( "#include_filters" ) . val ( '' ) ;
} ) ;
bootstrap _visualselector ( ) ;
function bootstrap _visualselector ( ) {
if ( 1 ) {
// bootstrap it, this will trigger everything else
$ ( "img#selector-background" ) . on ( "error" , function ( ) {
$ ( '.fetching-update-notice' ) . html ( "<strong>Ooops!</strong> The VisualSelector tool needs atleast one fetched page, please unpause the watch and/or wait for the watch to complete fetching and then reload this page." ) ;
$ ( '.fetching-update-notice' ) . css ( 'color' , '#bb0000' ) ;
$ ( '#selector-current-xpath' ) . hide ( ) ;
$ ( '#clear-selector' ) . hide ( ) ;
} ) . bind ( 'load' , function ( ) {
console . log ( "Loaded background..." ) ;
c = document . getElementById ( "selector-canvas" ) ;
// greyed out fill context
xctx = c . getContext ( "2d" ) ;
// redline highlight context
ctx = c . getContext ( "2d" ) ;
if ( $ ( "#include_filters" ) . val ( ) . trim ( ) . length ) {
current _default _xpath = $ ( "#include_filters" ) . val ( ) . split ( /\r?\n/g ) ;
} else {
current _default _xpath = [ ] ;
}
fetch _data ( ) ;
$ ( '#selector-canvas' ) . off ( "mousemove mousedown" ) ;
// screenshot_url defined in the edit.html template
} ) . attr ( "src" , screenshot _url ) ;
}
// Tell visualSelector that the image should update
var s = $ ( "img#selector-background" ) . attr ( 'src' ) + "?" + new Date ( ) . getTime ( ) ;
$ ( "img#selector-background" ) . attr ( 'src' , s )
}
// This is fired once the img src is loaded in bootstrap_visualselector()
function fetch _data ( ) {
// Image is ready
$ ( '.fetching-update-notice' ) . html ( "Fetching element data.." ) ;
$ . ajax ( {
url : watch _visual _selector _data _url ,
context : document . body
} ) . done ( function ( data ) {
$ ( '.fetching-update-notice' ) . html ( "Rendering.." ) ;
selector _data = data ;
console . log ( "Reported browser width from backend: " + data [ 'browser_width' ] ) ;
state _clicked = false ;
set _scale ( ) ;
reflow _selector ( ) ;
$ ( '.fetching-update-notice' ) . fadeOut ( ) ;
} ) ;
}
function set _scale ( ) {
// some things to check if the scaling doesnt work
// - that the widths/sizes really are about the actual screen size cat elements.json |grep -o width......|sort|uniq
$ ( "#selector-wrapper" ) . show ( ) ;
selector _image = $ ( "img#selector-background" ) [ 0 ] ;
selector _image _rect = selector _image . getBoundingClientRect ( ) ;
// make the canvas the same size as the image
$ ( '#selector-canvas' ) . attr ( 'height' , selector _image _rect . height ) ;
$ ( '#selector-canvas' ) . attr ( 'width' , selector _image _rect . width ) ;
$ ( '#selector-wrapper' ) . attr ( 'width' , selector _image _rect . width ) ;
x _scale = selector _image _rect . width / selector _data [ 'browser_width' ] ;
y _scale = selector _image _rect . height / selector _image . naturalHeight ;
ctx . strokeStyle = 'rgba(255,0,0, 0.9)' ;
ctx . fillStyle = 'rgba(255,0,0, 0.1)' ;
ctx . lineWidth = 3 ;
console . log ( "scaling set x: " + x _scale + " by y:" + y _scale ) ;
$ ( "#selector-current-xpath" ) . css ( 'max-width' , selector _image _rect . width ) ;
}
function reflow _selector ( ) {
$ ( window ) . resize ( function ( ) {
set _scale ( ) ;
highlight _current _selected _i ( ) ;
} ) ;
var selector _currnt _xpath _text = $ ( "#selector-current-xpath span" ) ;
set _scale ( ) ;
console . log ( selector _data [ 'size_pos' ] . length + " selectors found" ) ;
// highlight the default one if we can find it in the xPath list
// or the xpath matches the default one
found = false ;
if ( current _default _xpath . length ) {
// Find the first one that matches
// @todo In the future paint all that match
for ( const c of current _default _xpath ) {
for ( var i = selector _data [ 'size_pos' ] . length ; i !== 0 ; i -- ) {
if ( selector _data [ 'size_pos' ] [ i - 1 ] . xpath . trim ( ) === c . trim ( ) ) {
console . log ( "highlighting " + c ) ;
current _selected _i = i - 1 ;
highlight _current _selected _i ( ) ;
found = true ;
break ;
}
}
if ( found ) {
break ;
}
}
if ( ! found ) {
alert ( "Unfortunately your existing CSS/xPath Filter was no longer found!" ) ;
}
}
$ ( '#selector-canvas' ) . bind ( 'mousemove' , function ( e ) {
if ( state _clicked ) {
return ;
}
ctx . clearRect ( 0 , 0 , c . width , c . height ) ;
current _selected _i = null ;
// Add in offset
if ( ( typeof e . offsetX === "undefined" || typeof e . offsetY === "undefined" ) || ( e . offsetX === 0 && e . offsetY === 0 ) ) {
var targetOffset = $ ( e . target ) . offset ( ) ;
e . offsetX = e . pageX - targetOffset . left ;
e . offsetY = e . pageY - targetOffset . top ;
}
// Reverse order - the most specific one should be deeper/"laster"
// Basically, find the most 'deepest'
var found = 0 ;
ctx . fillStyle = 'rgba(205,0,0,0.35)' ;
// Will be sorted by smallest width*height first
for ( var i = 0 ; i <= selector _data [ 'size_pos' ] . length ; i ++ ) {
// draw all of them? let them choose somehow?
var sel = selector _data [ 'size_pos' ] [ i ] ;
// If we are in a bounding-box
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
) {
// FOUND ONE
set _current _selected _text ( sel . xpath ) ;
ctx . strokeRect ( sel . left * x _scale , sel . top * y _scale , sel . width * x _scale , sel . height * y _scale ) ;
ctx . fillRect ( sel . left * x _scale , sel . top * y _scale , sel . width * x _scale , sel . height * y _scale ) ;
// no need to keep digging
// @todo or, O to go out/up, I to go in
// or double click to go up/out the selector?
current _selected _i = i ;
found += 1 ;
break ;
}
}
} . debounce ( 5 ) ) ;
function set _current _selected _text ( s ) {
selector _currnt _xpath _text [ 0 ] . innerHTML = s ;
}
function highlight _current _selected _i ( ) {
if ( state _clicked ) {
state _clicked = false ;
xctx . clearRect ( 0 , 0 , c . width , c . height ) ;
return ;
}
var sel = selector _data [ 'size_pos' ] [ current _selected _i ] ;
if ( sel [ 0 ] == '/' ) {
// @todo - not sure just checking / is right
$ ( "#include_filters" ) . val ( 'xpath:' + sel . xpath ) ;
} else {
$ ( "#include_filters" ) . val ( sel . xpath ) ;
}
xctx . fillStyle = 'rgba(205,205,205,0.95)' ;
xctx . strokeStyle = 'rgba(225,0,0,0.9)' ;
xctx . lineWidth = 3 ;
xctx . fillRect ( 0 , 0 , c . width , c . height ) ;
// Clear out what only should be seen (make a clear/clean spot)
xctx . clearRect ( sel . left * x _scale , sel . top * y _scale , sel . width * x _scale , sel . height * y _scale ) ;
xctx . strokeRect ( sel . left * x _scale , sel . top * y _scale , sel . width * x _scale , sel . height * y _scale ) ;
state _clicked = true ;
set _current _selected _text ( sel . xpath ) ;
}
$ ( '#selector-canvas' ) . bind ( 'mousedown' , function ( e ) {
highlight _current _selected _i ( ) ;
} ) ;
}
} ) ;