<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>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.</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>'use strict';</p><p class=MsoNormal>/*</p><p class=MsoNormal>Copyright (c) 2017, John Carlson</p><p class=MsoNormal>All rights reserved.</p><p class=MsoNormal><o:p> </o:p></p><p class=MsoNormal>Redistribution and use in source and binary forms, with or without</p><p class=MsoNormal>modification, are permitted provided that the following conditions are met:</p><p class=MsoNormal><o:p> </o:p></p><p class=MsoNormal>* Redistributions of source code must retain the above copyright notice, this</p><p class=MsoNormal> list of conditions and the following disclaimer.</p><p class=MsoNormal><o:p> </o:p></p><p class=MsoNormal>* Redistributions in binary form must reproduce the above copyright notice,</p><p class=MsoNormal> this list of conditions and the following disclaimer in the documentation</p><p class=MsoNormal> and/or other materials provided with the distribution.</p><p class=MsoNormal><o:p> </o:p></p><p class=MsoNormal>* Neither the name of content nor the names of its</p><p class=MsoNormal> contributors may be used to endorse or promote products derived from</p><p class=MsoNormal> this software without specific prior written permission.</p><p class=MsoNormal><o:p> </o:p></p><p class=MsoNormal>THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"</p><p class=MsoNormal>AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE</p><p class=MsoNormal>IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE</p><p class=MsoNormal>DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE</p><p class=MsoNormal>FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL</p><p class=MsoNormal>DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR</p><p class=MsoNormal>SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER</p><p class=MsoNormal>CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,</p><p class=MsoNormal>OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE</p><p class=MsoNormal>OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE</p><p class=MsoNormal>*/</p><p class=MsoNormal><o:p> </o:p></p><p class=MsoNormal><o:p> </o:p></p><p class=MsoNormal>test();</p><p class=MsoNormal><o:p> </o:p></p><p class=MsoNormal>/**</p><p class=MsoNormal> * Override this to get rid of self-test code</p><p class=MsoNormal> * proxyAction should be paired with a node exclusively</p><p class=MsoNormal> */</p><p class=MsoNormal>function test() {</p><p class=MsoNormal> let fromNode = [ {d: { f: 7 , e: [1, 2, 3]}}, { c : [4]}]</p><p class=MsoNormal> let toNode = fromNode;</p><p class=MsoNormal> info("Node originally "+stringify(fromNode));</p><p class=MsoNormal> assert(fromNode, toNode);</p><p class=MsoNormal> let proxyAction = {};</p><p class=MsoNormal> let proxy = createProxy(proxyAction, fromNode);</p><p class=MsoNormal><o:p> </o:p></p><p class=MsoNormal> // Create actionable fields in the fromNode, that set</p><p class=MsoNormal> // fields in the toNode</p><p class=MsoNormal> //</p><p class=MsoNormal> // fields are MFStrings which follow a path down the node, one</p><p class=MsoNormal> // element at a time. The final SFSTring is thei field to modify.</p><p class=MsoNormal> //</p><p class=MsoNormal> // This does not handle script nodes yet (both getting and setting</p><p class=MsoNormal> // values, but could possibly be handled by modifying SetInternalField.</p><p class=MsoNormal> //</p><p class=MsoNormal> route(proxyAction,</p><p class=MsoNormal> fromNode, '"0" "d" "e" "0"',</p><p class=MsoNormal> toNode, '"1" "c"');</p><p class=MsoNormal> route(proxyAction,</p><p class=MsoNormal> fromNode, '"0" "d"',</p><p class=MsoNormal> toNode, '"1"');</p><p class=MsoNormal> route(proxyAction,</p><p class=MsoNormal> fromNode, '"0" "A"',</p><p class=MsoNormal> toNode, '"0" "B"');</p><p class=MsoNormal> // no changes to node yet</p><p class=MsoNormal> assert(fromNode, [{"d":{"f":7,"e":[1,2,3]}},{"c":[4]}]);</p><p class=MsoNormal><o:p> </o:p></p><p class=MsoNormal> setField(proxy, '"0" "d" "e" "0"', 5); </p><p class=MsoNormal> assert(fromNode, [{"d":{"f":7,"e":[5,2,3]}},{"c":5}]);</p><p class=MsoNormal> setField(proxy, '"0" "d" "e" "1"', 8); </p><p class=MsoNormal> assert(fromNode, [{"d":{"f":7,"e":[5,8,3]}},{"c":5}]);</p><p class=MsoNormal> setField(proxy, '"0" "d"', 6);</p><p class=MsoNormal> assert(fromNode, [{"d":6},6]);</p><p class=MsoNormal> setField(proxy, '"0" "d"', 9);</p><p class=MsoNormal> assert(fromNode, [{"d":9},9]);</p><p class=MsoNormal> // add field'</p><p class=MsoNormal> setField(proxy, '"0" "A"', 10);</p><p class=MsoNormal> assert(fromNode, [{"d":9,"B":10,"A":10},9]);</p><p class=MsoNormal> setField(proxy, '"0" "A"', [ "Test!" ]);</p><p class=MsoNormal> assert(fromNode, [{"d":9,"B":["Test!"],"A":["Test!"]},9]);</p><p class=MsoNormal> setField(proxy, '"0" "B"', null);</p><p class=MsoNormal> assert(fromNode, [{"d":9,"B":null,"A":["Test!"]},9]);</p><p class=MsoNormal> setField(proxy, '"0" "A" "0" "0"', ["Cr"]); // set a part of a string</p><p class=MsoNormal> assert(fromNode, [{"d":9,"B":null,"A":["Crest!"]},9]);</p><p class=MsoNormal> setField(proxy, '"0" "A"', { "/": "/" }); // now A and B point to same</p><p class=MsoNormal> assert(fromNode, [{"d":9,"B":{"/":"/"},"A":{"/":"/"}},9]);</p><p class=MsoNormal> setField(proxy, '"0" "A" "/"', "\\");</p><p class=MsoNormal> assert(fromNode, [{"d":9,"B":{"/":"\\"},"A":{"/":"\\"}},9]);</p><p class=MsoNormal> setField(proxy, '"0" "A"', { "\\": "\\" }); // now A and B point to same</p><p class=MsoNormal> assert(fromNode, [{"d":9,"B":{"\\":"\\"},"A":{"\\":"\\"}},9]);</p><p class=MsoNormal> setField(proxy, '"0" "A" "\\"', "]");</p><p class=MsoNormal> assert(fromNode, [{"d":9,"B":{"\\":"]"},"A":{"\\":"]"}},9]);</p><p class=MsoNormal> setField(proxy, '"0" "A"', { "]" : "]" }); // now A and B point to same</p><p class=MsoNormal> assert(fromNode, [{"d":9,"B":{"]":"]"},"A":{"]":"]"}},9]);</p><p class=MsoNormal> setField(proxy, '"0" "A" "]"', "[");</p><p class=MsoNormal> assert(fromNode, [{"d":9,"B":{"]":"["},"A":{"]":"["}},9]);</p><p class=MsoNormal> setField(proxy, '"0" "A"', { "[" : "[" }); // now A and B point to same</p><p class=MsoNormal> assert(fromNode, [{"d":9,"B":{"[":"["},"A":{"[":"["}},9]);</p><p class=MsoNormal> setField(proxy, '"0" "A" "["', "][");</p><p class=MsoNormal> assert(fromNode, [{"d":9,"B":{"[":"]["},"A":{"[":"]["}},9]);</p><p class=MsoNormal> setField(proxy, '"0" "A"', { "][" : "][" }); // now A and B point to same</p><p class=MsoNormal> assert(fromNode, [{"d":9,"B":{"][":"]["},"A":{"][":"]["}},9]);</p><p class=MsoNormal> setField(proxy, '"0" "A" "]["', "][][" );</p><p class=MsoNormal> assert(fromNode, [{"d":9,"B":{"][":"][]["},"A":{"][":"][]["}},9]);</p><p class=MsoNormal> setField(proxy, '"0" "A"', { "][][" : "][][" }); // now A and B point to same</p><p class=MsoNormal> assert(fromNode, [{"d":9,"B":{"][][":"][]["},"A":{"][][":"][]["}},9]);</p><p class=MsoNormal> setField(proxy, '"0" "A" "][]["', "[][]");</p><p class=MsoNormal> assert(fromNode, [{"d":9,"B":{"][][":"[][]"},"A":{"][][":"[][]"}},9]);</p><p class=MsoNormal> setField(proxy, '"0" "A"', { "[][]" : "[][]" }); // now A and B point to same</p><p class=MsoNormal> assert(fromNode, [{"d":9,"B":{"[][]":"[][]"},"A":{"[][]":"[][]"}},9]);</p><p class=MsoNormal> setField(proxy, '"0" "A" "[][]"', "C");</p><p class=MsoNormal> assert(fromNode, [{"d":9,"B":{"[][]":"C"},"A":{"[][]":"C"}},9]);</p><p class=MsoNormal> assert(fromNode, toNode);</p><p class=MsoNormal> info("Node finally "+stringify(fromNode));</p><p class=MsoNormal>}</p><p class=MsoNormal><o:p> </o:p></p><p class=MsoNormal>function setField(proxy, fromField, value) {</p><p class=MsoNormal> let selector = MFStringToProperty(fromField);</p><p class=MsoNormal> raw("\n");</p><p class=MsoNormal> info("Storing "+fromField+" = "+selector+" = "+stringify(value)+" in Proxy");</p><p class=MsoNormal> proxy[selector] = value;</p><p class=MsoNormal>}</p><p class=MsoNormal><o:p> </o:p></p><p class=MsoNormal>/**</p><p class=MsoNormal> * Override this if you don't want test warnings about node</p><p class=MsoNormal> */</p><p class=MsoNormal><o:p> </o:p></p><p class=MsoNormal>function assert(modifiedNode, goldenNode) {</p><p class=MsoNormal> var mod = stringify(modifiedNode);</p><p class=MsoNormal> var testcase = stringify(goldenNode);</p><p class=MsoNormal> if (mod !== testcase) {</p><p class=MsoNormal> fatal("Node "+mod)</p><p class=MsoNormal> fatal(" != "+testcase);</p><p class=MsoNormal> } else {</p><p class=MsoNormal> debug("Node "+mod+" == "+testcase);</p><p class=MsoNormal> info("TEST PASSED");</p><p class=MsoNormal> }</p><p class=MsoNormal>}</p><p class=MsoNormal><o:p> </o:p></p><p class=MsoNormal>/**</p><p class=MsoNormal> * Pass in a JSON parseable object be stringified,</p><p class=MsoNormal> * or a regular JSON object. Or a string to be parsed.</p><p class=MsoNormal> * selector returned the is the field into the proxy.</p><p class=MsoNormal> *</p><p class=MsoNormal> * This should normalize the string, but there may be issues</p><p class=MsoNormal> * with ordering objects.</p><p class=MsoNormal> */</p><p class=MsoNormal>function stringify(selectorField) {</p><p class=MsoNormal> if (typeof selectorField === 'string') {</p><p class=MsoNormal> debug("selector output "+selectorField);</p><p class=MsoNormal> return selectorField;</p><p class=MsoNormal> /*</p><p class=MsoNormal> let indexes = parse(selectorField);</p><p class=MsoNormal> let selector = JSON.stringify(indexes);</p><p class=MsoNormal> debug("selector output "+selector);</p><p class=MsoNormal> return selector;</p><p class=MsoNormal> */</p><p class=MsoNormal> } else {</p><p class=MsoNormal> let selector = JSON.stringify(selectorField);</p><p class=MsoNormal> debug("selector output "+selector);</p><p class=MsoNormal> return selector;</p><p class=MsoNormal> }</p><p class=MsoNormal>}</p><p class=MsoNormal><o:p> </o:p></p><p class=MsoNormal>/**</p><p class=MsoNormal> * breaks up selectorField into component pieces and returns them</p><p class=MsoNormal> */</p><p class=MsoNormal>function parse(selectorField) {</p><p class=MsoNormal> debug("selector input "+selectorField);</p><p class=MsoNormal> if (typeof selectorField === 'string') {</p><p class=MsoNormal> let indexes = JSON.parse(selectorField);</p><p class=MsoNormal> return indexes;</p><p class=MsoNormal> } else {</p><p class=MsoNormal> return selectorField;</p><p class=MsoNormal> }</p><p class=MsoNormal>}</p><p class=MsoNormal><o:p> </o:p></p><p class=MsoNormal>function MFStringToProperty(string) {</p><p class=MsoNormal> debug("MFString input "+string+" "+typeof string);</p><p class=MsoNormal> string = string.replace(/" "/g, ',');</p><p class=MsoNormal> string = string.substr(1, string.length-2);</p><p class=MsoNormal> return string;</p><p class=MsoNormal>}</p><p class=MsoNormal><o:p> </o:p></p><p class=MsoNormal>/*</p><p class=MsoNormal>function JsonToMFString(json) {</p><p class=MsoNormal> let str = stringify(json).split(/,/).join('" "')</p><p class=MsoNormal> str = "'"+str.substr(1, str.length-2)+'"';</p><p class=MsoNormal> return str;</p><p class=MsoNormal>}</p><p class=MsoNormal>*/</p><p class=MsoNormal><o:p> </o:p></p><p class=MsoNormal>function PropertyToJson(string) {</p><p class=MsoNormal> debug("property input "+string+" "+typeof string);</p><p class=MsoNormal> return string.split(/,/);</p><p class=MsoNormal>}</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</p><p class=MsoNormal> */</p><p class=MsoNormal>function raw(string) {</p><p class=MsoNormal> console.log(string);</p><p class=MsoNormal>}</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</p><p class=MsoNormal> */</p><p class=MsoNormal>function fatal(string) {</p><p class=MsoNormal> console.error("FATAL: "+string);</p><p class=MsoNormal>}</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</p><p class=MsoNormal> */</p><p class=MsoNormal>function warning(string) {</p><p class=MsoNormal> console.error("============ WARNING: "+string);</p><p class=MsoNormal>}</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</p><p class=MsoNormal> * disable this function</p><p class=MsoNormal> */</p><p class=MsoNormal>function debug(string) {</p><p class=MsoNormal> // raw(string);</p><p class=MsoNormal>}</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</p><p class=MsoNormal> * disable this function</p><p class=MsoNormal> */</p><p class=MsoNormal>function info(string) {</p><p class=MsoNormal> raw("****** "+string);</p><p class=MsoNormal>}</p><p class=MsoNormal><o:p> </o:p></p><p class=MsoNormal>/**</p><p class=MsoNormal> * setInternalField() -- set a field in a node internally. Use</p><p class=MsoNormal> * setField() and set up routes and the proxy object on the node so</p><p class=MsoNormal> * events flow.</p><p class=MsoNormal> * Override this if you want a different selector language.</p><p class=MsoNormal> * The node is the javascript object to set the field to value on.</p><p class=MsoNormal> * selectorField is a JSON array path of keys and indexes into the node.</p><p class=MsoNormal> * You may use quotes to backslashes to escape things</p><p class=MsoNormal> * The value is set in the node at the selectorField location</p><p class=MsoNormal> *</p><p class=MsoNormal> * The client may want the select to impact several objects. That is up</p><p class=MsoNormal> * to the implementer of setInternalField(). The selectorField affects the</p><p class=MsoNormal> * node.</p><p class=MsoNormal> * Right now we just have a simple JavaScript implementation. Something like</p><p class=MsoNormal> * JSONField is realizable in this framework I think.</p><p class=MsoNormal> *</p><p class=MsoNormal> * You should not use this method to set values on the proxy.</p><p class=MsoNormal> *</p><p class=MsoNormal> * Calls:</p><p class=MsoNormal> * stringify: to return a string for viewing.</p><p class=MsoNormal> * parse: to return javascript object indexes as a selector.</p><p class=MsoNormal> */</p><p class=MsoNormal>function setInternalField(node, selectorField, value) {</p><p class=MsoNormal> debug("Node before "+ stringify(node));</p><p class=MsoNormal> let skipDescendants = 0; // number of descendents to skip</p><p class=MsoNormal> let selectedValue = node;</p><p class=MsoNormal> let higherValue = selectedValue;</p><p class=MsoNormal> var selector = PropertyToJson(selectorField);</p><p class=MsoNormal> let depth = (selector.length - skipDescendants);</p><p class=MsoNormal> debug("Trying to Set "+stringify(node)+stringify(selector)+" = "+</p><p class=MsoNormal> stringify(value));</p><p class=MsoNormal> for (var index = 0; index < depth - 1; index++) {</p><p class=MsoNormal> debug("Index "+index+" is "+selector[index]);</p><p class=MsoNormal> higherValue = selectedValue;</p><p class=MsoNormal> debug("Previous downselected selectedValue === "+</p><p class=MsoNormal> stringify(selectedValue));</p><p class=MsoNormal> debug("Index "+index+" is "+selector[index]);</p><p class=MsoNormal> debug("New Selected Value === "+selectedValue[selector[index]]);</p><p class=MsoNormal> selectedValue = selectedValue[selector[index]];</p><p class=MsoNormal> debug("Now downselected selectedValue === "+selectedValue);</p><p class=MsoNormal> }</p><p class=MsoNormal> if (typeof value === 'string') {</p><p class=MsoNormal> value = value.replace(/\\\\/g, '\u005c');</p><p class=MsoNormal> }</p><p class=MsoNormal> debug("Index "+index+" is "+selector[index]);</p><p class=MsoNormal> /**</p><p class=MsoNormal> * This is the code that has to change to call functions in X3D Scripts</p><p class=MsoNormal> * fields. For toField, I would check to make sure the LHS is a</p><p class=MsoNormal> * function, and pass a value to the function (along with a timestamp),</p><p class=MsoNormal> * for From Field, I would make sure value is a function, and call it</p><p class=MsoNormal> * with a timestamp.</p><p class=MsoNormal> */</p><p class=MsoNormal> if (typeof selectedValue === 'string') {</p><p class=MsoNormal> var str = selectedValue.split('');</p><p class=MsoNormal> info("Setting "+ stringify(higherValue) +</p><p class=MsoNormal> "[" + selector[depth-2] + "] = "+</p><p class=MsoNormal> stringify(value));</p><p class=MsoNormal> str[selector[depth-1]] = value;</p><p class=MsoNormal> higherValue[selector[depth-2]] = str.join('');</p><p class=MsoNormal> // unless there's more than one</p><p class=MsoNormal> } else {</p><p class=MsoNormal> info("Setting "+</p><p class=MsoNormal> stringify(selectedValue) +</p><p class=MsoNormal> "[" + selector[depth-1] + "] = "+ stringify(value));</p><p class=MsoNormal> selectedValue[selector[depth-1]] = value;</p><p class=MsoNormal> // unless there's more than one</p><p class=MsoNormal> }</p><p class=MsoNormal> raw("RESULT node "+stringify(node));</p><p class=MsoNormal> return true;</p><p class=MsoNormal>}</p><p class=MsoNormal><o:p> </o:p></p><p class=MsoNormal><o:p> </o:p></p><p class=MsoNormal>/**</p><p class=MsoNormal> * This proxy works on individual values in a node, because a shadow</p><p class=MsoNormal> * node is keep with fromField kept as proxy objects.</p><p class=MsoNormal> *</p><p class=MsoNormal> * If a route hasn't been set up yet, then the toNode is not affected,</p><p class=MsoNormal> * unless it's a part of the fromSceneGraph.</p><p class=MsoNormal> */</p><p class=MsoNormal><o:p> </o:p></p><p class=MsoNormal>function createProxy(proxyAction, fromNode) {</p><p class=MsoNormal> var proxy = new Proxy(proxyAction, {</p><p class=MsoNormal> set : function(target, property, value, receiver) {</p><p class=MsoNormal> debug("Value set is "+value);</p><p class=MsoNormal> debug("property is "+ property);</p><p class=MsoNormal> for (let action in proxyAction) {</p><p class=MsoNormal> debug(" "+action+" "+typeof proxyAction[property]);</p><p class=MsoNormal> }</p><p class=MsoNormal> if (typeof proxyAction[property] === 'function') {</p><p class=MsoNormal> // set the toNode, act on the route</p><p class=MsoNormal> proxyAction[property](property, value);</p><p class=MsoNormal> } else {</p><p class=MsoNormal> warning("Failed to set value on toNode (no route)");</p><p class=MsoNormal> }</p><p class=MsoNormal> return setInternalField(fromNode, property, value);</p><p class=MsoNormal> }</p><p class=MsoNormal> });</p><p class=MsoNormal> return proxy;</p><p class=MsoNormal>}</p><p class=MsoNormal><o:p> </o:p></p><p class=MsoNormal>/**</p><p class=MsoNormal> * Proxy action, set on route. This should only be called when a route</p><p class=MsoNormal> * is in place. It's private an should not be called by others.</p><p class=MsoNormal> * Node are JavaSCript objects</p><p class=MsoNormal> * Field are selectors into node.</p><p class=MsoNormal> * property is the property being set and should be equal to fromField</p><p class=MsoNormal> * value is the value being set on the toNode. To set the</p><p class=MsoNormal> * fromNode, use setField() on the proxy or setInternalField, if you don'</p><p class=MsoNormal> * want to affect the toNode.</p><p class=MsoNormal> */</p><p class=MsoNormal>function proxySetAction(fromNode, fromField, toNode, toField, property, value) {</p><p class=MsoNormal> let fromProperty = MFStringToProperty(fromField);</p><p class=MsoNormal> if (fromProperty != property) {</p><p class=MsoNormal> fatal("from"+fromField+" out of sync with property "+property+". Did you forget to set a route?");</p><p class=MsoNormal> }</p><p class=MsoNormal> debug("fromField is "+fromField);</p><p class=MsoNormal> debug("toField is "+toField);</p><p class=MsoNormal> debug("property is "+property);</p><p class=MsoNormal> let toProperty = MFStringToProperty(toField);</p><p class=MsoNormal> debug("toProperty is "+toProperty);</p><p class=MsoNormal> if (toNode == fromNode && fromField == toField) {</p><p class=MsoNormal> warning("We don't need to set the same value twice!");</p><p class=MsoNormal> } else {</p><p class=MsoNormal> setInternalField(toNode, toProperty, value);</p><p class=MsoNormal> }</p><p class=MsoNormal><o:p> </o:p></p><p class=MsoNormal> return true;</p><p class=MsoNormal>}</p><p class=MsoNormal><o:p> </o:p></p><p class=MsoNormal>/**</p><p class=MsoNormal> * Activate a proxy route from fromField to toField</p><p class=MsoNormal> * The proxy map is a private object which must be passed around. I will later</p><p class=MsoNormal> * make it unaccessible.</p><p class=MsoNormal> * Field are selectors which can be used with setInternalField()</p><p class=MsoNormal> * fromNode and toNode may be separate. If they are the</p><p class=MsoNormal> * same, be sure that fromField and toField are distinct, or else the</p><p class=MsoNormal> * results may be undetermined.</p><p class=MsoNormal> */</p><p class=MsoNormal>function route(proxyAction, fromNode, fromField, toNode, toField) {</p><p class=MsoNormal> info("<ROUTE fromField='"+ fromField+ "' "+ "toField='"+ toField+ "'/>"</p><p class=MsoNormal> );</p><p class=MsoNormal> if (fromNode === toNode &&</p><p class=MsoNormal> fromField.startsWith(toField) &&</p><p class=MsoNormal> toField.startsWith(fromField) &&</p><p class=MsoNormal> fromField !== toField) {</p><p class=MsoNormal> warning("possible undetermined behavior, fromField "+fromField+" and toField "+toField+" overlap and the node are the same");</p><p class=MsoNormal> }</p><p class=MsoNormal> proxyAction[MFStringToProperty(fromField)] = function(property, value) {</p><p class=MsoNormal> return proxySetAction(fromNode, fromField,</p><p class=MsoNormal> toNode, toField,</p><p class=MsoNormal> property, value);</p><p class=MsoNormal> };</p><p class=MsoNormal>}<o:p></o:p></p></div></body></html>