// widget_jsontable.ysl2 template "widget[@type='JsonTable']", mode="widget_class" || class JsonTableWidget extends Widget{ // arbitrary defaults to avoid missing entries in query cache = [0,100,50]; init() { this.spread_json_data_bound = this.spread_json_data.bind(this); } handle_http_response(response) { if (!response.ok) { console.log("HTTP error, status = " + response.status); } return response.json(); } do_http_request(...opt) { const query = { args: this.args, range: this.cache[1], position: this.cache[2], visible: this.visible, extra: this.cache.slice(4), options: opt }; const options = { method: 'POST', body: JSON.stringify(query), headers: {'Content-Type': 'application/json'} }; fetch(this.args[0], options) .then(this.handle_http_response) .then(this.spread_json_data_bound); } dispatch(value, oldval, index) { this.cache[index] = value; this.do_http_request(); } make_on_click(...options){ let that = this; return function(evt){ that.do_http_request(...options); } } // on_click(evt, ...options) { // this.do_http_request(...options); // } } || template "svg:*", mode="json_table_elt_render" { error > JsonTable Widget can't contain element of type «local-name()». } const "hmi_textstylelists_descs", "$parsed_widgets/widget[@type = 'TextStyleList']"; const "hmi_textstylelists", "$hmi_elements[@id = $hmi_textstylelists_descs/@id]"; const "textstylelist_related" foreach "$hmi_textstylelists" list { attrib "listid" value "@id"; foreach "func:refered_elements(.)" elt { attrib "eltid" value "@id"; } } const "textstylelist_related_ns", "exsl:node-set($textstylelist_related)"; def "func:json_expressions" { param "expressions"; param "label"; // compute javascript expressions to access JSON data // desscribed in given svg element's "label" // knowing that parent element already has given "expressions". choose { when "$label" { const "suffixes", "str:split($label)"; const "res" foreach "$suffixes" expression { const "suffix","."; const "pos","position()"; // take last available expression (i.e can have more suffixes than expressions) const "expr","$expressions[position() <= $pos][last()]/expression"; choose { when "contains($suffix,'=')" { const "name", "substring-before($suffix,'=')"; if "$expr/@name[. != $name]" error > JsonTable : missplaced '=' or inconsistent names in Json data expressions. attrib "name" value "$name"; attrib "content" > «$expr/@content»«substring-after($suffix,'=')» } otherwise { copy "$expr/@name"; attrib "content" > «$expr/@content»«$suffix» } } } result "exsl:node-set($res)"; } // Empty labels are ignored, expressions are then passed as-is. otherwise result "$expressions"; } } const "initexpr" expression attrib "content" > jdata const "initexpr_ns", "exsl:node-set($initexpr)"; template "svg:use", mode="json_table_elt_render" { param "expressions"; // cloned element must be part of a HMI:List const "targetid", "substring-after(@xlink:href,'#')"; const "from_list", "$hmi_lists[(@id | */@id) = $targetid]"; choose { when "count($from_list) > 0" { | id("«@id»").setAttribute("xlink:href", // obtain new target id from HMI:List widget | "#"+hmi_widgets["«$from_list/@id»"].items[«$expressions/expression[1]/@content»]); } otherwise warning > Clones (svg:use) in JsonTable Widget must point to a valid HMI:List widget or item. Reference "«@xlink:href»" is not valid and will not be updated. } } template "svg:text", mode="json_table_elt_render" { param "expressions"; const "value_expr", "$expressions/expression[1]/@content"; const "original", "@original"; const "from_textstylelist", "$textstylelist_related_ns/list[elt/@eltid = $original]"; choose { when "count($from_textstylelist) > 0" { const "content_expr", "$expressions/expression[2]/@content"; if "string-length($content_expr) = 0 or $expressions/expression[2]/@name != 'textContent'" error > Clones (svg:use) in JsonTable Widget pointing to a HMI:TextStyleList widget or item must have a "textContent=.someVal" assignement following value expression in label. | { | let elt = id("«@id»"); | elt.textContent = String(«$content_expr»); | elt.style = hmi_widgets["«$from_textstylelist/@listid»"].styles[«$value_expr»]; | } } otherwise { | id("«@id»").textContent = String(«$value_expr»); } } } // only labels comming from Json widget are counted in def "func:filter_non_widget_label" { param "elt"; param "widget_elts"; const "eltid" choose { when "$elt/@original" value "$elt/@original"; otherwise value "$elt/@id"; } result "$widget_elts[@id=$eltid]/@inkscape:label"; } template "svg:*", mode="json_table_render_except_comments"{ param "expressions"; param "widget_elts"; const "label", "func:filter_non_widget_label(., $widget_elts)"; // filter out "# commented" elements if "not(starts-with($label,'#'))" apply ".", mode="json_table_render"{ with "expressions", "$expressions"; with "widget_elts", "$widget_elts"; with "label", "$label"; } } template "svg:*", mode="json_table_render" { param "expressions"; param "widget_elts"; param "label"; const "new_expressions", "func:json_expressions($expressions, $label)"; const "elt","."; foreach "$new_expressions/expression[position() > 1][starts-with(@name,'onClick')]" | id("«$elt/@id»").onclick = this.make_on_click('«@name»', «@content»); apply ".", mode="json_table_elt_render" with "expressions", "$new_expressions"; } template "svg:g", mode="json_table_render" { param "expressions"; param "widget_elts"; param "label"; const "gid", "@id"; // use intermediate variables for optimization const "varprefix" > obj_«$gid»_ | try { foreach "$expressions/expression"{ | let «$varprefix»«position()» = «@content»; | if(«$varprefix»«position()» == undefined) { | console.log("«$varprefix»«position()» = «@content»"); | throw null; | } } // because we put values in a variables, we can replace corresponding expression with variable name const "new_expressions" foreach "$expressions/expression" xsl:copy { copy "@name"; attrib "content" > «$varprefix»«position()» } // revert hiding in case it did happen before | id("«@id»").style = "«@style»"; apply "*", mode="json_table_render_except_comments" { with "expressions", "func:json_expressions(exsl:node-set($new_expressions), $label)"; with "widget_elts", "$widget_elts"; } | } catch(err) { | id("«$gid»").style = "display:none"; | } } template "widget[@type='JsonTable']", mode="widget_defs" { param "hmi_element"; labels("data"); optional_labels("forward backward cursor"); const "data_elt", "$result_svg_ns//*[@id = $hmi_element/@id]/*[@inkscape:label = 'data']"; | visible: «count($data_elt/*[@inkscape:label])», | spread_json_data: function(janswer) { | let [range,position,jdata] = janswer; | this.apply_hmi_value(1, range); | this.apply_hmi_value(2, position); | this.apply_hmi_value(3, this.visible); apply "$data_elt", mode="json_table_render_except_comments" { with "expressions","$initexpr_ns"; with "widget_elts","$hmi_element/*[@inkscape:label = 'data']/descendant::svg:*"; } | } }