# HG changeset patch # User Edouard Tisserant # Date 1572445025 -3600 # Node ID 4a81cec5f78617aae2da8b85de9defd6bdbc02aa # Parent 63b9a37b73c707d5a254d30816a11437bef496a7 SVGHMI - prepare page with cached data when switching. This prevents values that do not change and that was already subscribed in previous page from keeping undefined. diff -r 63b9a37b73c7 -r 4a81cec5f786 svghmi/gen_index_xhtml.xslt --- a/svghmi/gen_index_xhtml.xslt Tue Oct 29 11:18:58 2019 +0100 +++ b/svghmi/gen_index_xhtml.xslt Wed Oct 30 15:17:05 2019 +0100 @@ -344,6 +344,38 @@ </xsl:text> <xsl:text> </xsl:text> + <xsl:text>function dispatch_value_to_widget(widget, index, value, oldval) { +</xsl:text> + <xsl:text> let idxidx = widget.indexes.indexOf(index); +</xsl:text> + <xsl:text> if(idxidx == -1){ +</xsl:text> + <xsl:text> throw new Error("Dispatching to widget not interested, should not happen."); +</xsl:text> + <xsl:text> } +</xsl:text> + <xsl:text> let d = widget.dispatch; +</xsl:text> + <xsl:text> if(typeof(d) == "function" && idxidx == 0){ +</xsl:text> + <xsl:text> return d.call(widget, value, oldval); +</xsl:text> + <xsl:text> }else if(typeof(d) == "object" && d.length >= idxidx){ +</xsl:text> + <xsl:text> return d[idxidx].call(widget, value, oldval); +</xsl:text> + <xsl:text> }/* else dispatch_0, ..., dispatch_n ? */ +</xsl:text> + <xsl:text> /*else { +</xsl:text> + <xsl:text> throw new Error("Dunno how to dispatch to widget at index = " + index); +</xsl:text> + <xsl:text> }*/ +</xsl:text> + <xsl:text>} +</xsl:text> + <xsl:text> +</xsl:text> <xsl:text>function dispatch_value(index, value) { </xsl:text> <xsl:text> let widgets = subscribers[index]; @@ -360,378 +392,364 @@ </xsl:text> <xsl:text> for(let widget of widgets){ </xsl:text> - <xsl:text> let idxidx = widget.indexes.indexOf(index); -</xsl:text> - <xsl:text> if(idxidx == -1){ -</xsl:text> - <xsl:text> throw new Error("Dispatching to widget not interested, should not happen."); + <xsl:text> dispatch_value_to_widget(widget, index, value, oldval); +</xsl:text> + <xsl:text> } +</xsl:text> + <xsl:text> } +</xsl:text> + <xsl:text>}; +</xsl:text> + <xsl:text> +</xsl:text> + <xsl:text>function init_widgets() { +</xsl:text> + <xsl:text> Object.keys(hmi_widgets).forEach(function(id) { +</xsl:text> + <xsl:text> let widget = hmi_widgets[id]; +</xsl:text> + <xsl:text> let init = widget.init; +</xsl:text> + <xsl:text> if(typeof(init) == "function"){ +</xsl:text> + <xsl:text> return init.call(widget); +</xsl:text> + <xsl:text> } +</xsl:text> + <xsl:text> }); +</xsl:text> + <xsl:text>}; +</xsl:text> + <xsl:text> +</xsl:text> + <xsl:text>// Open WebSocket to relative "/ws" address +</xsl:text> + <xsl:text>var ws = new WebSocket(window.location.href.replace(/^http(s?:\/\/[^\/]*)\/.*$/, 'ws$1/ws')); +</xsl:text> + <xsl:text>ws.binaryType = 'arraybuffer'; +</xsl:text> + <xsl:text> +</xsl:text> + <xsl:text>const dvgetters = { +</xsl:text> + <xsl:text> INT: [DataView.prototype.getInt16, 2], +</xsl:text> + <xsl:text> BOOL: [DataView.prototype.getInt8, 1] +</xsl:text> + <xsl:text> /* TODO */ +</xsl:text> + <xsl:text>}; +</xsl:text> + <xsl:text> +</xsl:text> + <xsl:text>// Register message reception handler +</xsl:text> + <xsl:text>ws.onmessage = function (evt) { +</xsl:text> + <xsl:text> +</xsl:text> + <xsl:text> let data = evt.data; +</xsl:text> + <xsl:text> let dv = new DataView(data); +</xsl:text> + <xsl:text> let i = 0; +</xsl:text> + <xsl:text> try { +</xsl:text> + <xsl:text> for(let hash_int of hmi_hash) { +</xsl:text> + <xsl:text> if(hash_int != dv.getUint8(i)){ +</xsl:text> + <xsl:text> throw new Error("Hash doesn't match"); +</xsl:text> + <xsl:text> }; +</xsl:text> + <xsl:text> i++; +</xsl:text> + <xsl:text> }; +</xsl:text> + <xsl:text> +</xsl:text> + <xsl:text> while(i < data.byteLength){ +</xsl:text> + <xsl:text> let index = dv.getUint32(i, true); +</xsl:text> + <xsl:text> i += 4; +</xsl:text> + <xsl:text> let iectype = hmitree_types[index]; +</xsl:text> + <xsl:text> if(iectype != undefined){ +</xsl:text> + <xsl:text> let [dvgetter, bytesize] = dvgetters[iectype]; +</xsl:text> + <xsl:text> let value = dvgetter.call(dv,i,true); +</xsl:text> + <xsl:text> dispatch_value(index, value); +</xsl:text> + <xsl:text> i += bytesize; +</xsl:text> + <xsl:text> } else { +</xsl:text> + <xsl:text> throw new Error("Unknown index "+index) </xsl:text> <xsl:text> } </xsl:text> - <xsl:text> let d = widget.dispatch; -</xsl:text> - <xsl:text> if(typeof(d) == "function" && idxidx == 0){ -</xsl:text> - <xsl:text> return d.call(widget, value, oldval); -</xsl:text> - <xsl:text> }else if(typeof(d) == "object" && d.length >= idxidx){ -</xsl:text> - <xsl:text> return d[idxidx].call(widget, value, oldval); -</xsl:text> - <xsl:text> }/* else dispatch_0, ..., dispatch_n ? */ -</xsl:text> - <xsl:text> /*else { -</xsl:text> - <xsl:text> throw new Error("Dunno how to dispatch to widget at index = " + index); -</xsl:text> - <xsl:text> }*/ + <xsl:text> }; +</xsl:text> + <xsl:text> } catch(err) { +</xsl:text> + <xsl:text> // 1003 is for "Unsupported Data" +</xsl:text> + <xsl:text> // ws.close(1003, err.message); +</xsl:text> + <xsl:text> +</xsl:text> + <xsl:text> // TODO : remove debug alert ? +</xsl:text> + <xsl:text> alert("Error : "+err.message+"\nHMI will be reloaded."); +</xsl:text> + <xsl:text> +</xsl:text> + <xsl:text> // force reload ignoring cache +</xsl:text> + <xsl:text> location.reload(true); +</xsl:text> + <xsl:text> } +</xsl:text> + <xsl:text>}; +</xsl:text> + <xsl:text> +</xsl:text> + <xsl:text> +</xsl:text> + <xsl:text>function send_blob(data) { +</xsl:text> + <xsl:text> if(data.length > 0) { +</xsl:text> + <xsl:text> ws.send(new Blob([new Uint8Array(hmi_hash)].concat(data))); +</xsl:text> + <xsl:text> }; +</xsl:text> + <xsl:text>}; +</xsl:text> + <xsl:text> +</xsl:text> + <xsl:text>const typedarray_types = { +</xsl:text> + <xsl:text> INT: Int16Array, +</xsl:text> + <xsl:text> BOOL: Uint8Array +</xsl:text> + <xsl:text> /* TODO */ +</xsl:text> + <xsl:text>}; +</xsl:text> + <xsl:text> +</xsl:text> + <xsl:text>function send_reset() { +</xsl:text> + <xsl:text> send_blob(new Uint8Array([1])); /* reset = 1 */ +</xsl:text> + <xsl:text>}; +</xsl:text> + <xsl:text> +</xsl:text> + <xsl:text>// subscription state, as it should be in hmi server +</xsl:text> + <xsl:text>// hmitree indexed array of integers +</xsl:text> + <xsl:text>var subscriptions = hmitree_types.map(_ignored => 0); +</xsl:text> + <xsl:text> +</xsl:text> + <xsl:text>// subscription state as needed by widget now +</xsl:text> + <xsl:text>// hmitree indexed array of Sets of widgets objects +</xsl:text> + <xsl:text>var subscribers = hmitree_types.map(_ignored => new Set()); +</xsl:text> + <xsl:text> +</xsl:text> + <xsl:text>function update_subscriptions() { +</xsl:text> + <xsl:text> let delta = []; +</xsl:text> + <xsl:text> for(let index = 0; index < subscribers.length; index++){ +</xsl:text> + <xsl:text> let widgets = subscribers[index]; +</xsl:text> + <xsl:text> +</xsl:text> + <xsl:text> // periods are in ms +</xsl:text> + <xsl:text> let previous_period = subscriptions[index]; +</xsl:text> + <xsl:text> +</xsl:text> + <xsl:text> let new_period = 0; +</xsl:text> + <xsl:text> if(widgets.size > 0) { +</xsl:text> + <xsl:text> let maxfreq = 0; +</xsl:text> + <xsl:text> for(let widget of widgets) +</xsl:text> + <xsl:text> if(maxfreq < widget.frequency) +</xsl:text> + <xsl:text> maxfreq = widget.frequency; +</xsl:text> + <xsl:text> +</xsl:text> + <xsl:text> if(maxfreq != 0) +</xsl:text> + <xsl:text> new_period = 1000/maxfreq; </xsl:text> <xsl:text> } </xsl:text> + <xsl:text> +</xsl:text> + <xsl:text> if(previous_period != new_period) { +</xsl:text> + <xsl:text> subscriptions[index] = new_period; +</xsl:text> + <xsl:text> delta.push( +</xsl:text> + <xsl:text> new Uint8Array([2]), /* subscribe = 2 */ +</xsl:text> + <xsl:text> new Uint32Array([index]), +</xsl:text> + <xsl:text> new Uint16Array([new_period])); +</xsl:text> + <xsl:text> } +</xsl:text> <xsl:text> } </xsl:text> - <xsl:text>}; -</xsl:text> - <xsl:text> -</xsl:text> - <xsl:text>function init_widgets() { -</xsl:text> - <xsl:text> Object.keys(hmi_widgets).forEach(function(id) { -</xsl:text> - <xsl:text> let widget = hmi_widgets[id]; -</xsl:text> - <xsl:text> let init = widget.init; -</xsl:text> - <xsl:text> if(typeof(init) == "function"){ -</xsl:text> - <xsl:text> return init.call(widget); + <xsl:text> send_blob(delta); +</xsl:text> + <xsl:text>}; +</xsl:text> + <xsl:text> +</xsl:text> + <xsl:text>function send_hmi_value(index, value) { +</xsl:text> + <xsl:text> let iectype = hmitree_types[index]; +</xsl:text> + <xsl:text> let jstype = typedarray_types[iectype]; +</xsl:text> + <xsl:text> send_blob([ +</xsl:text> + <xsl:text> new Uint8Array([0]), /* setval = 0 */ +</xsl:text> + <xsl:text> new Uint32Array([index]), +</xsl:text> + <xsl:text> new jstype([value])]); +</xsl:text> + <xsl:text> +</xsl:text> + <xsl:text> cache[index] = value; +</xsl:text> + <xsl:text>}; +</xsl:text> + <xsl:text> +</xsl:text> + <xsl:text>function change_hmi_value(index, opstr) { +</xsl:text> + <xsl:text> let op = opstr[0]; +</xsl:text> + <xsl:text> let given_val = opstr.slice(1); +</xsl:text> + <xsl:text> let old_val = cache[index] +</xsl:text> + <xsl:text> let new_val; +</xsl:text> + <xsl:text> switch(op){ +</xsl:text> + <xsl:text> case "=": +</xsl:text> + <xsl:text> eval("new_val"+opstr); +</xsl:text> + <xsl:text> break; +</xsl:text> + <xsl:text> case "+": +</xsl:text> + <xsl:text> case "-": +</xsl:text> + <xsl:text> case "*": +</xsl:text> + <xsl:text> case "/": +</xsl:text> + <xsl:text> if(old_val != undefined) +</xsl:text> + <xsl:text> new_val = eval("old_val"+opstr); +</xsl:text> + <xsl:text> break; +</xsl:text> + <xsl:text> } +</xsl:text> + <xsl:text> if(new_val != undefined && old_val != new_val) +</xsl:text> + <xsl:text> send_hmi_value(index, new_val); +</xsl:text> + <xsl:text> return new_val; +</xsl:text> + <xsl:text>} +</xsl:text> + <xsl:text> +</xsl:text> + <xsl:text>var current_page; +</xsl:text> + <xsl:text> +</xsl:text> + <xsl:text>function switch_page(page_name) { +</xsl:text> + <xsl:text> let old_desc = page_desc[current_page]; +</xsl:text> + <xsl:text> let new_desc = page_desc[page_name]; +</xsl:text> + <xsl:text> /* TODO hide / show widgets */ +</xsl:text> + <xsl:text> +</xsl:text> + <xsl:text> /* remove subsribers of previous page if any */ +</xsl:text> + <xsl:text> if(old_desc) for(let widget of old_desc.widgets){ +</xsl:text> + <xsl:text> for(let index of widget.indexes){ +</xsl:text> + <xsl:text> subscribers[index].delete(widget); </xsl:text> <xsl:text> } </xsl:text> - <xsl:text> }); -</xsl:text> - <xsl:text>}; -</xsl:text> - <xsl:text> -</xsl:text> - <xsl:text>// Open WebSocket to relative "/ws" address -</xsl:text> - <xsl:text>var ws = new WebSocket(window.location.href.replace(/^http(s?:\/\/[^\/]*)\/.*$/, 'ws$1/ws')); -</xsl:text> - <xsl:text>ws.binaryType = 'arraybuffer'; -</xsl:text> - <xsl:text> -</xsl:text> - <xsl:text>const dvgetters = { -</xsl:text> - <xsl:text> INT: [DataView.prototype.getInt16, 2], -</xsl:text> - <xsl:text> BOOL: [DataView.prototype.getInt8, 1] -</xsl:text> - <xsl:text> /* TODO */ -</xsl:text> - <xsl:text>}; -</xsl:text> - <xsl:text> -</xsl:text> - <xsl:text>// Register message reception handler -</xsl:text> - <xsl:text>ws.onmessage = function (evt) { -</xsl:text> - <xsl:text> -</xsl:text> - <xsl:text> let data = evt.data; -</xsl:text> - <xsl:text> let dv = new DataView(data); -</xsl:text> - <xsl:text> let i = 0; -</xsl:text> - <xsl:text> try { -</xsl:text> - <xsl:text> for(let hash_int of hmi_hash) { -</xsl:text> - <xsl:text> if(hash_int != dv.getUint8(i)){ -</xsl:text> - <xsl:text> throw new Error("Hash doesn't match"); -</xsl:text> - <xsl:text> }; -</xsl:text> - <xsl:text> i++; -</xsl:text> - <xsl:text> }; -</xsl:text> - <xsl:text> -</xsl:text> - <xsl:text> while(i < data.byteLength){ -</xsl:text> - <xsl:text> let index = dv.getUint32(i, true); -</xsl:text> - <xsl:text> i += 4; -</xsl:text> - <xsl:text> let iectype = hmitree_types[index]; -</xsl:text> - <xsl:text> if(iectype != undefined){ -</xsl:text> - <xsl:text> let [dvgetter, bytesize] = dvgetters[iectype]; -</xsl:text> - <xsl:text> let value = dvgetter.call(dv,i,true); -</xsl:text> - <xsl:text> dispatch_value(index, value); -</xsl:text> - <xsl:text> i += bytesize; -</xsl:text> - <xsl:text> } else { -</xsl:text> - <xsl:text> throw new Error("Unknown index "+index) + <xsl:text> } +</xsl:text> + <xsl:text> +</xsl:text> + <xsl:text> if(new_desc) { +</xsl:text> + <xsl:text> /* add new subsribers if any */ +</xsl:text> + <xsl:text> for(let widget of new_desc.widgets){ +</xsl:text> + <xsl:text> for(let index of widget.indexes){ +</xsl:text> + <xsl:text> subscribers[index].add(widget); +</xsl:text> + <xsl:text> let cached_val = cache[index]; +</xsl:text> + <xsl:text> if(cached_val != undefined) +</xsl:text> + <xsl:text> dispatch_value_to_widget(widget, index, cached_val, cached_val); +</xsl:text> + <xsl:text> </xsl:text> <xsl:text> } </xsl:text> - <xsl:text> }; -</xsl:text> - <xsl:text> } catch(err) { -</xsl:text> - <xsl:text> // 1003 is for "Unsupported Data" -</xsl:text> - <xsl:text> // ws.close(1003, err.message); -</xsl:text> - <xsl:text> -</xsl:text> - <xsl:text> // TODO : remove debug alert ? -</xsl:text> - <xsl:text> alert("Error : "+err.message+"\nHMI will be reloaded."); -</xsl:text> - <xsl:text> -</xsl:text> - <xsl:text> // force reload ignoring cache -</xsl:text> - <xsl:text> location.reload(true); -</xsl:text> - <xsl:text> } -</xsl:text> - <xsl:text>}; -</xsl:text> - <xsl:text> -</xsl:text> - <xsl:text> -</xsl:text> - <xsl:text>function send_blob(data) { -</xsl:text> - <xsl:text> if(data.length > 0) { -</xsl:text> - <xsl:text> ws.send(new Blob([new Uint8Array(hmi_hash)].concat(data))); -</xsl:text> - <xsl:text> }; -</xsl:text> - <xsl:text>}; -</xsl:text> - <xsl:text> -</xsl:text> - <xsl:text>const typedarray_types = { -</xsl:text> - <xsl:text> INT: Int16Array, -</xsl:text> - <xsl:text> BOOL: Uint8Array -</xsl:text> - <xsl:text> /* TODO */ -</xsl:text> - <xsl:text>}; -</xsl:text> - <xsl:text> -</xsl:text> - <xsl:text>function send_reset() { -</xsl:text> - <xsl:text> send_blob(new Uint8Array([1])); /* reset = 1 */ -</xsl:text> - <xsl:text>}; -</xsl:text> - <xsl:text> -</xsl:text> - <xsl:text>// subscription state, as it should be in hmi server -</xsl:text> - <xsl:text>// hmitree indexed array of integers -</xsl:text> - <xsl:text>var subscriptions = hmitree_types.map(_ignored => 0); -</xsl:text> - <xsl:text> -</xsl:text> - <xsl:text>// subscription state as needed by widget now -</xsl:text> - <xsl:text>// hmitree indexed array of Sets of widgets objects -</xsl:text> - <xsl:text>var subscribers = hmitree_types.map(_ignored => new Set()); -</xsl:text> - <xsl:text> -</xsl:text> - <xsl:text>function update_subscriptions() { -</xsl:text> - <xsl:text> let delta = []; -</xsl:text> - <xsl:text> for(let index = 0; index < subscribers.length; index++){ -</xsl:text> - <xsl:text> let widgets = subscribers[index]; -</xsl:text> - <xsl:text> -</xsl:text> - <xsl:text> // periods are in ms -</xsl:text> - <xsl:text> let previous_period = subscriptions[index]; -</xsl:text> - <xsl:text> -</xsl:text> - <xsl:text> let new_period = 0; -</xsl:text> - <xsl:text> if(widgets.size > 0) { -</xsl:text> - <xsl:text> let maxfreq = 0; -</xsl:text> - <xsl:text> for(let widget of widgets) -</xsl:text> - <xsl:text> if(maxfreq < widget.frequency) -</xsl:text> - <xsl:text> maxfreq = widget.frequency; -</xsl:text> - <xsl:text> -</xsl:text> - <xsl:text> if(maxfreq != 0) -</xsl:text> - <xsl:text> new_period = 1000/maxfreq; -</xsl:text> <xsl:text> } </xsl:text> - <xsl:text> -</xsl:text> - <xsl:text> if(previous_period != new_period) { -</xsl:text> - <xsl:text> subscriptions[index] = new_period; -</xsl:text> - <xsl:text> delta.push( -</xsl:text> - <xsl:text> new Uint8Array([2]), /* subscribe = 2 */ -</xsl:text> - <xsl:text> new Uint32Array([index]), -</xsl:text> - <xsl:text> new Uint16Array([new_period])); -</xsl:text> - <xsl:text> } -</xsl:text> - <xsl:text> } -</xsl:text> - <xsl:text> send_blob(delta); -</xsl:text> - <xsl:text>}; -</xsl:text> - <xsl:text> -</xsl:text> - <xsl:text>function send_hmi_value(index, value) { -</xsl:text> - <xsl:text> let iectype = hmitree_types[index]; -</xsl:text> - <xsl:text> let jstype = typedarray_types[iectype]; -</xsl:text> - <xsl:text> send_blob([ -</xsl:text> - <xsl:text> new Uint8Array([0]), /* setval = 0 */ -</xsl:text> - <xsl:text> new Uint32Array([index]), -</xsl:text> - <xsl:text> new jstype([value])]); -</xsl:text> - <xsl:text> -</xsl:text> - <xsl:text> cache[index] = value; -</xsl:text> - <xsl:text>}; -</xsl:text> - <xsl:text> -</xsl:text> - <xsl:text>function change_hmi_value(index, opstr) { -</xsl:text> - <xsl:text> let op = opstr[0]; -</xsl:text> - <xsl:text> let given_val = opstr.slice(1); -</xsl:text> - <xsl:text> let old_val = cache[index] -</xsl:text> - <xsl:text> let new_val; -</xsl:text> - <xsl:text> switch(op){ -</xsl:text> - <xsl:text> case "=": -</xsl:text> - <xsl:text> eval("new_val"+opstr); -</xsl:text> - <xsl:text> break; -</xsl:text> - <xsl:text> case "+": -</xsl:text> - <xsl:text> case "-": -</xsl:text> - <xsl:text> case "*": -</xsl:text> - <xsl:text> case "/": -</xsl:text> - <xsl:text> if(old_val != undefined) -</xsl:text> - <xsl:text> new_val = eval("old_val"+opstr); -</xsl:text> - <xsl:text> break; -</xsl:text> - <xsl:text> } -</xsl:text> - <xsl:text> if(new_val != undefined && old_val != new_val) -</xsl:text> - <xsl:text> send_hmi_value(index, new_val); -</xsl:text> - <xsl:text> return new_val; -</xsl:text> - <xsl:text>} -</xsl:text> - <xsl:text> -</xsl:text> - <xsl:text>var current_page; -</xsl:text> - <xsl:text> -</xsl:text> - <xsl:text>function switch_page(page_name) { -</xsl:text> - <xsl:text> let old_desc = page_desc[current_page]; -</xsl:text> - <xsl:text> let new_desc = page_desc[page_name]; -</xsl:text> - <xsl:text> /* TODO hide / show widgets */ -</xsl:text> - <xsl:text> -</xsl:text> - <xsl:text> /* remove subsribers of previous page if any */ -</xsl:text> - <xsl:text> if(old_desc) for(let widget of old_desc.widgets){ -</xsl:text> - <xsl:text> for(let index of widget.indexes){ -</xsl:text> - <xsl:text> subscribers[index].delete(widget); -</xsl:text> - <xsl:text> } -</xsl:text> - <xsl:text> } -</xsl:text> - <xsl:text> -</xsl:text> - <xsl:text> if(new_desc) { -</xsl:text> - <xsl:text> /* add new subsribers if any */ -</xsl:text> - <xsl:text> for(let widget of new_desc.widgets){ -</xsl:text> - <xsl:text> for(let index of widget.indexes){ -</xsl:text> - <xsl:text> subscribers[index].add(widget); -</xsl:text> - <xsl:text> } -</xsl:text> - <xsl:text> } -</xsl:text> <xsl:text> svg_root.setAttribute('viewBox',new_desc.bbox.join(" ")); </xsl:text> + <xsl:text> // TODO dispatch current cache in newly opened page +</xsl:text> <xsl:text> } </xsl:text> <xsl:text> current_page = page_name; diff -r 63b9a37b73c7 -r 4a81cec5f786 svghmi/svghmi.js --- a/svghmi/svghmi.js Tue Oct 29 11:18:58 2019 +0100 +++ b/svghmi/svghmi.js Wed Oct 30 15:17:05 2019 +0100 @@ -2,6 +2,22 @@ var cache = hmitree_types.map(_ignored => undefined); +function dispatch_value_to_widget(widget, index, value, oldval) { + let idxidx = widget.indexes.indexOf(index); + if(idxidx == -1){ + throw new Error("Dispatching to widget not interested, should not happen."); + } + let d = widget.dispatch; + if(typeof(d) == "function" && idxidx == 0){ + return d.call(widget, value, oldval); + }else if(typeof(d) == "object" && d.length >= idxidx){ + return d[idxidx].call(widget, value, oldval); + }/* else dispatch_0, ..., dispatch_n ? */ + /*else { + throw new Error("Dunno how to dispatch to widget at index = " + index); + }*/ +} + function dispatch_value(index, value) { let widgets = subscribers[index]; @@ -10,19 +26,7 @@ if(widgets.size > 0) { for(let widget of widgets){ - let idxidx = widget.indexes.indexOf(index); - if(idxidx == -1){ - throw new Error("Dispatching to widget not interested, should not happen."); - } - let d = widget.dispatch; - if(typeof(d) == "function" && idxidx == 0){ - return d.call(widget, value, oldval); - }else if(typeof(d) == "object" && d.length >= idxidx){ - return d[idxidx].call(widget, value, oldval); - }/* else dispatch_0, ..., dispatch_n ? */ - /*else { - throw new Error("Dunno how to dispatch to widget at index = " + index); - }*/ + dispatch_value_to_widget(widget, index, value, oldval); } } }; @@ -193,6 +197,10 @@ for(let widget of new_desc.widgets){ for(let index of widget.indexes){ subscribers[index].add(widget); + let cached_val = cache[index]; + if(cached_val != undefined) + dispatch_value_to_widget(widget, index, cached_val, cached_val); + } } svg_root.setAttribute('viewBox',new_desc.bbox.join(" "));