src_aurelia-bodylight-plugin/src/elements/value.js
import {bindable} from 'aurelia-templating';
import _ from 'lodash';
import {inject} from 'aurelia-framework';
@inject(Element)
export class Value {
@bindable fromid;
@bindable refindex;
@bindable convertor;
@bindable default;
@bindable precision=4;
@bindable tofixed;
@bindable throttle=500;
@bindable dataevent=false;
@bindable adobeid;
@bindable tohash=false; //will change window.location.hash if true
previousvalue = '';
//constructor(){}
constructor(element) {
this.element = element;
//create lambda function which is added as listener later
this.handleValueChange = e => {
//e.detail do not reallocate - using same buffer, thus slicing to append to data array
//let edata = e.detail.data.slice();
//throttle update to reasonable frequency
// _.throttle(()=> this.updateValue(e.detail.data[this.refindex]), this.throttle)();
//call throttled function with args
this.myupdatevalue(e.detail.data[this.refindex])
};
this.handleRawValueChange = e => {
console.log('catched event from adobe',e)
//this.updateValue(e.target.value)
}
this.handleFMIAttached = e => {
const fromel = document.getElementById(this.fromid);
if (fromel) {
fromel.addEventListener('fmidata', this.handleValueChange);
} else {
console.warn('fmi attached, but no element with id found:',this.fromid);
}
}
}
bind() {
//register throttled update function
if (typeof this.throttle === 'string') this.throttle = parseInt(this.throttle, 10);
if (typeof this.tofixed === 'string') this.tofixed = parseInt(this.tofixed, 10);
if (typeof this.dataevent === 'string') this.dataevent = this.dataevent === 'true';
if (typeof this.tohash === 'string') this.tohash = this.tohash === 'true';
if (typeof this.precision === 'string') this.precision = parseInt(this.precision, 10);
this.myupdatevalue = _.throttle(this.updateValue, this.throttle);
if (this.default && typeof this.default === 'string') this.value = parseFloat(this.default);
//configure convertors - used to convert units received from fmi
if (this.convertor) {
//used code from fmi component
//TODO change to custom attribute
let convertvalues = [this.convertor];
let identity = x => x;
this.operation = [];
for (let i = 0; i < convertvalues.length; i++) {
if (convertvalues[i].includes(',') && !convertvalues[i].includes('(')) {
//convert values are in form numerator,denominator contains comma ',' and not parenthesis, e.g. function 'max(1,2)'
let convertitems = convertvalues[i].split(',');
if (convertitems[0] === '1' && convertitems[1] === '1' && convertitems[2] === '0') this.operation.push(identity);
else {
let numerator = parseFloat(convertitems[0]);
let denominator = parseFloat(convertitems[1]);
let addend = (convertitems.length>2)?parseFloat(convertitems[2]):0;
this.operation.push(x => ((x * numerator / denominator) + addend));
}
} else {
//convert values are in form of expression, do not contain comma
if (convertvalues === '1/x') this.operation.push(x=> 1 / x);
else {
// for eval() security filter only allowed characters:
// algebraic, digits, e, dot, modulo, parenthesis and 'x' and 'e' is allowed
let expression = convertvalues[i].replace(/[^-\d/*+.()%xeMathroundsicm]/g, '');
console.log('chartjs bind(), evaluating expression:' + convertvalues[i] + ' securely filtered to :' + expression);
// eslint-disable-next-line no-eval
this.operation.push(x => eval(expression));
}
}
}
}
}
attached() {
//listening to custom event fmidata
//listening to custom event fmidata and fmireset
const fromel = document.getElementById(this.fromid);
if (fromel) {
fromel.addEventListener('fmidata', this.handleValueChange);
} else {
console.warn('chartjs, null fromid element, waiting to be attached');
document.addEventListener('fmiattached',this.handleFMIAttached);
}
if (this.adobeid) {
const adobeel = document.getElementById(this.adobeid);
if (adobeel){
console.log('listening to adobe events');
adobeel.addEventListener('change', this.handleRawValueChange);
} else {
console.warn('no adobe element found to listen changes');
}
}
}
detached() {
if (document.getElementById(this.fromid)) document.getElementById(this.fromid).removeEventListener('fmidata', this.handleValueChange);
if (this.adobeid && document.getElementById(this.adobeid)) document.getElementById(this.adobeid).removeEventListener('change', this.handleRawValueChange);
}
updateValue(rawvalue) {
if (rawvalue.toPrecision) {
if ((this.tofixed !== null) && (this.tofixed !== undefined) && (this.tofixed>=0)) {
//probably will have toFixed function
if (this.operation) this.value = this.operation[0](rawvalue).toFixed(this.tofixed); // * this.numerator / this.denominator + this.addconst;
else this.value = rawvalue.toFixed(this.tofixed);
} else {
//go with toPrecision function
if (this.operation) this.value = this.operation[0](rawvalue).toPrecision(this.precision); // * this.numerator / this.denominator + this.addconst;
else this.value = rawvalue.toPrecision(this.precision);
}
} else this.value = rawvalue;
if (this.dataevent) {
let c = new CustomEvent('fmivalue', {detail: {value: this.value}});
this.element.dispatchEvent(c);
}
if (this.tohash) {
if (this.previousvalue != this.value) { //change only if it differs from previous value
this.previousvalue = this.value;
if (this.value.length>0 && this.value[0] === '#') window.location.hash = this.value;
else window.location.hash = '#'+this.value; //add hash if it is missing in the string value
}
}
}
}