// 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"); }