meeting-pane 2.3.12 → 2.3.13-3c0b6068

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,1058 @@
1
+ "use strict";
2
+
3
+ /* Meeting materials and tools Pane
4
+ **
5
+ ** Putting together some of the tools we have to manage a Meeting
6
+ */
7
+ // const VideoRoomPrefix = 'https://appear.in/'
8
+ var logic = require('solid-logic');
9
+
10
+ var VideoRoomPrefix = 'https://meet.jit.si/';
11
+
12
+ var UI = require('solid-ui');
13
+
14
+ var ns = UI.ns;
15
+
16
+ var $rdf = require('rdflib');
17
+
18
+ var meetingDetailsFormText = require('./meetingDetailsForm.js');
19
+
20
+ module.exports = {
21
+ icon: UI.icons.iconBase + 'noun_66617.svg',
22
+ name: 'meeting',
23
+ audience: [ns.solid('PowerUser')],
24
+ label: function label(subject, context) {
25
+ var kb = context.session.store;
26
+ var ns = UI.ns;
27
+
28
+ if (kb.holds(subject, ns.rdf('type'), ns.meeting('Meeting'))) {
29
+ return 'Meeting';
30
+ }
31
+
32
+ return null; // Suppress pane otherwise
33
+ },
34
+ // Create a new Meeting thing
35
+ //
36
+ // returns: A promise of a meeting object
37
+ //
38
+ mintClass: UI.ns.meeting('Meeting'),
39
+ mintNew: function mintNew(context, options) {
40
+ return new Promise(function (resolve, reject) {
41
+ var kb = context.session.store;
42
+ var ns = UI.ns;
43
+ options.newInstance = options.newInstance || kb.sym(options.newBase + 'index.ttl#this');
44
+ var meeting = options.newInstance;
45
+ var meetingDoc = meeting.doc();
46
+ var me = logic.authn.currentUser();
47
+
48
+ if (me) {
49
+ kb.add(meeting, ns.dc('author'), me, meetingDoc);
50
+ }
51
+
52
+ kb.add(meeting, ns.rdf('type'), ns.meeting('Meeting'), meetingDoc);
53
+ kb.add(meeting, ns.dc('created'), new Date(), meetingDoc);
54
+ kb.add(meeting, ns.ui('backgroundColor'), new $rdf.Literal('#ddddcc', undefined, ns.xsd('color')), meetingDoc);
55
+ var toolList = new $rdf.Collection();
56
+ kb.add(meeting, ns.meeting('toolList'), toolList, meetingDoc);
57
+ toolList.elements.push(meeting); // Add the meeting itself - see renderMain()
58
+
59
+ kb.updater.put(meetingDoc, kb.statementsMatching(undefined, undefined, undefined, meetingDoc), 'text/turtle', function (uri2, ok, message) {
60
+ if (ok) {
61
+ resolve(options);
62
+ } else {
63
+ reject(new Error('Error writing meeting configuration: ' + message));
64
+ }
65
+ });
66
+ });
67
+ },
68
+ // Returns a div
69
+ render: function render(subject, dataBrowserContext) {
70
+ var dom = dataBrowserContext.dom;
71
+ var kb = dataBrowserContext.session.store;
72
+ var ns = UI.ns;
73
+ var updater = kb.updater;
74
+ var thisPane = this;
75
+
76
+ var complain = function complain(message, color) {
77
+ console.log(message);
78
+ var pre = dom.createElement('pre');
79
+ pre.setAttribute('style', 'background-color: ' + color || '#eed' + ';');
80
+ div.appendChild(pre);
81
+ pre.appendChild(dom.createTextNode(message));
82
+ };
83
+
84
+ var complainIfBad = function complainIfBad(ok, message) {
85
+ if (!ok) complain(message);
86
+ };
87
+
88
+ var meeting = subject;
89
+ var meetingDoc = subject.doc();
90
+ var meetingBase = subject.dir().uri;
91
+ var div = dom.createElement('div');
92
+ var table = div.appendChild(dom.createElement('table'));
93
+ table.style = 'width: 100%; height: 100%; margin:0;';
94
+ var topTR = table.appendChild(dom.createElement('tr'));
95
+ topTR.appendChild(dom.createElement('div')); // topDiv
96
+
97
+ var mainTR = table.appendChild(dom.createElement('tr'));
98
+ var toolBar0 = table.appendChild(dom.createElement('td'));
99
+ var toolBar1 = toolBar0.appendChild(dom.createElement('table'));
100
+ var toolBar = toolBar1.appendChild(dom.createElement('tr'));
101
+ topTR.setAttribute('style', 'height: 2em;'); // spacer if notthing else
102
+
103
+ var me = null; // @@ Put code to find out logged in person
104
+
105
+ var saveBackMeetingDoc = function saveBackMeetingDoc() {
106
+ updater.put(meetingDoc, kb.statementsMatching(undefined, undefined, undefined, meetingDoc), 'text/turtle', function (uri2, ok, message) {
107
+ if (ok) {
108
+ tabs.refresh();
109
+ resetTools();
110
+ } else {
111
+ message = 'FAILED to save new thing at: ' + meetingDoc + ' : ' + message;
112
+ complain(message);
113
+ }
114
+ });
115
+ };
116
+
117
+ var saveAppDocumentLinkAndAddNewThing = function saveAppDocumentLinkAndAddNewThing(tool, thing, pred) {
118
+ var appDoc = thing.doc();
119
+
120
+ if (pred) {
121
+ kb.add(meeting, pred, thing, appDoc); // Specific Link back to meeting
122
+ }
123
+
124
+ kb.add(thing, ns.meeting('parentMeeting'), meeting, appDoc); // Generic link back to meeting
125
+
126
+ updater.put(appDoc, kb.statementsMatching(undefined, undefined, undefined, appDoc), 'text/turtle', function (uri2, ok, message) {
127
+ if (ok) {
128
+ saveBackMeetingDoc();
129
+ } else {
130
+ complain('FAILED to save new tool at: ' + thing + ' : ' + message);
131
+ }
132
+ });
133
+ };
134
+
135
+ var makeToolNode = function makeToolNode(target, pred, label, iconURI) {
136
+ if (pred) {
137
+ kb.add(meeting, pred, target, meetingDoc);
138
+ }
139
+
140
+ var x = UI.widgets.newThing(meetingDoc);
141
+ if (label) kb.add(x, ns.rdfs('label'), label, meetingDoc);
142
+ if (iconURI) kb.add(x, ns.meeting('icon'), kb.sym(iconURI), meetingDoc);
143
+ kb.add(x, ns.rdf('type'), ns.meeting('Tool'), meetingDoc);
144
+ kb.add(x, ns.meeting('target'), target, meetingDoc);
145
+ var toolList = kb.the(meeting, ns.meeting('toolList'));
146
+ toolList.elements.push(x);
147
+ return x;
148
+ }; // Map from end-user non-iframeable Google maps URI to G Maps API
149
+ // Input: like https://www.google.co.uk/maps/place/Mastercard/@53.2717971,-6.2042699,17z/...
150
+ // Output:
151
+
152
+
153
+ function googleMapsSpecial(page) {
154
+ var initialPrefix = /https:\/\/www\.google\..*\/maps\//;
155
+ var finalPrefix = 'https://www.google.com/maps/embed/v1/';
156
+ var myPersonalApiKEY = 'AIzaSyB8aaT6bY9tcLCmc2oPCkdUYLmTOWM8R54'; // Get your own key!
157
+ // GET YOUR KEY AT https://developers.google.com/maps/documentation/javascript/
158
+
159
+ var uri = page.uri;
160
+ if (!uri.match(initialPrefix)) return page;
161
+ if (uri.startsWith(finalPrefix)) return page; // Already done
162
+
163
+ var map = uri.replace(initialPrefix, finalPrefix) + '&key=' + myPersonalApiKEY;
164
+ console.log('Converted Google Map URI! ' + map);
165
+ return $rdf.sym(map);
166
+ } // //////////////////// DRAG and Drop
167
+
168
+
169
+ var handleDroppedThing = function handleDroppedThing(target) {
170
+ // @@ idea: look
171
+ return new Promise(function (resolve) {
172
+ // Add a meeting tab for a web resource. Alas many resource canot be framed
173
+ // as they block framing, or are insecure.
174
+ var addIframeTool = function addIframeTool(target) {
175
+ var tool = makeToolNode(target, UI.ns.wf('attachment'), UI.utils.label(target), null);
176
+ kb.add(tool, UI.ns.meeting('view'), 'iframe', meetingDoc);
177
+ };
178
+
179
+ var addLink = function addLink(target) {
180
+ var pred = ns.wf('attachment');
181
+ kb.add(subject, pred, target, subject.doc());
182
+ var toolObject = {
183
+ icon: 'noun_160581.svg',
184
+ // right arrow "link"
185
+ limit: 1,
186
+ shareTab: true // but many things behind it
187
+
188
+ };
189
+ var newPaneOptions = {
190
+ newInstance: subject,
191
+ // kb.sym(subject.doc().uri + '#LinkListTool'),
192
+ pane: dataBrowserContext.session.paneRegistry.byName('link'),
193
+ // the pane to be used to mint a new thing
194
+ predicate: ns.meeting('attachmentTool'),
195
+ tabTitle: 'Links',
196
+ view: 'link',
197
+ // The pane to be used when it is viewed
198
+ noIndexHTML: true
199
+ };
200
+ return makeNewPaneTool(toolObject, newPaneOptions);
201
+ }; // When paerson added to he meeting, make an ad hoc group
202
+ // of meeting participants is one does not already exist, and add them
203
+
204
+
205
+ var addParticipant = function addParticipant(target) {
206
+ var pref = kb.any(target, ns.foaf('preferredURI'));
207
+ var obj = pref ? kb.sym(pref) : target;
208
+ var group = kb.any(meeting, ns.meeting('attendeeGroup'));
209
+
210
+ var addPersonToGroup = function addPersonToGroup(obj, group) {
211
+ var ins = [$rdf.st(group, UI.ns.vcard('hasMember'), obj, group.doc())]; // @@@ Complex rules about webid?
212
+
213
+ var name = kb.any(obj, ns.vcard('fn')) || kb.any(obj, ns.foaf('name'));
214
+
215
+ if (name) {
216
+ ins.push($rdf.st(obj, UI.ns.vcard('fn'), name, group.doc()));
217
+ }
218
+
219
+ kb.fetcher.nowOrWhenFetched(group.doc(), undefined, function (ok, _body) {
220
+ if (!ok) {
221
+ complain("Can't read group to add person" + group);
222
+ return;
223
+ }
224
+
225
+ kb.updater.update([], ins, function (uri, ok, body) {
226
+ complainIfBad(ok, body);
227
+
228
+ if (ok) {
229
+ console.log('Addded to particpants OK: ' + obj);
230
+ }
231
+ });
232
+ });
233
+ };
234
+
235
+ if (group) {
236
+ addPersonToGroup(obj, group);
237
+ return;
238
+ }
239
+
240
+ makeParticipantsGroup().then(function (options) {
241
+ var group = options.newInstance;
242
+ addPersonToGroup(obj, group);
243
+ kb.fetcher.putBack(meetingDoc, {
244
+ contentType: 'text/turtle'
245
+ }).then(function (_xhr) {
246
+ console.log('Particiants Group created: ' + group);
247
+ });
248
+ })["catch"](function (err) {
249
+ complain(err);
250
+ });
251
+ };
252
+
253
+ console.log('Dropped on thing ' + target); // icon was: UI.icons.iconBase + 'noun_25830.svg'
254
+
255
+ var u = target.uri;
256
+
257
+ if (u.startsWith('http:') && u.indexOf('#') < 0) {
258
+ // insecure Plain document
259
+ addLink(target);
260
+ return resolve(target);
261
+ }
262
+
263
+ kb.fetcher.nowOrWhenFetched(target, function (ok, mess) {
264
+ function addAttachmentTab(target) {
265
+ target = googleMapsSpecial(target);
266
+ console.log('make web page attachement tab ' + target); // icon was: UI.icons.iconBase + 'noun_25830.svg'
267
+
268
+ var tool = makeToolNode(target, UI.ns.wf('attachment'), UI.utils.label(target), null);
269
+ kb.add(tool, UI.ns.meeting('view'), 'iframe', meetingDoc);
270
+ return resolve(target);
271
+ }
272
+
273
+ if (!ok) {
274
+ console.log('Error looking up dropped thing, will just add it anyway. ' + target + ': ' + mess);
275
+ return addAttachmentTab(target); // You can still try iframing it. (Could also add to list of links in PersonTR widgets)
276
+ } else {
277
+ var obj = target;
278
+ var types = kb.findTypeURIs(obj);
279
+
280
+ for (var ty in types) {
281
+ console.log(' drop object type includes: ' + ty);
282
+ }
283
+
284
+ if (ns.vcard('Individual').uri in types || ns.foaf('Person').uri in types || ns.foaf('Agent').uri in types) {
285
+ addParticipant(target);
286
+ return resolve(target);
287
+ }
288
+
289
+ if (u.startsWith('https:') && u.indexOf('#') < 0) {
290
+ // Plain secure document
291
+ // can we iframe it?
292
+ var hh = kb.fetcher.getHeader(target, 'x-frame-options');
293
+ var ok2 = true;
294
+
295
+ if (hh) {
296
+ for (var j = 0; j < hh.length; j++) {
297
+ console.log('x-frame-options: ' + hh[j]);
298
+
299
+ if (hh[j].indexOf('sameorigin') < 0) {
300
+ // (and diff origin @@)
301
+ ok2 = false;
302
+ }
303
+
304
+ if (hh[j].indexOf('deny') < 0) {
305
+ ok2 = false;
306
+ }
307
+ }
308
+ }
309
+
310
+ if (ok2) {
311
+ target = googleMapsSpecial(target); // tweak Google maps to embed OK
312
+
313
+ addIframeTool(target); // Something we can maybe iframe
314
+
315
+ return resolve(target);
316
+ }
317
+ } // Something we cannot iframe, and must link to:
318
+
319
+
320
+ console.log('Default: assume web page attachement ' + target); // icon was: UI.icons.iconBase + 'noun_25830.svg'
321
+
322
+ return addAttachmentTab(target);
323
+ }
324
+ });
325
+ }); // promise
326
+ }; // When a set of URIs are dropped on the tabs
327
+
328
+
329
+ var droppedURIHandler = function droppedURIHandler(uris) {
330
+ Promise.all(uris.map(function (u) {
331
+ var target = $rdf.sym(u); // Attachment needs text label to disinguish I think not icon.
332
+
333
+ return handleDroppedThing(target); // can add to meetingDoc but must be sync
334
+ })).then(function (_a) {
335
+ saveBackMeetingDoc();
336
+ });
337
+ };
338
+
339
+ var droppedFileHandler = function droppedFileHandler(files) {
340
+ UI.widgets.uploadFiles(kb.fetcher, files, meeting.dir().uri + 'Files', meeting.dir().uri + 'Pictures', function (theFile, _destURI) {
341
+ if (theFile.type.startsWith('image/')) {
342
+ makePicturesFolder('Files'); // If necessary
343
+ } else {
344
+ makeMaterialsFolder('Pictures');
345
+ }
346
+ });
347
+ }; // ////////////////////////////////////////////////////// end of drag drop
348
+
349
+
350
+ var makeGroup = function makeGroup(_toolObject) {
351
+ var newBase = meetingBase + 'Group/';
352
+ var kb = dataBrowserContext.session.store;
353
+ var group = kb.any(meeting, ns.meeting('particpants'));
354
+
355
+ if (!group) {
356
+ group = $rdf.sym(newBase + 'index.ttl#this');
357
+ }
358
+
359
+ console.log('Participant group: ' + group);
360
+ var tool = makeToolNode(group, ns.meeting('particpants'), 'Particpants', UI.icons.iconBase + 'noun_339237.svg'); // group: noun_339237.svg 'noun_15695.svg'
361
+
362
+ kb.add(tool, UI.ns.meeting('view'), 'peoplePicker', meetingDoc);
363
+ saveBackMeetingDoc();
364
+ };
365
+ /*
366
+ var makeAddressBook = function (toolObject) {
367
+ var newBase = meetingBase + 'Group/'
368
+ var kb = store
369
+ var group = kb.any(meeting, ns.meeting('addressBook'))
370
+ if (!group) {
371
+ group = $rdf.sym(newBase + 'index.ttl#this')
372
+ }
373
+ // Create a tab for the addressbook
374
+ var div = dom.createElement('div')
375
+ var context = { dom: dom, div: div }
376
+ var book
377
+ UI.login.findAppInstances(context, ns.vcard('AddressBook')).then(
378
+ function (context) {
379
+ if (context.instances.length === 0) {
380
+ complain('You have no solid address book. It is really handy to have one to keep track of people and groups')
381
+ } else if (context.instances.length > 1) {
382
+ var s = context.instances.map(function (x) { return '' + x }).join(', ')
383
+ complain('You have more than one solid address book: ' + s + ' Not supported yet.')
384
+ } else { // addressbook
385
+ book = context.instances[0]
386
+ var tool = makeToolNode(book, ns.meeting('addressBook'), 'Address Book', UI.icons.iconBase + 'noun_15695.svg') // group: noun_339237.svg
387
+ kb.add(tool, UI.ns.meeting('view'), 'contact', meetingDoc)
388
+ saveBackMeetingDoc()
389
+ }
390
+ }
391
+ )
392
+ }
393
+ */
394
+
395
+
396
+ var makePoll = function makePoll(toolObject) {
397
+ var newPaneOptions = {
398
+ useExisting: meeting,
399
+ // Regard the meeting as being the schedulable event itself.
400
+ // newInstance: meeting,
401
+ pane: dataBrowserContext.session.paneRegistry.byName('schedule'),
402
+ view: 'schedule',
403
+ // predicate: ns.meeting('schedulingPoll'),
404
+ // newBase: meetingBase + 'Schedule/', Not needed as uses existing meeting
405
+ tabTitle: 'Schedule poll',
406
+ noIndexHTML: true
407
+ };
408
+ return makeNewPaneTool(toolObject, newPaneOptions);
409
+ };
410
+
411
+ var makePicturesFolder = function makePicturesFolder(folderName) {
412
+ var toolObject = {
413
+ icon: 'noun_598334.svg',
414
+ // Slideshow @@ find a "picture" icon?
415
+ limit: 1,
416
+ shareTab: true // but many things behind it
417
+
418
+ };
419
+ var newPaneOptions = {
420
+ newInstance: kb.sym(meeting.dir().uri + folderName + '/'),
421
+ pane: dataBrowserContext.session.paneRegistry.byName('folder'),
422
+ // @@ slideshow??
423
+ predicate: ns.meeting('pictures'),
424
+ shareTab: true,
425
+ tabTitle: folderName,
426
+ view: 'slideshow',
427
+ noIndexHTML: true
428
+ };
429
+ return makeNewPaneTool(toolObject, newPaneOptions);
430
+ };
431
+
432
+ var makeMaterialsFolder = function makeMaterialsFolder(_folderName) {
433
+ var toolObject = {
434
+ icon: 'noun_681601.svg',
435
+ // Document
436
+ limit: 1,
437
+ shareTab: true // but many things behind it
438
+
439
+ };
440
+ var options = {
441
+ newInstance: kb.sym(meeting.dir().uri + 'Files/'),
442
+ pane: dataBrowserContext.session.paneRegistry.byName('folder'),
443
+ predicate: ns.meeting('materialsFolder'),
444
+ tabTitle: 'Materials',
445
+ noIndexHTML: true
446
+ };
447
+ return makeNewPaneTool(toolObject, options);
448
+ };
449
+
450
+ var makeParticipantsGroup = function makeParticipantsGroup() {
451
+ var toolObject = {
452
+ icon: 'noun_339237.svg',
453
+ // Group of people
454
+ limit: 1,
455
+ // Only one tab
456
+ shareTab: true // but many things behind it
457
+
458
+ };
459
+ var options = {
460
+ newInstance: kb.sym(meeting.dir().uri + 'Attendees/index.ttl#this'),
461
+ pane: dataBrowserContext.session.paneRegistry.byName('contact'),
462
+ predicate: ns.meeting('attendeeGroup'),
463
+ tabTitle: 'Attendees',
464
+ instanceClass: ns.vcard('Group'),
465
+ instanceName: UI.utils.label(subject) + ' attendees',
466
+ noIndexHTML: true
467
+ };
468
+ return makeNewPaneTool(toolObject, options);
469
+ }; // Make Pad for notes of meeting
470
+
471
+
472
+ var makePad = function makePad(toolObject) {
473
+ var newPaneOptions = {
474
+ newBase: meetingBase + 'SharedNotes/',
475
+ predicate: UI.ns.meeting('sharedNotes'),
476
+ tabTitle: 'Shared Notes',
477
+ pane: dataBrowserContext.session.paneRegistry.byName('pad')
478
+ };
479
+ return makeNewPaneTool(toolObject, newPaneOptions);
480
+ }; // Make Sub-meeting of meeting
481
+
482
+
483
+ var makeMeeting = function makeMeeting(toolObject) {
484
+ UI.widgets.askName(dom, kb, parameterCell, ns.foaf('name'), UI.ns.meeting('Meeting')).then(function (name) {
485
+ if (!name) {
486
+ return resetTools();
487
+ }
488
+
489
+ var URIsegment = encodeURIComponent(name);
490
+ var options = {
491
+ newBase: meetingBase + URIsegment + '/',
492
+ // @@@ sanitize
493
+ predicate: UI.ns.meeting('subMeeting'),
494
+ tabTitle: name,
495
+ pane: dataBrowserContext.session.paneRegistry.byName('meeting')
496
+ };
497
+ return makeNewPaneTool(toolObject, options);
498
+ })["catch"](function (e) {
499
+ complain('Error making new sub-meeting: ' + e);
500
+ });
501
+ }; // Returns promise of newPaneOptions
502
+ // In: options.
503
+ // me?, predicate, newInstance ?, newBase, instanceClass
504
+ // out: options. the above plus
505
+ // me, newInstance
506
+
507
+
508
+ function makeNewPaneTool(toolObject, options) {
509
+ return new Promise(function (resolve, reject) {
510
+ var kb = dataBrowserContext.session.store;
511
+
512
+ if (!options.useExisting) {
513
+ // useExisting means use existing object in new role
514
+ var existing = kb.any(meeting, options.predicate);
515
+
516
+ if (existing) {
517
+ if (toolObject.limit && toolObject.limit === 1 && !toolObject.shareTab) {
518
+ complain('Already have ' + existing + ' as ' + UI.utils.label(options.predicate));
519
+ complain('Cant have two');
520
+ return resolve(null);
521
+ }
522
+
523
+ if (toolObject.shareTab) {
524
+ // return existing one
525
+ console.log('Using existing ' + existing + ' as ' + UI.utils.label(options.predicate));
526
+ return resolve({
527
+ me: me,
528
+ newInstance: existing,
529
+ instanceClass: options.instanceClass
530
+ });
531
+ }
532
+ }
533
+ }
534
+
535
+ if (!me && !options.me) {
536
+ reject(new Error('Username not defined for new tool'));
537
+ }
538
+
539
+ options.me = options.me || me;
540
+ options.newInstance = options.useExisting || options.newInstance || kb.sym(options.newBase + 'index.ttl#this');
541
+ options.pane.mintNew(dataBrowserContext, options).then(function (options) {
542
+ var tool = makeToolNode(options.newInstance, options.predicate, options.tabTitle, options.pane.icon);
543
+
544
+ if (options.view) {
545
+ kb.add(tool, UI.ns.meeting('view'), options.view, meetingDoc);
546
+ }
547
+
548
+ saveBackMeetingDoc();
549
+ kb.fetcher.putBack(meetingDoc, {
550
+ contentType: 'text/turtle'
551
+ }).then(function (_xhr) {
552
+ resolve(options);
553
+ })["catch"](function (err) {
554
+ reject(err);
555
+ });
556
+ })["catch"](function (err) {
557
+ complain(err);
558
+ reject(err);
559
+ });
560
+ });
561
+ }
562
+
563
+ var makeAgenda = function makeAgenda(_toolObject) {// selectTool(icon)
564
+ };
565
+
566
+ var makeActions = function makeActions(_toolObject) {
567
+ var newBase = meetingBase + 'Actions/';
568
+ var kb = dataBrowserContext.session.store;
569
+
570
+ if (kb.holds(meeting, ns.meeting('actions'))) {
571
+ console.log('Ignored - already have actions');
572
+ return; // already got one
573
+ }
574
+
575
+ var appDoc = kb.sym(newBase + 'config.ttl');
576
+ var newInstance = kb.sym(newBase + 'config.ttl#this');
577
+ var stateStore = kb.sym(newBase + 'state.ttl');
578
+ kb.add(newInstance, ns.dc('title'), (kb.anyValue(meeting, ns.cal('summary')) || 'Meeting ') + ' actions', appDoc);
579
+ kb.add(newInstance, ns.wf('issueClass'), ns.wf('Task'), appDoc);
580
+ kb.add(newInstance, ns.wf('initialState'), ns.wf('Open'), appDoc);
581
+ kb.add(newInstance, ns.wf('stateStore'), stateStore, appDoc);
582
+ kb.add(newInstance, ns.wf('assigneeClass'), ns.foaf('Person'), appDoc); // @@ set to people in the meeting?
583
+
584
+ kb.add(newInstance, ns.rdf('type'), ns.wf('Tracker'), appDoc); // Flag its type in the chat itself as well as in the master meeting config file
585
+
586
+ kb.add(newInstance, ns.rdf('type'), ns.wf('Tracker'), appDoc);
587
+ var tool = makeToolNode(newInstance, ns.meeting('actions'), 'Actions', UI.icons.iconBase + 'noun_17020.svg');
588
+ saveAppDocumentLinkAndAddNewThing(tool, newInstance, ns.meeting('actions'));
589
+ };
590
+
591
+ var makeChat = function makeChat(_toolObject) {
592
+ var newBase = meetingBase + 'Chat/';
593
+ var kb = dataBrowserContext.session.store;
594
+
595
+ if (kb.holds(meeting, ns.meeting('chat'))) {
596
+ console.log('Ignored - already have chat');
597
+ return; // already got one
598
+ }
599
+
600
+ var messageStore = kb.sym(newBase + 'chat.ttl');
601
+ kb.add(messageStore, ns.rdf('type'), ns.meeting('Chat'), messageStore);
602
+ var tool = makeToolNode(messageStore, ns.meeting('chat'), 'Chat', UI.icons.iconBase + 'noun_346319.svg');
603
+ saveAppDocumentLinkAndAddNewThing(tool, messageStore, ns.meeting('chat'));
604
+ };
605
+
606
+ var makeVideoCall = function makeVideoCall(_toolObject) {
607
+ var kb = dataBrowserContext.session.store;
608
+ var newInstance = $rdf.sym(VideoRoomPrefix + UI.utils.genUuid());
609
+
610
+ if (kb.holds(meeting, ns.meeting('videoCallPage'))) {
611
+ console.log('Ignored - already have a videoCallPage');
612
+ return; // already got one
613
+ }
614
+
615
+ kb.add(newInstance, ns.rdf('type'), ns.meeting('VideoCallPage'), meetingDoc);
616
+ var tool = makeToolNode(newInstance, ns.meeting('videoCallPage'), 'Video call', UI.icons.iconBase + 'noun_260227.svg');
617
+ kb.add(tool, ns.meeting('view'), 'iframe', meetingDoc);
618
+ saveBackMeetingDoc();
619
+ };
620
+
621
+ var makeAttachment = function makeAttachment(_toolObject) {
622
+ UI.widgets.askName(dom, kb, parameterCell, ns.log('uri'), UI.ns.rdf('Resource')).then(function (uri) {
623
+ if (!uri) {
624
+ return resetTools();
625
+ }
626
+
627
+ var kb = dataBrowserContext.session.store;
628
+ var ns = UI.ns;
629
+ var target = kb.sym(uri);
630
+ var tool = makeToolNode(target, ns.wf('attachment'), UI.utils.label(target), null);
631
+ kb.add(tool, ns.meeting('view'), 'iframe', meetingDoc);
632
+ saveBackMeetingDoc();
633
+ })["catch"](function (e) {
634
+ complain('Error making new sub-meeting: ' + e);
635
+ });
636
+ };
637
+
638
+ var makeSharing = function makeSharing(toolObject) {
639
+ var kb = dataBrowserContext.session.store;
640
+ var ns = UI.ns;
641
+ var target = meeting.dir();
642
+
643
+ if (toolObject.limit && toolObject.limit === 1 && kb.holds(meeting, ns.wf('sharingControl'))) {
644
+ complain('Ignored - already have ' + UI.utils.label(options.predicate));
645
+ return;
646
+ }
647
+
648
+ var tool = makeToolNode(target, ns.wf('sharingControl'), 'Sharing', UI.icons.iconBase + 'noun_123691.svg');
649
+ kb.add(tool, ns.meeting('view'), 'sharing', meetingDoc);
650
+ saveBackMeetingDoc();
651
+ };
652
+
653
+ var makeNewMeeting = function makeNewMeeting() {
654
+ // @@@ make option of continuing series
655
+ var appDetails = {
656
+ noun: 'meeting'
657
+ };
658
+
659
+ var gotWS = function gotWS(ws, base) {
660
+ thisPane.mintNew(dataBrowserContext, {
661
+ newBase: base
662
+ }).then(function (options) {
663
+ var newInstance = options.newInstance;
664
+ parameterCell.removeChild(mintUI);
665
+ var p = parameterCell.appendChild(dom.createElement('p'));
666
+ p.setAttribute('style', 'font-size: 140%;');
667
+ p.innerHTML = "Your <a target='_blank' href='" + newInstance.uri + "'><b>new meeting</b></a> is ready to be set up. " + "<br/><br/><a target='_blank' href='" + newInstance.uri + "'>Go to your new meeting.</a>";
668
+ })["catch"](function (err) {
669
+ parameterCell.removeChild(mintUI);
670
+ parameterCell.appendChild(UI.widgets.errorMessageBlock(dom, err));
671
+ });
672
+ };
673
+
674
+ var mintUI = UI.login.selectWorkspace(dom, appDetails, gotWS);
675
+ parameterCell.appendChild(mintUI);
676
+ }; // //////////////////////////////////////////////////////////// end of new tab creation functions
677
+
678
+
679
+ var toolIcons = [{
680
+ icon: 'noun_339237.svg',
681
+ maker: makeGroup,
682
+ hint: 'Make a group of people',
683
+ limit: 1
684
+ }, {
685
+ icon: 'noun_346777.svg',
686
+ maker: makePoll,
687
+ hint: 'Make a poll to schedule the meeting'
688
+ }, // When meet THIS or NEXT time
689
+ {
690
+ icon: 'noun_48218.svg',
691
+ maker: makeAgenda,
692
+ limit: 1,
693
+ hint: 'Add an agenda list',
694
+ disabled: true
695
+ }, // When meet THIS or NEXT time
696
+ {
697
+ icon: 'noun_79217.svg',
698
+ maker: makePad,
699
+ hint: 'Add a shared notepad'
700
+ }, {
701
+ icon: 'noun_346319.svg',
702
+ maker: makeChat,
703
+ limit: 1,
704
+ hint: 'Add a chat channel for the meeting'
705
+ }, {
706
+ icon: 'noun_17020.svg',
707
+ maker: makeActions,
708
+ limit: 1,
709
+ hint: 'Add a list of action items'
710
+ }, // When meet THIS or NEXT time
711
+ {
712
+ icon: 'noun_260227.svg',
713
+ maker: makeVideoCall,
714
+ limit: 1,
715
+ hint: 'Add a video call for the meeting'
716
+ }, {
717
+ icon: 'noun_25830.svg',
718
+ maker: makeAttachment,
719
+ hint: 'Attach meeting materials',
720
+ disabled: false
721
+ }, {
722
+ icon: 'noun_123691.svg',
723
+ maker: makeSharing,
724
+ limit: 1,
725
+ hint: 'Control Sharing',
726
+ disabled: false
727
+ }, {
728
+ icon: 'noun_66617.svg',
729
+ maker: makeMeeting,
730
+ hint: 'Make a sub meeting',
731
+ disabled: false
732
+ }]; // 'noun_66617.svg'
733
+
734
+ var settingsForm = $rdf.sym('https://solid.github.io/solid-panes/meeting/meetingDetailsForm.ttl#settings');
735
+ $rdf.parse(meetingDetailsFormText, kb, settingsForm.doc().uri, 'text/turtle'); // Load form directly
736
+
737
+ var iconStyle = 'padding: 1em; width: 3em; height: 3em;';
738
+ var iconCell = toolBar.appendChild(dom.createElement('td'));
739
+ var parameterCell = toolBar.appendChild(dom.createElement('td'));
740
+ var star = iconCell.appendChild(dom.createElement('img'));
741
+ var visible = false; // the inividual tools tools
742
+
743
+ star.setAttribute('src', UI.icons.iconBase + 'noun_19460_green.svg'); // noun_272948.svg
744
+
745
+ star.setAttribute('style', iconStyle + 'opacity: 50%;');
746
+ star.setAttribute('title', 'Add another tool to the meeting');
747
+
748
+ var selectNewTool = function selectNewTool(_event) {
749
+ visible = !visible;
750
+ star.setAttribute('style', iconStyle + (visible ? 'background-color: yellow;' : ''));
751
+ styleTheIcons(visible ? '' : 'display: none;');
752
+ };
753
+
754
+ var loginOutButton;
755
+ logic.authn.checkUser().then(function (webId) {
756
+ if (webId) {
757
+ me = webId;
758
+ star.addEventListener('click', selectNewTool);
759
+ star.setAttribute('style', iconStyle);
760
+ return;
761
+ }
762
+
763
+ loginOutButton = UI.login.loginStatusBox(dom, function (webIdUri) {
764
+ if (webIdUri) {
765
+ me = kb.sym(webIdUri);
766
+ parameterCell.removeChild(loginOutButton); // loginOutButton.setAttribute('',iconStyle) // make it match the icons
767
+
768
+ star.addEventListener('click', selectNewTool);
769
+ star.setAttribute('style', iconStyle);
770
+ } else {
771
+ console.log('(Logged out)');
772
+ me = null;
773
+ }
774
+ });
775
+ loginOutButton.setAttribute('style', 'margin: 0.5em 1em;');
776
+ parameterCell.appendChild(loginOutButton);
777
+ });
778
+ var iconArray = [];
779
+
780
+ for (var i = 0; i < toolIcons.length; i++) {
781
+ var foo = function foo() {
782
+ var toolObject = toolIcons[i];
783
+ var icon = iconCell.appendChild(dom.createElement('img'));
784
+ icon.setAttribute('src', UI.icons.iconBase + toolObject.icon);
785
+ icon.setAttribute('style', iconStyle + 'display: none;');
786
+ iconArray.push(icon);
787
+ icon.tool = toolObject;
788
+ var maker = toolObject.maker;
789
+
790
+ if (!toolObject.disabled) {
791
+ icon.addEventListener('click', function (_event) {
792
+ selectTool(icon);
793
+ maker(toolObject);
794
+ });
795
+ }
796
+ };
797
+
798
+ foo();
799
+ }
800
+
801
+ var styleTheIcons = function styleTheIcons(style) {
802
+ for (var i = 0; i < iconArray.length; i++) {
803
+ var st = iconStyle + style;
804
+
805
+ if (toolIcons[i].disabled) {
806
+ st += 'opacity: 0.3;';
807
+ }
808
+
809
+ iconArray[i].setAttribute('style', st); // eg 'background-color: #ccc;'
810
+ }
811
+ };
812
+
813
+ var resetTools = function resetTools() {
814
+ styleTheIcons('display: none;');
815
+ star.setAttribute('style', iconStyle);
816
+ };
817
+
818
+ var selectTool = function selectTool(icon) {
819
+ styleTheIcons('display: none;'); // 'background-color: #ccc;'
820
+
821
+ icon.setAttribute('style', iconStyle + 'background-color: yellow;');
822
+ }; // //////////////////////////////
823
+
824
+
825
+ var renderTab = function renderTab(div, item) {
826
+ if (kb.holds(item, ns.rdf('type'), ns.meeting('Tool'))) {
827
+ var target = kb.any(item, ns.meeting('target'));
828
+ var label = kb.any(item, ns.rdfs('label'));
829
+ label = label ? label.value : UI.utils.label(target);
830
+ var s = div.appendChild(dom.createElement('div'));
831
+ s.textContent = label;
832
+ s.setAttribute('style', 'margin-left: 0.7em');
833
+ var icon = kb.any(item, ns.meeting('icon'));
834
+
835
+ if (icon) {
836
+ // Make sure the icon is cleanly on the left of the label
837
+ var table = div.appendChild(dom.createElement('table'));
838
+ var tr = table.appendChild(dom.createElement('tr'));
839
+ var left = tr.appendChild(dom.createElement('td'));
840
+ var right = tr.appendChild(dom.createElement('td')); // var img = div.appendChild(dom.createElement('img'))
841
+
842
+ var img = left.appendChild(dom.createElement('img'));
843
+ img.setAttribute('src', icon.uri); // img.setAttribute('style', 'max-width: 1.5em; max-height: 1.5em;') // @@ SVG shrinks to 0
844
+
845
+ img.setAttribute('style', 'width: 1.5em; height: 1.5em;'); // @
846
+
847
+ img.setAttribute('title', label);
848
+ right.appendChild(s);
849
+ } else {
850
+ div.appendChild(s);
851
+ }
852
+ } else {
853
+ div.textContent = UI.utils.label(item);
854
+ }
855
+ };
856
+
857
+ var tipDiv = function tipDiv(text) {
858
+ var d = dom.createElement('div');
859
+ var p = d.appendChild(dom.createElement('p'));
860
+ p.setAttribute('style', 'margin: 0em; padding:3em; color: #888;');
861
+ p.textContent = 'Tip: ' + text;
862
+ return d;
863
+ };
864
+
865
+ var renderTabSettings = function renderTabSettings(containerDiv, subject) {
866
+ containerDiv.innerHTML = '';
867
+ containerDiv.style += 'border-color: #eed;';
868
+ containerDiv.appendChild(dom.createElement('h3')).textContent = 'Adjust this tab';
869
+
870
+ if (kb.holds(subject, ns.rdf('type'), ns.meeting('Tool'))) {
871
+ var form = $rdf.sym('https://solid.github.io/solid-panes/meeting/meetingDetailsForm.ttl#settings');
872
+ UI.widgets.appendForm(document, containerDiv, {}, subject, form, meeting.doc(), complainIfBad);
873
+ var delButton = UI.widgets.deleteButtonWithCheck(dom, containerDiv, 'tab', function () {
874
+ var toolList = kb.the(meeting, ns.meeting('toolList'));
875
+
876
+ for (var i = 0; i < toolList.elements.length; i++) {
877
+ if (toolList.elements[i].sameTerm(subject)) {
878
+ toolList.elements.splice(i, 1);
879
+ break;
880
+ }
881
+ }
882
+
883
+ var target = kb.any(subject, ns.meeting('target'));
884
+ var ds = kb.statementsMatching(subject).concat(kb.statementsMatching(undefined, undefined, subject)).concat(kb.statementsMatching(meeting, undefined, target));
885
+ kb.remove(ds); // Remove all links to and from the tab node
886
+
887
+ saveBackMeetingDoc();
888
+ });
889
+ delButton.setAttribute('style', 'width: 1.5em; height: 1.5em;'); // delButton.setAttribute('class', '')
890
+ // delButton.setAttribute('style', 'height: 2em; width: 2em; margin: 1em; border-radius: 0.5em; padding: 1em; font-size: 120%; background-color: red; color: white;')
891
+ // delButton.textContent = 'Delete this tab'
892
+ } else {
893
+ containerDiv.appendChild(dom.createElement('h4')).textContent = '(No adjustments available)';
894
+ }
895
+ };
896
+
897
+ var renderMain = function renderMain(containerDiv, subject) {
898
+ var pane = null;
899
+ var table;
900
+ var selectedGroup = null;
901
+ containerDiv.innerHTML = '';
902
+
903
+ var complainIfBad = function complainIfBad(ok, message) {
904
+ if (!ok) {
905
+ containerDiv.textContent = '' + message;
906
+ }
907
+ };
908
+
909
+ var showIframe = function showIframe(target) {
910
+ var iframe = containerDiv.appendChild(dom.createElement('iframe')); // iframe.setAttribute('sandbox', '') // All restrictions
911
+
912
+ iframe.setAttribute('src', target.uri); // See https://stackoverflow.com/questions/325273/make-iframe-to-fit-100-of-containers-remaining-height
913
+ // Set the container position (sic) so it becaomes a 100% reference for the size of the iframe height 100%
914
+
915
+ /* For now at least , leave the container style as set by the tab system. 20200115b
916
+ containerDiv.setAttribute(
917
+ 'style',
918
+ 'position: relative; top: 0px; left:0px; right:0px; resize: both; overflow:scroll; min-width: 30em; min-height: 30em;'
919
+ )
920
+ */
921
+ // iframe.setAttribute('style', 'height: 350px; border: 0; margin: 0; padding: 0; resize:both; overflow:scroll; width: 100%;')
922
+ // iframe.setAttribute('style', 'border: none; margin: 0; padding: 0; height: 100%; width: 100%; resize: both; overflow:scroll;')
923
+
924
+ iframe.setAttribute('style', 'border: none; margin: 0; padding: 0; height: 100%; width: 100%;'); // Following https://dev.chromium.org/Home/chromium-security/deprecating-permissions-in-cross-origin-iframes :
925
+
926
+ iframe.setAttribute('allow', 'microphone camera'); // Allow iframe to request camera and mic
927
+ // containerDiv.style.resize = 'none' // Remove scroll bars on outer div - don't seem to work so well
928
+
929
+ iframe.setAttribute('name', 'disable-x-frame-options'); // For electron: see https://github.com/electron/electron/pull/573
930
+
931
+ containerDiv.style.padding = 0;
932
+ };
933
+
934
+ var renderPeoplePicker = function renderPeoplePicker() {
935
+ var context = {
936
+ div: containerDiv,
937
+ dom: dom
938
+ };
939
+ containerDiv.appendChild(dom.createElement('h4')).textContent = 'Meeting Participants';
940
+
941
+ var groupPickedCb = function groupPickedCb(group) {
942
+ var toIns = [$rdf.st(meeting, ns.meeting('particpantGroup'), group, meeting.doc())];
943
+ kb.updater.update([], toIns, function (uri, ok, message) {
944
+ if (ok) {
945
+ selectedGroup = group;
946
+ } else {
947
+ complain('Cant save participants group: ' + message);
948
+ }
949
+ });
950
+ };
951
+
952
+ selectedGroup = kb.any(meeting, ns.meeting('particpantGroup'));
953
+ logic.loadTypeIndexes(context).then(function () {
954
+ // Assumes that the type index has an entry for addressbook
955
+ var options = {
956
+ defaultNewGroupName: 'Meeting Participants',
957
+ selectedGroup: selectedGroup
958
+ };
959
+ var picker = new UI.widgets.PeoplePicker(context.div, context.index["private"][0], groupPickedCb, options);
960
+ picker.render();
961
+ });
962
+ };
963
+
964
+ var renderDetails = function renderDetails() {
965
+ containerDiv.appendChild(dom.createElement('h3')).textContent = 'Details of meeting';
966
+ var form = $rdf.sym('https://solid.github.io/solid-panes/meeting/meetingDetailsForm.ttl#main');
967
+ UI.widgets.appendForm(document, containerDiv, {}, meeting, form, meeting.doc(), complainIfBad);
968
+ containerDiv.appendChild(tipDiv('Drag URL-bar icons of web pages into the tab bar on the left to add new meeting materials.'));
969
+ me = logic.authn.currentUser();
970
+
971
+ if (me) {
972
+ kb.add(meeting, ns.dc('author'), me, meetingDoc); // @@ should nly be on initial creation?
973
+ }
974
+
975
+ var context = {
976
+ noun: 'meeting',
977
+ me: me,
978
+ statusArea: containerDiv,
979
+ div: containerDiv,
980
+ dom: dom
981
+ };
982
+ UI.login.registrationControl(context, meeting, ns.meeting('Meeting')).then(function (_context) {
983
+ console.log('Registration control finsished.');
984
+ });
985
+ var options = {};
986
+ UI.pad.manageParticipation(dom, containerDiv, meetingDoc, meeting, me, options); // "Make a new meeting" button
987
+
988
+ var imageStyle = 'height: 2em; width: 2em; margin:0.5em;';
989
+ var detailsBottom = containerDiv.appendChild(dom.createElement('div'));
990
+ var spawn = detailsBottom.appendChild(dom.createElement('img'));
991
+ spawn.setAttribute('src', UI.icons.iconBase + 'noun_145978.svg');
992
+ spawn.setAttribute('title', 'Make a fresh new meeting');
993
+ spawn.addEventListener('click', makeNewMeeting);
994
+ spawn.setAttribute('style', imageStyle); // "Fork me on Github" button
995
+
996
+ var forka = detailsBottom.appendChild(dom.createElement('a'));
997
+ forka.setAttribute('href', 'https://github.com/solid/solid-panes'); // @@ Move when code moves
998
+
999
+ forka.setAttribute('target', '_blank');
1000
+ var fork = forka.appendChild(dom.createElement('img'));
1001
+ fork.setAttribute('src', UI.icons.iconBase + 'noun_368567.svg');
1002
+ fork.setAttribute('title', 'Fork me on github');
1003
+ fork.setAttribute('style', imageStyle + 'opacity: 50%;');
1004
+ };
1005
+
1006
+ if (kb.holds(subject, ns.rdf('type'), ns.meeting('Tool'))) {
1007
+ var target = kb.any(subject, ns.meeting('target'));
1008
+
1009
+ if (target.sameTerm(meeting) && !kb.any(subject, ns.meeting('view'))) {
1010
+ // self reference? force details form
1011
+ renderDetails(); // Legacy meeting instances
1012
+ } else {
1013
+ var view = kb.any(subject, ns.meeting('view'));
1014
+ view = view ? view.value : null;
1015
+
1016
+ if (view === 'details') {
1017
+ renderDetails();
1018
+ } else if (view === 'peoplePicker') {
1019
+ renderPeoplePicker();
1020
+ } else if (view === 'iframe') {
1021
+ showIframe(target);
1022
+ } else {
1023
+ pane = view ? dataBrowserContext.session.paneRegistry.byName(view) : null;
1024
+ table = containerDiv.appendChild(dom.createElement('table'));
1025
+ table.style.width = '100%';
1026
+ dataBrowserContext.getOutliner(dom).GotoSubject(target, true, pane, false, undefined, table);
1027
+ }
1028
+ }
1029
+ } else if (subject.sameTerm(meeting)) {
1030
+ // self reference? force details form
1031
+ renderDetails();
1032
+ } else if (subject.sameTerm(subject.doc()) && !kb.holds(subject, UI.ns.rdf('type'), UI.ns.meeting('Chat')) && !kb.holds(subject, UI.ns.rdf('type'), UI.ns.meeting('PaneView')) // eslint-disable-next-line no-empty
1033
+ ) {} else {
1034
+ table = containerDiv.appendChild(dom.createElement('table'));
1035
+ dataBrowserContext.getOutliner(dom).GotoSubject(subject, true, undefined, false, undefined, table);
1036
+ }
1037
+ };
1038
+
1039
+ var options = {
1040
+ dom: dom
1041
+ };
1042
+ options.predicate = ns.meeting('toolList');
1043
+ options.subject = subject;
1044
+ options.ordered = true;
1045
+ options.orientation = 1; // tabs on LHS
1046
+
1047
+ options.renderMain = renderMain;
1048
+ options.renderTab = renderTab;
1049
+ options.renderTabSettings = renderTabSettings;
1050
+ options.backgroundColor = kb.anyValue(subject, ns.ui('backgroundColor')) || '#ddddcc';
1051
+ var tabs = mainTR.appendChild(UI.tabs.tabWidget(options));
1052
+ UI.aclControl.preventBrowserDropEvents(dom);
1053
+ UI.widgets.makeDropTarget(tabs.tabContainer, droppedURIHandler, droppedFileHandler);
1054
+ UI.widgets.makeDropTarget(iconCell, droppedURIHandler, droppedFileHandler);
1055
+ return div;
1056
+ }
1057
+ }; // ends
1058
+ //# sourceMappingURL=meetingPane.js.map