model.js 28 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736
  1. /*
  2. The MIT License (MIT)
  3. Copyright (c) 2014-2020 Nikolai Suslov and the Krestianstvo.org project contributors. (https://github.com/NikolaySuslov/livecodingspace/blob/master/LICENSE.md)
  4. Virtual World Framework Apache 2.0 license (https://github.com/NikolaySuslov/livecodingspace/blob/master/licenses/LICENSE_VWF.md)
  5. */
  6. /// FROM FABRIC.JS
  7. /// vwf/model.js is the common implementation of all Virtual World Framework models. Each model
  8. /// is part of a federation with other models attached to the simulation that implements part of
  9. /// the greater model. Taken together, the models create the entire model system for the
  10. /// simulation.
  11. ///
  12. /// Models are inside of, and directly part of the simulation. They may control the simulation
  13. /// and cause immediate change, but they cannot accept external input. The model configuration is
  14. /// identical for all participants in a shared world.
  15. ///
  16. /// A given model might be responsible for a certain subset of nodes in the the simulation, such
  17. /// as those representing Flash objects. Or it might implement part of the functionality of any
  18. /// node, such as translating 3-D transforms and material properties back and forth to a scene
  19. /// manager. Or it might implement functionality that is only active for a short period, such as
  20. /// importing a document.
  21. /// @module vwf/kernel/model
  22. /// @requires vwf/model
  23. import { Fabric } from '/core/vwf/fabric.js';
  24. class ModelKernel extends Fabric{
  25. constructor(module) {
  26. //console.log("ModelKernel constructor");
  27. super( module, "Model")
  28. }
  29. factory(){
  30. return this.load( this.module, {
  31. // == Module Definition ====================================================================
  32. initialize: function() {
  33. this.state.enabled = true; // kernel reentry allowed?
  34. this.state.blocked = false; // kernel reentry attempted?
  35. },
  36. /// Allow kernel reentry from the drivers.
  37. enable: function() {
  38. this.state.enabled = true;
  39. this.state.blocked = false;
  40. },
  41. /// Prevent kernel reentry from the drivers.
  42. disable: function() {
  43. this.state.enabled = false;
  44. this.state.blocked = false;
  45. },
  46. /// Indicate if kernel reentry is currently enabled.
  47. enabled: function() {
  48. return this.state.enabled;
  49. },
  50. /// Indicate if kernel reentry is currently disabled.
  51. disabled: function() {
  52. return ! this.state.enabled;
  53. },
  54. /// Indicate if a driver attempted to call back into the kernel while reentry was disabled,
  55. /// and clear the *blocked* flag.
  56. blocked: function() {
  57. var blocked = this.state.blocked;
  58. this.state.blocked = false;
  59. return blocked;
  60. },
  61. /// Invoke a task and record any async actions that it initiates. After the actions have
  62. /// completed, execute their callbacks, then call a completion callback.
  63. ///
  64. /// @param {Function} task
  65. /// The task to execute and monitor for async actions. `task` is invoked with no
  66. /// arguments.
  67. /// @param {Function} callback
  68. /// Invoked after the async actions have completed.
  69. /// @param {Object} [that]
  70. /// The `this` value for the `task` and `callback` functions.
  71. capturingAsyncs: function( task, callback, that ) {
  72. // Create an array to capture the callbacks and results from async actions. When
  73. // `this.state.asyncs` exists, async actions hand off their callbacks to `asyncs.defer`.
  74. var asyncs = this.state.asyncs = [];
  75. asyncs.defer = defer;
  76. asyncs.completed = 0;
  77. asyncs.callback = callback;
  78. asyncs.that = that;
  79. // Invoke the task.
  80. task.call( that );
  81. // Detach the array from `this.state.asyncs` to stop capturing async actions.
  82. this.state.asyncs = undefined;
  83. // If there were no async actions, call the completion callback immediately.
  84. if ( asyncs.completed == asyncs.length ) {
  85. asyncs.callback.call( asyncs.that );
  86. }
  87. /// The `this.state.asyncs` array `defer` method.
  88. ///
  89. /// Wrap a callback function with a new function that will defer the original callback
  90. /// until a collection of actions have completed, then call the deferred callbacks
  91. /// followed by a completion callback.
  92. function defer( callback /* result */ ) {
  93. var self = this;
  94. // Save the original callback. The wrapping callback will save the result here when
  95. // received.
  96. var deferred = {
  97. callback: callback /* result */,
  98. result: undefined
  99. };
  100. this.push( deferred );
  101. // Return a new callback in place of the original. Record the result, then if all
  102. // actions have completed, call the original callbacks, then call the completion
  103. // function.
  104. return function( result ) {
  105. deferred.result = result;
  106. if ( ++self.completed == self.length ) {
  107. // Call the original callbacks.
  108. self.forEach( function( deferred ) {
  109. deferred.callback && deferred.callback( deferred.result );
  110. } );
  111. // Call the completion callback.
  112. if ( self.callback ) {
  113. self.callback.call( self.that );
  114. }
  115. }
  116. }
  117. };
  118. },
  119. }, function( modelFunctionName ) {
  120. // == Model API ============================================================================
  121. // The kernel bypasses vwf/kernel/model and calls directly into the first driver stage.
  122. return undefined;
  123. }, function( kernelFunctionName ) {
  124. // == Kernel API ===========================================================================
  125. switch ( kernelFunctionName ) {
  126. // -- Read-write functions -------------------------------------------------------------
  127. // TODO: setState
  128. // TODO: getState
  129. // TODO: hashState
  130. case "createNode":
  131. return function( nodeComponent, nodeAnnotation, baseURI, when, callback /* nodeID */ ) {
  132. // Interpret `createNode( nodeComponent, when, callback )` as
  133. // `createNode( nodeComponent, undefined, undefined, when, callback )` and
  134. // `createNode( nodeComponent, nodeAnnotation, when, callback )` as
  135. // `createNode( nodeComponent, nodeAnnotation, undefined, when, callback )`.
  136. // `nodeAnnotation` was added in 0.6.12, and `baseURI` was added in 0.6.25.
  137. if ( typeof baseURI == "function" || baseURI instanceof Function ) {
  138. callback = baseURI;
  139. when = nodeAnnotation;
  140. baseURI = undefined;
  141. nodeAnnotation = undefined;
  142. } else if ( typeof when == "function" || when instanceof Function ) {
  143. callback = when;
  144. when = baseURI;
  145. baseURI = undefined;
  146. }
  147. // Make the call.
  148. if ( this.state.enabled ) {
  149. if ( when === undefined ) {
  150. if ( this.state.asyncs ) {
  151. callback = this.state.asyncs.defer( callback /* nodeID */ );
  152. }
  153. return this.kernel[kernelFunctionName]( nodeComponent, nodeAnnotation, function( nodeID ) {
  154. callback && callback( nodeID );
  155. } );
  156. } else {
  157. this.kernel.virtualTime.plan( undefined, kernelFunctionName, undefined,
  158. [ nodeComponent, nodeAnnotation ], when, callback /* result */ );
  159. }
  160. } else {
  161. this.state.blocked = true;
  162. }
  163. };
  164. case "deleteNode":
  165. return function( nodeID, when, callback ) {
  166. if ( this.state.enabled ) {
  167. if ( when === undefined ) {
  168. return this.kernel[kernelFunctionName]( nodeID );
  169. } else {
  170. this.kernel.virtualTime.plan( nodeID, kernelFunctionName, undefined,
  171. undefined, when, callback /* result */ );
  172. }
  173. } else {
  174. this.state.blocked = true;
  175. }
  176. };
  177. // TODO: setNode
  178. // TODO: getNode
  179. case "createChild":
  180. return function( nodeID, childName, childComponent, childURI, when, callback /* childID */ ) {
  181. if ( this.state.enabled ) {
  182. if ( when === undefined ) {
  183. if ( this.state.asyncs ) {
  184. callback = this.state.asyncs.defer( callback /* childID */ );
  185. }
  186. return this.kernel[kernelFunctionName]( nodeID, childName, childComponent, childURI, function( childID ) {
  187. callback && callback( childID );
  188. } );
  189. } else {
  190. this.kernel.virtualTime.plan( nodeID, kernelFunctionName, childName,
  191. [ childComponent, childURI ], when, callback /* result */ );
  192. }
  193. } else {
  194. this.state.blocked = true;
  195. }
  196. };
  197. case "deleteChild":
  198. return function( nodeID, childName, when, callback ) {
  199. if ( this.state.enabled ) {
  200. if ( when === undefined ) {
  201. return this.kernel[kernelFunctionName]( nodeID, childName );
  202. } else {
  203. this.kernel.virtualTime.plan( nodeID, kernelFunctionName, childName,
  204. undefined, when, callback /* result */ );
  205. }
  206. } else {
  207. this.state.blocked = true;
  208. }
  209. };
  210. case "addChild":
  211. return function( nodeID, childID, childName, when, callback ) {
  212. if ( this.state.enabled ) {
  213. if ( when === undefined ) {
  214. return this.kernel[kernelFunctionName]( nodeID, childID, childName );
  215. } else {
  216. this.kernel.virtualTime.plan( nodeID, kernelFunctionName, undefined,
  217. [ childID, childName ], when, callback /* result */ ); // TODO: swap childID & childName?
  218. }
  219. } else {
  220. this.state.blocked = true;
  221. }
  222. };
  223. case "removeChild":
  224. return function( nodeID, childID, when, callback ) {
  225. if ( this.state.enabled ) {
  226. if ( when === undefined ) {
  227. return this.kernel[kernelFunctionName]( nodeID, childID );
  228. } else {
  229. this.kernel.virtualTime.plan( nodeID, kernelFunctionName, undefined,
  230. [ childID ], when, callback /* result */ ); // TODO: swap childID & childName?
  231. }
  232. } else {
  233. this.state.blocked = true;
  234. }
  235. };
  236. case "setProperties":
  237. return function( nodeID, properties, when, callback ) {
  238. if ( this.state.enabled ) {
  239. if ( when === undefined ) {
  240. return this.kernel[kernelFunctionName]( nodeID, properties );
  241. } else {
  242. this.kernel.virtualTime.plan( nodeID, kernelFunctionName, undefined,
  243. [ properties ], when, callback /* result */ );
  244. }
  245. } else {
  246. this.state.blocked = true;
  247. }
  248. };
  249. case "getProperties":
  250. return function( nodeID, when, callback ) {
  251. if ( this.state.enabled ) {
  252. if ( when === undefined ) {
  253. return this.kernel[kernelFunctionName]( nodeID );
  254. } else {
  255. this.kernel.virtualTime.plan( nodeID, kernelFunctionName, undefined,
  256. undefined, when, callback /* result */ );
  257. }
  258. } else {
  259. this.state.blocked = true;
  260. }
  261. };
  262. case "createProperty":
  263. return function( nodeID, propertyName, propertyValue, propertyGet, propertySet, when, callback ) {
  264. if ( this.state.enabled ) {
  265. if ( when === undefined ) {
  266. return this.kernel[kernelFunctionName]( nodeID, propertyName, propertyValue, propertyGet, propertySet );
  267. } else {
  268. this.kernel.virtualTime.plan( nodeID, kernelFunctionName, propertyName,
  269. [ propertyValue, propertyGet, propertySet ], when, callback /* result */ ); // TODO: { value: propertyValue, get: propertyGet, set: propertySet } ? -- vwf.receive() needs to parse
  270. }
  271. } else {
  272. this.state.blocked = true;
  273. }
  274. };
  275. // TODO: deleteProperty
  276. case "setProperty":
  277. return function( nodeID, propertyName, propertyValue, when, callback ) {
  278. if ( this.state.enabled ) {
  279. if ( when === undefined ) {
  280. return this.kernel[kernelFunctionName]( nodeID, propertyName, propertyValue );
  281. } else {
  282. this.kernel.virtualTime.plan( nodeID, kernelFunctionName, propertyName,
  283. [ propertyValue ], when, callback /* result */ );
  284. }
  285. } else {
  286. this.state.blocked = true;
  287. }
  288. };
  289. case "getProperty":
  290. return function( nodeID, propertyName, when, callback ) {
  291. if ( this.state.enabled ) {
  292. if ( when === undefined ) {
  293. return this.kernel[kernelFunctionName]( nodeID, propertyName );
  294. } else {
  295. this.kernel.virtualTime.plan( nodeID, kernelFunctionName, propertyName,
  296. undefined, when, callback /* result */ );
  297. }
  298. } else {
  299. this.state.blocked = true;
  300. }
  301. };
  302. case "createMethod":
  303. return function( nodeID, methodName, methodParameters, methodBody, when, callback ) {
  304. if ( this.state.enabled ) {
  305. if ( when === undefined ) {
  306. return this.kernel[kernelFunctionName]( nodeID, methodName, methodParameters, methodBody );
  307. } else {
  308. this.kernel.virtualTime.plan( nodeID, kernelFunctionName, methodName,
  309. [ methodParameters, methodBody ], when, callback /* result */ ); // TODO: { parameters: methodParameters, body: methodBody } ? -- vwf.receive() needs to parse
  310. }
  311. } else {
  312. this.state.blocked = true;
  313. }
  314. };
  315. // TODO: deleteMethod
  316. case "setMethod":
  317. return function( nodeID, methodName, methodHandler, when, callback ) {
  318. if ( this.state.enabled ) {
  319. if ( when === undefined ) {
  320. return this.kernel[kernelFunctionName]( nodeID, methodName, methodHandler );
  321. } else {
  322. this.kernel.virtualTime.plan( nodeID, kernelFunctionName, methodName,
  323. [ methodHandler ], when, callback /* result */ );
  324. }
  325. } else {
  326. this.state.blocked = true;
  327. }
  328. };
  329. case "getMethod":
  330. return function( nodeID, methodName, when, callback ) {
  331. if ( this.state.enabled ) {
  332. if ( when === undefined ) {
  333. return this.kernel[kernelFunctionName]( nodeID, methodName );
  334. } else {
  335. this.kernel.virtualTime.plan( nodeID, kernelFunctionName, methodName,
  336. undefined, when, callback /* result */ );
  337. }
  338. } else {
  339. this.state.blocked = true;
  340. }
  341. };
  342. case "callMethod":
  343. return function( nodeID, methodName, methodParameters, when, callback ) {
  344. if ( this.state.enabled ) {
  345. if ( when === undefined ) {
  346. return this.kernel[kernelFunctionName]( nodeID, methodName, methodParameters );
  347. } else {
  348. this.kernel.virtualTime.plan( nodeID, kernelFunctionName, methodName,
  349. [ methodParameters ], when, callback /* result */ );
  350. }
  351. } else {
  352. this.state.blocked = true;
  353. }
  354. };
  355. case "createEvent":
  356. return function( nodeID, eventName, eventParameters, when, callback ) {
  357. if ( this.state.enabled ) {
  358. if ( when === undefined ) {
  359. return this.kernel[kernelFunctionName]( nodeID, eventName, eventParameters );
  360. } else {
  361. this.kernel.virtualTime.plan( nodeID, kernelFunctionName, eventName,
  362. [ eventParameters ], when, callback /* result */ );
  363. }
  364. } else {
  365. this.state.blocked = true;
  366. }
  367. };
  368. // TODO: deleteEvent
  369. case "addEventListener":
  370. return function( nodeID, eventName, eventHandler, eventContextID, eventPhases, when, callback ) {
  371. if ( this.state.enabled ) {
  372. if ( when === undefined ) {
  373. return this.kernel[kernelFunctionName]( nodeID, eventName, eventHandler, eventContextID, eventPhases );
  374. } else {
  375. this.kernel.virtualTime.plan( nodeID, kernelFunctionName, eventName,
  376. [ eventHandler, eventContextID, eventPhases ], when, callback /* result */ );
  377. }
  378. } else {
  379. this.state.blocked = true;
  380. }
  381. };
  382. case "removeEventListener":
  383. return function( nodeID, eventName, eventListenerID, when, callback ) {
  384. if ( this.state.enabled ) {
  385. if ( when === undefined ) {
  386. return this.kernel[kernelFunctionName]( nodeID, eventName, eventListenerID );
  387. } else {
  388. this.kernel.virtualTime.plan( nodeID, kernelFunctionName, eventName,
  389. [ eventListenerID ], when, callback /* result */ );
  390. }
  391. } else {
  392. this.state.blocked = true;
  393. }
  394. };
  395. case "flushEventListeners":
  396. return function( nodeID, eventName, eventContextID, when, callback ) {
  397. if ( this.state.enabled ) {
  398. if ( when === undefined ) {
  399. return this.kernel[kernelFunctionName]( nodeID, eventName, eventContextID );
  400. } else {
  401. this.kernel.virtualTime.plan( nodeID, kernelFunctionName, eventName,
  402. [ eventContextID ], when, callback /* result */ );
  403. }
  404. } else {
  405. this.state.blocked = true;
  406. }
  407. };
  408. case "fireEvent":
  409. return function( nodeID, eventName, eventParameters, when, callback ) {
  410. if ( this.state.enabled ) {
  411. if ( when === undefined ) {
  412. return this.kernel[kernelFunctionName]( nodeID, eventName, eventParameters );
  413. } else {
  414. this.kernel.virtualTime.plan( nodeID, kernelFunctionName, eventName,
  415. [ eventParameters ], when, callback /* result */ );
  416. }
  417. } else {
  418. this.state.blocked = true;
  419. }
  420. };
  421. case "dispatchEvent":
  422. return function( nodeID, eventName, eventParameters, eventNodeParameters, when, callback ) {
  423. if ( this.state.enabled ) {
  424. if ( when === undefined ) {
  425. return this.kernel[kernelFunctionName]( nodeID, eventName, eventParameters, eventNodeParameters );
  426. } else {
  427. this.kernel.virtualTime.plan( nodeID, kernelFunctionName, eventName,
  428. [ eventParameters, eventNodeParameters ], when, callback /* result */ );
  429. }
  430. } else {
  431. this.state.blocked = true;
  432. }
  433. };
  434. case "execute":
  435. return function( nodeID, scriptText, scriptType, when, callback /* result */ ) {
  436. if ( this.state.enabled ) {
  437. if ( when === undefined ) {
  438. if ( this.state.asyncs ) {
  439. callback = this.state.asyncs.defer( callback /* result */ );
  440. }
  441. return this.kernel[kernelFunctionName]( nodeID, scriptText, scriptType, function( result ) {
  442. callback && callback( result );
  443. } );
  444. } else {
  445. this.kernel.virtualTime.plan( nodeID, kernelFunctionName, undefined,
  446. [ scriptText, scriptType ], when, callback /* result */ ); // TODO: { text: scriptText, type: scriptType } ? -- vwf.receive() needs to parse
  447. }
  448. } else {
  449. this.state.blocked = true;
  450. }
  451. };
  452. case "random":
  453. return function( nodeID, when, callback ) {
  454. if ( this.state.enabled ) {
  455. if ( when === undefined ) {
  456. return this.kernel[kernelFunctionName]( nodeID );
  457. } else {
  458. this.kernel.virtualTime.plan( nodeID, kernelFunctionName, undefined,
  459. undefined, when, callback /* result */ );
  460. }
  461. } else {
  462. this.state.blocked = true;
  463. }
  464. };
  465. case "seed":
  466. return function( nodeID, seed, when, callback ) {
  467. if ( this.state.enabled ) {
  468. if ( when === undefined ) {
  469. return this.kernel[kernelFunctionName]( nodeID, seed );
  470. } else {
  471. this.kernel.virtualTime.plan( nodeID, kernelFunctionName, undefined,
  472. [ seed ], when, callback /* result */ );
  473. }
  474. } else {
  475. this.state.blocked = true;
  476. }
  477. };
  478. // -- Read-only functions --------------------------------------------------------------
  479. case "time":
  480. case "client":
  481. case "moniker":
  482. case "application":
  483. case "intrinsics":
  484. case "uri":
  485. case "name":
  486. case "prototype":
  487. case "prototypes":
  488. case "behaviors":
  489. case "ancestors":
  490. case "parent":
  491. case "children":
  492. case "descendants":
  493. case "find":
  494. case "test":
  495. case "findClients":
  496. return function() {
  497. return this.kernel[kernelFunctionName].apply( this.kernel, arguments );
  498. };
  499. }
  500. } );
  501. }
  502. }
  503. export {
  504. ModelKernel
  505. }