Edouard@2753: Edouard@3264: Edouard@3264: edouard@2941: Edouard@2879: Edouard@3383: Edouard@2877: Edouard@2790: Edouard@2790: Edouard@2790: HMI_PLC_STATUS Edouard@2790: Edouard@2790: Edouard@2790: HMI_CURRENT_PAGE Edouard@2790: Edouard@2790: Edouard@2790: Edouard@2867: Edouard@3019: Edouard@2867: Edouard@2867: edouard@2941: edouard@2941: edouard@2949: edouard@2949: edouard@2949: /* edouard@2949: edouard@2949: */ edouard@2949: edouard@2949: edouard@2949: edouard@2941: var hmi_hash = [ edouard@2941: edouard@2941: ]; edouard@2941: edouard@2941: edouard@2941: edouard@2941: var heartbeat_index = edouard@2941: edouard@2941: ; edouard@2941: edouard@2941: edouard@2941: Edouard@3383: var current_page_var_index = Edouard@3383: Edouard@3383: ; Edouard@3383: Edouard@3383: Edouard@3383: edouard@2941: var hmitree_types = [ edouard@2941: edouard@2941: Edouard@3383: " edouard@2941: edouard@2941: " edouard@2941: edouard@2941: , edouard@2941: edouard@2941: edouard@2941: edouard@2941: edouard@3097: ]; edouard@3097: edouard@3097: edouard@3097: Edouard@3206: var hmitree_paths = [ Edouard@3206: Edouard@3206: Edouard@3383: " Edouard@3206: Edouard@3206: " Edouard@3206: Edouard@3206: , Edouard@3206: Edouard@3206: Edouard@3206: Edouard@3206: Edouard@3206: ]; Edouard@3206: Edouard@3206: Edouard@3206: Edouard@3383: var hmitree_nodes = { Edouard@3383: Edouard@3383: Edouard@3383: " Edouard@3383: Edouard@3383: " : [ Edouard@3383: Edouard@3383: , " Edouard@3383: Edouard@3383: "] Edouard@3383: Edouard@3383: , Edouard@3383: Edouard@3383: Edouard@3383: Edouard@3383: Edouard@3383: }; Edouard@3383: Edouard@3383: Edouard@3383: edouard@2949: edouard@2949: edouard@2941: Edouard@3019: Edouard@2790: Edouard@2791: Edouard@2790: Edouard@2791: Edouard@2791: edouard@2890: edouard@2890: / edouard@2890: edouard@2890: edouard@2890: / edouard@2890: Edouard@2791: Edouard@2791: Edouard@2791: Edouard@2791: / Edouard@2791: Edouard@2791: Edouard@2791: Edouard@2791: Edouard@2790: Edouard@2790: Edouard@2790: Edouard@2790: Edouard@2790: Edouard@2790: Edouard@2791: Edouard@2791: Edouard@2791: Edouard@2790: Edouard@2790: Edouard@2790: Edouard@2790: Edouard@3019: Edouard@2867: Edouard@2867: Edouard@2867: Edouard@2867: Edouard@2867: Edouard@2790: Edouard@2790: Edouard@3019: Edouard@2790: Edouard@2791: Edouard@2791: Edouard@2791: Edouard@2790: Edouard@2790: Edouard@2790: Edouard@2790: Edouard@2790: Edouard@3019: Edouard@2790: Edouard@2791: Edouard@2791: Edouard@2791: Edouard@2790: Edouard@2790: edouard@3238: Edouard@3019: edouard@2886: edouard@3097: Edouard@2792: Edouard@2792: Edouard@2792: Edouard@2792: Edouard@2792: Edouard@2792: Edouard@2792: Edouard@2792: Edouard@2792: Edouard@2792: Edouard@2792: Edouard@2792: Edouard@3410: Edouard@3410: Edouard@3410: Edouard@3410: Edouard@3410: Edouard@3410: Edouard@3410: Edouard@3410: Edouard@3410: Edouard@3410: Edouard@3410: edouard@3414: Edouard@3410: Edouard@2792: Edouard@2792: Edouard@2792: Edouard@2792: Edouard@2792: Edouard@2792: Edouard@3410: Edouard@2792: Edouard@2792: Edouard@2792: edouard@2886: edouard@2886: edouard@2886: edouard@3097: edouard@2886: edouard@2886: edouard@2886: edouard@2886: edouard@3414: edouard@3414: Edouard@3410: Edouard@3410: Edouard@3410: edouard@2886: edouard@2886: edouard@2886: edouard@2886: edouard@2886: edouard@2886: edouard@2886: edouard@2886: edouard@2886: edouard@2886: edouard@2886: edouard@3238: edouard@3238: edouard@3238: edouard@3238: edouard@3097: Edouard@2793: edouard@3097: Edouard@2793: edouard@3238: edouard@3238: edouard@3238: edouard@3238: edouard@3238: edouard@3017: edouard@3238: edouard@3097: edouard@3238: edouard@3238: edouard@3238: edouard@3097: edouard@3017: edouard@3097: edouard@3238: edouard@3097: edouard@3097: Widget id: edouard@3097: edouard@3097: label: edouard@3097: edouard@3097: has wrong syntax of path section edouard@3097: edouard@3097: edouard@3097: edouard@3097: edouard@3229: edouard@3229: edouard@3229: edouard@3229: edouard@3229: PAGE_LOCAL edouard@3097: edouard@3229: edouard@3229: edouard@3097: edouard@3229: HMI_LOCAL edouard@3097: edouard@3229: edouard@3229: edouard@3229: edouard@3229: edouard@3229: edouard@3229: edouard@3229: Widget id: edouard@3229: edouard@3229: label: edouard@3229: edouard@3229: path section edouard@3229: edouard@3229: use min and max on non mumeric value edouard@3229: edouard@3229: edouard@3229: edouard@3229: edouard@3229: edouard@3229: edouard@3229: edouard@3229: edouard@3229: edouard@3229: edouard@3229: edouard@3229: edouard@3229: edouard@2886: edouard@2886: edouard@2886: Edouard@3264: Edouard@3264: Edouard@3264: Edouard@3264: Edouard@3264: edouard@2886: edouard@2886: edouard@2886: edouard@3229: edouard@3229: : edouard@3229: edouard@3229: edouard@3229: edouard@3229: @ edouard@3229: edouard@3229: Edouard@3240: , edouard@3229: Edouard@3240: , edouard@3229: edouard@3229: edouard@3229: edouard@3229: edouard@3229: HMI: edouard@3229: edouard@3229: edouard@3229: edouard@3229: edouard@2886: edouard@3128: edouard@3128: edouard@3128: edouard@3128: Edouard@3019: edouard@2886: edouard@2886: edouard@2886: edouard@2886: edouard@2886: Edouard@2885: edouard@2894: edouard@2894: edouard@2894: edouard@2894: edouard@2894: Edouard@2901: Edouard@2901: Edouard@2901: Edouard@2901: Edouard@2901: Edouard@2901: Edouard@2901: Edouard@3019: Edouard@2877: Edouard@2877: Edouard@2877: Edouard@2877: Edouard@2877: Edouard@2877: Edouard@2877: Edouard@2877: =" Edouard@2877: Edouard@2877: " Edouard@2877: Edouard@3118: Edouard@2877: Edouard@3019: Edouard@2877: Edouard@2877: Edouard@2877: Edouard@2877: Edouard@2877: edouard@2940: edouard@2940: edouard@2949: edouard@2949: edouard@2949: /* edouard@2949: edouard@2949: */ edouard@2949: edouard@2949: edouard@2949: Edouard@2877: Raw HMI tree Edouard@2877: Edouard@3019: Edouard@2877: Edouard@2877: Edouard@2877: Indexed HMI tree Edouard@2877: Edouard@3019: edouard@2886: edouard@2886: edouard@2886: Parsed Widgets edouard@2886: edouard@2886: Edouard@3019: edouard@2949: edouard@2949: Edouard@2877: Edouard@3199: Edouard@3199: Edouard@3199: edouard@2939: edouard@2940: edouard@2949: edouard@2949: edouard@2949: /* edouard@2949: edouard@2949: */ edouard@2949: edouard@2949: edouard@2949: Edouard@2877: ID, x, y, w, h Edouard@2877: Edouard@2879: Edouard@2877: Edouard@2877: Edouard@2877: Edouard@2877: Edouard@2877: Edouard@2877: Edouard@2877: Edouard@2877: Edouard@2877: Edouard@2877: Edouard@2877: Edouard@2877: Edouard@2877: edouard@2949: edouard@2949: Edouard@2877: Edouard@2877: Edouard@2877: Edouard@2877: Edouard@2877: Edouard@2877: Edouard@2877: Edouard@2877: Edouard@2877: Edouard@2877: Edouard@2877: Edouard@2877: Edouard@2877: Edouard@2877: Edouard@2877: Edouard@2877: Edouard@2877: Edouard@2877: Edouard@2877: Edouard@2877: Edouard@2877: Edouard@2877: Edouard@2877: Edouard@2877: Edouard@2877: Edouard@2877: Edouard@2877: Edouard@2877: Edouard@2877: Edouard@2877: Edouard@2877: Edouard@2877: Edouard@2877: Edouard@2877: Edouard@2877: Edouard@2877: Edouard@2877: Edouard@2877: Edouard@2877: Edouard@2877: edouard@3165: Edouard@2877: Edouard@2877: Edouard@2877: Edouard@2877: Edouard@2877: Edouard@2877: edouard@3355: edouard@3355: edouard@3355: edouard@3355: edouard@3355: edouard@3355: edouard@3355: edouard@3355: edouard@3355: edouard@3355: edouard@3355: edouard@3355: edouard@3355: edouard@3355: edouard@3355: edouard@3355: edouard@3355: edouard@3355: edouard@3355: edouard@3355: edouard@3355: edouard@3355: edouard@3355: Edouard@2887: Edouard@2887: Edouard@2877: Edouard@2877: Edouard@2877: Edouard@2877: Edouard@2887: Edouard@2877: Home Edouard@2877: Edouard@2877: Edouard@3118: Edouard@3118: No Home page defined! Edouard@3118: Edouard@2877: Edouard@2877: Edouard@2877: Edouard@2877: Edouard@3118: Edouard@3118: No page defined! Edouard@3118: Edouard@2877: Edouard@2877: edouard@2886: Edouard@2877: Edouard@2877: Edouard@2877: edouard@2941: edouard@2941: edouard@2941: edouard@2941: edouard@2949: /* edouard@2949: edouard@2949: */ edouard@2949: edouard@2949: edouard@2949: edouard@2949: edouard@2949: edouard@2941: var default_page = " edouard@2941: edouard@2941: "; edouard@2941: edouard@2949: edouard@2949: edouard@2941: edouard@2941: edouard@2941: Edouard@2877: Edouard@2877: Edouard@2877: Edouard@2877: Edouard@2877: Edouard@2877: Edouard@2877: Edouard@2877: Edouard@2877: Edouard@2877: Edouard@2877: Edouard@2877: Edouard@2877: Edouard@2877: edouard@3165: edouard@3165: Edouard@3170: Edouard@3170: edouard@3165: edouard@3165: edouard@3165: edouard@3165: edouard@3165: edouard@3165: Edouard@3170: edouard@3165: edouard@3165: edouard@3165: Edouard@2877: Edouard@2877: edouard@3165: Edouard@2877: Edouard@2877: Edouard@2877: Edouard@2877: Edouard@2877: Edouard@2877: Edouard@2877: Edouard@2877: Edouard@2877: Edouard@2877: Edouard@2877: Edouard@2877: Edouard@2877: Edouard@2877: Edouard@2877: Edouard@3186: Edouard@3387: Edouard@3199: Edouard@2877: Edouard@2877: Edouard@2877: Edouard@2877: edouard@3161: Edouard@2877: edouard@3161: Edouard@2877: Edouard@2877: Edouard@2877: Edouard@2877: Edouard@2877: Edouard@2877: Edouard@2877: Edouard@2877: Edouard@2877: Edouard@2877: Edouard@2877: Edouard@2877: Edouard@2913: Edouard@2877: edouard@2943: edouard@2943: edouard@2941: edouard@2941: edouard@2949: /* edouard@2949: edouard@2949: */ edouard@2949: edouard@2949: edouard@2949: edouard@2949: edouard@2949: edouard@2941: var detachable_elements = { edouard@2941: edouard@2941: edouard@2941: " edouard@2941: edouard@2941: ":[id(" edouard@2941: edouard@2941: "), id(" edouard@2941: edouard@2941: ")] edouard@2941: edouard@2941: , edouard@2941: edouard@2941: edouard@2941: edouard@2941: edouard@2941: } edouard@2941: edouard@2949: edouard@2949: edouard@2941: Edouard@2888: edouard@3124: Edouard@2888: Edouard@3019: Edouard@3118: Edouard@3118: Edouard@3118: HMI:Page Edouard@3118: Edouard@3118: is nested in another HMI:Page Edouard@3118: Edouard@3118: edouard@2886: Edouard@3170: Edouard@3170: Edouard@3170: Edouard@2877: Edouard@2877: Edouard@2877: edouard@3124: Edouard@2901: Edouard@2901: edouard@3165: edouard@3165: Edouard@2877: " Edouard@3170: Edouard@2877: ": { Edouard@2877: Edouard@2877: bbox: [ Edouard@2877: Edouard@2877: , Edouard@2877: Edouard@2877: , Edouard@2877: Edouard@2877: , Edouard@2877: Edouard@2877: ], Edouard@2877: Edouard@2877: Edouard@2877: Edouard@2877: Edouard@2877: Page id=" Edouard@2877: Edouard@2877: " : No match for path " Edouard@2877: Edouard@2877: " in HMI tree Edouard@2877: Edouard@2877: Edouard@2877: page_index: Edouard@2877: Edouard@2877: , Edouard@2877: Edouard@3383: page_class: " Edouard@3383: Edouard@3383: ", Edouard@3383: Edouard@2877: edouard@3005: widgets: [ edouard@3005: edouard@3005: edouard@3005: edouard@3005: edouard@3005: edouard@3005: edouard@3005: , edouard@3005: edouard@3005: edouard@3005: edouard@3005: [hmi_widgets[" Edouard@2877: edouard@3005: "], [ edouard@3005: edouard@3005: ]] Edouard@2877: Edouard@2877: , Edouard@2877: Edouard@2877: Edouard@2877: Edouard@2877: Edouard@2877: ], Edouard@2877: Edouard@2903: jumps: [ Edouard@2903: Edouard@2903: Edouard@3112: hmi_widgets[" Edouard@3112: Edouard@3112: "] Edouard@3112: Edouard@3112: , Edouard@2903: Edouard@3112: Edouard@3112: Edouard@2903: Edouard@2903: ], Edouard@2903: Edouard@2877: required_detachables: { Edouard@2877: Edouard@2877: Edouard@2877: " Edouard@2877: Edouard@2877: ": detachable_elements[" Edouard@2877: Edouard@2877: "] Edouard@2877: Edouard@2877: , Edouard@2877: Edouard@2877: Edouard@2877: Edouard@2877: Edouard@2877: } Edouard@2877: edouard@3238: Edouard@2901: Edouard@2901: Edouard@2877: } Edouard@2877: Edouard@2877: , Edouard@2877: Edouard@2877: Edouard@2877: Edouard@3170: Edouard@2877: edouard@3008: edouard@3008: edouard@2941: edouard@2941: edouard@2949: /* edouard@2949: edouard@2949: */ edouard@2949: edouard@2949: edouard@2949: edouard@2949: edouard@2949: edouard@2941: var page_desc = { edouard@2941: Edouard@3019: edouard@2941: } edouard@2941: edouard@2949: edouard@2949: edouard@2941: edouard@3238: edouard@2939: edouard@2940: edouard@2941: edouard@2941: edouard@2949: /* edouard@2949: edouard@2949: */ edouard@2949: edouard@2949: edouard@2949: edouard@2949: edouard@2949: Edouard@2888: DETACHABLES: Edouard@2888: Edouard@2877: Edouard@2877: Edouard@2877: Edouard@2877: Edouard@2877: Edouard@2877: Edouard@2888: In Foreach: Edouard@2888: Edouard@2888: Edouard@2888: Edouard@2888: Edouard@2888: Edouard@2888: Edouard@2888: edouard@3165: Overlapping edouard@3165: edouard@3165: edouard@3165: edouard@3165: Edouard@2877: Edouard@3031: Edouard@3031: Edouard@3031: Edouard@3031: Edouard@2877: Edouard@2877: Edouard@3019: Edouard@2877: Edouard@2877: Edouard@2877: Edouard@3019: Edouard@3019: Edouard@3019: Edouard@2877: Edouard@2877: Edouard@2877: none Edouard@2877: Edouard@2877: Edouard@2877: 100vh Edouard@2877: Edouard@2877: Edouard@2877: 100vw Edouard@2877: Edouard@3019: Edouard@2877: Edouard@2877: Edouard@3019: Edouard@2877: Edouard@2877: ViewBox settings other than X=0, Y=0 and Scale=1 are not supported Edouard@2877: Edouard@2877: Edouard@3019: Edouard@2877: Edouard@2877: All units must be set to "px" in Inkscape's document properties Edouard@2877: Edouard@2877: Edouard@3112: Edouard@3112: Edouard@3112: Edouard@3112: Edouard@3112: Edouard@3031: Edouard@3387: Edouard@3031: Edouard@3031: Edouard@3031: Edouard@3031: Edouard@3031: Edouard@3019: edouard@2996: Edouard@2877: Edouard@3031: edouard@2996: edouard@2996: edouard@2996: Edouard@2877: Edouard@2877: Edouard@2877: Edouard@3031: Edouard@2877: Edouard@2877: Edouard@2877: Edouard@2877: Edouard@2877: Edouard@2877: Edouard@2877: href Edouard@2877: Edouard@2877: Edouard@2877: width Edouard@2877: Edouard@2877: Edouard@2877: height Edouard@2877: Edouard@2877: Edouard@2877: x Edouard@2877: Edouard@2877: Edouard@2877: y Edouard@2877: usveticic@3045: usveticic@3045: id usveticic@3045: Edouard@2877: Edouard@2877: Edouard@2969: Edouard@2969: Edouard@2969: transform Edouard@2969: Edouard@2969: Edouard@2969: style Edouard@2969: Edouard@2969: Edouard@2969: Edouard@2877: edouard@2996: Edouard@3031: Edouard@2969: Edouard@3031: Edouard@3031: Edouard@3031: Edouard@3031: Edouard@3031: _ Edouard@3031: Edouard@3031: Edouard@3031: Edouard@3031: Edouard@3031: Edouard@3031: Edouard@3031: Edouard@2877: usveticic@3045: usveticic@3045: usveticic@3045: Edouard@3031: Edouard@3031: Edouard@3031: Edouard@2969: Edouard@2969: Edouard@2969: Edouard@2969: Edouard@2969: Edouard@2969: Edouard@2969: Edouard@2969: Edouard@2969: Edouard@2969: Edouard@2969: Edouard@2969: ; Edouard@2969: Edouard@2969: Edouard@2969: Edouard@2969: Edouard@2969: Edouard@2969: Edouard@2969: Edouard@2969: Edouard@2969: Edouard@2969: Edouard@2969: Edouard@2969: Edouard@2969: Edouard@3019: Edouard@3031: Edouard@2969: Edouard@2969: Edouard@2969: Edouard@2969: Edouard@2969: Edouard@2969: Edouard@2969: Edouard@2969: Edouard@3019: Edouard@3031: Edouard@2969: Edouard@2969: Edouard@2969: Edouard@2877: Edouard@2877: Edouard@3019: Edouard@2877: Edouard@2877: Edouard@2877: Edouard@2877: _ Edouard@2877: Edouard@2877: Edouard@3031: Edouard@3031: Edouard@3031: Edouard@2877: Edouard@3019: Edouard@2877: Edouard@2877: usveticic@3045: usveticic@3045: Edouard@3031: Edouard@3031: Edouard@3031: Edouard@3031: Edouard@3031: Edouard@3031: Edouard@3031: Edouard@3031: Edouard@3031: Edouard@3031: Edouard@3031: Edouard@3031: Edouard@3031: Edouard@3031: Edouard@3031: Edouard@3031: usveticic@3045: Edouard@3019: Edouard@2877: Edouard@2877: Edouard@3410: Edouard@2877: Edouard@2877: Edouard@2877: Edouard@2877: Edouard@2877: Edouard@2877: Edouard@2877: Edouard@2877: Edouard@3019: Edouard@2877: Edouard@2877: Edouard@2877: Edouard@2877: Edouard@2877: Edouard@2877: Edouard@2877: Edouard@3019: Edouard@2877: Edouard@2877: edouard@2941: edouard@2941: edouard@2949: edouard@2949: edouard@2949: /* edouard@2949: edouard@2949: */ edouard@2949: edouard@2949: edouard@2949: edouard@2941: let id = document.getElementById.bind(document); edouard@2941: edouard@2941: var svg_root = id(" edouard@2941: edouard@2941: "); edouard@2941: edouard@2949: edouard@2949: edouard@2941: edouard@2941: edouard@2941: edouard@2941: edouard@2941: edouard@2949: /* edouard@2949: edouard@2949: */ edouard@2949: edouard@2949: edouard@2949: edouard@2949: edouard@2949: edouard@2904: Unlinked : edouard@2904: Edouard@2878: Edouard@2878: Edouard@2878: Edouard@2878: Edouard@2878: edouard@2996: Not to unlink : edouard@2996: edouard@2996: edouard@2996: edouard@2996: edouard@2996: edouard@2996: edouard@2949: edouard@2949: Edouard@2878: Edouard@3112: Edouard@3112: Edouard@3112: Edouard@3112: Edouard@3112: Edouard@3112: Edouard@3112: Edouard@3112: Edouard@3112: Edouard@3112: Edouard@3112: Edouard@3112: Edouard@3112: Edouard@3112: Edouard@3112: Edouard@3112: Edouard@3112: Edouard@3112: Edouard@3112: Edouard@3112: Edouard@3112: Edouard@3112: Edouard@3112: Edouard@3112: Edouard@3112: Edouard@3112: Edouard@3112: /* Edouard@3112: Edouard@3112: */ Edouard@3112: Edouard@3112: Edouard@3112: Edouard@3112: Edouard@3150: var langs = [ ["Default", "C"], Edouard@3116: Edouard@3150: [" Edouard@3116: Edouard@3150: "," Edouard@3150: Edouard@3150: "] Edouard@3116: Edouard@3116: , Edouard@3116: Edouard@3116: Edouard@3116: ]; Edouard@3116: Edouard@3116: var translations = [ Edouard@3116: Edouard@3116: Edouard@3116: edouard@3128: edouard@3128: edouard@3128: [[ edouard@3128: edouard@3128: id(" edouard@3128: edouard@3128: ") edouard@3128: edouard@3128: , edouard@3128: edouard@3128: edouard@3128: ],[ Edouard@3116: Edouard@3116: " Edouard@3116: Edouard@3116: Edouard@3116: Edouard@3116: \n Edouard@3116: Edouard@3116: Edouard@3116: " Edouard@3116: Edouard@3116: , Edouard@3116: Edouard@3116: Edouard@3116: ]] Edouard@3112: Edouard@3112: , Edouard@3112: Edouard@3112: Edouard@3112: Edouard@3112: Edouard@3116: ] Edouard@3112: Edouard@3112: Edouard@3112: Edouard@3112: Edouard@3019: edouard@2886: Edouard@2881: edouard@2954: edouard@2954: edouard@2954: " Edouard@3024: edouard@2954: " edouard@2954: edouard@2954: , edouard@2954: edouard@2954: edouard@2954: edouard@2954: edouard@2954: edouard@2954: edouard@2954: edouard@3017: edouard@3017: Edouard@3058: edouard@3017: Widget edouard@3017: edouard@3017: id=" edouard@3017: edouard@3017: " : No match for path " edouard@3017: edouard@3017: " in HMI tree edouard@3017: Edouard@3058: undefined edouard@3017: edouard@3017: edouard@3017: " Edouard@3025: edouard@3017: " edouard@3017: edouard@3017: edouard@3017: hmi_local_index(" edouard@3017: edouard@3017: ") edouard@3017: edouard@3103: edouard@3103: edouard@3103: Internal error while processing widget's non indexed HMI tree path : unknown type edouard@3103: edouard@3103: edouard@3017: edouard@2954: edouard@2954: edouard@2954: edouard@2954: edouard@2954: edouard@3103: edouard@3103: , edouard@3103: edouard@3103: edouard@3103: edouard@3103: edouard@3103: edouard@3103: edouard@3103: edouard@3103: [ edouard@3103: edouard@3103: , edouard@3103: edouard@3103: ] edouard@3103: edouard@3103: edouard@3103: undefined edouard@3103: edouard@3103: edouard@3103: edouard@3103: , edouard@3103: edouard@2954: edouard@2954: Edouard@3410: Edouard@3410: edouard@3414: edouard@3414: Edouard@3410: Edouard@3410: Edouard@3410: undefined Edouard@3410: Edouard@3410: Edouard@3410: Edouard@2881: " Edouard@2881: edouard@2949: ": new Edouard@2881: edouard@2954: Widget (" Edouard@2881: Edouard@3410: ", Edouard@3410: Edouard@3410: ,[ edouard@2954: edouard@2954: ],[ edouard@2954: edouard@3103: ],[ edouard@3103: edouard@2954: ],{ Edouard@2881: Edouard@3019: Edouard@2881: Edouard@2881: edouard@2949: }) Edouard@2881: Edouard@2881: , Edouard@2881: Edouard@2881: Edouard@2793: Edouard@2792: edouard@3017: edouard@3017: edouard@2949: edouard@2949: edouard@2949: /* edouard@2949: edouard@2949: */ edouard@2949: edouard@2949: edouard@2949: Edouard@3147: Edouard@3147: edouard@3017: let hmi_locals = {}; edouard@3017: edouard@3017: var last_remote_index = hmitree_types.length - 1; edouard@3017: edouard@3017: var next_available_index = hmitree_types.length; edouard@3017: edouard@3128: let cookies = new Map(document.cookie.split("; ").map(s=>s.split("="))); edouard@3128: edouard@3017: edouard@3017: Edouard@3024: const local_defaults = { Edouard@3024: edouard@3128: Edouard@3024: Edouard@3024: Edouard@3024: VarInit Edouard@3024: Edouard@3024: must have only one variable given. Edouard@3024: Edouard@3024: Edouard@3024: Edouard@3024: Edouard@3024: VarInit Edouard@3024: Edouard@3024: only applies to HMI variable. Edouard@3024: Edouard@3024: edouard@3128: " Edouard@3024: Edouard@3025: ": edouard@3128: edouard@3128: edouard@3128: cookies.has(" edouard@3128: edouard@3128: ")?cookies.get(" edouard@3128: edouard@3128: "): edouard@3128: edouard@3128: edouard@3128: edouard@3128: edouard@3128: edouard@3128: edouard@3128: edouard@3128: edouard@3128: edouard@3128: , edouard@3128: edouard@3128: edouard@3128: }; edouard@3128: edouard@3128: edouard@3128: edouard@3128: const persistent_locals = new Set([ edouard@3128: edouard@3128: edouard@3128: " edouard@3128: edouard@3128: " Edouard@3024: Edouard@3024: , Edouard@3024: Edouard@3024: Edouard@3024: Edouard@3024: edouard@3128: ]); edouard@3128: edouard@3128: var persistent_indexes = new Map(); Edouard@3024: Edouard@3022: var cache = hmitree_types.map(_ignored => undefined); Edouard@3022: edouard@3154: var updates = new Map(); Edouard@3147: Edouard@3022: Edouard@3022: edouard@3017: function page_local_index(varname, pagename){ edouard@3017: edouard@3017: let pagevars = hmi_locals[pagename]; edouard@3017: Edouard@3022: let new_index; Edouard@3022: edouard@3017: if(pagevars == undefined){ edouard@3017: Edouard@3022: new_index = next_available_index++; Edouard@3022: Edouard@3022: hmi_locals[pagename] = {[varname]:new_index} Edouard@3022: edouard@3017: } else { edouard@3017: edouard@3017: let result = pagevars[varname]; edouard@3017: Edouard@3022: if(result != undefined) { Edouard@3022: Edouard@3022: return result; edouard@3017: edouard@3017: } edouard@3017: Edouard@3022: Edouard@3022: Edouard@3022: new_index = next_available_index++; Edouard@3022: Edouard@3022: pagevars[varname] = new_index; Edouard@3022: edouard@3017: } edouard@3017: Edouard@3025: let defaultval = local_defaults[varname]; Edouard@3025: edouard@3128: if(defaultval != undefined) { Edouard@3025: Edouard@3025: cache[new_index] = defaultval; Edouard@3025: edouard@3154: updates.set(new_index, defaultval); edouard@3141: edouard@3128: if(persistent_locals.has(varname)) edouard@3128: edouard@3128: persistent_indexes.set(new_index, varname); edouard@3128: edouard@3128: } edouard@3128: Edouard@3022: return new_index; Edouard@3022: edouard@3017: } edouard@3017: edouard@3017: edouard@3017: edouard@3017: function hmi_local_index(varname){ edouard@3017: edouard@3017: return page_local_index(varname, "HMI_LOCAL"); edouard@3017: edouard@3017: } edouard@3017: edouard@3017: edouard@3017: edouard@3017: edouard@3017: edouard@3017: edouard@3017: edouard@3017: edouard@3017: /* edouard@3017: edouard@3017: */ edouard@3017: edouard@3017: edouard@3017: Edouard@3019: var pending_widget_animates = []; Edouard@3019: Edouard@3019: Edouard@3019: edouard@2949: class Widget { edouard@2949: edouard@2962: offset = 0; edouard@2962: edouard@2962: frequency = 10; /* FIXME arbitrary default max freq. Obtain from config ? */ edouard@2962: Edouard@2980: unsubscribable = false; Edouard@2980: Edouard@3019: pending_animate = false; Edouard@3019: Edouard@3019: Edouard@3019: Edouard@3410: constructor(elt_id, freq, args, indexes, minmaxes, members){ edouard@2954: edouard@2961: this.element_id = elt_id; edouard@2961: edouard@2954: this.element = id(elt_id); edouard@2954: Edouard@3410: if(freq !== undefined) this.frequency = freq; Edouard@3410: edouard@2954: this.args = args; edouard@2954: edouard@2954: this.indexes = indexes; edouard@2954: edouard@3103: this.minmaxes = minmaxes; edouard@3103: edouard@2949: Object.keys(members).forEach(prop => this[prop]=members[prop]); edouard@2949: edouard@3141: this.lastapply = indexes.map(() => undefined); edouard@3141: edouard@3141: this.inhibit = indexes.map(() => undefined); edouard@3141: edouard@3141: this.pending = indexes.map(() => undefined); edouard@3141: edouard@3141: this.bound_unhinibit = this.unhinibit.bind(this); edouard@3141: edouard@2949: } edouard@2949: edouard@2961: edouard@2961: edouard@2954: unsub(){ edouard@2954: edouard@2954: /* remove subsribers */ edouard@2954: edouard@3005: if(!this.unsubscribable) edouard@3005: edouard@3005: for(let i = 0; i < this.indexes.length; i++) { edouard@3005: edouard@3141: /* flush updates pending because of inhibition */ edouard@3141: Edouard@3147: let inhibition = this.inhibit[i]; edouard@3141: edouard@3141: if(inhibition != undefined){ edouard@3141: edouard@3141: clearTimeout(inhibition); edouard@3141: Edouard@3147: this.lastapply[i] = undefined; Edouard@3147: Edouard@3147: this.unhinibit(i); edouard@3141: edouard@3141: } edouard@3141: edouard@3005: let index = this.indexes[i]; edouard@3005: edouard@3005: if(this.relativeness[i]) edouard@3005: edouard@3005: index += this.offset; edouard@3005: Edouard@3022: subscribers(index).delete(this); edouard@3005: edouard@3005: } edouard@3005: edouard@3005: this.offset = 0; edouard@3005: edouard@3005: this.relativeness = undefined; edouard@3005: edouard@3005: } edouard@3005: edouard@3005: edouard@3005: edouard@3017: sub(new_offset=0, relativeness, container_id){ edouard@3005: edouard@3005: this.offset = new_offset; edouard@3005: edouard@3005: this.relativeness = relativeness; edouard@3005: edouard@3017: this.container_id = container_id ; edouard@3017: edouard@3005: /* add this's subsribers */ edouard@3005: edouard@3005: if(!this.unsubscribable) edouard@3005: edouard@3005: for(let i = 0; i < this.indexes.length; i++) { edouard@3005: edouard@3017: let index = this.get_variable_index(i); edouard@3017: Edouard@3058: if(index == undefined) continue; Edouard@3058: Edouard@3022: subscribers(index).add(this); edouard@3005: edouard@3005: } edouard@3005: edouard@3005: need_cache_apply.push(this); edouard@3005: edouard@3005: } edouard@3005: edouard@3005: edouard@3005: edouard@3005: apply_cache() { edouard@3005: Edouard@3026: if(!this.unsubscribable) for(let index in this.indexes){ Edouard@3026: Edouard@3026: /* dispatch current cache in newly opened page widgets */ Edouard@3026: Edouard@3026: let realindex = this.get_variable_index(index); Edouard@3026: Edouard@3058: if(realindex == undefined) continue; Edouard@3058: Edouard@3026: let cached_val = cache[realindex]; Edouard@3026: Edouard@3026: if(cached_val != undefined) Edouard@3026: Edouard@3026: this._dispatch(cached_val, cached_val, index); Edouard@3026: Edouard@3026: } Edouard@3026: Edouard@3026: } Edouard@3026: Edouard@3026: Edouard@3026: Edouard@3026: get_variable_index(varnum) { Edouard@3026: Edouard@3026: let index = this.indexes[varnum]; Edouard@3026: Edouard@3026: if(typeof(index) == "string"){ Edouard@3026: Edouard@3026: index = page_local_index(index, this.container_id); Edouard@3026: Edouard@3026: } else { Edouard@3026: Edouard@3026: if(this.relativeness[varnum]){ Edouard@3026: Edouard@3026: index += this.offset; Edouard@3026: Edouard@3026: } Edouard@3026: Edouard@3026: } Edouard@3026: Edouard@3026: return index; Edouard@3026: Edouard@3026: } Edouard@3026: edouard@3097: edouard@3097: edouard@3103: overshot(new_val, max) { edouard@3103: edouard@3103: } edouard@3103: edouard@3103: edouard@3103: edouard@3103: undershot(new_val, min) { edouard@3103: edouard@3103: } edouard@3103: edouard@3103: edouard@3103: edouard@3103: clip_min_max(index, new_val) { edouard@3103: edouard@3103: let minmax = this.minmaxes[index]; edouard@3103: edouard@3103: if(minmax !== undefined && typeof new_val == "number") { edouard@3103: edouard@3103: let [min,max] = minmax; edouard@3103: edouard@3103: if(new_val < min){ edouard@3103: edouard@3103: this.undershot(new_val, min); edouard@3103: edouard@3103: return min; edouard@3103: edouard@3103: } edouard@3103: edouard@3103: if(new_val > max){ edouard@3103: edouard@3103: this.overshot(new_val, max); edouard@3103: edouard@3103: return max; edouard@3103: edouard@3103: } edouard@3103: edouard@3103: } edouard@3103: edouard@3103: return new_val; edouard@3103: edouard@3103: } edouard@3103: edouard@3103: edouard@3103: Edouard@3058: change_hmi_value(index, opstr) { Edouard@3058: Edouard@3058: let realindex = this.get_variable_index(index); Edouard@3058: Edouard@3058: if(realindex == undefined) return undefined; Edouard@3058: edouard@3097: let old_val = cache[realindex]; edouard@3097: edouard@3097: let new_val = eval_operation_string(old_val, opstr); edouard@3097: edouard@3103: new_val = this.clip_min_max(index, new_val); edouard@3103: edouard@3097: return apply_hmi_value(realindex, new_val); Edouard@3026: Edouard@3026: } Edouard@3026: Edouard@3026: Edouard@3026: edouard@3141: _apply_hmi_value(index, new_val) { edouard@3141: edouard@3141: let realindex = this.get_variable_index(index); edouard@3141: edouard@3141: if(realindex == undefined) return undefined; edouard@3141: edouard@3141: new_val = this.clip_min_max(index, new_val); edouard@3141: edouard@3141: return apply_hmi_value(realindex, new_val); edouard@3141: edouard@3141: } edouard@3141: edouard@3141: edouard@3141: edouard@3141: unhinibit(index){ edouard@3141: edouard@3141: this.inhibit[index] = undefined; edouard@3141: edouard@3141: let new_val = this.pending[index]; edouard@3141: edouard@3141: this.pending[index] = undefined; edouard@3141: edouard@3141: return this.apply_hmi_value(index, new_val); edouard@3141: edouard@3141: } edouard@3141: edouard@3141: edouard@3141: Edouard@3026: apply_hmi_value(index, new_val) { Edouard@3026: edouard@3141: if(this.inhibit[index] == undefined){ edouard@3141: edouard@3141: let now = Date.now(); edouard@3141: edouard@3141: let min_interval = 1000/this.frequency; edouard@3141: edouard@3141: let lastapply = this.lastapply[index]; edouard@3141: edouard@3141: if(lastapply == undefined || now > lastapply + min_interval){ edouard@3141: edouard@3141: this.lastapply[index] = now; edouard@3141: edouard@3141: return this._apply_hmi_value(index, new_val); edouard@3141: edouard@3141: } edouard@3141: edouard@3141: else { edouard@3141: edouard@3141: let elapsed = now - lastapply; edouard@3141: edouard@3141: this.pending[index] = new_val; edouard@3141: edouard@3141: this.inhibit[index] = setTimeout(this.bound_unhinibit, min_interval - elapsed, index); edouard@3141: edouard@3141: } edouard@3141: edouard@3141: } edouard@3141: edouard@3141: else { edouard@3141: edouard@3141: this.pending[index] = new_val; edouard@3141: edouard@3141: return new_val; edouard@3141: edouard@3141: } Edouard@3026: Edouard@3026: } Edouard@3026: Edouard@3026: Edouard@3026: Edouard@3026: new_hmi_value(index, value, oldval) { Edouard@3026: Edouard@3026: // TODO avoid searching, store index at sub() Edouard@3026: Edouard@3026: for(let i = 0; i < this.indexes.length; i++) { Edouard@3026: Edouard@3026: let refindex = this.get_variable_index(i); Edouard@3026: Edouard@3058: if(refindex == undefined) continue; Edouard@3058: Edouard@3026: Edouard@3026: Edouard@3026: if(index == refindex) { Edouard@3026: Edouard@3026: this._dispatch(value, oldval, i); Edouard@3026: Edouard@3026: break; Edouard@3026: Edouard@3026: } Edouard@3026: Edouard@3026: } Edouard@3026: Edouard@3026: } Edouard@3026: Edouard@3026: Edouard@3026: Edouard@3026: _dispatch(value, oldval, varnum) { Edouard@3026: Edouard@3025: let dispatch = this.dispatch; Edouard@3025: Edouard@3026: if(dispatch != undefined){ Edouard@3026: Edouard@3026: try { Edouard@3026: Edouard@3026: dispatch.call(this, value, oldval, varnum); Edouard@3026: Edouard@3026: } catch(err) { Edouard@3026: Edouard@3026: console.log(err); Edouard@3026: Edouard@3026: } edouard@2954: edouard@2954: } edouard@2954: edouard@2954: } edouard@2954: edouard@2954: edouard@2954: Edouard@3019: _animate(){ Edouard@3019: Edouard@3019: this.animate(); Edouard@3019: Edouard@3019: this.pending_animate = false; Edouard@3019: Edouard@3019: } Edouard@3019: Edouard@3019: Edouard@3019: Edouard@3019: request_animate(){ Edouard@3019: Edouard@3019: if(!this.pending_animate){ Edouard@3019: Edouard@3019: pending_widget_animates.push(this); Edouard@3019: Edouard@3019: this.pending_animate = true; Edouard@3019: Edouard@3019: requestHMIAnimation(); Edouard@3019: Edouard@3019: } Edouard@3019: Edouard@3019: Edouard@3019: Edouard@3019: } Edouard@3019: edouard@3128: edouard@3128: edouard@3128: activate_activable(eltsub) { edouard@3128: edouard@3128: eltsub.inactive.style.display = "none"; edouard@3128: edouard@3128: eltsub.active.style.display = ""; edouard@3128: edouard@3128: } edouard@3128: edouard@3128: edouard@3128: edouard@3128: inactivate_activable(eltsub) { edouard@3128: edouard@3128: eltsub.active.style.display = "none"; edouard@3128: edouard@3128: eltsub.inactive.style.display = ""; edouard@3128: edouard@3128: } edouard@3128: edouard@2949: } edouard@2949: edouard@2949: edouard@2949: edouard@2949: edouard@3128: Edouard@3264: edouard@3008: edouard@3008: edouard@2949: edouard@2949: edouard@2949: /* edouard@2949: edouard@2949: */ edouard@2949: edouard@2949: edouard@2949: edouard@3124: Edouard@3019: Edouard@3019: Edouard@3019: Edouard@3019: Edouard@3019: edouard@2949: class edouard@2949: edouard@2949: Widget extends Widget{ edouard@2949: edouard@2949: /* empty class, as edouard@2949: edouard@2949: widget didn't provide any */ edouard@2949: edouard@2949: } edouard@2949: edouard@2949: Edouard@3118: edouard@3124: edouard@3165: edouard@3008: edouard@3008: edouard@2949: edouard@2949: edouard@2949: /* edouard@2949: edouard@2949: */ edouard@2949: edouard@2949: edouard@2949: edouard@2941: var hmi_widgets = { edouard@2941: edouard@3124: edouard@2941: } edouard@2941: edouard@2949: edouard@2949: edouard@2941: Edouard@2808: Edouard@2808: Edouard@2808: edouard@2920: Edouard@2800: Edouard@2834: Edouard@2808: Edouard@2807: edouard@3165: Edouard@2836: edouard@2920: Edouard@2836: edouard@2920: Edouard@2836: Edouard@2836: widget must have a Edouard@2836: Edouard@2836: element Edouard@2836: Edouard@2836: Edouard@2836: Edouard@2836: Edouard@2852: Edouard@2807: edouard@2847: _elt: id(" edouard@2920: Edouard@2836: "), Edouard@2836: edouard@2920: edouard@2920: edouard@2920: edouard@2920: _sub: { edouard@2920: edouard@2920: edouard@2920: edouard@2920: edouard@2920: edouard@2920: edouard@2920: edouard@2920: edouard@2920: edouard@2920: widget must have a edouard@2920: edouard@2920: / edouard@2920: edouard@2920: element edouard@2920: edouard@2920: edouard@2920: /* missing edouard@2920: edouard@2920: / edouard@2920: edouard@2920: element */ edouard@2920: edouard@2920: edouard@2920: edouard@2920: " edouard@2920: edouard@2920: ": id(" edouard@2920: edouard@2920: ") edouard@2920: edouard@2920: , edouard@2920: edouard@2920: edouard@2920: edouard@2920: edouard@2920: edouard@2920: edouard@2920: }, edouard@2920: edouard@2920: Edouard@2836: Edouard@2836: Edouard@2807: Edouard@2808: Edouard@2829: Edouard@2829: Edouard@2829: Edouard@3024: Edouard@3024: Edouard@2829: Edouard@2829: Edouard@2829: Edouard@2829: Edouard@2829: Edouard@2829: edouard@3238: edouard@3238: class edouard@3238: AnimateWidget edouard@3238: extends Widget{ Edouard@3065: Edouard@3065: frequency = 5; Edouard@3065: Edouard@3065: speed = 0; Edouard@3065: Edouard@3065: start = false; Edouard@3065: Edouard@3065: widget_center = undefined; Edouard@3065: Edouard@3065: Edouard@3065: Edouard@3065: dispatch(value) { Edouard@3065: Edouard@3065: this.speed = value / 5; Edouard@3065: Edouard@3065: Edouard@3065: Edouard@3065: //reconfigure animation Edouard@3065: Edouard@3065: this.request_animate(); Edouard@3065: Edouard@3065: } Edouard@3065: Edouard@3065: Edouard@3065: Edouard@3065: animate(){ Edouard@3065: Edouard@3065: // change animation properties Edouard@3065: Edouard@3065: for(let child of this.element.children){ Edouard@3065: Edouard@3065: if(child.nodeName.startsWith("animate")){ Edouard@3065: Edouard@3065: if(this.speed != 0 && !this.start){ Edouard@3065: Edouard@3065: this.start = true; Edouard@3065: Edouard@3065: this.element.beginElement(); Edouard@3065: Edouard@3065: } Edouard@3065: Edouard@3065: Edouard@3065: Edouard@3065: if(this.speed > 0){ Edouard@3065: Edouard@3065: child.setAttribute("dur", this.speed+"s"); Edouard@3065: Edouard@3065: } Edouard@3065: Edouard@3065: else if(this.speed < 0){ Edouard@3065: Edouard@3065: child.setAttribute("dur", (-1)*this.speed+"s"); Edouard@3065: Edouard@3065: } Edouard@3065: Edouard@3065: else{ Edouard@3065: Edouard@3065: this.start = false; Edouard@3065: Edouard@3065: this.element.endElement(); Edouard@3065: Edouard@3065: } Edouard@3065: Edouard@3065: } Edouard@3065: Edouard@3065: } Edouard@3065: Edouard@3065: } Edouard@3065: Edouard@3065: Edouard@3065: Edouard@3065: init() { Edouard@3065: Edouard@3065: let widget_pos = this.element.getBBox(); Edouard@3065: Edouard@3065: this.widget_center = [(widget_pos.x+widget_pos.width/2), (widget_pos.y+widget_pos.height/2)]; Edouard@3065: Edouard@3065: } Edouard@3065: Edouard@3065: } Edouard@3065: Edouard@3065: edouard@3241: edouard@3241: edouard@3241: edouard@3241: edouard@3241: edouard@3241: AnimateRotation - DEPRECATED, do not use. edouard@3241: edouard@3241: Doesn't follow WYSIWYG principle, and forces user to add animateTransform tag in SVG (using inkscape XML editor for exemple) edouard@3241: edouard@3241: edouard@3241: edouard@3241: AnimateRotation - DEPRECATED edouard@3241: edouard@3241: edouard@3241: speed edouard@3241: edouard@3241: edouard@3238: edouard@3238: class edouard@3238: AnimateRotationWidget edouard@3238: extends Widget{ Edouard@3065: Edouard@3065: frequency = 5; Edouard@3065: Edouard@3065: speed = 0; Edouard@3065: Edouard@3065: widget_center = undefined; Edouard@3065: Edouard@3065: Edouard@3065: Edouard@3065: dispatch(value) { Edouard@3065: Edouard@3065: this.speed = value / 5; Edouard@3065: Edouard@3065: Edouard@3065: Edouard@3065: //reconfigure animation Edouard@3065: Edouard@3065: this.request_animate(); Edouard@3065: Edouard@3065: } Edouard@3065: Edouard@3065: Edouard@3065: Edouard@3065: animate(){ Edouard@3065: Edouard@3065: // change animation properties Edouard@3065: edouard@3241: // TODO : rewrite with proper es6 edouard@3241: Edouard@3065: for(let child of this.element.children){ Edouard@3065: Edouard@3065: if(child.nodeName == "animateTransform"){ Edouard@3065: Edouard@3065: if(this.speed > 0){ Edouard@3065: Edouard@3065: child.setAttribute("dur", this.speed+"s"); Edouard@3065: Edouard@3065: child.setAttribute("from", "0 "+this.widget_center[0]+" "+this.widget_center[1]); Edouard@3065: Edouard@3065: child.setAttribute("to", "360 "+this.widget_center[0]+" "+this.widget_center[1]); Edouard@3065: Edouard@3065: } Edouard@3065: Edouard@3065: else if(this.speed < 0){ Edouard@3065: Edouard@3065: child.setAttribute("dur", (-1)*this.speed+"s"); Edouard@3065: Edouard@3065: child.setAttribute("from", "360 "+this.widget_center[0]+" "+this.widget_center[1]); Edouard@3065: Edouard@3065: child.setAttribute("to", "0 "+this.widget_center[0]+" "+this.widget_center[1]); Edouard@3065: Edouard@3065: } Edouard@3065: Edouard@3065: else{ Edouard@3065: Edouard@3065: child.setAttribute("from", "0 "+this.widget_center[0]+" "+this.widget_center[1]); Edouard@3065: Edouard@3065: child.setAttribute("to", "0 "+this.widget_center[0]+" "+this.widget_center[1]); Edouard@3065: Edouard@3065: } Edouard@3065: Edouard@3065: } Edouard@3065: Edouard@3065: } Edouard@3065: Edouard@3065: } Edouard@3065: Edouard@3065: Edouard@3065: Edouard@3065: init() { Edouard@3065: Edouard@3065: let widget_pos = this.element.getBBox(); Edouard@3065: Edouard@3065: this.widget_center = [(widget_pos.x+widget_pos.width/2), (widget_pos.y+widget_pos.height/2)]; Edouard@3065: Edouard@3065: } Edouard@3065: Edouard@3065: } Edouard@3065: Edouard@3065: edouard@3241: edouard@3241: edouard@3241: edouard@3241: edouard@3241: edouard@3241: Back widget brings focus back to previous page in history when clicked. edouard@3241: edouard@3241: edouard@3241: edouard@3241: Jump to previous page edouard@3241: Edouard@3065: edouard@3238: edouard@3238: class edouard@3238: BackWidget edouard@3238: extends Widget{ edouard@2961: edouard@2961: on_click(evt) { Edouard@2902: Edouard@2902: if(jump_history.length > 1){ Edouard@2902: Edouard@2902: jump_history.pop(); Edouard@2902: Edouard@2902: let [page_name, index] = jump_history.pop(); Edouard@2902: Edouard@2902: switch_page(page_name, index); Edouard@2902: Edouard@2902: } Edouard@2902: edouard@2961: } edouard@2961: edouard@2961: init() { edouard@2961: edouard@2961: this.element.setAttribute("onclick", "hmi_widgets['"+this.element_id+"'].on_click(evt)"); edouard@2961: edouard@2961: } edouard@2961: edouard@2961: } edouard@2961: edouard@2961: edouard@3241: edouard@3241: edouard@3241: edouard@3241: edouard@3241: edouard@3241: Button widget takes one boolean variable path, and reflect current true edouard@3241: edouard@3241: or false value by showing "active" or "inactive" labeled element edouard@3241: edouard@3241: respectively. Pressing and releasing button changes variable to true and edouard@3241: edouard@3241: false respectively. Potential inconsistency caused by quick consecutive edouard@3241: edouard@3241: presses on the button is mitigated by using a state machine that wait for edouard@3241: edouard@3241: previous state change to be reflected on variable before applying next one. edouard@3241: edouard@3241: edouard@3241: edouard@3241: Push button reflecting consistently given boolean variable edouard@3241: edouard@3241: edouard@3241: Boolean variable edouard@3241: edouard@3241: edouard@3414: edouard@3414: edouard@3414: edouard@3414: edouard@3414: edouard@3414: edouard@3414: edouard@3414: edouard@3414: edouard@3414: edouard@3414: edouard@3414: edouard@3414: edouard@3414: edouard@3414: edouard@3414: edouard@3414: edouard@3414: edouard@3414: edouard@3414: edouard@3414: edouard@3414: edouard@3414: edouard@3414: edouard@3414: edouard@3414: edouard@3414: edouard@3414: edouard@3414: edouard@3414: edouard@3414: edouard@3414: edouard@3414: edouard@3414: edouard@3414: edouard@3414: edouard@3414: edouard@3414: edouard@3414: edouard@3414: edouard@3414: edouard@3414: edouard@3414: edouard@3414: edouard@3414: edouard@3414: edouard@3414: edouard@3414: edouard@3414: edouard@3414: edouard@3414: edouard@3414: edouard@3414: edouard@3414: edouard@3414: edouard@3414: Edouard@3085: Edouard@3085: Edouard@3085: Edouard@3085: Edouard@3085: Edouard@3085: Edouard@3085: Edouard@3085: Edouard@3085: Edouard@3085: Edouard@3085: Edouard@3085: Edouard@3085: Edouard@3085: Edouard@3085: Edouard@3085: Edouard@3085: Edouard@3085: Edouard@3085: Edouard@3085: Edouard@3085: Edouard@3085: Edouard@3085: Edouard@3085: Edouard@3085: Edouard@3085: Edouard@3085: Edouard@3085: Edouard@3085: Edouard@3085: Edouard@3085: Edouard@3085: Edouard@3085: Edouard@3085: Edouard@3085: Edouard@3085: Edouard@3085: Edouard@3085: Edouard@3085: Edouard@3085: Edouard@3085: Edouard@3085: Edouard@3085: Edouard@3085: Edouard@3085: Edouard@3085: Edouard@3085: Edouard@3085: Edouard@3085: Edouard@3085: Edouard@3085: Edouard@3085: Edouard@3085: Edouard@3085: Edouard@3085: Edouard@3085: Edouard@3085: Edouard@3085: Edouard@3085: Edouard@3085: Edouard@3085: Edouard@3085: Edouard@3085: Edouard@3085: Edouard@3085: Edouard@3085: switch (this.state) { Edouard@3085: Edouard@3085: Edouard@3085: } Edouard@3085: Edouard@3085: Edouard@3085: Edouard@3085: case " Edouard@3085: Edouard@3085: ": Edouard@3085: Edouard@3085: Edouard@3085: break; Edouard@3085: Edouard@3085: Edouard@3085: Edouard@3085: if(value == Edouard@3085: Edouard@3085: ) { Edouard@3085: Edouard@3085: Edouard@3085: } Edouard@3085: Edouard@3085: Edouard@3085: Edouard@3085: Edouard@3085: switch (this.state) { Edouard@3085: Edouard@3085: Edouard@3085: Edouard@3085: Edouard@3085: } Edouard@3085: Edouard@3085: Edouard@3085: Edouard@3085: Edouard@3085: case " Edouard@3085: Edouard@3085: ": Edouard@3085: Edouard@3085: Edouard@3085: break; Edouard@3085: Edouard@3085: Edouard@3085: Edouard@3085: Edouard@3085: Edouard@3085: Edouard@3085: this.state = " Edouard@3085: Edouard@3085: "; Edouard@3085: Edouard@3085: this. Edouard@3085: Edouard@3085: _action(); Edouard@3085: Edouard@3085: Edouard@3085: Edouard@3085: Edouard@3085: Edouard@3085: Edouard@3085: Edouard@3085: Edouard@3085: _action(){ Edouard@3085: edouard@3414: console.log("Entering state edouard@3414: edouard@3414: "); edouard@3414: Edouard@3085: Edouard@3085: } Edouard@3085: Edouard@3085: Edouard@3085: Edouard@3085: this.display = " Edouard@3085: Edouard@3085: "; Edouard@3085: Edouard@3085: this.request_animate(); Edouard@3085: Edouard@3085: Edouard@3085: Edouard@3085: this.apply_hmi_value(0, Edouard@3085: Edouard@3085: ); Edouard@3085: Edouard@3085: edouard@3414: edouard@3414: edouard@3414: frequency = 5; edouard@3414: edouard@3414: display = "inactive"; edouard@3414: edouard@3414: state = "init"; edouard@3414: edouard@3414: dispatch(value) { edouard@3414: edouard@3414: edouard@3414: } edouard@3414: edouard@3414: onmouseup(evt) { edouard@3414: edouard@3414: svg_root.removeEventListener("pointerup", this.bound_onmouseup, true); edouard@3414: edouard@3414: edouard@3414: edouard@3414: edouard@3414: } edouard@3414: edouard@3414: onmousedown(evt) { edouard@3414: edouard@3414: svg_root.addEventListener("pointerup", this.bound_onmouseup, true); edouard@3414: edouard@3414: edouard@3414: edouard@3414: edouard@3414: } edouard@3414: edouard@3414: edouard@3414: animate(){ edouard@3414: edouard@3414: if (this.active_elt && this.inactive_elt) { edouard@3414: edouard@3414: edouard@3414: if(this.display == " edouard@3414: edouard@3414: ") edouard@3414: edouard@3414: this. edouard@3414: edouard@3414: _elt.style.display = ""; edouard@3414: edouard@3414: else edouard@3414: edouard@3414: this. edouard@3414: edouard@3414: _elt.style.display = "none"; edouard@3414: edouard@3414: edouard@3414: } edouard@3414: edouard@3414: } edouard@3414: edouard@3414: init() { edouard@3414: edouard@3414: this.bound_onmouseup = this.onmouseup.bind(this); edouard@3414: edouard@3414: this.element.addEventListener("pointerdown", this.onmousedown.bind(this)); edouard@3414: edouard@3414: } edouard@3414: edouard@3414: edouard@3238: edouard@3238: class edouard@3238: ButtonWidget edouard@3238: extends Widget{ edouard@3238: Edouard@3085: edouard@3414: edouard@3414: edouard@3414: Edouard@3024: } usveticic@3015: usveticic@3015: edouard@3238: dgaberscek@2976: dgaberscek@2976: dgaberscek@2976: dgaberscek@2976: dgaberscek@2976: active inactive dgaberscek@2976: dgaberscek@2976: dgaberscek@2976: usveticic@3015: edouard@3414: edouard@3414: class edouard@3414: PushButtonWidget edouard@3414: extends Widget{ edouard@3414: edouard@3414: edouard@3414: edouard@3414: edouard@3414: edouard@3414: } edouard@3414: edouard@3414: edouard@3414: edouard@3414: edouard@3414: edouard@3414: edouard@3414: edouard@3414: active inactive edouard@3414: edouard@3414: edouard@3414: edouard@3414: edouard@3241: edouard@3241: edouard@3241: edouard@3241: edouard@3241: edouard@3241: CircularBar widget changes the end angle of a "path" labeled arc according edouard@3241: edouard@3241: to value of the single accepted variable. edouard@3241: edouard@3241: edouard@3241: edouard@3241: If "min" a "max" labeled texts are provided, then they are used as edouard@3241: edouard@3241: respective minimum and maximum value. Otherwise, value is expected to be edouard@3241: edouard@3241: in between 0 and 100. edouard@3241: edouard@3241: edouard@3241: edouard@3241: Change end angle of Inkscape's arc edouard@3241: edouard@3362: edouard@3362: minimum value edouard@3362: edouard@3362: edouard@3362: maximum value edouard@3362: edouard@3241: edouard@3241: Value to display edouard@3241: edouard@3241: edouard@3238: edouard@3238: class edouard@3238: CircularBarWidget edouard@3238: extends Widget{ usveticic@3045: usveticic@3045: frequency = 10; usveticic@3045: usveticic@3045: range = undefined; usveticic@3045: usveticic@3045: usveticic@3045: usveticic@3045: dispatch(value) { usveticic@3045: Edouard@3112: this.display_val = value; Edouard@3112: Edouard@3112: this.request_animate(); Edouard@3112: Edouard@3112: } Edouard@3112: Edouard@3112: Edouard@3112: Edouard@3112: animate(){ Edouard@3112: usveticic@3045: if(this.value_elt) usveticic@3045: Edouard@3112: this.value_elt.textContent = String(this.display_val); usveticic@3045: usveticic@3045: let [min,max,start,end] = this.range; usveticic@3045: usveticic@3045: let [cx,cy] = this.center; usveticic@3045: usveticic@3045: let [rx,ry] = this.proportions; usveticic@3045: Edouard@3112: let tip = start + (end-start)*Number(this.display_val)/(max-min); usveticic@3045: usveticic@3045: let size = 0; usveticic@3045: Edouard@3112: Edouard@3112: Edouard@3112: if (tip-start > Math.PI) usveticic@3045: usveticic@3045: size = 1; usveticic@3045: Edouard@3112: else usveticic@3045: usveticic@3045: size = 0; usveticic@3045: Edouard@3112: Edouard@3112: Edouard@3112: this.path_elt.setAttribute('d', "M "+(cx+rx*Math.cos(start))+","+(cy+ry*Math.sin(start))+ Edouard@3112: Edouard@3112: " A "+rx+","+ry+ Edouard@3112: Edouard@3112: " 0 "+size+ Edouard@3112: Edouard@3112: " 1 "+(cx+rx*Math.cos(tip))+","+(cy+ry*Math.sin(tip))); usveticic@3045: usveticic@3045: } usveticic@3045: usveticic@3045: usveticic@3045: usveticic@3045: init() { usveticic@3045: edouard@3362: if(this.args.length >= 2) edouard@3362: edouard@3362: [this.min, this.max]=this.args; edouard@3362: edouard@3362: edouard@3362: Edouard@3112: let [start, end, cx, cy, rx, ry] = ["start", "end", "cx", "cy", "rx", "ry"]. Edouard@3112: Edouard@3112: map(tag=>Number(this.path_elt.getAttribute('sodipodi:'+tag))) Edouard@3112: Edouard@3112: Edouard@3112: Edouard@3112: if (ry == 0) usveticic@3045: usveticic@3045: ry = rx; usveticic@3045: Edouard@3112: Edouard@3112: Edouard@3112: if (start > end) usveticic@3045: usveticic@3045: end = end + 2*Math.PI; usveticic@3045: Edouard@3112: Edouard@3112: Edouard@3112: let [min,max] = [[this.min_elt,0],[this.max_elt,100]].map(([elt,def],i)=>elt? Edouard@3112: Edouard@3112: Number(elt.textContent) : Edouard@3112: Edouard@3112: this.args.length >= i+1 ? this.args[i] : def); Edouard@3112: Edouard@3112: usveticic@3045: usveticic@3045: this.range = [min, max, start, end]; usveticic@3045: usveticic@3045: this.center = [cx, cy]; usveticic@3045: usveticic@3045: this.proportions = [rx, ry]; usveticic@3045: usveticic@3045: } usveticic@3045: usveticic@3045: } usveticic@3045: usveticic@3045: edouard@3238: dgaberscek@2944: dgaberscek@2944: dgaberscek@2944: dgaberscek@2944: dgaberscek@2944: path dgaberscek@2944: dgaberscek@2944: dgaberscek@2944: dgaberscek@2944: dgaberscek@2944: edouard@3362: min max dgaberscek@2944: dgaberscek@2944: dgaberscek@2944: dgaberscek@2944: edouard@3241: edouard@3241: edouard@3241: edouard@3241: edouard@3241: edouard@3241: CircularSlider - DEPRECATED, to be replaced by PathSlider edouard@3241: edouard@3241: This widget moves "handle" labeled group along "range" labeled edouard@3241: edouard@3241: arc, according to value of the single accepted variable. edouard@3241: edouard@3241: edouard@3241: edouard@3241: If "min" a "max" labeled texts are provided, or if first and second edouard@3241: edouard@3241: argument are given, then they are used as respective minimum and maximum edouard@3241: edouard@3241: value. Otherwise, value is expected to be in between 0 and 100. edouard@3241: edouard@3241: edouard@3241: edouard@3241: If "value" labeled text is found, then its content is replaced by value. edouard@3241: edouard@3241: During drag, "setpoint" labeled group is moved to position defined by user edouard@3241: edouard@3241: while "handle" reflects current value from variable. edouard@3241: edouard@3241: edouard@3241: edouard@3241: CircularSlider - DEPRECATED edouard@3241: edouard@3241: edouard@3241: minimum value edouard@3241: edouard@3241: edouard@3241: maximum value edouard@3241: edouard@3241: edouard@3241: Value to display edouard@3241: edouard@3241: edouard@3238: edouard@3238: class edouard@3238: CircularSliderWidget edouard@3238: extends Widget{ usveticic@3015: usveticic@3015: frequency = 5; usveticic@3015: usveticic@3015: range = undefined; usveticic@3015: usveticic@3015: circle = undefined; usveticic@3015: usveticic@3015: handle_pos = undefined; usveticic@3015: Edouard@3065: curr_value = 0; usveticic@3045: usveticic@3015: drag = false; usveticic@3015: usveticic@3015: enTimer = false; usveticic@3015: usveticic@3045: last_drag = false; usveticic@3045: usveticic@3015: usveticic@3015: usveticic@3015: dispatch(value) { usveticic@3015: Edouard@3065: let [min,max,start,totallength] = this.range; Edouard@3065: Edouard@3065: //save current value inside widget Edouard@3065: Edouard@3065: this.curr_value = value; Edouard@3065: Edouard@3065: Edouard@3065: Edouard@3065: //check if in range Edouard@3065: Edouard@3065: if (this.curr_value > max){ Edouard@3065: Edouard@3065: this.curr_value = max; Edouard@3065: Edouard@3065: this.apply_hmi_value(0, this.curr_value); Edouard@3065: Edouard@3065: } Edouard@3065: Edouard@3065: else if (this.curr_value < min){ Edouard@3065: Edouard@3065: this.curr_value = min; Edouard@3065: Edouard@3065: this.apply_hmi_value(0, this.curr_value); Edouard@3065: Edouard@3065: } Edouard@3065: Edouard@3065: Edouard@3065: usveticic@3045: if(this.value_elt) usveticic@3045: usveticic@3045: this.value_elt.textContent = String(value); usveticic@3045: usveticic@3045: usveticic@3045: Edouard@3065: //don't update if draging and setpoint ghost doesn't exist Edouard@3065: Edouard@3065: if(!this.drag || (this.setpoint_elt != undefined)){ Edouard@3065: Edouard@3065: this.update_DOM(value, this.handle_elt); Edouard@3065: Edouard@3065: } usveticic@3045: usveticic@3045: } usveticic@3045: usveticic@3045: usveticic@3045: usveticic@3045: update_DOM(value, elt){ usveticic@3045: usveticic@3045: let [min,max,totalDistance] = this.range; usveticic@3045: usveticic@3045: let length = Math.max(0,Math.min((totalDistance),(Number(value)-min)/(max-min)*(totalDistance))); usveticic@3045: usveticic@3045: let tip = this.range_elt.getPointAtLength(length); usveticic@3045: usveticic@3045: elt.setAttribute('transform',"translate("+(tip.x-this.handle_pos.x)+","+(tip.y-this.handle_pos.y)+")"); usveticic@3045: usveticic@3045: usveticic@3045: Edouard@3065: // show or hide ghost if exists Edouard@3065: usveticic@3045: if(this.setpoint_elt != undefined){ usveticic@3045: usveticic@3045: if(this.last_drag!= this.drag){ usveticic@3045: usveticic@3045: if(this.drag){ usveticic@3045: usveticic@3045: this.setpoint_elt.setAttribute("style", this.setpoint_style); usveticic@3045: usveticic@3045: }else{ usveticic@3045: usveticic@3045: this.setpoint_elt.setAttribute("style", "display:none"); usveticic@3045: usveticic@3045: } usveticic@3045: usveticic@3045: this.last_drag = this.drag; usveticic@3045: usveticic@3045: } usveticic@3015: usveticic@3015: } usveticic@3015: usveticic@3015: } usveticic@3015: usveticic@3015: usveticic@3015: usveticic@3015: on_release(evt) { usveticic@3015: Edouard@3065: //unbind events Edouard@3065: usveticic@3045: window.removeEventListener("touchmove", this.on_bound_drag, true); usveticic@3045: usveticic@3045: window.removeEventListener("mousemove", this.on_bound_drag, true); usveticic@3045: usveticic@3045: usveticic@3045: usveticic@3045: window.removeEventListener("mouseup", this.bound_on_release, true) usveticic@3045: usveticic@3045: window.removeEventListener("touchend", this.bound_on_release, true); usveticic@3045: usveticic@3045: window.removeEventListener("touchcancel", this.bound_on_release, true); usveticic@3045: Edouard@3065: Edouard@3065: Edouard@3065: //reset drag flag Edouard@3065: usveticic@3015: if(this.drag){ usveticic@3015: usveticic@3015: this.drag = false; usveticic@3015: usveticic@3015: } usveticic@3015: Edouard@3065: Edouard@3065: Edouard@3065: // get final position Edouard@3065: usveticic@3045: this.update_position(evt); usveticic@3045: usveticic@3045: } usveticic@3045: usveticic@3045: usveticic@3045: usveticic@3045: on_drag(evt){ usveticic@3045: Edouard@3065: //ignore drag event for X amount of time and if not selected Edouard@3065: usveticic@3045: if(this.enTimer && this.drag){ usveticic@3045: usveticic@3045: this.update_position(evt); usveticic@3045: Edouard@3065: Edouard@3065: usveticic@3045: //reset timer usveticic@3045: usveticic@3045: this.enTimer = false; usveticic@3045: usveticic@3045: setTimeout("{hmi_widgets['"+this.element_id+"'].enTimer = true;}", 100); usveticic@3045: usveticic@3045: } usveticic@3045: usveticic@3015: } usveticic@3015: usveticic@3015: usveticic@3015: usveticic@3015: update_position(evt){ usveticic@3015: usveticic@3015: if(this.drag && this.enTimer){ usveticic@3015: usveticic@3015: var svg_dist = 0; usveticic@3015: usveticic@3015: usveticic@3015: usveticic@3015: //calculate center of widget in html usveticic@3015: usveticic@3015: // --TODO maybe it would be better to bind this part to window change size event ??? usveticic@3015: usveticic@3015: let [xdest,ydest,svgWidth,svgHeight] = page_desc[current_visible_page].bbox; usveticic@3015: usveticic@3015: let [cX, cY,fiStart,fiEnd,minMax,x1,y1,width,height] = this.circle; usveticic@3015: usveticic@3015: let htmlCirc = this.range_elt.getBoundingClientRect(); usveticic@3015: usveticic@3015: let cxHtml = ((htmlCirc.right-htmlCirc.left)/(width)*(cX-x1))+htmlCirc.left; usveticic@3015: usveticic@3015: let cyHtml = ((htmlCirc.bottom-htmlCirc.top)/(height)*(cY-y1))+htmlCirc.top; usveticic@3015: usveticic@3015: usveticic@3015: usveticic@3015: usveticic@3015: usveticic@3015: //get mouse coordinates usveticic@3015: usveticic@3015: let mouseX = undefined; usveticic@3015: usveticic@3015: let mouseY = undefined; usveticic@3015: usveticic@3015: if (evt.type.startsWith("touch")){ usveticic@3015: usveticic@3015: mouseX = Math.ceil(evt.touches[0].clientX); usveticic@3015: usveticic@3015: mouseY = Math.ceil(evt.touches[0].clientY); usveticic@3015: usveticic@3015: } usveticic@3015: usveticic@3015: else{ usveticic@3015: usveticic@3015: mouseX = evt.pageX; usveticic@3015: usveticic@3015: mouseY = evt.pageY; usveticic@3015: usveticic@3015: } usveticic@3015: usveticic@3015: usveticic@3015: usveticic@3015: //calculate angle usveticic@3015: usveticic@3015: let fi = Math.atan2(cyHtml-mouseY, mouseX-cxHtml); usveticic@3015: usveticic@3015: usveticic@3015: usveticic@3015: // transform from 0 to 2PI usveticic@3015: usveticic@3015: if (fi > 0){ usveticic@3015: usveticic@3015: fi = 2*Math.PI-fi; usveticic@3015: usveticic@3015: } usveticic@3015: usveticic@3015: else{ usveticic@3015: usveticic@3015: fi = -fi; usveticic@3015: usveticic@3015: } usveticic@3015: usveticic@3015: usveticic@3015: usveticic@3015: //offset it to 0 usveticic@3015: usveticic@3015: fi = fi - fiStart; usveticic@3015: usveticic@3015: if (fi < 0){ usveticic@3015: usveticic@3015: fi = fi + 2*Math.PI; usveticic@3015: usveticic@3015: } usveticic@3015: usveticic@3015: usveticic@3015: usveticic@3015: //get handle distance from mouse position usveticic@3015: usveticic@3015: if(fi<fiEnd){ usveticic@3015: Edouard@3065: this.curr_value=(fi)/(fiEnd)*(this.range[1]-this.range[0]); usveticic@3015: usveticic@3015: } usveticic@3015: usveticic@3015: else if(fiEnd<fi && fi<fiEnd+minMax){ usveticic@3015: Edouard@3065: this.curr_value = this.range[1]; usveticic@3015: usveticic@3015: } usveticic@3015: usveticic@3015: else{ usveticic@3015: Edouard@3065: this.curr_value = this.range[0]; usveticic@3015: usveticic@3015: } usveticic@3015: usveticic@3015: usveticic@3015: Edouard@3065: //apply value to hmi Edouard@3065: Edouard@3065: this.apply_hmi_value(0, Math.ceil(this.curr_value)); Edouard@3065: Edouard@3065: Edouard@3065: Edouard@3065: //redraw handle Edouard@3065: Edouard@3065: this.request_animate(); Edouard@3065: Edouard@3065: usveticic@3015: usveticic@3015: } usveticic@3015: usveticic@3015: usveticic@3015: usveticic@3015: } usveticic@3015: usveticic@3015: usveticic@3015: usveticic@3045: animate(){ usveticic@3045: Edouard@3065: // redraw handle on screen refresh Edouard@3065: Edouard@3065: // check if setpoint(ghost) handle exsist otherwise update main handle Edouard@3065: Edouard@3065: if(this.setpoint_elt != undefined){ Edouard@3065: Edouard@3065: this.update_DOM(this.curr_value, this.setpoint_elt); Edouard@3065: Edouard@3065: } Edouard@3065: Edouard@3065: else{ Edouard@3065: Edouard@3065: this.update_DOM(this.curr_value, this.handle_elt); Edouard@3065: Edouard@3065: } usveticic@3045: usveticic@3045: } usveticic@3045: usveticic@3045: usveticic@3045: usveticic@3015: on_select(evt){ usveticic@3015: Edouard@3065: //enable drag flag and timer Edouard@3065: usveticic@3015: this.drag = true; usveticic@3015: usveticic@3015: this.enTimer = true; usveticic@3015: Edouard@3065: Edouard@3065: Edouard@3065: //bind events Edouard@3065: usveticic@3045: window.addEventListener("touchmove", this.on_bound_drag, true); usveticic@3045: usveticic@3045: window.addEventListener("mousemove", this.on_bound_drag, true); usveticic@3045: usveticic@3045: usveticic@3045: Edouard@3065: window.addEventListener("mouseup", this.bound_on_release, true); usveticic@3045: usveticic@3045: window.addEventListener("touchend", this.bound_on_release, true); usveticic@3045: usveticic@3045: window.addEventListener("touchcancel", this.bound_on_release, true); usveticic@3045: Edouard@3065: Edouard@3065: Edouard@3065: //update postion on mouse press Edouard@3065: usveticic@3015: this.update_position(evt); usveticic@3015: Edouard@3065: Edouard@3065: Edouard@3065: //prevent next events Edouard@3065: Edouard@3065: evt.stopPropagation(); Edouard@3065: usveticic@3015: } usveticic@3015: usveticic@3015: usveticic@3015: usveticic@3015: init() { usveticic@3015: usveticic@3015: //get min max usveticic@3015: usveticic@3015: let min = this.min_elt ? usveticic@3015: usveticic@3015: Number(this.min_elt.textContent) : usveticic@3015: usveticic@3015: this.args.length >= 1 ? this.args[0] : 0; usveticic@3015: usveticic@3015: let max = this.max_elt ? usveticic@3015: usveticic@3015: Number(this.max_elt.textContent) : usveticic@3015: usveticic@3015: this.args.length >= 2 ? this.args[1] : 100; usveticic@3015: usveticic@3015: usveticic@3015: usveticic@3015: //fiStart ==> offset usveticic@3015: usveticic@3015: let fiStart = Number(this.range_elt.getAttribute('sodipodi:start')); usveticic@3015: usveticic@3015: let fiEnd = Number(this.range_elt.getAttribute('sodipodi:end')); usveticic@3015: usveticic@3015: fiEnd = fiEnd - fiStart; usveticic@3015: usveticic@3015: usveticic@3015: usveticic@3015: //fiEnd ==> size of angle usveticic@3015: usveticic@3015: if (fiEnd < 0){ usveticic@3015: usveticic@3015: fiEnd = 2*Math.PI + fiEnd; usveticic@3015: usveticic@3015: } usveticic@3015: usveticic@3015: usveticic@3015: usveticic@3015: //min max barrier angle usveticic@3015: usveticic@3015: let minMax = (2*Math.PI - fiEnd)/2; usveticic@3015: usveticic@3015: usveticic@3015: usveticic@3015: //get parameters from svg usveticic@3015: usveticic@3015: let cX = Number(this.range_elt.getAttribute('sodipodi:cx')); usveticic@3015: usveticic@3015: let cY = Number(this.range_elt.getAttribute('sodipodi:cy')); usveticic@3015: usveticic@3015: this.range_elt.style.strokeMiterlimit="0"; //eliminates some weird border around html object usveticic@3015: usveticic@3015: this.range = [min, max,this.range_elt.getTotalLength()]; usveticic@3015: usveticic@3015: let cPos = this.range_elt.getBBox(); usveticic@3015: usveticic@3015: this.handle_pos = this.range_elt.getPointAtLength(0); usveticic@3015: usveticic@3015: this.circle = [cX, cY,fiStart,fiEnd,minMax,cPos.x,cPos.y,cPos.width,cPos.height]; usveticic@3015: usveticic@3015: usveticic@3015: usveticic@3045: //bind functions usveticic@3045: usveticic@3045: this.bound_on_select = this.on_select.bind(this); usveticic@3045: usveticic@3045: this.bound_on_release = this.on_release.bind(this); usveticic@3045: usveticic@3045: this.on_bound_drag = this.on_drag.bind(this); usveticic@3045: usveticic@3045: usveticic@3045: Edouard@3065: this.handle_elt.addEventListener("mousedown", this.bound_on_select); usveticic@3015: usveticic@3045: this.element.addEventListener("mousedown", this.bound_on_select); usveticic@3045: usveticic@3045: this.element.addEventListener("touchstart", this.bound_on_select); usveticic@3045: Edouard@3065: //touch recognised as page drag without next command Edouard@3065: Edouard@3065: document.body.addEventListener("touchstart", function(e){}, false); Edouard@3065: Edouard@3065: Edouard@3065: Edouard@3065: //save ghost style Edouard@3065: Edouard@3065: //save ghost style usveticic@3045: usveticic@3045: if(this.setpoint_elt != undefined){ usveticic@3045: usveticic@3045: this.setpoint_style = this.setpoint_elt.getAttribute("style"); usveticic@3045: usveticic@3045: this.setpoint_elt.setAttribute("style", "display:none"); usveticic@3045: usveticic@3045: } usveticic@3045: usveticic@3045: usveticic@3015: usveticic@3015: } usveticic@3015: usveticic@3015: } usveticic@3015: usveticic@3015: edouard@3238: usveticic@3015: usveticic@3015: usveticic@3015: usveticic@3015: usveticic@3015: handle range usveticic@3015: usveticic@3015: usveticic@3015: usveticic@3015: usveticic@3015: Edouard@3065: value min max setpoint usveticic@3015: usveticic@3015: usveticic@3015: usveticic@3015: usveticic@3015: usveticic@3015: edouard@3241: edouard@3241: edouard@3241: edouard@3241: edouard@3241: edouard@3241: CustomHtml widget allows insertion of HTML code in a svg:foreignObject. edouard@3241: edouard@3241: Widget content is replaced by foreignObject. HTML code is obtained from edouard@3241: edouard@3241: "code" labeled text content. HTML insert position and size is given with edouard@3241: edouard@3241: "container" labeled element. edouard@3241: edouard@3241: edouard@3241: edouard@3241: Custom HTML insert edouard@3241: edouard@3241: edouard@3238: edouard@3238: class edouard@3238: CustomHtmlWidget edouard@3238: extends Widget{ Edouard@3065: Edouard@3065: frequency = 5; Edouard@3065: Edouard@3065: widget_size = undefined; Edouard@3065: Edouard@3065: Edouard@3065: Edouard@3065: dispatch(value) { Edouard@3065: Edouard@3065: this.request_animate(); Edouard@3065: Edouard@3065: } Edouard@3065: Edouard@3065: Edouard@3065: Edouard@3065: animate(){ Edouard@3065: Edouard@3065: } Edouard@3065: Edouard@3065: Edouard@3065: Edouard@3065: init() { Edouard@3065: Edouard@3065: this.widget_size = this.container_elt.getBBox(); Edouard@3065: Edouard@3065: this.element.innerHTML ='<foreignObject x="'+ Edouard@3065: Edouard@3065: this.widget_size.x+'" y="'+this.widget_size.y+ Edouard@3065: Edouard@3065: '" width="'+this.widget_size.width+'" height="'+this.widget_size.height+'"> '+ Edouard@3065: Edouard@3065: this.code_elt.textContent+ Edouard@3065: Edouard@3065: ' </foreignObject>'; Edouard@3065: Edouard@3065: } Edouard@3065: Edouard@3065: } Edouard@3065: Edouard@3065: edouard@3238: Edouard@3065: Edouard@3065: Edouard@3065: Edouard@3065: Edouard@3065: container code Edouard@3065: Edouard@3065: edouard@3238: edouard@3241: edouard@3241: edouard@3241: edouard@3241: edouard@3241: edouard@3241: If Display widget is a svg:text element, then text content is replaced by edouard@3241: edouard@3241: value of given variables, space separated. edouard@3241: edouard@3241: edouard@3241: edouard@3241: Otherwise, if Display widget is a group containing a svg:text element edouard@3241: edouard@3241: labelled "format", then text content is replaced by printf-like formated edouard@3241: edouard@3241: string. In other words, if "format" labeled text is "%d %s %f", then 3 edouard@3241: edouard@3241: variables paths are expected : HMI_IN, HMI_STRING and HMI_REAL. edouard@3241: edouard@3241: edouard@3241: edouard@3241: In case Display widget is a svg::text element, it is also possible to give edouard@3241: edouard@3241: format string as first argument. edouard@3241: edouard@3241: edouard@3241: edouard@3241: Printf-like formated text display edouard@3241: edouard@3241: edouard@3241: printf-like format string when not given as svg:text edouard@3241: edouard@3241: edouard@3241: variables to be displayed edouard@3241: edouard@3241: edouard@3238: edouard@3238: class edouard@3238: DisplayWidget edouard@3238: extends Widget{ edouard@2998: edouard@2998: frequency = 5; edouard@2998: edouard@3008: dispatch(value, oldval, index) { edouard@3008: edouard@3008: this.fields[index] = value; edouard@3008: Edouard@3147: this.request_animate(); edouard@2998: edouard@2998: } edouard@2998: edouard@2998: } edouard@2998: edouard@2998: edouard@3238: edouard@2883: Edouard@3147: Edouard@3147: Edouard@3147: Edouard@3147: Edouard@3147: format Edouard@3147: Edouard@3147: Edouard@3147: Edouard@3147: Edouard@3147: Edouard@3147: Edouard@3147: edouard@2998: edouard@2998: Display Widget id=" edouard@2998: Edouard@3147: " must be a svg::text element itself or a group containing a svg:text element labelled "format" edouard@2998: edouard@2998: Edouard@3022: Edouard@3022: Edouard@3022: Edouard@3022: Edouard@3022: "" Edouard@3022: Edouard@3065: Edouard@3065: 0 Edouard@3065: Edouard@3022: Edouard@3022: Edouard@3022: , Edouard@3022: Edouard@3022: Edouard@3022: Edouard@3022: fields: [ Edouard@3022: Edouard@3022: ], edouard@3008: Edouard@3147: animate: function(){ Edouard@3147: Edouard@3147: Edouard@3147: Edouard@3147: if(this.format_elt.getAttribute("lang")) { Edouard@3147: Edouard@3147: this.format = svg_text_to_multiline(this.format_elt); Edouard@3147: Edouard@3147: this.format_elt.removeAttribute("lang"); Edouard@3147: Edouard@3147: } Edouard@3147: Edouard@3147: let str = vsprintf(this.format,this.fields); Edouard@3147: Edouard@3147: multiline_to_svg_text(this.format_elt, str); Edouard@3147: Edouard@3147: Edouard@3147: Edouard@3147: let str = this.args.length == 1 ? vsprintf(this.args[0],this.fields) : this.fields.join(' '); Edouard@3147: Edouard@3147: multiline_to_svg_text(this.element, str); Edouard@3147: Edouard@3147: Edouard@3147: Edouard@3147: }, Edouard@3147: Edouard@3147: Edouard@3147: Edouard@3147: Edouard@3147: init: function() { Edouard@3147: Edouard@3147: this.format = svg_text_to_multiline(this.format_elt); Edouard@3147: Edouard@3147: }, Edouard@3147: Edouard@3147: edouard@3008: edouard@3241: edouard@3241: edouard@3241: edouard@3241: edouard@3241: edouard@3241: DropDown widget let user select an entry in a list of texts, given as edouard@3241: edouard@3241: arguments. Single variable path is index of selection. edouard@3241: edouard@3241: edouard@3241: edouard@3355: It needs "text" (svg:text or svg:use referring to svg:text), edouard@3355: edouard@3355: "box" (svg:rect), "button" (svg:*), and "highlight" (svg:rect) edouard@3355: edouard@3355: labeled elements. edouard@3241: edouard@3241: edouard@3241: edouard@3241: When user clicks on "button", "text" is duplicated to display enties in the edouard@3241: edouard@3241: limit of available space in page, and "box" is extended to contain all edouard@3241: edouard@3241: texts. "highlight" is moved over pre-selected entry. edouard@3241: edouard@3241: edouard@3241: edouard@3355: When only one argument is given and argment contains "#langs" then list of edouard@3355: edouard@3355: texts is automatically set to the human-readable list of supported edouard@3355: edouard@3355: languages by this HMI. edouard@3355: edouard@3355: edouard@3355: edouard@3355: If "text" labeled element is of type svg:use and refers to a svg:text edouard@3355: edouard@3355: element part of a TextList widget, no argument is expected. In that case edouard@3355: edouard@3355: list of texts is set to TextList content. edouard@3241: edouard@3241: edouard@3241: edouard@3241: Let user select text entry in a drop-down menu edouard@3241: edouard@3241: edouard@3241: drop-down menu entries edouard@3241: edouard@3241: edouard@3241: selection index edouard@3241: edouard@3241: edouard@3238: edouard@3238: class edouard@3238: DropDownWidget edouard@3238: extends Widget{ Edouard@3090: Edouard@3090: dispatch(value) { Edouard@3090: Edouard@3090: if(!this.opened) this.set_selection(value); Edouard@3090: Edouard@3090: } Edouard@3090: Edouard@3090: init() { Edouard@3090: edouard@3355: this.init_specific(); edouard@3355: Edouard@3090: this.button_elt.onclick = this.on_button_click.bind(this); Edouard@3090: Edouard@3090: // Save original size of rectangle Edouard@3090: Edouard@3090: this.box_bbox = this.box_elt.getBBox() Edouard@3090: Edouard@3092: this.highlight_bbox = this.highlight_elt.getBBox() Edouard@3092: Edouard@3092: this.highlight_elt.style.visibility = "hidden"; Edouard@3092: Edouard@3090: Edouard@3090: Edouard@3090: // Compute margins Edouard@3090: Edouard@3091: this.text_bbox = this.text_elt.getBBox(); Edouard@3091: Edouard@3091: let lmargin = this.text_bbox.x - this.box_bbox.x; Edouard@3091: Edouard@3091: let tmargin = this.text_bbox.y - this.box_bbox.y; Edouard@3090: Edouard@3090: this.margins = [lmargin, tmargin].map(x => Math.max(x,0)); Edouard@3090: Edouard@3090: Edouard@3090: Edouard@3090: // Index of first visible element in the menu, when opened Edouard@3090: Edouard@3090: this.menu_offset = 0; Edouard@3090: Edouard@3090: Edouard@3090: Edouard@3090: // How mutch to lift the menu vertically so that it does not cross bottom border Edouard@3090: Edouard@3090: this.lift = 0; Edouard@3090: Edouard@3090: Edouard@3090: Edouard@3090: // Event handlers cannot be object method ('this' is unknown) Edouard@3090: Edouard@3090: // as a workaround, handler given to addEventListener is bound in advance. Edouard@3090: Edouard@3090: this.bound_close_on_click_elsewhere = this.close_on_click_elsewhere.bind(this); Edouard@3090: Edouard@3090: this.bound_on_selection_click = this.on_selection_click.bind(this); Edouard@3090: Edouard@3090: this.bound_on_backward_click = this.on_backward_click.bind(this); Edouard@3090: Edouard@3090: this.bound_on_forward_click = this.on_forward_click.bind(this); Edouard@3090: Edouard@3090: this.opened = false; Edouard@3090: Edouard@3091: this.clickables = []; Edouard@3091: Edouard@3090: } Edouard@3090: Edouard@3090: on_button_click() { Edouard@3090: Edouard@3090: this.open(); Edouard@3090: Edouard@3090: } Edouard@3090: Edouard@3090: // Called when a menu entry is clicked Edouard@3090: Edouard@3090: on_selection_click(selection) { Edouard@3090: Edouard@3090: this.close(); Edouard@3090: Edouard@3090: this.apply_hmi_value(0, selection); Edouard@3090: Edouard@3090: } Edouard@3090: Edouard@3090: on_backward_click(){ Edouard@3090: Edouard@3090: this.scroll(false); Edouard@3090: Edouard@3090: } Edouard@3090: Edouard@3090: on_forward_click(){ Edouard@3090: Edouard@3090: this.scroll(true); Edouard@3090: Edouard@3090: } Edouard@3090: Edouard@3090: set_selection(value) { Edouard@3090: Edouard@3090: let display_str; Edouard@3090: Edouard@3090: if(value >= 0 && value < this.content.length){ Edouard@3090: Edouard@3090: // if valid selection resolve content Edouard@3090: edouard@3355: display_str = gettext(this.content[value]); Edouard@3090: Edouard@3090: this.last_selection = value; Edouard@3090: Edouard@3090: } else { Edouard@3090: Edouard@3090: // otherwise show problem Edouard@3090: Edouard@3090: display_str = "?"+String(value)+"?"; Edouard@3090: Edouard@3090: } Edouard@3090: Edouard@3090: // It is assumed that first span always stays, Edouard@3090: Edouard@3090: // and contains selection when menu is closed Edouard@3090: Edouard@3090: this.text_elt.firstElementChild.textContent = display_str; Edouard@3090: Edouard@3090: } Edouard@3090: Edouard@3090: grow_text(up_to) { Edouard@3090: Edouard@3090: let count = 1; Edouard@3090: Edouard@3091: let txt = this.text_elt; Edouard@3090: Edouard@3090: let first = txt.firstElementChild; Edouard@3090: Edouard@3090: // Real world (pixels) boundaries of current page Edouard@3090: Edouard@3091: let bounds = svg_root.getBoundingClientRect(); Edouard@3090: Edouard@3090: this.lift = 0; Edouard@3090: Edouard@3090: while(count < up_to) { Edouard@3090: Edouard@3090: let next = first.cloneNode(); Edouard@3090: Edouard@3090: // relative line by line text flow instead of absolute y coordinate Edouard@3090: Edouard@3090: next.removeAttribute("y"); Edouard@3090: Edouard@3090: next.setAttribute("dy", "1.1em"); Edouard@3090: Edouard@3090: // default content to allow computing text element bbox Edouard@3090: Edouard@3090: next.textContent = "..."; Edouard@3090: Edouard@3090: // append new span to text element Edouard@3090: Edouard@3090: txt.appendChild(next); Edouard@3090: Edouard@3090: // now check if text extended by one row fits to page Edouard@3090: Edouard@3090: // FIXME : exclude margins to be more accurate on box size Edouard@3090: Edouard@3090: let rect = txt.getBoundingClientRect(); Edouard@3090: Edouard@3090: if(rect.bottom > bounds.bottom){ Edouard@3090: Edouard@3090: // in case of overflow at the bottom, lift up one row Edouard@3090: Edouard@3090: let backup = first.getAttribute("dy"); Edouard@3090: Edouard@3092: // apply lift as a dy added too first span (y attrib stays) Edouard@3090: Edouard@3090: first.setAttribute("dy", "-"+String((this.lift+1)*1.1)+"em"); Edouard@3090: Edouard@3090: rect = txt.getBoundingClientRect(); Edouard@3090: Edouard@3090: if(rect.top > bounds.top){ Edouard@3090: Edouard@3090: this.lift += 1; Edouard@3090: Edouard@3090: } else { Edouard@3090: Edouard@3090: // if it goes over the top, then backtrack Edouard@3090: Edouard@3090: // restore dy attribute on first span Edouard@3090: Edouard@3090: if(backup) Edouard@3090: Edouard@3090: first.setAttribute("dy", backup); Edouard@3090: Edouard@3090: else Edouard@3090: Edouard@3090: first.removeAttribute("dy"); Edouard@3090: Edouard@3090: // remove unwanted child Edouard@3090: Edouard@3090: txt.removeChild(next); Edouard@3090: Edouard@3090: return count; Edouard@3090: Edouard@3090: } Edouard@3090: Edouard@3090: } Edouard@3090: Edouard@3090: count++; Edouard@3090: Edouard@3090: } Edouard@3090: Edouard@3090: return count; Edouard@3090: Edouard@3090: } Edouard@3090: Edouard@3090: close_on_click_elsewhere(e) { Edouard@3090: Edouard@3090: // inhibit events not targetting spans (menu items) Edouard@3090: Edouard@3091: if([this.text_elt, this.element].indexOf(e.target.parentNode) == -1){ Edouard@3090: Edouard@3090: e.stopPropagation(); Edouard@3090: Edouard@3090: // close menu in case click is outside box Edouard@3090: Edouard@3090: if(e.target !== this.box_elt) Edouard@3090: Edouard@3090: this.close(); Edouard@3090: Edouard@3090: } Edouard@3090: Edouard@3090: } Edouard@3090: Edouard@3090: close(){ Edouard@3090: Edouard@3090: // Stop hogging all click events Edouard@3090: edouard@3238: svg_root.removeEventListener("pointerdown", this.numb_event, true); edouard@3238: edouard@3238: svg_root.removeEventListener("pointerup", this.numb_event, true); Edouard@3091: Edouard@3090: svg_root.removeEventListener("click", this.bound_close_on_click_elsewhere, true); Edouard@3090: Edouard@3090: // Restore position and sixe of widget elements Edouard@3090: Edouard@3090: this.reset_text(); Edouard@3090: Edouard@3091: this.reset_clickables(); Edouard@3091: Edouard@3090: this.reset_box(); Edouard@3090: Edouard@3092: this.reset_highlight(); Edouard@3092: Edouard@3090: // Put the button back in place Edouard@3090: Edouard@3090: this.element.appendChild(this.button_elt); Edouard@3090: Edouard@3090: // Mark as closed (to allow dispatch) Edouard@3090: Edouard@3090: this.opened = false; Edouard@3090: Edouard@3090: // Dispatch last cached value Edouard@3090: Edouard@3090: this.apply_cache(); Edouard@3090: Edouard@3090: } Edouard@3090: Edouard@3091: // Make item (text span) clickable by overlaying a rectangle on top of it Edouard@3091: Edouard@3091: make_clickable(span, func) { Edouard@3091: Edouard@3091: let txt = this.text_elt; Edouard@3091: Edouard@3092: let original_text_y = this.text_bbox.y; Edouard@3092: Edouard@3092: let highlight = this.highlight_elt; Edouard@3092: Edouard@3092: let original_h_y = this.highlight_bbox.y; Edouard@3092: Edouard@3092: let clickable = highlight.cloneNode(); Edouard@3092: Edouard@3092: let yoffset = span.getBBox().y - original_text_y; Edouard@3092: Edouard@3092: clickable.y.baseVal.value = original_h_y + yoffset; Edouard@3092: Edouard@3092: clickable.style.pointerEvents = "bounding-box"; Edouard@3092: Edouard@3092: //clickable.style.visibility = "hidden"; Edouard@3092: Edouard@3092: //clickable.onclick = () => alert("love JS"); Edouard@3092: Edouard@3092: clickable.onclick = func; Edouard@3092: Edouard@3092: this.element.appendChild(clickable); Edouard@3092: Edouard@3092: this.clickables.push(clickable) Edouard@3092: Edouard@3092: } Edouard@3092: Edouard@3092: reset_clickables() { Edouard@3092: Edouard@3092: while(this.clickables.length){ Edouard@3092: Edouard@3092: this.element.removeChild(this.clickables.pop()); Edouard@3092: Edouard@3092: } Edouard@3092: Edouard@3092: } Edouard@3092: Edouard@3092: // Set text content when content is smaller than menu (no scrolling) Edouard@3092: Edouard@3092: set_complete_text(){ Edouard@3092: Edouard@3092: let spans = this.text_elt.children; Edouard@3092: Edouard@3092: let c = 0; Edouard@3092: Edouard@3092: for(let item of this.content){ Edouard@3092: Edouard@3092: let span=spans[c]; Edouard@3092: edouard@3355: span.textContent = gettext(item); Edouard@3092: Edouard@3092: let sel = c; Edouard@3092: Edouard@3092: this.make_clickable(span, (evt) => this.bound_on_selection_click(sel)); Edouard@3092: Edouard@3092: c++; Edouard@3092: Edouard@3092: } Edouard@3092: Edouard@3092: } Edouard@3092: Edouard@3092: // Move partial view : Edouard@3092: Edouard@3092: // false : upward, lower value Edouard@3092: Edouard@3092: // true : downward, higher value Edouard@3092: Edouard@3092: scroll(forward){ Edouard@3092: Edouard@3092: let contentlength = this.content.length; Edouard@3092: Edouard@3092: let spans = this.text_elt.children; Edouard@3092: Edouard@3092: let spanslength = spans.length; Edouard@3092: Edouard@3092: // reduce accounted menu size according to prsence of scroll buttons Edouard@3092: Edouard@3092: // since we scroll there is necessarly one button Edouard@3092: Edouard@3092: spanslength--; Edouard@3092: Edouard@3092: if(forward){ Edouard@3092: Edouard@3092: // reduce accounted menu size because of back button Edouard@3092: edouard@3094: // in current view edouard@3094: edouard@3094: if(this.menu_offset > 0) spanslength--; Edouard@3092: Edouard@3092: this.menu_offset = Math.min( Edouard@3092: Edouard@3092: contentlength - spans.length + 1, Edouard@3092: Edouard@3092: this.menu_offset + spanslength); Edouard@3092: Edouard@3092: }else{ Edouard@3092: edouard@3094: // reduce accounted menu size because of back button edouard@3094: edouard@3094: // in view once scrolled edouard@3094: Edouard@3092: if(this.menu_offset - spanslength > 0) spanslength--; Edouard@3092: Edouard@3092: this.menu_offset = Math.max( Edouard@3092: Edouard@3092: 0, Edouard@3092: Edouard@3092: this.menu_offset - spanslength); Edouard@3092: Edouard@3092: } Edouard@3092: Edouard@3092: if(this.menu_offset == 1) Edouard@3092: Edouard@3092: this.menu_offset = 0; Edouard@3092: Edouard@3092: Edouard@3092: Edouard@3092: this.reset_highlight(); Edouard@3092: Edouard@3092: Edouard@3092: Edouard@3092: this.reset_clickables(); Edouard@3092: Edouard@3092: this.set_partial_text(); Edouard@3092: Edouard@3092: Edouard@3092: Edouard@3092: this.highlight_selection(); Edouard@3092: Edouard@3092: } Edouard@3092: Edouard@3092: // Setup partial view text content Edouard@3092: Edouard@3092: // with jumps at first and last entry when appropriate Edouard@3092: Edouard@3092: set_partial_text(){ Edouard@3092: Edouard@3092: let spans = this.text_elt.children; Edouard@3092: Edouard@3092: let contentlength = this.content.length; Edouard@3092: Edouard@3092: let spanslength = spans.length; Edouard@3092: Edouard@3092: let i = this.menu_offset, c = 0; Edouard@3092: Edouard@3092: let m = this.box_bbox; Edouard@3092: Edouard@3092: while(c < spanslength){ Edouard@3092: Edouard@3092: let span=spans[c]; Edouard@3092: Edouard@3092: let onclickfunc; Edouard@3092: Edouard@3092: // backward jump only present if not exactly at start Edouard@3092: Edouard@3092: if(c == 0 && i != 0){ Edouard@3092: Edouard@3092: span.textContent = "▲"; Edouard@3092: Edouard@3092: onclickfunc = this.bound_on_backward_click; Edouard@3092: Edouard@3092: let o = span.getBBox(); Edouard@3092: Edouard@3092: span.setAttribute("dx", (m.width - o.width)/2); Edouard@3092: Edouard@3092: // presence of forward jump when not right at the end Edouard@3092: Edouard@3092: }else if(c == spanslength-1 && i < contentlength - 1){ Edouard@3092: Edouard@3092: span.textContent = "▼"; Edouard@3092: Edouard@3092: onclickfunc = this.bound_on_forward_click; Edouard@3092: Edouard@3092: let o = span.getBBox(); Edouard@3092: Edouard@3092: span.setAttribute("dx", (m.width - o.width)/2); Edouard@3092: Edouard@3092: // otherwise normal content Edouard@3092: Edouard@3092: }else{ Edouard@3092: edouard@3355: span.textContent = gettext(this.content[i]); Edouard@3092: Edouard@3092: let sel = i; Edouard@3092: Edouard@3092: onclickfunc = (evt) => this.bound_on_selection_click(sel); Edouard@3092: Edouard@3092: span.removeAttribute("dx"); Edouard@3092: Edouard@3092: i++; Edouard@3092: Edouard@3092: } Edouard@3092: Edouard@3092: this.make_clickable(span, onclickfunc); Edouard@3092: Edouard@3092: c++; Edouard@3092: Edouard@3092: } Edouard@3092: Edouard@3092: } Edouard@3092: edouard@3238: numb_event(e) { edouard@3238: edouard@3238: e.stopPropagation(); edouard@3238: edouard@3238: } edouard@3238: Edouard@3092: open(){ Edouard@3092: Edouard@3092: let length = this.content.length; Edouard@3092: Edouard@3092: // systematically reset text, to strip eventual whitespace spans Edouard@3092: Edouard@3092: this.reset_text(); Edouard@3092: Edouard@3092: // grow as much as needed or possible Edouard@3092: Edouard@3092: let slots = this.grow_text(length); Edouard@3092: Edouard@3092: // Depending on final size Edouard@3092: Edouard@3092: if(slots == length) { Edouard@3092: Edouard@3092: // show all at once Edouard@3092: Edouard@3092: this.set_complete_text(); Edouard@3092: Edouard@3092: } else { Edouard@3092: Edouard@3092: // eventualy align menu to current selection, compensating for lift Edouard@3092: Edouard@3092: let offset = this.last_selection - this.lift; Edouard@3092: Edouard@3092: if(offset > 0) Edouard@3092: Edouard@3092: this.menu_offset = Math.min(offset + 1, length - slots + 1); Edouard@3092: Edouard@3092: else Edouard@3092: Edouard@3092: this.menu_offset = 0; Edouard@3092: Edouard@3092: // show surrounding values Edouard@3092: Edouard@3092: this.set_partial_text(); Edouard@3092: Edouard@3092: } Edouard@3092: Edouard@3092: // Now that text size is known, we can set the box around it Edouard@3092: Edouard@3092: this.adjust_box_to_text(); Edouard@3092: Edouard@3092: // Take button out until menu closed Edouard@3092: Edouard@3092: this.element.removeChild(this.button_elt); Edouard@3092: Edouard@3092: // Rise widget to top by moving it to last position among siblings Edouard@3092: Edouard@3092: this.element.parentNode.appendChild(this.element.parentNode.removeChild(this.element)); Edouard@3092: Edouard@3092: // disable interaction with background Edouard@3092: edouard@3238: svg_root.addEventListener("pointerdown", this.numb_event, true); edouard@3238: edouard@3238: svg_root.addEventListener("pointerup", this.numb_event, true); Edouard@3092: Edouard@3092: svg_root.addEventListener("click", this.bound_close_on_click_elsewhere, true); Edouard@3092: Edouard@3092: this.highlight_selection(); Edouard@3092: Edouard@3092: Edouard@3092: Edouard@3092: // mark as open Edouard@3092: Edouard@3092: this.opened = true; Edouard@3092: Edouard@3092: } Edouard@3092: Edouard@3092: // Put text element in normalized state Edouard@3092: Edouard@3092: reset_text(){ Edouard@3092: Edouard@3092: let txt = this.text_elt; Edouard@3092: Edouard@3091: let first = txt.firstElementChild; Edouard@3091: Edouard@3092: // remove attribute eventually added to first text line while opening Edouard@3092: Edouard@3092: first.onclick = null; Edouard@3092: Edouard@3092: first.removeAttribute("dy"); Edouard@3092: Edouard@3092: first.removeAttribute("dx"); Edouard@3092: Edouard@3092: // keep only the first line of text Edouard@3092: Edouard@3092: for(let span of Array.from(txt.children).slice(1)){ Edouard@3092: Edouard@3092: txt.removeChild(span) Edouard@3092: Edouard@3092: } Edouard@3092: Edouard@3092: } Edouard@3092: Edouard@3092: // Put rectangle element in saved original state Edouard@3092: Edouard@3092: reset_box(){ Edouard@3092: Edouard@3092: let m = this.box_bbox; Edouard@3092: Edouard@3092: let b = this.box_elt; Edouard@3092: Edouard@3092: b.x.baseVal.value = m.x; Edouard@3092: Edouard@3092: b.y.baseVal.value = m.y; Edouard@3092: Edouard@3092: b.width.baseVal.value = m.width; Edouard@3092: Edouard@3092: b.height.baseVal.value = m.height; Edouard@3092: Edouard@3092: } Edouard@3092: Edouard@3092: highlight_selection(){ Edouard@3092: edouard@3128: if(this.last_selection == undefined) return; edouard@3128: Edouard@3092: let highlighted_row = this.last_selection - this.menu_offset; Edouard@3092: Edouard@3092: if(highlighted_row < 0) return; Edouard@3092: Edouard@3092: let spans = this.text_elt.children; Edouard@3092: Edouard@3092: let spanslength = spans.length; Edouard@3092: Edouard@3092: let contentlength = this.content.length; Edouard@3092: Edouard@3092: if(this.menu_offset != 0) { Edouard@3092: Edouard@3092: spanslength--; Edouard@3092: Edouard@3092: highlighted_row++; Edouard@3092: Edouard@3092: } Edouard@3092: Edouard@3092: if(this.menu_offset + spanslength < contentlength - 1) spanslength--; Edouard@3092: Edouard@3092: if(highlighted_row > spanslength) return; Edouard@3092: Edouard@3091: let original_text_y = this.text_bbox.y; Edouard@3091: Edouard@3091: let highlight = this.highlight_elt; Edouard@3091: Edouard@3092: let span = spans[highlighted_row]; Edouard@3091: Edouard@3091: let yoffset = span.getBBox().y - original_text_y; Edouard@3091: Edouard@3092: highlight.y.baseVal.value = this.highlight_bbox.y + yoffset; Edouard@3092: Edouard@3092: highlight.style.visibility = "visible"; Edouard@3092: Edouard@3092: } Edouard@3092: Edouard@3092: reset_highlight(){ Edouard@3092: Edouard@3092: let highlight = this.highlight_elt; Edouard@3092: Edouard@3092: highlight.y.baseVal.value = this.highlight_bbox.y; Edouard@3092: Edouard@3092: highlight.style.visibility = "hidden"; Edouard@3090: Edouard@3090: } Edouard@3090: Edouard@3090: // Use margin and text size to compute box size Edouard@3090: Edouard@3090: adjust_box_to_text(){ Edouard@3090: Edouard@3090: let [lmargin, tmargin] = this.margins; Edouard@3090: Edouard@3090: let m = this.text_elt.getBBox(); Edouard@3090: Edouard@3090: let b = this.box_elt; Edouard@3090: Edouard@3091: // b.x.baseVal.value = m.x - lmargin; Edouard@3090: Edouard@3090: b.y.baseVal.value = m.y - tmargin; Edouard@3090: Edouard@3091: // b.width.baseVal.value = 2 * lmargin + m.width; Edouard@3090: Edouard@3090: b.height.baseVal.value = 2 * tmargin + m.height; Edouard@3090: Edouard@3090: } Edouard@3090: edouard@3238: } edouard@3238: edouard@3238: edouard@3238: Edouard@2922: Edouard@2922: Edouard@2922: Edouard@2922: edouard@3355: box button highlight Edouard@2922: Edouard@2922: edouard@3355: edouard@3355: init_specific: function() { edouard@3355: edouard@3134: edouard@3134: edouard@3355: this.text_elt = id(" edouard@3355: edouard@3355: "); edouard@3355: edouard@3355: this.content = langs; edouard@3355: edouard@3355: edouard@3355: edouard@3355: edouard@3355: edouard@3355: No argrument for HMI:DropDown widget id=" edouard@3355: edouard@3355: " and "text" labeled element is not a svg:use element edouard@3355: edouard@3355: edouard@3355: edouard@3355: this.text_elt = id(" edouard@3355: edouard@3355: "); edouard@3355: edouard@3355: edouard@3355: edouard@3355: edouard@3355: edouard@3355: HMI:DropDown widget id=" edouard@3355: edouard@3355: " "text" labeled element does not point to a svg:text owned by a HMI:List widget edouard@3355: edouard@3355: edouard@3355: this.content = hmi_widgets[" edouard@3355: edouard@3355: "].texts; edouard@3355: edouard@3134: edouard@3134: edouard@3355: this.text_elt = id(" edouard@3355: edouard@3355: "); edouard@3355: edouard@3355: this.content = [ edouard@3134: edouard@3134: edouard@3134: " edouard@3134: edouard@3134: ", edouard@3134: edouard@3134: edouard@3355: ]; edouard@3355: edouard@3134: edouard@3134: edouard@3355: } edouard@3355: edouard@3355: edouard@3355: edouard@3355: edouard@3355: edouard@3355: edouard@3355: /* edouard@3355: edouard@3355: */ edouard@3355: edouard@3355: edouard@3355: edouard@3355: function gettext(o) { edouard@3355: edouard@3355: if(typeof(o) == "string"){ edouard@3355: edouard@3355: return o; edouard@3355: edouard@3355: } edouard@3355: edouard@3355: return svg_text_to_multiline(o); edouard@3355: edouard@3355: }; edouard@3355: edouard@3355: Edouard@2922: Edouard@2922: edouard@3241: edouard@3241: edouard@3241: edouard@3241: edouard@3241: edouard@3241: ForEach widget is used to span a small set of widget over a larger set of edouard@3241: edouard@3241: repeated HMI_NODEs. edouard@3241: edouard@3241: edouard@3241: edouard@3241: Idea is somewhat similar to relative page, but it all happens inside the edouard@3241: edouard@3241: ForEach widget, no page involved. edouard@3241: edouard@3241: edouard@3241: edouard@3241: Together with relative Jump widgets it can be used to build a menu to reach edouard@3241: edouard@3241: relative pages covering many identical HMI_NODES siblings. edouard@3241: edouard@3241: edouard@3241: edouard@3241: ForEach widget takes a HMI_CLASS name as argument and a HMI_NODE path as edouard@3241: edouard@3241: variable. edouard@3241: edouard@3241: edouard@3241: edouard@3241: Direct sub-elements can be either groups of widget to be spanned, labeled edouard@3241: edouard@3241: "ClassName:offset", or buttons to control the spanning, labeled edouard@3241: edouard@3241: "ClassName:+/-number". edouard@3241: edouard@3241: edouard@3241: edouard@3241: span widgets over a set of repeated HMI_NODEs edouard@3241: edouard@3241: edouard@3241: HMI_CLASS name edouard@3241: edouard@3241: edouard@3241: where to find HMI_NODEs whose HMI_CLASS is class_name edouard@3241: edouard@3241: edouard@3238: edouard@2892: edouard@3005: edouard@3005: edouard@3005: ForEach widget edouard@3005: edouard@3005: must have one HMI path given. edouard@3005: edouard@3005: edouard@3005: edouard@3005: edouard@3005: ForEach widget edouard@3005: edouard@3005: must have one argument given : a class name. edouard@3005: edouard@3005: edouard@2894: edouard@2894: edouard@2894: edouard@2894: edouard@2894: edouard@2894: edouard@2894: Edouard@2893: index_pool: [ Edouard@2893: edouard@2894: edouard@2894: edouard@2894: edouard@2894: edouard@2894: , edouard@2894: edouard@2894: edouard@2894: edouard@2894: Edouard@2893: ], Edouard@2893: edouard@2896: init: function() { Edouard@2893: Edouard@2893: Edouard@2893: edouard@2896: edouard@2896: edouard@2896: edouard@2896: id(" Edouard@2893: edouard@2896: ").setAttribute("onclick", "hmi_widgets[' edouard@2896: edouard@2896: '].on_click(' edouard@2896: edouard@2896: ', evt)"); Edouard@2893: Edouard@2893: edouard@2896: Edouard@2893: Edouard@2895: this.items = [ Edouard@2893: Edouard@2893: Edouard@2893: Edouard@2893: Edouard@2893: Edouard@2893: edouard@2894: edouard@2894: Edouard@2895: [ /* item=" Edouard@2893: edouard@2894: " path=" edouard@2894: edouard@2894: " */ edouard@2892: Edouard@2893: Edouard@2893: Edouard@2893: Missing item labeled Edouard@2893: Edouard@2893: in ForEach widget Edouard@2893: Edouard@2893: Edouard@2893: Edouard@2893: edouard@2894: edouard@2894: edouard@2894: Widget id=" edouard@2894: edouard@2894: " label=" edouard@2894: edouard@2894: " is having wrong path. Accroding to ForEach widget ancestor id=" edouard@2894: edouard@2896: ", path should be descendant of " edouard@2894: edouard@2896: ". edouard@2894: edouard@2894: Edouard@2895: hmi_widgets[" edouard@2892: edouard@2892: "] edouard@2892: edouard@2892: , edouard@2892: edouard@2892: edouard@2892: edouard@2892: Edouard@2895: ] edouard@2892: edouard@2892: , edouard@2892: edouard@2892: edouard@2892: edouard@2892: Edouard@2895: ] Edouard@2895: Edouard@2895: }, Edouard@2895: Edouard@2895: item_offset: 0, edouard@2892: edouard@2954: edouard@3238: edouard@3238: class edouard@3238: ForEachWidget edouard@3238: extends Widget{ edouard@2954: edouard@3005: edouard@3005: edouard@3005: unsub_items(){ edouard@3005: edouard@3005: for(let item of this.items){ edouard@3005: edouard@3005: for(let widget of item) { edouard@3005: edouard@3005: widget.unsub(); edouard@3005: edouard@3005: } edouard@3005: edouard@3005: } edouard@3005: edouard@3005: } edouard@3005: edouard@3005: edouard@3005: edouard@2954: unsub(){ edouard@2954: edouard@3005: this.unsub_items(); edouard@3005: edouard@3005: this.offset = 0; edouard@3005: edouard@3005: this.relativeness = undefined; edouard@3005: edouard@3005: } edouard@3005: edouard@3005: edouard@3005: edouard@3005: sub_items(){ edouard@3005: edouard@3005: for(let i = 0; i < this.items.length; i++) { edouard@3005: edouard@3005: let item = this.items[i]; edouard@3005: edouard@3005: let orig_item_index = this.index_pool[i]; edouard@3005: edouard@3005: let item_index = this.index_pool[i+this.item_offset]; edouard@3005: edouard@3005: let item_index_offset = item_index - orig_item_index; edouard@3005: edouard@3005: if(this.relativeness[0]) edouard@3005: edouard@3005: item_index_offset += this.offset; edouard@2954: edouard@2954: for(let widget of item) { edouard@2954: edouard@3005: /* all variables of all widgets in a ForEach are all relative. edouard@3005: edouard@3005: Really. edouard@3005: edouard@3005: edouard@3005: edouard@3005: TODO: allow absolute variables in ForEach widgets edouard@3005: edouard@3005: */ edouard@3005: edouard@3005: widget.sub(item_index_offset, widget.indexes.map(_=>true)); edouard@2954: edouard@2954: } edouard@2942: edouard@2942: } edouard@2942: edouard@2942: } edouard@2942: edouard@2954: edouard@2954: edouard@3005: sub(new_offset=0, relativeness=[]){ edouard@3005: edouard@3005: this.offset = new_offset; edouard@3005: edouard@3005: this.relativeness = relativeness; edouard@3005: edouard@3005: this.sub_items(); edouard@3005: edouard@3005: } edouard@3005: edouard@3005: edouard@3005: edouard@3005: apply_cache() { edouard@3005: edouard@3005: this.items.forEach(item=>item.forEach(widget=>widget.apply_cache())); edouard@3005: edouard@3005: } edouard@3005: edouard@3005: edouard@3005: edouard@3005: on_click(opstr, evt) { edouard@3005: edouard@3005: let new_item_offset = eval(String(this.item_offset)+opstr); edouard@3005: edouard@3005: if(new_item_offset + this.items.length > this.index_pool.length) { edouard@3005: edouard@3005: if(this.item_offset + this.items.length == this.index_pool.length) edouard@3005: edouard@3005: new_item_offset = 0; edouard@3005: edouard@3005: else edouard@3005: edouard@3005: new_item_offset = this.index_pool.length - this.items.length; edouard@3005: edouard@3005: } else if(new_item_offset < 0) { edouard@3005: edouard@3005: if(this.item_offset == 0) edouard@3005: edouard@3005: new_item_offset = this.index_pool.length - this.items.length; edouard@3005: edouard@3005: else edouard@3005: edouard@3005: new_item_offset = 0; edouard@3003: edouard@3003: } edouard@3003: edouard@2954: this.item_offset = new_item_offset; edouard@2954: edouard@3005: this.unsub_items(); edouard@3005: edouard@3005: this.sub_items(); edouard@2954: edouard@2954: update_subscriptions(); edouard@2954: edouard@2954: need_cache_apply.push(this); edouard@2954: edouard@2954: jumps_need_update = true; edouard@2954: edouard@2954: requestHMIAnimation(); edouard@2954: edouard@2954: } edouard@2942: edouard@2942: } edouard@2942: edouard@2942: edouard@3238: edouard@3238: edouard@3238: edouard@3238: edouard@3238: edouard@3238: Input widget takes one variable path, and displays current value in edouard@3238: edouard@3241: optional "value" labeled sub-element. edouard@3241: edouard@3241: edouard@3241: edouard@3241: Click on optional "edit" labeled element opens keypad to edit value. edouard@3241: edouard@3241: edouard@3241: edouard@3241: Operation on current value is performed when click on sub-elements with edouard@3241: edouard@3241: label starting with '=', '+' or '-' sign. Value after sign is used as edouard@3241: edouard@3241: operand. edouard@3238: edouard@3238: edouard@3238: edouard@3238: Input field with predefined operation buttons edouard@3238: edouard@3241: edouard@3238: optional printf-like format edouard@3238: edouard@3241: edouard@3238: single variable to edit edouard@3238: edouard@3238: edouard@3238: edouard@3238: class edouard@3238: InputWidget edouard@3238: extends Widget{ edouard@3238: edouard@3238: on_op_click(opstr) { edouard@3238: edouard@3238: this.change_hmi_value(0, opstr); edouard@3238: edouard@3238: } edouard@3238: edouard@3238: edit_callback(new_val) { edouard@3238: edouard@3238: this.apply_hmi_value(0, new_val); edouard@3238: edouard@3238: } edouard@3238: edouard@3238: edouard@3238: edouard@3238: is_inhibited = false; edouard@3238: edouard@3238: alert(msg){ edouard@3238: edouard@3238: this.is_inhibited = true; edouard@3238: edouard@3238: this.display = msg; edouard@3238: edouard@3238: setTimeout(() => this.stopalert(), 1000); edouard@3238: edouard@3238: this.request_animate(); edouard@3238: edouard@3238: } edouard@3238: edouard@3238: edouard@3238: edouard@3238: stopalert(){ edouard@3238: edouard@3238: this.is_inhibited = false; edouard@3238: edouard@3238: this.display = this.last_value; edouard@3238: edouard@3238: this.request_animate(); edouard@3238: edouard@3238: } edouard@3238: edouard@3238: edouard@3238: edouard@3238: overshot(new_val, max) { edouard@3238: edouard@3238: this.alert("max"); edouard@3238: edouard@3238: } edouard@3238: edouard@3238: edouard@3238: edouard@3238: undershot(new_val, min) { edouard@3238: edouard@3238: this.alert("min"); edouard@3238: edouard@3238: } edouard@3238: edouard@3238: } edouard@3238: edouard@3238: edouard@3238: Edouard@2801: Edouard@2836: Edouard@2836: Edouard@2836: Edouard@2836: Edouard@2836: value Edouard@2836: Edouard@2836: Edouard@2836: Edouard@2836: Edouard@2861: Edouard@2836: edouard@3094: edouard@3094: edouard@3094: edouard@3094: edouard@3094: edit edouard@3094: edouard@3094: edouard@3094: edouard@3094: edouard@3094: edouard@3094: Edouard@2861: edouard@2851: frequency: 5, Edouard@2836: Edouard@2836: edouard@2851: dispatch: function(value) { Edouard@2801: edouard@3155: edouard@3154: edouard@3154: Edouard@3188: this.last_value = vsprintf(" edouard@3154: edouard@3155: ", [value]); edouard@3154: edouard@3154: edouard@3154: Edouard@3188: this.last_value = value; edouard@3154: edouard@3154: edouard@3154: Edouard@3188: if(!this.is_inhibited){ Edouard@3188: Edouard@3188: this.display = this.last_value; Edouard@3188: Edouard@3188: Edouard@3188: this.request_animate(); Edouard@3188: Edouard@3188: Edouard@3188: } Edouard@2836: Edouard@2836: edouard@2851: }, Edouard@2801: edouard@3103: edouard@3103: animate: function(){ edouard@3103: Edouard@3188: this.value_elt.textContent = String(this.display); edouard@3103: edouard@3103: }, edouard@3103: edouard@3103: edouard@2851: init: function() { Edouard@2801: edouard@3094: Edouard@3118: this.edit_elt.onclick = () => edit_value(" edouard@3094: edouard@3094: ", " edouard@3094: Edouard@3188: ", this, this.last_value); Edouard@2801: edouard@3154: edouard@3154: this.value_elt.style.pointerEvents = "none"; edouard@3154: edouard@3154: Edouard@2801: Edouard@2829: edouard@2851: id(" Edouard@2801: edouard@3094: ").onclick = () => this.on_op_click(" Edouard@2829: edouard@3094: "); Edouard@2801: Edouard@2801: edouard@2851: }, Edouard@2801: Edouard@2801: edouard@3241: edouard@3241: edouard@3241: edouard@3241: edouard@3241: edouard@3241: Send given variables as POST to http URL argument, spread returned JSON in edouard@3241: edouard@3241: SVG sub-elements of "data" labeled element. edouard@3241: edouard@3241: edouard@3241: Edouard@3387: Documentation to be written. see svghmi exemple. edouard@3241: edouard@3241: edouard@3241: edouard@3241: Http POST variables, spread JSON back edouard@3241: edouard@3241: edouard@3241: edouard@3241: edouard@3241: edouard@3241: single variable to edit edouard@3241: edouard@3241: edouard@3238: edouard@3238: class edouard@3238: JsonTableWidget edouard@3238: extends Widget{ edouard@2994: edouard@3069: // arbitrary defaults to avoid missing entries in query edouard@3069: Edouard@3187: cache = [0,0,0]; Edouard@3034: edouard@3181: init_common() { Edouard@3080: Edouard@3080: this.spread_json_data_bound = this.spread_json_data.bind(this); Edouard@3080: Edouard@3150: this.handle_http_response_bound = this.handle_http_response.bind(this); Edouard@3150: Edouard@3150: this.fetch_error_bound = this.fetch_error.bind(this); Edouard@3150: Edouard@3150: this.promised = false; Edouard@3150: Edouard@3080: } Edouard@3080: Edouard@3080: Edouard@3080: Edouard@3080: handle_http_response(response) { Edouard@3080: Edouard@3080: if (!response.ok) { Edouard@3080: Edouard@3080: console.log("HTTP error, status = " + response.status); Edouard@3080: Edouard@3080: } Edouard@3080: Edouard@3080: return response.json(); Edouard@3080: Edouard@3080: } Edouard@3080: Edouard@3080: Edouard@3080: Edouard@3150: fetch_error(e){ Edouard@3150: Edouard@3150: console.log("HTTP fetch error, message = " + e.message + "Widget:" + this.element_id); Edouard@3150: Edouard@3150: } Edouard@3150: Edouard@3150: Edouard@3150: Edouard@3048: do_http_request(...opt) { edouard@2996: Edouard@3150: this.abort_controller = new AbortController(); Edouard@3150: Edouard@3195: return Promise.resolve().then(() => { Edouard@3195: Edouard@3195: Edouard@3195: Edouard@3195: const query = { Edouard@3195: Edouard@3195: args: this.args, Edouard@3195: Edouard@3195: range: this.cache[1], Edouard@3195: Edouard@3195: position: this.cache[2], Edouard@3195: Edouard@3195: visible: this.visible, Edouard@3195: Edouard@3195: extra: this.cache.slice(4), Edouard@3195: Edouard@3195: options: opt Edouard@3195: Edouard@3195: }; Edouard@3195: Edouard@3195: Edouard@3195: Edouard@3195: const options = { Edouard@3195: Edouard@3195: method: 'POST', Edouard@3195: Edouard@3195: body: JSON.stringify(query), Edouard@3195: Edouard@3195: headers: {'Content-Type': 'application/json'}, Edouard@3195: Edouard@3195: signal: this.abort_controller.signal Edouard@3195: Edouard@3195: }; Edouard@3195: Edouard@3195: Edouard@3195: Edouard@3195: return fetch(this.args[0], options) Edouard@3195: Edouard@3195: .then(this.handle_http_response_bound) Edouard@3195: Edouard@3195: .then(this.spread_json_data_bound) Edouard@3195: Edouard@3195: .catch(this.fetch_error_bound); Edouard@3195: Edouard@3195: }); Edouard@3195: Edouard@3195: } Edouard@3195: Edouard@3195: Edouard@3150: Edouard@3150: unsub(){ Edouard@3150: Edouard@3150: this.abort_controller.abort(); Edouard@3150: Edouard@3150: super.unsub(); Edouard@3150: Edouard@3150: } Edouard@3150: Edouard@3150: edouard@2996: Edouard@3189: sub(...args){ Edouard@3189: Edouard@3189: this.cache[0] = undefined; Edouard@3189: Edouard@3189: super.sub(...args); Edouard@3189: Edouard@3189: } Edouard@3189: Edouard@3189: Edouard@3189: Edouard@3034: dispatch(value, oldval, index) { Edouard@3034: Edouard@3150: Edouard@3150: Edouard@3150: if(this.cache[index] != value) Edouard@3150: Edouard@3150: this.cache[index] = value; Edouard@3150: Edouard@3150: else Edouard@3150: Edouard@3150: return; Edouard@3150: Edouard@3150: Edouard@3150: Edouard@3150: if(!this.promised){ Edouard@3150: Edouard@3150: this.promised = true; Edouard@3150: Edouard@3150: this.do_http_request().finally(() => { Edouard@3150: Edouard@3150: this.promised = false; Edouard@3150: Edouard@3150: }); Edouard@3150: Edouard@3150: } edouard@2996: edouard@2996: } edouard@2996: Edouard@3080: make_on_click(...options){ Edouard@3080: Edouard@3084: let that = this; Edouard@3084: Edouard@3080: return function(evt){ Edouard@3080: Edouard@3084: that.do_http_request(...options); Edouard@3080: Edouard@3080: } Edouard@3080: Edouard@3080: } Edouard@3080: Edouard@3080: // on_click(evt, ...options) { Edouard@3080: Edouard@3080: // this.do_http_request(...options); Edouard@3080: Edouard@3080: // } edouard@2994: edouard@2994: } edouard@2994: edouard@2994: Edouard@3019: edouard@2997: edouard@2996: JsonTable Widget can't contain element of type edouard@2996: edouard@2996: . edouard@2996: edouard@2996: Edouard@3031: Edouard@3031: Edouard@3031: Edouard@3031: Edouard@3031: Edouard@3031: Edouard@3031: Edouard@3031: Edouard@3031: Edouard@3031: Edouard@3031: Edouard@3031: Edouard@3031: Edouard@3031: Edouard@3031: Edouard@3031: Edouard@3031: Edouard@3031: JsonTable : missplaced '=' or inconsistent names in Json data expressions. Edouard@3031: Edouard@3031: Edouard@3031: Edouard@3031: Edouard@3031: Edouard@3031: Edouard@3031: Edouard@3031: Edouard@3031: Edouard@3031: Edouard@3031: Edouard@3031: Edouard@3031: Edouard@3031: Edouard@3031: Edouard@3031: Edouard@3031: Edouard@3031: Edouard@3031: Edouard@3031: Edouard@3031: Edouard@3031: Edouard@3031: Edouard@3031: Edouard@3031: Edouard@3031: Edouard@3031: Edouard@3031: Edouard@3031: Edouard@3031: Edouard@3031: Edouard@3031: jdata Edouard@3031: Edouard@3031: Edouard@3031: Edouard@3031: Edouard@3019: Edouard@3031: edouard@2997: edouard@2997: usveticic@3045: usveticic@3045: usveticic@3045: id(" usveticic@3045: Edouard@3387: ").href.baseVal = usveticic@3045: usveticic@3045: "#"+hmi_widgets[" usveticic@3045: usveticic@3045: "].items[ Edouard@3031: Edouard@3387: ]; usveticic@3045: usveticic@3045: usveticic@3045: usveticic@3045: Edouard@3031: Clones (svg:use) in JsonTable Widget must point to a valid HMI:List widget or item. Reference " usveticic@3045: usveticic@3045: " is not valid and will not be updated. usveticic@3045: usveticic@3045: usveticic@3045: edouard@2996: Edouard@3019: Edouard@3031: Edouard@3031: Edouard@3031: Edouard@3031: Edouard@3031: Edouard@3031: Edouard@3031: Edouard@3031: Edouard@3031: Edouard@3031: 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: Edouard@3031: { Edouard@3031: Edouard@3031: let elt = id(" Edouard@3031: Edouard@3031: "); Edouard@3031: Edouard@3031: elt.textContent = String( Edouard@3031: Edouard@3031: ); Edouard@3031: Edouard@3031: elt.style = hmi_widgets[" Edouard@3031: Edouard@3031: "].styles[ Edouard@3031: Edouard@3031: ]; Edouard@3031: Edouard@3031: } Edouard@3031: Edouard@3031: Edouard@3031: Edouard@3031: id(" Edouard@3031: Edouard@3031: ").textContent = String( Edouard@3031: Edouard@3031: ); Edouard@3031: Edouard@3031: Edouard@3031: Edouard@3031: Edouard@3031: Edouard@3031: Edouard@3031: Edouard@3031: Edouard@3031: Edouard@3031: Edouard@3031: Edouard@3031: Edouard@3031: Edouard@3031: Edouard@3031: Edouard@3031: Edouard@3031: Edouard@3031: Edouard@3031: Edouard@3043: Edouard@3043: Edouard@3043: Edouard@3043: Edouard@3043: Edouard@3043: Edouard@3043: Edouard@3043: Edouard@3043: Edouard@3043: Edouard@3043: Edouard@3043: Edouard@3019: Edouard@3031: Edouard@3031: Edouard@3043: Edouard@3048: Edouard@3048: Edouard@3048: Edouard@3048: id(" Edouard@3048: Edouard@3080: ").onclick = this.make_on_click(' Edouard@3048: Edouard@3084: ', Edouard@3048: Edouard@3084: ); Edouard@3048: Edouard@3048: Edouard@3019: Edouard@3048: edouard@2996: edouard@2996: Edouard@3019: Edouard@3031: Edouard@3031: Edouard@3043: Edouard@3038: Edouard@3038: obj_ Edouard@3150: Edouard@3038: _ Edouard@3038: Edouard@3038: try { Edouard@3038: Edouard@3038: Edouard@3038: let Edouard@3038: Edouard@3038: Edouard@3038: = Edouard@3038: Edouard@3038: ; Edouard@3038: Edouard@3038: if( Edouard@3038: Edouard@3038: Edouard@3038: == undefined) { Edouard@3038: Edouard@3038: throw null; Edouard@3038: Edouard@3038: } Edouard@3038: Edouard@3038: Edouard@3038: Edouard@3038: Edouard@3038: Edouard@3038: Edouard@3038: Edouard@3038: Edouard@3038: Edouard@3038: Edouard@3038: Edouard@3038: Edouard@3038: Edouard@3038: id(" edouard@2997: Edouard@3080: ").style = " Edouard@3038: Edouard@3080: "; Edouard@3038: Edouard@3043: Edouard@3038: Edouard@3031: edouard@2996: Edouard@3038: } catch(err) { Edouard@3038: Edouard@3038: id(" Edouard@3150: Edouard@3080: ").style = "display:none"; Edouard@3038: Edouard@3038: } Edouard@3038: edouard@2996: edouard@3238: edouard@2996: edouard@2996: edouard@2996: edouard@2996: edouard@2996: data edouard@2996: edouard@2996: edouard@2996: Edouard@3038: visible: Edouard@3038: Edouard@3038: , Edouard@3038: Edouard@3038: spread_json_data: function(janswer) { Edouard@3038: Edouard@3038: let [range,position,jdata] = janswer; Edouard@3038: Edouard@3150: [[1, range], [2, position], [3, this.visible]].map(([i,v]) => { Edouard@3150: Edouard@3150: this.apply_hmi_value(i,v); Edouard@3150: Edouard@3150: this.cache[i] = v; Edouard@3150: Edouard@3150: }); Edouard@3065: Edouard@3043: Edouard@3031: Edouard@3031: edouard@2996: edouard@3181: }, edouard@3181: edouard@3181: init() { edouard@3181: edouard@3181: this.init_common(); edouard@3181: edouard@3181: edouard@3181: id(" edouard@3181: edouard@3181: ").onclick = this.make_on_click(" edouard@3181: edouard@3181: "); edouard@3181: edouard@3181: edouard@2997: } edouard@2996: edouard@2996: edouard@3241: edouard@3241: edouard@3241: edouard@3241: edouard@3241: edouard@3241: Jump widget brings focus to a different page. Mandatory single argument edouard@3241: edouard@3241: gives name of the page. edouard@3241: edouard@3241: edouard@3241: edouard@3241: Optional single path is used as new reference when jumping to a relative edouard@3241: edouard@3241: page, it must point to a HMI_NODE. edouard@3241: edouard@3241: edouard@3241: edouard@3241: "active"+"inactive" labeled elements can be provided and reflect current edouard@3241: edouard@3241: page being shown. edouard@3241: edouard@3241: edouard@3241: edouard@3241: "disabled" labeled element, if provided, is shown instead of "active" or edouard@3241: edouard@3241: "inactive" widget when pointed HMI_NODE is null. edouard@3241: edouard@3241: edouard@3241: edouard@3241: Jump to given page edouard@3241: edouard@3241: edouard@3241: name of page to jump to edouard@3241: edouard@3241: edouard@3241: reference for relative jump edouard@3241: edouard@3241: edouard@3238: edouard@3238: class edouard@3238: JumpWidget edouard@3238: extends Widget{ Edouard@3112: Edouard@3112: activable = false; Edouard@3112: Edouard@3112: active = false; Edouard@3112: Edouard@3112: disabled = false; Edouard@3112: Edouard@3112: frequency = 2; Edouard@3112: Edouard@3112: Edouard@3112: Edouard@3112: update_activity() { Edouard@3112: Edouard@3112: if(this.active) { Edouard@3112: Edouard@3112: /* show active */ Edouard@3112: Edouard@3240: this.active_elt.style.display = ""; Edouard@3112: Edouard@3112: /* hide inactive */ Edouard@3112: Edouard@3240: this.inactive_elt.style.display = "none"; Edouard@3112: Edouard@3112: } else { Edouard@3112: Edouard@3112: /* show inactive */ Edouard@3112: Edouard@3240: this.inactive_elt.style.display = ""; Edouard@3112: Edouard@3112: /* hide active */ Edouard@3112: Edouard@3240: this.active_elt.style.display = "none"; Edouard@3112: Edouard@3112: } Edouard@3112: Edouard@3112: } Edouard@3112: Edouard@3112: Edouard@3112: Edouard@3240: update_disability() { Edouard@3240: Edouard@3240: if(this.disabled) { Edouard@3240: Edouard@3240: /* show disabled */ Edouard@3240: Edouard@3240: this.disabled_elt.style.display = ""; Edouard@3240: Edouard@3240: /* hide inactive */ Edouard@3240: Edouard@3240: this.inactive_elt.style.display = "none"; Edouard@3240: Edouard@3240: /* hide active */ Edouard@3240: Edouard@3240: this.active_elt.style.display = "none"; Edouard@3240: Edouard@3240: } else { Edouard@3240: Edouard@3240: /* hide disabled */ Edouard@3240: Edouard@3240: this.disabled_elt.style.display = "none"; Edouard@3240: Edouard@3240: this.update_activity(); Edouard@3240: Edouard@3240: } Edouard@3240: Edouard@3240: } Edouard@3240: Edouard@3240: Edouard@3240: Edouard@3112: make_on_click() { Edouard@3112: Edouard@3112: let that = this; Edouard@3112: Edouard@3112: const name = this.args[0]; Edouard@3112: Edouard@3112: return function(evt){ Edouard@3112: Edouard@3240: /* TODO: in order to allow jumps to page selected through for exemple a dropdown, Edouard@3240: Edouard@3240: support path pointing to local variable whom value Edouard@3240: Edouard@3240: would be an HMI_TREE index and then jump to a relative page not hard-coded in advance */ Edouard@3240: Edouard@3240: Edouard@3240: Edouard@3240: if(!that.disabled) { Edouard@3240: Edouard@3240: const index = that.indexes.length > 0 ? that.indexes[0] + that.offset : undefined; Edouard@3240: Edouard@3240: switch_page(name, index); Edouard@3240: Edouard@3240: } Edouard@3112: Edouard@3112: } Edouard@3112: Edouard@3112: } Edouard@3112: Edouard@3112: Edouard@3112: Edouard@3112: notify_page_change(page_name, index) { Edouard@3112: Edouard@3112: if(this.activable) { Edouard@3112: Edouard@3112: const ref_index = this.indexes.length > 0 ? this.indexes[0] + this.offset : undefined; Edouard@3112: Edouard@3112: const ref_name = this.args[0]; Edouard@3112: Edouard@3112: this.active = ((ref_name == undefined || ref_name == page_name) && index == ref_index); Edouard@3112: Edouard@3240: this.update_state(); Edouard@3112: Edouard@3112: } Edouard@3112: Edouard@3112: } Edouard@3112: Edouard@3112: Edouard@3112: Edouard@3112: dispatch(value) { Edouard@3112: Edouard@3112: this.disabled = !Number(value); Edouard@3112: Edouard@3240: this.update_state(); Edouard@3112: Edouard@3112: } Edouard@3112: edouard@3238: } edouard@3238: edouard@3238: edouard@3238: edouard@2883: Edouard@2906: Edouard@3112: Edouard@2903: Edouard@3112: Edouard@3112: active inactive Edouard@3112: Edouard@3112: Edouard@2903: Edouard@2903: Edouard@2906: Edouard@2906: Edouard@2906: Edouard@3112: Edouard@2906: Edouard@3112: Edouard@3112: disabled Edouard@3112: Edouard@3112: Edouard@2906: Edouard@2906: Edouard@2906: Edouard@2906: edouard@2883: init: function() { edouard@2883: Edouard@3084: this.element.onclick = this.make_on_click(); edouard@2883: Edouard@2906: Edouard@3112: this.activable = true; Edouard@3112: Edouard@2903: Edouard@3240: Edouard@3240: this.unsubscribable = true; Edouard@3240: Edouard@3240: Edouard@3240: this.update_state = Edouard@2906: Edouard@2906: Edouard@3240: this.update_disability Edouard@3240: Edouard@3240: Edouard@3240: this.update_activity Edouard@2906: Edouard@2906: Edouard@3240: null Edouard@2906: Edouard@2906: Edouard@3240: ; Edouard@3240: edouard@2954: }, edouard@2954: Edouard@2906: edouard@3238: edouard@3238: Edouard@2901: Edouard@2901: Edouard@2901: Edouard@2901: Edouard@2901: Edouard@2901: Edouard@2901: Edouard@2901: Edouard@2901: Edouard@2901: Edouard@2901: Edouard@2901: Edouard@2901: Edouard@2901: Edouard@2901: Edouard@2901: Edouard@2901: Edouard@2901: Edouard@2901: Edouard@2901: Edouard@2901: Edouard@2901: Edouard@2901: Edouard@2901: Edouard@2901: Jump id=" Edouard@2901: Edouard@2901: " to page " Edouard@2901: Edouard@2901: " with incompatible path " Edouard@2901: Edouard@2901: (must be same class as " Edouard@2901: Edouard@2901: ") Edouard@2901: Edouard@2901: Edouard@2901: Edouard@2901: edouard@2943: edouard@2943: edouard@2949: edouard@2949: edouard@2949: /* edouard@2949: edouard@2949: */ edouard@2949: edouard@2949: edouard@2949: edouard@2942: var jumps_need_update = false; edouard@2942: edouard@2942: var jump_history = [[default_page, undefined]]; edouard@2942: edouard@2942: edouard@2942: edouard@2942: function update_jumps() { edouard@2942: edouard@2942: page_desc[current_visible_page].jumps.map(w=>w.notify_page_change(current_visible_page,current_page_index)); edouard@2942: edouard@2942: jumps_need_update = false; edouard@2942: edouard@2942: }; edouard@2942: edouard@2942: edouard@2942: edouard@2949: edouard@2949: edouard@2942: edouard@3241: edouard@3241: edouard@3241: edouard@3241: edouard@3241: edouard@3241: Keypad - to be written edouard@3241: edouard@3241: edouard@3241: edouard@3241: Keypad edouard@3241: edouard@3241: edouard@3241: keypad can input those types edouard@3241: edouard@3241: edouard@2943: edouard@2943: edouard@2941: edouard@2941: edouard@2949: /* edouard@2949: edouard@2949: */ edouard@2949: edouard@2949: edouard@2949: edouard@2949: edouard@2949: edouard@2941: var keypads = { edouard@2941: edouard@2941: edouard@2941: edouard@2941: edouard@2941: edouard@2941: " edouard@2941: edouard@2941: ":[" edouard@2941: edouard@2941: ", edouard@2941: edouard@2941: , edouard@2941: edouard@2941: ], edouard@2941: edouard@2941: edouard@2941: edouard@2941: } edouard@2941: edouard@2949: edouard@2949: edouard@2941: edouard@3238: edouard@3238: class edouard@3238: KeypadWidget edouard@3238: extends Widget{ usveticic@3015: usveticic@3015: on_key_click(symbols) { usveticic@3015: usveticic@3015: var syms = symbols.split(" "); usveticic@3015: usveticic@3015: this.shift |= this.caps; usveticic@3015: usveticic@3015: this.editstr += syms[this.shift?syms.length-1:0]; usveticic@3015: usveticic@3015: this.shift = false; usveticic@3015: usveticic@3015: this.update(); usveticic@3015: usveticic@3015: } usveticic@3015: usveticic@3015: usveticic@3015: usveticic@3015: on_Esc_click() { usveticic@3015: usveticic@3015: end_modal.call(this); usveticic@3015: usveticic@3015: } usveticic@3015: usveticic@3015: usveticic@3015: usveticic@3015: on_Enter_click() { usveticic@3015: Edouard@3034: let coercedval = (typeof this.initial) == "number" ? Number(this.editstr) : this.editstr; Edouard@3034: Edouard@3042: if(typeof coercedval == 'number' && isNaN(coercedval)){ Edouard@3042: Edouard@3042: // revert to initial so it explicitely shows input was ignored Edouard@3034: Edouard@3034: this.editstr = String(this.initial); Edouard@3034: Edouard@3034: this.update(); Edouard@3034: Edouard@3042: } else { Edouard@3034: Edouard@3034: let callback_obj = this.result_callback_obj; Edouard@3034: Edouard@3034: end_modal.call(this); Edouard@3034: Edouard@3034: callback_obj.edit_callback(coercedval); Edouard@3034: Edouard@3034: } usveticic@3015: usveticic@3015: } usveticic@3015: usveticic@3015: usveticic@3015: usveticic@3015: on_BackSpace_click() { usveticic@3015: usveticic@3015: this.editstr = this.editstr.slice(0,this.editstr.length-1); usveticic@3015: usveticic@3015: this.update(); usveticic@3015: usveticic@3015: } usveticic@3015: usveticic@3015: usveticic@3015: usveticic@3015: on_Sign_click() { usveticic@3015: usveticic@3015: if(this.editstr[0] == "-") usveticic@3015: usveticic@3015: this.editstr = this.editstr.slice(1,this.editstr.length); usveticic@3015: usveticic@3015: else usveticic@3015: usveticic@3015: this.editstr = "-" + this.editstr; usveticic@3015: usveticic@3015: this.update(); usveticic@3015: usveticic@3015: } usveticic@3015: usveticic@3015: usveticic@3015: usveticic@3015: on_NumDot_click() { usveticic@3015: usveticic@3015: if(this.editstr.indexOf(".") == "-1"){ usveticic@3015: usveticic@3015: this.editstr += "."; usveticic@3015: usveticic@3015: this.update(); usveticic@3015: usveticic@3015: } usveticic@3015: usveticic@3015: } usveticic@3015: usveticic@3015: usveticic@3015: usveticic@3015: on_Space_click() { usveticic@3015: usveticic@3015: this.editstr += " "; usveticic@3015: usveticic@3015: this.update(); usveticic@3015: usveticic@3015: } usveticic@3015: usveticic@3015: usveticic@3015: usveticic@3015: caps = false; usveticic@3015: usveticic@3015: _caps = undefined; usveticic@3015: usveticic@3015: on_CapsLock_click() { usveticic@3015: usveticic@3015: this.caps = !this.caps; usveticic@3015: usveticic@3015: this.update(); usveticic@3015: usveticic@3015: } usveticic@3015: usveticic@3015: usveticic@3015: usveticic@3015: shift = false; usveticic@3015: usveticic@3015: _shift = undefined; usveticic@3015: usveticic@3015: on_Shift_click() { usveticic@3015: usveticic@3015: this.shift = !this.shift; usveticic@3015: usveticic@3015: this.caps = false; usveticic@3015: usveticic@3015: this.update(); usveticic@3015: usveticic@3015: } usveticic@3015: usveticic@3015: editstr = ""; usveticic@3015: usveticic@3015: _editstr = undefined; usveticic@3015: usveticic@3015: result_callback_obj = undefined; usveticic@3015: usveticic@3015: start_edit(info, valuetype, callback_obj, initial,size) { usveticic@3015: usveticic@3015: show_modal.call(this,size); usveticic@3015: Edouard@3034: this.editstr = String(initial); usveticic@3015: usveticic@3015: this.result_callback_obj = callback_obj; usveticic@3015: usveticic@3015: this.Info_elt.textContent = info; usveticic@3015: usveticic@3015: this.shift = false; usveticic@3015: usveticic@3015: this.caps = false; usveticic@3015: Edouard@3034: this.initial = initial; Edouard@3034: Edouard@3034: Edouard@3034: usveticic@3015: this.update(); usveticic@3015: usveticic@3015: } usveticic@3015: usveticic@3015: usveticic@3015: usveticic@3015: update() { usveticic@3015: usveticic@3015: if(this.editstr != this._editstr){ usveticic@3015: usveticic@3015: this._editstr = this.editstr; usveticic@3015: usveticic@3015: this.Value_elt.textContent = this.editstr; usveticic@3015: usveticic@3015: } usveticic@3015: edouard@3103: if(this.Shift_sub && this.shift != this._shift){ usveticic@3015: usveticic@3015: this._shift = this.shift; usveticic@3015: edouard@3128: (this.shift?this.activate_activable:this.inactivate_activable)(this.Shift_sub); usveticic@3015: usveticic@3015: } usveticic@3015: edouard@3103: if(this.CapsLock_sub && this.caps != this._caps){ usveticic@3015: usveticic@3015: this._caps = this.caps; usveticic@3015: edouard@3128: (this.caps?this.activate_activable:this.inactivate_activable)(this.CapsLock_sub); usveticic@3015: usveticic@3015: } usveticic@3015: usveticic@3015: } usveticic@3015: usveticic@3015: } usveticic@3015: usveticic@3015: edouard@3238: Edouard@2917: Edouard@2917: Edouard@2917: Edouard@2917: Edouard@2917: Esc Enter BackSpace Keys Info Value Edouard@2917: Edouard@2917: Edouard@2917: Edouard@2917: Edouard@2917: Edouard@3118: Sign Space NumDot Edouard@2917: Edouard@2917: Edouard@2917: edouard@2920: edouard@2920: edouard@2920: edouard@2920: CapsLock Shift edouard@2920: edouard@2920: edouard@2920: edouard@2920: Edouard@2913: init: function() { Edouard@2913: Edouard@2917: Edouard@2917: id(" Edouard@2917: Edouard@2917: ").setAttribute("onclick", "hmi_widgets[' Edouard@2917: Edouard@2917: '].on_key_click(' Edouard@2917: Edouard@2917: ')"); Edouard@2917: Edouard@2917: edouard@2920: Edouard@2917: if(this. Edouard@2917: Edouard@2917: _elt) Edouard@2917: Edouard@2917: this. Edouard@2917: Edouard@2917: _elt.setAttribute("onclick", "hmi_widgets[' Edouard@2917: Edouard@2917: '].on_ Edouard@2917: Edouard@2917: _click()"); Edouard@2917: Edouard@2917: Edouard@2917: }, Edouard@2917: usveticic@3015: Edouard@2917: Edouard@2917: Edouard@2917: coordinates: [ Edouard@2917: Edouard@2917: , Edouard@2917: Edouard@2917: ], Edouard@2917: usveticic@3015: edouard@3241: edouard@3241: edouard@3241: edouard@3241: edouard@3355: edouard@3355: List widget is a svg:group, list items are labeled elements edouard@3355: edouard@3355: in that group. edouard@3355: edouard@3355: edouard@3355: edouard@3355: To use a List, clone (svg:use) one of the items inside the widget that edouard@3355: edouard@3355: expects a List. edouard@3355: edouard@3355: edouard@3355: edouard@3355: Positions of items are relative to each other, and they must all be in the edouard@3355: edouard@3355: same place. In order to make editing easier it is therefore recommanded to edouard@3355: edouard@3355: make stacked clones of svg elements spread nearby the list. edouard@3355: edouard@3355: edouard@3355: edouard@3355: A named list of named graphical elements edouard@3355: edouard@3355: edouard@3241: edouard@3238: edouard@2997: edouard@2997: items: { edouard@2997: edouard@2997: Edouard@3387: " Edouard@3031: Edouard@3387: ": " edouard@2997: edouard@2997: ", edouard@2997: edouard@2997: edouard@2997: }, edouard@2997: edouard@2997: Edouard@3387: Edouard@3387: Edouard@3387: Edouard@3387: Edouard@3387: Edouard@3387: ListSwitch widget displays one item of an HMI:List depending on value of Edouard@3387: Edouard@3387: given variable. Main element of the widget must be a clone of the list or Edouard@3387: Edouard@3387: of an item of that list. Edouard@3387: Edouard@3387: Edouard@3387: Edouard@3387: Given variable's current value is compared to list items Edouard@3387: Edouard@3387: label. For exemple if given variable type Edouard@3387: Edouard@3387: is HMI_INT and value is 1, then item with label '1' will be displayed. Edouard@3387: Edouard@3387: If matching variable of type HMI_STRING, then no quotes are needed. Edouard@3387: Edouard@3387: For exemple, 'hello' match HMI_STRING 'hello'. Edouard@3387: Edouard@3387: Edouard@3387: Edouard@3387: Displays item of an HMI:List whose label matches value. Edouard@3387: Edouard@3387: Edouard@3387: value to compare to labels Edouard@3387: Edouard@3387: Edouard@3387: Edouard@3387: class Edouard@3387: ListSwitchWidget Edouard@3387: extends Widget{ Edouard@3387: Edouard@3387: frequency = 5; Edouard@3387: Edouard@3387: } Edouard@3387: Edouard@3387: Edouard@3387: Edouard@3387: Edouard@3387: Edouard@3387: Edouard@3387: dispatch: function(value) { Edouard@3387: Edouard@3387: this.element.href.baseVal = "#"+hmi_widgets[" Edouard@3387: Edouard@3387: "].items[value]; Edouard@3387: Edouard@3387: }, Edouard@3387: Edouard@3387: edouard@3241: edouard@3241: edouard@3241: edouard@3241: edouard@3241: edouard@3241: Meter widget moves the end of "needle" labeled path along "range" labeled edouard@3241: edouard@3241: path, according to value of the single accepted variable. edouard@3241: edouard@3241: edouard@3241: edouard@3241: Needle is reduced to a single segment. If "min" a "max" labeled texts edouard@3241: edouard@3241: are provided, or if first and second argument are given, then they are used edouard@3241: edouard@3241: as respective minimum and maximum value. Otherwise, value is expected to be edouard@3241: edouard@3241: in between 0 and 100. edouard@3241: edouard@3241: edouard@3241: edouard@3241: Moves "needle" along "range" edouard@3241: edouard@3241: edouard@3241: minimum value edouard@3241: edouard@3241: edouard@3241: maximum value edouard@3241: edouard@3241: edouard@3241: Value to display edouard@3241: edouard@3241: Edouard@3276: edouard@3238: class Edouard@3276: MeterWidget edouard@3238: extends Widget{ usveticic@3045: usveticic@3045: frequency = 10; usveticic@3045: usveticic@3045: origin = undefined; usveticic@3045: usveticic@3045: range = undefined; usveticic@3045: usveticic@3045: usveticic@3045: usveticic@3045: dispatch(value) { usveticic@3045: Edouard@3112: this.display_val = value; Edouard@3112: Edouard@3112: this.request_animate(); Edouard@3112: Edouard@3112: } Edouard@3112: Edouard@3112: Edouard@3112: Edouard@3112: animate(){ Edouard@3112: usveticic@3045: if(this.value_elt) usveticic@3045: Edouard@3112: this.value_elt.textContent = String(this.display_val); usveticic@3045: usveticic@3045: let [min,max,totallength] = this.range; usveticic@3045: Edouard@3112: let length = Math.max(0,Math.min(totallength,(Number(this.display_val)-min)*totallength/(max-min))); usveticic@3045: usveticic@3045: let tip = this.range_elt.getPointAtLength(length); usveticic@3045: usveticic@3045: this.needle_elt.setAttribute('d', "M "+this.origin.x+","+this.origin.y+" "+tip.x+","+tip.y); usveticic@3045: usveticic@3045: } usveticic@3045: usveticic@3045: usveticic@3045: usveticic@3045: init() { usveticic@3045: Edouard@3112: let [min,max] = [[this.min_elt,0],[this.max_elt,100]].map(([elt,def],i)=>elt? Edouard@3112: Edouard@3112: Number(elt.textContent) : Edouard@3112: Edouard@3112: this.args.length >= i+1 ? this.args[i] : def); Edouard@3112: Edouard@3112: usveticic@3045: usveticic@3045: this.range = [min, max, this.range_elt.getTotalLength()] usveticic@3045: usveticic@3045: this.origin = this.needle_elt.getPointAtLength(0); usveticic@3045: usveticic@3045: } usveticic@3045: usveticic@3045: } usveticic@3045: usveticic@3045: edouard@3238: edouard@2883: edouard@2883: edouard@2883: edouard@2883: edouard@2883: needle range edouard@2883: edouard@2883: edouard@2883: edouard@2883: edouard@2883: edouard@3362: min max edouard@2883: edouard@2883: edouard@2883: Edouard@2797: edouard@3241: edouard@3241: edouard@3241: edouard@3241: Mutlistateh widget hides all subelements whose label do not match given edouard@3241: edouard@3241: variable value representation. For exemple if given variable type edouard@3241: edouard@3241: is HMI_INT and value is 1, then elements with label '1' will be displayed. edouard@3241: edouard@3241: Label can have comments, so '1#some comment' would also match. If matching edouard@3241: edouard@3241: variable of type HMI_STRING, then double quotes must be used. For exemple, edouard@3241: edouard@3241: '"hello"' or '"hello"#another comment' match HMI_STRING 'hello'. edouard@3241: edouard@3241: edouard@3241: edouard@3241: Click on widget changes variable value to next value in given list, or to edouard@3241: edouard@3241: first one if not initialized to value already part of the list. edouard@3241: edouard@3241: edouard@3241: edouard@3241: Show elements whose label match value. edouard@3241: edouard@3241: edouard@3241: value to compare to labels edouard@3241: edouard@3241: edouard@3238: edouard@3238: class edouard@3238: MultiStateWidget edouard@3238: extends Widget{ edouard@2962: edouard@2962: frequency = 5; edouard@2962: usveticic@3015: state = 0; usveticic@3015: edouard@2962: dispatch(value) { edouard@2962: usveticic@3015: this.state = value; usveticic@3015: edouard@2962: for(let choice of this.choices){ edouard@2962: usveticic@3015: if(this.state != choice.value){ edouard@2962: edouard@2962: choice.elt.setAttribute("style", "display:none"); edouard@2962: edouard@2962: } else { edouard@2962: edouard@2962: choice.elt.setAttribute("style", choice.style); edouard@2962: edouard@2962: } edouard@2962: edouard@2962: } edouard@2962: edouard@2962: } edouard@2962: usveticic@3015: usveticic@3015: usveticic@3015: on_click(evt) { usveticic@3015: usveticic@3015: //get current selected value usveticic@3015: usveticic@3015: let next_ind; usveticic@3015: usveticic@3015: for(next_ind=0; next_ind<this.choices.length; next_ind++){ usveticic@3015: usveticic@3015: if(this.state == this.choices[next_ind].value){ usveticic@3015: usveticic@3015: next_ind = next_ind + 1; usveticic@3015: usveticic@3015: break; usveticic@3015: usveticic@3015: } usveticic@3015: usveticic@3015: } usveticic@3015: usveticic@3015: usveticic@3015: usveticic@3015: //get next selected value usveticic@3015: usveticic@3015: if(this.choices.length > next_ind){ usveticic@3015: usveticic@3015: this.state = this.choices[next_ind].value; usveticic@3015: usveticic@3015: } usveticic@3015: usveticic@3015: else{ usveticic@3015: usveticic@3015: this.state = this.choices[0].value; usveticic@3015: usveticic@3015: } usveticic@3015: usveticic@3015: usveticic@3015: usveticic@3015: //post value to plc usveticic@3015: Edouard@3019: this.apply_hmi_value(0, this.state); usveticic@3015: usveticic@3015: } usveticic@3015: usveticic@3015: usveticic@3015: usveticic@3015: init() { usveticic@3015: usveticic@3015: this.element.setAttribute("onclick", "hmi_widgets['"+this.element_id+"'].on_click(evt)"); usveticic@3015: usveticic@3015: } usveticic@3015: edouard@2962: } edouard@2962: edouard@2962: edouard@3238: Edouard@2839: edouard@2851: choices: [ Edouard@2839: Edouard@2907: Edouard@2969: Edouard@2839: edouard@2851: { edouard@2851: edouard@2851: elt:id(" Edouard@2839: Edouard@2839: "), Edouard@2839: edouard@2851: style:" Edouard@2839: Edouard@2839: ", Edouard@2839: edouard@2851: value: Edouard@2839: Edouard@2839: Edouard@2839: edouard@2851: } Edouard@2839: Edouard@2839: , Edouard@2839: Edouard@2839: Edouard@2839: Edouard@2839: edouard@2851: ], Edouard@2801: Edouard@2801: edouard@3358: edouard@3358: edouard@3358: edouard@3358: edouard@3358: edouard@3358: PathSlider - edouard@3358: edouard@3358: edouard@3358: edouard@3358: Slide an SVG element along a path by dragging it edouard@3358: edouard@3358: edouard@3358: value edouard@3358: edouard@3358: edouard@3358: min edouard@3358: edouard@3358: edouard@3358: max edouard@3358: edouard@3358: edouard@3358: minimum value edouard@3358: edouard@3358: edouard@3358: maximum value edouard@3358: edouard@3358: edouard@3358: edouard@3358: class edouard@3358: PathSliderWidget edouard@3358: extends Widget{ edouard@3358: edouard@3358: frequency = 10; edouard@3358: edouard@3358: position = undefined; edouard@3358: edouard@3358: min = 0; edouard@3358: edouard@3358: max = 100; edouard@3358: edouard@3358: scannedPoints = []; edouard@3358: edouard@3358: pathLength = undefined; edouard@3358: edouard@3358: precision = undefined; edouard@3358: edouard@3358: origPt = undefined; edouard@3358: edouard@3358: edouard@3358: edouard@3358: edouard@3358: edouard@3358: scanPath() { edouard@3358: edouard@3358: this.pathLength = this.path_elt.getTotalLength(); edouard@3358: edouard@3358: this.precision = Math.floor(this.pathLength / 10); edouard@3358: edouard@3358: edouard@3358: edouard@3358: // save linear scan for coarse approximation edouard@3358: edouard@3358: for (var scanLength = 0; scanLength <= this.pathLength; scanLength += this.precision) { edouard@3358: edouard@3358: this.scannedPoints.push([this.path_elt.getPointAtLength(scanLength), scanLength]); edouard@3358: edouard@3358: } edouard@3358: edouard@3358: [this.origPt,] = this.scannedPoints[0]; edouard@3358: edouard@3358: } edouard@3358: edouard@3358: edouard@3358: edouard@3358: closestPoint(point) { edouard@3358: edouard@3358: var bestPoint, edouard@3358: edouard@3358: bestLength, edouard@3358: edouard@3358: bestDistance = Infinity, edouard@3358: edouard@3358: scanDistance; edouard@3358: edouard@3358: edouard@3358: edouard@3358: // use linear scan for coarse approximation edouard@3358: edouard@3358: for (let [scanPoint, scanLength] of this.scannedPoints){ edouard@3358: edouard@3358: if ((scanDistance = distance2(scanPoint)) < bestDistance) { edouard@3358: edouard@3358: bestPoint = scanPoint, edouard@3358: edouard@3358: bestLength = scanLength, edouard@3358: edouard@3358: bestDistance = scanDistance; edouard@3358: edouard@3358: } edouard@3358: edouard@3358: } edouard@3358: edouard@3358: edouard@3358: edouard@3358: // binary search for more precise estimate edouard@3358: edouard@3358: let precision = this.precision / 2; edouard@3358: edouard@3358: while (precision > 0.5) { edouard@3358: edouard@3358: var beforePoint, edouard@3358: edouard@3358: afterPoint, edouard@3358: edouard@3358: beforeLength, edouard@3358: edouard@3358: afterLength, edouard@3358: edouard@3358: beforeDistance, edouard@3358: edouard@3358: afterDistance; edouard@3358: edouard@3358: if ((beforeLength = bestLength - precision) >= 0 && edouard@3358: edouard@3358: (beforeDistance = distance2(beforePoint = this.path_elt.getPointAtLength(beforeLength))) < bestDistance) { edouard@3358: edouard@3358: bestPoint = beforePoint, edouard@3358: edouard@3358: bestLength = beforeLength, edouard@3358: edouard@3358: bestDistance = beforeDistance; edouard@3358: edouard@3358: } else if ((afterLength = bestLength + precision) <= this.pathLength && edouard@3358: edouard@3358: (afterDistance = distance2(afterPoint = this.path_elt.getPointAtLength(afterLength))) < bestDistance) { edouard@3358: edouard@3358: bestPoint = afterPoint, edouard@3358: edouard@3358: bestLength = afterLength, edouard@3358: edouard@3358: bestDistance = afterDistance; edouard@3358: edouard@3358: } edouard@3358: edouard@3358: precision /= 2; edouard@3358: edouard@3358: } edouard@3358: edouard@3358: edouard@3358: edouard@3358: return [bestPoint, bestLength]; edouard@3358: edouard@3358: edouard@3358: edouard@3358: function distance2(p) { edouard@3358: edouard@3358: var dx = p.x - point.x, edouard@3358: edouard@3358: dy = p.y - point.y; edouard@3358: edouard@3358: return dx * dx + dy * dy; edouard@3358: edouard@3358: } edouard@3358: edouard@3358: } edouard@3358: edouard@3358: edouard@3358: edouard@3358: dispatch(value,oldval, index) { edouard@3358: edouard@3358: switch(index) { edouard@3358: edouard@3358: case 0: edouard@3358: edouard@3358: this.position = value; edouard@3358: edouard@3358: break; edouard@3358: edouard@3358: case 1: edouard@3358: edouard@3358: this.min = value; edouard@3358: edouard@3358: break; edouard@3358: edouard@3358: case 2: edouard@3358: edouard@3358: this.max = value; edouard@3358: edouard@3358: break; edouard@3358: edouard@3358: } edouard@3358: edouard@3358: edouard@3358: edouard@3358: this.request_animate(); edouard@3358: edouard@3358: } edouard@3358: edouard@3358: edouard@3358: edouard@3358: get_current_point(){ edouard@3358: edouard@3358: let currLength = this.pathLength * (this.position - this.min) / (this.max - this.min) edouard@3358: edouard@3358: return this.path_elt.getPointAtLength(currLength); edouard@3358: edouard@3358: } edouard@3358: edouard@3358: edouard@3358: edouard@3358: animate(){ edouard@3358: edouard@3358: if(this.position == undefined) edouard@3358: edouard@3358: return; edouard@3358: edouard@3358: edouard@3358: edouard@3358: let currPt = this.get_current_point(); edouard@3358: edouard@3358: this.cursor_transform.setTranslate(currPt.x - this.origPt.x, currPt.y - this.origPt.y); edouard@3358: edouard@3358: } edouard@3358: edouard@3358: edouard@3358: edouard@3358: init() { edouard@3358: edouard@3358: if(this.args.length == 2) edouard@3358: edouard@3358: [this.min, this.max]=this.args; edouard@3358: edouard@3358: edouard@3358: edouard@3358: this.scanPath(); edouard@3358: edouard@3358: edouard@3358: edouard@3358: this.cursor_transform = svg_root.createSVGTransform(); edouard@3358: edouard@3358: edouard@3358: edouard@3358: this.cursor_elt.transform.baseVal.appendItem(this.cursor_transform); edouard@3358: edouard@3358: edouard@3358: edouard@3358: this.cursor_elt.onpointerdown = (e) => this.on_cursor_down(e); edouard@3358: edouard@3358: edouard@3358: edouard@3358: this.bound_drag = this.drag.bind(this); edouard@3358: edouard@3358: this.bound_drop = this.drop.bind(this); edouard@3358: edouard@3358: } edouard@3358: edouard@3358: edouard@3358: edouard@3358: start_dragging_from_event(e){ edouard@3358: edouard@3358: let clientPoint = new DOMPoint(e.clientX, e.clientY); edouard@3358: edouard@3358: let point = clientPoint.matrixTransform(this.invctm); edouard@3358: edouard@3358: let currPt = this.get_current_point(); edouard@3358: edouard@3358: this.draggingOffset = new DOMPoint(point.x - currPt.x , point.y - currPt.y); edouard@3358: edouard@3358: } edouard@3358: edouard@3358: edouard@3358: edouard@3358: apply_position_from_event(e){ edouard@3358: edouard@3358: let clientPoint = new DOMPoint(e.clientX, e.clientY); edouard@3358: edouard@3358: let rawPoint = clientPoint.matrixTransform(this.invctm); edouard@3358: edouard@3358: let point = new DOMPoint(rawPoint.x - this.draggingOffset.x , rawPoint.y - this.draggingOffset.y); edouard@3358: edouard@3358: let [closestPoint, closestLength] = this.closestPoint(point); edouard@3358: edouard@3358: let new_position = this.min + (this.max - this.min) * closestLength / this.pathLength; edouard@3358: edouard@3358: this.position = Math.round(Math.max(Math.min(new_position, this.max), this.min)); edouard@3358: edouard@3358: this.apply_hmi_value(0, this.position); edouard@3358: edouard@3358: } edouard@3358: edouard@3358: edouard@3358: edouard@3358: on_cursor_down(e){ edouard@3358: edouard@3358: // get scrollbar -> root transform edouard@3358: edouard@3358: let ctm = this.path_elt.getCTM(); edouard@3358: edouard@3358: // root -> path transform edouard@3358: edouard@3358: this.invctm = ctm.inverse(); edouard@3358: edouard@3358: this.start_dragging_from_event(e); edouard@3358: edouard@3358: svg_root.addEventListener("pointerup", this.bound_drop, true); edouard@3358: edouard@3358: svg_root.addEventListener("pointermove", this.bound_drag, true); edouard@3358: edouard@3358: } edouard@3358: edouard@3358: edouard@3358: edouard@3358: drop(e) { edouard@3358: edouard@3358: svg_root.removeEventListener("pointerup", this.bound_drop, true); edouard@3358: edouard@3358: svg_root.removeEventListener("pointermove", this.bound_drag, true); edouard@3358: edouard@3358: } edouard@3358: edouard@3358: edouard@3358: edouard@3358: drag(e) { edouard@3358: edouard@3358: this.apply_position_from_event(e); edouard@3358: edouard@3358: } edouard@3358: edouard@3358: } edouard@3358: edouard@3358: edouard@3358: edouard@3358: edouard@3358: edouard@3358: edouard@3358: edouard@3358: cursor path edouard@3358: edouard@3358: edouard@3358: edouard@3241: edouard@3241: edouard@3241: edouard@3241: edouard@3241: edouard@3358: ScrollBar - svg:rect based scrollbar edouard@3241: edouard@3241: edouard@3241: edouard@3241: ScrollBar edouard@3241: edouard@3241: edouard@3241: value edouard@3241: edouard@3241: edouard@3241: range edouard@3241: edouard@3241: edouard@3241: visible edouard@3241: edouard@3241: edouard@3238: edouard@3238: class edouard@3238: ScrollBarWidget edouard@3238: extends Widget{ edouard@3141: edouard@3141: frequency = 10; edouard@3141: edouard@3141: position = undefined; edouard@3141: edouard@3141: range = undefined; edouard@3141: edouard@3141: size = undefined; edouard@3141: edouard@3141: mincursize = 0.1; edouard@3141: edouard@3141: edouard@3141: edouard@3141: dispatch(value,oldval, index) { edouard@3141: edouard@3141: switch(index) { edouard@3141: edouard@3141: case 0: edouard@3141: Edouard@3178: this.range = Math.max(1,value); Edouard@3178: Edouard@3178: break; Edouard@3178: Edouard@3178: case 1: Edouard@3178: Edouard@3147: this.position = value; edouard@3141: edouard@3141: break; edouard@3141: Edouard@3178: case 2: Edouard@3178: Edouard@3178: this.size = value; edouard@3141: edouard@3141: break; edouard@3141: edouard@3141: } edouard@3141: edouard@3141: edouard@3141: edouard@3141: this.request_animate(); edouard@3141: edouard@3141: } edouard@3141: edouard@3141: edouard@3141: edouard@3141: get_ratios() { edouard@3141: edouard@3141: let range = this.range; edouard@3141: edouard@3358: let size = Math.max(range * this.mincursize, Math.min(this.size, range)); edouard@3141: edouard@3141: let maxh = this.range_elt.height.baseVal.value; edouard@3141: Edouard@3151: let pixels = maxh; Edouard@3151: edouard@3358: return [size, maxh, range, pixels]; edouard@3141: edouard@3141: } edouard@3141: edouard@3141: edouard@3141: edouard@3141: animate(){ edouard@3141: edouard@3141: if(this.position == undefined || this.range == undefined || this.size == undefined) edouard@3141: edouard@3141: return; edouard@3141: edouard@3358: let [size, maxh, range, pixels] = this.get_ratios(); edouard@3358: edouard@3358: edouard@3358: edouard@3358: let new_y = this.range_elt.y.baseVal.value + Math.round(Math.min(this.position,range-size) * pixels / range); edouard@3141: edouard@3141: let new_height = Math.round(maxh * size/range); edouard@3141: edouard@3141: edouard@3141: edouard@3141: this.cursor_elt.y.baseVal.value = new_y; edouard@3141: edouard@3141: this.cursor_elt.height.baseVal.value = new_height; edouard@3141: edouard@3141: } edouard@3141: edouard@3141: edouard@3141: edouard@3141: init_mandatory() { edouard@3141: edouard@3141: this.cursor_elt.onpointerdown = () => this.on_cursor_down(); edouard@3141: edouard@3141: edouard@3141: edouard@3141: this.bound_drag = this.drag.bind(this); edouard@3141: edouard@3141: this.bound_drop = this.drop.bind(this); edouard@3141: edouard@3141: } edouard@3141: edouard@3141: edouard@3141: edouard@3141: apply_position(position){ edouard@3141: Edouard@3151: this.position = Math.round(Math.max(Math.min(position, this.range - this.size), 0)); Edouard@3147: Edouard@3178: this.apply_hmi_value(1, this.position); edouard@3141: edouard@3141: } edouard@3141: edouard@3141: edouard@3141: edouard@3141: on_page_click(is_up){ edouard@3141: edouard@3141: this.apply_position(is_up ? this.position-this.size edouard@3141: edouard@3141: : this.position+this.size); edouard@3141: edouard@3141: } edouard@3141: edouard@3141: edouard@3141: edouard@3141: on_cursor_down(e){ edouard@3141: edouard@3141: // get scrollbar -> root transform edouard@3141: edouard@3141: let ctm = this.range_elt.getCTM(); edouard@3141: edouard@3141: // relative motion -> discard translation edouard@3141: edouard@3141: ctm.e = 0; edouard@3141: edouard@3141: ctm.f = 0; edouard@3141: edouard@3141: // root -> scrollbar transform edouard@3141: edouard@3141: this.invctm = ctm.inverse(); edouard@3141: edouard@3141: svg_root.addEventListener("pointerup", this.bound_drop, true); edouard@3141: edouard@3141: svg_root.addEventListener("pointermove", this.bound_drag, true); edouard@3141: Edouard@3147: this.dragpos = this.position; Edouard@3147: edouard@3141: } edouard@3141: edouard@3141: edouard@3141: edouard@3141: drop(e) { edouard@3141: edouard@3141: svg_root.removeEventListener("pointerup", this.bound_drop, true); edouard@3141: edouard@3141: svg_root.removeEventListener("pointermove", this.bound_drag, true); edouard@3141: edouard@3141: } edouard@3141: edouard@3141: edouard@3141: edouard@3141: drag(e) { edouard@3141: edouard@3358: let [size, maxh, range, pixels] = this.get_ratios(); edouard@3141: edouard@3141: if(pixels == 0) return; edouard@3141: edouard@3141: let point = new DOMPoint(e.movementX, e.movementY); edouard@3141: edouard@3141: let movement = point.matrixTransform(this.invctm).y; edouard@3141: edouard@3358: this.dragpos += movement * range / pixels; Edouard@3147: Edouard@3147: this.apply_position(this.dragpos); edouard@3141: edouard@3141: } edouard@3141: edouard@3141: } edouard@3141: edouard@3141: edouard@3238: edouard@3141: edouard@3141: edouard@3141: edouard@3141: edouard@3141: cursor range edouard@3141: edouard@3141: edouard@3141: edouard@3141: edouard@3141: edouard@3141: edouard@3141: pageup pagedown edouard@3141: edouard@3141: edouard@3141: edouard@3141: edouard@3141: edouard@3141: edouard@3141: init: function() { edouard@3141: edouard@3141: this.init_mandatory(); edouard@3141: edouard@3141: edouard@3141: this.pageup_elt.onclick = () => this.on_page_click(true); edouard@3141: edouard@3141: this.pagedown_elt.onclick = () => this.on_page_click(false); edouard@3141: edouard@3141: edouard@3141: }, edouard@3141: edouard@3141: edouard@3241: edouard@3241: edouard@3241: edouard@3241: edouard@3241: edouard@3241: Slider - DEPRECATED - use ScrollBar or PathSlider instead edouard@3241: edouard@3241: edouard@3241: edouard@3241: Slider - DEPRECATED - use ScrollBar instead edouard@3241: edouard@3241: edouard@3241: value edouard@3241: edouard@3241: edouard@3241: range edouard@3241: edouard@3241: edouard@3241: visible edouard@3241: edouard@3241: edouard@3238: edouard@3238: class edouard@3238: SliderWidget edouard@3238: extends Widget{ edouard@3238: usveticic@3015: frequency = 5; usveticic@3015: usveticic@3015: range = undefined; usveticic@3015: Edouard@3058: handle_orig = undefined; Edouard@3058: Edouard@3065: scroll_size = undefined; Edouard@3065: Edouard@3065: scroll_range = 0; Edouard@3065: Edouard@3065: scroll_visible = 7; Edouard@3058: Edouard@3058: min_size = 0.07; Edouard@3058: usveticic@3015: fi = undefined; usveticic@3015: Edouard@3058: curr_value = 0; usveticic@3045: usveticic@3015: drag = false; usveticic@3015: usveticic@3015: enTimer = false; usveticic@3015: Edouard@3058: handle_click = undefined; Edouard@3058: Edouard@3058: last_drag = false; Edouard@3058: usveticic@3015: usveticic@3015: Edouard@3065: dispatch(value,oldval, index) { Edouard@3065: Edouard@3065: if (index == 0){ Edouard@3065: Edouard@3065: let [min,max,start,totallength] = this.range; Edouard@3065: Edouard@3065: //save current value inside widget Edouard@3065: Edouard@3065: this.curr_value = value; Edouard@3065: Edouard@3065: Edouard@3065: Edouard@3065: //check if in range Edouard@3065: Edouard@3065: if (this.curr_value > max){ Edouard@3065: Edouard@3065: this.curr_value = max; Edouard@3065: Edouard@3065: this.apply_hmi_value(0, this.curr_value); Edouard@3065: Edouard@3065: } Edouard@3065: Edouard@3065: else if (this.curr_value < min){ Edouard@3065: Edouard@3065: this.curr_value = min; Edouard@3065: Edouard@3065: this.apply_hmi_value(0, this.curr_value); Edouard@3065: Edouard@3065: } Edouard@3065: Edouard@3065: Edouard@3065: Edouard@3065: if(this.value_elt) Edouard@3065: Edouard@3065: this.value_elt.textContent = String(value); Edouard@3065: Edouard@3065: } Edouard@3065: Edouard@3065: else if(index == 1){ Edouard@3065: Edouard@3065: this.scroll_range = value; Edouard@3065: Edouard@3065: this.set_scroll(); Edouard@3065: Edouard@3065: } Edouard@3065: Edouard@3065: else if(index == 2){ Edouard@3065: Edouard@3065: this.scroll_visible = value; Edouard@3065: Edouard@3065: this.set_scroll(); Edouard@3065: Edouard@3065: } Edouard@3019: Edouard@3019: Edouard@3019: Edouard@3058: //don't update if draging and setpoint ghost doesn't exist Edouard@3058: Edouard@3058: if(!this.drag || (this.setpoint_elt != undefined)){ Edouard@3058: Edouard@3065: this.update_DOM(this.curr_value, this.handle_elt); Edouard@3058: Edouard@3058: } Edouard@3021: Edouard@3021: } Edouard@3021: Edouard@3021: Edouard@3021: Edouard@3065: set_scroll(){ Edouard@3065: Edouard@3065: //check if range is bigger than visible and set scroll size Edouard@3065: Edouard@3065: if(this.scroll_range > this.scroll_visible){ Edouard@3065: Edouard@3065: this.scroll_size = this.scroll_range - this.scroll_visible; Edouard@3065: Edouard@3065: this.range[0] = 0; Edouard@3065: Edouard@3065: this.range[1] = this.scroll_size; Edouard@3065: Edouard@3065: } Edouard@3065: Edouard@3065: else{ Edouard@3065: Edouard@3065: this.scroll_size = 1; Edouard@3065: Edouard@3065: this.range[0] = 0; Edouard@3065: Edouard@3065: this.range[1] = 1; Edouard@3065: Edouard@3065: } Edouard@3065: Edouard@3065: } Edouard@3065: Edouard@3065: Edouard@3065: Edouard@3021: update_DOM(value, elt){ Edouard@3019: Edouard@3019: let [min,max,start,totallength] = this.range; Edouard@3019: Edouard@3058: // check if handle is resizeable Edouard@3058: Edouard@3058: if (this.scroll_size != undefined){ //size changes Edouard@3058: Edouard@3058: //get parameters Edouard@3058: Edouard@3058: let length = Math.max(min,Math.min(max,(Number(value)-min)*max/(max-min))); Edouard@3058: Edouard@3058: let tip = this.range_elt.getPointAtLength(length); Edouard@3058: Edouard@3058: let handle_min = totallength*this.min_size; Edouard@3058: Edouard@3058: Edouard@3058: Edouard@3058: let step = 1; Edouard@3058: Edouard@3058: //check if range is bigger than max displayed and recalculate step Edouard@3058: Edouard@3058: if ((totallength/handle_min) < (max-min+1)){ Edouard@3058: Edouard@3058: step = (max-min+1)/(totallength/handle_min-1); Edouard@3058: Edouard@3058: } Edouard@3058: Edouard@3058: Edouard@3058: Edouard@3058: let kx,ky,offseY,offseX = undefined; Edouard@3058: Edouard@3058: //scale on x or y axes Edouard@3058: Edouard@3058: if (this.fi > 0.75){ Edouard@3058: Edouard@3058: //get scale factor Edouard@3058: Edouard@3058: if(step > 1){ Edouard@3058: Edouard@3058: ky = handle_min/this.handle_orig.height; Edouard@3058: Edouard@3058: } Edouard@3058: Edouard@3058: else{ Edouard@3058: Edouard@3058: ky = (totallength-handle_min*(max-min))/this.handle_orig.height; Edouard@3058: Edouard@3058: } Edouard@3058: Edouard@3058: kx = 1; Edouard@3058: Edouard@3058: //get 0 offset to stay inside range Edouard@3058: Edouard@3058: offseY = start.y - (this.handle_orig.height + this.handle_orig.y) * ky; Edouard@3058: Edouard@3058: offseX = 0; Edouard@3058: Edouard@3058: //get distance from value Edouard@3058: Edouard@3058: tip.y =this.range_elt.getPointAtLength(0).y - length/step *handle_min; Edouard@3058: Edouard@3058: } Edouard@3058: Edouard@3058: else{ Edouard@3058: Edouard@3058: //get scale factor Edouard@3058: Edouard@3058: if(step > 1){ Edouard@3058: Edouard@3058: kx = handle_min/this.handle_orig.width; Edouard@3058: Edouard@3058: } Edouard@3058: Edouard@3058: else{ Edouard@3058: Edouard@3058: kx = (totallength-handle_min*(max-min))/this.handle_orig.width; Edouard@3058: Edouard@3058: } Edouard@3058: Edouard@3058: ky = 1; Edouard@3058: Edouard@3058: //get 0 offset to stay inside range Edouard@3058: Edouard@3058: offseX = start.x - (this.handle_orig.x * kx); Edouard@3058: Edouard@3058: offseY = 0; Edouard@3058: Edouard@3058: //get distance from value Edouard@3058: Edouard@3058: tip.x =this.range_elt.getPointAtLength(0).x + length/step *handle_min; Edouard@3058: Edouard@3058: } Edouard@3058: Edouard@3058: elt.setAttribute('transform',"matrix("+(kx)+" 0 0 "+(ky)+" "+(tip.x-start.x+offseX)+" "+(tip.y-start.y+offseY)+")"); Edouard@3058: Edouard@3058: } Edouard@3058: Edouard@3058: else{ //size stays the same Edouard@3058: Edouard@3058: let length = Math.max(0,Math.min(totallength,(Number(value)-min)*totallength/(max-min))); Edouard@3058: Edouard@3058: let tip = this.range_elt.getPointAtLength(length); Edouard@3058: Edouard@3058: elt.setAttribute('transform',"translate("+(tip.x-start.x)+","+(tip.y-start.y)+")"); Edouard@3058: Edouard@3058: } Edouard@3058: Edouard@3058: Edouard@3058: Edouard@3058: // show or hide ghost if exists Edouard@3021: Edouard@3021: if(this.setpoint_elt != undefined){ Edouard@3021: Edouard@3021: if(this.last_drag!= this.drag){ Edouard@3021: Edouard@3021: if(this.drag){ Edouard@3021: Edouard@3021: this.setpoint_elt.setAttribute("style", this.setpoint_style); Edouard@3021: Edouard@3021: }else{ Edouard@3021: Edouard@3021: this.setpoint_elt.setAttribute("style", "display:none"); Edouard@3021: Edouard@3021: } Edouard@3021: Edouard@3021: this.last_drag = this.drag; Edouard@3021: Edouard@3021: } Edouard@3021: Edouard@3021: } Edouard@3019: Edouard@3019: } Edouard@3019: Edouard@3019: Edouard@3019: Edouard@3019: on_release(evt) { Edouard@3019: Edouard@3058: //unbind events Edouard@3058: Edouard@3021: window.removeEventListener("touchmove", this.on_bound_drag, true); Edouard@3021: Edouard@3021: window.removeEventListener("mousemove", this.on_bound_drag, true); Edouard@3021: Edouard@3021: Edouard@3021: Edouard@3065: window.removeEventListener("mouseup", this.bound_on_release, true); Edouard@3021: Edouard@3021: window.removeEventListener("touchend", this.bound_on_release, true); Edouard@3021: Edouard@3021: window.removeEventListener("touchcancel", this.bound_on_release, true); Edouard@3021: Edouard@3058: Edouard@3058: Edouard@3058: //reset drag flag Edouard@3058: Edouard@3019: if(this.drag){ Edouard@3019: Edouard@3019: this.drag = false; usveticic@3015: usveticic@3015: } usveticic@3015: Edouard@3058: Edouard@3058: Edouard@3058: // get final position Edouard@3058: Edouard@3021: this.update_position(evt); Edouard@3021: Edouard@3058: Edouard@3058: Edouard@3021: } Edouard@3021: Edouard@3021: Edouard@3021: Edouard@3021: on_drag(evt){ Edouard@3021: Edouard@3058: //ignore drag event for X amount of time and if not selected Edouard@3058: Edouard@3021: if(this.enTimer && this.drag){ Edouard@3021: Edouard@3021: this.update_position(evt); Edouard@3021: Edouard@3058: Edouard@3058: Edouard@3021: //reset timer Edouard@3021: Edouard@3021: this.enTimer = false; Edouard@3021: Edouard@3021: setTimeout("{hmi_widgets['"+this.element_id+"'].enTimer = true;}", 100); Edouard@3021: Edouard@3021: } Edouard@3021: usveticic@3015: } usveticic@3015: usveticic@3015: usveticic@3015: Edouard@3019: update_position(evt){ Edouard@3019: Edouard@3021: var html_dist = 0; Edouard@3021: Edouard@3058: let [min,max,start,totallength] = this.range; Edouard@3058: Edouard@3021: Edouard@3021: Edouard@3021: //calculate size of widget in html Edouard@3021: Edouard@3021: var range_borders = this.range_elt.getBoundingClientRect(); Edouard@3021: Edouard@3058: var [minX,minY,maxX,maxY] = [range_borders.left,range_borders.bottom,range_borders.right,range_borders.top]; Edouard@3058: Edouard@3021: var range_length = Math.sqrt( range_borders.height*range_borders.height + range_borders.width*range_borders.width ); Edouard@3021: Edouard@3021: Edouard@3021: Edouard@3021: //get range and mouse coordinates Edouard@3021: Edouard@3021: var mouseX = undefined; Edouard@3021: Edouard@3021: var mouseY = undefined; Edouard@3021: Edouard@3021: if (evt.type.startsWith("touch")){ Edouard@3021: Edouard@3021: mouseX = Math.ceil(evt.touches[0].clientX); Edouard@3021: Edouard@3021: mouseY = Math.ceil(evt.touches[0].clientY); Edouard@3021: Edouard@3021: } Edouard@3021: Edouard@3021: else{ Edouard@3021: Edouard@3021: mouseX = evt.pageX; Edouard@3021: Edouard@3021: mouseY = evt.pageY; Edouard@3021: Edouard@3021: } Edouard@3021: Edouard@3021: Edouard@3021: Edouard@3058: // calculate position Edouard@3058: Edouard@3058: if (this.handle_click){ //if clicked on handle Edouard@3058: Edouard@3058: let moveDist = 0, resizeAdd = 0; Edouard@3058: Edouard@3058: let range_percent = 1; Edouard@3058: Edouard@3058: Edouard@3058: Edouard@3058: //set paramters for resizeable handle Edouard@3058: Edouard@3058: if (this.scroll_size != undefined){ Edouard@3058: Edouard@3058: // add one more object to stay inside range Edouard@3058: Edouard@3058: resizeAdd = 1; Edouard@3058: Edouard@3058: Edouard@3058: Edouard@3058: //chack if range is bigger than display option and Edouard@3058: Edouard@3058: // calculate percent of range with out handle Edouard@3058: Edouard@3058: if(((max/(max*this.min_size)) < (max-min+1))){ Edouard@3058: Edouard@3058: range_percent = 1-this.min_size; Edouard@3058: Edouard@3058: } Edouard@3058: Edouard@3058: else{ Edouard@3058: Edouard@3058: range_percent = 1-(max-max*this.min_size*(max-min))/max; Edouard@3058: Edouard@3058: } Edouard@3058: Edouard@3058: } Edouard@3058: Edouard@3058: Edouard@3058: Edouard@3058: //calculate value difference on x or y axis Edouard@3058: Edouard@3058: if(this.fi > 0.7){ Edouard@3058: Edouard@3058: moveDist = ((max-min+resizeAdd)/(range_length*range_percent))*((this.handle_click[1]-mouseY)/Math.sin(this.fi)); Edouard@3058: Edouard@3058: } Edouard@3058: Edouard@3058: else{ Edouard@3058: Edouard@3058: moveDist = ((max-min+resizeAdd)/(range_length*range_percent))*((mouseX-this.handle_click[0])/Math.cos(this.fi)); Edouard@3058: Edouard@3058: } Edouard@3058: Edouard@3058: Edouard@3058: Edouard@3058: this.curr_value = Math.ceil(this.handle_click[2] + moveDist); Edouard@3021: Edouard@3021: } Edouard@3021: Edouard@3058: else{ //if clicked on widget Edouard@3058: Edouard@3058: //get handle distance from mouse position Edouard@3058: Edouard@3058: if (minX > mouseX && minY < mouseY){ Edouard@3058: Edouard@3058: html_dist = 0; Edouard@3058: Edouard@3058: } Edouard@3058: Edouard@3058: else if (maxX < mouseX && maxY > mouseY){ Edouard@3058: Edouard@3058: html_dist = range_length; Edouard@3058: Edouard@3058: } Edouard@3058: Edouard@3058: else{ Edouard@3058: Edouard@3058: if(this.fi > 0.7){ Edouard@3058: Edouard@3058: html_dist = (minY - mouseY)/Math.sin(this.fi); Edouard@3058: Edouard@3058: } Edouard@3058: Edouard@3058: else{ Edouard@3058: Edouard@3058: html_dist = (mouseX - minX)/Math.cos(this.fi); Edouard@3058: Edouard@3058: } Edouard@3058: Edouard@3058: } Edouard@3058: Edouard@3058: //calculate distance Edouard@3058: Edouard@3058: this.curr_value=Math.ceil((html_dist/range_length)*(this.range[1]-this.range[0])+this.range[0]); Edouard@3021: Edouard@3021: } Edouard@3021: Edouard@3058: Edouard@3058: Edouard@3065: //check if in range and apply Edouard@3058: Edouard@3058: if (this.curr_value > max){ Edouard@3058: Edouard@3058: this.curr_value = max; Edouard@3058: Edouard@3058: } Edouard@3058: Edouard@3058: else if (this.curr_value < min){ Edouard@3058: Edouard@3058: this.curr_value = min; Edouard@3058: Edouard@3058: } Edouard@3058: Edouard@3058: this.apply_hmi_value(0, this.curr_value); Edouard@3058: Edouard@3058: Edouard@3058: Edouard@3058: //redraw handle Edouard@3058: Edouard@3058: this.request_animate(); Edouard@3058: Edouard@3058: Edouard@3058: Edouard@3058: } Edouard@3058: Edouard@3058: Edouard@3058: Edouard@3058: animate(){ Edouard@3058: Edouard@3058: // redraw handle on screen refresh Edouard@3058: Edouard@3058: // check if setpoint(ghost) handle exsist otherwise update main handle Edouard@3058: Edouard@3058: if(this.setpoint_elt != undefined){ Edouard@3058: Edouard@3058: this.update_DOM(this.curr_value, this.setpoint_elt); Edouard@3058: Edouard@3058: } Edouard@3058: Edouard@3021: else{ Edouard@3021: Edouard@3058: this.update_DOM(this.curr_value, this.handle_elt); Edouard@3058: Edouard@3058: } Edouard@3058: Edouard@3058: } Edouard@3058: Edouard@3058: Edouard@3058: Edouard@3058: on_select(evt){ Edouard@3058: Edouard@3058: //enable drag flag and timer Edouard@3058: Edouard@3058: this.drag = true; Edouard@3058: Edouard@3058: this.enTimer = true; Edouard@3058: Edouard@3058: Edouard@3058: Edouard@3058: //bind events Edouard@3058: Edouard@3058: window.addEventListener("touchmove", this.on_bound_drag, true); Edouard@3058: Edouard@3058: window.addEventListener("mousemove", this.on_bound_drag, true); Edouard@3058: Edouard@3058: Edouard@3058: Edouard@3065: window.addEventListener("mouseup", this.bound_on_release, true); Edouard@3058: Edouard@3058: window.addEventListener("touchend", this.bound_on_release, true); Edouard@3058: Edouard@3058: window.addEventListener("touchcancel", this.bound_on_release, true); Edouard@3058: Edouard@3058: Edouard@3058: Edouard@3058: // check if handle was pressed Edouard@3058: Edouard@3058: if (evt.currentTarget == this.handle_elt){ Edouard@3058: Edouard@3058: //get mouse position on the handle Edouard@3058: Edouard@3058: let mouseX = undefined; Edouard@3058: Edouard@3058: let mouseY = undefined; Edouard@3058: Edouard@3058: if (evt.type.startsWith("touch")){ Edouard@3058: Edouard@3058: mouseX = Math.ceil(evt.touches[0].clientX); Edouard@3058: Edouard@3058: mouseY = Math.ceil(evt.touches[0].clientY); Edouard@3019: Edouard@3019: } Edouard@3019: Edouard@3019: else{ Edouard@3019: Edouard@3058: mouseX = evt.pageX; Edouard@3058: Edouard@3058: mouseY = evt.pageY; Edouard@3019: Edouard@3019: } Edouard@3019: Edouard@3058: //save coordinates and orig value Edouard@3058: Edouard@3058: this.handle_click = [mouseX,mouseY,this.curr_value]; Edouard@3019: Edouard@3019: } usveticic@3015: Edouard@3058: else{ Edouard@3058: Edouard@3058: // get new handle position and reset if handle was not pressed Edouard@3058: Edouard@3058: this.handle_click = undefined; Edouard@3058: Edouard@3058: this.update_position(evt); Edouard@3021: Edouard@3021: } Edouard@3021: Edouard@3058: Edouard@3058: Edouard@3058: //prevent next events Edouard@3058: Edouard@3058: evt.stopPropagation(); Edouard@3058: Edouard@3065: Edouard@3065: Edouard@3065: } Edouard@3065: Edouard@3065: Edouard@3021: Edouard@3021: Edouard@3021: Edouard@3019: init() { Edouard@3019: Edouard@3058: //set min max value if not defined Edouard@3058: Edouard@3019: let min = this.min_elt ? Edouard@3019: Edouard@3019: Number(this.min_elt.textContent) : Edouard@3019: Edouard@3019: this.args.length >= 1 ? this.args[0] : 0; Edouard@3019: Edouard@3019: let max = this.max_elt ? Edouard@3019: Edouard@3019: Number(this.max_elt.textContent) : Edouard@3019: Edouard@3019: this.args.length >= 2 ? this.args[1] : 100; Edouard@3019: Edouard@3019: Edouard@3019: Edouard@3065: Edouard@3065: Edouard@3058: // save initial parameters Edouard@3058: Edouard@3058: this.range_elt.style.strokeMiterlimit="0"; Edouard@3058: Edouard@3019: this.range = [min, max, this.range_elt.getPointAtLength(0),this.range_elt.getTotalLength()]; Edouard@3019: Edouard@3019: let start = this.range_elt.getPointAtLength(0); Edouard@3019: Edouard@3019: let end = this.range_elt.getPointAtLength(this.range_elt.getTotalLength()); Edouard@3019: Edouard@3019: this.fi = Math.atan2(start.y-end.y, end.x-start.x); Edouard@3019: Edouard@3058: this.handle_orig = this.handle_elt.getBBox(); Edouard@3058: Edouard@3058: Edouard@3058: Edouard@3058: //bind functions Edouard@3019: Edouard@3021: this.bound_on_select = this.on_select.bind(this); Edouard@3021: Edouard@3021: this.bound_on_release = this.on_release.bind(this); Edouard@3021: Edouard@3021: this.on_bound_drag = this.on_drag.bind(this); Edouard@3021: Edouard@3021: Edouard@3021: Edouard@3058: this.handle_elt.addEventListener("mousedown", this.bound_on_select); Edouard@3058: Edouard@3021: this.element.addEventListener("mousedown", this.bound_on_select); Edouard@3021: Edouard@3021: this.element.addEventListener("touchstart", this.bound_on_select); Edouard@3021: Edouard@3065: //touch recognised as page drag without next command Edouard@3065: Edouard@3065: document.body.addEventListener("touchstart", function(e){}, false); Edouard@3065: Edouard@3065: Edouard@3065: Edouard@3065: //save ghost style Edouard@3021: Edouard@3021: if(this.setpoint_elt != undefined){ Edouard@3021: Edouard@3021: this.setpoint_style = this.setpoint_elt.getAttribute("style"); Edouard@3021: Edouard@3021: this.setpoint_elt.setAttribute("style", "display:none"); Edouard@3021: Edouard@3021: } usveticic@3015: usveticic@3015: usveticic@3015: usveticic@3015: } usveticic@3015: usveticic@3015: } usveticic@3015: edouard@3238: edouard@3238: usveticic@3015: usveticic@3015: usveticic@3015: usveticic@3015: usveticic@3015: handle range usveticic@3015: usveticic@3015: usveticic@3015: usveticic@3015: usveticic@3015: Edouard@3021: value min max setpoint usveticic@3015: usveticic@3015: usveticic@3015: edouard@3238: edouard@3241: edouard@3241: edouard@3241: edouard@3241: edouard@3241: edouard@3241: Switch widget hides all subelements whose label do not match given edouard@3241: edouard@3241: variable current value representation. For exemple if given variable type edouard@3241: edouard@3241: is HMI_INT and value is 1, then elements with label '1' will be displayed. edouard@3241: edouard@3241: Label can have comments, so '1#some comment' would also match. If matching edouard@3241: edouard@3241: variable of type HMI_STRING, then double quotes must be used. For exemple, edouard@3241: edouard@3241: '"hello"' or '"hello"#another comment' match HMI_STRING 'hello'. edouard@3241: edouard@3241: edouard@3241: Edouard@3387: Show elements whose label matches value. edouard@3241: edouard@3241: edouard@3241: value to compare to labels edouard@3241: edouard@3241: edouard@3238: edouard@3238: class edouard@3238: SwitchWidget edouard@3238: extends Widget{ usveticic@3015: usveticic@3015: frequency = 5; usveticic@3015: usveticic@3015: dispatch(value) { usveticic@3015: usveticic@3015: for(let choice of this.choices){ usveticic@3015: usveticic@3015: if(value != choice.value){ usveticic@3015: usveticic@3015: choice.elt.setAttribute("style", "display:none"); usveticic@3015: usveticic@3015: } else { usveticic@3015: usveticic@3015: choice.elt.setAttribute("style", choice.style); usveticic@3015: usveticic@3015: } usveticic@3015: usveticic@3015: } usveticic@3015: usveticic@3015: } usveticic@3015: usveticic@3015: } usveticic@3015: usveticic@3015: edouard@3238: usveticic@3015: usveticic@3015: choices: [ usveticic@3015: usveticic@3015: Edouard@3220: Edouard@3220: Edouard@3220: Edouard@3220: usveticic@3015: usveticic@3015: { usveticic@3015: usveticic@3015: elt:id(" usveticic@3015: usveticic@3015: "), usveticic@3015: usveticic@3015: style:" usveticic@3015: usveticic@3015: ", usveticic@3015: usveticic@3015: value: usveticic@3015: usveticic@3015: usveticic@3015: usveticic@3015: } usveticic@3015: usveticic@3015: , usveticic@3015: usveticic@3015: usveticic@3015: usveticic@3015: usveticic@3015: ], usveticic@3015: usveticic@3015: edouard@3355: edouard@3355: edouard@3355: edouard@3355: edouard@3355: edouard@3355: TextList widget is a svg:group, list items are labeled elements edouard@3355: edouard@3355: in that group. edouard@3355: edouard@3355: edouard@3355: edouard@3355: To use a TextList, clone (svg:use) one of the items inside the widget edouard@3355: edouard@3355: that expects a TextList. edouard@3355: edouard@3355: edouard@3355: edouard@3355: In this list, (translated) text content is what matters. Nevertheless edouard@3355: edouard@3355: text style of the cloned item will be applied in client widget. edouard@3355: edouard@3355: edouard@3355: edouard@3355: A named list of ordered texts edouard@3355: edouard@3355: edouard@3355: edouard@3355: edouard@3355: edouard@3355: texts: [ edouard@3355: edouard@3355: edouard@3355: id(" edouard@3355: edouard@3355: "), edouard@3355: edouard@3355: edouard@3355: ].reverse(), edouard@3355: edouard@3355: edouard@3355: edouard@3355: edouard@3355: edouard@3355: edouard@3355: edouard@3355: TextStyleList widget is a svg:group, list items are labeled elements edouard@3355: edouard@3355: in that group. edouard@3355: edouard@3355: edouard@3355: edouard@3355: To use a TextStyleList, clone (svg:use) one of the items inside the widget edouard@3355: edouard@3355: that expects a TextStyleList. edouard@3355: edouard@3355: edouard@3355: edouard@3355: In this list, only style matters. Text content is ignored. edouard@3355: edouard@3355: edouard@3355: edouard@3355: A named list of named texts edouard@3355: edouard@3355: edouard@3355: edouard@3355: edouard@3355: edouard@3355: styles: { edouard@3355: edouard@3355: edouard@3355: edouard@3355: edouard@3355: edouard@3355: : " edouard@3355: edouard@3355: ", edouard@3355: edouard@3355: edouard@3355: }, edouard@3355: edouard@3355: edouard@3241: edouard@3241: edouard@3241: edouard@3241: edouard@3241: edouard@3241: Button widget takes one boolean variable path, and reflect current true edouard@3241: edouard@3241: or false value by showing "active" or "inactive" labeled element edouard@3241: edouard@3241: respectively. Clicking or touching button toggles variable. edouard@3241: edouard@3241: edouard@3241: edouard@3241: Toggle button reflecting given boolean variable edouard@3241: edouard@3241: edouard@3241: Boolean variable edouard@3241: edouard@3241: edouard@3238: edouard@3238: class edouard@3238: ToggleButtonWidget edouard@3238: extends Widget{ usveticic@3015: usveticic@3015: frequency = 5; usveticic@3015: usveticic@3015: state = 0; usveticic@3015: usveticic@3015: active_style = undefined; usveticic@3015: usveticic@3015: inactive_style = undefined; usveticic@3015: usveticic@3015: usveticic@3015: usveticic@3015: dispatch(value) { usveticic@3015: Edouard@3065: this.state = value; Edouard@3065: Edouard@3065: //redraw toggle button Edouard@3065: Edouard@3065: this.request_animate(); Edouard@3065: Edouard@3065: } Edouard@3065: Edouard@3065: Edouard@3065: Edouard@3065: on_click(evt) { Edouard@3065: Edouard@3065: //toggle state and apply Edouard@3065: Edouard@3220: this.state = this.state ? false : true; Edouard@3058: Edouard@3065: this.apply_hmi_value(0, this.state); Edouard@3065: Edouard@3065: Edouard@3065: Edouard@3065: //redraw toggle button Edouard@3065: Edouard@3065: this.request_animate(); Edouard@3065: Edouard@3065: } Edouard@3065: Edouard@3065: Edouard@3065: Edouard@3220: activate(val) { Edouard@3220: Edouard@3220: let [active, inactive] = val ? ["none",""] : ["", "none"]; Edouard@3220: Edouard@3220: if (this.active_elt) Edouard@3220: Edouard@3220: this.active_elt.style.display = active; Edouard@3220: Edouard@3220: if (this.inactive_elt) Edouard@3220: Edouard@3220: this.inactive_elt.style.display = inactive; Edouard@3220: Edouard@3220: } Edouard@3220: Edouard@3220: Edouard@3220: Edouard@3065: animate(){ Edouard@3065: Edouard@3220: // redraw toggle button on screen refresh Edouard@3220: Edouard@3220: this.activate(this.state); Edouard@3065: Edouard@3065: } Edouard@3065: Edouard@3065: Edouard@3065: Edouard@3065: init() { Edouard@3065: Edouard@3220: this.activate(false); Edouard@3220: Edouard@3220: this.element.onclick = (evt) => this.on_click(evt); usveticic@3015: usveticic@3015: } usveticic@3015: usveticic@3015: } usveticic@3015: usveticic@3015: edouard@3238: dgaberscek@2977: dgaberscek@2977: dgaberscek@2977: dgaberscek@2977: dgaberscek@2977: active inactive dgaberscek@2977: Edouard@3065: dgaberscek@2977: dgaberscek@2977: edouard@2883: edouard@2883: edouard@2883: Made with SVGHMI. https://beremiz.org edouard@2883: Edouard@3264: Edouard@3212: Edouard@3264: Edouard@3212: Edouard@3084: edouard@2883: edouard@2883: edouard@2883: edouard@2883: edouard@2883: Edouard@2753: