svghmi/gen_index_xhtml.xslt
branchsvghmi
changeset 3090 9e172e4e50c7
parent 3085 6b1b23971960
child 3091 f475f39713aa
equal deleted inserted replaced
3089:10b2e620b57f 3090:9e172e4e50c7
  1899       <xsl:with-param name="labels">
  1899       <xsl:with-param name="labels">
  1900         <xsl:text>active inactive</xsl:text>
  1900         <xsl:text>active inactive</xsl:text>
  1901       </xsl:with-param>
  1901       </xsl:with-param>
  1902       <xsl:with-param name="mandatory" select="'no'"/>
  1902       <xsl:with-param name="mandatory" select="'no'"/>
  1903     </xsl:call-template>
  1903     </xsl:call-template>
  1904     <xsl:text>
       
  1905 </xsl:text>
       
  1906   </xsl:template>
  1904   </xsl:template>
  1907   <xsl:template mode="widget_class" match="widget[@type='CircularBar']">
  1905   <xsl:template mode="widget_class" match="widget[@type='CircularBar']">
  1908     <xsl:text>class CircularBarWidget extends Widget{
  1906     <xsl:text>class CircularBarWidget extends Widget{
  1909 </xsl:text>
  1907 </xsl:text>
  1910     <xsl:text>    frequency = 10;
  1908     <xsl:text>    frequency = 10;
  3061     <xsl:text>}(); // eslint-disable-line    
  3059     <xsl:text>}(); // eslint-disable-line    
  3062 </xsl:text>
  3060 </xsl:text>
  3063     <xsl:text>
  3061     <xsl:text>
  3064 </xsl:text>
  3062 </xsl:text>
  3065   </xsl:template>
  3063   </xsl:template>
       
  3064   <xsl:template mode="widget_class" match="widget[@type='DropDown']">
       
  3065     <xsl:text>    class DropDownWidget extends Widget{
       
  3066 </xsl:text>
       
  3067     <xsl:text>        dispatch(value) {
       
  3068 </xsl:text>
       
  3069     <xsl:text>            if(!this.opened) this.set_selection(value);
       
  3070 </xsl:text>
       
  3071     <xsl:text>        }
       
  3072 </xsl:text>
       
  3073     <xsl:text>        init() {
       
  3074 </xsl:text>
       
  3075     <xsl:text>            this.button_elt.onclick = this.on_button_click.bind(this);
       
  3076 </xsl:text>
       
  3077     <xsl:text>            // Save original size of rectangle
       
  3078 </xsl:text>
       
  3079     <xsl:text>            this.box_bbox = this.box_elt.getBBox()
       
  3080 </xsl:text>
       
  3081     <xsl:text>
       
  3082 </xsl:text>
       
  3083     <xsl:text>            // Compute margins
       
  3084 </xsl:text>
       
  3085     <xsl:text>            let text_bbox = this.text_elt.getBBox();
       
  3086 </xsl:text>
       
  3087     <xsl:text>            let lmargin = text_bbox.x - this.box_bbox.x;
       
  3088 </xsl:text>
       
  3089     <xsl:text>            let tmargin = text_bbox.y - this.box_bbox.y;
       
  3090 </xsl:text>
       
  3091     <xsl:text>            this.margins = [lmargin, tmargin].map(x =&gt; Math.max(x,0));
       
  3092 </xsl:text>
       
  3093     <xsl:text>
       
  3094 </xsl:text>
       
  3095     <xsl:text>            // Index of first visible element in the menu, when opened
       
  3096 </xsl:text>
       
  3097     <xsl:text>            this.menu_offset = 0;
       
  3098 </xsl:text>
       
  3099     <xsl:text>
       
  3100 </xsl:text>
       
  3101     <xsl:text>            // How mutch to lift the menu vertically so that it does not cross bottom border
       
  3102 </xsl:text>
       
  3103     <xsl:text>            this.lift = 0;
       
  3104 </xsl:text>
       
  3105     <xsl:text>
       
  3106 </xsl:text>
       
  3107     <xsl:text>            // Event handlers cannot be object method ('this' is unknown)
       
  3108 </xsl:text>
       
  3109     <xsl:text>            // as a workaround, handler given to addEventListener is bound in advance.
       
  3110 </xsl:text>
       
  3111     <xsl:text>            this.bound_close_on_click_elsewhere = this.close_on_click_elsewhere.bind(this);
       
  3112 </xsl:text>
       
  3113     <xsl:text>            this.bound_on_selection_click = this.on_selection_click.bind(this);
       
  3114 </xsl:text>
       
  3115     <xsl:text>            this.bound_on_backward_click = this.on_backward_click.bind(this);
       
  3116 </xsl:text>
       
  3117     <xsl:text>            this.bound_on_forward_click = this.on_forward_click.bind(this);
       
  3118 </xsl:text>
       
  3119     <xsl:text>            this.opened = false;
       
  3120 </xsl:text>
       
  3121     <xsl:text>        }
       
  3122 </xsl:text>
       
  3123     <xsl:text>        on_button_click() {
       
  3124 </xsl:text>
       
  3125     <xsl:text>            this.open();
       
  3126 </xsl:text>
       
  3127     <xsl:text>        }
       
  3128 </xsl:text>
       
  3129     <xsl:text>        // Called when a menu entry is clicked
       
  3130 </xsl:text>
       
  3131     <xsl:text>        on_selection_click(selection) {
       
  3132 </xsl:text>
       
  3133     <xsl:text>            this.close();
       
  3134 </xsl:text>
       
  3135     <xsl:text>            this.apply_hmi_value(0, selection);
       
  3136 </xsl:text>
       
  3137     <xsl:text>        }
       
  3138 </xsl:text>
       
  3139     <xsl:text>        on_backward_click(){
       
  3140 </xsl:text>
       
  3141     <xsl:text>            this.scroll(false);
       
  3142 </xsl:text>
       
  3143     <xsl:text>        }
       
  3144 </xsl:text>
       
  3145     <xsl:text>        on_forward_click(){
       
  3146 </xsl:text>
       
  3147     <xsl:text>            this.scroll(true);
       
  3148 </xsl:text>
       
  3149     <xsl:text>        }
       
  3150 </xsl:text>
       
  3151     <xsl:text>        set_selection(value) {
       
  3152 </xsl:text>
       
  3153     <xsl:text>            let display_str;
       
  3154 </xsl:text>
       
  3155     <xsl:text>            if(value &gt;= 0 &amp;&amp; value &lt; this.content.length){
       
  3156 </xsl:text>
       
  3157     <xsl:text>                // if valid selection resolve content
       
  3158 </xsl:text>
       
  3159     <xsl:text>                display_str = this.content[value];
       
  3160 </xsl:text>
       
  3161     <xsl:text>                this.last_selection = value;
       
  3162 </xsl:text>
       
  3163     <xsl:text>            } else {
       
  3164 </xsl:text>
       
  3165     <xsl:text>                // otherwise show problem
       
  3166 </xsl:text>
       
  3167     <xsl:text>                display_str = "?"+String(value)+"?";
       
  3168 </xsl:text>
       
  3169     <xsl:text>            }
       
  3170 </xsl:text>
       
  3171     <xsl:text>            // It is assumed that first span always stays,
       
  3172 </xsl:text>
       
  3173     <xsl:text>            // and contains selection when menu is closed
       
  3174 </xsl:text>
       
  3175     <xsl:text>            this.text_elt.firstElementChild.textContent = display_str;
       
  3176 </xsl:text>
       
  3177     <xsl:text>        }
       
  3178 </xsl:text>
       
  3179     <xsl:text>        grow_text(up_to) {
       
  3180 </xsl:text>
       
  3181     <xsl:text>            let count = 1;
       
  3182 </xsl:text>
       
  3183     <xsl:text>            let txt = this.text_elt; 
       
  3184 </xsl:text>
       
  3185     <xsl:text>            let first = txt.firstElementChild;
       
  3186 </xsl:text>
       
  3187     <xsl:text>            // Real world (pixels) boundaries of current page
       
  3188 </xsl:text>
       
  3189     <xsl:text>            let bounds = svg_root.getBoundingClientRect(); 
       
  3190 </xsl:text>
       
  3191     <xsl:text>            this.lift = 0;
       
  3192 </xsl:text>
       
  3193     <xsl:text>            while(count &lt; up_to) {
       
  3194 </xsl:text>
       
  3195     <xsl:text>                let next = first.cloneNode();
       
  3196 </xsl:text>
       
  3197     <xsl:text>                // relative line by line text flow instead of absolute y coordinate
       
  3198 </xsl:text>
       
  3199     <xsl:text>                next.removeAttribute("y");
       
  3200 </xsl:text>
       
  3201     <xsl:text>                next.setAttribute("dy", "1.1em");
       
  3202 </xsl:text>
       
  3203     <xsl:text>                // default content to allow computing text element bbox
       
  3204 </xsl:text>
       
  3205     <xsl:text>                next.textContent = "...";
       
  3206 </xsl:text>
       
  3207     <xsl:text>                // append new span to text element
       
  3208 </xsl:text>
       
  3209     <xsl:text>                txt.appendChild(next);
       
  3210 </xsl:text>
       
  3211     <xsl:text>                // now check if text extended by one row fits to page
       
  3212 </xsl:text>
       
  3213     <xsl:text>                // FIXME : exclude margins to be more accurate on box size
       
  3214 </xsl:text>
       
  3215     <xsl:text>                let rect = txt.getBoundingClientRect();
       
  3216 </xsl:text>
       
  3217     <xsl:text>                if(rect.bottom &gt; bounds.bottom){
       
  3218 </xsl:text>
       
  3219     <xsl:text>                    // in case of overflow at the bottom, lift up one row
       
  3220 </xsl:text>
       
  3221     <xsl:text>                    let backup = first.getAttribute("dy");
       
  3222 </xsl:text>
       
  3223     <xsl:text>                    // apply lift asr a dy added too first span (y attrib stays)
       
  3224 </xsl:text>
       
  3225     <xsl:text>                    first.setAttribute("dy", "-"+String((this.lift+1)*1.1)+"em");
       
  3226 </xsl:text>
       
  3227     <xsl:text>                    rect = txt.getBoundingClientRect();
       
  3228 </xsl:text>
       
  3229     <xsl:text>                    if(rect.top &gt; bounds.top){
       
  3230 </xsl:text>
       
  3231     <xsl:text>                        this.lift += 1;
       
  3232 </xsl:text>
       
  3233     <xsl:text>                    } else {
       
  3234 </xsl:text>
       
  3235     <xsl:text>                        // if it goes over the top, then backtrack
       
  3236 </xsl:text>
       
  3237     <xsl:text>                        // restore dy attribute on first span
       
  3238 </xsl:text>
       
  3239     <xsl:text>                        if(backup)
       
  3240 </xsl:text>
       
  3241     <xsl:text>                            first.setAttribute("dy", backup);
       
  3242 </xsl:text>
       
  3243     <xsl:text>                        else
       
  3244 </xsl:text>
       
  3245     <xsl:text>                            first.removeAttribute("dy");
       
  3246 </xsl:text>
       
  3247     <xsl:text>                        // remove unwanted child
       
  3248 </xsl:text>
       
  3249     <xsl:text>                        txt.removeChild(next);
       
  3250 </xsl:text>
       
  3251     <xsl:text>                        return count;
       
  3252 </xsl:text>
       
  3253     <xsl:text>                    }
       
  3254 </xsl:text>
       
  3255     <xsl:text>                }
       
  3256 </xsl:text>
       
  3257     <xsl:text>                count++;
       
  3258 </xsl:text>
       
  3259     <xsl:text>            }
       
  3260 </xsl:text>
       
  3261     <xsl:text>            return count;
       
  3262 </xsl:text>
       
  3263     <xsl:text>        }
       
  3264 </xsl:text>
       
  3265     <xsl:text>        close_on_click_elsewhere(e) {
       
  3266 </xsl:text>
       
  3267     <xsl:text>            // inhibit events not targetting spans (menu items)
       
  3268 </xsl:text>
       
  3269     <xsl:text>            if(e.target.parentNode !== this.text_elt){
       
  3270 </xsl:text>
       
  3271     <xsl:text>                e.stopPropagation();
       
  3272 </xsl:text>
       
  3273     <xsl:text>                // close menu in case click is outside box
       
  3274 </xsl:text>
       
  3275     <xsl:text>                if(e.target !== this.box_elt)
       
  3276 </xsl:text>
       
  3277     <xsl:text>                    this.close();
       
  3278 </xsl:text>
       
  3279     <xsl:text>            }
       
  3280 </xsl:text>
       
  3281     <xsl:text>        }
       
  3282 </xsl:text>
       
  3283     <xsl:text>        close(){
       
  3284 </xsl:text>
       
  3285     <xsl:text>            // Stop hogging all click events
       
  3286 </xsl:text>
       
  3287     <xsl:text>            svg_root.removeEventListener("click", this.bound_close_on_click_elsewhere, true);
       
  3288 </xsl:text>
       
  3289     <xsl:text>            // Restore position and sixe of widget elements
       
  3290 </xsl:text>
       
  3291     <xsl:text>            this.reset_text();
       
  3292 </xsl:text>
       
  3293     <xsl:text>            this.reset_box();
       
  3294 </xsl:text>
       
  3295     <xsl:text>            // Put the button back in place
       
  3296 </xsl:text>
       
  3297     <xsl:text>            this.element.appendChild(this.button_elt);
       
  3298 </xsl:text>
       
  3299     <xsl:text>            // Mark as closed (to allow dispatch)
       
  3300 </xsl:text>
       
  3301     <xsl:text>            this.opened = false;
       
  3302 </xsl:text>
       
  3303     <xsl:text>            // Dispatch last cached value
       
  3304 </xsl:text>
       
  3305     <xsl:text>            this.apply_cache();
       
  3306 </xsl:text>
       
  3307     <xsl:text>        }
       
  3308 </xsl:text>
       
  3309     <xsl:text>        // Set text content when content is smaller than menu (no scrolling)
       
  3310 </xsl:text>
       
  3311     <xsl:text>        set_complete_text(){
       
  3312 </xsl:text>
       
  3313     <xsl:text>            let spans = this.text_elt.children; 
       
  3314 </xsl:text>
       
  3315     <xsl:text>            let c = 0;
       
  3316 </xsl:text>
       
  3317     <xsl:text>            for(let item of this.content){
       
  3318 </xsl:text>
       
  3319     <xsl:text>                let span=spans[c];
       
  3320 </xsl:text>
       
  3321     <xsl:text>                span.textContent = item;
       
  3322 </xsl:text>
       
  3323     <xsl:text>                span.onclick = (evt) =&gt; this.bound_on_selection_click(c);
       
  3324 </xsl:text>
       
  3325     <xsl:text>                c++;
       
  3326 </xsl:text>
       
  3327     <xsl:text>            }
       
  3328 </xsl:text>
       
  3329     <xsl:text>        }
       
  3330 </xsl:text>
       
  3331     <xsl:text>        // Move partial view :
       
  3332 </xsl:text>
       
  3333     <xsl:text>        // false : upward, lower value
       
  3334 </xsl:text>
       
  3335     <xsl:text>        // true  : downward, higher value
       
  3336 </xsl:text>
       
  3337     <xsl:text>        scroll(forward){
       
  3338 </xsl:text>
       
  3339     <xsl:text>            let contentlength = this.content.length;
       
  3340 </xsl:text>
       
  3341     <xsl:text>            let spans = this.text_elt.children; 
       
  3342 </xsl:text>
       
  3343     <xsl:text>            let spanslength = spans.length;
       
  3344 </xsl:text>
       
  3345     <xsl:text>            // reduce accounted menu size according to jumps
       
  3346 </xsl:text>
       
  3347     <xsl:text>            if(this.menu_offset != 0) spanslength--;
       
  3348 </xsl:text>
       
  3349     <xsl:text>            if(this.menu_offset &lt; contentlength - 1) spanslength--;
       
  3350 </xsl:text>
       
  3351     <xsl:text>            if(forward){
       
  3352 </xsl:text>
       
  3353     <xsl:text>                this.menu_offset = Math.min(
       
  3354 </xsl:text>
       
  3355     <xsl:text>                    contentlength - spans.length + 1, 
       
  3356 </xsl:text>
       
  3357     <xsl:text>                    this.menu_offset + spanslength);
       
  3358 </xsl:text>
       
  3359     <xsl:text>            }else{
       
  3360 </xsl:text>
       
  3361     <xsl:text>                this.menu_offset = Math.max(
       
  3362 </xsl:text>
       
  3363     <xsl:text>                    0, 
       
  3364 </xsl:text>
       
  3365     <xsl:text>                    this.menu_offset - spanslength);
       
  3366 </xsl:text>
       
  3367     <xsl:text>            }
       
  3368 </xsl:text>
       
  3369     <xsl:text>            this.set_partial_text();
       
  3370 </xsl:text>
       
  3371     <xsl:text>        }
       
  3372 </xsl:text>
       
  3373     <xsl:text>        // Setup partial view text content
       
  3374 </xsl:text>
       
  3375     <xsl:text>        // with jumps at first and last entry when appropriate
       
  3376 </xsl:text>
       
  3377     <xsl:text>        set_partial_text(){
       
  3378 </xsl:text>
       
  3379     <xsl:text>            let spans = this.text_elt.children; 
       
  3380 </xsl:text>
       
  3381     <xsl:text>            let contentlength = this.content.length;
       
  3382 </xsl:text>
       
  3383     <xsl:text>            let spanslength = spans.length;
       
  3384 </xsl:text>
       
  3385     <xsl:text>            let i = this.menu_offset, c = 0;
       
  3386 </xsl:text>
       
  3387     <xsl:text>            while(c &lt; spanslength){
       
  3388 </xsl:text>
       
  3389     <xsl:text>                let span=spans[c];
       
  3390 </xsl:text>
       
  3391     <xsl:text>                // backward jump only present if not exactly at start
       
  3392 </xsl:text>
       
  3393     <xsl:text>                if(c == 0 &amp;&amp; i != 0){
       
  3394 </xsl:text>
       
  3395     <xsl:text>                    span.textContent = "&#x2191;  &#x2191;  &#x2191;";
       
  3396 </xsl:text>
       
  3397     <xsl:text>                    span.onclick = this.bound_on_backward_click;
       
  3398 </xsl:text>
       
  3399     <xsl:text>                // presence of forward jump when not right at the end
       
  3400 </xsl:text>
       
  3401     <xsl:text>                }else if(c == spanslength-1 &amp;&amp; i &lt; contentlength - 1){
       
  3402 </xsl:text>
       
  3403     <xsl:text>                    span.textContent = "&#x2193;  &#x2193;  &#x2193;";
       
  3404 </xsl:text>
       
  3405     <xsl:text>                    span.onclick = this.bound_on_forward_click;
       
  3406 </xsl:text>
       
  3407     <xsl:text>                // otherwise normal content
       
  3408 </xsl:text>
       
  3409     <xsl:text>                }else{
       
  3410 </xsl:text>
       
  3411     <xsl:text>                    span.textContent = this.content[i];
       
  3412 </xsl:text>
       
  3413     <xsl:text>                    let sel = i;
       
  3414 </xsl:text>
       
  3415     <xsl:text>                    span.onclick = (evt) =&gt; this.bound_on_selection_click(sel);
       
  3416 </xsl:text>
       
  3417     <xsl:text>                    i++;
       
  3418 </xsl:text>
       
  3419     <xsl:text>                }
       
  3420 </xsl:text>
       
  3421     <xsl:text>                c++;
       
  3422 </xsl:text>
       
  3423     <xsl:text>            }
       
  3424 </xsl:text>
       
  3425     <xsl:text>        }
       
  3426 </xsl:text>
       
  3427     <xsl:text>        open(){
       
  3428 </xsl:text>
       
  3429     <xsl:text>            let length = this.content.length;
       
  3430 </xsl:text>
       
  3431     <xsl:text>            // systematically reset text, to strip eventual whitespace spans
       
  3432 </xsl:text>
       
  3433     <xsl:text>            this.reset_text();
       
  3434 </xsl:text>
       
  3435     <xsl:text>            // grow as much as needed or possible
       
  3436 </xsl:text>
       
  3437     <xsl:text>            let slots = this.grow_text(length);
       
  3438 </xsl:text>
       
  3439     <xsl:text>            // Depending on final size
       
  3440 </xsl:text>
       
  3441     <xsl:text>            if(slots == length) {
       
  3442 </xsl:text>
       
  3443     <xsl:text>                // show all at once
       
  3444 </xsl:text>
       
  3445     <xsl:text>                this.set_complete_text();
       
  3446 </xsl:text>
       
  3447     <xsl:text>            } else {
       
  3448 </xsl:text>
       
  3449     <xsl:text>                // eventualy align menu to current selection, compensating for lift
       
  3450 </xsl:text>
       
  3451     <xsl:text>                let offset = this.last_selection - this.lift;
       
  3452 </xsl:text>
       
  3453     <xsl:text>                if(offset &gt; 0)
       
  3454 </xsl:text>
       
  3455     <xsl:text>                    this.menu_offset = Math.min(offset + 1, length - slots + 1);
       
  3456 </xsl:text>
       
  3457     <xsl:text>                else
       
  3458 </xsl:text>
       
  3459     <xsl:text>                    this.menu_offset = 0;
       
  3460 </xsl:text>
       
  3461     <xsl:text>                // show surrounding values
       
  3462 </xsl:text>
       
  3463     <xsl:text>                this.set_partial_text();
       
  3464 </xsl:text>
       
  3465     <xsl:text>            }
       
  3466 </xsl:text>
       
  3467     <xsl:text>            // Now that text size is known, we can set the box around it
       
  3468 </xsl:text>
       
  3469     <xsl:text>            this.adjust_box_to_text();
       
  3470 </xsl:text>
       
  3471     <xsl:text>            // Take button out until menu closed
       
  3472 </xsl:text>
       
  3473     <xsl:text>            this.element.removeChild(this.button_elt);
       
  3474 </xsl:text>
       
  3475     <xsl:text>            // Rise widget to top by moving it to last position among siblings
       
  3476 </xsl:text>
       
  3477     <xsl:text>            this.element.parentNode.appendChild(this.element.parentNode.removeChild(this.element));
       
  3478 </xsl:text>
       
  3479     <xsl:text>            // disable interaction with background
       
  3480 </xsl:text>
       
  3481     <xsl:text>            svg_root.addEventListener("click", this.bound_close_on_click_elsewhere, true);
       
  3482 </xsl:text>
       
  3483     <xsl:text>            // mark as open
       
  3484 </xsl:text>
       
  3485     <xsl:text>            this.opened = true;
       
  3486 </xsl:text>
       
  3487     <xsl:text>        }
       
  3488 </xsl:text>
       
  3489     <xsl:text>        // Put text element in normalized state
       
  3490 </xsl:text>
       
  3491     <xsl:text>        reset_text(){
       
  3492 </xsl:text>
       
  3493     <xsl:text>            let txt = this.text_elt; 
       
  3494 </xsl:text>
       
  3495     <xsl:text>            let first = txt.firstElementChild;
       
  3496 </xsl:text>
       
  3497     <xsl:text>            // remove attribute eventually added to first text line while opening
       
  3498 </xsl:text>
       
  3499     <xsl:text>            first.removeAttribute("onclick");
       
  3500 </xsl:text>
       
  3501     <xsl:text>            first.removeAttribute("dy");
       
  3502 </xsl:text>
       
  3503     <xsl:text>            // keep only the first line of text
       
  3504 </xsl:text>
       
  3505     <xsl:text>            for(let span of Array.from(txt.children).slice(1)){
       
  3506 </xsl:text>
       
  3507     <xsl:text>                txt.removeChild(span)
       
  3508 </xsl:text>
       
  3509     <xsl:text>            }
       
  3510 </xsl:text>
       
  3511     <xsl:text>        }
       
  3512 </xsl:text>
       
  3513     <xsl:text>        // Put rectangle element in saved original state
       
  3514 </xsl:text>
       
  3515     <xsl:text>        reset_box(){
       
  3516 </xsl:text>
       
  3517     <xsl:text>            let m = this.box_bbox;
       
  3518 </xsl:text>
       
  3519     <xsl:text>            let b = this.box_elt;
       
  3520 </xsl:text>
       
  3521     <xsl:text>            b.x.baseVal.value = m.x;
       
  3522 </xsl:text>
       
  3523     <xsl:text>            b.y.baseVal.value = m.y;
       
  3524 </xsl:text>
       
  3525     <xsl:text>            b.width.baseVal.value = m.width;
       
  3526 </xsl:text>
       
  3527     <xsl:text>            b.height.baseVal.value = m.height;
       
  3528 </xsl:text>
       
  3529     <xsl:text>        }
       
  3530 </xsl:text>
       
  3531     <xsl:text>        // Use margin and text size to compute box size
       
  3532 </xsl:text>
       
  3533     <xsl:text>        adjust_box_to_text(){
       
  3534 </xsl:text>
       
  3535     <xsl:text>            let [lmargin, tmargin] = this.margins;
       
  3536 </xsl:text>
       
  3537     <xsl:text>            let m = this.text_elt.getBBox();
       
  3538 </xsl:text>
       
  3539     <xsl:text>            let b = this.box_elt;
       
  3540 </xsl:text>
       
  3541     <xsl:text>            b.x.baseVal.value = m.x - lmargin;
       
  3542 </xsl:text>
       
  3543     <xsl:text>            b.y.baseVal.value = m.y - tmargin;
       
  3544 </xsl:text>
       
  3545     <xsl:text>            b.width.baseVal.value = 2 * lmargin + m.width;
       
  3546 </xsl:text>
       
  3547     <xsl:text>            b.height.baseVal.value = 2 * tmargin + m.height;
       
  3548 </xsl:text>
       
  3549     <xsl:text>        }
       
  3550 </xsl:text>
       
  3551     <xsl:text>    }
       
  3552 </xsl:text>
       
  3553   </xsl:template>
  3066   <xsl:template mode="widget_defs" match="widget[@type='DropDown']">
  3554   <xsl:template mode="widget_defs" match="widget[@type='DropDown']">
  3067     <xsl:param name="hmi_element"/>
  3555     <xsl:param name="hmi_element"/>
  3068     <xsl:call-template name="defs_by_labels">
  3556     <xsl:call-template name="defs_by_labels">
  3069       <xsl:with-param name="hmi_element" select="$hmi_element"/>
  3557       <xsl:with-param name="hmi_element" select="$hmi_element"/>
  3070       <xsl:with-param name="labels">
  3558       <xsl:with-param name="labels">
  3071         <xsl:text>text box button</xsl:text>
  3559         <xsl:text>text box button</xsl:text>
  3072       </xsl:with-param>
  3560       </xsl:with-param>
  3073     </xsl:call-template>
  3561     </xsl:call-template>
  3074     <xsl:text>    dispatch: function(value) {
  3562     <xsl:text>    // It is assumed that list content conforms to Array interface.
  3075 </xsl:text>
  3563 </xsl:text>
  3076     <xsl:text>        if(!this.opened) this.set_selection(value);
  3564     <xsl:text>    content: [
  3077 </xsl:text>
       
  3078     <xsl:text>    },
       
  3079 </xsl:text>
       
  3080     <xsl:text>    init: function() {
       
  3081 </xsl:text>
       
  3082     <xsl:text>        this.button_elt.setAttribute("onclick", "hmi_widgets['</xsl:text>
       
  3083     <xsl:value-of select="$hmi_element/@id"/>
       
  3084     <xsl:text>'].on_button_click()");
       
  3085 </xsl:text>
       
  3086     <xsl:text>        // Save original size of rectangle
       
  3087 </xsl:text>
       
  3088     <xsl:text>        this.box_bbox = this.box_elt.getBBox()
       
  3089 </xsl:text>
       
  3090     <xsl:text>
       
  3091 </xsl:text>
       
  3092     <xsl:text>        // Compute margins
       
  3093 </xsl:text>
       
  3094     <xsl:text>        text_bbox = this.text_elt.getBBox()
       
  3095 </xsl:text>
       
  3096     <xsl:text>        lmargin = text_bbox.x - this.box_bbox.x;
       
  3097 </xsl:text>
       
  3098     <xsl:text>        tmargin = text_bbox.y - this.box_bbox.y;
       
  3099 </xsl:text>
       
  3100     <xsl:text>        this.margins = [lmargin, tmargin].map(x =&gt; Math.max(x,0));
       
  3101 </xsl:text>
       
  3102     <xsl:text>
       
  3103 </xsl:text>
       
  3104     <xsl:text>        // It is assumed that list content conforms to Array interface.
       
  3105 </xsl:text>
       
  3106     <xsl:text>        this.content = [
       
  3107 </xsl:text>
  3565 </xsl:text>
  3108     <xsl:for-each select="arg">
  3566     <xsl:for-each select="arg">
  3109       <xsl:text>"</xsl:text>
  3567       <xsl:text>"</xsl:text>
  3110       <xsl:value-of select="@value"/>
  3568       <xsl:value-of select="@value"/>
  3111       <xsl:text>",
  3569       <xsl:text>",
  3112 </xsl:text>
  3570 </xsl:text>
  3113     </xsl:for-each>
  3571     </xsl:for-each>
  3114     <xsl:text>        ];
  3572     <xsl:text>    ],
  3115 </xsl:text>
  3573 </xsl:text>
  3116     <xsl:text>
  3574     <xsl:text>
  3117 </xsl:text>
       
  3118     <xsl:text>        // Index of first visible element in the menu, when opened
       
  3119 </xsl:text>
       
  3120     <xsl:text>        this.menu_offset = 0;
       
  3121 </xsl:text>
       
  3122     <xsl:text>
       
  3123 </xsl:text>
       
  3124     <xsl:text>        // How mutch to lift the menu vertically so that it does not cross bottom border
       
  3125 </xsl:text>
       
  3126     <xsl:text>        this.lift = 0;
       
  3127 </xsl:text>
       
  3128     <xsl:text>
       
  3129 </xsl:text>
       
  3130     <xsl:text>        // Event handlers cannot be object method ('this' is unknown)
       
  3131 </xsl:text>
       
  3132     <xsl:text>        // as a workaround, handler given to addEventListener is bound in advance.
       
  3133 </xsl:text>
       
  3134     <xsl:text>        this.bound_close_on_click_elsewhere = this.close_on_click_elsewhere.bind(this);
       
  3135 </xsl:text>
       
  3136     <xsl:text>
       
  3137 </xsl:text>
       
  3138     <xsl:text>        this.opened = false;
       
  3139 </xsl:text>
       
  3140     <xsl:text>    },
       
  3141 </xsl:text>
       
  3142     <xsl:text>    // Called when a menu entry is clicked
       
  3143 </xsl:text>
       
  3144     <xsl:text>    on_selection_click: function(selection) {
       
  3145 </xsl:text>
       
  3146     <xsl:text>        this.close();
       
  3147 </xsl:text>
       
  3148     <xsl:text>        this.apply_hmi_value(0, selection);
       
  3149 </xsl:text>
       
  3150     <xsl:text>    },
       
  3151 </xsl:text>
       
  3152     <xsl:text>    on_button_click: function() {
       
  3153 </xsl:text>
       
  3154     <xsl:text>        this.open();
       
  3155 </xsl:text>
       
  3156     <xsl:text>    },
       
  3157 </xsl:text>
       
  3158     <xsl:text>    on_backward_click: function(){
       
  3159 </xsl:text>
       
  3160     <xsl:text>        this.scroll(false);
       
  3161 </xsl:text>
       
  3162     <xsl:text>    },
       
  3163 </xsl:text>
       
  3164     <xsl:text>    on_forward_click:function(){
       
  3165 </xsl:text>
       
  3166     <xsl:text>        this.scroll(true);
       
  3167 </xsl:text>
       
  3168     <xsl:text>    },
       
  3169 </xsl:text>
       
  3170     <xsl:text>    set_selection: function(value) {
       
  3171 </xsl:text>
       
  3172     <xsl:text>        let display_str;
       
  3173 </xsl:text>
       
  3174     <xsl:text>        if(value &gt;= 0 &amp;&amp; value &lt; this.content.length){
       
  3175 </xsl:text>
       
  3176     <xsl:text>            // if valid selection resolve content
       
  3177 </xsl:text>
       
  3178     <xsl:text>            display_str = this.content[value];
       
  3179 </xsl:text>
       
  3180     <xsl:text>            this.last_selection = value;
       
  3181 </xsl:text>
       
  3182     <xsl:text>        } else {
       
  3183 </xsl:text>
       
  3184     <xsl:text>            // otherwise show problem
       
  3185 </xsl:text>
       
  3186     <xsl:text>            display_str = "?"+String(value)+"?";
       
  3187 </xsl:text>
       
  3188     <xsl:text>        }
       
  3189 </xsl:text>
       
  3190     <xsl:text>        // It is assumed that first span always stays,
       
  3191 </xsl:text>
       
  3192     <xsl:text>        // and contains selection when menu is closed
       
  3193 </xsl:text>
       
  3194     <xsl:text>        this.text_elt.firstElementChild.textContent = display_str;
       
  3195 </xsl:text>
       
  3196     <xsl:text>    },
       
  3197 </xsl:text>
       
  3198     <xsl:text>    grow_text: function(up_to) {
       
  3199 </xsl:text>
       
  3200     <xsl:text>        let count = 1;
       
  3201 </xsl:text>
       
  3202     <xsl:text>        let txt = this.text_elt; 
       
  3203 </xsl:text>
       
  3204     <xsl:text>        let first = txt.firstElementChild;
       
  3205 </xsl:text>
       
  3206     <xsl:text>        // Real world (pixels) boundaries of current page
       
  3207 </xsl:text>
       
  3208     <xsl:text>        let bounds = svg_root.getBoundingClientRect(); 
       
  3209 </xsl:text>
       
  3210     <xsl:text>        this.lift = 0;
       
  3211 </xsl:text>
       
  3212     <xsl:text>        while(count &lt; up_to) {
       
  3213 </xsl:text>
       
  3214     <xsl:text>            let next = first.cloneNode();
       
  3215 </xsl:text>
       
  3216     <xsl:text>            // relative line by line text flow instead of absolute y coordinate
       
  3217 </xsl:text>
       
  3218     <xsl:text>            next.removeAttribute("y");
       
  3219 </xsl:text>
       
  3220     <xsl:text>            next.setAttribute("dy", "1.1em");
       
  3221 </xsl:text>
       
  3222     <xsl:text>            // default content to allow computing text element bbox
       
  3223 </xsl:text>
       
  3224     <xsl:text>            next.textContent = "...";
       
  3225 </xsl:text>
       
  3226     <xsl:text>            // append new span to text element
       
  3227 </xsl:text>
       
  3228     <xsl:text>            txt.appendChild(next);
       
  3229 </xsl:text>
       
  3230     <xsl:text>            // now check if text extended by one row fits to page
       
  3231 </xsl:text>
       
  3232     <xsl:text>            // FIXME : exclude margins to be more accurate on box size
       
  3233 </xsl:text>
       
  3234     <xsl:text>            let rect = txt.getBoundingClientRect();
       
  3235 </xsl:text>
       
  3236     <xsl:text>            if(rect.bottom &gt; bounds.bottom){
       
  3237 </xsl:text>
       
  3238     <xsl:text>                // in case of overflow at the bottom, lift up one row
       
  3239 </xsl:text>
       
  3240     <xsl:text>                let backup = first.getAttribute("dy");
       
  3241 </xsl:text>
       
  3242     <xsl:text>                // apply lift asr a dy added too first span (y attrib stays)
       
  3243 </xsl:text>
       
  3244     <xsl:text>                first.setAttribute("dy", "-"+String((this.lift+1)*1.1)+"em");
       
  3245 </xsl:text>
       
  3246     <xsl:text>                rect = txt.getBoundingClientRect();
       
  3247 </xsl:text>
       
  3248     <xsl:text>                if(rect.top &gt; bounds.top){
       
  3249 </xsl:text>
       
  3250     <xsl:text>                    this.lift += 1;
       
  3251 </xsl:text>
       
  3252     <xsl:text>                } else {
       
  3253 </xsl:text>
       
  3254     <xsl:text>                    // if it goes over the top, then backtrack
       
  3255 </xsl:text>
       
  3256     <xsl:text>                    // restore dy attribute on first span
       
  3257 </xsl:text>
       
  3258     <xsl:text>                    if(backup)
       
  3259 </xsl:text>
       
  3260     <xsl:text>                        first.setAttribute("dy", backup);
       
  3261 </xsl:text>
       
  3262     <xsl:text>                    else
       
  3263 </xsl:text>
       
  3264     <xsl:text>                        first.removeAttribute("dy");
       
  3265 </xsl:text>
       
  3266     <xsl:text>                    // remove unwanted child
       
  3267 </xsl:text>
       
  3268     <xsl:text>                    txt.removeChild(next);
       
  3269 </xsl:text>
       
  3270     <xsl:text>                    return count;
       
  3271 </xsl:text>
       
  3272     <xsl:text>                }
       
  3273 </xsl:text>
       
  3274     <xsl:text>            }
       
  3275 </xsl:text>
       
  3276     <xsl:text>            count++;
       
  3277 </xsl:text>
       
  3278     <xsl:text>        }
       
  3279 </xsl:text>
       
  3280     <xsl:text>        return count;
       
  3281 </xsl:text>
       
  3282     <xsl:text>    },
       
  3283 </xsl:text>
       
  3284     <xsl:text>    close_on_click_elsewhere: function(e) {
       
  3285 </xsl:text>
       
  3286     <xsl:text>        // inhibit events not targetting spans (menu items)
       
  3287 </xsl:text>
       
  3288     <xsl:text>        if(e.target.parentNode !== this.text_elt){
       
  3289 </xsl:text>
       
  3290     <xsl:text>            e.stopPropagation();
       
  3291 </xsl:text>
       
  3292     <xsl:text>            // close menu in case click is outside box
       
  3293 </xsl:text>
       
  3294     <xsl:text>            if(e.target !== this.box_elt)
       
  3295 </xsl:text>
       
  3296     <xsl:text>                this.close();
       
  3297 </xsl:text>
       
  3298     <xsl:text>        }
       
  3299 </xsl:text>
       
  3300     <xsl:text>    },
       
  3301 </xsl:text>
       
  3302     <xsl:text>    close: function(){
       
  3303 </xsl:text>
       
  3304     <xsl:text>        // Stop hogging all click events
       
  3305 </xsl:text>
       
  3306     <xsl:text>        svg_root.removeEventListener("click", this.bound_close_on_click_elsewhere, true);
       
  3307 </xsl:text>
       
  3308     <xsl:text>        // Restore position and sixe of widget elements
       
  3309 </xsl:text>
       
  3310     <xsl:text>        this.reset_text();
       
  3311 </xsl:text>
       
  3312     <xsl:text>        this.reset_box();
       
  3313 </xsl:text>
       
  3314     <xsl:text>        // Put the button back in place
       
  3315 </xsl:text>
       
  3316     <xsl:text>        this.element.appendChild(this.button_elt);
       
  3317 </xsl:text>
       
  3318     <xsl:text>        // Mark as closed (to allow dispatch)
       
  3319 </xsl:text>
       
  3320     <xsl:text>        this.opened = false;
       
  3321 </xsl:text>
       
  3322     <xsl:text>        // Dispatch last cached value
       
  3323 </xsl:text>
       
  3324     <xsl:text>        this.apply_cache();
       
  3325 </xsl:text>
       
  3326     <xsl:text>    },
       
  3327 </xsl:text>
       
  3328     <xsl:text>    // Set text content when content is smaller than menu (no scrolling)
       
  3329 </xsl:text>
       
  3330     <xsl:text>    set_complete_text: function(){
       
  3331 </xsl:text>
       
  3332     <xsl:text>        let spans = this.text_elt.children; 
       
  3333 </xsl:text>
       
  3334     <xsl:text>        let c = 0;
       
  3335 </xsl:text>
       
  3336     <xsl:text>        for(let item of this.content){
       
  3337 </xsl:text>
       
  3338     <xsl:text>            let span=spans[c];
       
  3339 </xsl:text>
       
  3340     <xsl:text>            span.textContent = item;
       
  3341 </xsl:text>
       
  3342     <xsl:text>            span.setAttribute("onclick", "hmi_widgets['</xsl:text>
       
  3343     <xsl:value-of select="$hmi_element/@id"/>
       
  3344     <xsl:text>'].on_selection_click("+c+")");
       
  3345 </xsl:text>
       
  3346     <xsl:text>            c++;
       
  3347 </xsl:text>
       
  3348     <xsl:text>        }
       
  3349 </xsl:text>
       
  3350     <xsl:text>    },
       
  3351 </xsl:text>
       
  3352     <xsl:text>    // Move partial view :
       
  3353 </xsl:text>
       
  3354     <xsl:text>    // false : upward, lower value
       
  3355 </xsl:text>
       
  3356     <xsl:text>    // true  : downward, higher value
       
  3357 </xsl:text>
       
  3358     <xsl:text>    scroll: function(forward){
       
  3359 </xsl:text>
       
  3360     <xsl:text>        let contentlength = this.content.length;
       
  3361 </xsl:text>
       
  3362     <xsl:text>        let spans = this.text_elt.children; 
       
  3363 </xsl:text>
       
  3364     <xsl:text>        let spanslength = spans.length;
       
  3365 </xsl:text>
       
  3366     <xsl:text>        // reduce accounted menu size according to jumps
       
  3367 </xsl:text>
       
  3368     <xsl:text>        if(this.menu_offset != 0) spanslength--;
       
  3369 </xsl:text>
       
  3370     <xsl:text>        if(this.menu_offset &lt; contentlength - 1) spanslength--;
       
  3371 </xsl:text>
       
  3372     <xsl:text>        if(forward){
       
  3373 </xsl:text>
       
  3374     <xsl:text>            this.menu_offset = Math.min(
       
  3375 </xsl:text>
       
  3376     <xsl:text>                contentlength - spans.length + 1, 
       
  3377 </xsl:text>
       
  3378     <xsl:text>                this.menu_offset + spanslength);
       
  3379 </xsl:text>
       
  3380     <xsl:text>        }else{
       
  3381 </xsl:text>
       
  3382     <xsl:text>            this.menu_offset = Math.max(
       
  3383 </xsl:text>
       
  3384     <xsl:text>                0, 
       
  3385 </xsl:text>
       
  3386     <xsl:text>                this.menu_offset - spanslength);
       
  3387 </xsl:text>
       
  3388     <xsl:text>        }
       
  3389 </xsl:text>
       
  3390     <xsl:text>        this.set_partial_text();
       
  3391 </xsl:text>
       
  3392     <xsl:text>    },
       
  3393 </xsl:text>
       
  3394     <xsl:text>    // Setup partial view text content
       
  3395 </xsl:text>
       
  3396     <xsl:text>    // with jumps at first and last entry when appropriate
       
  3397 </xsl:text>
       
  3398     <xsl:text>    set_partial_text: function(){
       
  3399 </xsl:text>
       
  3400     <xsl:text>        let spans = this.text_elt.children; 
       
  3401 </xsl:text>
       
  3402     <xsl:text>        let contentlength = this.content.length;
       
  3403 </xsl:text>
       
  3404     <xsl:text>        let spanslength = spans.length;
       
  3405 </xsl:text>
       
  3406     <xsl:text>        let i = this.menu_offset, c = 0;
       
  3407 </xsl:text>
       
  3408     <xsl:text>        while(c &lt; spanslength){
       
  3409 </xsl:text>
       
  3410     <xsl:text>            let span=spans[c];
       
  3411 </xsl:text>
       
  3412     <xsl:text>            // backward jump only present if not exactly at start
       
  3413 </xsl:text>
       
  3414     <xsl:text>            if(c == 0 &amp;&amp; i != 0){
       
  3415 </xsl:text>
       
  3416     <xsl:text>                span.textContent = "&#x2191;  &#x2191;  &#x2191;";
       
  3417 </xsl:text>
       
  3418     <xsl:text>                span.setAttribute("onclick", "hmi_widgets['</xsl:text>
       
  3419     <xsl:value-of select="$hmi_element/@id"/>
       
  3420     <xsl:text>'].on_backward_click()");
       
  3421 </xsl:text>
       
  3422     <xsl:text>            // presence of forward jump when not right at the end
       
  3423 </xsl:text>
       
  3424     <xsl:text>            }else if(c == spanslength-1 &amp;&amp; i &lt; contentlength - 1){
       
  3425 </xsl:text>
       
  3426     <xsl:text>                span.textContent = "&#x2193;  &#x2193;  &#x2193;";
       
  3427 </xsl:text>
       
  3428     <xsl:text>                span.setAttribute("onclick", "hmi_widgets['</xsl:text>
       
  3429     <xsl:value-of select="$hmi_element/@id"/>
       
  3430     <xsl:text>'].on_forward_click()");
       
  3431 </xsl:text>
       
  3432     <xsl:text>            // otherwise normal content
       
  3433 </xsl:text>
       
  3434     <xsl:text>            }else{
       
  3435 </xsl:text>
       
  3436     <xsl:text>                span.textContent = this.content[i];
       
  3437 </xsl:text>
       
  3438     <xsl:text>                span.setAttribute("onclick", "hmi_widgets['</xsl:text>
       
  3439     <xsl:value-of select="$hmi_element/@id"/>
       
  3440     <xsl:text>'].on_selection_click("+i+")");
       
  3441 </xsl:text>
       
  3442     <xsl:text>                i++;
       
  3443 </xsl:text>
       
  3444     <xsl:text>            }
       
  3445 </xsl:text>
       
  3446     <xsl:text>            c++;
       
  3447 </xsl:text>
       
  3448     <xsl:text>        }
       
  3449 </xsl:text>
       
  3450     <xsl:text>    },
       
  3451 </xsl:text>
       
  3452     <xsl:text>    open: function(){
       
  3453 </xsl:text>
       
  3454     <xsl:text>        let length = this.content.length;
       
  3455 </xsl:text>
       
  3456     <xsl:text>        // systematically reset text, to strip eventual whitespace spans
       
  3457 </xsl:text>
       
  3458     <xsl:text>        this.reset_text();
       
  3459 </xsl:text>
       
  3460     <xsl:text>        // grow as much as needed or possible
       
  3461 </xsl:text>
       
  3462     <xsl:text>        let slots = this.grow_text(length);
       
  3463 </xsl:text>
       
  3464     <xsl:text>        // Depending on final size
       
  3465 </xsl:text>
       
  3466     <xsl:text>        if(slots == length) {
       
  3467 </xsl:text>
       
  3468     <xsl:text>            // show all at once
       
  3469 </xsl:text>
       
  3470     <xsl:text>            this.set_complete_text();
       
  3471 </xsl:text>
       
  3472     <xsl:text>        } else {
       
  3473 </xsl:text>
       
  3474     <xsl:text>            // eventualy align menu to current selection, compensating for lift
       
  3475 </xsl:text>
       
  3476     <xsl:text>            let offset = this.last_selection - this.lift;
       
  3477 </xsl:text>
       
  3478     <xsl:text>            if(offset &gt; 0)
       
  3479 </xsl:text>
       
  3480     <xsl:text>                this.menu_offset = Math.min(offset + 1, length - slots + 1);
       
  3481 </xsl:text>
       
  3482     <xsl:text>            else
       
  3483 </xsl:text>
       
  3484     <xsl:text>                this.menu_offset = 0;
       
  3485 </xsl:text>
       
  3486     <xsl:text>            // show surrounding values
       
  3487 </xsl:text>
       
  3488     <xsl:text>            this.set_partial_text();
       
  3489 </xsl:text>
       
  3490     <xsl:text>        }
       
  3491 </xsl:text>
       
  3492     <xsl:text>        // Now that text size is known, we can set the box around it
       
  3493 </xsl:text>
       
  3494     <xsl:text>        this.adjust_box_to_text();
       
  3495 </xsl:text>
       
  3496     <xsl:text>        // Take button out until menu closed
       
  3497 </xsl:text>
       
  3498     <xsl:text>        this.element.removeChild(this.button_elt);
       
  3499 </xsl:text>
       
  3500     <xsl:text>        // Rise widget to top by moving it to last position among siblings
       
  3501 </xsl:text>
       
  3502     <xsl:text>        this.element.parentNode.appendChild(this.element.parentNode.removeChild(this.element));
       
  3503 </xsl:text>
       
  3504     <xsl:text>        // disable interaction with background
       
  3505 </xsl:text>
       
  3506     <xsl:text>        svg_root.addEventListener("click", this.bound_close_on_click_elsewhere, true);
       
  3507 </xsl:text>
       
  3508     <xsl:text>        // mark as open
       
  3509 </xsl:text>
       
  3510     <xsl:text>        this.opened = true;
       
  3511 </xsl:text>
       
  3512     <xsl:text>    },
       
  3513 </xsl:text>
       
  3514     <xsl:text>    // Put text element in normalized state
       
  3515 </xsl:text>
       
  3516     <xsl:text>    reset_text: function(){
       
  3517 </xsl:text>
       
  3518     <xsl:text>        let txt = this.text_elt; 
       
  3519 </xsl:text>
       
  3520     <xsl:text>        let first = txt.firstElementChild;
       
  3521 </xsl:text>
       
  3522     <xsl:text>        // remove attribute eventually added to first text line while opening
       
  3523 </xsl:text>
       
  3524     <xsl:text>        first.removeAttribute("onclick");
       
  3525 </xsl:text>
       
  3526     <xsl:text>        first.removeAttribute("dy");
       
  3527 </xsl:text>
       
  3528     <xsl:text>        // keep only the first line of text
       
  3529 </xsl:text>
       
  3530     <xsl:text>        for(let span of Array.from(txt.children).slice(1)){
       
  3531 </xsl:text>
       
  3532     <xsl:text>            txt.removeChild(span)
       
  3533 </xsl:text>
       
  3534     <xsl:text>        }
       
  3535 </xsl:text>
       
  3536     <xsl:text>    },
       
  3537 </xsl:text>
       
  3538     <xsl:text>    // Put rectangle element in saved original state
       
  3539 </xsl:text>
       
  3540     <xsl:text>    reset_box: function(){
       
  3541 </xsl:text>
       
  3542     <xsl:text>        let m = this.box_bbox;
       
  3543 </xsl:text>
       
  3544     <xsl:text>        let b = this.box_elt;
       
  3545 </xsl:text>
       
  3546     <xsl:text>        b.x.baseVal.value = m.x;
       
  3547 </xsl:text>
       
  3548     <xsl:text>        b.y.baseVal.value = m.y;
       
  3549 </xsl:text>
       
  3550     <xsl:text>        b.width.baseVal.value = m.width;
       
  3551 </xsl:text>
       
  3552     <xsl:text>        b.height.baseVal.value = m.height;
       
  3553 </xsl:text>
       
  3554     <xsl:text>    },
       
  3555 </xsl:text>
       
  3556     <xsl:text>    // Use margin and text size to compute box size
       
  3557 </xsl:text>
       
  3558     <xsl:text>    adjust_box_to_text: function(){
       
  3559 </xsl:text>
       
  3560     <xsl:text>        let [lmargin, tmargin] = this.margins;
       
  3561 </xsl:text>
       
  3562     <xsl:text>        let m = this.text_elt.getBBox();
       
  3563 </xsl:text>
       
  3564     <xsl:text>        let b = this.box_elt;
       
  3565 </xsl:text>
       
  3566     <xsl:text>        b.x.baseVal.value = m.x - lmargin;
       
  3567 </xsl:text>
       
  3568     <xsl:text>        b.y.baseVal.value = m.y - tmargin;
       
  3569 </xsl:text>
       
  3570     <xsl:text>        b.width.baseVal.value = 2 * lmargin + m.width;
       
  3571 </xsl:text>
       
  3572     <xsl:text>        b.height.baseVal.value = 2 * tmargin + m.height;
       
  3573 </xsl:text>
       
  3574     <xsl:text>    },
       
  3575 </xsl:text>
  3575 </xsl:text>
  3576   </xsl:template>
  3576   </xsl:template>
  3577   <xsl:template mode="widget_defs" match="widget[@type='ForEach']">
  3577   <xsl:template mode="widget_defs" match="widget[@type='ForEach']">
  3578     <xsl:param name="hmi_element"/>
  3578     <xsl:param name="hmi_element"/>
  3579     <xsl:if test="count(path) != 1">
  3579     <xsl:if test="count(path) != 1">