| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686168716881689169016911692169316941695169616971698169917001701170217031704170517061707170817091710171117121713171417151716171717181719172017211722172317241725172617271728172917301731173217331734173517361737173817391740174117421743174417451746174717481749175017511752175317541755175617571758175917601761176217631764176517661767176817691770177117721773177417751776177717781779178017811782178317841785178617871788178917901791179217931794179517961797179817991800180118021803180418051806180718081809181018111812181318141815181618171818181918201821182218231824182518261827182818291830183118321833183418351836183718381839184018411842184318441845184618471848184918501851185218531854185518561857185818591860186118621863186418651866186718681869187018711872187318741875187618771878187918801881188218831884188518861887188818891890189118921893189418951896189718981899190019011902190319041905190619071908190919101911191219131914191519161917191819191920192119221923192419251926192719281929193019311932193319341935193619371938193919401941194219431944194519461947194819491950195119521953195419551956195719581959196019611962196319641965196619671968196919701971197219731974197519761977197819791980198119821983198419851986198719881989199019911992199319941995199619971998199920002001200220032004200520062007200820092010201120122013201420152016201720182019202020212022202320242025202620272028202920302031203220332034203520362037203820392040204120422043204420452046204720482049205020512052205320542055205620572058205920602061206220632064206520662067206820692070207120722073207420752076207720782079208020812082208320842085208620872088208920902091209220932094209520962097209820992100210121022103210421052106210721082109211021112112211321142115211621172118211921202121212221232124212521262127212821292130213121322133213421352136213721382139214021412142214321442145214621472148214921502151215221532154215521562157215821592160216121622163216421652166216721682169217021712172217321742175217621772178217921802181218221832184218521862187218821892190219121922193219421952196219721982199220022012202220322042205220622072208220922102211221222132214221522162217221822192220222122222223222422252226222722282229223022312232223322342235223622372238223922402241224222432244224522462247224822492250225122522253225422552256225722582259226022612262226322642265226622672268226922702271227222732274227522762277227822792280228122822283228422852286228722882289229022912292229322942295229622972298229923002301230223032304230523062307230823092310231123122313231423152316231723182319232023212322232323242325232623272328232923302331233223332334233523362337233823392340234123422343234423452346234723482349235023512352235323542355235623572358235923602361236223632364236523662367236823692370237123722373237423752376237723782379238023812382238323842385238623872388238923902391239223932394239523962397239823992400240124022403240424052406240724082409241024112412241324142415241624172418241924202421242224232424242524262427242824292430243124322433243424352436243724382439244024412442244324442445244624472448244924502451245224532454245524562457245824592460246124622463246424652466246724682469247024712472247324742475247624772478247924802481248224832484248524862487248824892490249124922493249424952496249724982499250025012502250325042505250625072508250925102511251225132514251525162517251825192520252125222523252425252526252725282529253025312532253325342535253625372538253925402541254225432544254525462547254825492550255125522553255425552556255725582559256025612562256325642565256625672568256925702571257225732574257525762577257825792580258125822583258425852586258725882589259025912592259325942595259625972598259926002601260226032604260526062607260826092610261126122613261426152616261726182619262026212622262326242625262626272628262926302631263226332634263526362637263826392640264126422643264426452646264726482649265026512652265326542655265626572658265926602661266226632664266526662667266826692670267126722673267426752676267726782679268026812682268326842685268626872688268926902691269226932694269526962697269826992700270127022703270427052706270727082709271027112712271327142715271627172718271927202721272227232724272527262727272827292730273127322733273427352736273727382739274027412742274327442745274627472748274927502751275227532754275527562757275827592760276127622763276427652766276727682769277027712772277327742775277627772778277927802781278227832784278527862787278827892790279127922793279427952796279727982799280028012802280328042805280628072808280928102811281228132814281528162817281828192820282128222823282428252826282728282829283028312832283328342835283628372838283928402841284228432844284528462847284828492850285128522853285428552856285728582859286028612862286328642865286628672868286928702871287228732874287528762877287828792880288128822883288428852886288728882889289028912892289328942895289628972898289929002901290229032904290529062907290829092910291129122913291429152916291729182919292029212922292329242925292629272928292929302931293229332934293529362937293829392940294129422943294429452946294729482949295029512952295329542955295629572958295929602961296229632964296529662967296829692970297129722973297429752976297729782979298029812982298329842985298629872988298929902991299229932994299529962997299829993000300130023003300430053006300730083009301030113012301330143015301630173018301930203021302230233024302530263027302830293030303130323033303430353036303730383039304030413042304330443045304630473048304930503051305230533054305530563057305830593060306130623063306430653066306730683069307030713072307330743075307630773078307930803081308230833084308530863087308830893090309130923093309430953096309730983099310031013102310331043105310631073108310931103111311231133114311531163117311831193120312131223123312431253126312731283129313031313132313331343135313631373138313931403141314231433144314531463147314831493150315131523153315431553156315731583159316031613162316331643165316631673168316931703171317231733174317531763177317831793180318131823183318431853186318731883189319031913192319331943195319631973198319932003201320232033204320532063207320832093210321132123213321432153216321732183219322032213222322332243225322632273228322932303231323232333234323532363237323832393240324132423243324432453246324732483249325032513252325332543255325632573258325932603261326232633264326532663267326832693270327132723273327432753276327732783279328032813282328332843285328632873288328932903291329232933294329532963297329832993300330133023303330433053306330733083309331033113312331333143315331633173318331933203321332233233324332533263327332833293330333133323333333433353336333733383339334033413342334333443345334633473348334933503351335233533354335533563357335833593360336133623363336433653366336733683369337033713372337333743375337633773378337933803381338233833384338533863387338833893390339133923393339433953396339733983399340034013402340334043405340634073408340934103411341234133414341534163417341834193420342134223423342434253426342734283429343034313432343334343435343634373438343934403441344234433444344534463447344834493450345134523453345434553456345734583459346034613462346334643465346634673468346934703471347234733474347534763477347834793480348134823483348434853486348734883489349034913492349334943495349634973498349935003501350235033504350535063507350835093510351135123513351435153516351735183519352035213522352335243525352635273528352935303531353235333534353535363537353835393540354135423543354435453546354735483549355035513552355335543555355635573558355935603561356235633564356535663567356835693570357135723573357435753576357735783579358035813582358335843585358635873588358935903591359235933594359535963597359835993600360136023603360436053606360736083609361036113612361336143615361636173618361936203621362236233624362536263627362836293630363136323633363436353636363736383639364036413642364336443645364636473648364936503651365236533654365536563657365836593660366136623663366436653666366736683669367036713672367336743675367636773678367936803681368236833684368536863687368836893690369136923693369436953696369736983699370037013702370337043705370637073708370937103711371237133714371537163717371837193720372137223723372437253726372737283729373037313732373337343735373637373738373937403741374237433744374537463747374837493750375137523753375437553756375737583759376037613762376337643765376637673768376937703771377237733774377537763777377837793780378137823783378437853786378737883789379037913792379337943795379637973798379938003801380238033804380538063807380838093810381138123813381438153816381738183819382038213822382338243825382638273828382938303831383238333834383538363837383838393840384138423843384438453846384738483849385038513852385338543855385638573858385938603861386238633864386538663867386838693870387138723873387438753876387738783879388038813882388338843885388638873888388938903891389238933894389538963897389838993900390139023903390439053906390739083909391039113912391339143915391639173918391939203921392239233924392539263927392839293930393139323933393439353936393739383939394039413942394339443945394639473948394939503951395239533954395539563957395839593960396139623963396439653966396739683969397039713972397339743975397639773978397939803981398239833984398539863987398839893990399139923993399439953996399739983999400040014002400340044005400640074008400940104011401240134014401540164017401840194020402140224023402440254026402740284029403040314032403340344035403640374038403940404041404240434044404540464047404840494050405140524053405440554056405740584059406040614062406340644065406640674068406940704071407240734074407540764077407840794080408140824083408440854086408740884089409040914092409340944095409640974098409941004101410241034104410541064107410841094110411141124113411441154116411741184119412041214122412341244125412641274128412941304131413241334134413541364137413841394140414141424143414441454146414741484149415041514152415341544155415641574158415941604161416241634164416541664167416841694170417141724173417441754176417741784179418041814182418341844185418641874188418941904191419241934194419541964197419841994200420142024203420442054206420742084209421042114212421342144215421642174218421942204221422242234224422542264227422842294230423142324233423442354236423742384239424042414242424342444245424642474248424942504251425242534254425542564257425842594260426142624263426442654266426742684269427042714272427342744275427642774278427942804281428242834284428542864287428842894290429142924293429442954296429742984299430043014302430343044305430643074308430943104311431243134314431543164317431843194320432143224323432443254326432743284329433043314332433343344335433643374338433943404341434243434344434543464347434843494350435143524353435443554356435743584359436043614362436343644365436643674368436943704371437243734374437543764377437843794380438143824383438443854386438743884389439043914392439343944395439643974398439944004401440244034404440544064407440844094410441144124413441444154416441744184419442044214422442344244425442644274428442944304431443244334434443544364437443844394440444144424443444444454446444744484449445044514452445344544455445644574458445944604461446244634464446544664467446844694470447144724473447444754476447744784479448044814482448344844485448644874488448944904491449244934494449544964497449844994500450145024503450445054506450745084509451045114512451345144515451645174518451945204521452245234524452545264527452845294530453145324533453445354536453745384539454045414542454345444545454645474548454945504551455245534554455545564557455845594560456145624563456445654566456745684569457045714572457345744575457645774578457945804581458245834584458545864587458845894590459145924593459445954596459745984599460046014602460346044605460646074608460946104611461246134614461546164617461846194620462146224623462446254626462746284629463046314632463346344635463646374638463946404641464246434644464546464647464846494650465146524653465446554656465746584659466046614662466346644665466646674668466946704671467246734674467546764677467846794680468146824683468446854686468746884689469046914692469346944695469646974698469947004701470247034704470547064707470847094710471147124713471447154716471747184719472047214722472347244725472647274728472947304731473247334734473547364737473847394740474147424743474447454746474747484749475047514752475347544755475647574758475947604761476247634764476547664767476847694770477147724773477447754776477747784779478047814782478347844785478647874788478947904791479247934794479547964797479847994800480148024803480448054806480748084809481048114812481348144815481648174818481948204821482248234824482548264827482848294830483148324833483448354836483748384839484048414842484348444845484648474848484948504851485248534854485548564857485848594860486148624863486448654866486748684869487048714872487348744875487648774878487948804881488248834884488548864887488848894890489148924893489448954896489748984899490049014902490349044905490649074908490949104911491249134914491549164917491849194920492149224923492449254926492749284929493049314932493349344935493649374938493949404941494249434944494549464947494849494950495149524953495449554956495749584959496049614962496349644965496649674968496949704971497249734974497549764977497849794980498149824983498449854986498749884989499049914992499349944995499649974998499950005001500250035004500550065007500850095010501150125013501450155016501750185019502050215022502350245025502650275028502950305031503250335034503550365037503850395040504150425043504450455046504750485049505050515052505350545055505650575058505950605061506250635064506550665067506850695070507150725073507450755076507750785079508050815082508350845085508650875088508950905091509250935094509550965097509850995100510151025103510451055106510751085109511051115112511351145115511651175118511951205121512251235124512551265127512851295130513151325133513451355136513751385139514051415142514351445145514651475148514951505151515251535154515551565157515851595160516151625163516451655166516751685169517051715172517351745175517651775178517951805181518251835184518551865187518851895190519151925193519451955196519751985199520052015202520352045205520652075208520952105211521252135214521552165217521852195220522152225223522452255226522752285229523052315232523352345235523652375238523952405241524252435244524552465247524852495250525152525253525452555256525752585259526052615262526352645265526652675268526952705271527252735274527552765277527852795280528152825283528452855286528752885289529052915292529352945295529652975298529953005301530253035304530553065307530853095310531153125313531453155316531753185319532053215322532353245325532653275328532953305331533253335334533553365337533853395340534153425343534453455346534753485349535053515352535353545355535653575358535953605361536253635364536553665367536853695370537153725373537453755376537753785379538053815382538353845385538653875388538953905391539253935394539553965397539853995400540154025403540454055406540754085409541054115412541354145415541654175418541954205421542254235424542554265427542854295430543154325433543454355436543754385439544054415442544354445445544654475448544954505451545254535454545554565457545854595460546154625463546454655466546754685469547054715472547354745475547654775478547954805481548254835484548554865487548854895490549154925493549454955496549754985499550055015502550355045505550655075508550955105511551255135514551555165517551855195520552155225523552455255526552755285529553055315532553355345535553655375538553955405541554255435544554555465547554855495550555155525553555455555556555755585559556055615562556355645565556655675568556955705571557255735574557555765577557855795580558155825583558455855586558755885589559055915592559355945595559655975598559956005601560256035604560556065607560856095610561156125613561456155616561756185619562056215622562356245625562656275628562956305631563256335634563556365637563856395640564156425643564456455646564756485649565056515652565356545655565656575658565956605661566256635664566556665667566856695670567156725673567456755676567756785679568056815682568356845685568656875688568956905691569256935694569556965697569856995700570157025703570457055706570757085709571057115712571357145715571657175718571957205721572257235724572557265727572857295730573157325733573457355736573757385739574057415742574357445745574657475748574957505751575257535754575557565757575857595760576157625763576457655766576757685769577057715772577357745775577657775778577957805781578257835784578557865787578857895790579157925793579457955796579757985799580058015802580358045805580658075808580958105811581258135814581558165817581858195820582158225823582458255826582758285829583058315832583358345835583658375838583958405841584258435844584558465847584858495850585158525853585458555856585758585859586058615862586358645865586658675868586958705871587258735874587558765877587858795880588158825883588458855886588758885889589058915892589358945895589658975898589959005901590259035904590559065907590859095910591159125913591459155916591759185919592059215922592359245925592659275928592959305931593259335934593559365937593859395940594159425943594459455946594759485949595059515952595359545955595659575958595959605961596259635964596559665967596859695970597159725973597459755976597759785979598059815982598359845985598659875988598959905991599259935994599559965997599859996000600160026003600460056006600760086009601060116012601360146015601660176018601960206021602260236024602560266027602860296030603160326033603460356036603760386039604060416042604360446045604660476048604960506051605260536054605560566057605860596060606160626063606460656066606760686069607060716072607360746075607660776078607960806081608260836084608560866087608860896090609160926093609460956096609760986099610061016102610361046105610661076108610961106111611261136114611561166117611861196120612161226123612461256126612761286129613061316132613361346135613661376138613961406141614261436144614561466147614861496150615161526153615461556156615761586159616061616162616361646165616661676168616961706171617261736174617561766177617861796180618161826183618461856186618761886189619061916192619361946195619661976198619962006201620262036204620562066207620862096210621162126213621462156216621762186219622062216222622362246225622662276228622962306231623262336234623562366237623862396240624162426243624462456246624762486249625062516252625362546255625662576258625962606261626262636264626562666267626862696270627162726273627462756276627762786279628062816282628362846285628662876288628962906291629262936294629562966297629862996300630163026303630463056306630763086309631063116312631363146315631663176318631963206321632263236324632563266327632863296330633163326333633463356336633763386339634063416342634363446345634663476348634963506351635263536354635563566357635863596360636163626363636463656366636763686369637063716372637363746375637663776378637963806381638263836384638563866387638863896390639163926393639463956396639763986399640064016402640364046405640664076408640964106411641264136414641564166417641864196420642164226423642464256426642764286429643064316432643364346435643664376438643964406441644264436444644564466447644864496450645164526453645464556456645764586459646064616462646364646465646664676468646964706471647264736474647564766477647864796480648164826483648464856486648764886489649064916492649364946495649664976498649965006501650265036504650565066507650865096510651165126513651465156516651765186519652065216522652365246525652665276528652965306531653265336534653565366537653865396540654165426543654465456546654765486549655065516552655365546555655665576558655965606561656265636564656565666567656865696570657165726573657465756576657765786579658065816582658365846585658665876588658965906591659265936594659565966597659865996600660166026603660466056606660766086609661066116612661366146615661666176618661966206621662266236624662566266627662866296630663166326633663466356636663766386639664066416642664366446645664666476648664966506651665266536654665566566657665866596660666166626663666466656666666766686669667066716672667366746675667666776678667966806681668266836684668566866687668866896690669166926693669466956696669766986699670067016702670367046705670667076708670967106711671267136714671567166717671867196720672167226723672467256726672767286729673067316732673367346735673667376738673967406741674267436744674567466747674867496750675167526753675467556756675767586759676067616762676367646765676667676768676967706771677267736774677567766777677867796780678167826783678467856786678767886789679067916792679367946795679667976798679968006801680268036804680568066807680868096810681168126813681468156816681768186819682068216822682368246825682668276828682968306831683268336834683568366837683868396840684168426843684468456846684768486849685068516852685368546855685668576858685968606861686268636864686568666867686868696870687168726873687468756876687768786879688068816882688368846885688668876888688968906891689268936894689568966897689868996900690169026903690469056906690769086909691069116912691369146915691669176918691969206921692269236924692569266927692869296930693169326933693469356936693769386939694069416942694369446945694669476948694969506951695269536954695569566957695869596960696169626963696469656966696769686969697069716972697369746975697669776978697969806981698269836984698569866987698869896990699169926993699469956996699769986999700070017002700370047005700670077008700970107011701270137014701570167017701870197020702170227023702470257026702770287029703070317032703370347035703670377038703970407041704270437044704570467047704870497050705170527053705470557056705770587059706070617062706370647065706670677068706970707071707270737074707570767077707870797080708170827083708470857086708770887089709070917092709370947095709670977098 | "use strict";/*The MIT License (MIT)Copyright (c) 2014-2018 Nikolai Suslov and the Krestianstvo.org project contributors. (https://github.com/NikolaySuslov/livecodingspace/blob/master/LICENSE.md)*/// Copyright 2012 United States Government, as represented by the Secretary of Defense, Under// Secretary of Defense (Personnel & Readiness).// // Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except// in compliance with the License. You may obtain a copy of the License at// //   http://www.apache.org/licenses/LICENSE-2.0// // Unless required by applicable law or agreed to in writing, software distributed under the License// is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express// or implied. See the License for the specific language governing permissions and limitations under// the License./// @module vwf/// vwf.js is the main Virtual World Framework manager. It is constructed as a JavaScript module/// (http://www.yuiblog.com/blog/2007/06/12/module-pattern) to isolate it from the rest of the/// page's JavaScript environment. The vwf module self-creates its own instance when loaded and/// attaches to the global window object as window.vwf. Nothing else should affect the global/// environment.( function( window ) {    window.console && console.debug && console.debug( "loading vwf" );    window.vwf = new function() {        var application;        var self = this;        window.console && console.debug && console.debug( "creating vwf" );        // == Public variables =====================================================================        /// The runtime environment (production, development, testing) and other configuration        /// settings appear here.        ///         /// @name module:vwf.configuration        ///         /// @private        this.configuration = undefined; // require( "vwf/configuration" ).active; // "active" updates in place and changes don't invalidate the reference  // TODO: assign here after converting vwf.js to a RequireJS module and listing "vwf/configuration" as a dependency        /// Kernel utility functions and objects.        ///         /// @name module:vwf.utility        ///         /// @private        this.kutility = undefined; // require( "vwf/kernel/utility" );  // TODO: assign here after converting vwf.js to a RequireJS module and listing "vwf/kernel/utility" as a dependency        /// The kernel logger.        ///         /// @name module:vwf.logger        ///         /// @private        this.logger = undefined; // require( "logger" ).for( undefined, this );  // TODO: for( "vwf", ... ), and update existing calls  // TODO: assign here after converting vwf.js to a RequireJS module and listing "vwf/logger" as a dependency        /// Each model and view module loaded by the main page registers itself here.        ///         /// @name module:vwf.modules        ///         /// @private        this.modules = [];        /// vwf.initialize() creates an instance of each model and view module configured on the main        /// page and attaches them here.        ///         /// @name module:vwf.models        ///         /// @private        this.models = [];        /// vwf.initialize() creates an instance of each model and view module configured on the main        /// page and attaches them here.        ///         /// @name module:vwf.views        ///         /// @private        this.views = [];        /// this.models is a list of references to the head of each driver pipeline. Define an        /// `actual` property that evaluates to a list of references to the pipeline tails. This is        /// a list of the actual drivers after any intermediate stages and is useful for debugging.        ///         /// @name module:vwf.models.actual        Object.defineProperty( this.models, "actual", {            get: function() {                // Map the array to the result.                var actual = this.map( function( model ) {                    return last( model );                } );                // Map the non-integer properties too.                for ( var propertyName in this ) {                    if ( isNaN( Number( propertyName ) ) ) {                        actual[propertyName] = last( this[propertyName] );                    }                }                // Follow a pipeline to the last stage.                function last( model ) {                    while ( model.model ) model = model.model;                    return model;                }                return actual;            }        } );        /// this.views is a list of references to the head of each driver pipeline. Define an        /// `actual` property that evaluates to a list of references to the pipeline tails. This is        /// a list of the actual drivers after any intermediate stages and is useful for debugging.        ///         /// @name module:vwf.views.actual        Object.defineProperty( this.views, "actual", {            get: function() {                // Map the array to the result.                var actual = this.map( function( model ) {                    return last( model );                } );                // Map the non-integer properties too.                for ( var propertyName in this ) {                    if ( isNaN( Number( propertyName ) ) ) {                        actual[propertyName] = last( this[propertyName] );                    }                }                // Follow a pipeline to the last stage.                function last( model ) {                    while ( model.model ) model = model.model;                    return model;                }                return actual;            }        } );        /// The simulation clock, which contains the current time in seconds. Time is controlled by        /// the reflector and updates here as we receive control messages.        ///         /// @name module:vwf.now        ///         /// @private        this.now = 0;        /// The queue's sequence number for the currently executing action.        ///         /// The queue enumerates actions in order of arrival, which is distinct from execution order        /// since actions may be scheduled to run in the future. `sequence_` can be used to        /// distinguish between actions that were previously placed on the queue for execution at a        /// later time, and those that arrived after the current action, regardless of their        /// scheduled time.        ///         /// @name module:vwf.sequence_        ///         /// @private        this.sequence_ = undefined;        /// The moniker of the client responsible for the currently executing action. `client_` will        /// be falsy for actions originating in the server, such as time ticks.        ///         /// @name module:vwf.client_        ///         /// @private        this.client_ = undefined;        /// The identifer assigned to the client by the server.        ///         /// @name module:vwf.moniker_        ///         /// @private        this.moniker_ = undefined;        /// Nodes that are receiving ticks.        ///         /// @name module:vwf.tickable        ///         /// @private        this.tickable = {            // models: [],            // views: [],            nodeIDs: [],        };        // == Private variables ====================================================================        /// @name module:vwf.private        ///         /// @private        this.private = {}; // for debugging        /// Components describe the objects that make up the simulation. They may also serve as        /// prototype objects for further derived components. External components are identified by        /// URIs. Once loaded, we save a mapping here from its URI to the node ID of its prototype so        /// that we can find it if it is reused. Components specified internally as object literals        /// are anonymous and are not indexed here.        ///         /// @name module:vwf~components        var components = this.private.components = {}; // maps component node ID => component specification        /// This is the connection to the reflector. In this sample implementation, "socket" is a        /// socket.io client that communicates over a channel provided by the server hosting the        /// client documents.        ///         /// @name module:vwf~socket        var socket = this.private.socket = undefined;        // Each node is assigned an ID as it is created. This is the most recent ID assigned.        // Communication between the manager and the models and views uses these IDs to refer to the        // nodes. The manager doesn't maintain any particular state for the nodes and knows them        // only as their IDs. The models work in federation to provide the meaning to each node.        // var lastID = 0;        /// Callback functions defined in this scope use this local "vwf" to locate the manager.        ///         /// @name module:vwf~vwf        var vwf = this;        // Store the jQuery module for reuse        //var jQuery;        // == Public functions =====================================================================        // -- loadConfiguration ---------------------------------------------------------------------------        // The main page only needs to call vwf.loadConfiguration() to launch the application. Use        // require.ready() or jQuery(document).ready() to call loadConfiguration() once the page has        // loaded. loadConfiguration() accepts three parameters.        //         // A component specification identifies the application to be loaded. modelInitializers and         // viewInitializers identify the model and view libraries that were parsed out of the URL that         // should be attached to the simulation. Each is specified as an object with each library         // name as a property of the object with any arguments as the value of the property.        // Arguments may be specified as an array [1], as a single value if there is only one [2], or as         // undefined if there are none[3].        //         //     [1] vwf.loadConfiguration( ..., { "vwf/model/glge": [ "#scene, "second param" ] }, { ... } )        //     [2] vwf.loadConfiguration( ..., { "vwf/model/glge": "#scene" }, { ... } )        //     [3] vwf.loadConfiguration( ..., { "vwf/model/javascript": undefined }, { ... } )       this.loadConfiguration = async function(/* [ componentURI|componentObject ] { modelInitializers }            { viewInitializers } */) {            var args = Array.prototype.slice.call( arguments );            if ( typeof args[0] != "object" || ! ( args[0] instanceof Array ) ) {                application = args.shift();            }            var userLibraries = args.shift() || {};            var applicationConfig = {};            var callback = args.shift();            var requireConfig = {                baseUrl: '/',                shim: {                                        "vwf/model/aframe/addon/aframe-interpolation": {                        deps: [ "vwf/model/aframe/aframe-master" ]                    },                    "vwf/model/aframe/extras/aframe-extras.loaders": {                        deps: [ "vwf/model/aframe/aframe-master" ]                    },                    "vwf/model/aframe/addon/aframe-sun-sky": {                        deps: [ "vwf/model/aframe/aframe-master" ]                    },                    "vwf/model/aframe/extras/aframe-extras.controls.min": {                        deps: [ "vwf/model/aframe/aframe-master" ]                    },                    "vwf/model/aframe/addon/SkyShader": {                        deps: [ "vwf/model/aframe/aframe-master" ]                    },                    "vwf/model/aframe/addon/BVHLoader": {                        deps: [ "vwf/model/aframe/aframe-master" ]                    },                    "vwf/model/aframe/addon/TransformControls": {                        deps: [ "vwf/model/aframe/aframe-master" ]                    },                    "vwf/model/aframe/addon/THREE.MeshLine": {                        deps: [ "vwf/model/aframe/aframe-master" ]                    },                    "vwf/model/aframe/addon/aframe-components": {                        deps: [ "vwf/model/aframe/aframe-master",                        "vwf/model/aframe/extras/aframe-extras.loaders",                        "vwf/model/aframe/addon/aframe-sun-sky",                        "vwf/model/aframe/addon/SkyShader",                        "vwf/model/aframe/addon/BVHLoader",                        "vwf/model/aframe/addon/TransformControls",                        "vwf/model/aframe/addon/THREE.MeshLine"                    ]                    }                                    }            };            //jQuery = require("jquery");            var requireArray = [                { library: "domReady", active: true },                { library: "vwf/configuration", active: true },                { library: "vwf/kernel/model", active: true },                { library: "vwf/model/javascript", active: true },                { library: "vwf/model/object", active: true },                { library: "vwf/model/stage/log", active: true },                { library: "vwf/model/ohm", active: true },                { library: "vwf/model/osc", active: true },                              { library: "vwf/model/aframe/addon/aframe-components",                 linkedLibraries: [ "vwf/model/aframe/addon/SkyShader" ],                 active: false             },                  { library: "vwf/model/aframe",                     linkedLibraries: [ "vwf/model/aframe/aframe-master",                    "vwf/model/aframe/extras/aframe-extras.loaders",                    "vwf/model/aframe/addon/aframe-interpolation",                    "vwf/model/aframe/addon/aframe-sun-sky",                    "vwf/model/aframe/addon/aframe-components",                    "vwf/model/aframe/addon/SkyShader",                    "vwf/model/aframe/extras/aframe-extras.controls.min",                    "vwf/model/aframe/addon/BVHLoader",                    "vwf/model/aframe/addon/TransformControls",                    "vwf/model/aframe/addon/THREE.MeshLine"                                        ],                     active: false                 },                { library: "vwf/model/aframeComponent", active: true },                { library: "vwf/kernel/view", active: true },                { library: "vwf/view/document", active: true },                { library: "vwf/view/editor-new", active: false },                { library: "vwf/view/webrtc",              //       linkedLibraries: ["vwf/view/webrtc/adapter"],                      active: true                 },                { library: "vwf/view/ohm", active: true },                { library: "vwf/view/osc", active: true },                                 { library: "vwf/view/aframe", active: true },                { library: "vwf/model/aframe/aframe-master", active: false },                { library: "vwf/model/aframe/extras/aframe-extras.loaders", active: false },                { library: "vwf/model/aframe/addon/aframe-interpolation", active: false },                { library: "vwf/model/aframe/addon/aframe-sun-sky", active: false },                { library: "vwf/model/aframe/addon/aframe-components", active: false },                { library: "vwf/model/aframe/addon/BVHLoader", active: false },                { library: "vwf/model/aframe/addon/TransformControls", active: false },                { library: "vwf/model/aframe/addon/THREE.MeshLine", active: false },                                                { library: "vwf/model/aframe/addon/SkyShader", active: false },                { library: "vwf/model/aframe/extras/aframe-extras.controls.min", active: false },                { library: "vwf/view/aframeComponent", active: true },                { library: "vwf/kernel/utility", active: true },                { library: "vwf/utility", active: true },               // { library: "vwf/view/webrtc/adapter", active: false },                //{ library: "vwf/view/webrtc/adapterWebRTC", active: false },                // { library: "vwf/admin", active: false }                           ];            var initializers = {                model: [                    { library: "vwf/model/javascript", active: true },                     { library: "vwf/model/ohm", active: true },                     { library: "vwf/model/osc", active: true },                                       { library: "vwf/model/aframe", active: true },                     { library: "vwf/model/aframeComponent", active: true },                    { library: "vwf/model/object", active: true }                ],                view: [                    { library: "vwf/view/aframe", active: true },                    { library: "vwf/view/aframeComponent", active: true },                    { library: "vwf/view/document", active: true },                    { library: "vwf/view/editor-new", active: false },                     { library: "vwf/view/ohm", active: true },                     { library: "vwf/view/osc", active: true },                    { library: "vwf/view/webrtc", active: true}                                  ]            };            mapLibraryName(requireArray);            mapLibraryName(initializers["model"]);            mapLibraryName(initializers["view"]);            function mapLibraryName(array) {                for(var i=0;i<array.length;i++) {                    array[array[i].library] = array[i];                }            }            function getActiveLibraries(libraryList, includeParameters) {                var activeLibraryList = [];                for(var i=0; i<libraryList.length; i++) {                    if(libraryList[i].active) {                        if(includeParameters) {                            var activeLibrary = {};                            activeLibrary[libraryList[i].library] = libraryList[i].parameters;                            activeLibraryList.push(activeLibrary);                        }                        else {                            activeLibraryList.push(libraryList[i].library);                        }                    }                }                return activeLibraryList;            }            let path =  JSON.parse(localStorage.getItem('lcs_app')).path.public_path;            let appName = JSON.parse(localStorage.getItem('lcs_app')).path.application.split(".").join("_");            let dbPath = appName + '_config_yaml';                       _LCS_WORLD_USER.get('worlds').get(path.slice(1)).get(dbPath).once().then(res => {                if (res) {                    let config = YAML.parse(res.file);                    return config                } else {                    return ""                }            })            .then(function(configLibraries) {                if(configLibraries && typeof configLibraries == "object") {                    if (typeof configLibraries.configuration == "object") {                        applicationConfig = configLibraries.configuration;                    }                    Object.keys(configLibraries).forEach(function(libraryType) {                        if(libraryType == 'info' && configLibraries[libraryType]["title"])                        {                            //jQuery('title').html(configLibraries[libraryType]["title"]);                            document.querySelector('title').innerHTML = configLibraries[libraryType]["title"]                        }                        if(!userLibraries[libraryType]) {                            userLibraries[libraryType] = {};                        }                        // Merge libraries from config file and URL together. Check for incompatible                        // libraries, and disable them.                        Object.keys(configLibraries[libraryType]).forEach(function(libraryName) {                            var disabled = false;                            if(requireArray[libraryName] && requireArray[libraryName].disabledBy) {                                for(var i=0; i<requireArray[libraryName].disabledBy.length; i++) {                                    Object.keys(userLibraries).forEach(function(userLibraryType) {                                        Object.keys(userLibraries[userLibraryType]).forEach(function(userLibraryName) {                                            if(requireArray[libraryName].disabledBy[i] == userLibraryName) {                                                disabled = true;                                            }                                        })                                    })                                }                            }                            if(!disabled) {                                if(userLibraries[libraryType][libraryName] == undefined) {                                    userLibraries[libraryType][libraryName] = configLibraries[libraryType][libraryName];                                }                                else if(typeof userLibraries[libraryType][libraryName] == "object" && typeof configLibraries[libraryType][libraryName] == "object") {                                    userLibraries[libraryType][libraryName] = Object.assign({}, configLibraries[libraryType][libraryName], userLibraries[libraryType][libraryName]);                                    // userLibraries[libraryType][libraryName] = jQuery.extend({}, configLibraries[libraryType][libraryName], userLibraries[libraryType][libraryName]);                                }                            }                        });                    });                }            }).then(function(){                Object.keys(userLibraries).forEach(function(libraryType) {                    if(initializers[libraryType]) {                        Object.keys(userLibraries[libraryType]).forEach(function(libraryName) {                            if(requireArray[libraryName]) {                                requireArray[libraryName].active = true;                                initializers[libraryType][libraryName].active = true;                                if(userLibraries[libraryType][libraryName] && userLibraries[libraryType][libraryName] != "") {                                    if(typeof initializers[libraryType][libraryName].parameters == "object") {                                                                                initializers[libraryType][libraryName].parameters = Object.assign({}, initializers[libraryType][libraryName].parameters, userLibraries[libraryType][libraryName]);                                        // initializers[libraryType][libraryName].parameters = jQuery.extend({}, initializers[libraryType][libraryName].parameters,                                        //     userLibraries[libraryType][libraryName]);                                    }                                    else {                                        initializers[libraryType][libraryName].parameters = userLibraries[libraryType][libraryName];                                    }                                }                                if(requireArray[libraryName].linkedLibraries) {                                    for(var i=0; i<requireArray[libraryName].linkedLibraries.length; i++) {                                        requireArray[requireArray[libraryName].linkedLibraries[i]].active = true;                                    }                                }                            }                        });                    }                });                // Load default renderer if no other librarys specified                if(Object.keys(userLibraries["model"]).length == 0 && Object.keys(userLibraries["view"]).length == 0) {                    // requireArray["vwf/model/threejs"].active = true;                    // requireArray["vwf/view/threejs"].active = true;                    // requireArray["vwf/model/threejs/three"].active = true;                    // requireArray["vwf/model/threejs/js/loaders/ColladaLoader"].active = true;                    // requireArray["vwf/model/threejs/js/loaders/gltf/glTF-parser"].active = true;                    // requireArray["vwf/model/threejs/js/loaders/gltf/glTFLoader"].active = true;                    // requireArray["vwf/model/threejs/js/loaders/gltf/glTFAnimation"].active = true;                    // requireArray["vwf/model/threejs/js/loaders/gltf/glTFLoaderUtils"].active = true;                    // requireArray["vwf/model/threejs/js/stereo/DeviceOrientationControls"].active = true;                    // requireArray["vwf/model/threejs/js/stereo/OrbitControls"].active = true;                    // requireArray["vwf/model/threejs/js/stereo/StereoEffect"].active = true;                    // initializers["model"]["vwf/model/threejs"].active = true;                    // initializers["view"]["vwf/view/threejs"].active = true;                }                require( requireConfig, getActiveLibraries(requireArray, false), function( ready ) {                    ready( async function() {                        // Merge any application configuration settings into the configuration                        // object.                        require( "vwf/configuration" ).instance = require( "vwf/utility" ).merge(                            {}, require( "vwf/configuration" ).instance, applicationConfig );                        // With the scripts loaded, we must initialize the framework. vwf.initialize()                        // accepts three parameters: a world specification, model configuration parameters,                        // and view configuration parameters.                        await vwf.initialize(application, getActiveLibraries(initializers["model"], true), getActiveLibraries(initializers["view"], true), callback);                    } );                } );            })            // jQuery.getJSON("admin/config", function(configLibraries) {            // }).always(function(jqXHR, textStatus) {             // });        }        // -- initialize ---------------------------------------------------------------------------        /// The main page only needs to call vwf.initialize() to launch the application. Use        /// require.ready() or jQuery(document).ready() to call initialize() once the page has        /// loaded. initialize() accepts three parameters.        ///         /// A component specification identifies the application to be loaded. If a URI is provided,        /// the specification is loaded from there [1]. Alternately, a JavaScript object literal        /// containing the specfication may be provided [2]. Since a component can extend and        /// specialize a prototype, using a simple object literal allows existing component to be        /// configured for special uses [3].        ///         ///     [1] vwf.initialize( "http://vwf.example.com/applications/sample12345", ... )        ///        ///     [2] vwf.initialize( { source: "model.dae", type: "model/vnd.collada+xml",        ///             properties: { "p1": ... }, ... }, ... )        ///        ///     [3] vwf.initialize( { extends: "http://vwf.example.com/applications/sample12345",        ///             source: "alternate-model.dae", type: "model/vnd.collada+xml" }, ... )        ///         /// modelInitializers and viewInitializers identify the model and view modules that should be        /// attached to the simulation. Each is specified as an array of objects that map the name of        /// a model or view to construct to the set of arguments to pass to its constructor. Modules        /// without parameters may be specified as a string [4]. Arguments may be specified as an        /// array [5], or as a single value if there is only one [6].        ///         ///     [4] vwf.initialize( ..., [ "vwf/model/javascript" ], [ ... ] )        ///     [5] vwf.initialize( ..., [ { "vwf/model/glge": [ "#scene, "second param" ] } ], [ ... ] )        ///     [6] vwf.initialize( ..., [ { "vwf/model/glge": "#scene" } ], [ ... ] )        ///         /// @name module:vwf.initialize        this.initialize = async function( /* [ componentURI|componentObject ] [ modelInitializers ]            [ viewInitializers ] */ ) {            var args = Array.prototype.slice.call( arguments );            //var application;            // Load the runtime configuration. We start with the factory defaults. The reflector may            // provide additional settings when we connect.            this.configuration = require( "vwf/configuration" ).active; // "active" updates in place and changes don't invalidate the reference            // Load the kernel utilities.            this.kutility = require( "vwf/kernel/utility" );            // Create the logger.            this.logger = require( "logger" ).for( "vwf", this );  // TODO: for( "vwf", ... ), and update existing calls            // Get the jQuery reference. This also happens in `loadConfiguration`, but the tests            // initialize using `initialize` and don't call `loadConfiguration` first.          //  jQuery = require("jquery");            // Parse the function parameters. If the first parameter is not an array, then treat it            // as the application specification. Otherwise, fall back to the "application" parameter            // in the query string.            if ( typeof args[0] != "object" || ! ( args[0] instanceof Array ) ) {                application = args.shift();            }            // Shift off the parameter containing the model list and initializer arguments.            var modelInitializers = args.shift() || [];            // Shift off the parameter containing the view list and initializer arguments.            var viewInitializers = args.shift() || [];            var callback = args.shift();            var compatibilityStatus = { compatible: true, errors: {} };            // Create the model interface to the kernel. Models can make direct calls that execute            // immediately or future calls that are placed on the queue and executed when removed.            this.models.kernel = require( "vwf/kernel/model" ).create( vwf );            // Create and attach each configured model.            modelInitializers.forEach( function( modelInitializer ) {                // Skip falsy values to allow initializers to be conditionally included by the                // loader.                if ( modelInitializer ) {                    // Accept either { "vwf/model/name": [ arguments] } or "vwf/model/name".                    if ( typeof modelInitializer == "object" && modelInitializer != null ) {                        var modelName = Object.keys( modelInitializer )[0];                        var modelArguments = modelInitializer[modelName];                    } else {                        var modelName = modelInitializer;                        var modelArguments = undefined;                    }                    var model = require( modelName ).create(                        this.models.kernel,                         // model's kernel access                        [ require( "vwf/model/stage/log" ) ],       // stages between the kernel and model                        {},                                         // state shared with a paired view                        [].concat( modelArguments || [] )           // arguments for initialize()                    );                    if ( model ) {                        this.models.push( model );                        this.models[modelName] = model; // also index by id  // TODO: this won't work if multiple model instances are allowed                        if ( modelName == "vwf/model/javascript" ) {  // TODO: need a formal way to follow prototype chain from vwf.js; this is peeking inside of vwf-model-javascript                            this.models.javascript = model;                            while ( this.models.javascript.model ) this.models.javascript = this.models.javascript.model;                        }                        if ( modelName == "vwf/model/object" ) {  // TODO: this is peeking inside of vwf-model-object                            this.models.object = model;                            while ( this.models.object.model ) this.models.object = this.models.object.model;                        }                                                if(model.model.compatibilityStatus) {                            if(!model.model.compatibilityStatus.compatible) {                                compatibilityStatus.compatible = false;                                Object.assign(compatibilityStatus.errors, model.model.compatibilityStatus.errors);                                //jQuery.extend(compatibilityStatus.errors, model.model.compatibilityStatus.errors);                            }                        }                    }                }            }, this );            // Create the view interface to the kernel. Views can only make replicated calls which            // bounce off the reflection server, are placed on the queue when received, and executed            // when removed.            this.views.kernel = require( "vwf/kernel/view" ).create( vwf );            // Create and attach each configured view.            viewInitializers.forEach( function( viewInitializer ) {                // Skip falsy values to allow initializers to be conditionally included by the                // loader.                if ( viewInitializer ) {                     // Accept either { "vwf/view/name": [ arguments] } or "vwf/view/name".                    if ( typeof viewInitializer == "object" && viewInitializer != null ) {                        var viewName = Object.keys( viewInitializer )[0];                        var viewArguments = viewInitializer[viewName];                    } else {                        var viewName = viewInitializer;                        var viewArguments = undefined;                    }                    if ( ! viewName.match( "^vwf/view/" ) ) { // old way                        var view = this.modules[viewName];                        if ( view ) {                            var instance = new view();                            instance.state = this.models.actual["vwf/model/"+viewName] && this.models.actual["vwf/model/"+viewName].state || {}; // state shared with a paired model                            view.apply( instance, [ vwf ].concat( viewArguments || [] ) );                            this.views.push( instance );                            this.views[viewName] = instance; // also index by id  // TODO: this won't work if multiple view instances are allowed                            if(view.compatibilityStatus) {                                if(!view.compatibilityStatus.compatible) {                                    compatibilityStatus.compatible = false;                                    Object.assign(compatibilityStatus.errors, view.compatibilityStatus.errors);                                   // jQuery.extend(compatibilityStatus.errors, view.compatibilityStatus.errors);                                }                            }                        }                    } else { // new way                        var modelPeer = this.models.actual[ viewName.replace( "vwf/view/", "vwf/model/" ) ];  // TODO: this.model.actual() is kind of heavy, but it's probably OK to use just a few times here at start-up                        var view = require( viewName ).create(                            this.views.kernel,                          // view's kernel access                            [],                                         // stages between the kernel and view                            modelPeer && modelPeer.state || {},         // state shared with a paired model                            [].concat( viewArguments || [] )            // arguments for initialize()                        );                        if ( view ) {                            this.views.push( view );                            this.views[viewName] = view; // also index by id  // TODO: this won't work if multiple view instances are allowed                            if(view.compatibilityStatus) {                                if(!view.compatibilityStatus.compatible) {                                    compatibilityStatus.compatible = false;                                    Object.assign(compatibilityStatus.errors, view.compatibilityStatus.errors);                                   // jQuery.extend(compatibilityStatus.errors, view.compatibilityStatus.errors);                                }                            }                        }                    }                }            }, this );            // Test for ECMAScript 5            if(!(function() { return !this })()) {                compatibilityStatus.compatible = false;                Object.assign(compatibilityStatus.errors, {"ES5": "This browser is not compatible. VWF requires ECMAScript 5."});                //jQuery.extend(compatibilityStatus.errors, {"ES5": "This browser is not compatible. VWF requires ECMAScript 5."});            }            // Test for WebSockets            // if( window.io && !io.Transport.websocket.check() )            // {            //     compatibilityStatus.compatible = false;            //     jQuery.extend(compatibilityStatus.errors, {"WS": "This browser is not compatible. VWF requires WebSockets."});            // }            if(callback) {                callback(compatibilityStatus);            }            //await _app.getApplicationState();            await _app.getApplicationState()                .then(res => self.ready( application, res))        };        // -- ready --------------------------------------------------------------------------------        /// @name module:vwf.ready        this.ready = function( component_uri_or_json_or_object, path ) {            // Connect to the reflector. This implementation uses the socket.io library, which            // communicates using a channel back to the server that provided the client documents.            try {                let objToRef = Object.assign({}, path);                if(path.saveObject){                    if ( path.saveObject[ "queue" ] ) {                        if ( path.saveObject[ "queue" ][ "time" ] ) {                            objToRef.saveObject = {                                "init": true,                                "queue":{                                    "time": path.saveObject[ "queue" ][ "time" ]                                }                            }                        }                    }                }                                          var options = {                    // The socket is relative to the application path.                    // resource: window.location.pathname.slice( 1,                    //      window.location.pathname.lastIndexOf("/") ),                    query: {                        pathname: window.location.pathname.slice( 1,                            window.location.pathname.lastIndexOf("/") ),                        appRoot: "./public",                        path: JSON.stringify(objToRef)//JSON.stringify(path)                      },                    // query: 'pathname=' + window.location.pathname.slice( 1,                    //     window.location.pathname.lastIndexOf("/") ),                    // Use a secure connection when the application comes from https.                                        secure: window.location.protocol === "https:",                    // Don't attempt to reestablish lost connections. The client reloads after a                    // disconnection to recreate the application from scratch.                    //reconnect: false,                    reconnection: false,                    upgrade: false,                    transports: ['websocket']                };                if ( isSocketIO07() ) {                    //window.location.host            var host = window._app.reflectorHost; //localStorage.getItem('lcs_reflector');             //if(!host) host = 'http://localhost:3002'; //window.location.origin;                   socket = io.connect( host, options );                                    } else {  // Ruby Server -- only supports socket.io 0.6                    io.util.merge( options, {                        // For socket.io 0.6, specify the port since the default isn't correct when                        // using https.                        port: window.location.port ||                            ( window.location.protocol === "https:" ? 443 : 80 ),                        // The ruby socket.io server only supports WebSockets. Don't try the others.                        transports: [                            'websocket',                        ],                        // Increase the timeout because of starvation while loading the scene. The                        // server timeout must also be increased. (For socket.io 0.7+, the client                        // timeout is controlled by the server.)                        transportOptions: {                            "websocket": { timeout: 90000 },                        },                    } );                 socket = io.connect( undefined, options );                }            } catch ( e ) {                // If a connection to the reflector is not available, then run in single-user mode.                // Messages intended for the reflector will loop directly back to us in this case.                // Start a timer to monitor the incoming queue and dispatch the messages as though                // they were received from the server.                this.dispatch();                setInterval( function() {                    var fields = {                        time: vwf.now + 0.010, // TODO: there will be a slight skew here since the callback intervals won't be exactly 10 ms; increment using the actual delta time; also, support play/pause/stop and different playback rates as with connected mode.                        origin: "reflector",                    };                    queue.insert( fields, true ); // may invoke dispatch(), so call last before returning to the host                }, 10 );            }            if ( socket ) {                socket.on('connect_error', function(err) {                    console.log(err);                    var errDiv = document.createElement("div");                    errDiv.innerHTML = "<div class='vwf-err' style='z-index: 10; position: absolute; top: 80px; right: 50px'>Connection error!" + err + "</div>";                    document.querySelector('body').appendChild(errDiv);                                    });                socket.on( "connect", function() {                    vwf.logger.infox( "-socket", "connected" );                    if ( isSocketIO07() ) {                        vwf.moniker_ = this.id;                                            } else {  //Ruby Server                        vwf.moniker_ = this.transport.sessionid;                    }                } );                // Configure a handler to receive messages from the server.                                // Note that this example code doesn't implement a robust parser capable of handling                // arbitrary text and that the messages should be placed in a dedicated priority                // queue for best performance rather than resorting the queue as each message                // arrives. Additionally, overlapping messages may cause actions to be performed out                // of order in some cases if messages are not processed on a single thread.                socket.on( "message", function( message ) {                    // vwf.logger.debugx( "-socket", "message", message );                    try {                        if ( isSocketIO07() ) {                            var fields = message;                        } else { // Ruby Server - Unpack the arguements                            var fields = JSON.parse( message );                        }                        fields.time = Number( fields.time );                        // TODO: other message validation (check node id, others?)                        fields.origin = "reflector";                        // Update the queue.  Messages in the queue are ordered by time, then by order of arrival.                        // Time is only advanced if the message has no action, meaning it is a tick.                        queue.insert( fields, !fields.action ); // may invoke dispatch(), so call last before returning to the host                        // Each message from the server allows us to move time forward. Parse the                        // timestamp from the message and call dispatch() to execute all queued                        // actions through that time, including the message just received.                                            // The simulation may perform immediate actions at the current time or it                        // may post actions to the queue to be performed in the future. But we only                        // move time forward for items arriving in the queue from the reflector.                    } catch ( e ) {                        vwf.logger.warn( fields.action, fields.node, fields.member, fields.parameters,                            "exception performing action:", require( "vwf/utility" ).exceptionMessage( e ) );                    }                } );                socket.on( "disconnect", function() {                    vwf.logger.infox( "-socket", "disconnected" );                    // Reload to rejoin the application.                    window.location = window.location.href;                } );                socket.on( "error", function() {                     //Overcome by compatibility.js websockets check                    document.querySelector('body').innerHTML = "<div class='vwf-err'>WebSockets connections are currently being blocked. Please check your proxy server settings.</div>";                    // jQuery('body').html("<div class='vwf-err'>WebSockets connections are currently being blocked. Please check your proxy server settings.</div>");                 } );                if ( !isSocketIO07() ) {                    // Start communication with the reflector.                     socket.connect();  // TODO: errors can occur here too, particularly if a local client contains the socket.io files but there is no server; do the loopback here instead of earlier in response to new io.Socket.                }            } else if ( component_uri_or_json_or_object ) {                // Load the application. The application is rooted in a single node constructed here                // as an instance of the component passed to initialize(). That component, its                // prototype(s), and its children, and their prototypes and children, flesh out the                // entire application.                // TODO: add note that this is only for a self-determined application; with socket, wait for reflection server to tell us.                // TODO: maybe depends on component_uri_or_json_or_object too; when to override and not connect to reflection server?                this.createNode( component_uri_or_json_or_object, "application" );            } else {  // TODO: also do this if component_uri_or_json_or_object was invalid and createNode() failed                // TODO: show a selection dialog            }        };        // -- plan ---------------------------------------------------------------------------------        /// @name module:vwf.plan        this.plan = function( nodeID, actionName, memberName, parameters, when, callback_async /* ( result ) */ ) {            this.logger.debuggx( "plan", nodeID, actionName, memberName,                parameters && parameters.length, when, callback_async && "callback" );            var time = when > 0 ? // absolute (+) or relative (-)                Math.max( this.now, when ) :                this.now + ( -when );            var fields = {                time: time,                node: nodeID,                action: actionName,                member: memberName,                parameters: parameters,                client: this.client_, // propagate originating client                origin: "future",                // callback: callback_async,  // TODO            };            queue.insert( fields );            this.logger.debugu();        };        // -- send ---------------------------------------------------------------------------------        /// Send a message to the reflector. The message will be reflected back to all participants        /// in the instance.        ///         /// @name module:vwf.send        this.send = function( nodeID, actionName, memberName, parameters, when, callback_async /* ( result ) */ ) {            this.logger.debuggx( "send", nodeID, actionName, memberName,                parameters && parameters.length, when, callback_async && "callback" );  // TODO: loggableParameters()            var time = when > 0 ? // absolute (+) or relative (-)                Math.max( this.now, when ) :                this.now + ( -when );            // Attach the current simulation time and pack the message as an array of the arguments.            var fields = {                time: time,                node: nodeID,                action: actionName,                member: memberName,                parameters: require( "vwf/utility" ).transform( parameters, require( "vwf/utility" ).transforms.transit ),                // callback: callback_async,  // TODO: provisionally add fields to queue (or a holding queue) then execute callback when received back from reflector            };            if ( socket ) {                    // Send the message.                var message = JSON.stringify( fields );                socket.send( message );             } else {                                // In single-user mode, loop the message back to the incoming queue.                fields.client = this.moniker_; // stamp with the originating client like the reflector does                fields.origin = "reflector";                queue.insert( fields );                }            this.logger.debugu();        };        // -- respond ------------------------------------------------------------------------------        /// Return a result for a function invoked by the server.        ///         /// @name module:vwf.respond        this.respond = function( nodeID, actionName, memberName, parameters, result ) {            this.logger.debuggx( "respond", nodeID, actionName, memberName,                parameters && parameters.length, "..." );  // TODO: loggableParameters(), loggableResult()            // Attach the current simulation time and pack the message as an array of the arguments.            var fields = {                // sequence: undefined,  // TODO: use to identify on return from reflector?                time: this.now,                node: nodeID,                action: actionName,                member: memberName,                parameters: require( "vwf/utility" ).transform( parameters, require( "vwf/utility" ).transforms.transit ),                result: require( "vwf/utility" ).transform( result, require( "vwf/utility" ).transforms.transit ),            };            if ( socket ) {                // Send the message.                var message = JSON.stringify( fields );                socket.send( message );            } else {                // Nothing to do in single-user mode.            }            this.logger.debugu();        };        // -- receive ------------------------------------------------------------------------------        /// Handle receipt of a message. Unpack the arguments and call the appropriate handler.        ///         /// @name module:vwf.receive        this.receive = function( nodeID, actionName, memberName, parameters, respond, origin ) {            // origin == "reflector" ?            //     this.logger.infogx( "receive", nodeID, actionName, memberName,            //         parameters && parameters.length, respond, origin ) :            //     this.logger.debuggx( "receive", nodeID, actionName, memberName,            //         parameters && parameters.length, respond, origin );// TODO: delegate parsing and validation to each action.            // Look up the action handler and invoke it with the remaining parameters.            // Note that the message should be validated before looking up and invoking an arbitrary            // handler.            var args = [], result;            if ( nodeID || nodeID === 0 ) args.push( nodeID );            if ( memberName ) args.push( memberName );            if ( parameters ) args = args.concat( parameters ); // flatten            if(actionName == 'createChild')                {                    console.log("create child!");                    // args.push(function(childID)                    // {                    //     //when creating over the reflector, call ready on heirarchy after create.                    //     //nodes from setState are readied in createNode                    //     // vwf.decendants(childID).forEach(function(i){                    //     //     vwf.callMethod(i,'ready',[]);                    //     // });                    //     // vwf.callMethod(childID,'ready',[]);                    //     console.log("create child!");                    // });                }            // Invoke the action.            if ( environment( actionName, parameters ) ) {                require( "vwf/configuration" ).environment = environment( actionName, parameters );            } else if ( origin !== "reflector" || ! nodeID || nodes.existing[ nodeID ] ) {                result = this[ actionName ] && this[ actionName ].apply( this, args );            } else {                this.logger.debugx( "receive", "ignoring reflector action on non-existent node", nodeID );                result = undefined;            }            // Return the result.            respond && this.respond( nodeID, actionName, memberName, parameters, result );            // origin == "reflector" ?            //     this.logger.infou() : this.logger.debugu();            /// The reflector sends a `setState` action as part of the application launch to pass            /// the server's execution environment to the client. A `setState` action isn't really            /// appropriate though since `setState` should be the last part of the launch, whereas            /// the environment ought to be set much earlier--ideally before the kernel loads.            ///             /// Executing the `setState` as received would overwrite any configuration settings            /// already applied by the application. So instead, we detect this particular message            /// and only use it to update the environment in the configuration object.            ///             /// `environment` determines if a message is the reflector's special pre-launch            /// `setState` action, and if so, and if the application hasn't been created yet,            /// returns the execution environment property.            function environment( actionName, param ) {                if ( actionName === "setState" && ! vwf.application() ) {                    var parameters = param;                    if (parameters[0].init){                        parameters = [JSON.parse(localStorage.getItem('lcs_app')).saveObject]                    }                    var applicationState = parameters && parameters[0];                    if ( applicationState && Object.keys( applicationState ).length === 1 &&                            applicationState.configuration && Object.keys( applicationState.configuration ).length === 1 ) {                        return applicationState.configuration.environment;                    }                }                return undefined;            }        };        // -- dispatch -----------------------------------------------------------------------------        /// Dispatch incoming messages waiting in the queue. "currentTime" specifies the current        /// simulation time that we should advance to and was taken from the time stamp of the last        /// message received from the reflector.        ///         /// @name module:vwf.dispatch        this.dispatch = function() {            var fields;            // Actions may use receive's ready function to suspend the queue for asynchronous            // operations, and to resume it when the operation is complete.            while ( fields = /* assignment! */ queue.pull() ) {                // Advance time to the message time.                if ( this.now != fields.time ) {                    this.sequence_ = undefined; // clear after the previous action                    this.client_ = undefined;   // clear after the previous action                    this.now = fields.time;                    this.tock();                }                // Perform the action.                if ( fields.action ) {  // TODO: don't put ticks on the queue but just use them to fast-forward to the current time (requires removing support for passing ticks to the drivers and nodes)                    this.sequence_ = fields.sequence; // note the message's queue sequence number for the duration of the action                    this.client_ = fields.client;     // ... and note the originating client                    this.receive( fields.node, fields.action, fields.member, fields.parameters, fields.respond, fields.origin );                }                else {                    this.tick();                }            }            // Advance time to the most recent time received from the server. Tick if the time            // changed.            if ( queue.ready() && this.now != queue.time ) {                this.sequence_ = undefined; // clear after the previous action                this.client_ = undefined;   // clear after the previous action                this.now = queue.time;                this.tock();            }                    };        // -- log ----------------------------------------------------------------------------------        /// Send a log message to the reflector.        ///         /// @name module:vwf.log        this.log = function() {            this.respond( undefined, "log", undefined, undefined, arguments );        }        // -- tick ---------------------------------------------------------------------------------        /// Tick each tickable model, view, and node. Ticks are sent on each reflector idle message.        ///         /// @name module:vwf.tick        // TODO: remove, in favor of drivers and nodes exclusively using future scheduling;        // TODO: otherwise, all clients must receive exactly the same ticks at the same times.        this.tick = function() {            // Call ticking() on each model.            this.models.forEach( function( model ) {                model.ticking && model.ticking( this.now ); // TODO: maintain a list of tickable models and only call those            }, this );            // Call ticked() on each view.            this.views.forEach( function( view ) {                view.ticked && view.ticked( this.now ); // TODO: maintain a list of tickable views and only call those            }, this );            // Call tick() on each tickable node.            this.tickable.nodeIDs.forEach( function( nodeID ) {                this.callMethod( nodeID, "tick", [ this.now ] );            }, this );        };        // -- tock ---------------------------------------------------------------------------------        /// Notify views of a kernel time change. Unlike `tick`, `tock` messages are sent each time        /// that time moves forward. Only view drivers are notified since the model state should be        /// independent of any particular sequence of idle messages.        ///         /// @name module:vwf.tock        this.tock = function() {            // Call tocked() on each view.            this.views.forEach( function( view ) {                view.tocked && view.tocked( this.now );            }, this );        };        // -- setState -----------------------------------------------------------------------------        /// setState may complete asynchronously due to its dependence on createNode. To prevent        /// actions from executing out of order, queue processing must be suspended while setState is        /// in progress. createNode suspends the queue when necessary, but additional calls to        /// suspend and resume the queue may be needed if other async operations are added.        ///         /// @name module:vwf.setState        ///         /// @see {@link module:vwf/api/kernel.setState}        this.setState = function( appState, callback_async /* () */ ) {            this.logger.debuggx( "setState" );  // TODO: loggableState            // Set the runtime configuration.            var applicationState = appState;            if (applicationState.init){                applicationState = JSON.parse(localStorage.getItem('lcs_app')).saveObject            }            if ( applicationState.configuration ) {                require( "vwf/configuration" ).instance = applicationState.configuration;            }            // Update the internal kernel state.            if ( applicationState.kernel ) {                if ( applicationState.kernel.time !== undefined ) vwf.now = applicationState.kernel.time;            }            // Create or update global nodes and their descendants.            var nodes = applicationState.nodes || [];            var annotations = applicationState.annotations || {};            var nodeIndex = 0;            async.forEachSeries( nodes, function( nodeComponent, each_callback_async /* ( err ) */ ) {                // Look up a possible annotation for this node. For backward compatibility, if the                // state has exactly one node and doesn't contain an annotations object, assume the                // node is the application.                var nodeAnnotation = nodes.length > 1 || applicationState.annotations ?                    annotations[nodeIndex] : "application";                vwf.createNode( nodeComponent, nodeAnnotation, function( nodeID ) /* async */ {                    each_callback_async( undefined );                } );                nodeIndex++;            }, function( err ) /* async */ {                // Clear the message queue, except for reflector messages that arrived after the                // current action.                queue.filter( function( fields ) {                    if ( fields.origin === "reflector" && fields.sequence > vwf.sequence_ ) {                        return true;                    } else {                        vwf.logger.debugx( "setState", function() {                            return [ "removing", JSON.stringify( loggableFields( fields ) ), "from queue" ];                        } );                    }                } );                // Set the queue time and add the incoming items to the queue.                if ( applicationState.queue ) {                    queue.time = applicationState.queue.time;                    queue.insert( applicationState.queue.queue || [] );                }                callback_async && callback_async();            } );            this.logger.debugu();        };        // -- getState -----------------------------------------------------------------------------        /// @name module:vwf.getState        ///         /// @see {@link module:vwf/api/kernel.getState}        this.getState = function( full, normalize ) {            this.logger.debuggx( "getState", full, normalize );            // Get the application nodes and queue.            var applicationState = {                // Runtime configuration.                configuration:                    require( "vwf/configuration" ).active,                // Internal kernel state.                kernel: {                    time: vwf.now,                },                // Global node and descendant deltas.                nodes: [  // TODO: all global objects                    this.getNode( "http://vwf.example.com/clients.vwf", full ),                    this.getNode( this.application(), full ),                ],                // `createNode` annotations, keyed by `nodes` indexes.                annotations: {                    1: "application",                },                // Message queue.                queue: {  // TODO: move to the queue object                    time: queue.time,                    queue: require( "vwf/utility" ).transform( queue.queue, queueTransitTransformation ),                },            };            // Normalize for consistency.            if ( normalize ) {                applicationState = require( "vwf/utility" ).transform(                    applicationState, require( "vwf/utility" ).transforms.hash );            }                this.logger.debugu();            return applicationState;        };        // -- hashState ----------------------------------------------------------------------------        /// @name module:vwf.hashState        ///         /// @see {@link module:vwf/api/kernel.hashState}        this.hashState = function() {            this.logger.debuggx( "hashState" );            var applicationState = this.getState( true, true );            // Hash the nodes.            var hashn = this.hashNode( applicationState.nodes[0] );  // TODO: all global objects            // Hash the queue.            var hashq = "q" + Crypto.MD5( JSON.stringify( applicationState.queue ) ).toString().substring( 0, 16 );            // Hash the other kernel properties.            var hashk = "k" + Crypto.MD5( JSON.stringify( applicationState.kernel ) ).toString().substring( 0, 16 );            this.logger.debugu();            // Generate the combined hash.            return hashn + ":" + hashq + ":" + hashk;        }        // -- createNode ---------------------------------------------------------------------------        /// Create a node from a component specification. Construction may require loading data from        /// multiple remote documents. This function returns before construction is complete. A        /// callback is invoked once the node has fully loaded.        ///         /// A simple node consists of a set of properties, methods and events, but a node may        /// specialize a prototype component and may also contain multiple child nodes, any of which        /// may specialize a prototype component and contain child nodes, etc. So components cover a        /// vast range of complexity. The application definition for the overall simulation is a        /// single component instance.        ///         /// A node is a component instance--a single, anonymous specialization of its component.        /// Nodes specialize components in the same way that any component may specialize a prototype        /// component. The prototype component is made available as a base, then new or modified        /// properties, methods, events, child nodes and scripts are attached to modify the base        /// implemenation.        ///         /// To create a node, we first make the prototoype available by loading it (if it has not        /// already been loaded). This is a recursive call to createNode() with the prototype        /// specification. Then we add new, and modify existing, properties, methods, and events        /// according to the component specification. Then we load and add any children, again        /// recursively calling createNode() for each. Finally, we attach any new scripts and invoke        /// an initialization function.        ///         /// createNode may complete asynchronously due to its dependence on setNode, createChild and        /// loadComponent. To prevent actions from executing out of order, queue processing must be        /// suspended while createNode is in progress. setNode, createChild and loadComponent suspend        /// the queue when necessary, but additional calls to suspend and resume the queue may be        /// needed if other async operations are added.        ///         /// @name module:vwf.createNode        ///         /// @see {@link module:vwf/api/kernel.createNode}        this.createNode = function( nodeComponent, nodeAnnotation, baseURI, callback_async /* ( nodeID ) */ ) {            // Interpret `createNode( nodeComponent, callback )` as            // `createNode( nodeComponent, undefined, undefined, callback )` and            // `createNode( nodeComponent, nodeAnnotation, callback )` as            // `createNode( nodeComponent, nodeAnnotation, undefined, callback )`. `nodeAnnotation`            // was added in 0.6.12, and `baseURI` was added in 0.6.25.            if ( typeof nodeAnnotation == "function" || nodeAnnotation instanceof Function ) {                callback_async = nodeAnnotation;                baseURI = undefined;                nodeAnnotation = undefined;            } else if ( typeof baseURI == "function" || baseURI instanceof Function ) {                callback_async = baseURI;                baseURI = undefined;            }            this.logger.debuggx( "createNode", function() {                return [ JSON.stringify( loggableComponent( nodeComponent ) ), nodeAnnotation ];            } );            var nodePatch;            if ( componentIsDescriptor( nodeComponent ) && nodeComponent.patches ) {                nodePatch = nodeComponent;                nodeComponent = nodeComponent.patches;  // TODO: possible sync errors if the patched node is a URI component and the kernel state (time, random) is different from when the node was created on the originating client            }            // nodeComponent may be a URI, a descriptor, or an ID. While being created, it will            // transform from a URI to a descriptor to an ID (depending on its starting state).            // nodeURI, nodeDescriptor, and nodeID capture the intermediate states.            var nodeURI, nodeDescriptor, nodeID;            async.series( [                // If `nodeComponent` is a URI, load the descriptor. `nodeComponent` may be a URI, a                // descriptor or an ID here.                function( series_callback_async /* ( err, results ) */ ) {                    if ( componentIsURI( nodeComponent ) ) { // URI  // TODO: allow non-vwf URIs (models, images, etc.) to pass through to stage 2 and pass directly to createChild()                        // Resolve relative URIs, but leave host-relative, locally-absolute                        // references intact.                        nodeURI = nodeComponent[0] == "/" ?                            nodeComponent : require( "vwf/utility" ).resolveURI( nodeComponent, baseURI );                        // Load the document if we haven't seen this URI yet. Mark the components                        // list to indicate that this component is loading.                        if ( ! components[nodeURI] ) { // uri is not loading (Array) or is loaded (id)                            components[nodeURI] = []; // [] => array of callbacks while loading => true                            loadComponent( nodeURI, undefined, function( nodeDescriptor ) /* async */ {                                nodeComponent = nodeDescriptor;                                series_callback_async(undefined, undefined);                            }, function( errorMessage ) {                                nodeComponent = undefined;                                series_callback_async( errorMessage, undefined );                            } );                        // If we've seen this URI, but it is still loading, just add our callback to                        // the list. The original load's completion will call our callback too.                        } else if ( components[nodeURI] instanceof Array ) { // uri is loading                             callback_async && components[nodeURI].push( callback_async );  // TODO: is this leaving a series callback hanging if we don't call series_callback_async?                        // If this URI has already loaded, skip to the end and call the callback                        // with the ID.                        } else { // uri is loaded                            if ( nodePatch ) {                                vwf.setNode( components[nodeURI], nodePatch, function( nodeID ) /* async */ {                                    callback_async && callback_async( components[nodeURI] );  // TODO: is this leaving a series callback hanging if we don't call series_callback_async?                                } );                            } else {                                callback_async && callback_async( components[nodeURI] );  // TODO: is this leaving a series callback hanging if we don't call series_callback_async?                            }                        }                    } else { // descriptor, ID or error                        series_callback_async( undefined, undefined );                    }                },                // Rudimentary support for `{ includes: prototype }`, which absorbs a prototype                // descriptor into the node descriptor before creating the node.                // Notes:                //                 //   - Only supports one level, so `{ includes: prototype }` won't work if the                //     prototype also contains a `includes` directive).                //   - Only works with prototype URIs, so `{ includes: { ... descriptor ... } }`                //     won't work.                //   - Loads the prototype on each reference, so unlike real prototypes, multiple                //     references to the same prototype cause multiple network loads.                //                 // Also see the `mergeDescriptors` limitations.                function( series_callback_async /* ( err, results ) */ ) {                    if ( componentIsDescriptor( nodeComponent ) && nodeComponent.includes && componentIsURI( nodeComponent.includes ) ) {  // TODO: for "includes:", accept an already-loaded component (which componentIsURI exludes) since the descriptor will be loaded again                        var prototypeURI = require( "vwf/utility" ).resolveURI( nodeComponent.includes, nodeURI || baseURI );                        loadComponent( prototypeURI, undefined, function( prototypeDescriptor ) /* async */ {                            prototypeDescriptor = resolvedDescriptor( prototypeDescriptor, prototypeURI );                            nodeComponent = mergeDescriptors( nodeComponent, prototypeDescriptor ); // modifies prototypeDescriptor                            series_callback_async( undefined, undefined );                        }, function( errorMessage ) {                            nodeComponent = undefined;                            series_callback_async( errorMessage, undefined );                        } );                    } else {                        series_callback_async( undefined, undefined );                    }                },                // If `nodeComponent` is a descriptor, construct and get the ID. `nodeComponent` may                // be a descriptor or an ID here.                function( series_callback_async /* ( err, results ) */ ) {                    if ( componentIsDescriptor( nodeComponent ) ) { // descriptor  // TODO: allow non-vwf URIs (models, images, etc.) to pass through to stage 2 and pass directly to createChild()                        nodeDescriptor = nodeComponent;                        // Create the node as an unnamed child global object.                        vwf.createChild( 0, nodeAnnotation, nodeDescriptor, nodeURI, function( nodeID ) /* async */ {                            nodeComponent = nodeID;                            series_callback_async( undefined, undefined );                        } );                                            } else { // ID or error                        series_callback_async( undefined, undefined );                    }                },                // nodeComponent is an ID here.                function( series_callback_async /* ( err, results ) */ ) {                    if ( componentIsID( nodeComponent ) || components[ nodeComponent ] instanceof Array ) {  // ID                        nodeID = nodeComponent;                        if ( nodePatch ) {                            vwf.setNode( nodeID, nodePatch, function( nodeID ) /* async */ {                                series_callback_async( undefined, undefined );                            } );                        } else {                            series_callback_async( undefined, undefined );                        }                    } else {  // error                        series_callback_async( undefined, undefined );  // TODO: error                    }                },            ], function( err, results ) /* async */ {                // If this node derived from a URI, save the list of callbacks waiting for                // completion and update the component list with the ID.                if ( nodeURI ) {                    var callbacks_async = components[nodeURI];                    components[nodeURI] = nodeID;                }                // Pass the ID to our callback.                callback_async && callback_async( nodeID );  // TODO: handle error if invalid id                // Call the other callbacks.                if ( nodeURI ) {                    callbacks_async.forEach( function( callback_async ) {                        callback_async && callback_async( nodeID );                    } );                }            } );            this.logger.debugu();        };        // -- deleteNode ---------------------------------------------------------------------------        /// @name module:vwf.deleteNode        ///         /// @see {@link module:vwf/api/kernel.deleteNode}        this.deleteNode = function( nodeID ) {            this.logger.debuggx( "deleteNode", nodeID );            // Send the meta event into the application. We send it before deleting the child so            // that the child will still be available for review.            var parentID = this.parent( nodeID );            if ( parentID !== 0 ) {                var nodeIndex = this.children( parentID ).indexOf( nodeID );                if ( nodeIndex < 0 ) {                    nodeIndex = undefined;                }                if ( this.models.kernel.enabled() ) {                    this.fireEvent( parentID, [ "children", "removed" ],                        [ nodeIndex, this.kutility.nodeReference( nodeID ) ] );                }            }            // Remove the entry in the components list if this was the root of a component loaded            // from a URI.            Object.keys( components ).some( function( nodeURI ) { // components: nodeURI => nodeID                if (  components[nodeURI] == nodeID ) {                    delete components[nodeURI];                    return true;                }            } );            // Call deletingNode() on each model. The node is considered deleted after all models            // have run.            this.models.forEach( function( model ) {                model.deletingNode && model.deletingNode( nodeID );            } );            // Unregister the node.            nodes.delete( nodeID );            // Call deletedNode() on each view. The view is being notified that a node has been            // deleted.            this.views.forEach( function( view ) {                view.deletedNode && view.deletedNode( nodeID );            } );            this.logger.debugu();        };        // -- setNode ------------------------------------------------------------------------------        /// setNode may complete asynchronously due to its dependence on createChild. To prevent        /// actions from executing out of order, queue processing must be suspended while setNode is        /// in progress. createChild suspends the queue when necessary, but additional calls to        /// suspend and resume the queue may be needed if other async operations are added.        ///         /// @name module:vwf.setNode        ///         /// @see {@link module:vwf/api/kernel.setNode}        this.setNode = function( nodeID, nodeComponent, callback_async /* ( nodeID ) */ ) {  // TODO: merge with createChild?            this.logger.debuggx( "setNode", function() {                return [ nodeID, JSON.stringify( loggableComponent( nodeComponent ) ) ];            } );            var node = nodes.existing[nodeID];            // Set the internal state.            vwf.models.object.internals( nodeID, nodeComponent );            // Suppress kernel reentry so that we can write the state without coloring from            // any scripts.            vwf.models.kernel.disable();            // Create the properties, methods, and events. For each item in each set, invoke            // createProperty(), createMethod(), or createEvent() to create the field. Each            // delegates to the models and views as above.            // Properties.            nodeComponent.properties && Object.keys( nodeComponent.properties ).forEach( function( propertyName ) {  // TODO: setProperties should be adapted like this to be used here                var propertyValue = nodeComponent.properties[ propertyName ];                // Is the property specification directing us to create a new property, or                // initialize a property already defined on a prototype?                // Create a new property if the property is not defined on a prototype.                // Otherwise, initialize the property.                var creating = ! node.properties.has( propertyName );  // not defined on node or prototype                // Translate node references in the descriptor's form `{ node: nodeID }` into kernel                // node references.                if ( valueHasAccessors( propertyValue ) && propertyValue.node ) {                    propertyValue = vwf.kutility.nodeReference( propertyValue.node );                }                // Create or initialize the property.                if ( creating ) {                    vwf.createProperty( nodeID, propertyName, propertyValue );                } else {                    vwf.setProperty( nodeID, propertyName, propertyValue );                }  // TODO: delete when propertyValue === null in patch            } );            // Methods.            nodeComponent.methods && Object.keys( nodeComponent.methods ).forEach( function( methodName ) {                var methodHandler = nodeComponent.methods[ methodName ];                var creating = ! node.methods.has( methodName );  // not defined on node or prototype                // Create or initialize the method.                if ( creating ) {                    vwf.createMethod( nodeID, methodName, methodHandler.parameters, methodHandler.body );                } else {                    vwf.setMethod( nodeID, methodName, methodHandler );                }  // TODO: delete when methodHandler === null in patch            } );            // Events.            nodeComponent.events && Object.keys( nodeComponent.events ).forEach( function( eventName ) {                var eventDescriptor = nodeComponent.events[ eventName ];                var creating = ! node.events.has( eventName );  // not defined on node or prototype                // Create or initialize the event.                if ( creating ) {                    vwf.createEvent( nodeID, eventName, eventDescriptor.parameters );                    vwf.setEvent( nodeID, eventName, eventDescriptor );  // set the listeners since `createEvent` can't do it yet                } else {                    vwf.setEvent( nodeID, eventName, eventDescriptor );                }  // TODO: delete when eventDescriptor === null in patch            } );            // Restore kernel reentry.            vwf.models.kernel.enable();            async.series( [                function( series_callback_async /* ( err, results ) */ ) {                    // Create and attach the children. For each child, call createChild() with the                    // child's component specification. createChild() delegates to the models and                    // views as before.                    async.forEach( Object.keys( nodeComponent.children || {} ), function( childName, each_callback_async /* ( err ) */ ) {                        var creating = ! nodeHasOwnChild.call( vwf, nodeID, childName );                        if ( creating ) {                            vwf.createChild( nodeID, childName, nodeComponent.children[childName], undefined, function( childID ) /* async */ {  // TODO: add in original order from nodeComponent.children  // TODO: ensure id matches nodeComponent.children[childName].id  // TODO: propagate childURI + fragment identifier to children of a URI component?                                each_callback_async( undefined );                            } );                        } else {                            vwf.setNode( nodeComponent.children[childName].id || nodeComponent.children[childName].patches,                                    nodeComponent.children[childName], function( childID ) /* async */ {                                each_callback_async( undefined );                            } );                        }  // TODO: delete when nodeComponent.children[childName] === null in patch                    }, function( err ) /* async */ {                        series_callback_async( err, undefined );                    } );                },                function( series_callback_async /* ( err, results ) */ ) {                    // Attach the scripts. For each script, load the network resource if the script                    // is specified as a URI, then once loaded, call execute() to direct any model                    // that manages scripts of this script's type to evaluate the script where it                    // will perform any immediate actions and retain any callbacks as appropriate                    // for the script type.                    var scripts = nodeComponent.scripts ?                        [].concat( nodeComponent.scripts ) : []; // accept either an array or a single item                    var baseURI = vwf.uri( nodeID, true );                    async.map( scripts, function( script, map_callback_async /* ( err, result ) */ ) {                        if ( valueHasType( script ) ) {                            if ( script.source ) {                                loadScript( script.source, baseURI, function( scriptText ) /* async */ {  // TODO: this load would be better left to the driver, which may want to ignore it in certain cases, but that would require a completion callback from kernel.execute()                                    map_callback_async( undefined, { text: scriptText, type: script.type } );                                }, function( errorMessage ) {                                    map_callback_async( errorMessage, undefined );                                } );                            } else {                                map_callback_async( undefined, { text: script.text, type: script.type } );                            }                        } else {                            map_callback_async( undefined, { text: script, type: undefined } );                        }                    }, function( err, scripts ) /* async */ {                        // Suppress kernel reentry so that initialization functions don't make any                        // changes during replication.                        vwf.models.kernel.disable();                        // Create each script.                        scripts.forEach( function( script ) {                            vwf.execute( nodeID, script.text, script.type ); // TODO: callback                        } );                        // Restore kernel reentry.                        vwf.models.kernel.enable();                        series_callback_async( err, undefined );                    } );                },            ], function( err, results ) /* async */ {                callback_async && callback_async( nodeID );            } );            this.logger.debugu();            return nodeComponent;        };        // -- getNode ------------------------------------------------------------------------------        /// @name module:vwf.getNode        ///         /// @see {@link module:vwf/api/kernel.getNode}        this.getNode = function( nodeID, full, normalize ) {  // TODO: options to include/exclude children, prototypes            this.logger.debuggx( "getNode", nodeID, full );            var node = nodes.existing[nodeID];            // Start the descriptor.            var nodeComponent = {};            // Arrange the component as a patch if the node originated in a URI component. We want            // to refer to the original URI but apply any changes that have been made to the node            // since it was loaded.            var patches = this.models.object.patches( nodeID ),                patched = false;            if ( node.patchable ) {                nodeComponent.patches = node.uri || nodeID;            } else {                nodeComponent.id = nodeID;            }            // Intrinsic state. These don't change once created, so they can be omitted if we're            // patching.            if ( full || ! node.patchable ) {                var intrinsics = this.intrinsics( nodeID ); // source, type                var prototypeID = this.prototype( nodeID );                if ( prototypeID === undefined ) {                    nodeComponent.extends = null;                } else if ( prototypeID !== this.kutility.protoNodeURI ) {                    nodeComponent.extends = this.getNode( prototypeID );  // TODO: move to vwf/model/object and get from intrinsics                }                nodeComponent.implements = this.behaviors( nodeID ).map( function( behaviorID ) {                    return this.getNode( behaviorID );  // TODO: move to vwf/model/object and get from intrinsics                }, this );                nodeComponent.implements.length || delete nodeComponent.implements;                if ( intrinsics.source !== undefined ) nodeComponent.source = intrinsics.source;                if ( intrinsics.type !== undefined ) nodeComponent.type = intrinsics.type;            }            // Internal state.            if ( full || ! node.patchable || patches.internals ) {                var internals = this.models.object.internals( nodeID ); // sequence and random                nodeComponent.sequence = internals.sequence;                nodeComponent.random = internals.random;            }            // Suppress kernel reentry so that we can read the state without coloring from any            // scripts.            vwf.models.kernel.disable();            // Properties.            if ( full || ! node.patchable ) {                // Want everything, or only want patches but the node is not patchable.                nodeComponent.properties = this.getProperties( nodeID );                for ( var propertyName in nodeComponent.properties ) {                    var propertyValue = nodeComponent.properties[propertyName];                    if ( propertyValue === undefined ) {                        delete nodeComponent.properties[propertyName];                    } else if ( this.kutility.valueIsNodeReference( propertyValue ) ) {                        // Translate kernel node references into descriptor node references.                        nodeComponent.properties[propertyName] = { node: propertyValue.id };                    }                }            } else if ( node.properties.changes ) {                // The node is patchable and properties have changed.                nodeComponent.properties = {};                Object.keys( node.properties.changes ).forEach( function( propertyName ) {                    if ( node.properties.changes[ propertyName ] !== "removed" ) {  // TODO: handle delete                        var propertyValue = this.getProperty( nodeID, propertyName );                        if ( this.kutility.valueIsNodeReference( propertyValue ) ) {                            // Translate kernel node references into descriptor node references.                            nodeComponent.properties[propertyName] = { node: propertyValue.id };                        } else {                            nodeComponent.properties[propertyName] = propertyValue;                        }                    }                }, this );            }            if ( Object.keys( nodeComponent.properties ).length == 0 ) {                delete nodeComponent.properties;            } else {                patched = true;            }            // Methods.            if ( full || ! node.patchable ) {                Object.keys( node.methods.existing ).forEach( function( methodName ) {                    nodeComponent.methods = nodeComponent.methods || {};                    nodeComponent.methods[ methodName ] = this.getMethod( nodeID, methodName );                    patched = true;                }, this );            } else if ( node.methods.changes ) {                Object.keys( node.methods.changes ).forEach( function( methodName ) {                    if ( node.methods.changes[ methodName ] !== "removed" ) {  // TODO: handle delete                        nodeComponent.methods = nodeComponent.methods || {};                        nodeComponent.methods[ methodName ] = this.getMethod( nodeID, methodName );                        patched = true;                    }                }, this );            }            // Events.            var events = full || ! node.patchable ?                node.events.existing : node.events.changes;            if ( events ) {                Object.keys( events ).forEach( function( eventName ) {                    nodeComponent.events = nodeComponent.events || {};                    nodeComponent.events[ eventName ] = this.getEvent( nodeID, eventName );                    patched = true;                }, this );            }            // Restore kernel reentry.            vwf.models.kernel.enable();            // Children.            nodeComponent.children = {};            this.children( nodeID ).forEach( function( childID ) {                nodeComponent.children[ this.name( childID ) ] = this.getNode( childID, full );            }, this );            for ( var childName in nodeComponent.children ) {  // TODO: distinguish add, change, remove                if ( nodeComponent.children[childName] === undefined ) {                    delete nodeComponent.children[childName];                }            }            if ( Object.keys( nodeComponent.children ).length == 0 ) {                 delete nodeComponent.children;            } else {                patched = true;            }            // Scripts.            // TODO: scripts            // Normalize for consistency.            if ( normalize ) {                nodeComponent = require( "vwf/utility" ).transform(                    nodeComponent, require( "vwf/utility" ).transforms.hash );            }            this.logger.debugu();            // Return the descriptor created, unless it was arranged as a patch and there were no            // changes. Otherwise, return the URI if this is the root of a URI component.            if ( full || ! node.patchable || patched ) {                return nodeComponent;            } else if ( node.uri ) {                return node.uri;            } else {                return undefined;            }        };        // -- hashNode -----------------------------------------------------------------------------        /// @name module:vwf.hashNode        ///         /// @see {@link module:vwf/api/kernel.hashNode}        this.hashNode = function( nodeID ) {  // TODO: works with patches?  // TODO: only for nodes from getNode( , , true )            this.logger.debuggx( "hashNode", typeof nodeID == "object" ? nodeID.id : nodeID );            var nodeComponent = typeof nodeID == "object" ? nodeID : this.getNode( nodeID, true, true );            // Hash the intrinsic state.            var internal = { id: nodeComponent.id, source: nodeComponent.source, type: nodeComponent.type };  // TODO: get subset same way as getNode() puts them in without calling out specific field names            internal.source === undefined && delete internal.source;            internal.type === undefined && delete internal.type;            var hashi = "i" + Crypto.MD5( JSON.stringify( internal ) ).toString().substring( 0, 16 );            // Hash the properties.            var properties = nodeComponent.properties || {};            var hashp = Object.keys( properties ).length ?                "p" + Crypto.MD5( JSON.stringify( properties ) ).toString().substring( 0, 16 ) : undefined;            // Hash the children.            var children = nodeComponent.children || {};            var hashc = Object.keys( children ).length ?                "c" + Crypto.MD5( JSON.stringify( children ) ).toString().substring( 0, 16 ) : undefined;            this.logger.debugu();            // Generate the combined hash.            return hashi + ( hashp ? "." + hashp : "" ) + ( hashc ? "/" + hashc : "" );        };        // -- createChild --------------------------------------------------------------------------        /// When we arrive here, we have a prototype node in hand (by way of its ID) and an object        /// containing a component specification. We now need to create and assemble the new node.        ///         /// The VWF manager doesn't directly manipulate any node. The various models act in        /// federation to create the greater model. The manager simply routes messages within the        /// system to allow the models to maintain the necessary data. Additionally, the views        /// receive similar messages that allow them to keep their interfaces current.        ///        /// To create a node, we simply assign a new ID, then invoke a notification on each model and        /// a notification on each view.        ///         /// createChild may complete asynchronously due to its dependence on createNode and the        /// creatingNode and createdNode driver calls. To prevent actions from executing out of        /// order, queue processing must be suspended while createChild is in progress. createNode        /// and the driver callbacks suspend the queue when necessary, but additional calls to        /// suspend and resume the queue may be needed if other async operations are added.        ///         /// @name module:vwf.createChild        ///         /// @see {@link module:vwf/api/kernel.createChild}        this.createChild = function( nodeID, childName, childComponent, childURI, callback_async /* ( childID ) */ ) {            this.logger.debuggx( "createChild", function() {                return [ nodeID, childName, JSON.stringify( loggableComponent( childComponent ) ), childURI ];            } );            childComponent = normalizedComponent( childComponent );            var child, childID, childIndex, childPrototypeID, childBehaviorIDs = [], deferredInitializations = {};            var resolvedSource;  // resolved `childComponent.source` for the drivers.            // Determine if we're replicating previously-saved state, or creating a fresh object.            var replicating = !! childComponent.id;            // Allocate an ID for the node. IDs must be unique and consistent across all clients            // sharing the same instance regardless of the component load order. Each node maintains            // a sequence counter, and we allocate the ID based on the parent's sequence counter and            // ID. Top-level nodes take the ID from their origin URI when available or from a hash            // of the descriptor. An existing ID is used when synchronizing to state drawn from            // another client or to a previously-saved state.            if ( childComponent.id ) {  // incoming replication: pre-calculated id                childID = childComponent.id;                childIndex = this.children( nodeID ).length;            } else if ( nodeID === 0 ) {  // global: component's URI or hash of its descriptor                childID = childURI ||                    Crypto.MD5( JSON.stringify( childComponent ) ).toString();  // TODO: MD5 may be too slow here                childIndex = childURI;            } else {  // descendant: parent id + next from parent's sequence                childID = nodeID + ":" + this.sequence( nodeID ) +                    ( this.configuration["randomize-ids"] ? "-" + ( "0" + Math.floor( this.random( nodeID ) * 100 ) ).slice( -2 ) : "" ) +                    ( this.configuration["humanize-ids"] ? "-" + childName.replace( /[^0-9A-Za-z_-]+/g, "-" ) : "" );                childIndex = this.children( nodeID ).length;            }            // Register the node.            child = nodes.create( childID, childPrototypeID, childBehaviorIDs, childURI, childName, nodeID );            // Register the node in vwf/model/object. Since the kernel delegates many node            // information functions to vwf/model/object, this serves to register it with the            // kernel. The node must be registered before any async operations occur to ensure that            // the parent's child list is correct when following siblings calculate their index            // numbers.            vwf.models.object.creatingNode( nodeID, childID, childPrototypeID, childBehaviorIDs,                childComponent.source, childComponent.type, childIndex, childName );  // TODO: move node metadata back to the kernel and only use vwf/model/object just as a property store?            // The base URI for relative references is the URI of this node or the closest ancestor.            var baseURI = vwf.uri( childID, true );            // Construct the node.            async.series( [                function( series_callback_async /* ( err, results ) */ ) {                    // Rudimentary support for `{ includes: prototype }`, which absorbs a prototype                    // descriptor into the child descriptor before creating the child. See the notes                    // in `createNode` and the `mergeDescriptors` limitations.                    // This first task always completes asynchronously (even if it doesn't perform                    // an async operation) so that the stack doesn't grow from node to node while                    // createChild() recursively traverses a component. If this task is moved,                    // replace it with an async stub, or make the next task exclusively async.                    if ( componentIsDescriptor( childComponent ) && childComponent.includes && componentIsURI( childComponent.includes ) ) {  // TODO: for "includes:", accept an already-loaded component (which componentIsURI exludes) since the descriptor will be loaded again                        var prototypeURI = require( "vwf/utility" ).resolveURI( childComponent.includes, baseURI );                        var sync = true; // will loadComponent() complete synchronously?                        loadComponent( prototypeURI, undefined, function( prototypeDescriptor ) /* async */ {                            // Resolve relative references with respect to the included component.                            prototypeDescriptor = resolvedDescriptor( prototypeDescriptor, prototypeURI );                            // Merge the child descriptor onto the `includes` descriptor.                            childComponent = mergeDescriptors( childComponent, prototypeDescriptor ); // modifies prototypeDescriptor                            if ( sync ) {                                queue.suspend( "before beginning " + childID ); // suspend the queue                                async.nextTick( async function() {                                   await series_callback_async( undefined, undefined );                                    queue.resume( "after beginning " + childID ); // resume the queue; may invoke dispatch(), so call last before returning to the host                                } );                            } else {                                series_callback_async( undefined, undefined );                            }                        }, function( errorMessage ) {                            childComponent = undefined;                            series_callback_async( errorMessage, undefined );                        } );                        sync = false; // not if we got here first                    } else {                        queue.suspend( "before beginning " + childID ); // suspend the queue                        async.nextTick( async function() {                            await series_callback_async( undefined, undefined );                            queue.resume( "after beginning " + childID ); // resume the queue; may invoke dispatch(), so call last before returning to the host                        } );                    }                },                function( series_callback_async /* ( err, results ) */ ) {                    // Create the prototype and behavior nodes (or locate previously created                    // instances).                    async.parallel( [                        function( parallel_callback_async /* ( err, results ) */ ) {                            // Create or find the prototype and save the ID in childPrototypeID.                            if ( childComponent.extends !== null ) {  // TODO: any way to prevent node loading node as a prototype without having an explicit null prototype attribute in node?                                var prototypeComponent = childComponent.extends || vwf.kutility.protoNodeURI;                                vwf.createNode( prototypeComponent, undefined, baseURI, function( prototypeID ) /* async */ {                                    childPrototypeID = prototypeID;// TODO: the GLGE driver doesn't handle source/type or properties in prototypes properly; as a work-around pull those up into the component when not already definedif ( ! childComponent.source ) {    var prototype_intrinsics = vwf.intrinsics( prototypeID );    if ( prototype_intrinsics.source ) {        var prototype_uri = vwf.uri( prototypeID );        var prototype_properties = vwf.getProperties( prototypeID );        childComponent.source = require( "vwf/utility" ).resolveURI( prototype_intrinsics.source, prototype_uri );        childComponent.type = prototype_intrinsics.type;        childComponent.properties = childComponent.properties || {};        Object.keys( prototype_properties ).forEach( function( prototype_property_name ) {            if ( childComponent.properties[prototype_property_name] === undefined && prototype_property_name != "transform" ) {                childComponent.properties[prototype_property_name] = prototype_properties[prototype_property_name];            }        } );    }}                                    parallel_callback_async( undefined, undefined );                                } );                            } else {                                childPrototypeID = undefined;                                parallel_callback_async( undefined, undefined );                            }                        },                        function( parallel_callback_async /* ( err, results ) */ ) {                            // Create or find the behaviors and save the IDs in childBehaviorIDs.                            var behaviorComponents = childComponent.implements ?                                [].concat( childComponent.implements ) : []; // accept either an array or a single item                            async.map( behaviorComponents, function( behaviorComponent, map_callback_async /* ( err, result ) */ ) {                                vwf.createNode( behaviorComponent, undefined, baseURI, function( behaviorID ) /* async */ {                                    map_callback_async( undefined, behaviorID );                                } );                            }, function( err, behaviorIDs ) /* async */ {                                childBehaviorIDs = behaviorIDs;                                parallel_callback_async( err, undefined );                            } );                        },                    ], function( err, results ) /* async */ {                        series_callback_async( err, undefined );                    } );                },                function( series_callback_async /* ( err, results ) */ ) {                    // Re-register the node now that we have the prototypes and behaviors.                    child = nodes.create( childID, childPrototypeID, childBehaviorIDs, childURI, childName, nodeID );                    // For the proto-prototype node `node.vwf`, register the meta events.                    if ( childID === vwf.kutility.protoNodeURI ) {                        child.events.create( namespaceEncodedName( [ "properties", "created" ] ) );                        child.events.create( namespaceEncodedName( [ "properties", "initialized" ] ) );                        child.events.create( namespaceEncodedName( [ "properties", "deleted" ] ) );                        child.events.create( namespaceEncodedName( [ "methods", "created" ] ) );                        child.events.create( namespaceEncodedName( [ "methods", "deleted" ] ) );                        child.events.create( namespaceEncodedName( [ "events", "created" ] ) );                        child.events.create( namespaceEncodedName( [ "events", "deleted" ] ) );                        child.events.create( namespaceEncodedName( [ "children", "added" ] ) );                        child.events.create( namespaceEncodedName( [ "children", "removed" ] ) );                    }                    // Re-register the node in vwf/model/object now that we have the prototypes and                    // behaviors. vwf/model/object knows that we call it more than once and only                    // updates the new information.                    vwf.models.object.creatingNode( nodeID, childID, childPrototypeID, childBehaviorIDs,                        childComponent.source, childComponent.type, childIndex, childName );  // TODO: move node metadata back to the kernel and only use vwf/model/object just as a property store?                    // Resolve the asset source URL for the drivers.                    resolvedSource = childComponent.source &&                        require( "vwf/utility" ).resolveURI( childComponent.source, baseURI );                    // Call creatingNode() on each model. The node is considered to be constructed                    // after all models have run.                    async.forEachSeries( vwf.models, function( model, each_callback_async /* ( err ) */ ) {                        var driver_ready = true;                        var timeoutID;                        // TODO: suppress kernel reentry here (just for childID?) with kernel/model showing a warning when breached; no actions are allowed until all drivers have seen creatingNode()                        model.creatingNode && model.creatingNode( nodeID, childID, childPrototypeID, childBehaviorIDs,                                resolvedSource, childComponent.type, childIndex, childName, function( ready ) /* async */ {                            if ( driver_ready && ! ready ) {                                suspend();                            } else if ( ! driver_ready && ready ) {                                resume();                            }                            function suspend() {                                queue.suspend( "while loading " + childComponent.source + " for " + childID + " in creatingNode" ); // suspend the queue                                timeoutID = window.setTimeout( function() { resume( "timeout loading " + childComponent.source ) }, vwf.configuration[ "load-timeout" ] * 1000 );                                driver_ready = false;                            }                            async function resume( err ) {                                window.clearTimeout( timeoutID );                                driver_ready = true;                                err && vwf.logger.warnx( "createChild", nodeID, childName + ":", err );                                await each_callback_async( err ); // resume createChild()                                queue.resume( "after loading " + childComponent.source + " for " + childID + " in creatingNode" ); // resume the queue; may invoke dispatch(), so call last before returning to the host                            }                        } );                        // TODO: restore kernel reentry here                        driver_ready && each_callback_async( undefined );                    }, function( err ) /* async */ {                        series_callback_async( err, undefined );                    } );                },                function( series_callback_async /* ( err, results ) */ ) {                    // Call createdNode() on each view. The view is being notified of a node that has                    // been constructed.                    async.forEachSeries( vwf.views, function( view, each_callback_async /* ( err ) */ ) {                        var driver_ready = true;                        var timeoutID;                        view.createdNode && view.createdNode( nodeID, childID, childPrototypeID, childBehaviorIDs,                                resolvedSource, childComponent.type, childIndex, childName, function( ready ) /* async */ {                            if ( driver_ready && ! ready ) {                                suspend();                            } else if ( ! driver_ready && ready ) {                                resume();                            }                            function suspend() {                                queue.suspend( "while loading " + childComponent.source + " for " + childID + " in createdNode" ); // suspend the queue                                timeoutID = window.setTimeout( function() { resume( "timeout loading " + childComponent.source ) }, vwf.configuration[ "load-timeout" ] * 1000 );                                driver_ready = false;                            }                            async function resume( err ) {                                window.clearTimeout( timeoutID );                                driver_ready = true;                                err && vwf.logger.warnx( "createChild", nodeID, childName + ":", err );                                await each_callback_async( err ); // resume createChild()                                queue.resume( "after loading " + childComponent.source + " for " + childID + " in createdNode" ); // resume the queue; may invoke dispatch(), so call last before returning to the host                            }                        } );                        driver_ready && each_callback_async( undefined );                    }, function( err ) /* async */ {                        series_callback_async( err, undefined );                    } );                },                function( series_callback_async /* ( err, results ) */ ) {                    // Set the internal state.                    vwf.models.object.internals( childID, childComponent );                    // Suppress kernel reentry so that we can read the state without coloring from                    // any scripts.                    replicating && vwf.models.kernel.disable();                    // Create the properties, methods, and events. For each item in each set, invoke                    // createProperty(), createMethod(), or createEvent() to create the field. Each                    // delegates to the models and views as above.                    childComponent.properties && Object.keys( childComponent.properties ).forEach( function( propertyName ) {                        var propertyValue = childComponent.properties[ propertyName ];                        var value = propertyValue, get, set, create;                        if ( valueHasAccessors( propertyValue ) ) {                            value = propertyValue.node ? vwf.kutility.nodeReference( propertyValue.node ) : propertyValue.value;                            get = propertyValue.get;                            set = propertyValue.set;                            create = propertyValue.create;                        }                        // Is the property specification directing us to create a new property, or                        // initialize a property already defined on a prototype?                        // Create a new property if an explicit getter or setter are provided or if                        // the property is not defined on a prototype. Initialize the property when                        // the property is already defined on a prototype and no explicit getter or                        // setter are provided.                        var creating = create || // explicit create directive, or                            get !== undefined || set !== undefined || // explicit accessor, or                            ! child.properties.has( propertyName );  // not defined on prototype                        // Are we assigning the value here, or deferring assignment until the node                        // is constructed because setters will run?                        var assigning = value === undefined || // no value, or                            set === undefined && ( creating || ! nodePropertyHasSetter.call( vwf, childID, propertyName ) ) || // no setter, or                            replicating; // replicating previously-saved state (setters never run during replication)                        if ( ! assigning ) {                            deferredInitializations[propertyName] = value;                            value = undefined;                        }                        // Create or initialize the property.                        if ( creating ) {                            vwf.createProperty( childID, propertyName, value, get, set );                        } else {                            vwf.setProperty( childID, propertyName, value );                        }                    } );                    childComponent.methods && Object.keys( childComponent.methods ).forEach( function( methodName ) {                        var methodValue = childComponent.methods[ methodName ];                        if ( valueHasBody( methodValue ) ) {                            vwf.createMethod( childID, methodName, methodValue.parameters, methodValue.body );                        } else {                            vwf.createMethod( childID, methodName, undefined, methodValue );                        }                    } );                    childComponent.events && Object.keys( childComponent.events ).forEach( function( eventName ) {                        var eventValue = childComponent.events[ eventName ];                        if ( valueHasBody( eventValue ) ) {                            vwf.createEvent( childID, eventName, eventValue.parameters );                            vwf.setEvent( childID, eventName, eventValue );  // set the listeners since `createEvent` can't do it yet                        } else {                            vwf.createEvent( childID, eventName, undefined );                        }                    } );                    // Restore kernel reentry.                    replicating && vwf.models.kernel.enable();                    // Create and attach the children. For each child, call createChild() with the                    // child's component specification. createChild() delegates to the models and                    // views as before.                    async.forEach( Object.keys( childComponent.children || {} ), function( childName, each_callback_async /* ( err ) */ ) {                        var childValue = childComponent.children[childName];                        vwf.createChild( childID, childName, childValue, undefined, function( childID ) /* async */ {  // TODO: add in original order from childComponent.children  // TODO: propagate childURI + fragment identifier to children of a URI component?                            each_callback_async( undefined );                        } );                    }, function( err ) /* async */ {                        series_callback_async( err, undefined );                    } );                },                function( series_callback_async /* ( err, results ) */ ) {                    // Attach the scripts. For each script, load the network resource if the script is                    // specified as a URI, then once loaded, call execute() to direct any model that                    // manages scripts of this script's type to evaluate the script where it will                    // perform any immediate actions and retain any callbacks as appropriate for the                    // script type.                    var scripts = childComponent.scripts ?                        [].concat( childComponent.scripts ) : []; // accept either an array or a single item                    async.map( scripts, function( script, map_callback_async /* ( err, result ) */ ) {                        if ( valueHasType( script ) ) {                            if ( script.source ) {                                loadScript( script.source, baseURI, function( scriptText ) /* async */ {  // TODO: this load would be better left to the driver, which may want to ignore it in certain cases, but that would require a completion callback from kernel.execute()                                    map_callback_async( undefined, { text: scriptText, type: script.type } );                                }, function( errorMessage ) {                                    map_callback_async( errorMessage, undefined );                                } );                            } else {                                map_callback_async( undefined, { text: script.text, type: script.type } );                            }                        } else {                            map_callback_async( undefined, { text: script, type: undefined } );                        }                    }, function( err, scripts ) /* async */ {                        // Watch for any async kernel calls generated as we run the scripts and wait                        // for them complete before completing the node.                        vwf.models.kernel.capturingAsyncs( function() {                            // Suppress kernel reentry so that initialization functions don't make                            // any changes during replication.                            replicating && vwf.models.kernel.disable();                            // Create each script.                            scripts.forEach( function( script ) {                                vwf.execute( childID, script.text, script.type ); // TODO: callback                            } );                            // Perform initializations for properties with setter functions. These                            // are assigned here so that the setters run on a fully-constructed node.                            Object.keys( deferredInitializations ).forEach( function( propertyName ) {                                vwf.setProperty( childID, propertyName, deferredInitializations[propertyName] );                            } );                            // TODO: Adding the node to the tickable list here if it contains a tick() function in JavaScript at initialization time. Replace with better control of ticks on/off and the interval by the node.                            if ( vwf.execute( childID, "Boolean( this.tick )" ) ) {                                vwf.tickable.nodeIDs.push( childID );                            }                            // Restore kernel reentry.                            replicating && vwf.models.kernel.enable();                        }, function() {                            // This function is called when all asynchronous calls from the previous                            // function have returned.                            // Call initializingNode() on each model and initializedNode() on each                            // view to indicate that the node is fully constructed.                            // Since nodes don't (currently) inherit their prototypes' children,                            // for each prototype also call initializingNodeFromPrototype() to allow                            // model drivers to apply the prototypes' initializers to the node.                            async.forEachSeries( vwf.prototypes( childID, true ).reverse().concat( childID ),                                    function( childInitializingNodeID, each_callback_async /* err */ ) {                                // Call initializingNode() on each model.                                vwf.models.kernel.capturingAsyncs( function() {                                    vwf.models.forEach( function( model ) {                                        // Suppress kernel reentry so that initialization functions                                        // don't make any changes during replication.                                        replicating && vwf.models.kernel.disable();                                        // For a prototype, call `initializingNodeFromPrototype` to                                        // run the prototype's initializer on the node. For the                                        // node, call `initializingNode` to run its own initializer.                                        //                                         // `initializingNodeFromPrototype` is separate from                                        // `initializingNode` so that `initializingNode` remains a                                        // single call that indicates that the node is fully                                        // constructed. Existing drivers, and any drivers that don't                                        // care about prototype initializers will by default ignore                                        // the intermediate initializations.                                        // (`initializingNodeFromPrototype` was added in 0.6.23.)                                        if ( childInitializingNodeID !== childID ) {                                            model.initializingNodeFromPrototype &&                                                model.initializingNodeFromPrototype( nodeID, childID, childInitializingNodeID );                                        } else {                                            model.initializingNode &&                                                model.initializingNode( nodeID, childID, childPrototypeID, childBehaviorIDs,                                                    resolvedSource, childComponent.type, childIndex, childName );                                        }                                        // Restore kernel reentry.                                        replicating && vwf.models.kernel.enable();                                    } );                                }, function() {                                    each_callback_async( undefined );                                } );                            }, function( err ) /* async */ {                                // Call initializedNode() on each view.                                vwf.views.forEach( function( view ) {                                    view.initializedNode && view.initializedNode( nodeID, childID, childPrototypeID, childBehaviorIDs,                                        resolvedSource, childComponent.type, childIndex, childName );                                } );                                // Mark the node as initialized.                                nodes.initialize( childID );                                // Send the meta event into the application.                                if ( ! replicating && nodeID !== 0 ) {                                    vwf.fireEvent( nodeID, [ "children", "added" ],                                        [ childIndex, vwf.kutility.nodeReference( childID ) ] );                                }                                // Dismiss the loading spinner                                if ( childID === vwf.application() ) {                                    var progressbar= document.getElementById( "load-progressbar" );                                    if (progressbar) {                                        //document.querySelector('body').removeChild(progressbar);                                        progressbar.classList.remove( "visible" );                                        progressbar.classList.add( "not-visible" );                                        progressbar.classList.add( "mdc-linear-progress--closed" );                                                                            }                                       // var spinner = document.getElementById( "vwf-loading-spinner" );                                    // spinner && spinner.classList.remove( "pace-active" );                                }                                series_callback_async( err, undefined );                            } );                        } );                    } );                },            ], function( err, results ) /* async */ {                // The node is complete. Invoke the callback method and pass the new node ID and the                // ID of its prototype. If this was the root node for the application, the                // application is now fully initialized.                // Always complete asynchronously so that the stack doesn't grow from node to node                // while createChild() recursively traverses a component.                if ( callback_async ) {                    queue.suspend( "before completing " + childID ); // suspend the queue                    async.nextTick( async function() {                        await callback_async( childID );                        queue.resume( "after completing " + childID ); // resume the queue; may invoke dispatch(), so call last before returning to the host                    } );                }            } );            this.logger.debugu();        };        // -- deleteChild --------------------------------------------------------------------------        /// @name module:vwf.deleteChild        ///         /// @see {@link module:vwf/api/kernel.deleteChild}        this.deleteChild = function( nodeID, childName ) {            var childID = this.children( nodeID ).filter( function( childID ) {                return this.name( childID ) === childName;            }, this )[0];            if ( childID !== undefined ) {                return this.deleteNode( childID );            }        }        // -- addChild -----------------------------------------------------------------------------        /// @name module:vwf.addChild        ///         /// @see {@link module:vwf/api/kernel.addChild}        this.addChild = function( nodeID, childID, childName ) {            this.logger.debuggx( "addChild", nodeID, childID, childName );            // Call addingChild() on each model. The child is considered added after all models have            // run.            this.models.forEach( function( model ) {                model.addingChild && model.addingChild( nodeID, childID, childName );            } );            // Call addedChild() on each view. The view is being notified that a child has been            // added.            this.views.forEach( function( view ) {                view.addedChild && view.addedChild( nodeID, childID, childName );            } );            this.logger.debugu();        };        // -- removeChild --------------------------------------------------------------------------        /// @name module:vwf.removeChild        ///         /// @see {@link module:vwf/api/kernel.removeChild}        this.removeChild = function( nodeID, childID ) {            this.logger.debuggx( "removeChild", nodeID, childID );            // Call removingChild() on each model. The child is considered removed after all models            // have run.            this.models.forEach( function( model ) {                model.removingChild && model.removingChild( nodeID, childID );            } );            // Call removedChild() on each view. The view is being notified that a child has been            // removed.            this.views.forEach( function( view ) {                view.removedChild && view.removedChild( nodeID, childID );            } );            this.logger.debugu();        };        // -- setProperties ------------------------------------------------------------------------        /// Set all of the properties for a node.        ///         /// @name module:vwf.setProperties        ///         /// @see {@link module:vwf/api/kernel.setProperties}        this.setProperties = function( nodeID, properties ) {  // TODO: rework as a cover for setProperty(), or remove; passing all properties to each driver is impractical since initializing and setting are different, and reentry can't be controlled when multiple sets are in progress.            this.logger.debuggx( "setProperties", nodeID, properties );            var node = nodes.existing[nodeID];            var entrants = this.setProperty.entrants;            // Call settingProperties() on each model.            properties = this.models.reduceRight( function( intermediate_properties, model, index ) {  // TODO: note that we can't go left to right and stop after the first that accepts the set since we are setting all of the properties as a batch; verify that this creates the same result as calling setProperty individually on each property and that there are no side effects from setting through a driver after the one that handles the set.                var model_properties = {};                if ( model.settingProperties ) {                    model_properties = model.settingProperties( nodeID, properties );                } else if ( model.settingProperty ) {                    Object.keys( node.properties.existing ).forEach( function( propertyName ) {                        if ( properties[propertyName] !== undefined ) {                            var reentry = entrants[nodeID+'-'+propertyName] = { index: index }; // the active model number from this call  // TODO: need unique nodeID+propertyName hash                            model_properties[propertyName] =                                model.settingProperty( nodeID, propertyName, properties[propertyName] );                            if ( vwf.models.kernel.blocked() ) {                                model_properties[propertyName] = undefined; // ignore result from a blocked setter                            }                            delete entrants[nodeID+'-'+propertyName];                        }                    } );                }                Object.keys( node.properties.existing ).forEach( function( propertyName ) {                    if ( model_properties[propertyName] !== undefined ) { // copy values from this model                        intermediate_properties[propertyName] = model_properties[propertyName];                    } else if ( intermediate_properties[propertyName] === undefined ) { // as well as recording any new keys                        intermediate_properties[propertyName] = undefined;                    }                } );                return intermediate_properties;            }, {} );            // Record the change.            if ( node.initialized && node.patchable ) {                Object.keys( properties ).forEach( function( propertyName ) {                    node.properties.change( propertyName );                } );            }            // Call satProperties() on each view.            this.views.forEach( function( view ) {                if ( view.satProperties ) {                    view.satProperties( nodeID, properties );                } else if ( view.satProperty ) {                    for ( var propertyName in properties ) {                        view.satProperty( nodeID, propertyName, properties[propertyName] );  // TODO: be sure this is the value actually set, not the incoming value                    }                }            } );            this.logger.debugu();            return properties;        };        // -- getProperties ------------------------------------------------------------------------        /// Get all of the properties for a node.        ///         /// @name module:vwf.getProperties        ///         /// @see {@link module:vwf/api/kernel.getProperties}        this.getProperties = function( nodeID ) {  // TODO: rework as a cover for getProperty(), or remove; passing all properties to each driver is impractical since reentry can't be controlled when multiple gets are in progress.            this.logger.debuggx( "getProperties", nodeID );            var node = nodes.existing[nodeID];            var entrants = this.getProperty.entrants;            // Call gettingProperties() on each model.            var properties = this.models.reduceRight( function( intermediate_properties, model, index ) {  // TODO: note that we can't go left to right and take the first result since we are getting all of the properties as a batch; verify that this creates the same result as calling getProperty individually on each property and that there are no side effects from getting through a driver after the one that handles the get.                var model_properties = {};                if ( model.gettingProperties ) {                    model_properties = model.gettingProperties( nodeID, properties );                } else if ( model.gettingProperty ) {                    Object.keys( node.properties.existing ).forEach( function( propertyName ) {                        var reentry = entrants[nodeID+'-'+propertyName] = { index: index }; // the active model number from this call  // TODO: need unique nodeID+propertyName hash                        model_properties[propertyName] =                            model.gettingProperty( nodeID, propertyName, intermediate_properties[propertyName] );                        if ( vwf.models.kernel.blocked() ) {                            model_properties[propertyName] = undefined; // ignore result from a blocked getter                        }                        delete entrants[nodeID+'-'+propertyName];                    } );                }                Object.keys( node.properties.existing ).forEach( function( propertyName ) {                    if ( model_properties[propertyName] !== undefined ) { // copy values from this model                        intermediate_properties[propertyName] = model_properties[propertyName];                    } else if ( intermediate_properties[propertyName] === undefined ) { // as well as recording any new keys                        intermediate_properties[propertyName] = undefined;                    }                } );                return intermediate_properties;            }, {} );            // Call gotProperties() on each view.            this.views.forEach( function( view ) {                if ( view.gotProperties ) {                    view.gotProperties( nodeID, properties );                } else if ( view.gotProperty ) {                    for ( var propertyName in properties ) {                        view.gotProperty( nodeID, propertyName, properties[propertyName] );  // TODO: be sure this is the value actually gotten and not an intermediate value from above                    }                }            } );            this.logger.debugu();            return properties;        };        // -- createProperty -----------------------------------------------------------------------        /// Create a property on a node and assign an initial value.        ///         /// @name module:vwf.createProperty        ///         /// @see {@link module:vwf/api/kernel.createProperty}        this.createProperty = function( nodeID, propertyName, propertyValue, propertyGet, propertySet ) {            this.logger.debuggx( "createProperty", function() {                return [ nodeID, propertyName, JSON.stringify( loggableValue( propertyValue ) ),                    loggableScript( propertyGet ), loggableScript( propertySet ) ];            } );            var node = nodes.existing[nodeID];            // Register the property.            node.properties.create( propertyName, node.initialized && node.patchable );            // Call creatingProperty() on each model. The property is considered created after all            // models have run.            this.models.forEach( function( model ) {                model.creatingProperty && model.creatingProperty( nodeID, propertyName, propertyValue,                    propertyGet, propertySet );            } );            // Call createdProperty() on each view. The view is being notified that a property has            // been created.            this.views.forEach( function( view ) {                view.createdProperty && view.createdProperty( nodeID, propertyName, propertyValue,                    propertyGet, propertySet );            } );            // Send the meta event into the application.            if ( this.models.kernel.enabled() ) {                this.fireEvent( nodeID, [ "properties", "created" ], [ propertyName ] );            }            this.logger.debugu();            return propertyValue;        };        // -- setProperty --------------------------------------------------------------------------        /// Set a property value on a node.        ///         /// @name module:vwf.setProperty        ///         /// @see {@link module:vwf/api/kernel.setProperty}        this.setProperty = function( nodeID, propertyName, propertyValue ) {            this.logger.debuggx( "setProperty", function() {                return [ nodeID, propertyName, JSON.stringify( loggableValue( propertyValue ) ) ];            } );            var node = nodes.existing[nodeID];            // Record calls into this function by nodeID and propertyName so that models may call            // back here (directly or indirectly) to delegate responses further down the chain            // without causing infinite recursion.            var entrants = this.setProperty.entrants;            var entry = entrants[nodeID+'-'+propertyName] || {}; // the most recent call, if any  // TODO: need unique nodeID+propertyName hash            var reentry = entrants[nodeID+'-'+propertyName] = {}; // this call            // Select the actual driver calls. Create the property if it doesn't exist on this node            // or its prototypes. Initialize it if it exists on a prototype but not on this node.            // Set it if it already exists on this node.            if ( ! node.properties.has( propertyName ) || entry.creating ) {                reentry.creating = true;                var settingPropertyEtc = "creatingProperty";                var satPropertyEtc = "createdProperty";                node.properties.create( propertyName, node.initialized && node.patchable );            } else if ( ! node.properties.hasOwn( propertyName ) || entry.initializing ) {                reentry.initializing = true;                var settingPropertyEtc = "initializingProperty";                var satPropertyEtc = "initializedProperty";                node.properties.create( propertyName, node.initialized && node.patchable );            } else {                var settingPropertyEtc = "settingProperty";                var satPropertyEtc = "satProperty";            }            // Keep track of the number of assignments made by this `setProperty` call and others            // invoked indirectly by it, starting with the outermost call.            var outermost = entrants.assignments === undefined;            if ( outermost ) {                entrants.assignments = 0;            }            // Have we been called for the same property on the same node for a property still being            // assigned (such as when a setter function assigns the property to itself)? If so, then            // the inner call should skip drivers that the outer call has already invoked, and the            // outer call should complete without invoking drivers that the inner call will have            // already called.            var reentered = ( entry.index !== undefined );            // We'll need to know if the set was delegated to other properties or actually assigned            // here.            var delegated = false, assigned = false;            // Call settingProperty() on each model. The first model to return a non-undefined value            // has performed the set and dictates the return value. The property is considered set            // after all models have run.            this.models.some( function( model, index ) {                // Skip initial models that an outer call has already invoked for this node and                // property (if any). If an inner call completed for this node and property, skip                // the remaining models.                if ( ( ! reentered || index > entry.index ) && ! reentry.completed ) {                    // Record the active model number.                     reentry.index = index;                    // Record the number of assignments made since the outermost call. When                    // `entrants.assignments` increases, a driver has called `setProperty` to make                    // an assignment elsewhere.                    var assignments = entrants.assignments;                    // Make the call.                    if ( ! delegated && ! assigned ) {                        var value = model[settingPropertyEtc] && model[settingPropertyEtc]( nodeID, propertyName, propertyValue );                    } else {                        model[settingPropertyEtc] && model[settingPropertyEtc]( nodeID, propertyName, undefined );                    }                    // Ignore the result if reentry is disabled and the driver attempted to call                    // back into the kernel. Kernel reentry is disabled during replication to                     // prevent coloring from accessor scripts.                    if ( this.models.kernel.blocked() ) {  // TODO: this might be better handled wholly in vwf/kernel/model by converting to a stage and clearing blocked results on the return                        value = undefined;                    }                    // The property was delegated if the call made any assignments.                    if ( entrants.assignments !== assignments ) {                        delegated = true;                    }                    // Otherwise if the call returned a value, the property was assigned here.                    else if ( value !== undefined ) {                        entrants.assignments++;                        assigned = true;                    }                    // Record the value actually assigned. This may differ from the incoming value                    // if it was range limited, quantized, etc. by the model. This is the value                    // passed to the views.                    if ( value !== undefined ) {                        propertyValue = value;                    }                    // If we are setting, exit from the this.models.some() iterator once the value                    // has been set. Don't exit early if we are creating or initializing since every                    // model needs the opportunity to register the property.                    return settingPropertyEtc == "settingProperty" && ( delegated || assigned );                }            }, this );            // Record the change if the property was assigned here.            if ( assigned && node.initialized && node.patchable ) {                node.properties.change( propertyName );            }            // Call satProperty() on each view. The view is being notified that a property has            // been set. Only call for value properties as they are actually assigned. Don't call            // for accessor properties that have delegated to other properties. Notifying when            // setting an accessor property would be useful, but since that information is            // ephemeral, and views on late-joining clients would never see it, it's best to never            // send those notifications.            if ( assigned ) {                this.views.forEach( function( view ) {                    view[satPropertyEtc] && view[satPropertyEtc]( nodeID, propertyName, propertyValue );                } );            }            if ( reentered ) {                // For a reentrant call, restore the previous state and move the index forward to                // cover the models we called.                entrants[nodeID+'-'+propertyName] = entry;                entry.completed = true;            } else {                // Delete the call record if this is the first, non-reentrant call here (the normal                // case).                delete entrants[nodeID+'-'+propertyName];                // If the property was created or initialized, send the corresponding meta event                // into the application.                if ( this.models.kernel.enabled() ) {                    if ( settingPropertyEtc === "creatingProperty" ) {                        this.fireEvent( nodeID, [ "properties", "created" ], [ propertyName ] );                    } else if ( settingPropertyEtc === "initializingProperty" ) {                        this.fireEvent( nodeID, [ "properties", "initialized" ], [ propertyName ] );                    }                }            }            // Clear the assignment counter when the outermost `setProperty` completes.            if ( outermost ) {                delete entrants.assignments;            }            this.logger.debugu();            return propertyValue;        };        this.setProperty.entrants = {}; // maps ( nodeID + '-' + propertyName ) => { index: i, value: v }        // -- getProperty --------------------------------------------------------------------------        /// Get a property value for a node.        ///         /// @name module:vwf.getProperty        ///         /// @see {@link module:vwf/api/kernel.getProperty}        this.getProperty = function( nodeID, propertyName, ignorePrototype ) {            this.logger.debuggx( "getProperty", nodeID, propertyName );            var propertyValue = undefined;            // Record calls into this function by nodeID and propertyName so that models may call            // back here (directly or indirectly) to delegate responses further down the chain            // without causing infinite recursion.            var entrants = this.getProperty.entrants;            var entry = entrants[nodeID+'-'+propertyName] || {}; // the most recent call, if any  // TODO: need unique nodeID+propertyName hash            var reentry = entrants[nodeID+'-'+propertyName] = {}; // this call            // Keep track of the number of retrievals made by this `getProperty` call and others            // invoked indirectly by it, starting with the outermost call.            var outermost = entrants.retrievals === undefined;            if ( outermost ) {                entrants.retrievals = 0;            }            // Have we been called for the same property on the same node for a property still being            // retrieved (such as when a getter function retrieves the property from itself)? If so,            // then the inner call should skip drivers that the outer call has already invoked, and            // the outer call should complete without invoking drivers that the inner call will have            // already called.            var reentered = ( entry.index !== undefined );            // We'll need to know if the get was delegated to other properties or actually retrieved            // here.            var delegated = false, retrieved = false;            // Call gettingProperty() on each model. The first model to return a non-undefined value            // dictates the return value.            this.models.some( function( model, index ) {                // Skip initial models that an outer call has already invoked for this node and                // property (if any). If an inner call completed for this node and property, skip                // the remaining models.                if ( ( ! reentered || index > entry.index ) && ! reentry.completed ) {                    // Record the active model number.                     reentry.index = index;                    // Record the number of retrievals made since the outermost call. When                    // `entrants.retrievals` increases, a driver has called `getProperty` to make                    // a retrieval elsewhere.                    var retrievals = entrants.retrievals;                    // Make the call.                    var value = model.gettingProperty &&                        model.gettingProperty( nodeID, propertyName, propertyValue );  // TODO: probably don't need propertyValue here                    // Ignore the result if reentry is disabled and the driver attempted to call                    // back into the kernel. Kernel reentry is disabled during replication to                     // prevent coloring from accessor scripts.                    if ( this.models.kernel.blocked() ) {  // TODO: this might be better handled wholly in vwf/kernel/model by converting to a stage and clearing blocked results on the return                        value = undefined;                    }                    // The property was delegated if the call made any retrievals.                    if ( entrants.retrievals !== retrievals ) {                        delegated = true;                    }                    // Otherwise if the call returned a value, the property was retrieved here.                    else if ( value !== undefined ) {                        entrants.retrievals++;                        retrieved = true;                    }                    // Record the value retrieved.                    if ( value !== undefined ) {                        propertyValue = value;                    }                    // Exit from the this.models.some() iterator once we have a return value.                    return delegated || retrieved;                }            }, this );            if ( reentered ) {                // For a reentrant call, restore the previous state, move the index forward to cover                // the models we called.                entrants[nodeID+'-'+propertyName] = entry;                entry.completed = true;            } else {                // Delete the call record if this is the first, non-reentrant call here (the normal                // case).                delete entrants[nodeID+'-'+propertyName];                // Delegate to the behaviors and prototype if we didn't get a result from the                // current node.                if ( propertyValue === undefined && ! ignorePrototype ) {                    this.behaviors( nodeID ).reverse().concat( this.prototype( nodeID ) ).                        some( function( prototypeID, prototypeIndex, prototypeArray ) {                        if ( prototypeIndex < prototypeArray.length - 1 ) {                            propertyValue = this.getProperty( prototypeID, propertyName, true ); // behavior node only, not its prototypes                        } else if ( prototypeID !== this.kutility.protoNodeURI ) {                            propertyValue = this.getProperty( prototypeID, propertyName ); // prototype node, recursively                        }                        return propertyValue !== undefined;                    }, this );                }                // Call gotProperty() on each view.                this.views.forEach( function( view ) {                    view.gotProperty && view.gotProperty( nodeID, propertyName, propertyValue );  // TODO: be sure this is the value actually gotten and not an intermediate value from above                } );            }            // Clear the retrieval counter when the outermost `getProperty` completes.            if ( outermost ) {                delete entrants.retrievals;            }            this.logger.debugu();            return propertyValue;        };        this.getProperty.entrants = {}; // maps ( nodeID + '-' + propertyName ) => { index: i, value: v }        // -- createMethod -------------------------------------------------------------------------        /// @name module:vwf.createMethod        ///         /// @see {@link module:vwf/api/kernel.createMethod}        this.createMethod = function( nodeID, methodName, methodParameters, methodBody ) {            this.logger.debuggx( "createMethod", function() {                return [ nodeID, methodName, methodParameters, loggableScript( methodBody ) ];            } );            var node = nodes.existing[nodeID];            // Register the method.            node.methods.create( methodName, node.initialized && node.patchable );            // Call `creatingMethod` on each model. The method is considered created after all            // models have run.            this.models.forEach( function( model ) {                model.creatingMethod && model.creatingMethod( nodeID, methodName, methodParameters,                    methodBody );            } );            // Call `createdMethod` on each view. The view is being notified that a method has been            // created.            this.views.forEach( function( view ) {                view.createdMethod && view.createdMethod( nodeID, methodName, methodParameters,                    methodBody );            } );            // Send the meta event into the application.            if ( this.models.kernel.enabled() ) {                this.fireEvent( nodeID, [ "methods", "created" ], [ methodName ] );            }            this.logger.debugu();        };        // -- setMethod ----------------------------------------------------------------------------        /// @name module:vwf.setMethod        ///         /// @see {@link module:vwf/api/kernel.setMethod}        this.setMethod = function( nodeID, methodName, methodHandler ) {            this.logger.debuggx( "setMethod", function() {                return [ nodeID, methodName ];  // TODO loggable methodHandler            } );            var node = nodes.existing[nodeID];            methodHandler = normalizedHandler( methodHandler );            if ( ! node.methods.hasOwn( methodName ) ) {                // If the method doesn't exist on this node, delegate to `kernel.createMethod` to                // create and assign the method.                this.createMethod( nodeID, methodName, methodHandler.parameters, methodHandler.body );  // TODO: result            } else {                // Call `settingMethod` on each model. The first model to return a non-undefined                // value dictates the return value.                this.models.some( function( model ) {                    // Call the driver.                    var handler = model.settingMethod && model.settingMethod( nodeID, methodName, methodHandler );                    // Update the value to the value assigned if the driver handled it.                    if ( handler !== undefined ) {                        methodHandler = require( "vwf/utility" ).merge( {}, handler );  // omit `undefined` values                    }                    // Exit the iterator once a driver has handled the assignment.                    return handler !== undefined;                } );                // Record the change.                if ( node.initialized && node.patchable ) {                    node.methods.change( methodName );                }                // Call `satMethod` on each view.                this.views.forEach( function( view ) {                    view.satMethod && view.satMethod( nodeID, methodName, methodHandler );                } );            }            this.logger.debugu();            return methodHandler;        };        // -- getMethod ----------------------------------------------------------------------------        /// @name module:vwf.getMethod        ///         /// @see {@link module:vwf/api/kernel.getMethod}        this.getMethod = function( nodeID, methodName ) {            this.logger.debuggx( "getMethod", function() {                return [ nodeID, methodName ];            } );            var node = nodes.existing[nodeID];            // Call `gettingMethod` on each model. The first model to return a non-undefined value            // dictates the return value.            var methodHandler = {};            this.models.some( function( model ) {                // Call the driver.                var handler = model.gettingMethod && model.gettingMethod( nodeID, methodName );                // Update the value to the value assigned if the driver handled it.                if ( handler !== undefined ) {                    methodHandler = require( "vwf/utility" ).merge( {}, handler );  // omit `undefined` values                }                // Exit the iterator once a driver has handled the retrieval.                return handler !== undefined;            } );            // Call `gotMethod` on each view.            this.views.forEach( function( view ) {                view.gotMethod && view.gotMethod( nodeID, methodName, methodHandler );            } );            this.logger.debugu();            return methodHandler;        };        // -- callMethod ---------------------------------------------------------------------------        /// @name module:vwf.callMethod        ///         /// @see {@link module:vwf/api/kernel.callMethod}        this.callMethod = function( nodeID, methodName, methodParameters ) {            this.logger.debuggx( "callMethod", function() {                return [ nodeID, methodName, JSON.stringify( loggableValues( methodParameters ) ) ];            } );            // Call callingMethod() on each model. The first model to return a non-undefined value            // dictates the return value.            var methodValue = undefined;            this.models.some( function( model ) {                methodValue = model.callingMethod && model.callingMethod( nodeID, methodName, methodParameters );                return methodValue !== undefined;            } );            // Call calledMethod() on each view.            this.views.forEach( function( view ) {                view.calledMethod && view.calledMethod( nodeID, methodName, methodParameters, methodValue );            } );            this.logger.debugu();            return methodValue;        };        // -- createEvent --------------------------------------------------------------------------        /// @name module:vwf.createEvent        ///         /// @see {@link module:vwf/api/kernel.createEvent}        this.createEvent = function( nodeID, eventName, eventParameters ) {  // TODO: parameters (used? or just for annotation?)  // TODO: allow a handler body here and treat as this.*event* = function() {} (a self-targeted handler); will help with ui event handlers            this.logger.debuggx( "createEvent", nodeID, eventName, eventParameters );            var node = nodes.existing[nodeID];            // Encode any namespacing into the name. (Namespaced names were added in 0.6.21.)            var encodedEventName = namespaceEncodedName( eventName );            // Register the event.            node.events.create( encodedEventName, node.initialized && node.patchable, eventParameters );            // Call `creatingEvent` on each model. The event is considered created after all models            // have run.            this.models.forEach( function( model ) {                model.creatingEvent && model.creatingEvent( nodeID, encodedEventName, eventParameters );            } );            // Call `createdEvent` on each view. The view is being notified that a event has been            // created.            this.views.forEach( function( view ) {                view.createdEvent && view.createdEvent( nodeID, encodedEventName, eventParameters );            } );            // Send the meta event into the application.            if ( this.models.kernel.enabled() ) {                this.fireEvent( nodeID, [ "events", "created" ], [ eventName ] );            }            this.logger.debugu();        };        // -- setEvent -----------------------------------------------------------------------------        /// @name module:vwf.setEvent        ///         /// @see {@link module:vwf/api/kernel.setEvent}        this.setEvent = function( nodeID, eventName, eventDescriptor ) {            this.logger.debuggx( "setEvent", function() {                return [ nodeID, eventName ];  // TODO: loggable eventDescriptor            } );            var node = nodes.existing[nodeID];            // eventDescriptor = normalizedHandler( eventDescriptor );  // TODO            // Encode any namespacing into the name.            var encodedEventName = namespaceEncodedName( eventName );            if ( ! node.events.hasOwn( encodedEventName ) ) {                // If the event doesn't exist on this node, delegate to `kernel.createEvent` to                // create and assign the event.                this.createEvent( nodeID, eventName, eventDescriptor.parameters );  // TODO: result                ( eventDescriptor.listeners || [] ).forEach( function( listener ) {                    return this.addEventListener( nodeID, eventName, listener, listener.context, listener.phases );                }, this );            } else {                // Locate the event in the registry.                var event = node.events.existing[ encodedEventName ];                // xxx                eventDescriptor = {                    parameters: event.parameters ?                        event.parameters.slice() : [],  // TODO: note: we're ignoring eventDescriptor.parameters in the set                    listeners: ( eventDescriptor.listeners || [] ).map( function( listener ) {                        if ( event.listeners.hasOwn( listener.id ) ) {                            return this.setEventListener( nodeID, eventName, listener.id, listener );                        } else {                            return this.addEventListener( nodeID, eventName, listener, listener.context, listener.phases );                        }                    }, this ),                };            }            this.logger.debugu();            return eventDescriptor;        };        // -- getEvent -----------------------------------------------------------------------------        /// @name module:vwf.getEvent        ///         /// @see {@link module:vwf/api/kernel.getEvent}        this.getEvent = function( nodeID, eventName ) {            this.logger.debuggx( "getEvent", function() {                return [ nodeID, eventName ];            } );            var node = nodes.existing[nodeID];            // Encode any namespacing into the name.            var encodedEventName = namespaceEncodedName( eventName );            // Locate the event in the registry.            var event = node.events.existing[ encodedEventName ];            // Build the result descriptor. Omit the `parameters` and `listeners` fields when the            // parameters or listeners are missing or empty, respectively.            var eventDescriptor = {};            if ( event.parameters ) {                eventDescriptor.parameters = event.parameters.slice();            }            if ( event.listeners.existing.length ) {                eventDescriptor.listeners = event.listeners.existing.map( function( eventListenerID ) {                    var listener = this.getEventListener( nodeID, eventName, eventListenerID );                    listener.id = eventListenerID;                    return listener;                }, this );            }            this.logger.debugu();            return eventDescriptor;        };        // -- addEventListener ---------------------------------------------------------------------        /// @name module:vwf.addEventListener        ///         /// @see {@link module:vwf/api/kernel.addEventListener}        this.addEventListener = function( nodeID, eventName, eventHandler, eventContextID, eventPhases ) {            this.logger.debuggx( "addEventListener", function() {                return [ nodeID, eventName, loggableScript( eventHandler ),                    eventContextID, eventPhases ];            } );            var node = nodes.existing[nodeID];            // Encode any namespacing into the name.            var encodedEventName = namespaceEncodedName( eventName );            // Register the event if this is the first listener added to an event on a prototype.            if ( ! node.events.hasOwn( encodedEventName ) ) {                node.events.create( encodedEventName, node.initialized && node.patchable );            }            // Locate the event in the registry.            var event = node.events.existing[ encodedEventName ];            // Normalize the descriptor.            eventHandler = normalizedHandler( eventHandler, event.parameters );            // Register the listener.            var eventListenerID = eventHandler.id || this.sequence( nodeID );            event.listeners.create( eventListenerID, node.initialized && node.patchable );            // Call `addingEventListener` on each model.            this.models.forEach( function( model ) {                model.addingEventListener &&                    model.addingEventListener( nodeID, encodedEventName, eventListenerID,                        eventHandler, eventContextID, eventPhases );            } );            // Call `addedEventListener` on each view.            this.views.forEach( function( view ) {                view.addedEventListener &&                    view.addedEventListener( nodeID, encodedEventName, eventListenerID,                        eventHandler, eventContextID, eventPhases );            } );            this.logger.debugu();            return eventListenerID;        };        // -- removeEventListener ------------------------------------------------------------------        /// @name module:vwf.removeEventListener        ///         /// @see {@link module:vwf/api/kernel.removeEventListener}        this.removeEventListener = function( nodeID, eventName, eventListenerID ) {            this.logger.debuggx( "removeEventListener", function() {                return [ nodeID, eventName, loggableScript( eventListenerID ) ];            } );            var node = nodes.existing[nodeID];            // Encode any namespacing into the name.            var encodedEventName = namespaceEncodedName( eventName );            // Locate the event in the registry.            var event = node.events.existing[ encodedEventName ];            // Unregister the listener.            event.listeners.delete( eventListenerID, node.initialized && node.patchable );            // Call `removingEventListener` on each model.            this.models.forEach( function( model ) {                model.removingEventListener &&                    model.removingEventListener( nodeID, encodedEventName, eventListenerID );            } );            // Call `removedEventListener` on each view.            this.views.forEach( function( view ) {                view.removedEventListener &&                    view.removedEventListener( nodeID, encodedEventName, eventListenerID );            } );            this.logger.debugu();            return eventListenerID;        };        // -- setEventListener ---------------------------------------------------------------------        /// @name module:vwf.setEventListener        ///         /// @see {@link module:vwf/api/kernel.setEventListener}        this.setEventListener = function( nodeID, eventName, eventListenerID, eventListener ) {            this.logger.debuggx( "setEventListener", function() {                return [ nodeID, eventName, eventListenerID ];  // TODO: loggable eventListener            } );            var node = nodes.existing[nodeID];            // Encode any namespacing into the name.            var encodedEventName = namespaceEncodedName( eventName );            // Locate the event in the registry.            var event = node.events.existing[ encodedEventName ];            // Normalize the descriptor.            eventListener = normalizedHandler( eventListener, event.parameters );            // Record the change.            if ( node.initialized && node.patchable ) {                event.listeners.change( eventListenerID );            }            // Call `settingEventListener` on each model. The first model to return a non-undefined            // value dictates the return value.            this.models.some( function( model ) {                // Call the driver.                var listener = model.settingEventListener &&                    model.settingEventListener( nodeID, encodedEventName, eventListenerID, eventListener );                // Update the value to the value assigned if the driver handled it.                if ( listener !== undefined ) {                    eventListener = require( "vwf/utility" ).merge( {}, listener );  // omit `undefined` values                }                // Exit the iterator once a driver has handled the assignment.                return listener !== undefined;            } );            // Call `satEventListener` on each view.            this.views.forEach( function( view ) {                view.satEventListener &&                    view.satEventListener( nodeID, encodedEventName, eventListenerID, eventListener );            } );            this.logger.debugu();            return eventListener;        };        // -- getEventListener ---------------------------------------------------------------------        /// @name module:vwf.getEventListener        ///         /// @see {@link module:vwf/api/kernel.getEventListener}        this.getEventListener = function( nodeID, eventName, eventListenerID ) {            this.logger.debuggx( "getEventListener", function() {                return [ nodeID, eventName, eventListenerID ];            } );            // Encode any namespacing into the name.            var encodedEventName = namespaceEncodedName( eventName );            // Call `gettingEventListener` on each model. The first model to return a non-undefined            // value dictates the return value.            var eventListener = {};            this.models.some( function( model ) {                // Call the driver.                var listener = model.gettingEventListener &&                    model.gettingEventListener( nodeID, encodedEventName, eventListenerID );                // Update the value to the value assigned if the driver handled it.                if ( listener !== undefined ) {                    eventListener = require( "vwf/utility" ).merge( {}, listener );  // omit `undefined` values                }                // Exit the iterator once a driver has handled the assignment.                return listener !== undefined;            } );            // Call `gotEventListener` on each view.            this.views.forEach( function( view ) {                view.gotEventListener &&                    view.gotEventListener( nodeID, encodedEventName, eventListenerID, eventListener );            } );            this.logger.debugu();            return eventListener;        };        // -- flushEventListeners ------------------------------------------------------------------        /// @name module:vwf.flushEventListeners        ///         /// @see {@link module:vwf/api/kernel.flushEventListeners}        this.flushEventListeners = function( nodeID, eventName, eventContextID ) {            this.logger.debuggx( "flushEventListeners", nodeID, eventName, eventContextID );            // Encode any namespacing into the name.            var encodedEventName = namespaceEncodedName( eventName );            // Retrieve the event in case we need to remove listeners from it            var node = nodes.existing[ nodeID ];            var event = node.events.existing[ encodedEventName ];            // Call `flushingEventListeners` on each model.            this.models.forEach( function( model ) {                var removedIds = model.flushingEventListeners &&                    model.flushingEventListeners( nodeID, encodedEventName, eventContextID );                                // If the model driver returned an array of the ids of event listeners that it                 // removed, remove their references in the kernel                if ( removedIds && removedIds.length ) {                    // Unregister the listeners that were flushed                    removedIds.forEach( function( removedId ) {                        event.listeners.delete( removedId, node.initialized && node.patchable );                    } );                }            } );            // Call `flushedEventListeners` on each view.            this.views.forEach( function( view ) {                view.flushedEventListeners &&                    view.flushedEventListeners( nodeID, encodedEventName, eventContextID );            } );            // TODO: `flushEventListeners` can be interpreted by the kernel now and handled with a            // `removeEventListener` call instead of separate `flushingEventListeners` and            // `flushedEventListeners` calls to the drivers.            this.logger.debugu();        };        // -- fireEvent ----------------------------------------------------------------------------        /// @name module:vwf.fireEvent        ///         /// @see {@link module:vwf/api/kernel.fireEvent}        this.fireEvent = function( nodeID, eventName, eventParameters ) {            this.logger.debuggx( "fireEvent", function() {                return [ nodeID, eventName, JSON.stringify( loggableValues( eventParameters ) ) ];            } );            // Encode any namespacing into the name. (Namespaced names were added in 0.6.21.)            var encodedEventName = namespaceEncodedName( eventName );            // Call `firingEvent` on each model.            var handled = this.models.reduce( function( handled, model ) {                return model.firingEvent && model.firingEvent( nodeID, encodedEventName, eventParameters ) || handled;            }, false );            // Call `firedEvent` on each view.            this.views.forEach( function( view ) {                view.firedEvent && view.firedEvent( nodeID, encodedEventName, eventParameters );            } );            this.logger.debugu();            // TODO: `fireEvent` needs to tell the drivers to invoke each listener individually            // now that listeners can be spread across multiple drivers.            return handled;        };        // -- dispatchEvent ------------------------------------------------------------------------        /// Dispatch an event toward a node. Using fireEvent(), capture (down) and bubble (up) along        /// the path from the global root to the node. Cancel when one of the handlers returns a        /// truthy value to indicate that it has handled the event.        ///         /// @name module:vwf.dispatchEvent        ///         /// @see {@link module:vwf/api/kernel.dispatchEvent}        this.dispatchEvent = function( nodeID, eventName, eventParameters, eventNodeParameters ) {            this.logger.debuggx( "dispatchEvent", function() {                return [ nodeID, eventName, JSON.stringify( loggableValues( eventParameters ) ),                    JSON.stringify( loggableIndexedValues( eventNodeParameters ) ) ];            } );            // Defaults for the parameters to send with the events. Values from `eventParameters`            // are sent to each node. `eventNodeParameters` contains additional values to send to            // specific nodes.            eventParameters = eventParameters || [];            eventNodeParameters = eventNodeParameters || {};            // Find the inheritance path from the node to the root.            var ancestorIDs = this.ancestors( nodeID );            var lastAncestorID = "";            // Make space to record the parameters sent to each node. Parameters provided for upper            // nodes cascade down until another definition is found for a lower node. We'll remember            // these on the way down and replay them on the way back up.            var cascadedEventNodeParameters = {                "": eventNodeParameters[""] || [] // defaults come from the "" key in eventNodeParameters            };            // Parameters passed to the handlers are the concatention of the eventParameters array,            // the eventNodeParameters for the node (cascaded), and the phase.            var targetEventParameters = undefined;            var phase = undefined;            var handled = false;            // Capturing phase.            phase = "capture"; // only handlers tagged "capture" will be invoked            handled = handled || ancestorIDs.reverse().some( function( ancestorID ) {  // TODO: reverse updates the array in place every time and we'd rather not                cascadedEventNodeParameters[ancestorID] = eventNodeParameters[ancestorID] ||                    cascadedEventNodeParameters[lastAncestorID];                lastAncestorID = ancestorID;                targetEventParameters =                    eventParameters.concat( cascadedEventNodeParameters[ancestorID], phase );                                targetEventParameters.phase = phase; // smuggle the phase across on the parameters array  // TODO: add "phase" as a fireEvent() parameter? it isn't currently needed in the kernel public API (not queueable, not called by the drivers), so avoid if possible                return this.fireEvent( ancestorID, eventName, targetEventParameters );            }, this );            // At target.            phase = undefined; // invoke all handlers            cascadedEventNodeParameters[nodeID] = eventNodeParameters[nodeID] ||                cascadedEventNodeParameters[lastAncestorID];            targetEventParameters =                eventParameters.concat( cascadedEventNodeParameters[nodeID], phase );            handled = handled || this.fireEvent( nodeID, eventName, targetEventParameters );            // Bubbling phase.            phase = undefined; // invoke all handlers            handled = handled || ancestorIDs.reverse().some( function( ancestorID ) {  // TODO: reverse updates the array in place every time and we'd rather not                targetEventParameters =                    eventParameters.concat( cascadedEventNodeParameters[ancestorID], phase );                return this.fireEvent( ancestorID, eventName, targetEventParameters );            }, this );            this.logger.debugu();        };        // -- execute ------------------------------------------------------------------------------        /// @name module:vwf.execute        ///         /// @see {@link module:vwf/api/kernel.execute}        this.execute = function( nodeID, scriptText, scriptType, callback_async /* result */ ) {            this.logger.debuggx( "execute", function() {                return [ nodeID, loggableScript( scriptText ), scriptType ];            } );            // Assume JavaScript if the type is not specified and the text is a string.            if ( ! scriptType && ( typeof scriptText == "string" || scriptText instanceof String ) ) {                scriptType = "application/javascript";            }            // Call executing() on each model. The script is considered executed after all models            // have run.            var scriptValue = undefined;            // Watch for any async kernel calls generated as we execute the scriptText and wait for            // them to complete before calling the callback.            vwf.models.kernel.capturingAsyncs( function() {                vwf.models.some( function( model ) {                    scriptValue = model.executing &&                                  model.executing( nodeID, scriptText, scriptType );                    return scriptValue !== undefined;                } );                // Call executed() on each view to notify view that a script has been executed.                vwf.views.forEach( function( view ) {                    view.executed && view.executed( nodeID, scriptText, scriptType );                } );            }, function() {                callback_async && callback_async( scriptValue );            } );            this.logger.debugu();            return scriptValue;        };        // -- random -------------------------------------------------------------------------------        /// @name module:vwf.random        ///         /// @see {@link module:vwf/api/kernel.random}        this.random = function( nodeID ) {            return this.models.object.random( nodeID );        };        // -- seed ---------------------------------------------------------------------------------        /// @name module:vwf.seed        ///         /// @see {@link module:vwf/api/kernel.seed}        this.seed = function( nodeID, seed ) {            return this.models.object.seed( nodeID, seed );        };        // -- time ---------------------------------------------------------------------------------        /// The current simulation time.        ///         /// @name module:vwf.time        ///         /// @see {@link module:vwf/api/kernel.time}        this.time = function() {            return this.now;        };        // -- client -------------------------------------------------------------------------------        /// The moniker of the client responsible for the current action. Will be falsy for actions        /// originating in the server, such as time ticks.        ///         /// @name module:vwf.client        ///         /// @see {@link module:vwf/api/kernel.client}        this.client = function() {            return this.client_;        };        // -- moniker ------------------------------------------------------------------------------        /// The identifer the server assigned to this client.        ///         /// @name module:vwf.moniker        ///         /// @see {@link module:vwf/api/kernel.moniker}        this.moniker = function() {            return this.moniker_;        };        // -- application --------------------------------------------------------------------------        /// @name module:vwf.application        ///         /// @see {@link module:vwf/api/kernel.application}        this.application = function( initializedOnly ) {            var applicationID;            Object.keys( nodes.globals ).forEach( function( globalID ) {                var global = nodes.existing[ globalID ];                if ( ( ! initializedOnly || global.initialized ) && global.name === "application" ) {                    applicationID = globalID;                }            }, this );            return applicationID;        };        // -- intrinsics ---------------------------------------------------------------------------        /// @name module:vwf.intrinsics        ///         /// @see {@link module:vwf/api/kernel.intrinsics}        this.intrinsics = function( nodeID, result ) {            return this.models.object.intrinsics( nodeID, result );        };        // -- uri ----------------------------------------------------------------------------------        /// @name module:vwf.uri        ///         /// @see {@link module:vwf/api/kernel.uri}        this.uri = function( nodeID, searchAncestors, initializedOnly ) {            var uri = this.models.object.uri( nodeID );            if ( searchAncestors ) {                while ( ! uri && ( nodeID = this.parent( nodeID, initializedOnly ) ) ) {                    uri = this.models.object.uri( nodeID );                }            }            return uri;        };        // -- name ---------------------------------------------------------------------------------        /// @name module:vwf.name        ///         /// @see {@link module:vwf/api/kernel.name}        this.name = function( nodeID ) {            return this.models.object.name( nodeID );        };        // -- prototype ----------------------------------------------------------------------------        /// @name module:vwf.prototype        ///         /// @see {@link module:vwf/api/kernel.prototype}        this.prototype = function( nodeID ) {            return this.models.object.prototype( nodeID );        };        // -- prototypes ---------------------------------------------------------------------------        /// @name module:vwf.prototypes        ///         /// @see {@link module:vwf/api/kernel.prototypes}        this.prototypes = function( nodeID, includeBehaviors ) {            var prototypes = [];            do {                // Add the current node's behaviors.                if ( includeBehaviors ) {                    var b = [].concat( this.behaviors( nodeID ) );                    Array.prototype.push.apply( prototypes, b.reverse() );                }                // Get the next prototype.                nodeID = this.prototype( nodeID );                // Add the prototype.                if ( nodeID ) {                    prototypes.push( nodeID );                }            } while ( nodeID );            return prototypes;        };        // -- behaviors ----------------------------------------------------------------------------        /// @name module:vwf.behaviors        ///         /// @see {@link module:vwf/api/kernel.behaviors}        this.behaviors = function( nodeID ) {            return this.models.object.behaviors( nodeID );        };        // -- globals ------------------------------------------------------------------------------        /// @name module:vwf.globals        ///         /// @see {@link module:vwf/api/kernel.globals}        this.globals = function( initializedOnly ) {            var globals = {};            Object.keys( nodes.globals ).forEach( function( globalID ) {                if ( ! initializedOnly || nodes.existing[ globalID ].initialized ) {                    globals[ globalID ] = undefined;                }            }, this );            return globals;        };        // -- global -------------------------------------------------------------------------------        /// @name module:vwf.global        ///         /// @see {@link module:vwf/api/kernel.global}        this.global = function( globalReference, initializedOnly ) {            var globals = this.globals( initializedOnly );            // Look for a global node whose URI matches `globalReference`. If there is no match by            // URI, then search again by name.            return matches( "uri" ) || matches( "name" );            // Look for a global node where the field named by `field` matches `globalReference`.            function matches( field ) {                var matchingID;                Object.keys( globals ).some( function( globalID ) {                    if ( nodes.existing[ globalID ][ field ] === globalReference ) {                        matchingID = globalID;                        return true;                    }                } );                return matchingID;            }        };        // -- root ---------------------------------------------------------------------------------        /// @name module:vwf.root        ///         /// @see {@link module:vwf/api/kernel.root}        this.root = function( nodeID, initializedOnly ) {            var rootID;            // Walk the ancestors to the top of the tree. Stop when we reach the pseudo-node at the            // global root, which unlike all other nodes has a falsy ID, or `undefined` if we could            // not reach the top because `initializedOnly` is set and we attempted to cross between            // nodes that have and have not completed initialization.            do {                rootID = nodeID;                nodeID = this.parent( nodeID, initializedOnly );            } while ( nodeID );            // Return the root ID, or `undefined` when `initializedOnly` is set and the node can't            // see the root.            return nodeID === undefined ? undefined : rootID;        };        // -- ancestors ----------------------------------------------------------------------------        /// @name module:vwf.ancestors        ///         /// @see {@link module:vwf/api/kernel.ancestors}        this.ancestors = function( nodeID, initializedOnly ) {            var ancestors = [];            nodeID = this.parent( nodeID, initializedOnly );            while ( nodeID ) {                ancestors.push( nodeID );                nodeID = this.parent( nodeID, initializedOnly );            }            return ancestors;        };        // -- parent -------------------------------------------------------------------------------        /// @name module:vwf.parent        ///         /// @see {@link module:vwf/api/kernel.parent}        this.parent = function( nodeID, initializedOnly ) {            return this.models.object.parent( nodeID, initializedOnly );        };        // -- children -----------------------------------------------------------------------------        /// @name module:vwf.children        ///         /// @see {@link module:vwf/api/kernel.children}        this.children = function( nodeID, initializedOnly ) {            if ( nodeID === undefined ) {                this.logger.errorx( "children", "cannot retrieve children of nonexistent node" );                return;            }            return this.models.object.children( nodeID, initializedOnly );        };        // -- child --------------------------------------------------------------------------------        /// @name module:vwf.child        ///         /// @see {@link module:vwf/api/kernel.child}        this.child = function( nodeID, childReference, initializedOnly ) {            var children = this.children( nodeID, initializedOnly );            if ( typeof childReference === "number" || childReference instanceof Number ) {                return children[ childReference ];            } else {                return children.filter( function( childID ) {                    return childID && this.name( childID ) === childReference;                }, this )[ 0 ];            }        };        // -- descendants --------------------------------------------------------------------------        /// @name module:vwf.descendants        ///         /// @see {@link module:vwf/api/kernel.descendants}        this.descendants = function( nodeID, initializedOnly ) {            if ( nodeID === undefined ) {                this.logger.errorx( "descendants", "cannot retrieve children of nonexistent node" );                return;            }            var descendants = [];            this.children( nodeID, initializedOnly ).forEach( function( childID ) {                descendants.push( childID );                childID && Array.prototype.push.apply( descendants, this.descendants( childID, initializedOnly ) );            }, this );            return descendants;        };        // -- sequence -----------------------------------------------------------------------------        /// @name module:vwf.sequence        ///         /// @see {@link module:vwf/api/kernel.sequence}        this.sequence = function( nodeID ) {            return this.models.object.sequence( nodeID );        };        /// Locate nodes matching a search pattern. See vwf.api.kernel#find for details.        ///         /// @name module:vwf.find        ///        /// @param {ID} nodeID        ///   The reference node. Relative patterns are resolved with respect to this node. `nodeID`        ///   is ignored for absolute patterns.        /// @param {String} matchPattern        ///   The search pattern.        /// @param {Boolean} [initializedOnly]        ///   Interpret nodes that haven't completed initialization as though they don't have        ///   ancestors. Drivers that manage application code should set `initializedOnly` since        ///   applications should never have access to uninitialized parts of the application graph.        /// @param {Function} [callback]        ///   A callback to receive the search results. If callback is provided, find invokes        ///   callback( matchID ) for each match. Otherwise the result is returned as an array.        ///         /// @returns {ID[]|undefined}        ///   If callback is provided, undefined; otherwise an array of the node ids of the result.        ///         /// @see {@link module:vwf/api/kernel.find}        this.find = function( nodeID, matchPattern, initializedOnly, callback /* ( matchID ) */ ) {            // Interpret `find( nodeID, matchPattern, callback )` as            // `find( nodeID, matchPattern, undefined, callback )`. (`initializedOnly` was added in            // 0.6.8.)            if ( typeof initializedOnly == "function" || initializedOnly instanceof Function ) {                callback = initializedOnly;                initializedOnly = undefined;            }            // Run the query.            var matchIDs = find.call( this, nodeID, matchPattern, initializedOnly );            // Return the result. Invoke the callback if one was provided. Otherwise, return the            // array directly.            if ( callback ) {                matchIDs.forEach( function( matchID ) {                    callback( matchID );                } );            } else {  // TODO: future iterator proxy                return matchIDs;            }        };        // -- findClients ------------------------------------------------------------------------------        /// Locate client nodes matching a search pattern.         ///        /// @name module:vwf.findClients        ///        /// @param {ID} nodeID        ///   The reference node. Relative patterns are resolved with respect to this node. `nodeID`        ///   is ignored for absolute patterns.        /// @param {String} matchPattern        ///   The search pattern.        /// @param {Function} [callback]        ///   A callback to receive the search results. If callback is provided, find invokes        ///   callback( matchID ) for each match. Otherwise the result is returned as an array.        ///         /// @returns {ID[]|undefined}        ///   If callback is provided, undefined; otherwise an array of the node ids of the result.        ///         /// @deprecated in version 0.6.21. Instead of `kernel.findClients( reference, "/pattern" )`,        ///   use `kernel.find( reference, "doc('http://vwf.example.com/clients.vwf')/pattern" )`.        ///         /// @see {@link module:vwf/api/kernel.findClients}        this.findClients = function( nodeID, matchPattern, callback /* ( matchID ) */ ) {            this.logger.warn( "`kernel.findClients` is deprecated. Use " +                "`kernel.find( nodeID, \"doc('http://vwf.example.com/clients.vwf')/pattern\" )`" +                " instead." );            var clientsMatchPattern = "doc('http://vwf.example.com/clients.vwf')" +                ( matchPattern[0] === "/" ? "" : "/" ) + matchPattern;            return this.find( nodeID || this.application(), clientsMatchPattern, callback );        };        /// Test a node against a search pattern. See vwf.api.kernel#test for details.        ///         /// @name module:vwf.test        ///         /// @param {ID} nodeID        ///   The reference node. Relative patterns are resolved with respect to this node. `nodeID`        ///   is ignored for absolute patterns.        /// @param {String} matchPattern        ///   The search pattern.        /// @param {ID} testID        ///   A node to test against the pattern.        /// @param {Boolean} [initializedOnly]        ///   Interpret nodes that haven't completed initialization as though they don't have        ///   ancestors. Drivers that manage application code should set `initializedOnly` since        ///   applications should never have access to uninitialized parts of the application graph.        ///         /// @returns {Boolean}        ///   true when testID matches the pattern.        ///         /// @see {@link module:vwf/api/kernel.test}        this.test = function( nodeID, matchPattern, testID, initializedOnly ) {            // Run the query.            var matchIDs = find.call( this, nodeID, matchPattern, initializedOnly );            // Search for the test node in the result.            return matchIDs.some( function( matchID ) {                return matchID == testID;            } );        };        // == Private functions ====================================================================        var isSocketIO07 = function() {            //return ( parseFloat( io.version ) >= 0.7 );            return true        }        // -- loadComponent ------------------------------------------------------------------------        /// @name module:vwf~loadComponent        var loadComponent = async function( nodeURI, baseURI, callback_async /* nodeDescriptor */, errback_async /* errorMessage */ ) {  // TODO: turn this into a generic xhr loader exposed as a kernel function?            if ( nodeURI == vwf.kutility.protoNodeURI ) {                await callback_async( vwf.kutility.protoNodeDescriptor );            } else if ( nodeURI.match( RegExp( "^data:application/json;base64," ) ) ) {                // Primarly for testing, parse one specific form of data URIs. We need to parse                // these ourselves since Chrome can't load data URIs due to cross origin                // restrictions.                await callback_async( JSON.parse( atob( nodeURI.substring( 29 ) ) ) );  // TODO: support all data URIs            } else {                queue.suspend( "while loading " + nodeURI ); // suspend the queue                let fetchUrl =  remappedURI( require( "vwf/utility" ).resolveURI( nodeURI, baseURI ) );                let dbName = fetchUrl.replace(window.location.origin + '/', "").split(".").join("_") + '_yaml';                let worldName = dbName.split('/')[0];                var userDB = window._LCS_WORLD_USER.get('worlds').get(worldName);                var fileName = "";                if(dbName.includes("vwf_example_com")){                    userDB = window._LCS_SYS_USER.get('proxy');                    fileName = dbName;                } else {                   fileName = dbName.replace(worldName + '/', "");                }                      userDB.get(fileName).once(async function(res) {                    let result = YAML.parse(res.file);                    let nativeObject = result;                    // console.log(nativeObject);                      if(nativeObject) {                         await callback_async( nativeObject );                         queue.resume( "after loading " + nodeURI ); // resume the queue; may invoke dispatch(), so call last before returning to the host                          } else {                          vwf.logger.warnx( "loadComponent", "error loading", nodeURI + ":", error );                         errback_async( error );                         queue.resume( "after loading " + nodeURI ); // resume the queue; may invoke dispatch(), so call last before returning to the host                           }                }, {wait: 200})            }        };        // -- loadScript ---------------------------------------------------------------------------        /// @name module:vwf~loadScript        var loadScript = async function( scriptURI, baseURI, callback_async /* scriptText */, errback_async /* errorMessage */ ) {            if ( scriptURI.match( RegExp( "^data:application/javascript;base64," ) ) ) {                // Primarly for testing, parse one specific form of data URIs. We need to parse                // these ourselves since Chrome can't load data URIs due to cross origin                // restrictions.                await callback_async( atob( scriptURI.substring( 35 ) ) );  // TODO: support all data URIs            } else {                queue.suspend( "while loading " + scriptURI ); // suspend the queue                let fetchUrl = remappedURI( require( "vwf/utility" ).resolveURI( scriptURI, baseURI ) );                let dbName = fetchUrl.replace(window.location.origin + '/', "").split(".").join("_");                let worldName = dbName.split('/')[0];                var userDB = window._LCS_WORLD_USER.get('worlds').get(worldName);                var fileName = "";                if(dbName.includes("vwf_example_com")){                    userDB = window._LCS_SYS_USER.get('proxy');                    fileName = dbName                } else {                    fileName = dbName.replace(worldName + '/', "");                }                userDB.get(fileName).once(async function(res) {                    let scriptText = res.file;                    try {                        await callback_async( scriptText );                        queue.resume( "after loading " + scriptURI ); // resume the queue; may invoke dispatch(), so call last before returning to the host                    } catch (e) {                        vwf.logger.warnx( "loadScript", "error loading", scriptURI + ":", error );                        errback_async( error );                        queue.resume( "after loading " + scriptURI ); // resume the queue; may invoke dispatch(), so call last before returning to the host                    }                }, {wait: 200})            }        };        /// Determine if a given property of a node has a setter function, either directly on the        /// node or inherited from a prototype.        ///         /// This function must run as a method of the kernel. Invoke as: nodePropertyHasSetter.call(        ///   kernel, nodeID, propertyName ).        ///         /// @name module:vwf~nodePropertyHasSetter        ///         /// @param {ID} nodeID        /// @param {String} propertyName        ///         /// @returns {Boolean}        var nodePropertyHasSetter = function( nodeID, propertyName ) { // invoke with the kernel as "this"  // TODO: this is peeking inside of vwf-model-javascript; need to delegate to all script drivers            var node = this.models.javascript.nodes[nodeID];            var setter = node.private.setters && node.private.setters[propertyName];            return typeof setter == "function" || setter instanceof Function;        };        /// Determine if a given property of a node has a setter function. The node's prototypes are        /// not considered.        ///         /// This function must run as a method of the kernel. Invoke as:        ///   nodePropertyHasOwnSetter.call( kernel, nodeID, propertyName ).        ///         /// @name module:vwf~nodePropertyHasOwnSetter        ///         /// @param {ID} nodeID        /// @param {String} propertyName        ///         /// @returns {Boolean}        var nodePropertyHasOwnSetter = function( nodeID, propertyName ) { // invoke with the kernel as "this"  // TODO: this is peeking inside of vwf-model-javascript; need to delegate to all script drivers            var node = this.models.javascript.nodes[nodeID];            var setter = node.private.setters && node.private.setters.hasOwnProperty( propertyName ) && node.private.setters[propertyName];            return typeof setter == "function" || setter instanceof Function;        };        /// Determine if a node has a child with the given name, either directly on the node or        /// inherited from a prototype.        ///         /// This function must run as a method of the kernel. Invoke as: nodeHasChild.call(        ///   kernel, nodeID, childName ).        ///         /// @name module:vwf~nodeHasChild        ///         /// @param {ID} nodeID        /// @param {String} childName        ///         /// @returns {Boolean}        var nodeHasChild = function( nodeID, childName ) { // invoke with the kernel as "this"  // TODO: this is peeking inside of vwf-model-javascript            var node = this.models.javascript.nodes[nodeID];            return childName in node.children;        };        /// Determine if a node has a child with the given name. The node's prototypes are not        /// considered.        ///         /// This function must run as a method of the kernel. Invoke as: nodeHasOwnChild.call(        ///   kernel, nodeID, childName ).        ///         /// @name module:vwf~nodeHasOwnChild        ///         /// @param {ID} nodeID        /// @param {String} childName        ///         /// @returns {Boolean}        var nodeHasOwnChild = function( nodeID, childName ) { // invoke with the kernel as "this"  // TODO: this is peeking inside of vwf-model-javascript            var node = this.models.javascript.nodes[nodeID];            var hasChild = false;            if ( parseInt( childName ).toString() !== childName ) {                hasChild = node.children.hasOwnProperty( childName );  // TODO: this is peeking inside of vwf-model-javascript            }            else {                // Children with numeric names do not get added as properties of the children array, so loop over the children                // to check manually                for(var i=0, il=node.children.length; i<il;i++) {                    if(childName === node.children[i].name) {                        hasChild = true;                     }                }            }            return hasChild;        };        /// Determine if a component specifier is a URI.        ///         /// A component may be specified as the URI of a resource containing a descriptor (string),        /// a descriptor (object), or the ID of a previously-created node (primitive).        ///         /// @name module:vwf~componentIsURI        ///         /// @param {String|Object} candidate        ///         /// @returns {Boolean}        var componentIsURI = function( candidate ) {            return ( typeof candidate == "string" || candidate instanceof String ) && ! componentIsID( candidate );        };        /// Determine if a component specifier is a descriptor.        ///         /// A component may be specified as the URI of a resource containing a descriptor (string),        /// a descriptor (object), or the ID of a previously-created node (primitive).        ///         /// @name module:vwf~componentIsDescriptor        ///         /// @param {String|Object} candidate        ///         /// @returns {Boolean}        var componentIsDescriptor = function( candidate ) {            return typeof candidate == "object" && candidate != null && ! isPrimitive( candidate );        };        /// Determine if a component specifier is an ID.        ///         /// A component may be specified as the URI of a resource containing a descriptor (string),        /// a descriptor (object), or the ID of a previously-created node (primitive).        ///         /// @name module:vwf~componentIsID        ///         /// @param {String|Object} candidate        ///         /// @returns {Boolean}        var componentIsID = function( candidate ) {            return isPrimitive( candidate ) && vwf.models.object.exists( candidate ) &&                ! ( components[candidate] instanceof Array );        };        /// Determine if a value is a JavaScript primitive, or the boxed version of a JavaScript        /// primitive.        ///         /// Node IDs are JavaScript primitives. This function may be used to determine if a value        /// has the correct type to be a node ID.        ///         /// @name module:vwf~isPrimitive        ///         /// @param candidate        ///         /// @returns {Boolean}        var isPrimitive = function( candidate ) {            switch ( typeof candidate ) {                case "string":                case "number":                case "boolean":                    return true;                case "object":                    return candidate instanceof String || candidate instanceof Number ||                        candidate instanceof Boolean;                default:                    return false;            }        };        /// Determine if an object is a component descriptor. Detect the type by searching for        /// descriptor keys in the candidate object.        ///         /// @name module:vwf~objectIsComponent        ///         /// @param {Object} candidate        ///         /// @returns {Boolean}        var objectIsComponent = function( candidate ) {            var componentAttributes = [                "extends",                "implements",                "source",                "type",                "properties",                "methods",                "events",                "children",                "scripts",            ];            var isComponent = false;            if ( typeof candidate == "object" && candidate != null ) {                isComponent = componentAttributes.some( function( attributeName ) {                    return candidate.hasOwnProperty( attributeName );                } );            }                        return isComponent;         };        /// Determine if a property initializer is a detailed initializer containing explicit        /// accessor and value parameters (rather than a simple value specification). Detect the        /// type by searching for property initializer keys in the candidate object.        ///         /// @name module:vwf~valueHasAccessors        ///         /// @param {Object} candidate        ///         /// @returns {Boolean}        var valueHasAccessors = function( candidate ) {            var accessorAttributes = [                "get",                "set",                "value",                "node",                "create",                "undefined",            ];            var hasAccessors = false;            if ( typeof candidate == "object" && candidate != null ) {                hasAccessors = accessorAttributes.some( function( attributeName ) {                    return candidate.hasOwnProperty( attributeName );                } );            }                        return hasAccessors;         };        /// Determine if a method or event initializer is a detailed initializer containing a        /// parameter list along with the body text (method initializers only). Detect the type by        /// searching for method and event initializer keys in the candidate object.        ///         /// @name module:vwf~valueHasBody        ///         /// @param {Object} candidate        ///         /// @returns {Boolean}        var valueHasBody = function( candidate ) {  // TODO: refactor and share with valueHasAccessors and possibly objectIsComponent  // TODO: unlike a property initializer, we really only care if it's an object vs. text; text == use as body; object == presume o.parameters and o.body  // TODO: except that a script in the unnamed-list format would appear as an object but should be used as the body            var bodyAttributes = [                "parameters",                "body",                "listeners",            ];            var hasBody = false;  // TODO: "body" term is confusing, but that's the current terminology used in vwf/model/javascript            if ( typeof candidate == "object" && candidate != null ) {                hasBody = bodyAttributes.some( function( attributeName ) {                    return candidate.hasOwnProperty( attributeName );                } );            }                        return hasBody;         };        /// Determine if a script initializer is a detailed initializer containing explicit text and        /// type parameters (rather than being a simple text specification). Detect the type by        /// searching for the script initializer keys in the candidate object.        ///         /// @name module:vwf~valueHasType        ///         /// @param {Object} candidate        ///         /// @returns {Boolean}        var valueHasType = function( candidate ) {  // TODO: refactor and share with valueHasBody, valueHasAccessors and possibly objectIsComponent            var typeAttributes = [                "source",                "text",                "type",            ];            var hasType = false;            if ( typeof candidate == "object" && candidate != null ) {                hasType = typeAttributes.some( function( attributeName ) {                    return candidate.hasOwnProperty( attributeName );                } );            }                        return hasType;         };        /// Convert a potentially-namespaced member name into a string such that a namespaced name        /// will be distinct from an encoded name in any other namespace, or from any simple name        /// not having a namespace.        ///         /// Simple names are strings such as `"name"`. Namespaced names are arrays of strings, such        /// as `[ "ns", "name" ]` or `[ "outer", "inner", "name" ]`. An array containing a single        /// string, such as `[ "name" ]`, is not namespaced and is the same name as `"name"`.        ///         /// Each of the following encodes into a distinct value:        ///         ///   `"name"` or `[ "name" ]`        ///   `[ "a", "name" ]`        ///   `[ "b", "name" ]`        ///   `[ "a", "a", "name" ]`        ///   `[ "a", "b", "name" ]`        ///   `[ "b", "b", "name" ]`        ///   *etc.*        ///         /// @name module:vwf~namespaceEncodedName        ///         /// @param {String|String[]} memberName        ///   A string, or an array of strings containing a name preceded by any number of namespace        ///   names. In an array, each element defines a unique space for the member name and for        ///   any intermediate namespaces.        ///         /// @returns {String}        var namespaceEncodedName = function( memberName ) {            if ( typeof memberName === "object" && memberName instanceof Array ) {                return ( memberName.length !== 1 ) ? "vwf$" + memberName.join( "$" ) : memberName[0];            } else {                return memberName;            }        };        /// Convert a (potentially-abbreviated) component specification to a descriptor parsable by        /// vwf.createChild. The following forms are accepted:        ///         ///   - Descriptor: { extends: component, source: ..., type: ..., ... }        ///   - Component URI: http://host/path/to/component.vwf        ///   - Asset URI: http://host/ath/to/asset.type        ///   - Node ID        ///         /// They are converted as follows:        ///         ///   - Descriptor: unchanged [1]        ///   - Component URI: a component that extends the component identified by the URI        ///   - Asset URI: a component having the asset identified by the URI as its source        ///   - Node ID: a component that extends the previously-created node identified by the ID        ///         /// [1] As a special case, missing MIME types are filled in for assets matcching the        /// patterns *.unity3d and *.dae, and components having assets of those types but no        /// prototype declared will be upgraded to extend scene.vwf and navscene.vwf, respectively.        ///         /// @name module:vwf~normalizedComponent        ///         /// @param {String|Object} component        ///         /// @returns {Object}        var normalizedComponent = function( component ) {            // Convert a component URI to an instance of that type or an asset reference to an            // untyped reference to that asset. Convert a component ID to an instance of that            // prototype.            if ( componentIsURI( component ) ) {                if ( component.match( /\.vwf$/ ) ) {  // TODO: detect component from mime-type instead of extension?                    component = { extends: component };                } else {                    component = { source: component };                }            } else if ( componentIsID( component ) ) {                component = { extends: component };            }            // Fill in the mime type from the source specification if not provided.            if ( component.source && ! component.type ) {  // TODO: validate component                var match = component.source.match( /\.([^.]*)$/ ); // TODO: get type from mime-type (from server if remote, from os if local, or (?) from this internal table otherwise)                if ( match ) {                    switch ( match[1] ) {                        case "unity3d":                            component.type = "application/vnd.unity";                            break;                        case "dae":                            component.type = "model/vnd.collada+xml";                            break;                    }                }            }            // Fill in the component type from the mime type if not provided.            if ( component.type && ! component.extends ) {  // TODO: load from a server configuration file                switch ( component.type ) {                    case "application/vnd.unity":                        component.extends = "http://vwf.example.com/scene.vwf";                        break;                    case "model/vnd.collada+xml":                        component.extends = "http://vwf.example.com/navscene.vwf";                        break;                }            }            return component;        };        /// Convert a `Handler` specification into the standard form of an object containing        /// `parameters`, `body` and `type` fields.        ///         /// @name module:vwf~normalizedHandler        ///         /// @param {Handler|string}        /// @param {string[]} [defaultParameters]        ///         /// @returns {Handler}        var normalizedHandler = function( handler, defaultParameters ) {            // Convert abbreviated forms to the explict `Handler` form.            if ( typeof handler !== "object" || handler instanceof Array ) {                handler = { body: handler };            } else if ( require( "vwf/configuration" ).active[ "preserve-script-closures" ] && ( typeof handler == "function" || handler instanceof Function ) ) {                handler = { body: handler };            }            // Use a default parameter list if the handler doesn't provide its own and if defaults            // were provided.            if ( ! handler.parameters && defaultParameters ) {                handler.parameters = defaultParameters;            }            // Fill in a default media type if `type` is not provided. A `body` of type `string` is            // taken to be `application/javascript`.            if ( handler.type === undefined ) {                if ( typeof handler.body === "string" || handler.body instanceof String ) {                    handler.type = "application/javascript";                }            }            return handler;        };        /// Convert a `fields` object as passed between the client and reflector and stored in the        /// message queue into a form suitable for writing to a log.        ///         /// @name module:vwf~loggableFields        ///         /// @param {Object} fields        ///         /// @returns {Object}        var loggableFields = function( fields ) {            return require( "vwf/utility" ).transform( fields, require( "vwf/utility" ).transforms.transit );        };        /// Convert a component URI, descriptor or ID into a form suitable for writing to a log.        ///         /// @name module:vwf~loggableComponent        ///         /// @param {String|Object} component        ///         /// @returns {String|Object}        var loggableComponent = function( component ) {            return require( "vwf/utility" ).transform( component, loggableComponentTransformation );        };        /// Convert an arbitrary JavaScript value into a form suitable for writing to a log.        ///         /// @name module:vwf~loggableValue        ///         /// @param {Object} value        ///         /// @returns {Object}        var loggableValue = function( value ) {            return require( "vwf/utility" ).transform( value, function( object, names, depth ) {                object = require( "vwf/utility" ).transforms.transit( object, names, depth );                return typeof object == "number" ? Number( object.toPrecision(5) ) : object; // reduce numeric precision to remove visual noise            } );        };        /// Convert an array of arbitrary JavaScript values into a form suitable for writing to a        /// log.        ///         /// @name module:vwf~loggableValues        ///         /// @param {Object[]|undefined} values        ///         /// @returns {Object[]|undefined}        var loggableValues = function( values ) {            return loggableValue( values );        };        /// Convert an object indexing arrays of arbitrary JavaScript values into a form suitable        /// for writing to a log.        ///         /// @name module:vwf~loggableIndexedValues        ///         /// @param {Object|undefined} values        ///         /// @returns {Object|undefined}        var loggableIndexedValues = function( values ) {            return loggableValue( values );        };        /// Convert script text into a form suitable for writing to a log.        ///         /// @name module:vwf~loggableScript        ///         /// @param {String|undefined} script        ///         /// @returns {String}        var loggableScript = function( script ) {            return ( script || "" ).replace( /\s+/g, " " ).substring( 0, 100 );        };        // -- remappedURI --------------------------------------------------------------------------        /// Remap a component URI to its location in a local cache.        ///         /// http://vwf.example.com/component.vwf => http://localhost/proxy/vwf.example.com/component.vwf        ///         /// @name module:vwf~remappedURI        var remappedURI = function( uri ) {            var match = uri.match( RegExp( "http://(vwf.example.com)/(.*)" ) );            if ( match ) {                uri = window.location.protocol + "//" + window.location.host +                    "/proxy/" + match[1] + "/" + match[2];            }            return uri;        };        // -- resolvedDescriptor -------------------------------------------------------------------        /// Resolve relative URIs in a component descriptor.        ///         /// @name module:vwf~resolvedDescriptor        var resolvedDescriptor = function( component, baseURI ) {            return require( "vwf/utility" ).transform( component, resolvedDescriptorTransformationWithBaseURI );            function resolvedDescriptorTransformationWithBaseURI( object, names, depth ) {                return resolvedDescriptorTransformation.call( this, object, names, depth, baseURI );            }        };        // -- queueTransitTransformation -----------------------------------------------------------        /// vwf/utility/transform() transformation function to convert the message queue for proper        /// JSON serialization.        ///         /// queue: [ { ..., parameters: [ [ arguments ] ], ... }, { ... }, ... ]        ///         /// @name module:vwf~queueTransitTransformation        var queueTransitTransformation = function( object, names, depth ) {            if ( depth == 0 ) {                // Omit any private direct messages for this client, then sort by arrival order                // (rather than by time) so that messages will retain the same arrival order when                // reinserted.                return object.filter( function( fields ) {                    return ! ( fields.origin === "reflector" && fields.sequence > vwf.sequence_ ) && fields.action;  // TODO: fields.action is here to filter out tick messages  // TODO: don't put ticks on the queue but just use them to fast-forward to the current time (requires removing support for passing ticks to the drivers and nodes)                } ).sort( function( fieldsA, fieldsB ) {                    return fieldsA.sequence - fieldsB.sequence;                } );            } else if ( depth == 1 ) {                // Remove the sequence fields since they're just local annotations used to keep                // messages ordered by insertion order and aren't directly meaniful outside of this                // client.                var filtered = {};                Object.keys( object ).filter( function( key ) {                    return key != "sequence";                } ).forEach( function( key ) {                    filtered[key] = object[key];                } );                return filtered;            }            return object;        };        // -- loggableComponentTransformation ------------------------------------------------------        /// vwf/utility/transform() transformation function to truncate the verbose bits of a        /// component so that it may be written to a log.        ///         /// @name module:vwf~loggableComponentTransformation        var loggableComponentTransformation = function( object, names, depth ) {            // Get our bearings at the current recusion level.            var markers = descriptorMarkers( object, names, depth );            // Transform the object here.            switch ( markers.containerName ) {                case "extends":                    // Omit a component descriptor for the prototype.                    if ( markers.memberIndex == 0 && componentIsDescriptor( object ) ) {                        return {};                    }                    break;                case "implements":                    // Omit component descriptors for the behaviors.                    if ( markers.memberIndex == 0 && componentIsDescriptor( object ) ) {                        return {};                    }                    break;                case "properties":                    // Convert property values to a loggable version, and omit getter and setter                    // text.                    if ( markers.memberIndex == 0 && ! valueHasAccessors( object ) ||                            markers.memberIndex == 1 && names[0] == "value" ) {                        return loggableValue( object );                    } else if ( markers.memberIndex == 1 && ( names[0] == "get" || names[0] == "set" ) ) {                        return "...";                    }                    break;                case "methods":                    // Omit method body text.                    if ( markers.memberIndex == 0 && ! valueHasBody( object ) ||                            markers.memberIndex == 1 && names[0] == "body" ) {                        return "...";                    }                    break;                case "events":                    // Nothing for events.                    break;                case "children":                    // Omit child component descriptors.                    if ( markers.memberIndex == 0 && componentIsDescriptor( object ) ) {                        return {};                    }                    break;                case "scripts":                    // Shorten script text.                    if ( markers.memberIndex == 0 && ! valueHasType( object ) ||                            markers.memberIndex == 1 && names[0] == "text" ) {                        return "...";                    }                    break;            }            return object;        };        // -- resolvedDescriptorTransformation -----------------------------------------------------        /// vwf/utility/transform() transformation function to resolve relative URIs in a component        /// descriptor.        ///         /// @name module:vwf~resolvedDescriptorTransformation        var resolvedDescriptorTransformation = function( object, names, depth, baseURI ) {            // Get our bearings at the current recusion level.            var markers = descriptorMarkers( object, names, depth );            // Resolve all the URIs.            switch ( markers.containerName ) {                case "extends":                case "implements":                case "source":                case "children":                    if ( markers.memberIndex == 0 && componentIsURI( object ) ) {                        return require( "vwf/utility" ).resolveURI( object, baseURI );                    }                    break;                case "scripts":                    if ( markers.memberIndex == 1 && names[0] == "source" ) {                        return require( "vwf/utility" ).resolveURI( object, baseURI );                    }                    break;            }            return object;        };        // -- descriptorMarkers --------------------------------------------------------------------        /// Locate the closest container (`properties`, `methods`, `events`, `children`) and        /// contained member in a `vwf/utility/transform` iterator call on a component descriptor.        ///         /// @name module:vwf~descriptorMarkers        var descriptorMarkers = function( object, names, depth ) {            // Find the index of the lowest nested component in the names list.            var componentIndex = names.length;            while ( componentIndex > 2 && names[componentIndex-1] == "children" ) {                componentIndex -= 2;            }            // depth                                                  names  notes            // -----                                                  -----  -----            // 0:                                                        []  the component            // 1:                                          [ "properties" ]  its properties object            // 2:                          [ "propertyName", "properties" ]  one property            // 1:                                            [ "children" ]  the children object            // 2:                               [ "childName", "children" ]  one child            // 3:                 [ "properties", "childName", "children" ]  the child's properties            // 4: [ "propertyName", "properties", "childName", "children" ]  one child property            if ( componentIndex > 0 ) {                // Locate the container ("properties", "methods", "events", etc.) below the                // component in the names list.                var containerIndex = componentIndex - 1;                var containerName = names[containerIndex];                // Locate the member as appropriate for the container.                if ( containerName == "extends" ) {                    var memberIndex = containerIndex;                    var memberName = names[memberIndex];                } else if ( containerName == "implements" ) {                    if ( containerIndex > 0 ) {                        if ( typeof names[containerIndex-1] == "number" ) {                            var memberIndex = containerIndex - 1;                            var memberName = names[memberIndex];                        } else {                            var memberIndex = containerIndex;                            var memberName = undefined;                        }                    } else if ( typeof object != "object" || ! ( object instanceof Array ) ) {                        var memberIndex = containerIndex;                        var memberName = undefined;                    }                } else if ( containerName == "source" || containerName == "type" ) {                    var memberIndex = containerIndex;                    var memberName = names[memberIndex];                } else if ( containerName == "properties" || containerName == "methods" || containerName == "events" ||                        containerName == "children" ) {                    if ( containerIndex > 0 ) {                        var memberIndex = containerIndex - 1;                        var memberName = names[memberIndex];                    }                    } else if ( containerName == "scripts" ) {                    if ( containerIndex > 0 ) {                        if ( typeof names[containerIndex-1] == "number" ) {                            var memberIndex = containerIndex - 1;                            var memberName = names[memberIndex];                        } else {                            var memberIndex = containerIndex;                            var memberName = undefined;                        }                    } else if ( typeof object != "object" || ! ( object instanceof Array ) ) {                        var memberIndex = containerIndex;                        var memberName = undefined;                    }                } else {                    containerIndex = undefined;                    containerName = undefined;                }            }            return {                containerIndex: containerIndex,                containerName: containerName,                memberIndex: memberIndex,                memberName: memberName,            };        };        /// Locate nodes matching a search pattern. {@link module:vwf/api/kernel.find} describes the        /// supported patterns.        ///         /// This is the internal implementation used by {@link module:vwf.find} and        /// {@link module:vwf.test}.        ///         /// This function must run as a method of the kernel. Invoke it as:        ///   `find.call( kernel, nodeID, matchPattern, initializedOnly )`.        ///         /// @name module:vwf~find        ///         /// @param {ID} nodeID        ///   The reference node. Relative patterns are resolved with respect to this node. `nodeID`        ///   is ignored for absolute patterns.        /// @param {String} matchPattern        ///   The search pattern.        /// @param {Boolean} [initializedOnly]        ///   Interpret nodes that haven't completed initialization as though they don't have        ///   ancestors. Drivers that manage application code should set `initializedOnly` since        ///   applications should never have access to uninitialized parts of the application graph.        ///         /// @returns {ID[]|undefined}        ///   An array of the node ids of the result.        var find = function( nodeID, matchPattern, initializedOnly ) {            // Evaluate the expression using the provided node as the reference. Take the root node            // to be the root of the reference node's tree. If a reference node is not provided, use            // the application as the root.            var rootID = nodeID ? this.root( nodeID, initializedOnly ) :                this.application( initializedOnly );            return require( "vwf/utility" ).xpath.resolve( matchPattern, rootID, nodeID,                resolverWithInitializedOnly, this );            // Wrap `xpathResolver` to pass `initializedOnly` through.            function resolverWithInitializedOnly( step, contextID, resolveAttributes ) {                return xpathResolver.call( this, step, contextID, resolveAttributes, initializedOnly );            }        }        // -- xpathResolver ------------------------------------------------------------------------        /// Interpret the steps of an XPath expression being resolved. Use with        /// vwf.utility.xpath#resolve.        ///        /// @name module:vwf~xpathResolver        ///         /// @param {Object} step        /// @param {ID} contextID        /// @param {Boolean} [resolveAttributes]        /// @param {Boolean} [initializedOnly]        ///   Interpret nodes that haven't completed initialization as though they don't have        ///   ancestors. Drivers that manage application code should set `initializedOnly` since        ///   applications should never have access to uninitialized parts of the application graph.        ///         /// @returns {ID[]}        var xpathResolver = function( step, contextID, resolveAttributes, initializedOnly ) {            var resultIDs = [];            switch ( step.axis ) {                // case "preceding":  // TODO                // case "preceding-sibling":  // TODO                case "ancestor-or-self":                    resultIDs.push( contextID );                    Array.prototype.push.apply( resultIDs, this.ancestors( contextID, initializedOnly ) );                    break;                case "ancestor":                    Array.prototype.push.apply( resultIDs, this.ancestors( contextID, initializedOnly ) );                    break;                case "parent":                    var parentID = this.parent( contextID, initializedOnly );                    parentID && resultIDs.push( parentID );                    break;                case "self":                    resultIDs.push( contextID );                    break;                case "child":                    Array.prototype.push.apply( resultIDs,                        this.children( contextID, initializedOnly ).filter( function( childID ) {                            return childID;                        }, this )                    );                    break;                case "descendant":                    Array.prototype.push.apply( resultIDs,                        this.descendants( contextID, initializedOnly ).filter( function( descendantID ) {                            return descendantID;                        }, this )                    );                    break;                case "descendant-or-self":                    resultIDs.push( contextID );                    Array.prototype.push.apply( resultIDs,                        this.descendants( contextID, initializedOnly ).filter( function( descendantID ) {                            return descendantID;                        }, this )                    );                    break;                // case "following-sibling":  // TODO                // case "following":  // TODO                case "attribute":                    if ( resolveAttributes ) {                        resultIDs.push( "@" + contextID );  // TODO: @?                    }                    break;                // n/a: case "namespace":                // n/a:   break;            }            switch ( step.kind ) {                // Name test.                case undefined:                    resultIDs = resultIDs.filter( function( resultID ) {                        if ( resultID[0] != "@" ) {  // TODO: @?                            return xpathNodeMatchesStep.call( this, resultID, step.name );                        } else {                            return xpathPropertyMatchesStep.call( this, resultID.slice( 1 ), step.name );  // TODO: @?                        }                    }, this );                    break;                // Element test.                case "element":                    // Cases: kind(node,type)                    // element()                    // element(name)                    // element(,type)                    // element(name,type)                    resultIDs = resultIDs.filter( function( resultID ) {                        return resultID[0] != "@" && xpathNodeMatchesStep.call( this, resultID, step.name, step.type );  // TODO: @?                    }, this );                    break;                // Attribute test.                case "attribute":                    resultIDs = resultIDs.filter( function( resultID ) {                        return resultID[0] == "@" && xpathPropertyMatchesStep.call( this, resultID.slice( 1 ), step.name );  // TODO: @?                    }, this );                    break;                // The `doc()` function for referencing globals outside the current tree.                // http://www.w3.org/TR/xpath-functions/#func-doc.                case "doc":                    if ( this.root( contextID, initializedOnly ) ) {                        var globalID = this.global( step.name, initializedOnly );                        resultIDs = globalID ? [ globalID ] : [];                    } else {                        resultIDs = [];                    }                    break;                // Any-kind test.                case "node":                    break;                // Unimplemented test.                default:                    resultIDs = [];                    break;            }            return resultIDs;        }        // -- xpathNodeMatchesStep -----------------------------------------------------------------        /// Determine if a node matches a step of an XPath expression being resolved.        ///        /// @name module:vwf~xpathNodeMatchesStep        ///         /// @param {ID} nodeID        /// @param {String} [name]        /// @param {String} [type]        ///         /// @returns {Boolean}        var xpathNodeMatchesStep = function( nodeID, name, type ) {            if ( name && this.name( nodeID ) != name ) {                return false;            }            var matches_type = ! type || this.uri( nodeID ) == type ||                this.prototypes( nodeID, true ).some( function( prototypeID ) {                    return this.uri( prototypeID ) == type;            }, this );            return matches_type;        }        // -- xpathPropertyMatchesStep -------------------------------------------------------------        /// Determine if a property matches a step of an XPath expression being resolved.        ///        /// @name module:vwf~xpathPropertyMatchesStep        ///         /// @param {ID} nodeID        /// @param {String} [name]        ///         /// @returns {Boolean}        var xpathPropertyMatchesStep = function( nodeID, name ) {            var properties = this.models.object.properties( nodeID );            if ( name ) {                return properties[name];            } else {                return Object.keys( properties ).some( function( propertyName ) {                    return properties[propertyName];                }, this );            }        }        /// Merge two component descriptors into a single descriptor for a combined component. A        /// component created from the combined descriptor will behave in the same way as a        /// component created from `nodeDescriptor` that extends a component created from        /// `prototypeDescriptor`.        ///        /// Warning: this implementation modifies `prototypeDescriptor`.        ///        /// @name module:vwf~mergeDescriptors        ///        /// @param {Object} nodeDescriptor        ///   A descriptor representing a node extending `prototypeDescriptor`.        /// @param {Object} prototypeDescriptor        ///   A descriptor representing a prototype for `nodeDescriptor`.        // Limitations:        //         //   - Doesn't merge children from the prototype with like-named children in the node.        //   - Doesn't merge property setters and getters from the prototype when the node provides        //     an initializing value.        //   - Methods from the prototype descriptor are lost with no way to invoke them if the node        //     overrides them.        //   - Scripts from both the prototype and the node are retained, but if both define an        //     `initialize` function, the node's `initialize` will overwrite `initialize` in        //     the prototype.        //   - The prototype doesn't carry its location with it, so relative paths will load with        //     respect to the location of the node.        var mergeDescriptors = function( nodeDescriptor, prototypeDescriptor ) {            if ( nodeDescriptor.implements ) {                prototypeDescriptor.implements = ( prototypeDescriptor.implements || [] ).                    concat( nodeDescriptor.implements );            }            if ( nodeDescriptor.source ) {                prototypeDescriptor.source = nodeDescriptor.source;                prototypeDescriptor.type = nodeDescriptor.type;            }            if ( nodeDescriptor.properties ) {                prototypeDescriptor.properties = prototypeDescriptor.properties || {};                for ( var propertyName in nodeDescriptor.properties ) {                    prototypeDescriptor.properties[propertyName] = nodeDescriptor.properties[propertyName];                }            }            if ( nodeDescriptor.methods ) {                prototypeDescriptor.methods = prototypeDescriptor.methods || {};                for ( var methodName in nodeDescriptor.methods ) {                    prototypeDescriptor.methods[methodName] = nodeDescriptor.methods[methodName];                }            }            if ( nodeDescriptor.events ) {                prototypeDescriptor.events = prototypeDescriptor.events || {};                for ( var eventName in nodeDescriptor.events ) {                    prototypeDescriptor.events[eventName] = nodeDescriptor.events[eventName];                }            }            if ( nodeDescriptor.children ) {                prototypeDescriptor.children = prototypeDescriptor.children || {};                for ( var childName in nodeDescriptor.children ) {                    prototypeDescriptor.children[childName] = nodeDescriptor.children[childName];                }            }            if ( nodeDescriptor.scripts ) {                prototypeDescriptor.scripts = ( prototypeDescriptor.scripts || [] ).                    concat( nodeDescriptor.scripts );            }            return prototypeDescriptor;        };        /// Return an {@link external:Object.defineProperty} descriptor for a property that is to be        /// enumerable, but not writable or configurable.         ///         /// @param value        ///   A value to wrap in a descriptor.        ///         /// @returns        ///   An {@link external:Object.defineProperty} descriptor.        var enumerable = function( value ) {            return {                value: value,                enumerable: true,                writable: false,                configurable: false,            };        };        /// Return an {@link external:Object.defineProperty} descriptor for a property that is to be        /// enumerable and writable, but not configurable.         ///         /// @param value        ///   A value to wrap in a descriptor.        ///         /// @returns        ///   An {@link external:Object.defineProperty} descriptor.        var writable = function( value ) {            return {                value: value,                enumerable: true,                writable: true,                configurable: false,            };        };        /// Return an {@link external:Object.defineProperty} descriptor for a property that is to be        /// enumerable, writable, and configurable.         ///         /// @param value        ///   A value to wrap in a descriptor.        ///         /// @returns        ///   An {@link external:Object.defineProperty} descriptor.        var configurable = function( value ) {            return {                value: value,                enumerable: true,                writable: true,                configurable: true,            };        };        // == Private variables ====================================================================        /// Prototype for name-based, unordered collections in the node registry, including        /// `node.properties`, `node.methods`, and `node.events`.        var keyedCollectionPrototype = {            /// Record that a property, method or event has been created.            ///             /// @param {String} name            ///   The member name.            /// @param {Boolean} changes            ///   For patchable nodes, record changes so that `kernel.getNode` may create a patch            ///   when retrieving the node.            /// @param [value]            ///   An optional value to assign to the record. If `value` is omitted, the record will            ///   exist in the collection but have the value `undefined`.            ///             /// @returns {Boolean}            ///   `true` if the member was successfully added. `false` if a member by that name            ///   already exists.            create: function( name, changes, value ) {                if ( ! this.hasOwn( name ) ) {                    this.makeOwn( "existing" );                    // Add the member. `Object.defineProperty` is used instead of                    // `this.existing[name] = ...` since the prototype may be a behavior proxy, and                    // the accessor properties would prevent normal assignment.                    Object.defineProperty( this.existing, name,                        configurable( value ? value : undefined ) );                    if ( changes ) {                        this.makeOwn( "changes" );                        if ( this.changes[ name ] !== "removed" ) {                            this.changes[ name ] = "added";                        } else {                            this.changes[ name ] = "changed";  // previously removed, then added                        }                        if ( this.container && this.containerMember ) {                            this.container.change( this.containerMember );                        }                    }                    return true;                }                return false;            },            /// Record that a member has been deleted.            ///             /// @param {String} name            ///   The member name.            ///             /// @returns {Boolean}            ///   `true` if the member was successfully removed. `false` if a member by that name            ///   does not exist.            delete: function( name, changes ) {                if ( this.hasOwn( name ) ) {                    delete this.existing[ name ];                    if ( changes ) {                        this.makeOwn( "changes" );                        if ( this.changes[ name ] !== "added" ) {                            this.changes[ name ] = "removed";                        } else {                            delete this.changes[ name ];  // previously added, then removed                        }                        if ( this.container && this.containerMember ) {                            this.container.change( this.containerMember );                        }                    }                    return true;                }                return false;            },            /// Record that a member has changed.            ///             /// @param {String} name            ///   The member name.            ///             /// @returns {Boolean}            ///   `true` if the change was successfully recorded. `false` if a member by that name            ///   does not exist.            change: function( name, value ) {                if ( this.hasOwn( name ) ) {                    this.makeOwn( "changes" );                    if ( this.changes[ name ] !== "added" ) {                        this.changes[ name ] = value ?                            value : this.changes[ name ] || "changed";                    }                    if ( this.container && this.containerMember ) {                        this.container.change( this.containerMember );                    }                    return true;                }                return false;            },            /// Determine if a node has a member with the given name, either directly on the node or            /// inherited from a prototype.            ///             /// @param {String} name            ///   The member name.            ///             /// @returns {Boolean}            has: function( name ) {                return name in this.existing;            },            /// Determine if a node has a member with the given name. The node's prototypes are not            /// considered.            ///             /// @param {String} name            ///   The member name.            ///             /// @returns {Boolean}            // Since prototypes of the collection objects mirror the node's prototype chain,            // collection objects for the proto-prototype `node.vwf` intentionally don't inherit            // from `Object.prototype`. Otherwise the Object members `hasOwnProperty`,            // `isPrototypeOf`, etc. would be mistaken as members of a VWF node.            // Instead of using the simpler `this.existing.hasOwnProperty( name )`, we must reach            // `hasOwnProperty through `Object.prototype`.            hasOwn: function( name ) {                return Object.prototype.hasOwnProperty.call( this.existing, name );            },            /// Hoist a field from a prototype to the collection in preparation for making local            /// changes.            ///             /// If the field in the prototype is an object, create a new object with that field as            /// its prototype. If the field in the prototype is an array, clone the field since            /// arrays can't readily serve as prototypes for other arrays. In other cases, copy the            /// field from the prototype. Only objects, arrays and primitive values are supported.            ///             /// @param {String} fieldName            ///   The name of a field to hoist from the collection's prototype.            makeOwn: function( fieldName ) {                if ( ! this.hasOwnProperty( fieldName ) ) {                    if ( this[ fieldName ] instanceof Array ) {                        this[ fieldName ] = this[ fieldName ].slice();  // clone arrays                    } else if ( typeof this[ fieldName ] === "object" && this[ fieldName ] !== null ) {                        this[ fieldName ] = Object.create( this[ fieldName ] );  // inherit from objects                    } else {                        this[ fieldName ] = this[ fieldName ];  // copy primitives                    }                }            },            /// The property, method, or event members defined in this collection.            ///             /// `existing` is an unordered collection of elements and optional values. The keys are            /// the primary data. Existence on the object is significant regardless of the value.            /// Some collections store data in the element when the kernel owns additional details            /// about the member. Values will be `undefined` in other collections.            ///             /// For each collection, `existing` is the authoritative list of the node's members. Use            /// `collection.hasOwn( memberName )` to determine if the node defines a property,            /// method or event by that name.            ///             /// The prototype of each `existing` object will be the `existing` object of the node's            /// prototype (or a proxy to the top behavior for nodes with behaviors). Use            /// `collection.has( memberName )` to determine if a property, method or event is            /// defined on the node or its prototypes.            existing: Object.create( null                // name: undefined,                // name: { ... } -- details                // ...            ),            /// The change list for members in this collection.            ///             /// For patchable nodes, `changes` records the members that have been added, removed, or            /// changed since the node was first initialized. `changes` is not created in the            /// collection until the first change occurs. Only the change is recorded here. The            /// state behind the change is retrieved from the drivers when needed.            changes: {                // name: "added"                // name: "removed"                // name: "changed"                // name: { ... } -- changed, with details                // ...            },            /// The parent collection if this collection is a member of another. Changes applied to            /// members of this collection will call `container.change( containerMember )` to also            /// set the change flag for the containing member.            ///             /// For example, members of the `node.events` collection contain listener collections at            /// `node.events.existing[name].listeners`. Each listener collection knows its event            /// name and points back to `node.events`. Changing a listener will call            /// `node.events.change( name )` to mark the event as changed.            container: undefined,            /// This collection's name in the parent if this collection is a member of another            /// collection. Changes to members of this collection will mark that member changed in            /// the containing collection.            containerMember: undefined,        };        /// Prototype for index-based, ordered collections in the node registry, including        /// `event.listeners`.        var indexedCollectionPrototype = {            /// Record that a member has been created.            ///             /// @param {string|number|boolean|null} id            ///   The member's unique id.            /// @param {Boolean} changes            ///   For patchable nodes, record changes so that `kernel.getNode` may create a patch            ///   when retrieving the node.            ///             /// @returns {Boolean}            ///   `true` if the member was successfully added. `false` if a member with that id            ///   already exists.            create: function( id, changes ) {                if ( ! this.hasOwn( id ) ) {                    this.makeOwn( "existing" );                    this.existing.push( id );                    if ( changes ) {                        this.makeOwn( "changes" );                        var removedIndex = this.changes.removed ?                            this.changes.removed.indexOf( id ) : -1;                        if ( removedIndex < 0 ) {                            this.changes.added = this.changes.added || [];                            this.changes.added.push( id );                        } else {                            this.changes.removed.splice( removedIndex, 1 );                            this.changes.changed = this.changes.changed || [];                            this.changes.changed.push( id );                        }                        if ( this.container && this.containerMember ) {                            this.container.change( this.containerMember );                        }                    }                    return true;                }                return false;            },            /// Record that a member has been deleted.            ///             /// @param {string|number|boolean|null} id            ///   The member's unique id.            ///             /// @returns {Boolean}            ///   `true` if the member was successfully removed. `false` if a member with that id            ///   does not exist.            delete: function( id, changes ) {                if ( this.hasOwn( id ) ) {                    this.existing.splice( this.existing.indexOf( id ), 1 );                    if ( changes ) {                        this.makeOwn( "changes" );                        var addedIndex = this.changes.added ?                            this.changes.added.indexOf( id ) : -1;                        if ( addedIndex < 0 ) {                            this.changes.removed = this.changes.removed || [];                            this.changes.removed.push( id );                        } else {                            this.changes.added.splice( addedIndex, 1 );                        }                        if ( this.container && this.containerMember ) {                            this.container.change( this.containerMember );                        }                    }                    return true;                }                return false;            },            /// Record that a member has changed.            ///             /// @param {string|number|boolean|null} id            ///   The member's unique id.            ///             /// @returns {Boolean}            ///   `true` if the change was successfully recorded. `false` if a member with that id            ///   does not exist.            change: function( id ) {                if ( this.hasOwn( id ) ) {                    this.makeOwn( "changes" );                    var addedIndex = this.changes.added ?                        this.changes.added.indexOf( id ) : -1;                    var changedIndex = this.changes.changed ?                        this.changes.changed.indexOf( id ) : -1;                    if ( addedIndex < 0 && changedIndex < 0 ) {                        this.changes.changed = this.changes.changed || [];                        this.changes.changed.push( id );                    }                    if ( this.container && this.containerMember ) {                        this.container.change( this.containerMember );                    }                    return true;                }                return false;            },            /// Determine if a node has a member with the given id.            ///             /// `has` is the same as `hasOwn` for `indexedCollectionPrototype` since index-based            /// collections don't automatically inherit from their prototypes.            ///             /// @param {string|number|boolean|null} id            ///   The member's unique id.            ///             /// @returns {Boolean}            has: function( id ) {                return this.hasOwn( id );            },            /// Determine if a node has a member with the given id. The node's prototypes are not            /// considered.            ///             /// @param {string|number|boolean|null} id            ///   The member's unique id.            ///             /// @returns {Boolean}            hasOwn: function( id ) {                return this.existing ? this.existing.indexOf( id ) >= 0 : false;            },            /// Hoist a field from a prototype to the collection in preparation for making local            /// changes.            ///             /// If the field in the prototype is an object, create a new object with that field as            /// its prototype. If the field in the prototype is an array, clone the field since            /// arrays can't readily serve as prototypes for other arrays. In other cases, copy the            /// field from the prototype. Only objects, arrays and primitive values are supported.            ///             /// @param {String} fieldName            ///   The name of a field to hoist from the collection's prototype.            makeOwn: function( fieldName ) {                if ( ! this.hasOwnProperty( fieldName ) ) {                    if ( this[ fieldName ] instanceof Array ) {                        this[ fieldName ] = this[ fieldName ].slice();  // clone arrays                    } else if ( typeof this[ fieldName ] === "object" && this[ fieldName ] !== null ) {                        this[ fieldName ] = Object.create( this[ fieldName ] );  // inherit from objects                    } else {                        this[ fieldName ] = this[ fieldName ];  // copy primitives                    }                }            },            /// IDs of the members defined in this collection.            ///             /// `existing` is an ordered list of IDs, which much be unique within the collection.            /// The IDs retain the order in which they were originally added.            ///             /// For each collection, `existing` is the authoritative list of the node's members. Use            /// `collection.hasOwn( memberID )` to determine if the collection contains a member            /// with that id. Unlike `keyedCollectionPrototype` collections,            /// `indexedCollectionPrototype` collections aren't connected in parallel with their            /// containers' prototype chains.            existing: [                // id,                // id,                // ...            ],            /// The change list for members in this collection.            ///             /// For patchable nodes, `changes` records the members that have been added, removed, or            /// changed since the node was first initialized. Changes are recorded in separate            /// `added`, `removed`, and `changed` arrays, respectively. The `added` array retains            /// the order in which the members were added. Although `removed` and `changed` are also            /// arrays, the order of removals and changes is not significant.            ///             /// `changes` is not created in the collection until the first change occurs. Only the            /// change is recorded here. The state behind the change is retrieved from the drivers            /// when needed.            changes: {                // added: [ id, ... ],                // removed: [ id, ... ],                // changed: [ id, ... ],            },            /// The parent collection if this collection is a member of another. Changes applied to            /// members of this collection will call `container.change( containerMember )` to also            /// set the change flag for the containing member.            ///             /// For example, members of the `node.events` collection contain listener collections at            /// `node.events.existing[name].listeners`. Each listener collection knows its event            /// name and points back to `node.events`. Changing a listener will call            /// `node.events.change( name )` to mark the event as changed.            container: undefined,            /// This collection's name in the parent if this collection is a member of another            /// collection. Changes to members of this collection will mark that member changed in            /// the containing collection.            containerMember: undefined,        };        // Prototype for the `events` collection in the `nodes` objects.        var eventCollectionPrototype = Object.create( keyedCollectionPrototype, {            create: {                value: function( name, changes, parameters ) {                    var value = parameters ? {                        parameters: parameters.slice(), // clone                    } : {};                    value.listeners = Object.create( indexedCollectionPrototype, {                        container: enumerable( this ),                        containerMember: enumerable( name ),                    } );                    return keyedCollectionPrototype.create.call( this, name, changes, value );                }            },        } );        /// The application's nodes, indexed by ID.        ///         /// The kernel defines an application as:        ///         ///   * A tree of nodes,        ///   * Extending prototypes and implementing behaviors,        ///   * Publishing properties, and        ///   * Communicating using methods and events.        ///         /// This definition is as abstract as possible to avoid imposing unnecessary policy on the        /// application. The concrete realization of these concepts lives in the hearts and minds of        /// the drivers configured for the application. `nodes` contains the kernel's authoritative        /// data about this arrangement.        ///         /// @name module:vwf~nodes        // Note: this is a first step towards moving authoritative data out of the vwf/model/object        // and vwf/model/javascript drivers and removing the kernel's dependency on them as special        // cases. Only `nodes.existing[id].properties` is currently implemented this way.        var nodes = {            /// Register a node as it is created.            ///             /// @param {ID} nodeID            ///   The ID assigned to the new node. The node will be indexed in `nodes` by this ID.            /// @param {ID} prototypeID            ///   The ID of the node's prototype, or `undefined` if this is the proto-prototype,            ///   `node.vwf`.            /// @param {ID[]} behaviorIDs            ///   An array of IDs of the node's behaviors. `behaviorIDs` should be an empty array if            ///   the node doesn't have any behaviors.            /// @param {String} nodeURI            ///   The node's URI. `nodeURI` should be the component URI if this is the root node of            ///   a component loaded from a URI, and undefined in all other cases.            /// @param {String} nodeName            ///   The node's name.            /// @param {ID} parentID            ///   The ID of the node's parent, or `undefined` if this is the application root node            ///   or another global, top-level node.            ///             /// @returns {Object}             ///   The kernel `node` object if the node was successfully added. `undefined` if a node            ///   identified by `nodeID` already exists.            create: function( nodeID, prototypeID, behaviorIDs, nodeURI, nodeName, parentID ) {                // if ( ! this.existing[nodeID] ) {                    var self = this;                    var prototypeNode = behaviorIDs.reduce( function( prototypeNode, behaviorID ) {                        return self.proxy( prototypeNode, self.existing[behaviorID] );                    }, this.existing[prototypeID] );                    // Look up the parent.                    var parentNode = this.existing[parentID];                    // If this is the global root of a new tree, add it to the `globals` set.                    if ( ! parentNode ) {                        this.globals[nodeID] = undefined;                    }                    // Add the node to the registry.                    return this.existing[nodeID] = {                        // id: ...,                        // Inheritance. -- not implemented here yet; still using vwf/model/object                        // prototype: ...,                        // behaviors: [],                        // Intrinsic state. -- not implemented here yet.                        // source: ...,                        // type: ...,                        uri: nodeURI,                        name: nodeName,                        // Internal state. The change flags are omitted until needed. -- not implemented here yet; still using vwf/model/object                        // sequence: ...,                        // sequenceChanged: true / false,                        // prng: ...,                        // prngChanged: true / false,                        // Tree. -- not implemented here yet; still using vwf/model/object                        // parent: ...,                        // children: [],                        // Property, Method and Event members defined on the node.                        properties: Object.create( keyedCollectionPrototype, {                            existing: enumerable( Object.create( prototypeNode ?                                prototypeNode.properties.existing : null ) ),                        } ),                        methods: Object.create( keyedCollectionPrototype, {                            existing: enumerable( Object.create( prototypeNode ?                                prototypeNode.methods.existing : null ) ),                        } ),                        events: Object.create( eventCollectionPrototype, {                            existing: enumerable( Object.create( prototypeNode ?                                prototypeNode.events.existing : null ) ),                        } ),                        // Is this node patchable? Nodes are patchable if they were loaded from a                        // component.                        patchable: !! ( nodeURI ||                            parentNode && ! parentNode.initialized && parentNode.patchable ),                        // Has this node completed initialization? For applications, visibility to                        // ancestors from uninitialized nodes is blocked. Change tracking starts                        // after initialization.                        initialized: false,                    };                // } else {                //     return undefined;                // }            },            /// Record that a node has initialized.            initialize: function( nodeID ) {                if ( this.existing[nodeID] ) {                    this.existing[nodeID].initialized = true;                    return true;                }                return false;            },            /// Unregister a node as it is deleted.            delete: function( nodeID ) {                if ( this.existing[nodeID] ) {                    delete this.existing[nodeID];                    delete this.globals[nodeID];                    return true;                }                return false;            },            /// Create a proxy node in the form of the nodes created by `nodes.create` to represent            /// a behavior node in another node's prototype chain. The `existing` objects of the            /// proxy's collections link to the prototype collection's `existing` objects, just as            /// with a regular prototype. The proxy's members delegate to the corresponding members            /// in the behavior.            proxy: function( prototypeNode, behaviorNode ) {                return {                    properties: {                        existing: Object.create(                            prototypeNode ? prototypeNode.properties.existing : null,                            propertyDescriptorsFor( behaviorNode.properties.existing )                        ),                    },                    methods: {                        existing: Object.create(                            prototypeNode ? prototypeNode.methods.existing : null,                            propertyDescriptorsFor( behaviorNode.methods.existing )                        ),                    },                    events: {                        existing: Object.create(                            prototypeNode ? prototypeNode.events.existing : null,                            propertyDescriptorsFor( behaviorNode.events.existing )                        ),                    },                };                /// Return an `Object.create` properties object for a proxy object for the provided                /// collection's `existing` object.                function propertyDescriptorsFor( collectionExisting ) {                    return Object.keys( collectionExisting ).reduce(                        function( propertiesObject, memberName ) {                            propertiesObject[memberName] = {                                get: function() { return collectionExisting[memberName] },                                enumerable: true,                            };                            return propertiesObject;                        },                        {}                    );                }            },            /// Registry of all nodes, indexed by ID. Each is an object created by `nodes.create`.            existing: {                // id: {                //     id: ...,                //     uri: ...,                //     name: ...,                //     ...                // }            },            /// Global root nodes. Each of these is the root of a tree.            ///             /// The `globals` object is a set: the keys are the data, and only existence on the            /// object is significant.            globals: {                // id: undefined,            },        };        /// Control messages from the reflector are stored here in a priority queue, ordered by        /// execution time.        ///         /// @name module:vwf~queue        var queue = this.private.queue = {            /// Insert a message or messages into the queue. Optionally execute the simulation            /// through the time marked on the message.            ///             /// When chronic (chron-ic) is set, vwf#dispatch is called to execute the simulation up            /// through the indicated time. To prevent actions from executing out of order, insert            /// should be the caller's last operation before returning to the host when invoked with            /// chronic.            ///             /// @name module:vwf~queue.insert            ///             /// @param {Object|Object[]} fields            /// @param {Boolean} [chronic]            insert: function( fields, chronic ) {                var messages = fields instanceof Array ? fields : [ fields ];                messages.forEach( function( fields ) {                    // if ( fields.action ) {  // TODO: don't put ticks on the queue but just use them to fast-forward to the current time (requires removing support for passing ticks to the drivers and nodes)                        fields.sequence = ++this.sequence; // track the insertion order for use as a sort key                        this.queue.push( fields );                    // }                    if ( chronic ) {                        this.time = Math.max( this.time, fields.time ); // save the latest allowed time for suspend/resume                    }                }, this );                // Sort by time, then future messages ahead of reflector messages, then by sequence.  // TODO: we probably want a priority queue here for better performance                //                 // The sort by origin ensures that the queue is processed in a well-defined order                // when future messages and reflector messages share the same time, even if the                // reflector message has not arrived at the client yet.                //                 // The sort by sequence number ensures that the messages remain in their arrival                // order when the earlier sort keys don't provide the order.                this.queue.sort( function( a, b ) {                    if ( a.time != b.time ) {                        return a.time - b.time;                    } else if ( a.origin != "reflector" && b.origin == "reflector" ) {                        return -1;                    } else if ( a.origin == "reflector" && b.origin != "reflector" ) {                        return 1;                    } else {                        return a.sequence - b.sequence;                    }                } );                // Execute the simulation through the new time.                // To prevent actions from executing out of order, callers should immediately return                // to the host after invoking insert with chronic set.                if ( chronic ) {                    vwf.dispatch();                }            },            /// Pull the next message from the queue.            ///             /// @name module:vwf~queue.pull            ///             /// @returns {Object|undefined} The next message if available, otherwise undefined.            pull: function() {                if ( this.suspension == 0 && this.queue.length > 0 && this.queue[0].time <= this.time ) {                    return this.queue.shift();                                }            },            /// Update the queue to include only the messages selected by a filtering function.            ///             /// @name module:vwf~queue.filter            ///             /// @param {Function} callback            ///   `filter` calls `callback( fields )` once for each message in the queue. If            ///   `callback` returns a truthy value, the message will be retained. Otherwise it will            ///   be removed from the queue.            filter: function( callback /* fields */ ) {                this.queue = this.queue.filter( callback );            },            /// Suspend message execution.            ///             /// @name module:vwf~queue.suspend            ///             /// @returns {Boolean} true if the queue was suspended by this call.            suspend: function( why ) {                if ( this.suspension++ == 0 ) {                    vwf.logger.infox( "-queue#suspend", "suspending queue at time", vwf.now, why ? why : "" );                    return true;                } else {                    vwf.logger.debugx( "-queue#suspend", "further suspending queue at time", vwf.now, why ? why : "" );                    return false;                }            },            /// Resume message execution.            ///            /// vwf#dispatch may be called to continue the simulation. To prevent actions from            /// executing out of order, resume should be the caller's last operation before            /// returning to the host.            ///             /// @name module:vwf~queue.resume            ///             /// @returns {Boolean} true if the queue was resumed by this call.            resume: function( why ) {                if ( --this.suspension == 0 ) {                    vwf.logger.infox( "-queue#resume", "resuming queue at time", vwf.now, why ? why : "" );                    vwf.dispatch();                    return true;                } else {                    vwf.logger.debugx( "-queue#resume", "partially resuming queue at time", vwf.now, why ? why : "" );                    return false;                }            },            /// Return the ready state of the queue.            ///             /// @name module:vwf~queue.ready            ///             /// @returns {Boolean}            ready: function() {                return this.suspension == 0;            },            /// Current time as provided by the reflector. Messages to be executed at this time or            /// earlier are available from #pull.            ///             /// @name module:vwf~queue.time            time: 0,            /// Suspension count. Queue processing is suspended when suspension is greater than 0.            ///             /// @name module:vwf~queue.suspension            suspension: 0,            /// Sequence counter for tagging messages by order of arrival. Messages are sorted by            /// time, origin, then by arrival order.            ///             /// @name module:vwf~queue.sequence            sequence: 0,            /// Array containing the messages in the queue.            ///             /// @name module:vwf~queue.queue            queue: [],        };    };} ) ( window );
 |