diff --git a/package-lock.json b/package-lock.json
index e7772bb4..e67b6b81 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -1,6 +1,6 @@
{
"name": "voyager",
- "version": "0.35.0",
+ "version": "0.37.0",
"lockfileVersion": 1,
"requires": true,
"dependencies": {
@@ -175,19 +175,6 @@
}
}
},
- "@sindresorhus/is": {
- "version": "0.14.0",
- "resolved": "https://registry.npmjs.org/@sindresorhus/is/-/is-0.14.0.tgz",
- "integrity": "sha512-9NET910DNaIPngYnLLPeg+Ogzqsi9uM4mSboU5y6p8S5DzMTVEsJZrawi+BoDNUVBa2DhJqQYUFvMDfgU062LQ=="
- },
- "@szmarczak/http-timer": {
- "version": "1.1.2",
- "resolved": "https://registry.npmjs.org/@szmarczak/http-timer/-/http-timer-1.1.2.tgz",
- "integrity": "sha512-XIB2XbzHTN6ieIjfIMV9hlVcfPU26s2vafYWQcZHWXHOxiaRZYEDKEwdl129Zyg50+foYV2jCgtrqSA6qNuNSA==",
- "requires": {
- "defer-to-connect": "^1.0.1"
- }
- },
"@tootallnate/once": {
"version": "1.1.2",
"resolved": "https://registry.npmjs.org/@tootallnate/once/-/once-1.1.2.tgz",
@@ -605,11 +592,6 @@
"integrity": "sha1-l6ERlkmyEa0zaR2fn0hqjsn74KM=",
"dev": true
},
- "an-array": {
- "version": "1.0.0",
- "resolved": "https://registry.npmjs.org/an-array/-/an-array-1.0.0.tgz",
- "integrity": "sha1-wSWlu4JXd4419LT2qpx9D6nkJmU="
- },
"ansi-colors": {
"version": "4.1.1",
"resolved": "https://registry.npmjs.org/ansi-colors/-/ansi-colors-4.1.1.tgz",
@@ -679,22 +661,12 @@
"resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz",
"integrity": "sha512-PCVAQswWemu6UdxsDFFX/+gVeYqKAod3D3UVm91jHwynguOwAvYPhx8nNlM++NqRcK6CxxpUafjmhIdKiHibqg=="
},
- "array-shuffle": {
- "version": "1.0.1",
- "resolved": "https://registry.npmjs.org/array-shuffle/-/array-shuffle-1.0.1.tgz",
- "integrity": "sha1-fqSIKjVrS8pfVF4LblLq9tlxVXo="
- },
"arrify": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/arrify/-/arrify-1.0.1.tgz",
"integrity": "sha512-3CYzex9M9FGQjCGMGyi6/31c8GJbgb0qGyrx5HWxPd0aCwh4cB2YjMb2Xf9UuoogrMrlO9cTqnB5rI5GHZTcUA==",
"dev": true
},
- "as-number": {
- "version": "1.0.0",
- "resolved": "https://registry.npmjs.org/as-number/-/as-number-1.0.0.tgz",
- "integrity": "sha1-rLJ+NPj52KsNqeN287iVmGD4CmY="
- },
"asap": {
"version": "2.0.6",
"resolved": "https://registry.npmjs.org/asap/-/asap-2.0.6.tgz",
@@ -930,35 +902,6 @@
}
}
},
- "cacheable-request": {
- "version": "6.1.0",
- "resolved": "https://registry.npmjs.org/cacheable-request/-/cacheable-request-6.1.0.tgz",
- "integrity": "sha512-Oj3cAGPCqOZX7Rz64Uny2GYAZNliQSqfbePrgAQ1wKAihYmCUnraBtJtKcGR4xz7wF+LoJC+ssFZvv5BgF9Igg==",
- "requires": {
- "clone-response": "^1.0.2",
- "get-stream": "^5.1.0",
- "http-cache-semantics": "^4.0.0",
- "keyv": "^3.0.0",
- "lowercase-keys": "^2.0.0",
- "normalize-url": "^4.1.0",
- "responselike": "^1.0.2"
- },
- "dependencies": {
- "get-stream": {
- "version": "5.2.0",
- "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-5.2.0.tgz",
- "integrity": "sha512-nBF+F1rAZVCu/p7rjzgA+Yb4lfYXrpl7a6VmJrU8wF9I1CKvP/QwPNZHnOlwbTkY6dvtFIzFMSyQXbLoTQPRpA==",
- "requires": {
- "pump": "^3.0.0"
- }
- },
- "lowercase-keys": {
- "version": "2.0.0",
- "resolved": "https://registry.npmjs.org/lowercase-keys/-/lowercase-keys-2.0.0.tgz",
- "integrity": "sha512-tqNXrS78oMOE73NMxK4EMLQsQowWf8jKooH9g7xPavRT706R6bkQJ6DY2Te7QukaZsulxa30wQ7bk0pm4XiHmA=="
- }
- }
- },
"call-bind": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.2.tgz",
@@ -1159,14 +1102,6 @@
"shallow-clone": "^3.0.0"
}
},
- "clone-response": {
- "version": "1.0.3",
- "resolved": "https://registry.npmjs.org/clone-response/-/clone-response-1.0.3.tgz",
- "integrity": "sha512-ROoL94jJH2dUVML2Y/5PEDNaSHgeOdSDicUyS7izcF63G6sTc/FTjLub4b8Il9S8S0beOfYt0TaA5qvFK+w0wA==",
- "requires": {
- "mimic-response": "^1.0.0"
- }
- },
"color-convert": {
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz",
@@ -1537,14 +1472,6 @@
}
}
},
- "decompress-response": {
- "version": "3.3.0",
- "resolved": "https://registry.npmjs.org/decompress-response/-/decompress-response-3.3.0.tgz",
- "integrity": "sha1-gKTdMjdIOEv6JICDYirt7Jgq3/M=",
- "requires": {
- "mimic-response": "^1.0.0"
- }
- },
"deep-eql": {
"version": "4.1.3",
"resolved": "https://registry.npmjs.org/deep-eql/-/deep-eql-4.1.3.tgz",
@@ -1559,11 +1486,6 @@
"resolved": "https://registry.npmjs.org/deepmerge/-/deepmerge-4.2.2.tgz",
"integrity": "sha512-FJ3UgI4gIl+PHZm53knsuSFpE+nESMr7M4v9QcgB7S63Kj/6WqMiFQJpBBYz1Pt+66bZpP3Q7Lye0Oo9MPKEdg=="
},
- "defer-to-connect": {
- "version": "1.1.3",
- "resolved": "https://registry.npmjs.org/defer-to-connect/-/defer-to-connect-1.1.3.tgz",
- "integrity": "sha512-0ISdNousHvZT2EiFlZeZAHBUvSxmKswVCEf8hW7KWgG4a8MVEu/3Vb6uWYozkjylyCxe0JBIiRB1jV45S70WVQ=="
- },
"delayed-stream": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz",
@@ -1669,16 +1591,6 @@
"integrity": "sha512-rlBi9d8jpv9Sf1klPjNfFAuWDjKLwTIJJ/VxtoTwIR6hnZxcEOQCZg2oIL3MWBYw5GpUDKOEnND7LXTbIpQ03Q==",
"dev": true
},
- "dtype": {
- "version": "2.0.0",
- "resolved": "https://registry.npmjs.org/dtype/-/dtype-2.0.0.tgz",
- "integrity": "sha1-zQUjI84GFETs0uj1dI9popvihDQ="
- },
- "duplexer3": {
- "version": "0.1.5",
- "resolved": "https://registry.npmjs.org/duplexer3/-/duplexer3-0.1.5.tgz",
- "integrity": "sha512-1A8za6ws41LQgv9HrE/66jyC5yuSjQ3L/KOpFtoBilsAK2iA2wuS5rTt1OCzIvtS2V7nVmedsUU+DGRcjBmOYA=="
- },
"ecc-jsbn": {
"version": "0.1.2",
"resolved": "https://registry.npmjs.org/ecc-jsbn/-/ecc-jsbn-0.1.2.tgz",
@@ -1739,14 +1651,6 @@
}
}
},
- "end-of-stream": {
- "version": "1.4.4",
- "resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.4.tgz",
- "integrity": "sha512-+uw1inIHVPQoaVuHzRyXd21icM+cnt4CzD5rW+NC1wjOUSTOs+Te7FOv7AhN7vS9x/oIyhLP5PR1H+phQAHu5Q==",
- "requires": {
- "once": "^1.4.0"
- }
- },
"enhanced-resolve": {
"version": "5.8.3",
"resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.8.3.tgz",
@@ -2060,14 +1964,6 @@
"integrity": "sha512-b6suED+5/3rTpUBdG1gupIl8MPFCAMA0QXwmljLhvCUKcUvdE4gWky9zpuGCcXHOsz4J9wPGNWq6OKpmIzz3hQ==",
"dev": true
},
- "flatten-vertex-data": {
- "version": "1.0.2",
- "resolved": "https://registry.npmjs.org/flatten-vertex-data/-/flatten-vertex-data-1.0.2.tgz",
- "integrity": "sha512-BvCBFK2NZqerFTdMDgqfHBwxYWnxeCkwONsw6PvBMcUXqo8U/KDWwmXhqx1x2kLIg7DqIsJfOaJFOmlua3Lxuw==",
- "requires": {
- "dtype": "^2.0.0"
- }
- },
"forever-agent": {
"version": "0.6.1",
"resolved": "https://registry.npmjs.org/forever-agent/-/forever-agent-0.6.1.tgz",
@@ -2201,14 +2097,6 @@
"integrity": "sha512-F5aQMywwJ2n85s4hJPTT9RPxGmubonuB10MNYo17/xph174n2MIR33HRguhzVag10O/npM7SPk73LMZNP+FaWw==",
"dev": true
},
- "get-stream": {
- "version": "4.1.0",
- "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-4.1.0.tgz",
- "integrity": "sha512-GMat4EJ5161kIy2HevLlr4luNjBgvmj413KaQA7jt4V8B4RDsfpHk7WQ9GVqfYyyx8OS/L66Kox+rJRNklLK7w==",
- "requires": {
- "pump": "^3.0.0"
- }
- },
"getpass": {
"version": "0.1.7",
"resolved": "https://registry.npmjs.org/getpass/-/getpass-0.1.7.tgz",
@@ -2282,24 +2170,6 @@
}
}
},
- "got": {
- "version": "9.6.0",
- "resolved": "https://registry.npmjs.org/got/-/got-9.6.0.tgz",
- "integrity": "sha512-R7eWptXuGYxwijs0eV+v3o6+XH1IqVK8dJOEecQfTmkncw9AV4dcw/Dhxi8MdlqPthxxpZyizMzyg8RTmEsG+Q==",
- "requires": {
- "@sindresorhus/is": "^0.14.0",
- "@szmarczak/http-timer": "^1.1.2",
- "cacheable-request": "^6.0.0",
- "decompress-response": "^3.3.0",
- "duplexer3": "^0.1.4",
- "get-stream": "^4.1.0",
- "lowercase-keys": "^1.0.1",
- "mimic-response": "^1.0.1",
- "p-cancelable": "^1.0.0",
- "to-readable-stream": "^1.0.0",
- "url-parse-lax": "^3.0.0"
- }
- },
"graceful-fs": {
"version": "4.2.8",
"resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.8.tgz",
@@ -2466,7 +2336,8 @@
"http-cache-semantics": {
"version": "4.1.1",
"resolved": "https://registry.npmjs.org/http-cache-semantics/-/http-cache-semantics-4.1.1.tgz",
- "integrity": "sha512-er295DKPVsV82j5kw1Gjt+ADA/XYHsajl82cGNQG2eyoPkvgUhX+nDIyelzhIWbbsXP39EHcI6l5tYs2FYqYXQ=="
+ "integrity": "sha512-er295DKPVsV82j5kw1Gjt+ADA/XYHsajl82cGNQG2eyoPkvgUhX+nDIyelzhIWbbsXP39EHcI6l5tYs2FYqYXQ==",
+ "dev": true
},
"http-errors": {
"version": "2.0.0",
@@ -2837,11 +2708,6 @@
"integrity": "sha512-UVU9dibq2JcFWxQPA6KCqj5O42VOmAY3zQUfEKxU0KpTGXwNoCjkX1e13eHNvw/xPynt6pU0rZ1htjWTNTSXsg==",
"dev": true
},
- "json-buffer": {
- "version": "3.0.0",
- "resolved": "https://registry.npmjs.org/json-buffer/-/json-buffer-3.0.0.tgz",
- "integrity": "sha1-Wx85evx11ne96Lz8Dkfh+aPZqJg="
- },
"json-parse-even-better-errors": {
"version": "2.3.1",
"resolved": "https://registry.npmjs.org/json-parse-even-better-errors/-/json-parse-even-better-errors-2.3.1.tgz",
@@ -2916,14 +2782,6 @@
"verror": "1.10.0"
}
},
- "keyv": {
- "version": "3.1.0",
- "resolved": "https://registry.npmjs.org/keyv/-/keyv-3.1.0.tgz",
- "integrity": "sha512-9ykJ/46SN/9KPM/sichzQ7OvXyGDYKGTaDlKMGCAlg2UK8KRy4jb0d8sFc+0Tt0YYnThq8X2RZgCg74RPxgcVA==",
- "requires": {
- "json-buffer": "3.0.0"
- }
- },
"kind-of": {
"version": "6.0.3",
"resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.3.tgz",
@@ -2940,16 +2798,6 @@
"resolved": "https://registry.npmjs.org/layerr/-/layerr-2.0.1.tgz",
"integrity": "sha512-z0730CwG/JO24evdORnyDkwG1Q7b7mF2Tp1qRQ0YvrMMARbt1DFG694SOv439Gm7hYKolyZyaB49YIrYIfZBdg=="
},
- "layout-bmfont-text": {
- "version": "1.3.4",
- "resolved": "https://registry.npmjs.org/layout-bmfont-text/-/layout-bmfont-text-1.3.4.tgz",
- "integrity": "sha1-8g8sVGR3T0jabOipl/vObUaUW4E=",
- "requires": {
- "as-number": "^1.0.0",
- "word-wrapper": "^1.0.7",
- "xtend": "^4.0.0"
- }
- },
"license-checker": {
"version": "25.0.1",
"resolved": "https://registry.npmjs.org/license-checker/-/license-checker-25.0.1.tgz",
@@ -3141,11 +2989,6 @@
}
}
},
- "lowercase-keys": {
- "version": "1.0.1",
- "resolved": "https://registry.npmjs.org/lowercase-keys/-/lowercase-keys-1.0.1.tgz",
- "integrity": "sha512-G2Lj61tXDnVFFOi8VZds+SoQjtQC3dgokKdDG2mTm1tx4m50NUHBOZSBwQQHyy0V12A0JTG4icfZQH+xPyh8VA=="
- },
"lru-cache": {
"version": "6.0.0",
"resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz",
@@ -3190,24 +3033,6 @@
"ssri": "^8.0.0"
}
},
- "map-limit": {
- "version": "0.0.1",
- "resolved": "https://registry.npmjs.org/map-limit/-/map-limit-0.0.1.tgz",
- "integrity": "sha1-63lhAxwPDo0AG/LVb6toXViCLzg=",
- "requires": {
- "once": "~1.3.0"
- },
- "dependencies": {
- "once": {
- "version": "1.3.3",
- "resolved": "https://registry.npmjs.org/once/-/once-1.3.3.tgz",
- "integrity": "sha1-suJhVXzkwxTsgwTz+oJmPkKXyiA=",
- "requires": {
- "wrappy": "1"
- }
- }
- }
- },
"map-obj": {
"version": "4.3.0",
"resolved": "https://registry.npmjs.org/map-obj/-/map-obj-4.3.0.tgz",
@@ -3355,11 +3180,6 @@
"integrity": "sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==",
"dev": true
},
- "mimic-response": {
- "version": "1.0.1",
- "resolved": "https://registry.npmjs.org/mimic-response/-/mimic-response-1.0.1.tgz",
- "integrity": "sha512-j5EctnkH7amfV/q5Hgmoal1g2QHFJRraOtmx0JpIqkxhBhI/lJSl1nMpQ45hVarwNETOoWEimndZ4QK0RHxuxQ=="
- },
"min-indent": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/min-indent/-/min-indent-1.0.1.tgz",
@@ -3644,22 +3464,6 @@
"resolved": "https://registry.npmjs.org/nested-property/-/nested-property-4.0.0.tgz",
"integrity": "sha512-yFehXNWRs4cM0+dz7QxCd06hTbWbSkV0ISsqBfkntU6TOY4Qm3Q88fRRLOddkGh2Qq6dZvnKVAahfhjcUvLnyA=="
},
- "new-array": {
- "version": "1.0.0",
- "resolved": "https://registry.npmjs.org/new-array/-/new-array-1.0.0.tgz",
- "integrity": "sha1-XbxjnZYerH8an7wacUbsEvKST78="
- },
- "nice-color-palettes": {
- "version": "3.0.0",
- "resolved": "https://registry.npmjs.org/nice-color-palettes/-/nice-color-palettes-3.0.0.tgz",
- "integrity": "sha512-lL4AjabAAFi313tjrtmgm/bxCRzp4l3vCshojfV/ij3IPdtnRqv6Chcw+SqJUhbe7g3o3BecaqCJYUNLswGBhQ==",
- "requires": {
- "got": "^9.2.2",
- "map-limit": "0.0.1",
- "minimist": "^1.2.0",
- "new-array": "^1.0.0"
- }
- },
"no-case": {
"version": "3.0.4",
"resolved": "https://registry.npmjs.org/no-case/-/no-case-3.0.4.tgz",
@@ -3839,11 +3643,6 @@
"resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz",
"integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA=="
},
- "normalize-url": {
- "version": "4.5.1",
- "resolved": "https://registry.npmjs.org/normalize-url/-/normalize-url-4.5.1.tgz",
- "integrity": "sha512-9UZCFRHQdNrfTpGg8+1INIg93B6zE0aXMVFkw1WFwvO4SlZywU6aLg5Of0Ap/PgcbSw4LNxvMWXMeugwMCX0AA=="
- },
"npm-normalize-package-bin": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/npm-normalize-package-bin/-/npm-normalize-package-bin-1.0.1.tgz",
@@ -3918,7 +3717,8 @@
"object-assign": {
"version": "4.1.1",
"resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz",
- "integrity": "sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM="
+ "integrity": "sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM=",
+ "dev": true
},
"object-inspect": {
"version": "1.12.3",
@@ -3942,6 +3742,7 @@
"version": "1.4.0",
"resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz",
"integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=",
+ "dev": true,
"requires": {
"wrappy": "1"
}
@@ -3977,11 +3778,6 @@
"os-tmpdir": "^1.0.0"
}
},
- "p-cancelable": {
- "version": "1.1.0",
- "resolved": "https://registry.npmjs.org/p-cancelable/-/p-cancelable-1.1.0.tgz",
- "integrity": "sha512-s73XxOZ4zpt1edZYZzvhqFa6uvQc1vwUa0K0BdtIZgQMAJj9IbebH+JkgKZc9h+B05PKHLOTl4ajG1BmNrVZlw=="
- },
"p-limit": {
"version": "3.1.0",
"resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz",
@@ -4510,11 +4306,6 @@
"integrity": "sha512-97DXOFbQJhk71ne5/Mt6cOu6yxsSfM0QGQyl0L25Gca4yGWEGJaig7l7gbCX623VqTBNGLRLaVUCnNkcedlRSQ==",
"dev": true
},
- "prepend-http": {
- "version": "2.0.0",
- "resolved": "https://registry.npmjs.org/prepend-http/-/prepend-http-2.0.0.tgz",
- "integrity": "sha1-6SQ0v6XqjBn0HN/UAddBo8gZ2Jc="
- },
"pretty-error": {
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/pretty-error/-/pretty-error-4.0.0.tgz",
@@ -4572,15 +4363,6 @@
"resolved": "https://registry.npmjs.org/pstree.remy/-/pstree.remy-1.1.8.tgz",
"integrity": "sha512-77DZwxQmxKnu3aR542U+X8FypNzbfJ+C5XQDk3uWjWxn6151aIMGthWYRXTqT1E5oJvg+ljaa2OJi+VfvCOQ8w=="
},
- "pump": {
- "version": "3.0.0",
- "resolved": "https://registry.npmjs.org/pump/-/pump-3.0.0.tgz",
- "integrity": "sha512-LwZy+p3SFs1Pytd/jYct4wpv49HiYCqd9Rlc5ZVdk0V+8Yzv6jR5Blk3TRmPL1ft69TxP0IMZGJ+WPFU2BFhww==",
- "requires": {
- "end-of-stream": "^1.1.0",
- "once": "^1.3.1"
- }
- },
"punycode": {
"version": "2.1.1",
"resolved": "https://registry.npmjs.org/punycode/-/punycode-2.1.1.tgz",
@@ -4594,16 +4376,6 @@
"side-channel": "^1.0.4"
}
},
- "quad-indices": {
- "version": "2.0.1",
- "resolved": "https://registry.npmjs.org/quad-indices/-/quad-indices-2.0.1.tgz",
- "integrity": "sha1-ppQdiaE9Y+7WwdSlpiGgRjYXqBQ=",
- "requires": {
- "an-array": "^1.0.0",
- "dtype": "^2.0.0",
- "is-buffer": "^1.0.2"
- }
- },
"querystringify": {
"version": "2.2.0",
"resolved": "https://registry.npmjs.org/querystringify/-/querystringify-2.2.0.tgz",
@@ -4934,14 +4706,6 @@
"resolved": "https://registry.npmjs.org/resolve-pathname/-/resolve-pathname-3.0.0.tgz",
"integrity": "sha512-C7rARubxI8bXFNB/hqcp/4iUeIXJhJZvFPFPiSPRnhU5UPxzMFIl+2E6yY6c4k9giDJAhtV+enfA+G89N6Csng=="
},
- "responselike": {
- "version": "1.0.2",
- "resolved": "https://registry.npmjs.org/responselike/-/responselike-1.0.2.tgz",
- "integrity": "sha1-kYcg7ztjHFZCvgaPFa3lpG9Loec=",
- "requires": {
- "lowercase-keys": "^1.0.0"
- }
- },
"retry": {
"version": "0.12.0",
"resolved": "https://registry.npmjs.org/retry/-/retry-0.12.0.tgz",
@@ -5673,27 +5437,6 @@
"resolved": "https://registry.npmjs.org/three/-/three-0.158.0.tgz",
"integrity": "sha512-TALj4EOpdDPF1henk2Q+s17K61uEAAWQ7TJB68nr7FKxqwyDr3msOt5IWdbGm4TaWKjrtWS8DJJWe9JnvsWOhQ=="
},
- "three-bmfont-text": {
- "version": "git+ssh://git@github.com/Smithsonian/three-bmfont-text.git#50da80d887873140dd7f20b28675e68de0b2b1da",
- "from": "three-bmfont-text@git+https://github.com/Smithsonian/three-bmfont-text.git#50da80d887",
- "requires": {
- "array-shuffle": "^1.0.1",
- "inherits": "^2.0.1",
- "layout-bmfont-text": "^1.2.0",
- "nice-color-palettes": "^3.0.0",
- "object-assign": "^4.0.1",
- "quad-indices": "^2.0.1",
- "three-buffer-vertex-data": "^1.0.0"
- }
- },
- "three-buffer-vertex-data": {
- "version": "1.1.0",
- "resolved": "https://registry.npmjs.org/three-buffer-vertex-data/-/three-buffer-vertex-data-1.1.0.tgz",
- "integrity": "sha1-zyKOeEJ2ZYhLlhpMq+H4XtOfgrE=",
- "requires": {
- "flatten-vertex-data": "^1.0.0"
- }
- },
"timsort": {
"version": "0.3.0",
"resolved": "https://registry.npmjs.org/timsort/-/timsort-0.3.0.tgz",
@@ -5705,11 +5448,6 @@
"resolved": "https://registry.npmjs.org/tinymce/-/tinymce-6.3.1.tgz",
"integrity": "sha512-+oCwXuTxAdJXVJ0130OxQz0JDNsqg3deuzgeUo8X5Vb27EzCJgXwO5eWvCxvkxpQo4oiHMVlM4tUIpTUHufHGQ=="
},
- "to-readable-stream": {
- "version": "1.0.0",
- "resolved": "https://registry.npmjs.org/to-readable-stream/-/to-readable-stream-1.0.0.tgz",
- "integrity": "sha512-Iq25XBt6zD5npPhlLVXGFN3/gyR2/qODcKNNyTMd4vbm39HUaOiAM4PMq0eMVC/Tkxz+Zjdsc55g9yyz+Yq00Q=="
- },
"to-regex-range": {
"version": "5.0.1",
"resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz",
@@ -5977,14 +5715,6 @@
"requires-port": "^1.0.0"
}
},
- "url-parse-lax": {
- "version": "3.0.0",
- "resolved": "https://registry.npmjs.org/url-parse-lax/-/url-parse-lax-3.0.0.tgz",
- "integrity": "sha1-FrXK/Afb42dsGxmZF3gj1lA6yww=",
- "requires": {
- "prepend-http": "^2.0.0"
- }
- },
"util-deprecate": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz",
@@ -6351,11 +6081,6 @@
"integrity": "sha512-JcKqAHLPxcdb9KM49dufGXn2x3ssnfjbcaQdLlfZsL9rH9wgDQjUtDxbo8NE0F6SFvydeu1VhZe7hZuHsB2/pw==",
"dev": true
},
- "word-wrapper": {
- "version": "1.0.7",
- "resolved": "https://registry.npmjs.org/word-wrapper/-/word-wrapper-1.0.7.tgz",
- "integrity": "sha1-HxSv6/Zt/fD+9V79NxhO+9CMKLY="
- },
"wordwrap": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/wordwrap/-/wordwrap-1.0.0.tgz",
@@ -6381,7 +6106,8 @@
"wrappy": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz",
- "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8="
+ "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=",
+ "dev": true
},
"xml-js": {
"version": "1.6.11",
@@ -6399,11 +6125,6 @@
"xml-js": "^1.6.2"
}
},
- "xtend": {
- "version": "4.0.2",
- "resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.2.tgz",
- "integrity": "sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ=="
- },
"y18n": {
"version": "5.0.8",
"resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz",
diff --git a/source/client/annotations/AnnotationSprite.ts b/source/client/annotations/AnnotationSprite.ts
index 8604c952..c58aa0a3 100755
--- a/source/client/annotations/AnnotationSprite.ts
+++ b/source/client/annotations/AnnotationSprite.ts
@@ -22,6 +22,7 @@ import HTMLSprite, { SpriteElement, html } from "@ff/three/HTMLSprite";
import Annotation from "../models/Annotation";
import CVAssetReader from "client/components/CVAssetReader";
+import AnnotationOverlay from "client/ui/explorer/AnnotationOverlay";
////////////////////////////////////////////////////////////////////////////////
@@ -66,6 +67,7 @@ export default class AnnotationSprite extends HTMLSprite
static readonly typeName: string = "Annotation";
isAdaptive = true;
+ isAnimating = false;
assetManager = null;
audioManager = null;
@@ -140,6 +142,25 @@ export default class AnnotationSprite extends HTMLSprite
export class AnnotationElement extends SpriteElement
{
protected sprite: AnnotationSprite;
+ protected isTruncated: boolean = false;
+ protected isOverlayed: boolean = false;
+
+ get truncated()
+ {
+ return this.isTruncated
+ }
+ set truncated(value: boolean)
+ {
+ this.isTruncated = value;
+ }
+ get overlayed()
+ {
+ return this.isOverlayed
+ }
+ set overlayed(value: boolean)
+ {
+ this.isOverlayed = value;
+ }
constructor(sprite: AnnotationSprite)
{
@@ -166,4 +187,15 @@ export class AnnotationElement extends SpriteElement
{
event.stopPropagation();
}
+
+ showOverlay(content: HTMLElement)
+ {
+ this.requestUpdate().then(() => {
+ AnnotationOverlay.show(this.parentElement, content, this.sprite).then(() => {
+ this.overlayed = false;
+ this.append(content); // attach content back to original container
+ this.requestUpdate();
+ });
+ });
+ }
}
\ No newline at end of file
diff --git a/source/client/annotations/CircleSprite.ts b/source/client/annotations/CircleSprite.ts
index 1626ff13..52a4a527 100755
--- a/source/client/annotations/CircleSprite.ts
+++ b/source/client/annotations/CircleSprite.ts
@@ -43,6 +43,9 @@ export default class CircleSprite extends AnnotationSprite
protected offset: Group;
protected anchorMesh: Mesh;
+ protected adaptive = true;
+ protected originalHeight;
+ protected originalWidth;
constructor(annotation: Annotation)
{
@@ -112,6 +115,16 @@ export default class CircleSprite extends AnnotationSprite
this.offset.visible = isShowing;
+ // update adaptive settings
+ if(this.adaptive !== this.isAdaptive) {
+ if(!this.isAdaptive) {
+ element.truncated = false;
+ element.classList.remove("sv-short");
+ element.requestUpdate();
+ }
+ this.adaptive = this.isAdaptive;
+ }
+
// don't show if behind the camera
this.setVisible(!this.isBehindCamera(this.offset, camera) && isShowing);
if(!this.getVisible()) {
@@ -119,28 +132,23 @@ export default class CircleSprite extends AnnotationSprite
}
// check if annotation is out of bounds and update if needed
- if (annotation.expanded) {
- element.classList.add("sv-expanded");
-
- let x = element.getBoundingClientRect().left - container.getBoundingClientRect().left;
- let y = element.getBoundingClientRect().top - container.getBoundingClientRect().top;
-
- if (x + element.offsetWidth >= container.offsetWidth && !element.classList.contains("sv-align-right")) {
- element.classList.add("sv-align-right");
- element.requestUpdate();
- }
- else if (x + element.offsetWidth < container.offsetWidth && element.classList.contains("sv-align-right")){
- element.classList.remove("sv-align-right");
- element.requestUpdate();
- }
- if (y + element.offsetHeight >= container.offsetHeight && !element.classList.contains("sv-align-bottom")) {
- element.classList.add("sv-align-bottom");
- element.requestUpdate();
- }
- else if (y + element.offsetHeight < container.offsetHeight && element.classList.contains("sv-align-bottom")) {
- element.classList.remove("sv-align-bottom");
- element.requestUpdate();
+ if (this.adaptive && !this.isAnimating && annotation.expanded) {
+
+ if(!element.truncated) {
+ if(!element.classList.contains("sv-expanded")) {
+ element.requestUpdate().then(() => {
+ this.originalHeight = element.offsetHeight;
+ this.originalWidth = element.offsetWidth;
+ this.checkTruncate(element, container);
+ });
+ }
+ else {
+ this.originalHeight = element.offsetHeight;
+ this.originalWidth = element.offsetWidth;
+ }
}
+
+ this.checkTruncate(element, container);
}
}
@@ -148,6 +156,46 @@ export default class CircleSprite extends AnnotationSprite
{
return new CircleAnnotation(this);
}
+
+ // Helper function to check if annotation should truncate
+ protected checkTruncate(element: AnnotationElement, container: HTMLElement) {
+ const x = element.getBoundingClientRect().left - container.getBoundingClientRect().left;
+ const y = element.getBoundingClientRect().top - container.getBoundingClientRect().top;
+
+ const shouldTruncate = y + this.originalHeight >= container.offsetHeight;
+ if(shouldTruncate !== element.truncated) {
+ element.truncated = shouldTruncate;
+ element.requestUpdate().then(() => {
+ this.checkBounds(element, container);
+ });
+ }
+ else {
+ this.checkBounds(element, container);
+ }
+ }
+
+ // Helper function to check and handle annotation overlap with bounds of container
+ protected checkBounds(element: AnnotationElement, container: HTMLElement) {
+ const x = element.getBoundingClientRect().left - container.getBoundingClientRect().left;
+ const y = element.getBoundingClientRect().top - container.getBoundingClientRect().top;
+
+ if (x + element.offsetWidth >= container.offsetWidth && !element.classList.contains("sv-align-right")) {
+ element.classList.add("sv-align-right");
+ element.requestUpdate();
+ }
+ else if (x + element.offsetWidth < container.offsetWidth && element.classList.contains("sv-align-right")){
+ element.classList.remove("sv-align-right");
+ element.requestUpdate();
+ }
+ if (y + element.offsetHeight >= container.offsetHeight && !element.classList.contains("sv-align-bottom")) {
+ element.classList.add("sv-align-bottom");
+ element.requestUpdate();
+ }
+ else if (y + element.offsetHeight < container.offsetHeight && element.classList.contains("sv-align-bottom")) {
+ element.classList.remove("sv-align-bottom");
+ element.requestUpdate();
+ }
+ }
}
AnnotationFactory.registerType(CircleSprite);
@@ -169,6 +217,7 @@ class CircleAnnotation extends AnnotationElement
this.onClickArticle = this.onClickArticle.bind(this);
this.onClickAudio = this.onClickAudio.bind(this);
this.onKeyDown = this.onKeyDown.bind(this);
+ this.onClickOverlay = this.onClickOverlay.bind(this);
this.markerElement = this.appendElement("div");
this.markerElement.classList.add("sv-marker");
@@ -197,17 +246,20 @@ class CircleAnnotation extends AnnotationElement
const annotation = this.sprite.annotation;
const annotationData = annotation.data;
+ const isTruncated = !this.overlayed && this.truncated
+ && (annotationData.imageUri || annotationData.articleId || annotation.lead.length > 0); // make sure we have content to truncate
const audio = this.sprite.audioManager;
// update title
this.markerElement.innerText = annotationData.marker;
-
+
const contentTemplate = html`
-
${annotation.title}
- ${annotationData.imageUri ? html`${annotation.imageCredit ? html`
${annotation.imageCredit}
` : null}
` : null}
- ${unsafeHTML(annotation.lead)}
- ${annotationData.audioId ? html`` : null}
- ${annotationData.articleId ? html`` : null}`;
+ ${!this.isOverlayed ? html`${annotation.title}
` : null}
+ ${annotationData.imageUri && !isTruncated ? html`${annotation.imageCredit ? html`
${annotation.imageCredit}
` : null}
` : null}
+ ${!isTruncated ? html`${unsafeHTML(annotation.lead)}
` : null}
+ ${annotationData.audioId && !this.isOverlayed ? html`` : null}
+ ${annotationData.articleId && !isTruncated ? html`` : null}
+ ${isTruncated ? html`` : null}`;
render(contentTemplate, this.contentElement);
@@ -221,7 +273,7 @@ class CircleAnnotation extends AnnotationElement
}
// update expanded/collapsed
- if (this.isExpanded !== annotationData.expanded) {
+ if (this.isExpanded !== annotationData.expanded && !this.overlayed) {
this.isExpanded = annotationData.expanded;
@@ -233,7 +285,7 @@ class CircleAnnotation extends AnnotationElement
this.classList.add("sv-expanded");
//this.style.minWidth = annotationData.lead.length < 40 && (!annotationData.audioId || annotationData.audioId.length == 0) ? "0" : "";
this.contentElement.style.display = "block";
- this.contentElement.style.height = this.contentElement.scrollHeight + "px";
+ this.contentElement.style.height = "auto"; //this.contentElement.scrollHeight + "px";
}
else {
this.classList.remove("sv-expanded");
@@ -257,7 +309,7 @@ class CircleAnnotation extends AnnotationElement
}
const audioView = this.querySelector(".sv-audio-view");
- if(annotationData.audioId) {
+ if(annotationData.audioId && !this.overlayed) {
if(annotationData.expanded && !audioView) {
const audioContainer = this.querySelector("#audio_container");
audioContainer.append(audio.getPlayerById(annotationData.audioId));
@@ -281,10 +333,17 @@ class CircleAnnotation extends AnnotationElement
this.sprite.emitLinkEvent(this.sprite.annotation.data.articleId);
}
+ protected onClickOverlay(event: MouseEvent)
+ {
+ event.stopPropagation();
+ const content = this.contentElement;
+ this.overlayed = true;
+ this.showOverlay(content);
+ }
+
protected onClickAudio(event: MouseEvent)
{
event.stopPropagation();
- this.sprite.emitClickEvent();
}
protected onKeyDown(event: KeyboardEvent)
diff --git a/source/client/annotations/ExtendedSprite.ts b/source/client/annotations/ExtendedSprite.ts
index d474e978..b1118a67 100755
--- a/source/client/annotations/ExtendedSprite.ts
+++ b/source/client/annotations/ExtendedSprite.ts
@@ -26,6 +26,7 @@ import "@ff/ui/Button";
import AnnotationSprite, { Annotation, AnnotationElement } from "./AnnotationSprite";
import AnnotationFactory from "./AnnotationFactory";
+import { EQuadrant } from "client/../../libs/ff-three/source/HTMLSprite";
////////////////////////////////////////////////////////////////////////////////
@@ -40,6 +41,8 @@ export default class ExtendedSprite extends AnnotationSprite
protected stemLine: Line;
protected quadrant = -1;
protected adaptive = true;
+ protected originalHeight;
+ protected originalWidth;
constructor(annotation: Annotation)
{
@@ -92,25 +95,73 @@ export default class ExtendedSprite extends AnnotationSprite
this.quadrant = this.orientationQuadrant;
}
- // update adaptive width
+ // update adaptive settings
if(this.adaptive !== this.isAdaptive) {
if(this.isAdaptive) {
element.classList.remove("sv-static-width");
}
else {
element.classList.add("sv-static-width");
+ element.truncated = false;
+ element.classList.remove("sv-short");
+ element.requestUpdate();
}
this.adaptive = this.isAdaptive;
}
// don't show if behind the camera
this.setVisible(!this.isBehindCamera(this.stemLine, camera));
+
+ // check if annotation is out of bounds and update if needed
+ if (this.adaptive && !this.isAnimating && this.annotation.data.expanded) {
+
+ if(!element.truncated) {
+ if(!element.classList.contains("sv-expanded")) {
+ element.requestUpdate().then(() => {
+ this.originalHeight = element.offsetHeight;
+ this.originalWidth = element.offsetWidth;
+ this.checkTruncate(element, container);
+ });
+ return;
+ }
+ else {
+ this.originalHeight = element.offsetHeight;
+ this.originalWidth = element.offsetWidth;
+ }
+ }
+
+ this.checkTruncate(element, container);
+ }
}
protected createHTMLElement(): ExtendedAnnotation
{
return new ExtendedAnnotation(this);
}
+
+ // Helper function to check if annotation should truncate
+ protected checkTruncate(element: AnnotationElement, container: HTMLElement) {
+ const top = this.quadrant == EQuadrant.TopLeft || this.quadrant == EQuadrant.TopRight;
+ const right = this.quadrant == EQuadrant.TopRight || this.quadrant == EQuadrant.BottomRight;
+ const x = right ? element.getBoundingClientRect().left - container.getBoundingClientRect().left
+ : element.getBoundingClientRect().right - container.getBoundingClientRect().left;
+ const y = top ? element.getBoundingClientRect().bottom - container.getBoundingClientRect().top
+ : element.getBoundingClientRect().top - container.getBoundingClientRect().top;
+
+ const shouldTruncateVert = !top ? y + this.originalHeight >= container.offsetHeight : y - this.originalHeight <= 0;
+ const shouldTruncateHoriz = right ? x + this.originalWidth >= container.offsetWidth : x - this.originalWidth <= 0;
+ const shouldTruncate = shouldTruncateVert || shouldTruncateHoriz;
+ if(shouldTruncate !== element.truncated) {
+ element.truncated = shouldTruncate;
+ shouldTruncate ? element.classList.add("sv-short") : element.classList.remove("sv-short");
+ element.requestUpdate().then(() => {
+ //this.checkBounds(element, container);
+ });
+ }
+ else {
+ //this.checkBounds(element, container);
+ }
+ }
}
AnnotationFactory.registerType(ExtendedSprite);
@@ -123,7 +174,7 @@ class ExtendedAnnotation extends AnnotationElement
protected titleElement: HTMLDivElement;
protected contentElement: HTMLDivElement;
protected wrapperElement: HTMLDivElement;
- protected handler = 0;
+ //protected handler = 0;
protected isExpanded = undefined;
constructor(sprite: AnnotationSprite)
@@ -134,6 +185,7 @@ class ExtendedAnnotation extends AnnotationElement
this.onClickArticle = this.onClickArticle.bind(this);
this.onClickAudio = this.onClickAudio.bind(this);
this.onKeyDown = this.onKeyDown.bind(this);
+ this.onClickOverlay = this.onClickOverlay.bind(this);
this.titleElement = this.appendElement("div");
this.titleElement.classList.add("sv-title");
@@ -161,15 +213,18 @@ class ExtendedAnnotation extends AnnotationElement
const annotationObj = this.sprite.annotation;
const annotation = this.sprite.annotation.data;
const audio = this.sprite.audioManager;
+ const isTruncated = !this.overlayed && this.truncated
+ && (annotation.imageUri || annotation.articleId || annotationObj.lead.length > 0); // make sure we have content to truncate;
// update title
this.titleElement.innerText = this.sprite.annotation.title;
const contentTemplate = html`
- ${annotation.imageUri ? html`${annotationObj.imageCredit ? html`
${annotationObj.imageCredit}
` : null}
` : null}
- ${unsafeHTML(annotationObj.lead)}
- ${annotation.audioId ? html`` : null}
- ${annotation.articleId ? html`` : null}`;
+ ${annotation.imageUri && !isTruncated ? html`${annotationObj.imageCredit ? html`
${annotationObj.imageCredit}
` : null}
` : null}
+ ${!isTruncated ? html`${unsafeHTML(annotationObj.lead)}
` : null}
+ ${annotation.audioId && !this.overlayed ? html`` : null}
+ ${annotation.articleId && !isTruncated ? html`` : null}
+ ${isTruncated ? html`` : null}`;
render(contentTemplate, this.contentElement);
@@ -183,10 +238,10 @@ class ExtendedAnnotation extends AnnotationElement
}
// update expanded/collapsed
- if (this.isExpanded !== annotation.expanded) {
+ if (this.isExpanded !== annotation.expanded && !this.overlayed) {
this.isExpanded = annotation.expanded;
- window.clearTimeout(this.handler);
+ //window.clearTimeout(this.handler);
if (this.isExpanded) {
if(annotation.audioId) {
@@ -195,19 +250,31 @@ class ExtendedAnnotation extends AnnotationElement
this.classList.add("sv-expanded");
this.style.minWidth = this.sprite.annotation.lead.length < 40 && (!annotation.audioId || annotation.audioId.length == 0) ? "0" : "";
- this.contentElement.style.display = "inherit";
- this.contentElement.style.height = this.contentElement.scrollHeight + "px";
+ this.contentElement.style.display = "block";
+ this.contentElement.style.height = "auto"; //this.contentElement.scrollHeight + "px";
}
else {
this.classList.remove("sv-expanded");
this.contentElement.style.height = "0";
- this.handler = window.setTimeout(() => this.contentElement.style.display = "none", 300);
+ //this.handler = window.setTimeout(() => this.contentElement.style.display = "none", 300);
+ this.contentElement.style.display = "none";
if(audio.activeId == annotation.audioId) {
this.sprite.audioManager.stop();
}
}
}
+
+ const audioView = this.querySelector(".sv-audio-view");
+ if(annotation.audioId && !this.overlayed) {
+ if(annotation.expanded && !audioView) {
+ const audioContainer = this.querySelector("#audio_container");
+ audioContainer.append(audio.getPlayerById(annotation.audioId));
+ }
+ else if(!annotation.expanded && audioView && audio.activeId == annotation.audioId) {
+ audio.stop();
+ }
+ }
}
protected onClickTitle(event: MouseEvent)
@@ -225,7 +292,14 @@ class ExtendedAnnotation extends AnnotationElement
protected onClickAudio(event: MouseEvent)
{
event.stopPropagation();
- this.sprite.emitClickEvent();
+ }
+
+ protected onClickOverlay(event: MouseEvent)
+ {
+ event.stopPropagation();
+ const content = this.contentElement;
+ this.overlayed = true;
+ this.showOverlay(content);
}
protected onKeyDown(event: KeyboardEvent)
diff --git a/source/client/components/CVAnnotationView.ts b/source/client/components/CVAnnotationView.ts
index d79f1740..1fc550a2 100755
--- a/source/client/components/CVAnnotationView.ts
+++ b/source/client/components/CVAnnotationView.ts
@@ -46,6 +46,7 @@ import CVAssetReader from "./CVAssetReader";
import CVAudioManager from "./CVAudioManager";
import CVAssetManager from "./CVAssetManager";
import CVSnapshots from "./CVSnapshots";
+import CPulse from "client/../../libs/ff-graph/source/components/CPulse";
////////////////////////////////////////////////////////////////////////////////
@@ -92,6 +93,9 @@ export default class CVAnnotationView extends CObject3D
private _viewports = new Set();
private _sprites: Dictionary = {};
+ private _truncateLock = false;
+ private _activeView = false;
+
protected get model() {
return this.getComponent(CVModel2);
}
@@ -144,6 +148,12 @@ export default class CVAnnotationView extends CObject3D
if (annotation) {
annotation.set("expanded", true);
this.updateSprite(annotation);
+
+ // need to lock truncation checking during a tween
+ if(this._activeView) {
+ this._truncateLock = true;
+ this._activeView = false;
+ }
}
const ins = this.ins;
@@ -315,6 +325,16 @@ export default class CVAnnotationView extends CObject3D
const spriteGroup = this.object3D as HTMLSpriteGroup;
spriteGroup.render(viewport.overlay, context.camera);
+
+ // Handle locking truncation for view animation only after
+ // the sprite has a chance to do an initial update.
+ if(this._truncateLock) {
+ const annotation = this.activeAnnotation.data;
+ const sprite = this._sprites[annotation.id] as AnnotationSprite;
+ sprite.isAnimating = true;
+ this.snapshots.outs.tweening.once("value", () => { sprite.isAnimating = false; }, this);
+ this._truncateLock = false;
+ }
}
dispose()
@@ -473,11 +493,21 @@ export default class CVAnnotationView extends CObject3D
{
this.emit(event);
+ // start view animation if it exists
const annotation = event.annotation;
- if(annotation && annotation.data.viewId.length) {
+ if(annotation && annotation.data.viewId.length && !this.arManager.outs.isPresenting.value) {
this.normalizeViewOrbit(annotation.data.viewId);
- this.snapshots.ins.id.setValue(annotation.data.viewId);
- this.snapshots.ins.tween.set();
+
+ // If activeAnnotation is being tracked, make sure it is set
+ const activeIdx = this.snapshots.getTargetProperties().findIndex(prop => prop.name == "ActiveId");
+ if(activeIdx >= 0) {
+ const viewState = this.snapshots.getState(annotation.data.viewId);
+ viewState.values[activeIdx] = annotation.data.id;
+ }
+
+ const pulse = this.getMainComponent(CPulse);
+ this.snapshots.tweenTo(annotation.data.viewId, pulse.context.secondsElapsed);
+ this._activeView = true;
}
}
@@ -574,9 +604,12 @@ export default class CVAnnotationView extends CObject3D
const orbitIdx = this.snapshots.getTargetProperties().findIndex(prop => prop.name == "Orbit");
const viewState = this.snapshots.getState(viewId);
const currentOrbit = this.snapshots.getCurrentValues()[orbitIdx];
+ let angleOffset = 0;
currentOrbit.forEach((n, i) => {
const mult = Math.round((n-viewState.values[orbitIdx][i])/360);
- viewState.values[orbitIdx][i] += 360*mult;
+ viewState.values[orbitIdx][i] += 360*mult;
+ angleOffset += Math.abs(n-viewState.values[orbitIdx][i]);
});
+ viewState.duration = angleOffset > 0.01 ? 1.0 : 0; // don't animate if we are already there
}
}
\ No newline at end of file
diff --git a/source/client/components/CVCamera.ts b/source/client/components/CVCamera.ts
index 5d69be4a..0b43812e 100644
--- a/source/client/components/CVCamera.ts
+++ b/source/client/components/CVCamera.ts
@@ -16,7 +16,7 @@
*/
import CCamera, { EProjection } from "@ff/scene/components/CCamera";
-import { Node } from "@ff/scene/components/CObject3D";
+import { Node, types } from "@ff/scene/components/CObject3D";
import { IDocument, INode, ICamera } from "client/schema/document";
@@ -29,6 +29,12 @@ export default class CVCamera extends CCamera
static readonly text: string = "Camera";
static readonly icon: string = "video";
+ protected static readonly cameraAddIns = {
+ autoNearFar: types.Boolean("Frustum.AutoNearFar", true),
+ };
+
+ addIns = this.addInputs(CVCamera.cameraAddIns);
+
get settingProperties() {
return [
this.ins.projection,
@@ -36,6 +42,7 @@ export default class CVCamera extends CCamera
this.ins.size,
this.ins.near,
this.ins.far,
+ this.addIns.autoNearFar
]
}
@@ -53,6 +60,10 @@ export default class CVCamera extends CCamera
const data = document.cameras[node.camera];
+ if(data.autoNearFar != undefined) {
+ this.addIns.autoNearFar.setValue(data.autoNearFar);
+ }
+
if (data.type === "perspective") {
this.ins.copyValues({
projection: EProjection.Perspective,
@@ -96,6 +107,8 @@ export default class CVCamera extends CCamera
}
}
+ data.autoNearFar = this.addIns.autoNearFar.value;
+
document.cameras = document.cameras || [];
const cameraIndex = document.cameras.length;
document.cameras.push(data);
diff --git a/source/client/components/CVDocument.ts b/source/client/components/CVDocument.ts
index fa8f151c..ed1954c8 100755
--- a/source/client/components/CVDocument.ts
+++ b/source/client/components/CVDocument.ts
@@ -57,6 +57,7 @@ export default class CVDocument extends CRenderGraph
protected static readonly validator = new DocumentValidator();
protected titles: Dictionary = {};
+ protected intros: Dictionary = {};
protected meta: CVMeta = null;
protected static readonly ins = {
@@ -64,11 +65,13 @@ export default class CVDocument extends CRenderGraph
dumpTree: types.Event("Document.DumpTree"),
download: types.Event("Document.Download"),
title: types.String("Document.Title"),
+ intro: types.String("Document.Intro", ""),
};
protected static readonly outs = {
assetPath: types.AssetPath("Asset.Path", { preset: "scene.svx.json" }),
title: types.String("Document.Title"),
+ intro: types.String("Document.Intro", ""),
};
ins = this.addInputs(CVDocument.ins);
@@ -112,12 +115,12 @@ export default class CVDocument extends CRenderGraph
{
super.create();
this.innerGraph.components.on(CVMeta, this.onMetaComponent, this);
- this.setup.language.outs.language.on("value", this.updateTitle, this);
+ this.setup.language.outs.language.on("value", this.onLanguageUpdate, this);
}
dispose()
{
- this.setup.language.outs.language.off("value", this.updateTitle, this);
+ this.setup.language.outs.language.off("value", this.onLanguageUpdate, this);
this.innerGraph.components.off(CVMeta, this.onMetaComponent, this);
super.dispose();
}
@@ -151,10 +154,18 @@ export default class CVDocument extends CRenderGraph
if(ins.title.value) {
this.titles[ELanguageType[language.outs.language.value]] = ins.title.value;
- this.updateTitlesMeta();
+ this.updateMeta();
}
}
+ if(ins.intro.changed && this.intros) {
+ const language = this.setup.language;
+ outs.intro.setValue(ins.intro.value);
+
+ this.intros[ELanguageType[language.outs.language.value]] = ins.intro.value;
+ this.updateMeta();
+ }
+
return true;
}
@@ -284,6 +295,7 @@ export default class CVDocument extends CRenderGraph
{
const meta = event.object;
const propTitle = this.ins.title;
+ const propIntro = this.ins.intro;
const language = this.setup.language;
if(this.meta === null) {
@@ -293,6 +305,7 @@ export default class CVDocument extends CRenderGraph
if (event.add && !propTitle.value) {
meta.once("load", () => {
this.titles = meta.collection.get("titles") || {};
+ this.intros = meta.collection.get("intros") || {};
// TODO: Temporary - remove when single string properties are phased out
if(Object.keys(this.titles).length === 0) {
@@ -302,23 +315,28 @@ export default class CVDocument extends CRenderGraph
const title = this.titles[ELanguageType[language.outs.language.value]];
propTitle.setValue(title);
+ const intro = this.intros[ELanguageType[language.outs.language.value]] || "";
+ propIntro.setValue(intro);
this.analytics.setTitle(title);
this.meta = meta;
});
}
}
- protected updateTitle() {
+ protected onLanguageUpdate() {
const language = this.setup.language;
const newTitle = this.titles[ELanguageType[language.outs.language.value]];
this.ins.title.setValue(newTitle);
+ const newIntro = this.intros[ELanguageType[language.outs.language.value]];
+ this.ins.intro.setValue(newIntro);
}
- protected updateTitlesMeta() {
+ protected updateMeta() {
const meta = this.meta;
if(meta) {
- meta.collection.dictionary["titles"] = this.titles;
+ meta.collection.dictionary["titles"] = this.titles;
+ meta.collection.dictionary["intros"] = this.intros;
}
}
}
\ No newline at end of file
diff --git a/source/client/components/CVGrid.ts b/source/client/components/CVGrid.ts
index 1ef8ba02..c4a8ce49 100644
--- a/source/client/components/CVGrid.ts
+++ b/source/client/components/CVGrid.ts
@@ -99,6 +99,7 @@ export default class CVGrid extends CObject3D
this.tape.ins.startPosition.setValue([0,0,0]);
this.tape.ins.endPosition.setValue([0,0,0]);
this.tape.ins.visible.setValue(false);
+ this.tape.addTag("no_settings"); // hack to exclude from scene settings
super.create();
}
diff --git a/source/client/components/CVModel2.ts b/source/client/components/CVModel2.ts
index 8fecd140..bdb0b4d5 100644
--- a/source/client/components/CVModel2.ts
+++ b/source/client/components/CVModel2.ts
@@ -700,7 +700,8 @@ export default class CVModel2 extends CObject3D
return derivative.load(this.assetReader)
.then(() => {
- if (!derivative.model || !this.node) {
+ if (!derivative.model || !this.node ||
+ (this._activeDerivative && derivative.data.quality != this.ins.quality.value)) {
derivative.unload();
return;
}
diff --git a/source/client/components/CVScene.ts b/source/client/components/CVScene.ts
index 92eac59c..29e6dd9c 100644
--- a/source/client/components/CVScene.ts
+++ b/source/client/components/CVScene.ts
@@ -266,11 +266,13 @@ export default class CVScene extends CVNode
}
this.cameras.forEach(camera => {
- const far = 4 * Math.max(orbitRadius, this.outs.boundingRadius.value);
- const near = Math.min(far / 1000.0, this.outs.boundingRadius.value / 100.0);
- if(far < camera.ins.far.value || camera.ins.far.value < 2*this.setup.navigation.ins.maxOffset.value[2]) {
- camera.ins.far.setValue(far);
- camera.ins.near.setValue(near);
+ if(camera.addIns.autoNearFar.value) {
+ const far = 4 * Math.max(orbitRadius, this.outs.boundingRadius.value);
+ const near = Math.min(far / 1000.0, this.outs.boundingRadius.value / 100.0);
+ if(far < camera.ins.far.value || camera.ins.far.value < 2*this.setup.navigation.ins.maxOffset.value[2]) {
+ camera.ins.far.setValue(far);
+ camera.ins.near.setValue(near);
+ }
}
});
}
diff --git a/source/client/io/ModelReader.ts b/source/client/io/ModelReader.ts
index fcf02bcc..a0085240 100644
--- a/source/client/io/ModelReader.ts
+++ b/source/client/io/ModelReader.ts
@@ -118,6 +118,12 @@ export default class ModelReader
const uberMat = material.type === "MeshPhysicalMaterial" ? new UberPBRAdvMaterial() : new UberPBRMaterial();
+ if (material.flatShading) {
+ mesh.geometry.computeVertexNormals();
+ material.flatShading = false;
+ console.warn("Normals unavailable so they have been calculated. For best outcomes, please provide normals with geometry.");
+ }
+
// copy properties from previous material
if (material.type === "MeshPhysicalMaterial" || material.type === "MeshStandardMaterial") {
uberMat.copy(material);
diff --git a/source/client/models/Annotation.ts b/source/client/models/Annotation.ts
index ec7c5b9a..7200869c 100755
--- a/source/client/models/Annotation.ts
+++ b/source/client/models/Annotation.ts
@@ -228,7 +228,7 @@ export default class Annotation extends Document
}
const color = data.color;
- if (color && (color[0] !== 1 || color[1] !== 1 || color[2] !== 1)) {
+ if (color && (color[0] !== 0 || color[1] !== 0.61 || color[2] !== 0.87)) {
json.color = color.slice();
}
diff --git a/source/client/schema/document.ts b/source/client/schema/document.ts
index 2220bcf3..7b0d5e03 100644
--- a/source/client/schema/document.ts
+++ b/source/client/schema/document.ts
@@ -92,6 +92,7 @@ export interface ICamera
type: TCameraType;
perspective?: IPerspectiveCameraProps;
orthographic?: IOrthographicCameraProps;
+ autoNearFar?: boolean;
}
/**
diff --git a/source/client/schema/json/document.schema.json b/source/client/schema/json/document.schema.json
index 238b6aaf..a5604026 100644
--- a/source/client/schema/json/document.schema.json
+++ b/source/client/schema/json/document.schema.json
@@ -188,13 +188,17 @@
"znear",
"zfar"
]
+ },
+ "autoNearFar": {
+ "type": "boolean",
+ "default": true
}
},
"required": [
"type"
],
"not": {
- "required": [ "perspective", "orthographic" ]
+ "required": [ "perspective", "orthographic", "autoNearFar" ]
}
},
diff --git a/source/client/schema/json/meta.schema.json b/source/client/schema/json/meta.schema.json
index 9181d1ba..38de3289 100644
--- a/source/client/schema/json/meta.schema.json
+++ b/source/client/schema/json/meta.schema.json
@@ -78,6 +78,10 @@
"description": "Array of tags, categorizing the annotation with language key.",
"type": "object"
},
+ "intros": {
+ "description": "Introductory splash screen text with language key.",
+ "type": "object"
+ },
"uri": {
"description": "Location of the article resource, absolute URL or path relative to this document",
"type": "string",
diff --git a/source/client/schema/meta.ts b/source/client/schema/meta.ts
index fc346b0e..56183c14 100644
--- a/source/client/schema/meta.ts
+++ b/source/client/schema/meta.ts
@@ -61,6 +61,7 @@ export interface IArticle
leads?: Dictionary;
tags?: string[];
taglist?: Dictionary;
+ intros?: Dictionary;
mimeType?: string;
thumbnailUri?: string;
diff --git a/source/client/ui/SceneView.ts b/source/client/ui/SceneView.ts
index 3b355e38..d58d4ced 100644
--- a/source/client/ui/SceneView.ts
+++ b/source/client/ui/SceneView.ts
@@ -86,6 +86,7 @@ export default class SceneView extends SystemView
this.setAttribute("touch-action", "none");
this.tabIndex = 0;
+ this.id = "sv-scene"
this.ariaLabel = "Interactive 3D Model. Use mouse, touch, or arrow keys to rotate.";
this.setAttribute("role", "application"),
diff --git a/source/client/ui/explorer/AnnotationOverlay.ts b/source/client/ui/explorer/AnnotationOverlay.ts
new file mode 100644
index 00000000..7a05e200
--- /dev/null
+++ b/source/client/ui/explorer/AnnotationOverlay.ts
@@ -0,0 +1,136 @@
+/**
+ * 3D Foundation Project
+ * Copyright 2024 Smithsonian Institution
+ *
+ * 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.
+ */
+
+import Popup, { customElement, html } from "@ff/ui/Popup";
+
+import "@ff/ui/Button";
+import "@ff/ui/TextEdit";
+import {getFocusableElements, focusTrap} from "../../utils/focusHelpers";
+import AnnotationSprite from "client/annotations/AnnotationSprite";
+
+////////////////////////////////////////////////////////////////////////////////
+
+@customElement("sv-annotation-overlay")
+export default class AnnotationOverlay extends Popup
+{
+ protected content: HTMLElement = null;
+ protected sprite: AnnotationSprite = null;
+ protected resizeObserver: ResizeObserver = null;
+
+ static show(parent: HTMLElement, content: HTMLElement, sprite: AnnotationSprite): Promise
+ {
+ const popup = new AnnotationOverlay(content, sprite);
+ parent.appendChild(popup);
+
+ return new Promise((resolve, reject) => {
+ popup.on("close", () => resolve());
+ });
+ }
+
+ constructor( content: HTMLElement, sprite: AnnotationSprite )
+ {
+ super();
+
+ this.close = this.close.bind(this);
+
+ this.content = content;
+ this.title = sprite.annotation.title;
+ this.sprite = sprite;
+ this.position = "center";
+ this.modal = true;
+ }
+
+ close()
+ {
+ this.dispatchEvent(new CustomEvent("close"));
+ this.remove();
+ }
+
+ protected firstConnected()
+ {
+ super.firstConnected();
+ this.classList.add("sv-annotation-overlay", "sv-annotation");
+ }
+
+ protected connected()
+ {
+ super.connected();
+
+ this.sprite.addEventListener("link", this.close);
+
+ if(!this.resizeObserver) {
+ this.resizeObserver = new ResizeObserver(() => this.onResize());
+ }
+ this.resizeObserver.observe(this);
+ }
+
+ protected disconnected()
+ {
+ this.resizeObserver.disconnect();
+
+ this.sprite.removeEventListener("link", this.close);
+
+ super.disconnected();
+ }
+
+ protected render()
+ {
+ return html`
+ this.discardEvents(e)} @pointerdown=${(e) => this.discardEvents(e)} aria-label="Annotation pop-up" @keydown=${e =>this.onKeyDownMain(e)}>
+
+
+ `;
+ }
+
+ protected firstUpdated(changedProperties) {
+ super.firstUpdated(changedProperties);
+
+ const annoContainer = this.querySelector("#anno_container");
+ annoContainer.append(this.content);
+
+ //(Array.from(this.getElementsByClassName("sv-entry")).find(elem => elem.getAttribute("tabIndex") === "0") as HTMLElement).focus();
+ }
+
+ protected onKeyDownMain(e: KeyboardEvent)
+ {
+ if (e.code === "Escape") {
+ this.close();
+ }
+ else if(e.code === "Tab") {
+ focusTrap(getFocusableElements(this) as HTMLElement[], e);
+ }
+ }
+
+ // resets tabIndex if needed
+ protected tabReset(e: FocusEvent) {
+ const currentActive = e.target instanceof Element ? e.target as Element : null;
+ if(currentActive) {
+ const currentSelected = Array.from(currentActive.parentElement.children).find(elem => elem.hasAttribute("selected"));
+ if(currentSelected !== currentActive) {
+ currentActive.setAttribute("tabIndex", "-1");
+ currentSelected.setAttribute("tabIndex", "0");
+ }
+ }
+ }
+
+ protected discardEvents(event: PointerEvent | WheelEvent) {
+ event.stopPropagation();
+ }
+}
diff --git a/source/client/ui/explorer/ChromeView.ts b/source/client/ui/explorer/ChromeView.ts
index c9f040de..1effb067 100644
--- a/source/client/ui/explorer/ChromeView.ts
+++ b/source/client/ui/explorer/ChromeView.ts
@@ -36,6 +36,7 @@ import DocumentView, { customElement, html } from "./DocumentView";
import LanguageMenu from "./LanguageMenu";
import { EUIElements } from "client/components/CVInterface";
import CVAssetReader from "client/components/CVAssetReader";
+import SplashScreen from "./SplashScreen";
////////////////////////////////////////////////////////////////////////////////
@@ -45,6 +46,7 @@ export default class ChromeView extends DocumentView
protected documentProps = new Subscriber("value", this.onUpdate, this);
protected titleElement: HTMLDivElement;
protected assetPath: string = "";
+ protected needsSplash: boolean = true;
protected get toolProvider() {
return this.system.getMainComponent(CVToolProvider);
@@ -127,6 +129,14 @@ export default class ChromeView extends DocumentView
const showTourEndMsg = this.activeDocument.setup.tours.outs.ending.value;
this.activeDocument.setup.tours.outs.ending.setValue(false);
+ const introText = this.activeDocument.outs.intro.value;
+ if(this.needsSplash && introText && introText.length > 0) {
+ this.needsSplash = false;
+ SplashScreen.show(this, this.activeDocument.setup.language, introText).then(() => {
+ (this.getRootNode() as ShadowRoot).getElementById("sv-scene").focus();
+ });
+ }
+
if (!interfaceVisible) {
return html``;
}
@@ -173,6 +183,16 @@ export default class ChromeView extends DocumentView
`;
}
+ protected firstUpdated(_changedProperties: Map): void {
+ const introText = this.activeDocument.outs.intro.value;
+ if(this.needsSplash && introText.length > 0) {
+ this.needsSplash = false;
+ SplashScreen.show(this, this.activeDocument.setup.language, introText).then(() => {
+ //(this.querySelector("#main-help") as HTMLElement).focus();
+ });
+ }
+ }
+
protected onSelectTour(event: ITourMenuSelectEvent)
{
const tours = this.activeDocument.setup.tours;
diff --git a/source/client/ui/explorer/SplashScreen.ts b/source/client/ui/explorer/SplashScreen.ts
new file mode 100644
index 00000000..bbb89237
--- /dev/null
+++ b/source/client/ui/explorer/SplashScreen.ts
@@ -0,0 +1,105 @@
+/**
+ * 3D Foundation Project
+ * Copyright 2023 Smithsonian Institution
+ *
+ * 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.
+ */
+
+import Popup, { customElement, html } from "@ff/ui/Popup";
+
+import "@ff/ui/Button";
+import CVLanguageManager from "client/components/CVLanguageManager";
+import {getFocusableElements, focusTrap} from "../../utils/focusHelpers";
+
+////////////////////////////////////////////////////////////////////////////////
+
+@customElement("sv-splash")
+export default class SplashScreen extends Popup
+{
+ protected url: string;
+ protected language: CVLanguageManager = null;
+ protected content: string = "";
+ protected contentElement: HTMLDivElement;
+
+
+ static show(parent: HTMLElement, language: CVLanguageManager, content: string): Promise
+ {
+ const screen = new SplashScreen(language, content);
+ parent.appendChild(screen);
+
+ return new Promise((resolve, reject) => {
+ screen.on("close", () => resolve());
+ });
+ }
+
+ constructor( language: CVLanguageManager, content: string )
+ {
+ super();
+
+ this.language = language;
+ this.content = content;
+ this.position = "center";
+ this.modal = true;
+
+ this.url = window.location.href;
+ }
+
+ close()
+ {
+ this.dispatchEvent(new CustomEvent("close"));
+ this.remove();
+ }
+
+ protected firstConnected()
+ {
+ super.firstConnected();
+ this.contentElement = this.createElement("div", null);
+ this.classList.add("sv-splash");
+ }
+
+ protected render()
+ {
+ const language = this.language;
+ const contentElement = this.contentElement;
+
+ contentElement.innerHTML = this.content;
+
+ return html`
+ this.onKeyDownMain(e)}>
+
+
${language.getLocalizedString("Welcome to Voyager")}
+
+
+
+ ${contentElement}
+
+
+ `;
+ }
+
+ protected firstUpdated(changedProperties) {
+ super.firstUpdated(changedProperties);
+
+ (this.querySelector("#main") as HTMLElement).focus();
+ }
+
+ protected onKeyDownMain(e: KeyboardEvent)
+ {
+ if (e.code === "Escape") {
+ this.close();
+ }
+ else if(e.code === "Tab") {
+ focusTrap(getFocusableElements(this) as HTMLElement[], e);
+ }
+ }
+}
diff --git a/source/client/ui/explorer/styles.scss b/source/client/ui/explorer/styles.scss
index fa7dd388..f1befa8f 100644
--- a/source/client/ui/explorer/styles.scss
+++ b/source/client/ui/explorer/styles.scss
@@ -360,6 +360,11 @@ $pad: $canvas-border-width + $main-menu-button-size + 8px;
z-index: 2;
}
+ &.sv-short {
+ width: unset;
+ min-width: min-content;
+ }
+
&.sv-q0 {
transform: translate(0, -100%);
border-bottom-style: solid;
@@ -391,6 +396,7 @@ $pad: $canvas-border-width + $main-menu-button-size + 8px;
.sv-title {
padding: 1px 0 2px 0;
font-weight: bold;
+ -webkit-tap-highlight-color: transparent;
}
.sv-content {
@@ -436,6 +442,27 @@ $pad: $canvas-border-width + $main-menu-button-size + 8px;
}
}
+.sv-annotation-overlay {
+ padding: 7px;
+ pointer-events: auto;
+ background-color: $menu-color-background;
+ max-width: #{"min(80%, 800px)"};
+ min-width: 50%;
+ max-height: 90%;
+ overflow-y: auto;
+ cursor: auto;
+
+ .sv-annotation-body {
+ .ff-button {
+ background: rgba(0, 0, 0, 0.01); // hack to get click events
+
+ &:hover {
+ text-decoration: underline;
+ }
+ }
+ }
+}
+
////////////////////////////////////////////////////////////////////////////////
// AR MODE
@@ -1390,11 +1417,27 @@ $tour-entry-indent: 12px;
max-width: 85%;
}
+.sv-splash {
+ padding: 7px;
+ pointer-events: auto;
+ background-color: $menu-color-background;
+ max-width: #{"min(80%, 800px)"};
+ min-width: 50%;
+ max-height: 90%;
+ overflow-y: auto;
+
+ & > :focus-visible {
+ outline: none;
+ box-shadow: 0 0;
+ }
+}
+
.sv-audio-view {
display: flex;
align-items: center;
flex: 1 1 auto;
max-width: 100%;
+ min-width: 110px;
height: 30px;
background-color: $menu-color-text;
border-radius: 15px;
diff --git a/source/client/ui/story/CollectionPanel.ts b/source/client/ui/story/CollectionPanel.ts
index 3f679b07..fee6854c 100644
--- a/source/client/ui/story/CollectionPanel.ts
+++ b/source/client/ui/story/CollectionPanel.ts
@@ -53,6 +53,8 @@ export default class CollectionPanel extends DocumentView
`;
}
@@ -71,6 +73,13 @@ export default class CollectionPanel extends DocumentView
}
));
}
+ else if (target.name === "intro") {
+ activeDoc.ins.intro.setValue(sanitizeHtml(text,
+ {
+ allowedTags: [ 'i','b','p' ]
+ }
+ ));
+ }
}
}
diff --git a/source/client/ui/story/SettingsTaskView.ts b/source/client/ui/story/SettingsTaskView.ts
index a662d658..f7b5f9e9 100644
--- a/source/client/ui/story/SettingsTaskView.ts
+++ b/source/client/ui/story/SettingsTaskView.ts
@@ -100,7 +100,7 @@ export class SettingsTree extends Tree
protected createNodeTreeNode(node: Node): ITreeNode
{
- const components = node.components.getArray().filter(component => component["settingProperties"]);
+ const components = node.components.getArray().filter(component => component["settingProperties"] && !component.tags.has("no_settings"));
return {
id: node.id,