janusweb 1.5.32 → 1.5.36
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.
- package/CHANGELOG +39 -0
- package/media/assets/webui/apps/avatar/avatar.js +2 -1
- package/media/assets/webui/apps/buttons/buttons.html +2 -2
- package/media/assets/webui/apps/buttons/buttons.js +2 -2
- package/media/assets/webui/apps/comms/voip.js +2 -4
- package/media/assets/webui/apps/editor/codemirror/addon/hint/javascript-hint.js +162 -0
- package/media/assets/webui/apps/editor/codemirror/addon/hint/show-hint.css +36 -0
- package/media/assets/webui/apps/editor/codemirror/addon/hint/show-hint.js +529 -0
- package/media/assets/webui/apps/editor/codemirror/addon/hint/xml-hint.js +140 -0
- package/media/assets/webui/apps/editor/codemirror/keymap/vim.js +5734 -0
- package/media/assets/webui/apps/editor/codemirror/lib/codemirror.css +350 -0
- package/media/assets/webui/apps/editor/codemirror/lib/codemirror.js +9800 -0
- package/media/assets/webui/apps/editor/codemirror/lib/jmldark.css +33 -0
- package/media/assets/webui/apps/editor/codemirror/mode/css/css.js +864 -0
- package/media/assets/webui/apps/editor/codemirror/mode/htmlmixed/htmlmixed.js +153 -0
- package/media/assets/webui/apps/editor/codemirror/mode/javascript/javascript.js +942 -0
- package/media/assets/webui/apps/editor/codemirror/mode/xml/xml.js +413 -0
- package/media/assets/webui/apps/editor/editor.css +3 -0
- package/media/assets/webui/apps/editor/editor.js +407 -9
- package/media/assets/webui/apps/editor/editor.json +6 -3
- package/media/assets/webui/apps/inventory/inventory.js +2 -2
- package/media/assets/webui/apps/locomotion/teleporter.js +8 -7
- package/media/assets/webui/apps/navigation/navigation.js +12 -12
- package/media/assets/webui/apps/vfs/browserfs/browserfs.js +8 -0
- package/media/assets/webui/apps/vfs/vfs.css +52 -0
- package/media/assets/webui/apps/vfs/vfs.js +452 -0
- package/media/assets/webui/apps/vfs/vfs.json +10 -0
- package/media/assets/webui/default.json +2 -1
- package/media/assets/webui/themes/default.css +29 -2
- package/media/lib/physx/physx.release.js +21 -0
- package/media/lib/physx/physx.release.wasm +0 -0
- package/media/manifest.json +5 -5
- package/media/service-worker.js +7 -0
- package/package.json +2 -9
- package/scripts/client.js +65 -45
- package/scripts/config.js +2 -0
- package/scripts/elements/linesegments.js +5 -2
- package/scripts/image.js +7 -1
- package/scripts/janusbase.js +28 -11
- package/scripts/janusghost.js +4 -3
- package/scripts/janusparagraph.js +5 -3
- package/scripts/janusparticle.js +25 -14
- package/scripts/janusplayer.js +25 -10
- package/scripts/janusweb.js +22 -3
- package/scripts/janusxrplayer.js +4 -1
- package/scripts/multiplayermanager.js +3 -3
- package/scripts/object.js +172 -82
- package/scripts/portal.js +12 -10
- package/scripts/room.js +113 -54
- package/scripts/sound.js +0 -1
- package/scripts/text.js +10 -2
- package/scripts/translators/janusvfs.js +20 -0
- package/scripts/video.js +1 -1
- package/scripts/websurface.js +11 -2
- package/utils/build.sh +3 -0
- package/utils/init.sh +1 -1
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
janus-ui-vfs-browser ui-flexpanel>ui-treeview {
|
|
2
|
+
flex: 0;
|
|
3
|
+
border-right: 1px solid #333;
|
|
4
|
+
margin-right: 1em;
|
|
5
|
+
padding-right: 1em;
|
|
6
|
+
}
|
|
7
|
+
janus-ui-vfs-browser-directorylist {
|
|
8
|
+
display: grid;
|
|
9
|
+
grid-template-columns: repeat(auto-fit, 90px);
|
|
10
|
+
width: 40em;
|
|
11
|
+
height: 15em;
|
|
12
|
+
overflow: auto;
|
|
13
|
+
outline: 0;
|
|
14
|
+
}
|
|
15
|
+
janus-ui-vfs-browser-directorylist.dragactive {
|
|
16
|
+
border: 2px solid lime;
|
|
17
|
+
border-radius: 10px;
|
|
18
|
+
background: rgba(0,255,0,.2);
|
|
19
|
+
margin: -2px;
|
|
20
|
+
}
|
|
21
|
+
janus-ui-vfs-browser-file.dragactive {
|
|
22
|
+
border: 2px solid lime;
|
|
23
|
+
background: rgba(0,255,0,.2);
|
|
24
|
+
}
|
|
25
|
+
janus-ui-vfs-browser-file {
|
|
26
|
+
width: 80px;
|
|
27
|
+
height: 80px;
|
|
28
|
+
border: 1px solid black;
|
|
29
|
+
border-radius: 10px;
|
|
30
|
+
margin: 5px;
|
|
31
|
+
display: flex;
|
|
32
|
+
text-align: center;
|
|
33
|
+
align-items: flex-end;
|
|
34
|
+
justify-content: center;
|
|
35
|
+
cursor: pointer;
|
|
36
|
+
background: rgba(0,0,0,.2);
|
|
37
|
+
}
|
|
38
|
+
janus-ui-vfs-browser-file[selected] {
|
|
39
|
+
background: rgba(255,255,255,.2);
|
|
40
|
+
}
|
|
41
|
+
janus-ui-vfs-browser-file:hover {
|
|
42
|
+
background: rgba(0,255,0,.2);
|
|
43
|
+
}
|
|
44
|
+
janus-ui-vfs-browser-file[selected]:hover {
|
|
45
|
+
background: rgba(255,255,255,.4);
|
|
46
|
+
}
|
|
47
|
+
janus-ui-vfs-browser-file ui-label {
|
|
48
|
+
width: 100%;
|
|
49
|
+
overflow: hidden;
|
|
50
|
+
text-overflow: ellipsis;
|
|
51
|
+
pointer-events: none;
|
|
52
|
+
}
|
|
@@ -0,0 +1,452 @@
|
|
|
1
|
+
elation.extend('engine.assetloader.agent.janus-vfs', new function() {
|
|
2
|
+
this.initBrowserFS = function() {
|
|
3
|
+
return new Promise((accept, reject) => {
|
|
4
|
+
BrowserFS.configure({
|
|
5
|
+
fs: "MountableFileSystem",
|
|
6
|
+
options: {
|
|
7
|
+
//'/': { fs: "LocalStorage" },
|
|
8
|
+
//'/blurp': { fs: "IndexedDB", options: { storeName: 'janusVFS' } },
|
|
9
|
+
'/': { fs: "IndexedDB", options: { storeName: 'janusVFS' } },
|
|
10
|
+
//'/mnt/usb0': { fs: "LocalStorage" }
|
|
11
|
+
}
|
|
12
|
+
}, (e) => {
|
|
13
|
+
if (e) {
|
|
14
|
+
} else {
|
|
15
|
+
var fs = BrowserFS.BFSRequire('fs');
|
|
16
|
+
this.fs = fs;
|
|
17
|
+
accept(this.fs);
|
|
18
|
+
}
|
|
19
|
+
});
|
|
20
|
+
});
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
this.getFS = async function() {
|
|
24
|
+
if (!this.fs) {
|
|
25
|
+
await this.initBrowserFS();
|
|
26
|
+
}
|
|
27
|
+
return this.fs;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
this.fetch = async function(url) {
|
|
31
|
+
let fs = await this.getFS();
|
|
32
|
+
|
|
33
|
+
let pathparts = url.split('://');
|
|
34
|
+
return new Promise((resolve, reject) => {
|
|
35
|
+
let filepath = pathparts[1];
|
|
36
|
+
fs.readFile(filepath, ( e, file) => {
|
|
37
|
+
if (file) {
|
|
38
|
+
resolve({
|
|
39
|
+
target: {
|
|
40
|
+
responseURL: url,
|
|
41
|
+
data: file.buffer,
|
|
42
|
+
response: file.buffer,
|
|
43
|
+
getResponseHeader(header) {
|
|
44
|
+
if (header.toLowerCase() == 'content-type') {
|
|
45
|
+
// FIXME - We should implement mime type detection at a lower level, and avoid the need to access the XHR object in calling code
|
|
46
|
+
return 'application/octet-stream';
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
},
|
|
50
|
+
});
|
|
51
|
+
} else {
|
|
52
|
+
reject();
|
|
53
|
+
}
|
|
54
|
+
});
|
|
55
|
+
});
|
|
56
|
+
}
|
|
57
|
+
});
|
|
58
|
+
|
|
59
|
+
elation.elements.define('janus.ui.vfs.browser', class extends elation.elements.base {
|
|
60
|
+
init() {
|
|
61
|
+
super.init();
|
|
62
|
+
this.defineAttributes({
|
|
63
|
+
cwd: { type: 'string', default: '/' },
|
|
64
|
+
chroot: { type: 'string', default: '' },
|
|
65
|
+
});
|
|
66
|
+
}
|
|
67
|
+
create() {
|
|
68
|
+
super.create();
|
|
69
|
+
|
|
70
|
+
/*
|
|
71
|
+
this.win = elation.elements.create('ui-window', {
|
|
72
|
+
append: document.body,
|
|
73
|
+
center: true,
|
|
74
|
+
windowtitle: '/',
|
|
75
|
+
});
|
|
76
|
+
*/
|
|
77
|
+
this.content = elation.elements.create('ui-rowlayout', {
|
|
78
|
+
append: this,
|
|
79
|
+
});
|
|
80
|
+
this.controls = elation.elements.create('ui-content', {
|
|
81
|
+
append: this.content,
|
|
82
|
+
});
|
|
83
|
+
this.updirbutton = elation.elements.create('ui-button', {
|
|
84
|
+
append: this.controls,
|
|
85
|
+
label: '⬅',
|
|
86
|
+
onclick: ev => this.handleUpDirectoryClick(ev),
|
|
87
|
+
});
|
|
88
|
+
this.deletebutton = elation.elements.create('ui-button', {
|
|
89
|
+
append: this.controls,
|
|
90
|
+
label: '🗑️',
|
|
91
|
+
onclick: ev => this.handleDeleteClick(ev),
|
|
92
|
+
});
|
|
93
|
+
this.addbuttonlist = elation.elements.create('ui-list', {
|
|
94
|
+
selectable: true,
|
|
95
|
+
items: ['Upload', 'Directory', 'HTML', 'JS'],
|
|
96
|
+
onselectionchange: ev => this.handleAddButtonSelect(ev),
|
|
97
|
+
});
|
|
98
|
+
this.addbutton = elation.elements.create('ui-popupbutton', {
|
|
99
|
+
append: this.controls,
|
|
100
|
+
label: '➕',
|
|
101
|
+
popupcontent: this.addbuttonlist,
|
|
102
|
+
});
|
|
103
|
+
this.mainpanel = elation.elements.create('ui-flexpanel', {
|
|
104
|
+
append: this.content,
|
|
105
|
+
});
|
|
106
|
+
|
|
107
|
+
this.dirtree = elation.elements.create('ui-treeview', {
|
|
108
|
+
append: this.mainpanel,
|
|
109
|
+
attrs: {
|
|
110
|
+
name: 'path',
|
|
111
|
+
label: 'name',
|
|
112
|
+
},
|
|
113
|
+
});
|
|
114
|
+
elation.events.add(this.dirtree, 'ui_treeview_select', ev => this.handleDirectoryTreeSelect(ev));
|
|
115
|
+
|
|
116
|
+
this.dirlist = elation.elements.create('janus-ui-vfs-browser-directorylist', {
|
|
117
|
+
append: this.mainpanel,
|
|
118
|
+
itemcomponent: 'janus-ui-vfs-browser-file',
|
|
119
|
+
selectable: true,
|
|
120
|
+
multiselect: true,
|
|
121
|
+
draggable: true,
|
|
122
|
+
});
|
|
123
|
+
elation.events.add(this.dirlist, 'directorychange', ev => this.handleDirectoryChange(ev));
|
|
124
|
+
elation.events.add(this.dirlist, 'selectionchange', ev => this.handleFilesSelectionChange(ev));
|
|
125
|
+
|
|
126
|
+
elation.events.add(this.dirlist, 'dragstart', ev => this.handleDragStart(ev));
|
|
127
|
+
elation.events.add(this.dirlist, 'dragover', ev => this.handleDragOver(ev));
|
|
128
|
+
elation.events.add(this.dirlist, 'dragenter', ev => this.handleDragEnter(ev));
|
|
129
|
+
elation.events.add(this.dirlist, 'dragleave', ev => this.handleDragLeave(ev));
|
|
130
|
+
elation.events.add(this.dirlist, 'drop', ev => this.handleDrop(ev));
|
|
131
|
+
|
|
132
|
+
elation.events.add(this.dirlist, 'keypress', ev => this.handleFilesKeypress(ev));
|
|
133
|
+
|
|
134
|
+
//this.win.setcontent(this);
|
|
135
|
+
this.updateDirectories();
|
|
136
|
+
this.updateFiles();
|
|
137
|
+
}
|
|
138
|
+
async updateFiles() {
|
|
139
|
+
let fs = await elation.engine.assetloader.agent['janus-vfs'].getFS();
|
|
140
|
+
|
|
141
|
+
fs.readdir(this.chroot + this.cwd, (e, contents) => {
|
|
142
|
+
let entries = {};
|
|
143
|
+
let promises = [];
|
|
144
|
+
for (let i = 0; i < contents.length; i++) {
|
|
145
|
+
promises.push(new Promise(accept => {
|
|
146
|
+
fs.stat(this.chroot + this.cwd + contents[i], (e, stats) => {
|
|
147
|
+
entries[contents[i]] = {
|
|
148
|
+
name: contents[i],
|
|
149
|
+
path: this.cwd,
|
|
150
|
+
stats: stats,
|
|
151
|
+
};
|
|
152
|
+
accept(stats);
|
|
153
|
+
});
|
|
154
|
+
}));
|
|
155
|
+
}
|
|
156
|
+
Promise.all(promises).then(() => {
|
|
157
|
+
this.dirlist.clear();
|
|
158
|
+
this.dirlist.setItems(entries);
|
|
159
|
+
});
|
|
160
|
+
});
|
|
161
|
+
this.updirbutton.disabled = (this.cwd == '/');
|
|
162
|
+
this.deletebutton.disabled = (this.dirlist.selection.length == 0);
|
|
163
|
+
//this.win.settitle(this.cwd);
|
|
164
|
+
}
|
|
165
|
+
async updateDirectories() {
|
|
166
|
+
let dirtree = {
|
|
167
|
+
'/': {
|
|
168
|
+
name: '/',
|
|
169
|
+
path: '/',
|
|
170
|
+
items: await this.getDirectoryTree(),
|
|
171
|
+
}
|
|
172
|
+
};
|
|
173
|
+
this.dirtree.setItems(dirtree);
|
|
174
|
+
let selected = this.dirtree.find(this.cwd);
|
|
175
|
+
if (selected) {
|
|
176
|
+
selected.select();
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
console.log('my full dirtree', dirtree, this.dirtree, selected);
|
|
180
|
+
}
|
|
181
|
+
async getDirectoryTree(dir='/', entries) {
|
|
182
|
+
if (!entries) entries = {};
|
|
183
|
+
let fs = await elation.engine.assetloader.agent['janus-vfs'].getFS();
|
|
184
|
+
return new Promise(async (resolve, reject) => {
|
|
185
|
+
fs.readdir(this.chroot + dir, async (e, contents) => {
|
|
186
|
+
let promises = [];
|
|
187
|
+
for (let i = 0; i < contents.length; i++) {
|
|
188
|
+
promises.push(new Promise(accept => {
|
|
189
|
+
let stats = fs.stat(this.chroot + dir + contents[i], async (e, stats) => {
|
|
190
|
+
if (stats.isDirectory()) {
|
|
191
|
+
entries[contents[i]] = {
|
|
192
|
+
name: contents[i],
|
|
193
|
+
path: dir + contents[i] + '/',
|
|
194
|
+
stats: stats,
|
|
195
|
+
};
|
|
196
|
+
let children = await this.getDirectoryTree(dir + contents[i] + '/');
|
|
197
|
+
entries[contents[i]].items = (Object.keys(children).length > 0 ? children : false);
|
|
198
|
+
}
|
|
199
|
+
accept();
|
|
200
|
+
});
|
|
201
|
+
}));
|
|
202
|
+
}
|
|
203
|
+
await Promise.all(promises);
|
|
204
|
+
resolve(entries);
|
|
205
|
+
});
|
|
206
|
+
});
|
|
207
|
+
}
|
|
208
|
+
chdir(dir) {
|
|
209
|
+
this.cwd = dir + (dir.lastIndexOf('/') != dir.length - 1 ? '/' : '');
|
|
210
|
+
this.dirlist.selectall(false);
|
|
211
|
+
|
|
212
|
+
let selected = this.dirtree.find(this.cwd);
|
|
213
|
+
if (selected && selected !== this.dirtree.selected) {
|
|
214
|
+
selected.select();
|
|
215
|
+
} else {
|
|
216
|
+
this.updateFiles();
|
|
217
|
+
}
|
|
218
|
+
}
|
|
219
|
+
updir() {
|
|
220
|
+
let pathparts = this.cwd.split('/');
|
|
221
|
+
pathparts.pop();
|
|
222
|
+
pathparts.pop();
|
|
223
|
+
let newpath = (pathparts.length > 0 ? pathparts.join('/') + '/' : '/');
|
|
224
|
+
//newpath = newpath.replaceAll('//', '/');
|
|
225
|
+
this.chdir(newpath);
|
|
226
|
+
}
|
|
227
|
+
async mkdir(dir) {
|
|
228
|
+
let fs = await elation.engine.assetloader.agent['janus-vfs'].getFS();
|
|
229
|
+
let fulldir = this.chroot + (dir[0] == '/' ? dir : this.cwd + dir);
|
|
230
|
+
await fs.mkdir(fulldir);
|
|
231
|
+
this.updateDirectories();
|
|
232
|
+
this.updateFiles();
|
|
233
|
+
}
|
|
234
|
+
async rm(files) {
|
|
235
|
+
let fs = await elation.engine.assetloader.agent['janus-vfs'].getFS();
|
|
236
|
+
if (!elation.utils.isArray(files)) files = [files];
|
|
237
|
+
console.log('delete files', files);
|
|
238
|
+
let promises = [];
|
|
239
|
+
files.forEach(file => {
|
|
240
|
+
promises.push(new Promise((resolve, reject) => {
|
|
241
|
+
fs.unlink(this.chroot + this.cwd + file, e => {
|
|
242
|
+
if (!e) {
|
|
243
|
+
resolve();
|
|
244
|
+
} else {
|
|
245
|
+
reject();
|
|
246
|
+
}
|
|
247
|
+
});
|
|
248
|
+
}));
|
|
249
|
+
});
|
|
250
|
+
await Promise.all(promises);
|
|
251
|
+
this.updateFiles();
|
|
252
|
+
}
|
|
253
|
+
async rmdir(dirs) {
|
|
254
|
+
let fs = await elation.engine.assetloader.agent['janus-vfs'].getFS();
|
|
255
|
+
if (!elation.utils.isArray(dirs)) dirs = [dirs];
|
|
256
|
+
console.log('delete dirs', dirs);
|
|
257
|
+
let promises = [];
|
|
258
|
+
dirs.forEach(dir => {
|
|
259
|
+
promises.push(new Promise((resolve, reject) => {
|
|
260
|
+
fs.rmdir(this.chroot + this.cwd + dir, e => {
|
|
261
|
+
if (!e) {
|
|
262
|
+
resolve();
|
|
263
|
+
} else {
|
|
264
|
+
reject();
|
|
265
|
+
}
|
|
266
|
+
});
|
|
267
|
+
}));
|
|
268
|
+
});
|
|
269
|
+
await Promise.all(promises);
|
|
270
|
+
this.updateDirectories();
|
|
271
|
+
this.updateFiles();
|
|
272
|
+
}
|
|
273
|
+
showUploadPrompt() {
|
|
274
|
+
let input = document.createElement('input');
|
|
275
|
+
input.type = 'file';
|
|
276
|
+
input.multiple = true;
|
|
277
|
+
input.style.width = 0;
|
|
278
|
+
input.style.height = 0;
|
|
279
|
+
input.style.overflow = 'hidden';
|
|
280
|
+
input.addEventListener('change', ev => this.handleFileUpload(ev));
|
|
281
|
+
this.appendChild(input);
|
|
282
|
+
input.click();
|
|
283
|
+
}
|
|
284
|
+
handleDirectoryTreeSelect(ev) {
|
|
285
|
+
let newpath = ev.data.item.path;
|
|
286
|
+
this.chdir(newpath);
|
|
287
|
+
}
|
|
288
|
+
handleDirectoryChange(ev) {
|
|
289
|
+
let newdir = ev.target;
|
|
290
|
+
let newpath = newdir.path + newdir.name;
|
|
291
|
+
this.chdir(newpath);
|
|
292
|
+
}
|
|
293
|
+
handleUpDirectoryClick(ev) {
|
|
294
|
+
if (!ev.target.disabled) {
|
|
295
|
+
this.updir();
|
|
296
|
+
}
|
|
297
|
+
}
|
|
298
|
+
handleDeleteClick(ev) {
|
|
299
|
+
if (this.dirlist.selection.length > 0) {
|
|
300
|
+
console.log('delete', this.dirlist.selection);
|
|
301
|
+
let files = this.dirlist.selection.filter(n => !n.stats.isDirectory()).map(n => n.name),
|
|
302
|
+
dirs = this.dirlist.selection.filter(n => n.stats.isDirectory()).map(n => n.name);
|
|
303
|
+
if (files.length > 0) this.rm(files);
|
|
304
|
+
if (dirs.length > 0) this.rmdir(dirs);
|
|
305
|
+
this.dirlist.selectall(false);
|
|
306
|
+
}
|
|
307
|
+
}
|
|
308
|
+
handleAddButtonSelect(ev) {
|
|
309
|
+
let selected = this.addbuttonlist.selection[0];
|
|
310
|
+
if (selected) {
|
|
311
|
+
this.addbuttonlist.selectall(false);
|
|
312
|
+
this.addbutton.hidePopup();
|
|
313
|
+
|
|
314
|
+
switch(selected.value) {
|
|
315
|
+
case 'Upload':
|
|
316
|
+
this.showUploadPrompt();
|
|
317
|
+
break;
|
|
318
|
+
case 'Directory':
|
|
319
|
+
let dirname = prompt('Directory name?'); // FIXME - make this an HTML pop-up prompt
|
|
320
|
+
if (dirname !== null) {
|
|
321
|
+
this.mkdir(dirname);
|
|
322
|
+
}
|
|
323
|
+
}
|
|
324
|
+
}
|
|
325
|
+
}
|
|
326
|
+
handleFilesSelectionChange(ev) {
|
|
327
|
+
this.deletebutton.disabled = (this.dirlist.selection.length == 0);
|
|
328
|
+
}
|
|
329
|
+
handleFilesKeypress(ev) {
|
|
330
|
+
if (ev.keyCode == 127) {
|
|
331
|
+
this.handleDeleteClick(ev);
|
|
332
|
+
}
|
|
333
|
+
}
|
|
334
|
+
async handleFileUpload(ev) {
|
|
335
|
+
let files = ev.target.files;
|
|
336
|
+
console.log('upload some files', ev, files);
|
|
337
|
+
if (files && files.length > 0) {
|
|
338
|
+
let filenames = await this.saveFiles(files);
|
|
339
|
+
console.log('finished writing files', filenames);
|
|
340
|
+
}
|
|
341
|
+
}
|
|
342
|
+
handleDragStart(ev) {
|
|
343
|
+
let urilist = '';
|
|
344
|
+
this.dirlist.selection.forEach(f => {
|
|
345
|
+
urilist += 'janus-vfs://' + this.chroot + this.cwd + f.name + '\r\n';
|
|
346
|
+
});
|
|
347
|
+
ev.dataTransfer.setData('text/uri-list', urilist);
|
|
348
|
+
console.log('drag it!', urilist, ev, this.dirlist.selection);
|
|
349
|
+
}
|
|
350
|
+
handleDragOver(ev) {
|
|
351
|
+
ev.dataTransfer.dropEffect = "copy"
|
|
352
|
+
ev.preventDefault();
|
|
353
|
+
}
|
|
354
|
+
handleDragEnter(ev) {
|
|
355
|
+
let dragtarget = this.getDragTarget(ev);
|
|
356
|
+
if (dragtarget && !dragtarget.hasclass('dragactive')) {
|
|
357
|
+
dragtarget.addclass('dragactive');
|
|
358
|
+
}
|
|
359
|
+
}
|
|
360
|
+
handleDragLeave(ev) {
|
|
361
|
+
//let dragtarget = this.getDragTarget(ev);
|
|
362
|
+
let dragtarget = ev.target;
|
|
363
|
+
if (dragtarget && elation.html.hasclass(dragtarget, 'dragactive')) elation.html.removeclass(dragtarget, 'dragactive');
|
|
364
|
+
if (this.dirlist.hasclass('dragactive') && !this.dirlist.contains(dragtarget)) this.dirlist.removeclass('dragactive');
|
|
365
|
+
}
|
|
366
|
+
async handleDrop(ev) {
|
|
367
|
+
ev.preventDefault();
|
|
368
|
+
let dragtarget = this.getDragTarget(ev);
|
|
369
|
+
if (dragtarget) dragtarget.removeclass('dragactive');
|
|
370
|
+
let files = ev.dataTransfer.files;
|
|
371
|
+
let subdir = (dragtarget instanceof elation.elements.janus.ui.vfs.browser.file && dragtarget.stats.isDirectory() ? dragtarget.name : '');
|
|
372
|
+
console.log('upload some files', ev, files);
|
|
373
|
+
if (files && files.length > 0) {
|
|
374
|
+
let filenames = await this.saveFiles(files, subdir);
|
|
375
|
+
console.log('finished writing files', filenames);
|
|
376
|
+
}
|
|
377
|
+
}
|
|
378
|
+
getDragTarget(ev) {
|
|
379
|
+
let dragtarget = ev.target;
|
|
380
|
+
|
|
381
|
+
while (dragtarget !== this.dirlist && !(dragtarget instanceof elation.elements.janus.ui.vfs.browser.file && dragtarget.stats.isDirectory())) {
|
|
382
|
+
dragtarget = dragtarget.parentNode;
|
|
383
|
+
}
|
|
384
|
+
console.log('my dragtarget!', dragtarget);
|
|
385
|
+
return dragtarget;
|
|
386
|
+
}
|
|
387
|
+
async saveFiles(files, subdir) {
|
|
388
|
+
let fs = await elation.engine.assetloader.agent['janus-vfs'].getFS();
|
|
389
|
+
|
|
390
|
+
let Buffer = BrowserFS.BFSRequire('buffer').Buffer;
|
|
391
|
+
let promises = [];
|
|
392
|
+
for (let i = 0; i < files.length; i++) {
|
|
393
|
+
let file = files[i];
|
|
394
|
+
let fname = this.chroot + this.cwd + (subdir ? subdir + '/' : '') + file.name;
|
|
395
|
+
console.log('Got file!', file.name, file.size, fname);
|
|
396
|
+
promises.push(new Promise((accept, reject) => {
|
|
397
|
+
let reader = new FileReader();
|
|
398
|
+
reader.onload = function() {
|
|
399
|
+
let buffer = Buffer.from(this.result);
|
|
400
|
+
fs.writeFile(fname, buffer, (e, f) => {
|
|
401
|
+
accept(fname);
|
|
402
|
+
});
|
|
403
|
+
}
|
|
404
|
+
reader.readAsArrayBuffer(file);
|
|
405
|
+
}));
|
|
406
|
+
}
|
|
407
|
+
Promise.all(promises).then((filenames) => {
|
|
408
|
+
this.updateFiles();
|
|
409
|
+
return filenames;
|
|
410
|
+
});
|
|
411
|
+
}
|
|
412
|
+
});
|
|
413
|
+
elation.elements.define('janus.ui.vfs.browser.directorytree', class extends elation.elements.ui.treeview {
|
|
414
|
+
create() {
|
|
415
|
+
super.create();
|
|
416
|
+
}
|
|
417
|
+
});
|
|
418
|
+
elation.elements.define('janus.ui.vfs.browser.directorylist', class extends elation.elements.ui.list {
|
|
419
|
+
create() {
|
|
420
|
+
super.create();
|
|
421
|
+
}
|
|
422
|
+
});
|
|
423
|
+
elation.elements.define('janus.ui.vfs.browser.directory', class extends elation.elements.ui.item {
|
|
424
|
+
create() {
|
|
425
|
+
}
|
|
426
|
+
});
|
|
427
|
+
elation.elements.define('janus.ui.vfs.browser.file', class extends elation.elements.ui.item {
|
|
428
|
+
create() {
|
|
429
|
+
super.create();
|
|
430
|
+
|
|
431
|
+
this.name = this.value.name;
|
|
432
|
+
this.path = this.value.path;
|
|
433
|
+
this.stats = this.value.stats;
|
|
434
|
+
|
|
435
|
+
this.title = this.name;
|
|
436
|
+
|
|
437
|
+
elation.elements.create('ui-label', {
|
|
438
|
+
append: this,
|
|
439
|
+
label: this.name + (this.stats.isDirectory() ? '/' : ''),
|
|
440
|
+
});
|
|
441
|
+
|
|
442
|
+
this.addEventListener('dblclick', ev => this.handleDblClick(ev));
|
|
443
|
+
}
|
|
444
|
+
handleDblClick(ev) {
|
|
445
|
+
if (this.stats.isDirectory()) {
|
|
446
|
+
this.dispatchEvent({type: 'directorychange', bubbles: true});
|
|
447
|
+
} else {
|
|
448
|
+
console.log('do something with this thing!', this);
|
|
449
|
+
}
|
|
450
|
+
ev.preventDefault();
|
|
451
|
+
}
|
|
452
|
+
});
|
|
@@ -10,7 +10,8 @@
|
|
|
10
10
|
"locomotion": "./apps/locomotion/locomotion.json",
|
|
11
11
|
"virtualgamepad": "./apps/virtualgamepad/virtualgamepad.json",
|
|
12
12
|
"buttons": "./apps/buttons/buttons.json",
|
|
13
|
-
"xrmenu": "./apps/xrmenu/xrmenu.json"
|
|
13
|
+
"xrmenu": "./apps/xrmenu/xrmenu.json",
|
|
14
|
+
"vfs": "./apps/vfs/vfs.json"
|
|
14
15
|
},
|
|
15
16
|
"includes": [
|
|
16
17
|
],
|
|
@@ -71,6 +71,10 @@ ui-list>ui-item[hover],
|
|
|
71
71
|
ui-checklist>ui-checklistitem[hover] {
|
|
72
72
|
background-color: rgba(128,128,255,.05);
|
|
73
73
|
}
|
|
74
|
+
ui-list[selectable]>ui-item[hover] {
|
|
75
|
+
background-color: rgba(128,128,255,.2);
|
|
76
|
+
cursor: pointer;
|
|
77
|
+
}
|
|
74
78
|
ui-list>ui-item.state_selected,
|
|
75
79
|
ui-checklist>ui-checklistitem.state_selected {
|
|
76
80
|
background: rgba(128,128,255,.5);
|
|
@@ -184,6 +188,10 @@ ui-tabs>ui-tabbar>ui-tabcountbutton {
|
|
|
184
188
|
margin-bottom: 0;
|
|
185
189
|
white-space: nowrap;
|
|
186
190
|
display: inline-block;
|
|
191
|
+
max-width:10em;
|
|
192
|
+
text-overflow: ellipsis;
|
|
193
|
+
direction: rtl;
|
|
194
|
+
overflow: hidden;
|
|
187
195
|
}
|
|
188
196
|
ui-tabs>ui-tabbar>ui-button[selected],
|
|
189
197
|
ui-tabs>ui-tabbar>ui-button[selected][hover],
|
|
@@ -272,6 +280,8 @@ ui-window {
|
|
|
272
280
|
border: 1px solid #666;
|
|
273
281
|
box-shadow: 1px 1px 10px rgba(0,0,0,1);
|
|
274
282
|
color: white;
|
|
283
|
+
display: flex;
|
|
284
|
+
flex-direction: column;
|
|
275
285
|
}
|
|
276
286
|
ui-window>ui-window-titlebar {
|
|
277
287
|
display: flex;
|
|
@@ -286,6 +296,7 @@ ui-window>ui-window-titlebar {
|
|
|
286
296
|
white-space: nowrap;
|
|
287
297
|
vertical-align: middle;
|
|
288
298
|
border-bottom: 1px solid #666;
|
|
299
|
+
order: 0;
|
|
289
300
|
}
|
|
290
301
|
ui-window.state_movable>ui-window-titlebar:hover {
|
|
291
302
|
cursor: -webkit-grab;
|
|
@@ -331,6 +342,9 @@ ui-window>.ui_window_content {
|
|
|
331
342
|
-moz-box-sizing: border-box;
|
|
332
343
|
position: relative;
|
|
333
344
|
}
|
|
345
|
+
ui-window>ui-window-content {
|
|
346
|
+
order: 1;
|
|
347
|
+
}
|
|
334
348
|
ui-window[scrollable]>ui-window-content {
|
|
335
349
|
overflow: auto;
|
|
336
350
|
display: block;
|
|
@@ -673,6 +687,7 @@ ui-button,
|
|
|
673
687
|
ui-togglebutton,
|
|
674
688
|
ui-dropdownbutton,
|
|
675
689
|
ui-tabcountbutton,
|
|
690
|
+
ui-popupbutton,
|
|
676
691
|
ui-notificationbutton {
|
|
677
692
|
display: inline-block;
|
|
678
693
|
border: none;
|
|
@@ -687,11 +702,13 @@ ui-notificationbutton {
|
|
|
687
702
|
|
|
688
703
|
background-image: linear-gradient(#484e55, #3a3f44 60%, #313539);
|
|
689
704
|
background-color: transparent;
|
|
705
|
+
position: relative;
|
|
690
706
|
}
|
|
691
707
|
ui-button[hover],
|
|
692
708
|
ui-togglebutton[hover],
|
|
693
709
|
ui-dropdownbutton[hover],
|
|
694
710
|
ui-tabcountbutton[hover],
|
|
711
|
+
ui-popupbutton[hover],
|
|
695
712
|
ui-notificationbutton[hover] {
|
|
696
713
|
background-image: linear-gradient(#323232, #404142 40%, #393b3d);
|
|
697
714
|
}
|
|
@@ -699,6 +716,7 @@ ui-button:active,
|
|
|
699
716
|
ui-togglebutton:active,
|
|
700
717
|
ui-dropdownbutton:active,
|
|
701
718
|
ui-tabcountbutton:active,
|
|
719
|
+
ui-popupbutton:active,
|
|
702
720
|
ui-notificationbutton:active {
|
|
703
721
|
background-image: linear-gradient(#020202, #101112 40%, #191b1d);
|
|
704
722
|
}
|
|
@@ -706,9 +724,10 @@ ui-button[disabled],
|
|
|
706
724
|
ui-togglebutton:disabled,
|
|
707
725
|
ui-dropdownbutton:disabled,
|
|
708
726
|
ui-tabcountbutton:disabled,
|
|
727
|
+
ui-popupbutton:disabled,
|
|
709
728
|
ui-notificationbutton:disabled {
|
|
710
729
|
color: #999;
|
|
711
|
-
background-image: linear-gradient(#
|
|
730
|
+
background-image: linear-gradient(#383e45, #2a2f34 60%, #212549);
|
|
712
731
|
cursor: not-allowed;
|
|
713
732
|
}
|
|
714
733
|
|
|
@@ -1034,6 +1053,7 @@ ui-treeview {
|
|
|
1034
1053
|
background: black;
|
|
1035
1054
|
color: white;
|
|
1036
1055
|
*/
|
|
1056
|
+
display: block;
|
|
1037
1057
|
}
|
|
1038
1058
|
ui-treeview ui-list {
|
|
1039
1059
|
/*background: rgba(0,0,0,.8);*/
|
|
@@ -1060,8 +1080,12 @@ ui-treeview ui-treeviewitem {
|
|
|
1060
1080
|
order: 2;
|
|
1061
1081
|
padding-left: 1em;
|
|
1062
1082
|
}
|
|
1083
|
+
ui-treeview ui-treeviewitem[collapsed]>ui-treeviewitem {
|
|
1084
|
+
display: none;
|
|
1085
|
+
}
|
|
1063
1086
|
ui-treeview ui-treeviewitem>ui-text {
|
|
1064
1087
|
order: 1;
|
|
1088
|
+
user-select: none;
|
|
1065
1089
|
}
|
|
1066
1090
|
ui-treeview ui-treeviewitem:hover>ui-text {
|
|
1067
1091
|
color: #9c9;
|
|
@@ -1083,11 +1107,14 @@ ui-treeview ui-treeviewitem ui-text:before {
|
|
|
1083
1107
|
float: left;
|
|
1084
1108
|
clear: left;
|
|
1085
1109
|
width: .8em;
|
|
1086
|
-
|
|
1110
|
+
padding-right: .5em;
|
|
1087
1111
|
}
|
|
1088
1112
|
ui-treeview ui-treeviewitem.haschildren>ui-text:before {
|
|
1089
1113
|
content: '▼';
|
|
1090
1114
|
}
|
|
1115
|
+
ui-treeview ui-treeviewitem.haschildren[collapsed]>ui-text:before {
|
|
1116
|
+
content: '▶';
|
|
1117
|
+
}
|
|
1091
1118
|
ui-treeview ui-treeviewitem:last-child>ul {
|
|
1092
1119
|
border-left: 0;
|
|
1093
1120
|
}
|