edouard@2884: // widgets_common.ysl2 edouard@2884: Edouard@2808: in xsl decl labels(*ptr, name="defs_by_labels") alias call-template { Edouard@2808: with "hmi_element", "$hmi_element"; Edouard@2810: with "labels"{text *ptr}; edouard@2937: content; Edouard@2808: }; Edouard@2808: edouard@2937: decl optional_labels(*ptr) alias - { edouard@2937: /* TODO add some per label xslt variable to check if exist */ edouard@2937: labels(*ptr){ edouard@2937: with "mandatory","'no'"; edouard@2937: content; edouard@2937: } Edouard@2836: }; Edouard@2836: Edouard@3522: decl warning_labels(*ptr) alias - { Edouard@3522: labels(*ptr){ Edouard@3522: with "mandatory","'warn'"; Edouard@3522: content; Edouard@3522: } Edouard@3522: }; Edouard@3522: Edouard@3595: decl _activable(*level) alias - { Edouard@3595: | activable_sub:{ Edouard@3595: const "activity" labels("/active /inactive") { Edouard@3595: with "mandatory"{text *level}; Edouard@3595: content; Edouard@3595: } Edouard@3595: value "$activity"; Edouard@3595: const "has_activity","string-length($activity)>0"; Edouard@3595: | }, Edouard@3596: | has_activity: «$has_activity», Edouard@3595: }; Edouard@3595: Edouard@3520: decl activable() alias - { Edouard@3595: _activable("warn") Edouard@3595: }; Edouard@3595: decl optional_activable() alias - { Edouard@3595: _activable("no") Edouard@3595: }; Edouard@3595: edouard@2937: decl activable_labels(*ptr) alias - { edouard@2937: optional_labels(*ptr) { edouard@2937: with "subelements","'active inactive'"; edouard@2937: content; edouard@2937: } edouard@2920: }; edouard@2920: edouard@3233: in xsl decl widget_desc(%name, match="widget[@type='%name']", mode="widget_desc") alias template { edouard@3233: type > «@type» edouard@3233: content; edouard@3233: }; edouard@3233: edouard@3232: in xsl decl widget_class(%name, *clsname="%nameWidget", match="widget[@type='%name']", mode="widget_class") alias template { edouard@3232: | class `text **clsname` extends Widget{ edouard@3232: content; edouard@3232: | } edouard@3232: }; edouard@3232: edouard@3232: in xsl decl widget_defs(%name, match="widget[@type='%name']", mode="widget_defs") alias template { edouard@3232: param "hmi_element"; Edouard@3595: // all widget potentially has a "disabled" labeled element Edouard@3600: const "disability" optional_labels("/disabled"); Edouard@3595: value "$disability"; Edouard@3595: const "has_disability","string-length($disability)>0"; edouard@3232: content; edouard@3232: }; edouard@3232: edouard@3232: in xsl decl widget_page(%name, match="widget[@type='%name']", mode="widget_page") alias template { edouard@3232: param "page_desc"; edouard@3232: content; edouard@3232: }; edouard@3232: edouard@3232: decl gen_index_xhtml alias - { edouard@3232: content; edouard@3232: }; edouard@3232: edouard@2955: template "svg:*", mode="hmi_widgets" { edouard@2886: const "widget", "func:widget(@id)"; Edouard@2881: const "eltid","@id"; Edouard@3024: const "args" foreach "$widget/arg" > "«func:escape_quotes(@value)»"`if "position()!=last()" > ,` edouard@2950: const "indexes" foreach "$widget/path" { Edouard@3594: if "position()!=last()" > , Edouard@3594: } Edouard@3594: Edouard@3594: const "variables" foreach "$widget/path" { Edouard@3594: > [ Edouard@2881: choose { Edouard@2881: when "not(@index)" { edouard@3017: choose { Edouard@3058: when "not(@type)" { usveticic@3056: warning > Widget «$widget/@type» id="«$eltid»" : No match for path "«@value»" in HMI tree edouard@3101: > undefined Edouard@3058: } edouard@3017: when "@type = 'PAGE_LOCAL'" edouard@3101: > "«@value»" edouard@3017: when "@type = 'HMI_LOCAL'" edouard@3101: > hmi_local_index("«@value»") edouard@3101: otherwise edouard@3101: error > Internal error while processing widget's non indexed HMI tree path : unknown type edouard@3017: } Edouard@2881: } Edouard@2881: otherwise { edouard@3101: > «@index» edouard@3101: } edouard@3101: } Edouard@3594: > , { Edouard@3594: if "@min and @max"{ Edouard@3594: > minmax:[«@min», «@max»] Edouard@3594: if "@assign" Edouard@3594: > , Edouard@3594: } Edouard@3594: if "@assign" Edouard@3594: > assign:"«@assign»" Edouard@3594: > }] edouard@3099: if "position()!=last()" > , edouard@3099: } edouard@3099: Edouard@3408: const "freq" choose { edouard@3412: when "$widget/@freq" Edouard@3455: > "«$widget/@freq»" Edouard@3408: otherwise Edouard@3408: > undefined Edouard@3408: } Edouard@3408: Edouard@3594: const "enable_expr" choose{ Edouard@3594: when "$widget/@enable_expr" Edouard@3593: > true Edouard@3593: otherwise Edouard@3593: > false Edouard@3593: } Edouard@3593: Edouard@3594: | "«@id»": new «$widget/@type»Widget ("«@id»",«$freq»,[«$args»],[«$variables»],«$enable_expr»,{ Edouard@3594: if "$widget/@enable_expr" { Edouard@3594: Edouard@3686: | enable_assignments: [], Edouard@3594: | compute_enable: function(value, oldval, varnum) { Edouard@3594: | let result = false; Edouard@3594: | do { Edouard@3594: foreach "$widget/path" { Edouard@3594: const "varid","generate-id()"; Edouard@3594: const "varnum","position()-1"; Edouard@3594: if "@assign" foreach "$widget/path[@assign]" if "$varid = generate-id()" { Edouard@3686: | if(varnum == «$varnum») this.enable_assignments[«position()-1»] = value; Edouard@3686: | let «@assign» = this.enable_assignments[«position()-1»]; Edouard@3594: | if(«@assign» == undefined) break; Edouard@3594: } Edouard@3594: } Edouard@3594: | result = «$widget/@enable_expr»; Edouard@3594: | } while(0); Edouard@3594: | this.enable(result); Edouard@3594: | }, Edouard@3594: } Edouard@2881: apply "$widget", mode="widget_defs" with "hmi_element","."; edouard@2949: | })`if "position()!=last()" > ,` edouard@2949: } edouard@2948: edouard@3017: emit "preamble:local-variable-indexes" { edouard@3017: || edouard@3142: edouard@3017: let hmi_locals = {}; edouard@3017: var last_remote_index = hmitree_types.length - 1; edouard@3017: var next_available_index = hmitree_types.length; edouard@3128: let cookies = new Map(document.cookie.split("; ").map(s=>s.split("="))); edouard@3017: Edouard@3025: const local_defaults = { Edouard@3025: || edouard@3128: foreach "$parsed_widgets/widget[starts-with(@type,'VarInit')]"{ Edouard@3025: if "count(path) != 1" error > VarInit «@id» must have only one variable given. Edouard@3025: if "path/@type != 'PAGE_LOCAL' and path/@type != 'HMI_LOCAL'" error > VarInit «@id» only applies to HMI variable. edouard@3128: > "«path/@value»": edouard@3128: choose { edouard@3128: when "@type = 'VarInitPersistent'" > cookies.has("«path/@value»")?cookies.get("«path/@value»"):«arg[1]/@value» edouard@3128: otherwise > «arg[1]/@value» edouard@3128: } edouard@3128: > \n edouard@3128: if "position()!=last()" > , Edouard@3025: } Edouard@3025: || Edouard@3025: }; edouard@3128: edouard@3128: const persistent_locals = new Set([ edouard@3128: || edouard@3128: foreach "$parsed_widgets/widget[@type='VarInitPersistent']"{ edouard@3128: | "«path/@value»"`if "position()!=last()" > ,` edouard@3128: } edouard@3128: || edouard@3128: ]); edouard@3128: var persistent_indexes = new Map(); Edouard@3022: var cache = hmitree_types.map(_ignored => undefined); Edouard@3022: edouard@3017: function page_local_index(varname, pagename){ edouard@3017: let pagevars = hmi_locals[pagename]; Edouard@3022: let new_index; edouard@3017: if(pagevars == undefined){ Edouard@3022: new_index = next_available_index++; Edouard@3623: hmi_locals[pagename] = {[varname]:new_index}; edouard@3017: } else { edouard@3017: let result = pagevars[varname]; Edouard@3022: if(result != undefined) { Edouard@3022: return result; Edouard@3022: } Edouard@3022: Edouard@3022: new_index = next_available_index++; Edouard@3022: pagevars[varname] = new_index; Edouard@3022: } Edouard@3025: let defaultval = local_defaults[varname]; edouard@3128: if(defaultval != undefined) { Edouard@3025: cache[new_index] = defaultval; edouard@3128: if(persistent_locals.has(varname)) edouard@3128: persistent_indexes.set(new_index, varname); edouard@3128: } Edouard@3022: return new_index; edouard@3017: } edouard@3017: edouard@3017: function hmi_local_index(varname){ edouard@3017: return page_local_index(varname, "HMI_LOCAL"); edouard@3017: } edouard@3017: || edouard@3017: } edouard@3017: edouard@2949: emit "preamble:widget-base-class" { edouard@3017: || Edouard@3019: var pending_widget_animates = []; Edouard@3019: Edouard@3520: function _hide(elt, placeholder){ Edouard@3520: if(elt.parentNode != null) Edouard@3520: placeholder.parentNode.removeChild(elt); Edouard@3520: } Edouard@3520: function _show(elt, placeholder){ Edouard@3520: placeholder.parentNode.insertBefore(elt, placeholder); Edouard@3520: } Edouard@3520: Edouard@3596: function set_activity_state(eltsub, state){ Edouard@3522: if(eltsub.active_elt != undefined){ Edouard@3522: if(eltsub.active_elt_placeholder == undefined){ Edouard@3522: eltsub.active_elt_placeholder = document.createComment(""); Edouard@3522: eltsub.active_elt.parentNode.insertBefore(eltsub.active_elt_placeholder, eltsub.active_elt); Edouard@3522: } Edouard@3522: (state?_show:_hide)(eltsub.active_elt, eltsub.active_elt_placeholder); Edouard@3522: } Edouard@3522: if(eltsub.inactive_elt != undefined){ Edouard@3522: if(eltsub.inactive_elt_placeholder == undefined){ Edouard@3522: eltsub.inactive_elt_placeholder = document.createComment(""); Edouard@3522: eltsub.inactive_elt.parentNode.insertBefore(eltsub.inactive_elt_placeholder, eltsub.inactive_elt); Edouard@3522: } Edouard@3522: ((state || state==undefined)?_hide:_show)(eltsub.inactive_elt, eltsub.inactive_elt_placeholder); Edouard@3522: } Edouard@3520: } Edouard@3520: edouard@2949: class Widget { edouard@2963: offset = 0; edouard@2963: frequency = 10; /* FIXME arbitrary default max freq. Obtain from config ? */ Edouard@2980: unsubscribable = false; Edouard@3019: pending_animate = false; Edouard@3019: Edouard@3594: constructor(elt_id, freq, args, variables, enable_expr, members){ edouard@2958: this.element_id = elt_id; edouard@2950: this.element = id(elt_id); edouard@2950: this.args = args; Edouard@3596: Edouard@3594: [this.indexes, this.variables_options] = (variables.length>0) ? zip(...variables) : [[],[]]; Edouard@3594: this.indexes_length = this.indexes.length; Edouard@3596: Edouard@3594: this.enable_expr = enable_expr; Edouard@3595: this.enable_state = true; Edouard@3595: this.enable_displayed_state = true; Edouard@3595: this.enabled_elts = []; Edouard@3595: edouard@2949: Object.keys(members).forEach(prop => this[prop]=members[prop]); Edouard@3594: this.lastapply = this.indexes.map(() => undefined); Edouard@3594: this.inhibit = this.indexes.map(() => undefined); Edouard@3594: this.pending = this.indexes.map(() => undefined); Edouard@3504: this.bound_uninhibit = this.uninhibit.bind(this); Edouard@3504: Edouard@3594: this.lastdispatch = this.indexes.map(() => undefined); Edouard@3594: this.deafen = this.indexes.map(() => undefined); Edouard@3594: this.incoming = this.indexes.map(() => undefined); Edouard@3504: this.bound_undeafen = this.undeafen.bind(this); Edouard@3504: Edouard@3417: this.forced_frequency = freq; Edouard@3453: this.clip = true; edouard@2949: } edouard@2958: Edouard@3455: do_init(){ Edouard@3469: let forced = this.forced_frequency; Edouard@3469: if(forced !== undefined){ Edouard@3455: /* Edouard@3455: once every 10 seconds : 10s Edouard@3455: once per minute : 1m Edouard@3455: once per hour : 1h Edouard@3455: once per day : 1d Edouard@3455: */ Edouard@3469: let unit = forced.slice(-1); Edouard@3455: let factor = { Edouard@3455: "s":1, Edouard@3455: "m":60, Edouard@3455: "h":3600, Edouard@3455: "d":86400}[unit]; Edouard@3455: Edouard@3469: this.frequency = factor ? 1/(factor * Number(forced.slice(0,-1))) Edouard@3469: : Number(forced); Edouard@3455: } Edouard@3455: Edouard@3455: let init = this.init; Edouard@3455: if(typeof(init) == "function"){ Edouard@3455: try { Edouard@3455: init.call(this); Edouard@3455: } catch(err) { Edouard@3455: console.log(err); Edouard@3455: } Edouard@3455: } Edouard@3593: Edouard@3594: if(this.enable_expr){ Edouard@3593: this.enable_state = false; Edouard@3593: this.enable_displayed_state = false; Edouard@3593: for(let child of Array.from(this.element.children)){ Edouard@3595: let label = child.getAttribute("inkscape:label"); Edouard@3595: if(label!="disabled"){ Edouard@3593: this.enabled_elts.push(child); Edouard@3593: this.element.removeChild(child); Edouard@3593: } Edouard@3593: } Edouard@3593: } Edouard@3455: } Edouard@3455: edouard@2951: unsub(){ edouard@2951: /* remove subsribers */ Edouard@3595: for(let i = 0; i < this.indexes_length; i++) { Edouard@3595: /* flush updates pending because of inhibition */ Edouard@3595: let inhibition = this.inhibit[i]; Edouard@3595: if(inhibition != undefined){ Edouard@3595: clearTimeout(inhibition); Edouard@3595: this.lastapply[i] = undefined; Edouard@3595: this.uninhibit(i); Edouard@3595: } Edouard@3595: let deafened = this.deafen[i]; Edouard@3595: if(deafened != undefined){ Edouard@3595: clearTimeout(deafened); Edouard@3595: this.lastdispatch[i] = undefined; Edouard@3595: this.undeafen(i); Edouard@3595: } Edouard@3603: let index = this.get_variable_index(i); Edouard@3595: subscribers(index).delete(this); Edouard@3595: } edouard@2951: this.offset = 0; edouard@3005: this.relativeness = undefined; edouard@3005: } edouard@3005: edouard@4024: sub(new_offset, relativeness, container_id){ edouard@2951: this.offset = new_offset; edouard@3005: this.relativeness = relativeness; edouard@3017: this.container_id = container_id ; edouard@2951: /* add this's subsribers */ Edouard@3595: for(let i = 0; i < this.indexes_length; i++) { Edouard@3595: let index = this.get_variable_index(i); Edouard@3595: if(index == undefined) continue; Edouard@3595: subscribers(index).add(this); Edouard@3595: } Edouard@3603: this.apply_cache(); edouard@2951: } edouard@2951: edouard@2951: apply_cache() { Edouard@3603: for(let i = 0; i < this.indexes_length; i++) { edouard@2951: /* dispatch current cache in newly opened page widgets */ Edouard@3603: let realindex = this.get_variable_index(i); Edouard@3058: if(realindex == undefined) continue; edouard@2951: let cached_val = cache[realindex]; edouard@2951: if(cached_val != undefined) Edouard@3603: this.feed_data_for_dispatch(cached_val, cached_val, i); edouard@2951: } edouard@2951: } edouard@2951: edouard@3017: get_variable_index(varnum) { edouard@3017: let index = this.indexes[varnum]; edouard@3017: if(typeof(index) == "string"){ edouard@3017: index = page_local_index(index, this.container_id); edouard@3017: } else { edouard@3017: if(this.relativeness[varnum]){ edouard@3017: index += this.offset; edouard@3017: } edouard@3017: } edouard@3017: return index; edouard@3001: } edouard@3098: edouard@3099: overshot(new_val, max) { edouard@3099: } edouard@3099: edouard@3099: undershot(new_val, min) { edouard@3099: } edouard@3099: edouard@3099: clip_min_max(index, new_val) { Edouard@3594: let minmax = this.variables_options[index].minmax; edouard@3099: if(minmax !== undefined && typeof new_val == "number") { edouard@3099: let [min,max] = minmax; edouard@3099: if(new_val < min){ edouard@3099: this.undershot(new_val, min); edouard@3099: return min; edouard@3099: } edouard@3099: if(new_val > max){ edouard@3099: this.overshot(new_val, max); edouard@3099: return max; edouard@3099: } edouard@3099: } edouard@3099: return new_val; edouard@3099: } edouard@3099: Edouard@3058: change_hmi_value(index, opstr) { Edouard@3058: let realindex = this.get_variable_index(index); Edouard@3058: if(realindex == undefined) return undefined; edouard@3098: let old_val = cache[realindex]; edouard@3098: let new_val = eval_operation_string(old_val, opstr); Edouard@3453: if(this.clip) Edouard@3453: new_val = this.clip_min_max(index, new_val); edouard@3098: return apply_hmi_value(realindex, new_val); edouard@3004: } edouard@3004: edouard@3139: _apply_hmi_value(index, new_val) { Edouard@3058: let realindex = this.get_variable_index(index); Edouard@3058: if(realindex == undefined) return undefined; Edouard@3453: if(this.clip) Edouard@3453: new_val = this.clip_min_max(index, new_val); Edouard@3058: return apply_hmi_value(realindex, new_val); edouard@3004: } edouard@3006: Edouard@3504: uninhibit(index){ edouard@3139: this.inhibit[index] = undefined; edouard@3139: let new_val = this.pending[index]; edouard@3139: this.pending[index] = undefined; edouard@3139: return this.apply_hmi_value(index, new_val); edouard@3139: } edouard@3139: edouard@3139: apply_hmi_value(index, new_val) { edouard@3139: if(this.inhibit[index] == undefined){ edouard@3139: let now = Date.now(); edouard@3139: let min_interval = 1000/this.frequency; edouard@3139: let lastapply = this.lastapply[index]; edouard@3139: if(lastapply == undefined || now > lastapply + min_interval){ edouard@3139: this.lastapply[index] = now; edouard@3139: return this._apply_hmi_value(index, new_val); edouard@3139: } edouard@3139: else { edouard@3139: let elapsed = now - lastapply; edouard@3139: this.pending[index] = new_val; Edouard@3504: this.inhibit[index] = setTimeout(this.bound_uninhibit, min_interval - elapsed, index); edouard@3139: } edouard@3139: } edouard@3139: else { edouard@3139: this.pending[index] = new_val; edouard@3139: return new_val; edouard@3139: } edouard@3139: } edouard@3139: edouard@3006: new_hmi_value(index, value, oldval) { Edouard@3025: // TODO avoid searching, store index at sub() Edouard@3593: for(let i = 0; i < this.indexes_length; i++) { Edouard@3025: let refindex = this.get_variable_index(i); Edouard@3058: if(refindex == undefined) continue; Edouard@3025: Edouard@3025: if(index == refindex) { Edouard@3600: this.feed_data_for_dispatch(value, oldval, i); Edouard@3025: break; Edouard@3025: } edouard@3006: } edouard@3006: } Edouard@3702: Edouard@3504: undeafen(index){ Edouard@3504: this.deafen[index] = undefined; Edouard@3504: let [new_val, old_val] = this.incoming[index]; Edouard@3504: this.incoming[index] = undefined; Edouard@3702: this.lastdispatch[index] = Date.now(); Edouard@3600: this.do_dispatch(new_val, old_val, index); Edouard@3504: } Edouard@3504: Edouard@3593: enable(enabled){ Edouard@3593: if(this.enable_state != enabled){ Edouard@3593: this.enable_state = enabled; Edouard@3593: this.request_animate(); Edouard@3593: } Edouard@3593: } Edouard@3593: Edouard@3593: animate_enable(){ Edouard@3593: if(this.enable_state && !this.enable_displayed_state){ Edouard@3593: //show widget Edouard@3593: for(let child of this.enabled_elts){ Edouard@3593: this.element.appendChild(child); Edouard@3593: } Edouard@3593: Edouard@3593: //hide disabled content Edouard@3593: if(this.disabled_elt && this.disabled_elt.parentNode != null) Edouard@3593: this.element.removeChild(this.disabled_elt); Edouard@3593: Edouard@3593: this.enable_displayed_state = true; Edouard@3593: Edouard@3593: }else if(!this.enable_state && this.enable_displayed_state){ Edouard@3593: Edouard@3593: //hide widget Edouard@3593: for(let child of this.enabled_elts){ Edouard@3593: if(child.parentNode != null) Edouard@3593: this.element.removeChild(child); Edouard@3593: } Edouard@3593: Edouard@3593: //show disabled content Edouard@3593: if(this.disabled_elt) Edouard@3593: this.element.appendChild(this.disabled_elt); Edouard@3593: Edouard@3593: this.enable_displayed_state = false; Edouard@3596: Edouard@3596: // once disabled activity display is lost Edouard@3596: this.activity_displayed_state = undefined; Edouard@3593: } Edouard@3593: } Edouard@3593: Edouard@3600: feed_data_for_dispatch(value, oldval, varnum) { Edouard@3600: if(this.dispatch || this.enable_expr){ Edouard@3504: if(this.deafen[varnum] == undefined){ Edouard@3504: let now = Date.now(); Edouard@3504: let min_interval = 1000/this.frequency; Edouard@3504: let lastdispatch = this.lastdispatch[varnum]; Edouard@3504: if(lastdispatch == undefined || now > lastdispatch + min_interval){ Edouard@3504: this.lastdispatch[varnum] = now; Edouard@3600: this.do_dispatch(value, oldval, varnum) Edouard@3504: } Edouard@3504: else { Edouard@3504: let elapsed = now - lastdispatch; Edouard@3504: this.incoming[varnum] = [value, oldval]; Edouard@3504: this.deafen[varnum] = setTimeout(this.bound_undeafen, min_interval - elapsed, varnum); Edouard@3504: } Edouard@3504: } Edouard@3504: else { Edouard@3504: this.incoming[varnum] = [value, oldval]; Edouard@3026: } Edouard@3026: } Edouard@3026: } Edouard@3026: Edouard@3600: do_dispatch(value, oldval, varnum) { Edouard@3600: if(this.dispatch) try { Edouard@3600: this.dispatch(value, oldval, varnum); Edouard@3600: } catch(err) { Edouard@3600: console.log(err); Edouard@3600: } Edouard@3600: if(this.enable_expr) try { Edouard@3600: this.compute_enable(value, oldval, varnum); Edouard@3600: } catch(err) { Edouard@3600: console.log(err); Edouard@3600: } Edouard@3600: } Edouard@3600: Edouard@3019: _animate(){ Edouard@3594: if(this.enable_expr) Edouard@3593: this.animate_enable(); Edouard@3596: // inhibit widget animation when disabled Edouard@3596: if(!this.enable_expr || this.enable_state){ Edouard@3596: if(this.has_activity) Edouard@3596: this.animate_activity(); Edouard@3596: if(this.animate != undefined) Edouard@3596: this.animate(); Edouard@3596: } Edouard@3019: this.pending_animate = false; Edouard@3019: } Edouard@3019: Edouard@3019: request_animate(){ Edouard@3019: if(!this.pending_animate){ Edouard@3019: pending_widget_animates.push(this); Edouard@3019: this.pending_animate = true; Edouard@3019: requestHMIAnimation(); Edouard@3019: } Edouard@3520: } Edouard@3520: Edouard@3596: animate_activity(){ Edouard@3596: if(this.activity_displayed_state != this.activity_state){ Edouard@3596: set_activity_state(this.activable_sub, this.activity_state); Edouard@3596: this.activity_displayed_state = this.activity_state; Edouard@3596: } edouard@3125: } edouard@2949: } edouard@2949: || edouard@2949: } edouard@2949: edouard@3128: const "excluded_types", "str:split('Page VarInit VarInitPersistent')"; edouard@3120: edouard@3120: // Key to filter unique types edouard@3120: key "TypesKey", "widget", "@type"; edouard@3120: edouard@3007: emit "declarations:hmi-classes" { edouard@3120: const "used_widget_types", """$parsed_widgets/widget[ edouard@3120: generate-id() = generate-id(key('TypesKey', @type)) and edouard@3120: not(@type = $excluded_types)]"""; edouard@2948: apply "$used_widget_types", mode="widget_class"; Edouard@3556: Edouard@3556: } Edouard@3556: Edouard@3556: template "widget", mode="widget_class" { Edouard@3556: || Edouard@3556: class «@type»Widget extends Widget{ Edouard@3556: /* empty class, as «@type» widget didn't provide any */ Edouard@3556: } Edouard@3556: || Edouard@3556: warning > «@type» widget is used in SVG but widget type is not declared Edouard@3556: } edouard@2949: Edouard@3119: const "included_ids","$parsed_widgets/widget[not(@type = $excluded_types) and not(@id = $discardable_elements/@id)]/@id"; Edouard@3685: const "page_ids","$parsed_widgets/widget[@type = 'Page']/@id"; Edouard@3121: const "hmi_widgets","$hmi_elements[@id = $included_ids]"; Edouard@3685: const "page_widgets","$hmi_elements[@id = $page_ids]"; edouard@3162: const "result_widgets","$result_svg_ns//*[@id = $hmi_widgets/@id]"; edouard@2955: edouard@3007: emit "declarations:hmi-elements" { edouard@2941: | var hmi_widgets = { Edouard@3685: apply "$hmi_widgets | $page_widgets", mode="hmi_widgets"; edouard@2941: | } Edouard@3556: | edouard@2941: } edouard@2941: Edouard@2881: function "defs_by_labels" { Edouard@2881: param "labels","''"; Edouard@2881: param "mandatory","'yes'"; edouard@2920: param "subelements","/.."; Edouard@2881: param "hmi_element"; Edouard@2881: const "widget_type","@type"; Edouard@3522: const "widget_id","@id"; Edouard@2881: foreach "str:split($labels)" { Edouard@3452: const "absolute", "starts-with(., '/')"; Edouard@3452: const "name","substring(.,number($absolute)+1)"; Edouard@3452: const "widget","$result_widgets[@id = $hmi_element/@id]"; Edouard@3452: const "elt","($widget//*[not($absolute) and @inkscape:label=$name] | $widget/*[$absolute and @inkscape:label=$name])[1]"; Edouard@2881: choose { edouard@2920: when "not($elt/@id)" { Edouard@3522: if "$mandatory!='no'" { Edouard@3522: const "errmsg" > «$widget_type» widget (id=«$widget_id») must have a «$name» element Edouard@3522: choose { Edouard@3522: when "$mandatory='yes'" { Edouard@3522: error > «$errmsg» Edouard@3522: } Edouard@3522: otherwise { Edouard@3522: warning > «$errmsg» Edouard@3522: } Edouard@3522: } Edouard@2834: } Edouard@2881: // otherwise produce nothing Edouard@2797: } Edouard@2881: otherwise { edouard@2920: | «$name»_elt: id("«$elt/@id»"), edouard@2920: if "$subelements" { edouard@2920: | «$name»_sub: { edouard@2920: foreach "str:split($subelements)" { edouard@2920: const "subname","."; edouard@2920: const "subelt","$elt/*[@inkscape:label=$subname][1]"; edouard@2920: choose { edouard@2920: when "not($subelt/@id)" { Edouard@3522: if "$mandatory!='no'" { Edouard@3522: const "errmsg" > «$widget_type» widget (id=«$widget_id») must have a «$name»/«$subname» element Edouard@3522: choose { Edouard@3522: when "$mandatory='yes'" { Edouard@3522: error > «$errmsg» Edouard@3522: } Edouard@3522: otherwise { Edouard@3522: warning > «$errmsg» Edouard@3522: } Edouard@3522: } edouard@2920: } edouard@2920: | /* missing «$name»/«$subname» element */ edouard@2920: } edouard@2920: otherwise { Edouard@3520: | "«$subname»_elt": id("«$subelt/@id»")`if "position()!=last()" > ,` edouard@2920: } edouard@2920: } edouard@2920: } edouard@2920: | }, edouard@2920: } Edouard@2836: } Edouard@2808: } Edouard@2808: } Edouard@2881: } Edouard@2808: edouard@2883: def "func:escape_quotes" { edouard@2883: param "txt"; edouard@2883: // have to use a python string to enter escaped quote Edouard@3024: // const "frstln", "string-length($frst)"; edouard@2883: choose { Edouard@3024: when !"contains($txt,'\"')"! { Edouard@3024: result !"concat(substring-before($txt,'\"'),'\\\"',func:escape_quotes(substring-after($txt,'\"')))"!; edouard@2883: } edouard@2883: otherwise { edouard@2883: result "$txt"; edouard@2883: } edouard@2883: } edouard@2883: } edouard@2883: