SVGHMI: Fixed typo on session manager unregister, leading to wrong count of sessions and then exceptions when creating more session than allowed in protocol options. Also added more safety check in protocol in case session would be missing.
// 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")
||
class SliderWidget extends Widget{
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");
}