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