view.js 55 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347
  1. /* jshint esversion: 6 */
  2. /* eslint "indent": [ "error", 4, { "SwitchCase": 1 } ] */
  3. var view = view || {};
  4. var zip = zip || require('./zip');
  5. var gzip = gzip || require('./gzip');
  6. var tar = tar || require('./tar');
  7. var protobuf = protobuf || require('protobufjs');
  8. var prototxt = prototxt || require('protobufjs/ext/prototxt');
  9. var d3 = d3 || require('d3');
  10. var dagre = dagre || require('dagre');
  11. var sidebar = sidebar || require('./view-sidebar');
  12. var grapher = grapher || require('./view-grapher');
  13. view.View = class {
  14. constructor(host) {
  15. this._host = host;
  16. this._model = null;
  17. this._selection = [];
  18. this._sidebar = new sidebar.Sidebar(this._host);
  19. this._host.initialize(this);
  20. this._showAttributes = false;
  21. this._showInitializers = true;
  22. this._showNames = false;
  23. this._searchText = '';
  24. this._modelFactoryService = new view.ModelFactoryService(this._host);
  25. this._host.document.documentElement.style.overflow = 'hidden';
  26. this._host.document.body.scroll = 'no';
  27. this._host.document.getElementById('zoom-in-button').addEventListener('click', () => {
  28. this.zoomIn();
  29. });
  30. this._host.document.getElementById('zoom-out-button').addEventListener('click', () => {
  31. this.zoomOut();
  32. });
  33. this._host.document.getElementById('toolbar').addEventListener('mousewheel', (e) => {
  34. this.preventZoom(e);
  35. });
  36. this._host.document.getElementById('sidebar').addEventListener('mousewheel', (e) => {
  37. this.preventZoom(e);
  38. });
  39. this._host.document.addEventListener('keydown', () => {
  40. this.clearSelection();
  41. });
  42. if (this._host.environment('zoom') == 'scroll') {
  43. this._host.document.getElementById('graph-container').addEventListener('mousewheel', (e) => {
  44. this._mouseWheelHandler(e);
  45. });
  46. this._host.document.getElementById('graph').addEventListener('mousewheel', (e) => {
  47. this._mouseWheelHandler(e);
  48. });
  49. }
  50. }
  51. show(page) {
  52. if (!page) {
  53. page = (!this._model && !this._activeGraph) ? 'Welcome' : 'Graph';
  54. }
  55. this._host.screen(page);
  56. this._sidebar.close();
  57. var welcomeElement = this._host.document.getElementById('welcome');
  58. var openFileButton = this._host.document.getElementById('open-file-button');
  59. var spinnerElement = this._host.document.getElementById('spinner');
  60. var graphElement = this._host.document.getElementById('graph');
  61. var toolbarElement = this._host.document.getElementById('toolbar');
  62. if (page == 'Welcome') {
  63. this._host.document.body.style.cursor = 'default';
  64. welcomeElement.style.display = 'block';
  65. openFileButton.style.display = 'block';
  66. openFileButton.style.opacity = 1;
  67. spinnerElement.style.display = 'none';
  68. graphElement.style.display = 'none';
  69. graphElement.style.opacity = 0;
  70. toolbarElement.style.display = 'none';
  71. }
  72. if (page == 'Spinner') {
  73. this._host.document.body.style.cursor = 'wait';
  74. welcomeElement.style.display = 'block';
  75. spinnerElement.style.display = 'block';
  76. openFileButton.style.display = 'block';
  77. graphElement.style.display = 'block';
  78. graphElement.style.opacity = 0;
  79. toolbarElement.style.display = 'none';
  80. }
  81. if (page == 'Graph') {
  82. welcomeElement.style.display = 'none';
  83. openFileButton.style.display = 'none';
  84. openFileButton.style.opacity = 0;
  85. spinnerElement.style.display = 'none';
  86. graphElement.style.display = 'block';
  87. graphElement.style.opacity = 1;
  88. toolbarElement.style.display = 'block';
  89. this._host.document.body.style.cursor = 'default';
  90. }
  91. }
  92. cut() {
  93. this._host.document.execCommand('cut');
  94. }
  95. copy() {
  96. this._host.document.execCommand('copy');
  97. }
  98. paste() {
  99. this._host.document.execCommand('paste');
  100. }
  101. selectAll() {
  102. this._host.document.execCommand('selectall');
  103. }
  104. find() {
  105. if (this._activeGraph) {
  106. this.clearSelection();
  107. var graphElement = document.getElementById('graph');
  108. var view = new sidebar.FindSidebar(this._host, graphElement, this._activeGraph);
  109. view.on('search-text-changed', (sender, text) => {
  110. this._searchText = text;
  111. });
  112. view.on('select', (sender, selection) => {
  113. this._sidebar.close();
  114. this.select(selection);
  115. });
  116. this._sidebar.open(view.content, 'Find');
  117. view.focus(this._searchText);
  118. }
  119. }
  120. toggleAttributes() {
  121. this._showAttributes = !this._showAttributes;
  122. this._reload();
  123. }
  124. get showAttributes() {
  125. return this._showAttributes;
  126. }
  127. toggleInitializers() {
  128. this._showInitializers = !this._showInitializers;
  129. this._reload();
  130. }
  131. get showInitializers() {
  132. return this._showInitializers;
  133. }
  134. toggleNames() {
  135. this._showNames = !this._showNames;
  136. this._reload();
  137. }
  138. get showNames() {
  139. return this._showNames;
  140. }
  141. _reload() {
  142. this.show('Spinner');
  143. if (this._model && this._activeGraph) {
  144. this._updateGraph(this._model, this._activeGraph).catch((error) => {
  145. if (error) {
  146. this.error('Graph update failed.', error);
  147. }
  148. });
  149. }
  150. }
  151. _timeout(time) {
  152. return new Promise((resolve) => {
  153. setTimeout(() => { resolve(); }, time);
  154. });
  155. }
  156. zoomIn() {
  157. switch (this._host.environment('zoom')) {
  158. case 'scroll':
  159. if (this._zoom) {
  160. this._zoom = this._zoom * 1.05;
  161. if (this._zoom > 2) {
  162. this._zoom = 2;
  163. }
  164. this.applyZoom();
  165. }
  166. break;
  167. case 'd3':
  168. if (this._zoom) {
  169. this._zoom.scaleBy(d3.select(this._host.document.getElementById('graph')), 1.2);
  170. }
  171. break;
  172. }
  173. }
  174. zoomOut() {
  175. switch (this._host.environment('zoom')) {
  176. case 'scroll':
  177. if (this._zoom) {
  178. this._zoom = this._zoom * 0.95;
  179. if (this._zoom < 0.1) {
  180. this._zoom = 0.1;
  181. }
  182. this.applyZoom();
  183. }
  184. break;
  185. case 'd3':
  186. if (this._zoom) {
  187. this._zoom.scaleBy(d3.select(this._host.document.getElementById('graph')), 0.8);
  188. }
  189. break;
  190. }
  191. }
  192. resetZoom() {
  193. switch (this._host.environment('zoom')) {
  194. case 'scroll':
  195. if (this._zoom) {
  196. this._zoom = 1;
  197. this.applyZoom();
  198. }
  199. break;
  200. case 'd3':
  201. if (this._zoom) {
  202. this._zoom.scaleTo(d3.select(this._host.document.getElementById('graph')), 1);
  203. }
  204. break;
  205. }
  206. }
  207. preventZoom(e) {
  208. if (e.shiftKey || e.ctrlKey) {
  209. e.preventDefault();
  210. }
  211. }
  212. applyZoom() {
  213. var svgElement = this._host.document.getElementById('graph');
  214. svgElement.setAttribute('style', 'zoom: ' + this._zoom + ';');
  215. // svgElement.setAttribute('style', 'transform: scale(' + this._zoom + ',' + this._zoom + ')');
  216. // svgElement.setAttribute('width', this._width * this._zoom);
  217. // svgElement.setAttribute('height', this._height * this._zoom);
  218. }
  219. _mouseWheelHandler(e) {
  220. if (e.shiftKey || e.ctrlKey) {
  221. if (this._zoom) {
  222. // var oldWidth = this._width * this._zoom;
  223. // var oldHeight = this._height * this._zoom;
  224. this._zoom = this._zoom + (e.wheelDelta * 1.0 / 6000.0);
  225. if (this._zoom < 0.1) { this._zoom = 0.1; }
  226. if (this._zoom > 2) { this._zoom = 2; }
  227. this.applyZoom();
  228. /* var svgElement = document.getElementById('graph');
  229. va r newWidth = this._width * this._zoom;
  230. var newHeight = this._height * this._zoom;
  231. svgElement.setAttribute('width', newWidth);
  232. svgElement.setAttribute('height', newHeight); */
  233. // var dx = (oldWidth - newWidth) / 2;
  234. // var dy = (oldHeight - newHeight) / 2;
  235. // window.scrollBy(dx, dy);
  236. e.preventDefault();
  237. }
  238. }
  239. }
  240. select(selection) {
  241. this.clearSelection();
  242. if (selection && selection.length > 0) {
  243. var graphElement = this._host.document.getElementById('graph');
  244. var graphRect = graphElement.getBoundingClientRect();
  245. var x = 0;
  246. var y = 0;
  247. for (var element of selection) {
  248. element.classList.add('select');
  249. this._selection.push(element);
  250. var box = element.getBBox();
  251. var ex = box.x + (box.width / 2);
  252. var ey = box.y + (box.height / 2);
  253. var transform = element.transform.baseVal.consolidate();
  254. if (transform) {
  255. ex = transform.matrix.e;
  256. ey = transform.matrix.f;
  257. }
  258. x += ex;
  259. y += ey;
  260. }
  261. x = x / selection.length;
  262. y = y / selection.length;
  263. this._zoom.transform(d3.select(graphElement), d3.zoomIdentity.translate((graphRect.width / 2) - x, (graphRect.height / 2) - y));
  264. }
  265. }
  266. clearSelection() {
  267. while (this._selection.length > 0) {
  268. var element = this._selection.pop();
  269. element.classList.remove('select');
  270. }
  271. }
  272. error(message, err) {
  273. this._sidebar.close();
  274. this._host.exception(err, false);
  275. this._host.error(message, err.toString());
  276. this.show('Welcome');
  277. }
  278. accept(file) {
  279. return this._modelFactoryService.accept(file);
  280. }
  281. open(context) {
  282. this._host.event('Model', 'Open', 'Size', context.buffer.length);
  283. this._sidebar.close();
  284. return this._timeout(2).then(() => {
  285. return this._modelFactoryService.open(context).then((model) => {
  286. var format = model.format;
  287. if (format) {
  288. format = format + (model.producer ? ' (' + model.producer + ')' : '');
  289. this._host.event('Model', 'Format', format);
  290. }
  291. return this._timeout(20).then(() => {
  292. var graph = model.graphs.length > 0 ? model.graphs[0] : null;
  293. return this._updateGraph(model, graph);
  294. });
  295. });
  296. });
  297. }
  298. _updateActiveGraph(name) {
  299. this._sidebar.close();
  300. if (this._model) {
  301. var model = this._model;
  302. var graph = model.graphs.filter(graph => name == graph.name).shift();
  303. if (graph) {
  304. this.show('Spinner');
  305. this._timeout(200).then(() => {
  306. return this._updateGraph(model, graph).catch((error) => {
  307. if (error) {
  308. this.error('Graph update failed.', error);
  309. }
  310. });
  311. });
  312. }
  313. }
  314. }
  315. _updateGraph(model, graph) {
  316. return this._timeout(100).then(() => {
  317. if (graph && graph != this._activeGraph) {
  318. var nodes = graph.nodes;
  319. if (nodes.length > 1400) {
  320. if (!this._host.confirm('Large model detected.', 'This graph contains a large number of nodes and might take a long time to render. Do you want to continue?')) {
  321. this._host.event('Graph', 'Render', 'Skip', nodes.length);
  322. this.show(null);
  323. return null;
  324. }
  325. }
  326. }
  327. return this.renderGraph(graph).then(() => {
  328. this._model = model;
  329. this._activeGraph = graph;
  330. this.show('Graph');
  331. return this._model;
  332. }).catch((error) => {
  333. this.renderGraph(this._activeGraph).then(() => {
  334. this.show('Graph');
  335. throw error;
  336. }).catch(() => {
  337. throw error;
  338. });
  339. });
  340. });
  341. }
  342. renderGraph(graph) {
  343. try {
  344. if (!graph) {
  345. return Promise.resolve();
  346. }
  347. else {
  348. var graphElement = this._host.document.getElementById('graph');
  349. while (graphElement.lastChild) {
  350. graphElement.removeChild(graphElement.lastChild);
  351. }
  352. switch (this._host.environment('zoom')) {
  353. case 'scroll':
  354. this._zoom = 0;
  355. graphElement.style.position = 'static';
  356. graphElement.style.margin = 'auto';
  357. break;
  358. case 'd3':
  359. this._zoom = null;
  360. graphElement.style.position = 'absolute';
  361. graphElement.style.margin = '0';
  362. break;
  363. }
  364. var groups = graph.groups;
  365. var graphOptions = {};
  366. graphOptions.nodesep = 25;
  367. graphOptions.ranksep = 20;
  368. var g = new dagre.graphlib.Graph({ compound: groups });
  369. g.setGraph(graphOptions);
  370. g.setDefaultEdgeLabel(() => { return {}; });
  371. var nodeId = 0;
  372. var edgeMap = {};
  373. var clusterMap = {};
  374. var clusterParentMap = {};
  375. var id = new Date().getTime();
  376. var nodes = graph.nodes;
  377. if (nodes.length > 1500) {
  378. graphOptions.ranker = 'longest-path';
  379. }
  380. this._host.event('Graph', 'Render', 'Size', nodes.length);
  381. var node;
  382. if (groups) {
  383. for (node of nodes) {
  384. if (node.group) {
  385. var path = node.group.split('/');
  386. while (path.length > 0) {
  387. var name = path.join('/');
  388. path.pop();
  389. clusterParentMap[name] = path.join('/');
  390. }
  391. }
  392. }
  393. }
  394. var input;
  395. var output;
  396. var argument;
  397. var tuple;
  398. var self = this;
  399. for (node of nodes) {
  400. var element = new grapher.NodeElement(this._host.document);
  401. var addNode = function(element, node, edges) {
  402. var header = element.block('header');
  403. var styles = [ 'node-item-operator' ];
  404. var category = node.category;
  405. if (category) {
  406. styles.push('node-item-operator-' + category.toLowerCase());
  407. }
  408. var content = self.showNames && node.name ? node.name : node.operator;
  409. var tooltip = self.showNames && node.name ? node.operator : node.name;
  410. header.add(null, styles, content, tooltip, () => {
  411. self.showNodeProperties(node, null);
  412. });
  413. if (node.function) {
  414. header.add(null, [ 'node-item-function' ], '+', null, () => {
  415. // debugger;
  416. });
  417. }
  418. var initializers = [];
  419. var hiddenInitializers = false;
  420. if (self._showInitializers) {
  421. for (var input of node.inputs) {
  422. if (input.visible && input.arguments.length == 1 && input.arguments[0].initializer != null) {
  423. initializers.push(input);
  424. }
  425. if ((!input.visible || input.arguments.length > 1) &&
  426. input.arguments.some((argument) => argument.initializer != null)) {
  427. hiddenInitializers = true;
  428. }
  429. }
  430. }
  431. var attributes = [];
  432. if (self.showAttributes && node.attributes) {
  433. attributes = node.attributes.filter((attribute) => attribute.visible);
  434. }
  435. if (initializers.length > 0 || hiddenInitializers || attributes.length > 0) {
  436. var block = element.block('list');
  437. block.handler = () => {
  438. self.showNodeProperties(node);
  439. };
  440. for (var initializer of initializers) {
  441. var argument = initializer.arguments[0];
  442. var type = argument.type;
  443. var shape = '';
  444. var separator = '';
  445. if (type &&
  446. type.shape &&
  447. type.shape.dimensions &&
  448. Object.prototype.hasOwnProperty.call(type.shape.dimensions, 'length')) {
  449. shape = '\u3008' + type.shape.dimensions.map((d) => d ? d : '?').join('\u00D7') + '\u3009';
  450. if (type.shape.dimensions.length == 0 && argument.initializer && !argument.initializer.state) {
  451. shape = argument.initializer.toString();
  452. if (shape && shape.length > 10) {
  453. shape = shape.substring(0, 10) + '\u2026';
  454. }
  455. separator = ' = ';
  456. }
  457. }
  458. block.add('initializer-' + argument.id, initializer.name, shape, type ? type.toString() : '', separator);
  459. }
  460. if (hiddenInitializers) {
  461. block.add(null, '\u3008' + '\u2026' + '\u3009', '', null, '');
  462. }
  463. for (var attribute of attributes) {
  464. if (attribute.visible) {
  465. var attributeValue = sidebar.NodeSidebar.formatAttributeValue(attribute.value, attribute.type);
  466. if (attributeValue && attributeValue.length > 25) {
  467. attributeValue = attributeValue.substring(0, 25) + '\u2026';
  468. }
  469. block.add(null, attribute.name, attributeValue, attribute.type, ' = ');
  470. }
  471. }
  472. }
  473. if (edges) {
  474. var inputs = node.inputs;
  475. for (input of inputs) {
  476. for (argument of input.arguments) {
  477. if (argument.id != '' && !argument.initializer) {
  478. var tuple = edgeMap[argument.id];
  479. if (!tuple) {
  480. tuple = { from: null, to: [] };
  481. edgeMap[argument.id] = tuple;
  482. }
  483. tuple.to.push({
  484. node: nodeId,
  485. name: input.name
  486. });
  487. }
  488. }
  489. }
  490. var outputs = node.outputs;
  491. if (node.chain && node.chain.length > 0) {
  492. var chainOutputs = node.chain[node.chain.length - 1].outputs;
  493. if (chainOutputs.length > 0) {
  494. outputs = chainOutputs;
  495. }
  496. }
  497. for (output of outputs) {
  498. for (argument of output.arguments) {
  499. if (argument.id != '') {
  500. tuple = edgeMap[argument.id];
  501. if (!tuple) {
  502. tuple = { from: null, to: [] };
  503. edgeMap[argument.id] = tuple;
  504. }
  505. tuple.from = {
  506. node: nodeId,
  507. name: output.name,
  508. type: argument.type
  509. };
  510. }
  511. }
  512. }
  513. }
  514. if (node.chain && node.chain.length > 0) {
  515. for (var innerNode of node.chain) {
  516. addNode(element, innerNode, false);
  517. }
  518. }
  519. if (node.inner) {
  520. addNode(element, node.inner, false);
  521. }
  522. }
  523. addNode(element, node, true);
  524. if (node.controlDependencies && node.controlDependencies.length > 0) {
  525. for (var controlDependency of node.controlDependencies) {
  526. tuple = edgeMap[controlDependency];
  527. if (!tuple) {
  528. tuple = { from: null, to: [] };
  529. edgeMap[controlDependency] = tuple;
  530. }
  531. tuple.to.push({
  532. node: nodeId,
  533. name: controlDependency,
  534. controlDependency: true
  535. });
  536. }
  537. }
  538. var nodeName = node.name;
  539. if (nodeName) {
  540. g.setNode(nodeId, { label: element.format(graphElement), id: 'node-' + nodeName });
  541. }
  542. else {
  543. g.setNode(nodeId, { label: element.format(graphElement), id: 'node-' + id.toString() });
  544. id++;
  545. }
  546. var createCluster = function(name) {
  547. if (!clusterMap[name]) {
  548. g.setNode(name, { rx: 5, ry: 5});
  549. clusterMap[name] = true;
  550. var parent = clusterParentMap[name];
  551. if (parent) {
  552. createCluster(parent);
  553. g.setParent(name, parent);
  554. }
  555. }
  556. }
  557. if (groups) {
  558. var groupName = node.group;
  559. if (groupName && groupName.length > 0) {
  560. if (!Object.prototype.hasOwnProperty.call(clusterParentMap, groupName)) {
  561. var lastIndex = groupName.lastIndexOf('/');
  562. if (lastIndex != -1) {
  563. groupName = groupName.substring(0, lastIndex);
  564. if (!Object.prototype.hasOwnProperty.call(clusterParentMap, groupName)) {
  565. groupName = null;
  566. }
  567. }
  568. else {
  569. groupName = null;
  570. }
  571. }
  572. if (groupName) {
  573. createCluster(groupName);
  574. g.setParent(nodeId, groupName);
  575. }
  576. }
  577. }
  578. nodeId++;
  579. }
  580. for (input of graph.inputs) {
  581. for (argument of input.arguments) {
  582. tuple = edgeMap[argument.id];
  583. if (!tuple) {
  584. tuple = { from: null, to: [] };
  585. edgeMap[argument.id] = tuple;
  586. }
  587. tuple.from = {
  588. node: nodeId,
  589. type: argument.type
  590. };
  591. }
  592. var types = input.arguments.map((argument) => argument.type || '').join('\n');
  593. var inputName = input.name || '';
  594. if (inputName.length > 16) {
  595. inputName = inputName.split('/').pop();
  596. }
  597. var inputElement = new grapher.NodeElement(this._host.document);
  598. var inputHeader = inputElement.block('header');
  599. inputHeader.add(null, [ 'graph-item-input' ], inputName, types, () => {
  600. this.showModelProperties();
  601. });
  602. g.setNode(nodeId++, { label: inputElement.format(graphElement), class: 'graph-input' } );
  603. }
  604. for (output of graph.outputs) {
  605. for (argument of output.arguments) {
  606. tuple = edgeMap[argument.id];
  607. if (!tuple) {
  608. tuple = { from: null, to: [] };
  609. edgeMap[argument.id] = tuple;
  610. }
  611. tuple.to.push({ node: nodeId });
  612. }
  613. var outputTypes = output.arguments.map((argument) => argument.type || '').join('\n');
  614. var outputName = output.name || '';
  615. if (outputName.length > 16) {
  616. outputName = outputName.split('/').pop();
  617. }
  618. var outputElement = new grapher.NodeElement(this._host.document);
  619. var outputHeader = outputElement.block('header');
  620. outputHeader.add(null, [ 'graph-item-output' ], outputName, outputTypes, () => {
  621. this.showModelProperties();
  622. });
  623. g.setNode(nodeId++, { label: outputElement.format(graphElement) } );
  624. }
  625. for (var edge of Object.keys(edgeMap)) {
  626. tuple = edgeMap[edge];
  627. if (tuple.from != null) {
  628. for (var to of tuple.to) {
  629. var text = '';
  630. var type = tuple.from.type;
  631. if (type && type.shape && type.shape.dimensions && type.shape.dimensions.length > 0) {
  632. text = type.shape.dimensions.join('\u00D7');
  633. }
  634. if (this._showNames) {
  635. text = edge.split('\n').shift(); // custom argument id
  636. }
  637. if (to.controlDependency) {
  638. g.setEdge(tuple.from.node, to.node, { label: text, id: 'edge-' + edge, arrowhead: 'vee', class: 'edge-path-control-dependency' } );
  639. }
  640. else {
  641. g.setEdge(tuple.from.node, to.node, { label: text, id: 'edge-' + edge, arrowhead: 'vee' } );
  642. }
  643. }
  644. }
  645. }
  646. // Workaround for Safari background drag/zoom issue:
  647. // https://stackoverflow.com/questions/40887193/d3-js-zoom-is-not-working-with-mousewheel-in-safari
  648. var backgroundElement = this._host.document.createElementNS('http://www.w3.org/2000/svg', 'rect');
  649. backgroundElement.setAttribute('id', 'background');
  650. if (this._host.environment('zoom') == 'd3') {
  651. backgroundElement.setAttribute('width', '100%');
  652. backgroundElement.setAttribute('height', '100%');
  653. }
  654. backgroundElement.setAttribute('fill', 'none');
  655. backgroundElement.setAttribute('pointer-events', 'all');
  656. graphElement.appendChild(backgroundElement);
  657. var originElement = this._host.document.createElementNS('http://www.w3.org/2000/svg', 'g');
  658. originElement.setAttribute('id', 'origin');
  659. graphElement.appendChild(originElement);
  660. if (this._host.environment('zoom') == 'd3') {
  661. // Set up zoom support
  662. var svg = d3.select(graphElement);
  663. this._zoom = d3.zoom();
  664. this._zoom(svg);
  665. this._zoom.scaleExtent([0.1, 2]);
  666. this._zoom.on('zoom', () => {
  667. originElement.setAttribute('transform', d3.event.transform.toString());
  668. });
  669. this._zoom.transform(svg, d3.zoomIdentity);
  670. }
  671. return this._timeout(20).then(() => {
  672. var graphRenderer = new grapher.Renderer(this._host.document, originElement);
  673. graphRenderer.render(g);
  674. var inputElements = graphElement.getElementsByClassName('graph-input');
  675. switch (this._host.environment('zoom')) {
  676. case 'scroll':
  677. var size = graphElement.getBBox();
  678. var graphMin = Math.min(size.width, size.height);
  679. var windowMin = Math.min(window.innerWidth, window.innerHeight);
  680. var delta = (Math.max(graphMin, windowMin) / 2.0) * 0.2;
  681. var width = Math.ceil(delta + size.width + delta);
  682. var height = Math.ceil(delta + size.height + delta);
  683. originElement.setAttribute('transform', 'translate(' + delta.toString() + ', ' + delta.toString() + ') scale(1)');
  684. backgroundElement.setAttribute('width', width);
  685. backgroundElement.setAttribute('height', height);
  686. this._width = width;
  687. this._height = height;
  688. this._zoom = 1;
  689. graphElement.setAttribute('viewBox', '0 0 ' + width + ' ' + height);
  690. graphElement.setAttribute('width', width / this._zoom);
  691. graphElement.setAttribute('height', height / this._zoom);
  692. if (inputElements && inputElements.length > 0) {
  693. // Center view based on input elements
  694. for (var j = 0; j < inputElements.length; j++) {
  695. inputElements[j].scrollIntoView({ behavior: 'instant' });
  696. break;
  697. }
  698. }
  699. else {
  700. // this._zoom.transform(svg, d3.zoomIdentity.translate((svgSize.width - g.graph().width) / 2, (svgSize.height - g.graph().height) / 2));
  701. }
  702. break;
  703. case 'd3':
  704. var svgSize = graphElement.getBoundingClientRect();
  705. if (inputElements && inputElements.length > 0) {
  706. // Center view based on input elements
  707. var xs = [];
  708. var ys = [];
  709. for (var i = 0; i < inputElements.length; i++) {
  710. var inputTransform = inputElements[i].transform.baseVal.consolidate().matrix;
  711. xs.push(inputTransform.e);
  712. ys.push(inputTransform.f);
  713. }
  714. var x = xs[0];
  715. var y = ys[0];
  716. if (ys.every(y => y == ys[0])) {
  717. x = xs.reduce((a,b) => { return a + b; }) / xs.length;
  718. }
  719. this._zoom.transform(svg, d3.zoomIdentity.translate((svgSize.width / 2) - x, (svgSize.height / 4) - y));
  720. }
  721. else {
  722. this._zoom.transform(svg, d3.zoomIdentity.translate((svgSize.width - g.graph().width) / 2, (svgSize.height - g.graph().height) / 2));
  723. }
  724. break;
  725. }
  726. return;
  727. });
  728. }
  729. }
  730. catch (error) {
  731. return Promise.reject(error);
  732. }
  733. }
  734. applyStyleSheet(element, name) {
  735. var rules = [];
  736. for (var i = 0; i < this._host.document.styleSheets.length; i++) {
  737. var styleSheet = this._host.document.styleSheets[i];
  738. if (styleSheet && styleSheet.href && styleSheet.href.endsWith('/' + name)) {
  739. rules = styleSheet.cssRules;
  740. break;
  741. }
  742. }
  743. var nodes = element.getElementsByTagName('*');
  744. for (var j = 0; j < nodes.length; j++) {
  745. var node = nodes[j];
  746. for (var k = 0; k < rules.length; k++) {
  747. var rule = rules[k];
  748. if (node.matches(rule.selectorText)) {
  749. for (var l = 0; l < rule.style.length; l++) {
  750. var item = rule.style.item(l);
  751. node.style[item] = rule.style[item];
  752. }
  753. }
  754. }
  755. }
  756. }
  757. export(file) {
  758. var extension = '';
  759. var lastIndex = file.lastIndexOf('.');
  760. if (lastIndex != -1) {
  761. extension = file.substring(lastIndex + 1);
  762. }
  763. if (this._activeGraph && (extension == 'png' || extension == 'svg')) {
  764. var graphElement = this._host.document.getElementById('graph');
  765. var exportElement = graphElement.cloneNode(true);
  766. this.applyStyleSheet(exportElement, 'view-grapher.css');
  767. exportElement.setAttribute('id', 'export');
  768. exportElement.removeAttribute('width');
  769. exportElement.removeAttribute('height');
  770. exportElement.style.removeProperty('opacity');
  771. exportElement.style.removeProperty('display');
  772. var backgroundElement = exportElement.querySelector('#background');
  773. var originElement = exportElement.querySelector('#origin');
  774. originElement.setAttribute('transform', 'translate(0,0) scale(1)');
  775. backgroundElement.removeAttribute('width');
  776. backgroundElement.removeAttribute('height');
  777. var parentElement = graphElement.parentElement;
  778. parentElement.insertBefore(exportElement, graphElement);
  779. var size = exportElement.getBBox();
  780. parentElement.removeChild(exportElement);
  781. parentElement.removeChild(graphElement);
  782. parentElement.appendChild(graphElement);
  783. var delta = (Math.min(size.width, size.height) / 2.0) * 0.1;
  784. var width = Math.ceil(delta + size.width + delta);
  785. var height = Math.ceil(delta + size.height + delta);
  786. originElement.setAttribute('transform', 'translate(' + delta.toString() + ', ' + delta.toString() + ') scale(1)');
  787. exportElement.setAttribute('width', width);
  788. exportElement.setAttribute('height', height);
  789. backgroundElement.setAttribute('width', width);
  790. backgroundElement.setAttribute('height', height);
  791. backgroundElement.setAttribute('fill', '#fff');
  792. var data = new XMLSerializer().serializeToString(exportElement);
  793. if (extension == 'svg') {
  794. var blob = new Blob([ data ], { type: 'image/svg' });
  795. this._host.export(file, blob);
  796. }
  797. if (extension == 'png') {
  798. var imageElement = new Image();
  799. imageElement.onload = () => {
  800. var max = Math.max(width, height);
  801. var scale = ((max * 2.0) > 24000) ? (24000.0 / max) : 2.0;
  802. var canvas = this._host.document.createElement('canvas');
  803. canvas.width = Math.ceil(width * scale);
  804. canvas.height = Math.ceil(height * scale);
  805. var context = canvas.getContext('2d');
  806. context.scale(scale, scale);
  807. context.drawImage(imageElement, 0, 0);
  808. this._host.document.body.removeChild(imageElement);
  809. canvas.toBlob((blob) => {
  810. if (blob) {
  811. this._host.export(file, blob);
  812. }
  813. else {
  814. var err = new Error();
  815. err.name = 'Error exporting image.';
  816. err.message = 'Image may be too large to render as PNG.';
  817. this._host.exception(err, false);
  818. this._host.error(err.name, err.message);
  819. }
  820. }, 'image/png');
  821. };
  822. imageElement.src = 'data:image/svg+xml;base64,' + window.btoa(unescape(encodeURIComponent(data)));
  823. this._host.document.body.insertBefore(imageElement, this._host.document.body.firstChild);
  824. }
  825. }
  826. }
  827. showModelProperties() {
  828. if (this._model) {
  829. var modelSidebar = new sidebar.ModelSidebar(this._host, this._model, this._activeGraph);
  830. modelSidebar.on('update-active-graph', (sender, name) => {
  831. this._updateActiveGraph(name);
  832. });
  833. this._sidebar.open(modelSidebar.render(), 'Model Properties');
  834. }
  835. }
  836. showNodeProperties(node, input) {
  837. if (node) {
  838. var nodeSidebar = new sidebar.NodeSidebar(this._host, node);
  839. nodeSidebar.on('show-documentation', (/* sender, e */) => {
  840. this.showOperatorDocumentation(node);
  841. });
  842. nodeSidebar.on('export-tensor', (sender, tensor) => {
  843. this._host.require('./numpy').then((numpy) => {
  844. var defaultPath = tensor.name ? tensor.name.split('/').join('_').split(':').join('_').split('.').join('_') : 'tensor';
  845. this._host.save('NumPy Array', 'npy', defaultPath, (file) => {
  846. try {
  847. var array = new numpy.Array(tensor.value, tensor.type.dataType, tensor.type.shape.dimensions);
  848. var blob = new Blob([ array.toBuffer() ], { type: 'application/octet-stream' });
  849. this._host.export(file, blob);
  850. }
  851. catch (error) {
  852. this.error('Error saving NumPy tensor.', error);
  853. }
  854. });
  855. }).catch(() => {
  856. });
  857. });
  858. if (input) {
  859. nodeSidebar.toggleInput(input.name);
  860. }
  861. this._sidebar.open(nodeSidebar.render(), 'Node Properties');
  862. }
  863. }
  864. showOperatorDocumentation(node) {
  865. var documentation = node.documentation;
  866. if (documentation) {
  867. var documentationSidebar = new sidebar.OperatorDocumentationSidebar(documentation);
  868. documentationSidebar.on('navigate', (sender, e) => {
  869. this._host.openURL(e.link);
  870. });
  871. this._sidebar.push(documentationSidebar.render(), 'Documentation');
  872. }
  873. }
  874. };
  875. class ModelError extends Error {
  876. constructor(message) {
  877. super(message);
  878. this.name = 'Error loading model.';
  879. }
  880. }
  881. class ModelContext {
  882. constructor(context) {
  883. this._context = context;
  884. this._tags = new Map();
  885. }
  886. request(file, encoding) {
  887. return this._context.request(file, encoding);
  888. }
  889. get identifier() {
  890. return this._context.identifier;
  891. }
  892. get buffer() {
  893. return this._context.buffer;
  894. }
  895. get text() {
  896. if (!this._text) {
  897. this._text = new TextDecoder('utf-8').decode(this.buffer);
  898. }
  899. return this._text;
  900. }
  901. tags(extension) {
  902. var tags = this._tags.get(extension);
  903. if (!tags) {
  904. tags = new Map();
  905. try {
  906. var reader = null;
  907. switch (extension) {
  908. case 'pbtxt':
  909. var b = this.buffer;
  910. var length = b.length;
  911. var signature =
  912. (length >= 3 && b[0] === 0xef && b[1] === 0xbb && b[2] === 0xbf) ||
  913. (length >= 4 && b[0] === 0x00 && b[1] === 0x00 && b[2] === 0xfe && b[3] === 0xff) ||
  914. (length >= 4 && b[0] === 0xff && b[1] === 0xfe && b[2] === 0x00 && b[3] === 0x00) ||
  915. (length >= 4 && b[0] === 0x84 && b[1] === 0x31 && b[2] === 0x95 && b[3] === 0x33) ||
  916. (length >= 2 && b[0] === 0xfe && b[1] === 0xff) ||
  917. (length >= 2 && b[0] === 0xff && b[1] === 0xfe);
  918. if (!signature && b.subarray(0, Math.min(1024, length)).some((c) => c < 7 || (c > 14 && c < 32))) {
  919. break;
  920. }
  921. reader = prototxt.TextReader.create(this.text);
  922. reader.start(false);
  923. while (!reader.end(false)) {
  924. var tag = reader.tag();
  925. tags.set(tag, true);
  926. reader.skip();
  927. }
  928. break;
  929. case 'pb':
  930. reader = new protobuf.Reader.create(this.buffer);
  931. while (reader.pos < reader.len) {
  932. var tagType = reader.uint32();
  933. tags.set(tagType >>> 3, tagType & 7);
  934. try {
  935. reader.skipType(tagType & 7);
  936. }
  937. catch (err) {
  938. tags = new Map();
  939. break;
  940. }
  941. }
  942. break;
  943. }
  944. }
  945. catch (error) {
  946. tags = new Map();
  947. }
  948. this._tags.set(extension, tags);
  949. }
  950. return tags;
  951. }
  952. }
  953. class ArchiveContext {
  954. constructor(entries, rootFolder, identifier, buffer) {
  955. this._entries = {};
  956. if (entries) {
  957. for (var entry of entries) {
  958. if (entry.name.startsWith(rootFolder)) {
  959. var name = entry.name.substring(rootFolder.length);
  960. if (identifier.length > 0 && identifier.indexOf('/') < 0) {
  961. this._entries[name] = entry;
  962. }
  963. }
  964. }
  965. }
  966. this._identifier = identifier.substring(rootFolder.length);
  967. this._buffer = buffer;
  968. }
  969. request(file, encoding) {
  970. var entry = this._entries[file];
  971. if (!entry) {
  972. return Promise.reject(new Error('File not found.'));
  973. }
  974. var data = entry.data;
  975. if (encoding != null) {
  976. data = new TextDecoder(encoding).decode(data);
  977. }
  978. return Promise.resolve(data);
  979. }
  980. get identifier() {
  981. return this._identifier;
  982. }
  983. get buffer() {
  984. return this._buffer;
  985. }
  986. }
  987. class ArchiveError extends Error {
  988. constructor(message) {
  989. super(message);
  990. this.name = 'Error loading archive.';
  991. }
  992. }
  993. view.ModelFactoryService = class {
  994. constructor(host) {
  995. this._host = host;
  996. this._extensions = [];
  997. this.register('./onnx', [ '.onnx', '.pb', '.pbtxt', '.prototxt' ]);
  998. this.register('./mxnet', [ '.mar', '.model', '.json', '.params' ]);
  999. this.register('./keras', [ '.h5', '.hd5', '.hdf5', '.keras', '.json', '.model' ]);
  1000. this.register('./coreml', [ '.mlmodel' ]);
  1001. this.register('./caffe', [ '.caffemodel', '.pbtxt', '.prototxt', '.pt' ]);
  1002. this.register('./caffe2', [ '.pb', '.pbtxt', '.prototxt' ]);
  1003. this.register('./pytorch', [ '.pt', '.pth', '.pkl', '.h5', '.t7', '.model', '.dms', '.pth.tar', '.ckpt', '.bin' ]);
  1004. this.register('./torch', [ '.t7' ]);
  1005. this.register('./torchscript', [ '.pt', '.pth' ]);
  1006. this.register('./tflite', [ '.tflite', '.lite', '.tfl', '.bin' ]);
  1007. this.register('./tf', [ '.pb', '.meta', '.pbtxt', '.prototxt', '.json' ]);
  1008. this.register('./sklearn', [ '.pkl', '.joblib', '.model' ]);
  1009. this.register('./cntk', [ '.model', '.cntk', '.cmf', '.dnn' ]);
  1010. this.register('./openvino', [ '.xml' ]);
  1011. this.register('./darknet', [ '.cfg' ]);
  1012. this.register('./paddle', [ '.paddle', '__model__' ]);
  1013. this.register('./ncnn', [ '.param', '.bin', '.cfg.ncnn', '.weights.ncnn' ]);
  1014. this.register('./dl4j', [ 'configuration.json' ]);
  1015. }
  1016. register(id, extensions) {
  1017. for (var extension of extensions) {
  1018. this._extensions.push({ extension: extension, id: id });
  1019. }
  1020. }
  1021. open(context) {
  1022. return this._openArchive(context).then((context) => {
  1023. context = new ModelContext(context);
  1024. var extension = context.identifier.split('.').pop().toLowerCase();
  1025. var modules = this._filter(context);
  1026. if (modules.length == 0) {
  1027. throw new ModelError("Unsupported file extension '." + extension + "'.");
  1028. }
  1029. var errors = [];
  1030. var match = false;
  1031. var nextModule = () => {
  1032. if (modules.length > 0) {
  1033. var id = modules.shift();
  1034. return this._host.require(id).then((module) => {
  1035. if (!module.ModelFactory) {
  1036. throw new ModelError("Failed to load module '" + id + "'.");
  1037. }
  1038. var modelFactory = new module.ModelFactory();
  1039. if (!modelFactory.match(context)) {
  1040. return nextModule();
  1041. }
  1042. match++;
  1043. return modelFactory.open(context, this._host).then((model) => {
  1044. return model;
  1045. }).catch((error) => {
  1046. errors.push(error);
  1047. return nextModule();
  1048. });
  1049. });
  1050. }
  1051. else {
  1052. if (match) {
  1053. if (errors.length == 1) {
  1054. throw errors[0];
  1055. }
  1056. throw new ModelError(errors.map((err) => err.message).join('\n'));
  1057. }
  1058. throw new ModelError("Unsupported file content for extension '." + extension + "' in '" + context.identifier + "'.");
  1059. }
  1060. };
  1061. return nextModule();
  1062. });
  1063. }
  1064. _openArchive(context) {
  1065. var extension;
  1066. var archive;
  1067. var entry;
  1068. var message;
  1069. var identifier = context.identifier;
  1070. var buffer = context.buffer;
  1071. try {
  1072. extension = identifier.split('.').pop().toLowerCase();
  1073. if (extension == 'gz' || extension == 'tgz') {
  1074. archive = new gzip.Archive(buffer);
  1075. if (archive.entries.length == 1) {
  1076. entry = archive.entries[0];
  1077. if (entry.name) {
  1078. identifier = entry.name;
  1079. }
  1080. else {
  1081. identifier = identifier.substring(0, identifier.lastIndexOf('.'));
  1082. if (extension == 'tgz') {
  1083. identifier += '.tar';
  1084. }
  1085. }
  1086. buffer = entry.data;
  1087. archive = null;
  1088. }
  1089. }
  1090. }
  1091. catch (error) {
  1092. message = error && error.message ? error.message : error.toString();
  1093. message = message.endsWith('.') ? message.substring(0, message.length - 1) : message;
  1094. return Promise.reject(new ArchiveError(message + " in '" + identifier + "'."));
  1095. }
  1096. try {
  1097. extension = identifier.split('.').pop().toLowerCase();
  1098. switch (extension) {
  1099. case 'tar':
  1100. // handle .pth.tar
  1101. var torch = [ 0x8a, 0x0a, 0x6c, 0xfc, 0x9c, 0x46, 0xf9, 0x20, 0x6a, 0xa8, 0x50, 0x19 ];
  1102. if (!buffer || buffer.length < 14 || buffer[0] != 0x80 || !torch.every((v, i) => v == buffer[i + 2])) {
  1103. archive = new tar.Archive(buffer);
  1104. }
  1105. break;
  1106. case 'zip':
  1107. archive = new zip.Archive(buffer);
  1108. break;
  1109. }
  1110. }
  1111. catch (error) {
  1112. message = error && error.message ? error.message : error.toString();
  1113. message = message.endsWith('.') ? message.substring(0, message.length - 1) : message;
  1114. return Promise.reject(new ArchiveError(message + " in '" + identifier + "'."));
  1115. }
  1116. if (!archive) {
  1117. return Promise.resolve(context);
  1118. }
  1119. try {
  1120. var folders = {};
  1121. for (entry of archive.entries) {
  1122. if (entry.name.indexOf('/') != -1) {
  1123. folders[entry.name.split('/').shift() + '/'] = true;
  1124. }
  1125. else {
  1126. folders['/'] = true;
  1127. }
  1128. }
  1129. if (extension == 'tar') {
  1130. delete folders['PaxHeader/'];
  1131. }
  1132. var rootFolder = Object.keys(folders).length == 1 ? Object.keys(folders)[0] : '';
  1133. rootFolder = rootFolder == '/' ? '' : rootFolder;
  1134. var matches = [];
  1135. var entries = archive.entries.slice();
  1136. var nextEntry = () => {
  1137. if (entries.length > 0) {
  1138. var entry = entries.shift();
  1139. if (entry.name.startsWith(rootFolder)) {
  1140. var identifier = entry.name.substring(rootFolder.length);
  1141. if (identifier.length > 0 && identifier.indexOf('/') < 0 && !identifier.startsWith('.')) {
  1142. var context = new ModelContext(new ArchiveContext(null, rootFolder, entry.name, entry.data));
  1143. var modules = this._filter(context);
  1144. var nextModule = () => {
  1145. if (modules.length > 0) {
  1146. var id = modules.shift();
  1147. return this._host.require(id).then((module) => {
  1148. if (!module.ModelFactory) {
  1149. throw new ArchiveError("Failed to load module '" + id + "'.", null);
  1150. }
  1151. var factory = new module.ModelFactory();
  1152. if (factory.match(context)) {
  1153. matches.push(entry);
  1154. modules = [];
  1155. }
  1156. return nextModule();
  1157. });
  1158. }
  1159. else {
  1160. return nextEntry();
  1161. }
  1162. };
  1163. return nextModule();
  1164. }
  1165. }
  1166. return nextEntry();
  1167. }
  1168. else {
  1169. if (matches.length == 0) {
  1170. return Promise.reject(new ArchiveError('Archive does not contain model file.'));
  1171. }
  1172. else if (matches.length > 1) {
  1173. if (matches.length == 2 &&
  1174. matches.some((e) => e.name.endsWith('.params')) &&
  1175. matches.some((e) => e.name.endsWith('-symbol.json'))) {
  1176. matches = matches.filter((e) => e.name.endsWith('.params'));
  1177. }
  1178. else {
  1179. return Promise.reject(new ArchiveError('Archive contains multiple model files.'));
  1180. }
  1181. }
  1182. var match = matches[0];
  1183. return Promise.resolve(new ModelContext(new ArchiveContext(archive.entries, rootFolder, match.name, match.data)));
  1184. }
  1185. };
  1186. return nextEntry();
  1187. }
  1188. catch (error) {
  1189. return Promise.reject(new ArchiveError(error.message));
  1190. }
  1191. }
  1192. accept(identifier) {
  1193. identifier = identifier.toLowerCase();
  1194. for (var extension of this._extensions) {
  1195. if (identifier.endsWith(extension.extension)) {
  1196. return true;
  1197. }
  1198. }
  1199. if (identifier.endsWith('.zip') ||
  1200. identifier.endsWith('.tar') ||
  1201. identifier.endsWith('.tar.gz') ||
  1202. identifier.endsWith('.tgz')) {
  1203. return true;
  1204. }
  1205. return false;
  1206. }
  1207. _filter(context) {
  1208. var moduleList = [];
  1209. var moduleMap = {};
  1210. var identifier = context.identifier.toLowerCase();
  1211. for (var extension of this._extensions) {
  1212. if (identifier.endsWith(extension.extension)) {
  1213. if (!moduleMap[extension.id]) {
  1214. moduleList.push(extension.id);
  1215. moduleMap[extension.id] = true;
  1216. }
  1217. }
  1218. }
  1219. return moduleList;
  1220. }
  1221. };
  1222. if (typeof module !== 'undefined' && typeof module.exports === 'object') {
  1223. module.exports.View = view.View;
  1224. module.exports.ModelFactoryService = view.ModelFactoryService;
  1225. }