<html xmlns:o="urn:schemas-microsoft-com:office:office" xmlns:w="urn:schemas-microsoft-com:office:word" xmlns:m="http://schemas.microsoft.com/office/2004/12/omml" xmlns="http://www.w3.org/TR/REC-html40"><head><meta http-equiv=Content-Type content="text/html; charset=utf-8"><meta name=Generator content="Microsoft Word 15 (filtered medium)"><style><!--
/* Font Definitions */
@font-face
{font-family:"Cambria Math";
panose-1:2 4 5 3 5 4 6 3 2 4;}
@font-face
{font-family:Calibri;
panose-1:2 15 5 2 2 2 4 3 2 4;}
/* Style Definitions */
p.MsoNormal, li.MsoNormal, div.MsoNormal
{margin:0in;
margin-bottom:.0001pt;
font-size:11.0pt;
font-family:"Calibri",sans-serif;}
a:link, span.MsoHyperlink
{mso-style-priority:99;
color:blue;
text-decoration:underline;}
a:visited, span.MsoHyperlinkFollowed
{mso-style-priority:99;
color:#954F72;
text-decoration:underline;}
.MsoChpDefault
{mso-style-type:export-only;}
@page WordSection1
{size:8.5in 11.0in;
margin:1.0in 1.0in 1.0in 1.0in;}
div.WordSection1
{page:WordSection1;}
--></style></head><body lang=EN-US link=blue vlink="#954F72"><div class=WordSection1><p class=MsoNormal>Note that this code handles MFNodes as fromNodes and toNodes, ala JavaScript.</p><p class=MsoNormal><o:p> </o:p></p><p class=MsoNormal>John</p><p class=MsoNormal><o:p> </o:p></p><p class=MsoNormal>Sent from <a href="https://go.microsoft.com/fwlink/?LinkId=550986">Mail</a> for Windows 10</p><p class=MsoNormal><o:p> </o:p></p><div style='mso-element:para-border-div;border:none;border-top:solid #E1E1E1 1.0pt;padding:3.0pt 0in 0in 0in'><p class=MsoNormal style='border:none;padding:0in'><b>From: </b><a href="mailto:yottzumm@gmail.com">yottzumm@gmail.com</a><br><b>Sent: </b>Sunday, April 16, 2017 11:03 PM<br><b>To: </b><a href="mailto:andreasplesch@gmail.com">Andreas Plesch</a><br><b>Cc: </b><a href="mailto:x3d-public@web3d.org">X3D Graphics public mailing list</a><br><b>Subject: </b>RE: Routes with JS Proxy</p></div><p class=MsoNormal><o:p> </o:p></p><p class=MsoNormal>Perhaps this translation will make it more palatable! I can proxy to any field in the subtree of a node! The only issue is that proxyAction properties must be unique, therefore, you need the full path (which is okay in a single object, and in a tree). It’s essentially the identical code! Scenegraph == Node and path == field.<o:p></o:p></p><p class=MsoNormal><o:p> </o:p></p><p class=MsoNormal>John<o:p></o:p></p><p class=MsoNormal><o:p> </o:p></p><p class=MsoNormal>'use strict';<o:p></o:p></p><p class=MsoNormal>/*<o:p></o:p></p><p class=MsoNormal>Copyright (c) 2017, John Carlson<o:p></o:p></p><p class=MsoNormal>All rights reserved.<o:p></o:p></p><p class=MsoNormal><o:p> </o:p></p><p class=MsoNormal>Redistribution and use in source and binary forms, with or without<o:p></o:p></p><p class=MsoNormal>modification, are permitted provided that the following conditions are met:<o:p></o:p></p><p class=MsoNormal><o:p> </o:p></p><p class=MsoNormal>* Redistributions of source code must retain the above copyright notice, this<o:p></o:p></p><p class=MsoNormal> list of conditions and the following disclaimer.<o:p></o:p></p><p class=MsoNormal><o:p> </o:p></p><p class=MsoNormal>* Redistributions in binary form must reproduce the above copyright notice,<o:p></o:p></p><p class=MsoNormal> this list of conditions and the following disclaimer in the documentation<o:p></o:p></p><p class=MsoNormal> and/or other materials provided with the distribution.<o:p></o:p></p><p class=MsoNormal><o:p> </o:p></p><p class=MsoNormal>* Neither the name of content nor the names of its<o:p></o:p></p><p class=MsoNormal> contributors may be used to endorse or promote products derived from<o:p></o:p></p><p class=MsoNormal> this software without specific prior written permission.<o:p></o:p></p><p class=MsoNormal><o:p> </o:p></p><p class=MsoNormal>THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"<o:p></o:p></p><p class=MsoNormal>AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE<o:p></o:p></p><p class=MsoNormal>IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE<o:p></o:p></p><p class=MsoNormal>DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE<o:p></o:p></p><p class=MsoNormal>FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL<o:p></o:p></p><p class=MsoNormal>DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR<o:p></o:p></p><p class=MsoNormal>SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER<o:p></o:p></p><p class=MsoNormal>CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,<o:p></o:p></p><p class=MsoNormal>OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE<o:p></o:p></p><p class=MsoNormal>OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE<o:p></o:p></p><p class=MsoNormal>*/<o:p></o:p></p><p class=MsoNormal><o:p> </o:p></p><p class=MsoNormal><o:p> </o:p></p><p class=MsoNormal>test();<o:p></o:p></p><p class=MsoNormal><o:p> </o:p></p><p class=MsoNormal>/**<o:p></o:p></p><p class=MsoNormal>* Override this to get rid of self-test code<o:p></o:p></p><p class=MsoNormal>* proxyAction should be paired with a node exclusively<o:p></o:p></p><p class=MsoNormal>*/<o:p></o:p></p><p class=MsoNormal>function test() {<o:p></o:p></p><p class=MsoNormal> let fromNode = [ {d: { f: 7 , e: [1, 2, 3]}}, { c : [4]}]<o:p></o:p></p><p class=MsoNormal> let toNode = fromNode;<o:p></o:p></p><p class=MsoNormal> info("Node originally "+stringify(fromNode));<o:p></o:p></p><p class=MsoNormal> assert(fromNode, toNode);<o:p></o:p></p><p class=MsoNormal> let proxyAction = {};<o:p></o:p></p><p class=MsoNormal> let proxy = createProxy(proxyAction, fromNode);<o:p></o:p></p><p class=MsoNormal><o:p> </o:p></p><p class=MsoNormal> // Create actionable fields in the fromNode, that set<o:p></o:p></p><p class=MsoNormal> // fields in the toNode<o:p></o:p></p><p class=MsoNormal> //<o:p></o:p></p><p class=MsoNormal> // fields are MFStrings which follow a path down the node, one<o:p></o:p></p><p class=MsoNormal> // element at a time. The final SFSTring is thei field to modify.<o:p></o:p></p><p class=MsoNormal> //<o:p></o:p></p><p class=MsoNormal> // This does not handle script nodes yet (both getting and setting<o:p></o:p></p><p class=MsoNormal> // values, but could possibly be handled by modifying SetInternalField.<o:p></o:p></p><p class=MsoNormal> //<o:p></o:p></p><p class=MsoNormal> route(proxyAction,<o:p></o:p></p><p class=MsoNormal> fromNode, '"0" "d" "e" "0"',<o:p></o:p></p><p class=MsoNormal> toNode, '"1" "c"');<o:p></o:p></p><p class=MsoNormal> route(proxyAction,<o:p></o:p></p><p class=MsoNormal> fromNode, '"0" "d"',<o:p></o:p></p><p class=MsoNormal> toNode, '"1"');<o:p></o:p></p><p class=MsoNormal> route(proxyAction,<o:p></o:p></p><p class=MsoNormal> fromNode, '"0" "A"',<o:p></o:p></p><p class=MsoNormal> toNode, '"0" "B"');<o:p></o:p></p><p class=MsoNormal> // no changes to node yet<o:p></o:p></p><p class=MsoNormal> assert(fromNode, [{"d":{"f":7,"e":[1,2,3]}},{"c":[4]}]);<o:p></o:p></p><p class=MsoNormal><o:p> </o:p></p><p class=MsoNormal> setField(proxy, '"0" "d" "e" "0"', 5); <o:p></o:p></p><p class=MsoNormal> assert(fromNode, [{"d":{"f":7,"e":[5,2,3]}},{"c":5}]);<o:p></o:p></p><p class=MsoNormal> setField(proxy, '"0" "d" "e" "1"', 8); <o:p></o:p></p><p class=MsoNormal> assert(fromNode, [{"d":{"f":7,"e":[5,8,3]}},{"c":5}]);<o:p></o:p></p><p class=MsoNormal> setField(proxy, '"0" "d"', 6);<o:p></o:p></p><p class=MsoNormal> assert(fromNode, [{"d":6},6]);<o:p></o:p></p><p class=MsoNormal> setField(proxy, '"0" "d"', 9);<o:p></o:p></p><p class=MsoNormal> assert(fromNode, [{"d":9},9]);<o:p></o:p></p><p class=MsoNormal> // add field'<o:p></o:p></p><p class=MsoNormal> setField(proxy, '"0" "A"', 10);<o:p></o:p></p><p class=MsoNormal> assert(fromNode, [{"d":9,"B":10,"A":10},9]);<o:p></o:p></p><p class=MsoNormal> setField(proxy, '"0" "A"', [ "Test!" ]);<o:p></o:p></p><p class=MsoNormal> assert(fromNode, [{"d":9,"B":["Test!"],"A":["Test!"]},9]);<o:p></o:p></p><p class=MsoNormal> setField(proxy, '"0" "B"', null);<o:p></o:p></p><p class=MsoNormal> assert(fromNode, [{"d":9,"B":null,"A":["Test!"]},9]);<o:p></o:p></p><p class=MsoNormal> setField(proxy, '"0" "A" "0" "0"', ["Cr"]); // set a part of a string<o:p></o:p></p><p class=MsoNormal> assert(fromNode, [{"d":9,"B":null,"A":["Crest!"]},9]);<o:p></o:p></p><p class=MsoNormal> setField(proxy, '"0" "A"', { "/": "/" }); // now A and B point to same<o:p></o:p></p><p class=MsoNormal> assert(fromNode, [{"d":9,"B":{"/":"/"},"A":{"/":"/"}},9]);<o:p></o:p></p><p class=MsoNormal> setField(proxy, '"0" "A" "/"', "\\");<o:p></o:p></p><p class=MsoNormal> assert(fromNode, [{"d":9,"B":{"/":"\\"},"A":{"/":"\\"}},9]);<o:p></o:p></p><p class=MsoNormal> setField(proxy, '"0" "A"', { "\\": "\\" }); // now A and B point to same<o:p></o:p></p><p class=MsoNormal> assert(fromNode, [{"d":9,"B":{"\\":"\\"},"A":{"\\":"\\"}},9]);<o:p></o:p></p><p class=MsoNormal> setField(proxy, '"0" "A" "\\"', "]");<o:p></o:p></p><p class=MsoNormal> assert(fromNode, [{"d":9,"B":{"\\":"]"},"A":{"\\":"]"}},9]);<o:p></o:p></p><p class=MsoNormal> setField(proxy, '"0" "A"', { "]" : "]" }); // now A and B point to same<o:p></o:p></p><p class=MsoNormal> assert(fromNode, [{"d":9,"B":{"]":"]"},"A":{"]":"]"}},9]);<o:p></o:p></p><p class=MsoNormal> setField(proxy, '"0" "A" "]"', "[");<o:p></o:p></p><p class=MsoNormal> assert(fromNode, [{"d":9,"B":{"]":"["},"A":{"]":"["}},9]);<o:p></o:p></p><p class=MsoNormal> setField(proxy, '"0" "A"', { "[" : "[" }); // now A and B point to same<o:p></o:p></p><p class=MsoNormal> assert(fromNode, [{"d":9,"B":{"[":"["},"A":{"[":"["}},9]);<o:p></o:p></p><p class=MsoNormal> setField(proxy, '"0" "A" "["', "][");<o:p></o:p></p><p class=MsoNormal> assert(fromNode, [{"d":9,"B":{"[":"]["},"A":{"[":"]["}},9]);<o:p></o:p></p><p class=MsoNormal> setField(proxy, '"0" "A"', { "][" : "][" }); // now A and B point to same<o:p></o:p></p><p class=MsoNormal> assert(fromNode, [{"d":9,"B":{"][":"]["},"A":{"][":"]["}},9]);<o:p></o:p></p><p class=MsoNormal> setField(proxy, '"0" "A" "]["', "][][" );<o:p></o:p></p><p class=MsoNormal> assert(fromNode, [{"d":9,"B":{"][":"][]["},"A":{"][":"][]["}},9]);<o:p></o:p></p><p class=MsoNormal> setField(proxy, '"0" "A"', { "][][" : "][][" }); // now A and B point to same<o:p></o:p></p><p class=MsoNormal> assert(fromNode, [{"d":9,"B":{"][][":"][]["},"A":{"][][":"][]["}},9]);<o:p></o:p></p><p class=MsoNormal> setField(proxy, '"0" "A" "][]["', "[][]");<o:p></o:p></p><p class=MsoNormal> assert(fromNode, [{"d":9,"B":{"][][":"[][]"},"A":{"][][":"[][]"}},9]);<o:p></o:p></p><p class=MsoNormal> setField(proxy, '"0" "A"', { "[][]" : "[][]" }); // now A and B point to same<o:p></o:p></p><p class=MsoNormal> assert(fromNode, [{"d":9,"B":{"[][]":"[][]"},"A":{"[][]":"[][]"}},9]);<o:p></o:p></p><p class=MsoNormal> setField(proxy, '"0" "A" "[][]"', "C");<o:p></o:p></p><p class=MsoNormal> assert(fromNode, [{"d":9,"B":{"[][]":"C"},"A":{"[][]":"C"}},9]);<o:p></o:p></p><p class=MsoNormal> assert(fromNode, toNode);<o:p></o:p></p><p class=MsoNormal> info("Node finally "+stringify(fromNode));<o:p></o:p></p><p class=MsoNormal>}<o:p></o:p></p><p class=MsoNormal><o:p> </o:p></p><p class=MsoNormal>function setField(proxy, fromField, value) {<o:p></o:p></p><p class=MsoNormal> let selector = MFStringToProperty(fromField);<o:p></o:p></p><p class=MsoNormal> raw("\n");<o:p></o:p></p><p class=MsoNormal> info("Storing "+fromField+" = "+selector+" = "+stringify(value)+" in Proxy");<o:p></o:p></p><p class=MsoNormal> proxy[selector] = value;<o:p></o:p></p><p class=MsoNormal>}<o:p></o:p></p><p class=MsoNormal><o:p> </o:p></p><p class=MsoNormal>/**<o:p></o:p></p><p class=MsoNormal>* Override this if you don't want test warnings about node<o:p></o:p></p><p class=MsoNormal>*/<o:p></o:p></p><p class=MsoNormal><o:p> </o:p></p><p class=MsoNormal>function assert(modifiedNode, goldenNode) {<o:p></o:p></p><p class=MsoNormal> var mod = stringify(modifiedNode);<o:p></o:p></p><p class=MsoNormal> var testcase = stringify(goldenNode);<o:p></o:p></p><p class=MsoNormal> if (mod !== testcase) {<o:p></o:p></p><p class=MsoNormal> fatal("Node "+mod)<o:p></o:p></p><p class=MsoNormal> fatal(" != "+testcase);<o:p></o:p></p><p class=MsoNormal> } else {<o:p></o:p></p><p class=MsoNormal> debug("Node "+mod+" == "+testcase);<o:p></o:p></p><p class=MsoNormal> info("TEST PASSED");<o:p></o:p></p><p class=MsoNormal> }<o:p></o:p></p><p class=MsoNormal>}<o:p></o:p></p><p class=MsoNormal><o:p> </o:p></p><p class=MsoNormal>/**<o:p></o:p></p><p class=MsoNormal>* Pass in a JSON parseable object be stringified,<o:p></o:p></p><p class=MsoNormal>* or a regular JSON object. Or a string to be parsed.<o:p></o:p></p><p class=MsoNormal>* selector returned the is the field into the proxy.<o:p></o:p></p><p class=MsoNormal>*<o:p></o:p></p><p class=MsoNormal>* This should normalize the string, but there may be issues<o:p></o:p></p><p class=MsoNormal>* with ordering objects.<o:p></o:p></p><p class=MsoNormal>*/<o:p></o:p></p><p class=MsoNormal>function stringify(selectorField) {<o:p></o:p></p><p class=MsoNormal> if (typeof selectorField === 'string') {<o:p></o:p></p><p class=MsoNormal> debug("selector output "+selectorField);<o:p></o:p></p><p class=MsoNormal> return selectorField;<o:p></o:p></p><p class=MsoNormal> /*<o:p></o:p></p><p class=MsoNormal> let indexes = parse(selectorField);<o:p></o:p></p><p class=MsoNormal> let selector = JSON.stringify(indexes);<o:p></o:p></p><p class=MsoNormal> debug("selector output "+selector);<o:p></o:p></p><p class=MsoNormal> return selector;<o:p></o:p></p><p class=MsoNormal> */<o:p></o:p></p><p class=MsoNormal> } else {<o:p></o:p></p><p class=MsoNormal> let selector = JSON.stringify(selectorField);<o:p></o:p></p><p class=MsoNormal> debug("selector output "+selector);<o:p></o:p></p><p class=MsoNormal> return selector;<o:p></o:p></p><p class=MsoNormal> }<o:p></o:p></p><p class=MsoNormal>}<o:p></o:p></p><p class=MsoNormal><o:p> </o:p></p><p class=MsoNormal>/**<o:p></o:p></p><p class=MsoNormal>* breaks up selectorField into component pieces and returns them<o:p></o:p></p><p class=MsoNormal>*/<o:p></o:p></p><p class=MsoNormal>function parse(selectorField) {<o:p></o:p></p><p class=MsoNormal> debug("selector input "+selectorField);<o:p></o:p></p><p class=MsoNormal> if (typeof selectorField === 'string') {<o:p></o:p></p><p class=MsoNormal> let indexes = JSON.parse(selectorField);<o:p></o:p></p><p class=MsoNormal> return indexes;<o:p></o:p></p><p class=MsoNormal> } else {<o:p></o:p></p><p class=MsoNormal> return selectorField;<o:p></o:p></p><p class=MsoNormal> }<o:p></o:p></p><p class=MsoNormal>}<o:p></o:p></p><p class=MsoNormal><o:p> </o:p></p><p class=MsoNormal>function MFStringToProperty(string) {<o:p></o:p></p><p class=MsoNormal> debug("MFString input "+string+" "+typeof string);<o:p></o:p></p><p class=MsoNormal> string = string.replace(/" "/g, ',');<o:p></o:p></p><p class=MsoNormal> string = string.substr(1, string.length-2);<o:p></o:p></p><p class=MsoNormal> return string;<o:p></o:p></p><p class=MsoNormal>}<o:p></o:p></p><p class=MsoNormal><o:p> </o:p></p><p class=MsoNormal>/*<o:p></o:p></p><p class=MsoNormal>function JsonToMFString(json) {<o:p></o:p></p><p class=MsoNormal> let str = stringify(json).split(/,/).join('" "')<o:p></o:p></p><p class=MsoNormal> str = "'"+str.substr(1, str.length-2)+'"';<o:p></o:p></p><p class=MsoNormal> return str;<o:p></o:p></p><p class=MsoNormal>}<o:p></o:p></p><p class=MsoNormal>*/<o:p></o:p></p><p class=MsoNormal><o:p> </o:p></p><p class=MsoNormal>function PropertyToJson(string) {<o:p></o:p></p><p class=MsoNormal> debug("property input "+string+" "+typeof string);<o:p></o:p></p><p class=MsoNormal> return string.split(/,/);<o:p></o:p></p><p class=MsoNormal>}<o:p></o:p></p><p class=MsoNormal><o:p> </o:p></p><p class=MsoNormal>/** override this function if you want a feature other than a raw message<o:p></o:p></p><p class=MsoNormal>*/<o:p></o:p></p><p class=MsoNormal>function raw(string) {<o:p></o:p></p><p class=MsoNormal> console.log(string);<o:p></o:p></p><p class=MsoNormal>}<o:p></o:p></p><p class=MsoNormal><o:p> </o:p></p><p class=MsoNormal>/** override this function if you want a feature other than a fatal message<o:p></o:p></p><p class=MsoNormal>*/<o:p></o:p></p><p class=MsoNormal>function fatal(string) {<o:p></o:p></p><p class=MsoNormal> console.error("FATAL: "+string);<o:p></o:p></p><p class=MsoNormal>}<o:p></o:p></p><p class=MsoNormal><o:p> </o:p></p><p class=MsoNormal>/** override this function if you want a feature other than a warning message<o:p></o:p></p><p class=MsoNormal>*/<o:p></o:p></p><p class=MsoNormal>function warning(string) {<o:p></o:p></p><p class=MsoNormal> console.error("============ WARNING: "+string);<o:p></o:p></p><p class=MsoNormal>}<o:p></o:p></p><p class=MsoNormal><o:p> </o:p></p><p class=MsoNormal>/** override this function if you want a feature other than console.log or to<o:p></o:p></p><p class=MsoNormal>* disable this function<o:p></o:p></p><p class=MsoNormal>*/<o:p></o:p></p><p class=MsoNormal>function debug(string) {<o:p></o:p></p><p class=MsoNormal> // raw(string);<o:p></o:p></p><p class=MsoNormal>}<o:p></o:p></p><p class=MsoNormal><o:p> </o:p></p><p class=MsoNormal>/** override this function if you want a feature other than console.log or to<o:p></o:p></p><p class=MsoNormal>* disable this function<o:p></o:p></p><p class=MsoNormal>*/<o:p></o:p></p><p class=MsoNormal>function info(string) {<o:p></o:p></p><p class=MsoNormal> raw("****** "+string);<o:p></o:p></p><p class=MsoNormal>}<o:p></o:p></p><p class=MsoNormal><o:p> </o:p></p><p class=MsoNormal>/**<o:p></o:p></p><p class=MsoNormal>* setInternalField() -- set a field in a node internally. Use<o:p></o:p></p><p class=MsoNormal>* setField() and set up routes and the proxy object on the node so<o:p></o:p></p><p class=MsoNormal>* events flow.<o:p></o:p></p><p class=MsoNormal>* Override this if you want a different selector language.<o:p></o:p></p><p class=MsoNormal>* The node is the javascript object to set the field to value on.<o:p></o:p></p><p class=MsoNormal>* selectorField is a JSON array path of keys and indexes into the node.<o:p></o:p></p><p class=MsoNormal>* You may use quotes to backslashes to escape things<o:p></o:p></p><p class=MsoNormal>* The value is set in the node at the selectorField location<o:p></o:p></p><p class=MsoNormal>*<o:p></o:p></p><p class=MsoNormal>* The client may want the select to impact several objects. That is up<o:p></o:p></p><p class=MsoNormal>* to the implementer of setInternalField(). The selectorField affects the<o:p></o:p></p><p class=MsoNormal>* node.<o:p></o:p></p><p class=MsoNormal>* Right now we just have a simple JavaScript implementation. Something like<o:p></o:p></p><p class=MsoNormal>* JSONField is realizable in this framework I think.<o:p></o:p></p><p class=MsoNormal>*<o:p></o:p></p><p class=MsoNormal>* You should not use this method to set values on the proxy.<o:p></o:p></p><p class=MsoNormal>*<o:p></o:p></p><p class=MsoNormal>* Calls:<o:p></o:p></p><p class=MsoNormal>* stringify: to return a string for viewing.<o:p></o:p></p><p class=MsoNormal>* parse: to return javascript object indexes as a selector.<o:p></o:p></p><p class=MsoNormal>*/<o:p></o:p></p><p class=MsoNormal>function setInternalField(node, selectorField, value) {<o:p></o:p></p><p class=MsoNormal> debug("Node before "+ stringify(node));<o:p></o:p></p><p class=MsoNormal> let skipDescendants = 0; // number of descendents to skip<o:p></o:p></p><p class=MsoNormal> let selectedValue = node;<o:p></o:p></p><p class=MsoNormal> let higherValue = selectedValue;<o:p></o:p></p><p class=MsoNormal> var selector = PropertyToJson(selectorField);<o:p></o:p></p><p class=MsoNormal> let depth = (selector.length - skipDescendants);<o:p></o:p></p><p class=MsoNormal> debug("Trying to Set "+stringify(node)+stringify(selector)+" = "+<o:p></o:p></p><p class=MsoNormal> stringify(value));<o:p></o:p></p><p class=MsoNormal> for (var index = 0; index < depth - 1; index++) {<o:p></o:p></p><p class=MsoNormal> debug("Index "+index+" is "+selector[index]);<o:p></o:p></p><p class=MsoNormal> higherValue = selectedValue;<o:p></o:p></p><p class=MsoNormal> debug("Previous downselected selectedValue === "+<o:p></o:p></p><p class=MsoNormal> stringify(selectedValue));<o:p></o:p></p><p class=MsoNormal> debug("Index "+index+" is "+selector[index]);<o:p></o:p></p><p class=MsoNormal> debug("New Selected Value === "+selectedValue[selector[index]]);<o:p></o:p></p><p class=MsoNormal> selectedValue = selectedValue[selector[index]];<o:p></o:p></p><p class=MsoNormal> debug("Now downselected selectedValue === "+selectedValue);<o:p></o:p></p><p class=MsoNormal> }<o:p></o:p></p><p class=MsoNormal> if (typeof value === 'string') {<o:p></o:p></p><p class=MsoNormal> value = value.replace(/\\\\/g, '\u005c');<o:p></o:p></p><p class=MsoNormal> }<o:p></o:p></p><p class=MsoNormal> debug("Index "+index+" is "+selector[index]);<o:p></o:p></p><p class=MsoNormal> /**<o:p></o:p></p><p class=MsoNormal> * This is the code that has to change to call functions in X3D Scripts<o:p></o:p></p><p class=MsoNormal> * fields. For toField, I would check to make sure the LHS is a<o:p></o:p></p><p class=MsoNormal> * function, and pass a value to the function (along with a timestamp),<o:p></o:p></p><p class=MsoNormal> * for From Field, I would make sure value is a function, and call it<o:p></o:p></p><p class=MsoNormal> * with a timestamp.<o:p></o:p></p><p class=MsoNormal> */<o:p></o:p></p><p class=MsoNormal> if (typeof selectedValue === 'string') {<o:p></o:p></p><p class=MsoNormal> var str = selectedValue.split('');<o:p></o:p></p><p class=MsoNormal> info("Setting "+ stringify(higherValue) +<o:p></o:p></p><p class=MsoNormal> "[" + selector[depth-2] + "] = "+<o:p></o:p></p><p class=MsoNormal> stringify(value));<o:p></o:p></p><p class=MsoNormal> str[selector[depth-1]] = value;<o:p></o:p></p><p class=MsoNormal> higherValue[selector[depth-2]] = str.join('');<o:p></o:p></p><p class=MsoNormal> // unless there's more than one<o:p></o:p></p><p class=MsoNormal> } else {<o:p></o:p></p><p class=MsoNormal> info("Setting "+<o:p></o:p></p><p class=MsoNormal> stringify(selectedValue) +<o:p></o:p></p><p class=MsoNormal> "[" + selector[depth-1] + "] = "+ stringify(value));<o:p></o:p></p><p class=MsoNormal> selectedValue[selector[depth-1]] = value;<o:p></o:p></p><p class=MsoNormal> // unless there's more than one<o:p></o:p></p><p class=MsoNormal> }<o:p></o:p></p><p class=MsoNormal> raw("RESULT node "+stringify(node));<o:p></o:p></p><p class=MsoNormal> return true;<o:p></o:p></p><p class=MsoNormal>}<o:p></o:p></p><p class=MsoNormal><o:p> </o:p></p><p class=MsoNormal><o:p> </o:p></p><p class=MsoNormal>/**<o:p></o:p></p><p class=MsoNormal>* This proxy works on individual values in a node, because a shadow<o:p></o:p></p><p class=MsoNormal>* node is keep with fromField kept as proxy objects.<o:p></o:p></p><p class=MsoNormal>*<o:p></o:p></p><p class=MsoNormal>* If a route hasn't been set up yet, then the toNode is not affected,<o:p></o:p></p><p class=MsoNormal>* unless it's a part of the fromSceneGraph.<o:p></o:p></p><p class=MsoNormal>*/<o:p></o:p></p><p class=MsoNormal><o:p> </o:p></p><p class=MsoNormal>function createProxy(proxyAction, fromNode) {<o:p></o:p></p><p class=MsoNormal> var proxy = new Proxy(proxyAction, {<o:p></o:p></p><p class=MsoNormal> set : function(target, property, value, receiver) {<o:p></o:p></p><p class=MsoNormal> debug("Value set is "+value);<o:p></o:p></p><p class=MsoNormal> debug("property is "+ property);<o:p></o:p></p><p class=MsoNormal> for (let action in proxyAction) {<o:p></o:p></p><p class=MsoNormal> debug(" "+action+" "+typeof proxyAction[property]);<o:p></o:p></p><p class=MsoNormal> }<o:p></o:p></p><p class=MsoNormal> if (typeof proxyAction[property] === 'function') {<o:p></o:p></p><p class=MsoNormal> // set the toNode, act on the route<o:p></o:p></p><p class=MsoNormal> proxyAction[property](property, value);<o:p></o:p></p><p class=MsoNormal> } else {<o:p></o:p></p><p class=MsoNormal> warning("Failed to set value on toNode (no route)");<o:p></o:p></p><p class=MsoNormal> }<o:p></o:p></p><p class=MsoNormal> return setInternalField(fromNode, property, value);<o:p></o:p></p><p class=MsoNormal> }<o:p></o:p></p><p class=MsoNormal> });<o:p></o:p></p><p class=MsoNormal> return proxy;<o:p></o:p></p><p class=MsoNormal>}<o:p></o:p></p><p class=MsoNormal><o:p> </o:p></p><p class=MsoNormal>/**<o:p></o:p></p><p class=MsoNormal>* Proxy action, set on route. This should only be called when a route<o:p></o:p></p><p class=MsoNormal>* is in place. It's private an should not be called by others.<o:p></o:p></p><p class=MsoNormal>* Node are JavaSCript objects<o:p></o:p></p><p class=MsoNormal>* Field are selectors into node.<o:p></o:p></p><p class=MsoNormal>* property is the property being set and should be equal to fromField<o:p></o:p></p><p class=MsoNormal>* value is the value being set on the toNode. To set the<o:p></o:p></p><p class=MsoNormal>* fromNode, use setField() on the proxy or setInternalField, if you don'<o:p></o:p></p><p class=MsoNormal>* want to affect the toNode.<o:p></o:p></p><p class=MsoNormal>*/<o:p></o:p></p><p class=MsoNormal>function proxySetAction(fromNode, fromField, toNode, toField, property, value) {<o:p></o:p></p><p class=MsoNormal> let fromProperty = MFStringToProperty(fromField);<o:p></o:p></p><p class=MsoNormal> if (fromProperty != property) {<o:p></o:p></p><p class=MsoNormal> fatal("from"+fromField+" out of sync with property "+property+". Did you forget to set a route?");<o:p></o:p></p><p class=MsoNormal> }<o:p></o:p></p><p class=MsoNormal> debug("fromField is "+fromField);<o:p></o:p></p><p class=MsoNormal> debug("toField is "+toField);<o:p></o:p></p><p class=MsoNormal> debug("property is "+property);<o:p></o:p></p><p class=MsoNormal> let toProperty = MFStringToProperty(toField);<o:p></o:p></p><p class=MsoNormal> debug("toProperty is "+toProperty);<o:p></o:p></p><p class=MsoNormal> if (toNode == fromNode && fromField == toField) {<o:p></o:p></p><p class=MsoNormal> warning("We don't need to set the same value twice!");<o:p></o:p></p><p class=MsoNormal> } else {<o:p></o:p></p><p class=MsoNormal> setInternalField(toNode, toProperty, value);<o:p></o:p></p><p class=MsoNormal> }<o:p></o:p></p><p class=MsoNormal><o:p> </o:p></p><p class=MsoNormal> return true;<o:p></o:p></p><p class=MsoNormal>}<o:p></o:p></p><p class=MsoNormal><o:p> </o:p></p><p class=MsoNormal>/**<o:p></o:p></p><p class=MsoNormal>* Activate a proxy route from fromField to toField<o:p></o:p></p><p class=MsoNormal>* The proxy map is a private object which must be passed around. I will later<o:p></o:p></p><p class=MsoNormal>* make it unaccessible.<o:p></o:p></p><p class=MsoNormal>* Field are selectors which can be used with setInternalField()<o:p></o:p></p><p class=MsoNormal>* fromNode and toNode may be separate. If they are the<o:p></o:p></p><p class=MsoNormal>* same, be sure that fromField and toField are distinct, or else the<o:p></o:p></p><p class=MsoNormal>* results may be undetermined.<o:p></o:p></p><p class=MsoNormal>*/<o:p></o:p></p><p class=MsoNormal>function route(proxyAction, fromNode, fromField, toNode, toField) {<o:p></o:p></p><p class=MsoNormal> info("<ROUTE fromField='"+ fromField+ "' "+ "toField='"+ toField+ "'/>"<o:p></o:p></p><p class=MsoNormal> );<o:p></o:p></p><p class=MsoNormal> if (fromNode === toNode &&<o:p></o:p></p><p class=MsoNormal> fromField.startsWith(toField) &&<o:p></o:p></p><p class=MsoNormal> toField.startsWith(fromField) &&<o:p></o:p></p><p class=MsoNormal> fromField !== toField) {<o:p></o:p></p><p class=MsoNormal> warning("possible undetermined behavior, fromField "+fromField+" and toField "+toField+" overlap and the node are the same");<o:p></o:p></p><p class=MsoNormal> }<o:p></o:p></p><p class=MsoNormal> proxyAction[MFStringToProperty(fromField)] = function(property, value) {<o:p></o:p></p><p class=MsoNormal> return proxySetAction(fromNode, fromField,<o:p></o:p></p><p class=MsoNormal> toNode, toField,<o:p></o:p></p><p class=MsoNormal> property, value);<o:p></o:p></p><p class=MsoNormal> };<o:p></o:p></p><p class=MsoNormal>}<o:p></o:p></p><p class=MsoNormal><o:p> </o:p></p></div></body></html>