<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>