mascot-vis 2.0.0 → 2.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,592 @@
1
+ <!DOCTYPE html>
2
+ <html>
3
+ <head>
4
+ <meta charset="utf-8" />
5
+ <title>Mascot - Interactive Demos</title>
6
+ <script src="https://d3js.org/d3.v7.min.js"></script>
7
+ <script src="lib/three.js"></script>
8
+ <script src="lib/pixi.min.js"></script>
9
+ <script src="dist/mascot.js"></script>
10
+ <script src="lib/codemirror.js"></script>
11
+ <script src="lib/acorn.js"></script>
12
+ <link rel="stylesheet" href="lib/codemirror.css" />
13
+ <script src="lib/javascript.js"></script>
14
+ <script src="lib/jquery-1.12.4.js"></script>
15
+ <script src="lib/jquery-ui.js"></script>
16
+ <link rel="stylesheet" href="lib/jquery-ui.css" />
17
+ <link
18
+ rel="stylesheet"
19
+ type="text/css"
20
+ href="https://cdn.datatables.net/1.10.24/css/jquery.dataTables.css"
21
+ />
22
+
23
+ <script
24
+ type="text/javascript"
25
+ charset="utf8"
26
+ src="https://cdn.datatables.net/1.10.24/js/jquery.dataTables.js"
27
+ ></script>
28
+
29
+ <style>
30
+ html {
31
+ margin: 0px;
32
+ padding: 0px;
33
+ height: 100%;
34
+ font-family: Arial;
35
+ }
36
+ body {
37
+ height: 100vh;
38
+ margin: 0px;
39
+ padding: 0px;
40
+ background: #f2f2f2;
41
+ overflow-x: hidden;
42
+ overflow-y: hidden;
43
+ }
44
+ #homeBtn {
45
+ position: absolute;
46
+ top: 5px;
47
+ left: 15px;
48
+ font-size: 12px;
49
+ }
50
+ #demoList {
51
+ width: 152px;
52
+ height: calc(99% - 20px);
53
+ border-right: 1px solid #ccc;
54
+ background: #f2f2f2;
55
+ padding: 5px 5px 0 25px;
56
+ float: left;
57
+ list-style: none;
58
+ font-size: 12px;
59
+ overflow-y: scroll;
60
+ }
61
+ .category {
62
+ margin: 15px 0 10px -7px;
63
+ font-weight: bold;
64
+ }
65
+ .demos {
66
+ margin-bottom: 10px;
67
+ padding: 0px 15px 0 5px;
68
+ list-style: none;
69
+ }
70
+ .demos li {
71
+ margin-bottom: 8px;
72
+ }
73
+ .demos li:hover {
74
+ cursor: pointer;
75
+ color: blue;
76
+ }
77
+ #canvasElement {
78
+ width: 1600;
79
+ height: 1000;
80
+ margin: 0;
81
+ padding: 0;
82
+ position: absolute;
83
+ top: 0;
84
+ left: 183px;
85
+ }
86
+ #svgElement {
87
+ width: calc(100% - 183px);
88
+ height: 99%;
89
+ margin: 0;
90
+ padding: 0;
91
+ position: absolute;
92
+ top: 0;
93
+ left: 183px;
94
+ background: white;
95
+ }
96
+ #hint {
97
+ position: absolute;
98
+ top: 0;
99
+ padding: 5px;
100
+ left: 183px;
101
+ display: block;
102
+ background: #fffde5;
103
+ border: 1px solid #ddd;
104
+ border-radius: 5px;
105
+ margin: 2px 0 0 2px;
106
+ }
107
+ #gallery {
108
+ width: calc(100% - 183px);
109
+ height: 99%;
110
+ overflow-y: scroll;
111
+ background: #fafafa;
112
+ position: absolute;
113
+ top: 0;
114
+ right: 0;
115
+ border: 1px solid #000;
116
+ z-index: 1000;
117
+ }
118
+ .galleryItem {
119
+ max-width: 220px;
120
+ min-width: 220px;
121
+ height: 170px;
122
+ vertical-align: text-bottom;
123
+ text-align: center;
124
+ border: 2px solid #eee;
125
+ display: inline-block;
126
+ margin: 10px 15px 10px 15px;
127
+ }
128
+ .galleryItem:hover {
129
+ box-shadow: 0px 0px 5px #888;
130
+ cursor: pointer;
131
+ }
132
+ #console {
133
+ width: calc(100% - 198px);
134
+ position: absolute;
135
+ left: 191px;
136
+ height: 70%;
137
+ resize: none;
138
+ border: none;
139
+ display: none;
140
+ padding: 60px;
141
+ }
142
+ #btmPanel {
143
+ width: calc(100% - 183px);
144
+ height: 260px;
145
+ position: absolute;
146
+ bottom: 0px;
147
+ right: 0px;
148
+ }
149
+ .ui-tabs {
150
+ padding: 0;
151
+ }
152
+ .ui-tabs .ui-tabs-panel {
153
+ padding: 0;
154
+ }
155
+ .ui-tabs .ui-tabs-nav li a {
156
+ font-size: 8.5pt !important;
157
+ padding: 0;
158
+ }
159
+ #codeTab {
160
+ width: 100%;
161
+ height: calc(100% - 40px);
162
+ }
163
+ #dataTab {
164
+ width: 100%;
165
+ height: calc(100% - 40px);
166
+ }
167
+ #jsonTab {
168
+ width: 100%;
169
+ height: calc(100% - 40px);
170
+ }
171
+ #codeControls {
172
+ width: 350px;
173
+ height: 30px;
174
+ position: absolute;
175
+ top: 0;
176
+ right: 0;
177
+ margin: 8px 10px 0 0;
178
+ /* border-top: 1px solid #ccc; */
179
+ /* background: #f2f2f2; */
180
+ font-size: 8.5pt;
181
+ vertical-align: top;
182
+ }
183
+ .CodeMirror {
184
+ width: 100%;
185
+ height: 100%;
186
+ resize: none;
187
+ border: none;
188
+ padding: 0px;
189
+ font-family: Courier;
190
+ font-size: 12.5px;
191
+ overflow-x: hidden;
192
+ }
193
+ </style>
194
+ </head>
195
+ <body>
196
+ <ul id="demoList">
197
+ <button onclick="d3.select('#gallery').style('visibility', 'visible')">
198
+ Show Gallery
199
+ </button>
200
+ </ul>
201
+ <canvas id="canvasElement"></canvas>
202
+ <svg id="svgElement"></svg>
203
+ <div id="hint">
204
+ To update the demo after modifying the code, click the "run" button; to
205
+ see the construction process of a demo step by step, click the "play"
206
+ button.
207
+ </div>
208
+ <div id="console"></div>
209
+ <div id="btmPanel">
210
+ <ul>
211
+ <li><a href="#codeTab">Code</a></li>
212
+ <li><a id="dataTabLi" href="#dataTab">Data</a></li>
213
+ <li><a id="jsonTabLi" href="#jsonTab">Data</a></li>
214
+ <li><a href="#infoTab">About</a></li>
215
+ </ul>
216
+ <div id="codeTab">
217
+ <textarea id="scriptEditor"></textarea>
218
+ </div>
219
+ <div id="dataTab" style="overflow-y: auto">
220
+ <table
221
+ id="dataTable"
222
+ class="display"
223
+ style="font-size: 0.75rem; width: 100%"
224
+ ></table>
225
+ </div>
226
+ <div id="jsonTab" style="overflow-y: auto">
227
+ </div>
228
+ <div id="infoTab" style="padding: 15px"></div>
229
+ <div id="codeControls">
230
+ Renderer:
231
+ <input type="radio" id="SVG" name="renderer" value="svg" checked />
232
+ <label for="SVG">SVG</label>
233
+ <!-- <input type="radio" id="Canvas" name="renderer" value="Canvas">
234
+ <label for="Canvas">Canvas</label> -->
235
+ <input type="radio" id="WebGL" name="renderer" value="webgl" />
236
+ <label for="WebGL">WebGL</label>
237
+ <button style="margin-left: 40px" onclick="window.loadScript()">run</button>
238
+ <button onclick="window.stepThrough()">play</button>
239
+ <button onclick="window.save()">save</button>
240
+ </div>
241
+ </div>
242
+ <div id="gallery"></div>
243
+ <a id="download"></a>
244
+ <script type="module">
245
+ import {JSONEditor} from './lib/jsoneditor.js'
246
+ var editor = CodeMirror.fromTextArea(
247
+ document.getElementById("scriptEditor"),
248
+ { lineNumbers: true }
249
+ );
250
+ var dataTable = null;
251
+ var metaData;
252
+
253
+ function showError(message, source, lineno, colno, error) {
254
+ let csl = document.getElementById("console");
255
+ removeAllChildren(csl);
256
+ var msgNode = document.createElement("div");
257
+ msgNode.textContent = message
258
+ ? message
259
+ : error
260
+ ? JSON.stringify(error)
261
+ : "";
262
+ msgNode.style.color = "red";
263
+ csl.appendChild(msgNode);
264
+ if (error && error.stack) {
265
+ var stackNode = document.createElement("div");
266
+ stackNode.textContent = error.stack
267
+ .replace(/\n/g, "\r\n")
268
+ .replace(/@/g, " at ");
269
+ stackNode.style["white-space"] = "pre-line";
270
+ stackNode.style.color = "#777";
271
+ csl.appendChild(stackNode);
272
+ }
273
+ csl.style.display = "block";
274
+ }
275
+
276
+ window.onerror = showError;
277
+
278
+ $(function () {
279
+ $("#btmPanel").resizable({
280
+ minHeight: 100,
281
+ handles: "n",
282
+ });
283
+ });
284
+
285
+ var jsonEditor;
286
+ $(function () {
287
+ $("#btmPanel").tabs({
288
+ activate: function (event, ui) {
289
+ let t = ui.newTab
290
+ .attr("li", "innerHTML")[0]
291
+ .getElementsByTagName("a")[0].innerHTML;
292
+ if (t == "Code") $("#codeControls").css("visibility", "visible");
293
+ else $("#codeControls").css("visibility", "hidden");
294
+ },
295
+ });
296
+ let content = {
297
+ text: undefined,
298
+ json: {}
299
+ }
300
+ jsonEditor = new JSONEditor({
301
+ target: document.getElementById('jsonTab'),
302
+ props: {
303
+ content,
304
+ readOnly: true,
305
+ onChange: (updatedContent, previousContent, patchResult) => {
306
+ // content is an object { json: JSONData } | { text: string }
307
+ console.log('onChange', updatedContent, previousContent, patchResult)
308
+ content = updatedContent
309
+ }
310
+ }
311
+ })
312
+ });
313
+
314
+ function loadDataTable(responseText){
315
+ // Set the data for the csv in the script
316
+ let csvRegex = /msc\.csv\("(.+)"\);/;
317
+ let csvPath = csvRegex.exec(responseText)[1];
318
+
319
+ // Request the csv from the server
320
+ fetch(csvPath)
321
+ .then((resp) => {
322
+ return resp.text();
323
+ })
324
+ .then((text) => {
325
+ let rows = text.trim().split("\n");
326
+ // Destroy current table
327
+ if (dataTable !== null) {
328
+ dataTable.destroy();
329
+ }
330
+ // Set the thead for the table
331
+ const table = document.getElementById("dataTable");
332
+ let newHead = `<thead><tr>${rows[0]
333
+ .split(",")
334
+ .reduce(
335
+ (acc, row) => (acc += `<th>${row}</th>`),
336
+ ""
337
+ )}</tr></thead>`;
338
+ table.innerHTML = newHead;
339
+ // Initialize table
340
+ dataTable = $("#dataTable").DataTable({
341
+ data: rows.slice(1).map((row) => row.split(",")),
342
+ searching: false,
343
+ paging: false,
344
+ scrollCollapse: true,
345
+ fixedColumns: true,
346
+ info: false,
347
+ //responsive: true
348
+ });
349
+ });
350
+ }
351
+
352
+ function loadJson(responseText) {
353
+ let jsonRegex = /msc\.(tree|graph)json\("(.+)"\);/;
354
+ let jsonPath = jsonRegex.exec(responseText)[2];
355
+ // Request the csv from the server
356
+ fetch(jsonPath)
357
+ .then((resp) => {
358
+ return resp.text();
359
+ })
360
+ .then((text) => {
361
+ let json = JSON.parse(text);
362
+ let content = {
363
+ text: undefined,
364
+ json: json
365
+ }
366
+ jsonEditor.set(content);
367
+ });
368
+ }
369
+
370
+ function loadDemo(d) {
371
+ var oReq = new XMLHttpRequest();
372
+ oReq.addEventListener("load", function () {
373
+ loadScript(this.responseText);
374
+ if (this.responseText.indexOf(".csv") > 0) {
375
+ $("#jsonTabLi").hide();
376
+ $("#dataTabLi").show();
377
+ loadDataTable(this.responseText);
378
+ } else if (this.responseText.indexOf(".treejson") > 0 || this.responseText.indexOf(".graphjson") > 0) {
379
+ $("#jsonTabLi").show();
380
+ $("#dataTabLi").hide();
381
+ loadJson(this.responseText);
382
+ }
383
+ $("#btmPanel").tabs("option", "active", 0);
384
+ if (metaData && metaData[d]) d3.select("#infoTab").html(metaData[d].about);
385
+ });
386
+ oReq.open("GET", "demos/interactive/" + d + ".js");
387
+ oReq.send();
388
+ }
389
+
390
+ function removeAllChildren(e) {
391
+ while (e.firstChild) {
392
+ e.firstChild.remove();
393
+ }
394
+ }
395
+
396
+ function makeAsync(s) {
397
+ let c =
398
+ "try {\n" +
399
+ s +
400
+ "\n} catch (err) {showError(err.name ? err.name + ': ' + err.message : undefined, 0, 0, 0, err);}";
401
+ return "(async () => {" + c + "})();";
402
+ }
403
+
404
+ window.loadScript = function(s) {
405
+ let script = s ? s : editor.getValue();
406
+ let parsed = acorn.parse(script, { ecmaVersion: "latest" });
407
+ let blocks = parsed.body.map((d) => script.substring(d.start, d.end));
408
+
409
+ //find lines with renderer and remove them
410
+ blocks = blocks.filter((l) => l.indexOf("render") < 0);
411
+
412
+ //add renderer lines
413
+ let sceneName = blocks
414
+ .filter((l) => l.indexOf("msc.scene(") > 0)[0]
415
+ .split("=")[0]
416
+ .replace("let", " ")
417
+ .trim();
418
+ let r = $("input[type='radio'][name='renderer']:checked").val();
419
+ let ele = r == "svg" ? "svgElement" : "canvasElement";
420
+ let newScript =
421
+ blocks.join("\n") +
422
+ "\nmsc.renderer('" +
423
+ r + "','" + ele +
424
+ "').render(" +
425
+ sceneName +
426
+ ");";
427
+ //", {'collectionBounds': true});";
428
+ let csl = document.getElementById("console");
429
+ removeAllChildren(csl);
430
+ csl.style.display = "none";
431
+ document.getElementById("gallery").style.visibility = "hidden";
432
+
433
+ if (r == "svg") {
434
+ let svg = document.getElementById("svgElement");
435
+ removeAllChildren(svg);
436
+ svg.style.display = "block";
437
+ document.getElementById("canvasElement").style.display = "none";
438
+ } else {
439
+ let canvas = document.getElementById("canvasElement");
440
+ canvas.style.display = "block";
441
+ document.getElementById("svgElement").style.display = "none";
442
+ }
443
+
444
+ editor.setValue(newScript);
445
+ newScript = makeAsync(newScript);
446
+ eval(newScript);
447
+ }
448
+
449
+ let requestAnimationFrame = function (f) {
450
+ return setTimeout(f, 1000);
451
+ };
452
+
453
+ window.stepThrough = function() {
454
+ let script = editor.getValue().replace(/await/g, "");
455
+ let parsed = acorn.parse(script, { ecmaVersion: "latest" });
456
+
457
+ let blocks = parsed.body.map((d) => script.substring(d.start, d.end));
458
+ let renderIdx = blocks.findIndex((d) => d.indexOf("renderer") > 0),
459
+ renderLines = blocks.slice(renderIdx).join(";\n"),
460
+ code = "",
461
+ frames = [".mark", ".divide", ".densify", ".layout", ".encode", ".align", ".affix", ".axis", ".legend", ".gridlines", ".scale.rangeExtent"];
462
+ let currentIdx = 0;
463
+
464
+ let step = function (timeStamp) {
465
+ if (currentIdx >= renderIdx) return;
466
+ removeAllChildren(document.getElementById("svgElement"));
467
+ //let s = blocks.slice(0, currentIdx).join(";\n") + ";" + blocks.slice(renderIdx).join(";\n");
468
+ while (currentIdx < renderIdx) {
469
+ let newBlock = blocks[currentIdx];
470
+ if (newBlock.indexOf("msc.csv") > 0)
471
+ newBlock = newBlock.replace("msc.csv", "await msc.csv");
472
+ if (newBlock.indexOf("msc.treejson") > 0)
473
+ newBlock = newBlock.replace("msc.treejson", "await msc.treejson");
474
+ if (newBlock.indexOf("msc.graphjson") > 0)
475
+ newBlock = newBlock.replace("msc.graphjson", "await msc.graphjson");
476
+ code += newBlock + "\n";
477
+ let skip = true;
478
+ for (let f of frames) {
479
+ if (newBlock.indexOf(f) > 0) {
480
+ skip = false;
481
+ break;
482
+ }
483
+ }
484
+ if (!skip)
485
+ break;
486
+ else
487
+ currentIdx++;
488
+ }
489
+ editor.setValue(code + renderLines);
490
+ let s = makeAsync(code + renderLines);
491
+ eval(s);
492
+ currentIdx++;
493
+ requestAnimationFrame(step);
494
+ };
495
+ requestAnimationFrame(step);
496
+ }
497
+
498
+ window.save = function() {
499
+ let code = editor.getValue();
500
+ let demo = window.location.hash.replace("#", "");
501
+ let scn = code.split("=")[0].replace("let", "").trim();
502
+ code += "const a = document.getElementById('download');";
503
+ //code += "let spec = msc.specGenerator().run(" + scn + ");"
504
+ code += "let spec = " + scn + ".toJSON();"
505
+ code += "a.href = URL.createObjectURL(new Blob([JSON.stringify(spec, null, 2)], {type: 'application/json'}));";
506
+ // code += "a.setAttribute('download', '" + demo + ".json');";
507
+ code += "a.setAttribute('download', '" + demo + ".alscn');";
508
+ code += "a.click();";
509
+ eval(makeAsync(code));
510
+ }
511
+
512
+ function refresh() {
513
+ let demo = window.location.hash.replace("#", "");
514
+ if (demo && demo != "") {
515
+ loadDemo(demo);
516
+ } else if (demo == "") {
517
+ document.getElementById("gallery").style.visibility = "visible";
518
+ }
519
+ }
520
+
521
+ function populateList(json) {
522
+ let category = "",
523
+ list;
524
+ for (let k of Object.keys(json)) {
525
+ if (json[k].draft) continue;
526
+ let m = json[k].type;
527
+ if (m != category) {
528
+ d3.select("#demoList")
529
+ .append("li")
530
+ .attr("class", "category")
531
+ .text(m);
532
+ category = m;
533
+ list = d3.select("#demoList").append("ul").attr("class", "demos");
534
+ }
535
+ list
536
+ .append("li")
537
+ .text(json[k].name)
538
+ .on("click", () => {
539
+ document.getElementById("gallery").style.visibility = "hidden";
540
+ window.location.hash = k;
541
+ });
542
+ }
543
+ }
544
+
545
+ function populateGallery(json) {
546
+ let keys = Object.keys(json);
547
+ keys = keys
548
+ .map((a) => ({ sort: Math.random(), value: a }))
549
+ .sort((a, b) => a.sort - b.sort)
550
+ .map((a) => a.value);
551
+ for (let k of keys) {
552
+ if (json[k].draft) continue;
553
+ let item = d3.select("#gallery")
554
+ .append("div").attr("class", "galleryItem");
555
+ item.append("div")
556
+ .style("display", "block").style("height", "150px")
557
+ .style("background", "white")
558
+ .style("background-image", 'url("demos/thumbnails/' + k + '.png")')
559
+ .style("background-size", "auto 100%")
560
+ .style("background-repeat", "no-repeat")
561
+ .style("background-position", "left top")
562
+ .on("click", () => {
563
+ document.getElementById("gallery").style.visibility = "hidden";
564
+ window.location.hash = k;
565
+ });
566
+ item.append("div").style("display", "block").style("background", "#FAFAFA").style("font-size", "11.5px")
567
+ .style("font-weight", 600).style("text-align", "left").style("color", "#222")
568
+ .style("height", "16px").style("padding", "5px 0 0 3px").text(json[k].title);
569
+ }
570
+ }
571
+
572
+ window.onhashchange = refresh;
573
+
574
+ $("input[type=radio][name=renderer]").change(function () {
575
+ loadScript();
576
+ });
577
+
578
+ $(document).ready(() => {
579
+ fetch("demos/interactive.json")
580
+ .then((resp) => {
581
+ return resp.text();
582
+ })
583
+ .then((text) => {
584
+ metaData = JSON.parse(text);
585
+ populateList(metaData);
586
+ populateGallery(metaData);
587
+ refresh();
588
+ });
589
+ });
590
+ </script>
591
+ </body>
592
+ </html>
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "mascot-vis",
3
- "version": "2.0.0",
3
+ "version": "2.1.0",
4
4
  "description": "Manipulable Semantic Components in Data Visualization",
5
5
  "scripts": {
6
6
  "build": "rollup --config",