SVGHMI: add automatic reconnection of websocket with 1s reconnection delay after first attempt.
// widget_slider.ysl2
widget_desc("Slider") {
longdesc
||
Slider - DEPRECATED - use ScrollBar or PathSlider instead
||
shortdesc > Slider - DEPRECATED - use ScrollBar instead
path name="value" accepts="HMI_INT" > value
path name="range" accepts="HMI_INT" > range
path name="visible" accepts="HMI_INT" > visible
}
widget_class("Slider")
||
frequency = 5;
range = undefined;
handle_orig = undefined;
scroll_size = undefined;
scroll_range = 0;
scroll_visible = 7;
min_size = 0.07;
fi = undefined;
curr_value = 0;
drag = false;
enTimer = false;
handle_click = undefined;
last_drag = false;
dispatch(value,oldval, index) {
if (index == 0){
let [min,max,start,totallength] = this.range;
//save current value inside widget
this.curr_value = value;
//check if in range
if (this.curr_value > max){
this.curr_value = max;
this.apply_hmi_value(0, this.curr_value);
}
else if (this.curr_value < min){
this.curr_value = min;
this.apply_hmi_value(0, this.curr_value);
}
if(this.value_elt)
this.value_elt.textContent = String(value);
}
else if(index == 1){
this.scroll_range = value;
this.set_scroll();
}
else if(index == 2){
this.scroll_visible = value;
this.set_scroll();
}
//don't update if draging and setpoint ghost doesn't exist
if(!this.drag || (this.setpoint_elt != undefined)){
this.update_DOM(this.curr_value, this.handle_elt);
}
}
set_scroll(){
//check if range is bigger than visible and set scroll size
if(this.scroll_range > this.scroll_visible){
this.scroll_size = this.scroll_range - this.scroll_visible;
this.range[0] = 0;
this.range[1] = this.scroll_size;
}
else{
this.scroll_size = 1;
this.range[0] = 0;
this.range[1] = 1;
}
}
update_DOM(value, elt){
let [min,max,start,totallength] = this.range;
// check if handle is resizeable
if (this.scroll_size != undefined){ //size changes
//get parameters
let length = Math.max(min,Math.min(max,(Number(value)-min)*max/(max-min)));
let tip = this.range_elt.getPointAtLength(length);
let handle_min = totallength*this.min_size;
let step = 1;
//check if range is bigger than max displayed and recalculate step
if ((totallength/handle_min) < (max-min+1)){
step = (max-min+1)/(totallength/handle_min-1);
}
let kx,ky,offseY,offseX = undefined;
//scale on x or y axes
if (this.fi > 0.75){
//get scale factor
if(step > 1){
ky = handle_min/this.handle_orig.height;
}
else{
ky = (totallength-handle_min*(max-min))/this.handle_orig.height;
}
kx = 1;
//get 0 offset to stay inside range
offseY = start.y - (this.handle_orig.height + this.handle_orig.y) * ky;
offseX = 0;
//get distance from value
tip.y =this.range_elt.getPointAtLength(0).y - length/step *handle_min;
}
else{
//get scale factor
if(step > 1){
kx = handle_min/this.handle_orig.width;
}
else{
kx = (totallength-handle_min*(max-min))/this.handle_orig.width;
}
ky = 1;
//get 0 offset to stay inside range
offseX = start.x - (this.handle_orig.x * kx);
offseY = 0;
//get distance from value
tip.x =this.range_elt.getPointAtLength(0).x + length/step *handle_min;
}
elt.setAttribute('transform',"matrix("+(kx)+" 0 0 "+(ky)+" "+(tip.x-start.x+offseX)+" "+(tip.y-start.y+offseY)+")");
}
else{ //size stays the same
let length = Math.max(0,Math.min(totallength,(Number(value)-min)*totallength/(max-min)));
let tip = this.range_elt.getPointAtLength(length);
elt.setAttribute('transform',"translate("+(tip.x-start.x)+","+(tip.y-start.y)+")");
}
// show or hide ghost if exists
if(this.setpoint_elt != undefined){
if(this.last_drag!= this.drag){
if(this.drag){
this.setpoint_elt.setAttribute("style", this.setpoint_style);
}else{
this.setpoint_elt.setAttribute("style", "display:none");
}
this.last_drag = this.drag;
}
}
}
on_release(evt) {
//unbind events
window.removeEventListener("touchmove", this.on_bound_drag, true);
window.removeEventListener("mousemove", this.on_bound_drag, true);
window.removeEventListener("mouseup", this.bound_on_release, true);
window.removeEventListener("touchend", this.bound_on_release, true);
window.removeEventListener("touchcancel", this.bound_on_release, true);
//reset drag flag
if(this.drag){
this.drag = false;
}
// get final position
this.update_position(evt);
}
on_drag(evt){
//ignore drag event for X amount of time and if not selected
if(this.enTimer && this.drag){
this.update_position(evt);
//reset timer
this.enTimer = false;
setTimeout("{hmi_widgets['"+this.element_id+"'].enTimer = true;}", 100);
}
}
update_position(evt){
var html_dist = 0;
let [min,max,start,totallength] = this.range;
//calculate size of widget in html
var range_borders = this.range_elt.getBoundingClientRect();
var [minX,minY,maxX,maxY] = [range_borders.left,range_borders.bottom,range_borders.right,range_borders.top];
var range_length = Math.sqrt( range_borders.height*range_borders.height + range_borders.width*range_borders.width );
//get range and mouse coordinates
var mouseX = undefined;
var mouseY = undefined;
if (evt.type.startsWith("touch")){
mouseX = Math.ceil(evt.touches[0].clientX);
mouseY = Math.ceil(evt.touches[0].clientY);
}
else{
mouseX = evt.pageX;
mouseY = evt.pageY;
}
// calculate position
if (this.handle_click){ //if clicked on handle
let moveDist = 0, resizeAdd = 0;
let range_percent = 1;
//set paramters for resizeable handle
if (this.scroll_size != undefined){
// add one more object to stay inside range
resizeAdd = 1;
//chack if range is bigger than display option and
// calculate percent of range with out handle
if(((max/(max*this.min_size)) < (max-min+1))){
range_percent = 1-this.min_size;
}
else{
range_percent = 1-(max-max*this.min_size*(max-min))/max;
}
}
//calculate value difference on x or y axis
if(this.fi > 0.7){
moveDist = ((max-min+resizeAdd)/(range_length*range_percent))*((this.handle_click[1]-mouseY)/Math.sin(this.fi));
}
else{
moveDist = ((max-min+resizeAdd)/(range_length*range_percent))*((mouseX-this.handle_click[0])/Math.cos(this.fi));
}
this.curr_value = Math.ceil(this.handle_click[2] + moveDist);
}
else{ //if clicked on widget
//get handle distance from mouse position
if (minX > mouseX && minY < mouseY){
html_dist = 0;
}
else if (maxX < mouseX && maxY > mouseY){
html_dist = range_length;
}
else{
if(this.fi > 0.7){
html_dist = (minY - mouseY)/Math.sin(this.fi);
}
else{
html_dist = (mouseX - minX)/Math.cos(this.fi);
}
}
//calculate distance
this.curr_value=Math.ceil((html_dist/range_length)*(this.range[1]-this.range[0])+this.range[0]);
}
//check if in range and apply
if (this.curr_value > max){
this.curr_value = max;
}
else if (this.curr_value < min){
this.curr_value = min;
}
this.apply_hmi_value(0, this.curr_value);
//redraw handle
this.request_animate();
}
animate(){
// redraw handle on screen refresh
// check if setpoint(ghost) handle exsist otherwise update main handle
if(this.setpoint_elt != undefined){
this.update_DOM(this.curr_value, this.setpoint_elt);
}
else{
this.update_DOM(this.curr_value, this.handle_elt);
}
}
on_select(evt){
//enable drag flag and timer
this.drag = true;
this.enTimer = true;
//bind events
window.addEventListener("touchmove", this.on_bound_drag, true);
window.addEventListener("mousemove", this.on_bound_drag, true);
window.addEventListener("mouseup", this.bound_on_release, true);
window.addEventListener("touchend", this.bound_on_release, true);
window.addEventListener("touchcancel", this.bound_on_release, true);
// check if handle was pressed
if (evt.currentTarget == this.handle_elt){
//get mouse position on the handle
let mouseX = undefined;
let mouseY = undefined;
if (evt.type.startsWith("touch")){
mouseX = Math.ceil(evt.touches[0].clientX);
mouseY = Math.ceil(evt.touches[0].clientY);
}
else{
mouseX = evt.pageX;
mouseY = evt.pageY;
}
//save coordinates and orig value
this.handle_click = [mouseX,mouseY,this.curr_value];
}
else{
// get new handle position and reset if handle was not pressed
this.handle_click = undefined;
this.update_position(evt);
}
//prevent next events
evt.stopPropagation();
}
init() {
//set min max value if not defined
let min = this.min_elt ?
Number(this.min_elt.textContent) :
this.args.length >= 1 ? this.args[0] : 0;
let max = this.max_elt ?
Number(this.max_elt.textContent) :
this.args.length >= 2 ? this.args[1] : 100;
// save initial parameters
this.range_elt.style.strokeMiterlimit="0";
this.range = [min, max, this.range_elt.getPointAtLength(0),this.range_elt.getTotalLength()];
let start = this.range_elt.getPointAtLength(0);
let end = this.range_elt.getPointAtLength(this.range_elt.getTotalLength());
this.fi = Math.atan2(start.y-end.y, end.x-start.x);
this.handle_orig = this.handle_elt.getBBox();
//bind functions
this.bound_on_select = this.on_select.bind(this);
this.bound_on_release = this.on_release.bind(this);
this.on_bound_drag = this.on_drag.bind(this);
this.handle_elt.addEventListener("mousedown", this.bound_on_select);
this.element.addEventListener("mousedown", this.bound_on_select);
this.element.addEventListener("touchstart", this.bound_on_select);
//touch recognised as page drag without next command
document.body.addEventListener("touchstart", function(e){}, false);
//save ghost style
if(this.setpoint_elt != undefined){
this.setpoint_style = this.setpoint_elt.getAttribute("style");
this.setpoint_elt.setAttribute("style", "display:none");
}
}
||
widget_defs("Slider") {
labels("handle range");
optional_labels("value min max setpoint");
}