solid-ui 2.6.1 → 3.0.0-63a1640
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/LICENSE.md +3 -1
- package/README.md +236 -30
- package/dist/acl/access-controller.js +238 -0
- package/dist/acl/access-controller.js.map +1 -0
- package/{lib → dist}/acl/access-groups.d.ts +2 -2
- package/{lib → dist}/acl/access-groups.d.ts.map +1 -1
- package/dist/acl/access-groups.js +323 -0
- package/dist/acl/access-groups.js.map +1 -0
- package/dist/acl/acl-control.js +173 -0
- package/dist/acl/acl-control.js.map +1 -0
- package/dist/acl/acl.js +495 -0
- package/dist/acl/acl.js.map +1 -0
- package/dist/acl/add-agent-buttons.js +217 -0
- package/dist/acl/add-agent-buttons.js.map +1 -0
- package/dist/acl/index.js +32 -0
- package/dist/acl/index.js.map +1 -0
- package/dist/acl/types.js +6 -0
- package/dist/acl/types.js.map +1 -0
- package/dist/chat/keys.js +106 -0
- package/dist/chat/keys.js.map +1 -0
- package/dist/chat/signature.js +63 -0
- package/dist/chat/signature.js.map +1 -0
- package/dist/create/create.js +249 -0
- package/dist/create/create.js.map +1 -0
- package/dist/create/index.js +5 -0
- package/dist/create/index.js.map +1 -0
- package/dist/create/types.js +2 -0
- package/dist/create/types.js.map +1 -0
- package/dist/debug.d.ts.map +1 -0
- package/dist/debug.js +13 -0
- package/dist/debug.js.map +1 -0
- package/dist/footer/index.js +67 -0
- package/dist/footer/index.js.map +1 -0
- package/dist/header/empty-profile.js +11 -0
- package/dist/header/empty-profile.js.map +1 -0
- package/dist/header/index.js +260 -0
- package/dist/header/index.js.map +1 -0
- package/dist/iconBase.js +37 -0
- package/dist/iconBase.js.map +1 -0
- package/dist/icons/solid_logo.js.map +1 -0
- package/{lib → dist}/index.d.ts +7 -9
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +102 -0
- package/dist/index.js.map +1 -0
- package/{lib → dist}/log.d.ts.map +1 -1
- package/dist/log.js +182 -0
- package/dist/log.js.map +1 -0
- package/dist/login/login.js +858 -0
- package/dist/login/login.js.map +1 -0
- package/dist/matrix/index.js +5 -0
- package/dist/matrix/index.js.map +1 -0
- package/dist/matrix/matrix.js +217 -0
- package/dist/matrix/matrix.js.map +1 -0
- package/dist/matrix/types.js +2 -0
- package/dist/matrix/types.js.map +1 -0
- package/dist/media/index.js +6 -0
- package/dist/media/index.js.map +1 -0
- package/dist/media/media-capture.js +161 -0
- package/dist/media/media-capture.js.map +1 -0
- package/dist/pad.js +775 -0
- package/dist/pad.js.map +1 -0
- package/{lib → dist}/participation.d.ts.map +1 -1
- package/dist/participation.js +184 -0
- package/dist/participation.js.map +1 -0
- package/dist/solid-ui.esm.js +25531 -0
- package/dist/solid-ui.esm.js.map +1 -0
- package/dist/solid-ui.esm.min.js +43 -0
- package/dist/solid-ui.esm.min.js.map +1 -0
- package/dist/solid-ui.js +23479 -68931
- package/dist/solid-ui.js.map +1 -1
- package/dist/solid-ui.min.js +40 -2
- package/dist/solid-ui.min.js.map +1 -1
- package/dist/tabs.js +388 -0
- package/dist/tabs.js.map +1 -0
- package/{lib → dist}/utils/headerFooterHelpers.d.ts.map +1 -1
- package/dist/utils/headerFooterHelpers.js +114 -0
- package/dist/utils/headerFooterHelpers.js.map +1 -0
- package/dist/utils/keyHelpers/accessData.js +64 -0
- package/dist/utils/keyHelpers/accessData.js.map +1 -0
- package/dist/utils/keyHelpers/acl.js +74 -0
- package/dist/utils/keyHelpers/acl.js.map +1 -0
- package/dist/utils/keyHelpers/otherHelpers.js +13 -0
- package/dist/utils/keyHelpers/otherHelpers.js.map +1 -0
- package/dist/utils/label.js +111 -0
- package/dist/utils/label.js.map +1 -0
- package/dist/versionInfo.js +30 -0
- package/dist/versionInfo.js.map +1 -0
- package/dist/widgets/buttons/iconLinks.js +44 -0
- package/dist/widgets/buttons/iconLinks.js.map +1 -0
- package/dist/widgets/buttons.js +1280 -0
- package/dist/widgets/buttons.js.map +1 -0
- package/dist/widgets/error.d.ts +14 -0
- package/dist/widgets/error.d.ts.map +1 -0
- package/dist/widgets/error.js +35 -0
- package/dist/widgets/error.js.map +1 -0
- package/dist/widgets/forms/autocomplete/autocompleteBar.js +123 -0
- package/dist/widgets/forms/autocomplete/autocompleteBar.js.map +1 -0
- package/dist/widgets/forms/autocomplete/autocompleteField.js +199 -0
- package/dist/widgets/forms/autocomplete/autocompleteField.js.map +1 -0
- package/dist/widgets/forms/autocomplete/autocompletePicker.js +256 -0
- package/dist/widgets/forms/autocomplete/autocompletePicker.js.map +1 -0
- package/dist/widgets/forms/autocomplete/language.js +104 -0
- package/dist/widgets/forms/autocomplete/language.js.map +1 -0
- package/dist/widgets/forms/autocomplete/publicData.js +460 -0
- package/dist/widgets/forms/autocomplete/publicData.js.map +1 -0
- package/dist/widgets/forms/basic.js +241 -0
- package/dist/widgets/forms/basic.js.map +1 -0
- package/dist/widgets/forms/comment.js +46 -0
- package/dist/widgets/forms/comment.js.map +1 -0
- package/dist/widgets/forms/fieldFunction.js +44 -0
- package/dist/widgets/forms/fieldFunction.js.map +1 -0
- package/dist/widgets/forms/fieldParams.js +89 -0
- package/dist/widgets/forms/fieldParams.js.map +1 -0
- package/{lib → dist}/widgets/forms/formStyle.d.ts.map +1 -1
- package/dist/widgets/forms/formStyle.js +36 -0
- package/dist/widgets/forms/formStyle.js.map +1 -0
- package/{lib → dist}/widgets/widgetHelpers.d.ts.map +1 -1
- package/{lib → dist}/widgets/widgetHelpers.js +14 -25
- package/dist/widgets/widgetHelpers.js.map +1 -0
- package/package.json +48 -52
- package/dist/789.solid-ui.min.js +0 -1
- package/dist/841.solid-ui.min.js +0 -3
- package/dist/841.solid-ui.min.js.LICENSE.txt +0 -58
- package/dist/841.solid-ui.min.js.map +0 -1
- package/dist/_2b19.solid-ui.js +0 -14
- package/dist/_2b19.solid-ui.js.map +0 -1
- package/dist/index.html +0 -1
- package/dist/solid-ui.min.js.LICENSE.txt +0 -57
- package/dist/vendors-node_modules_jsonld_lib_jsonld_js.solid-ui.js +0 -12247
- package/dist/vendors-node_modules_jsonld_lib_jsonld_js.solid-ui.js.map +0 -1
- package/lib/acl/access-controller.js +0 -391
- package/lib/acl/access-controller.js.map +0 -1
- package/lib/acl/access-groups.js +0 -507
- package/lib/acl/access-groups.js.map +0 -1
- package/lib/acl/acl-control.js +0 -237
- package/lib/acl/acl-control.js.map +0 -1
- package/lib/acl/acl.js +0 -517
- package/lib/acl/acl.js.map +0 -1
- package/lib/acl/add-agent-buttons.js +0 -434
- package/lib/acl/add-agent-buttons.js.map +0 -1
- package/lib/acl/index.js +0 -39
- package/lib/acl/index.js.map +0 -1
- package/lib/acl/types.js +0 -6
- package/lib/acl/types.js.map +0 -1
- package/lib/chat/bookmarks.js +0 -303
- package/lib/chat/bookmarks.js.map +0 -1
- package/lib/chat/chatLogic.js +0 -420
- package/lib/chat/chatLogic.js.map +0 -1
- package/lib/chat/dateFolder.js +0 -328
- package/lib/chat/dateFolder.js.map +0 -1
- package/lib/chat/infinite.js +0 -994
- package/lib/chat/infinite.js.map +0 -1
- package/lib/chat/keys.js +0 -232
- package/lib/chat/keys.js.map +0 -1
- package/lib/chat/message.js +0 -715
- package/lib/chat/message.js.map +0 -1
- package/lib/chat/messageTools.js +0 -538
- package/lib/chat/messageTools.js.map +0 -1
- package/lib/chat/signature.js +0 -109
- package/lib/chat/signature.js.map +0 -1
- package/lib/chat/thread.js +0 -535
- package/lib/chat/thread.js.map +0 -1
- package/lib/create/create.js +0 -242
- package/lib/create/create.js.map +0 -1
- package/lib/create/index.js +0 -11
- package/lib/create/index.js.map +0 -1
- package/lib/create/types.js +0 -6
- package/lib/create/types.js.map +0 -1
- package/lib/debug.d.ts.map +0 -1
- package/lib/debug.js +0 -30
- package/lib/debug.js.map +0 -1
- package/lib/folders.js +0 -132
- package/lib/folders.js.map +0 -1
- package/lib/footer/index.js +0 -123
- package/lib/footer/index.js.map +0 -1
- package/lib/header/empty-profile.js +0 -8
- package/lib/header/empty-profile.js.map +0 -1
- package/lib/header/index.js +0 -375
- package/lib/header/index.js.map +0 -1
- package/lib/iconBase.js +0 -44
- package/lib/iconBase.js.map +0 -1
- package/lib/icons/solid_logo.js.map +0 -1
- package/lib/index.d.ts.map +0 -1
- package/lib/index.js +0 -223
- package/lib/index.js.map +0 -1
- package/lib/log.js +0 -213
- package/lib/log.js.map +0 -1
- package/lib/login/login.js +0 -1241
- package/lib/login/login.js.map +0 -1
- package/lib/matrix/index.js +0 -11
- package/lib/matrix/index.js.map +0 -1
- package/lib/matrix/matrix.js +0 -216
- package/lib/matrix/matrix.js.map +0 -1
- package/lib/matrix/types.js +0 -6
- package/lib/matrix/types.js.map +0 -1
- package/lib/media/index.js +0 -12
- package/lib/media/index.js.map +0 -1
- package/lib/media/media-capture.js +0 -194
- package/lib/media/media-capture.js.map +0 -1
- package/lib/messageArea.js +0 -319
- package/lib/messageArea.js.map +0 -1
- package/lib/noun_Camera_1618446_000000.js +0 -8
- package/lib/noun_Camera_1618446_000000.js.map +0 -1
- package/lib/ns.js +0 -17
- package/lib/ns.js.map +0 -1
- package/lib/pad.js +0 -805
- package/lib/pad.js.map +0 -1
- package/lib/participation.js +0 -219
- package/lib/participation.js.map +0 -1
- package/lib/preferences.js +0 -215
- package/lib/preferences.js.map +0 -1
- package/lib/signup/config-default.js +0 -43
- package/lib/signup/config-default.js.map +0 -1
- package/lib/signup/signup.js +0 -74
- package/lib/signup/signup.js.map +0 -1
- package/lib/stories/decorators.js +0 -10
- package/lib/stories/decorators.js.map +0 -1
- package/lib/style.js +0 -158
- package/lib/style.js.map +0 -1
- package/lib/styleConstants.js +0 -35
- package/lib/styleConstants.js.map +0 -1
- package/lib/style_multiSelect.js +0 -62
- package/lib/style_multiSelect.js.map +0 -1
- package/lib/table.js +0 -1573
- package/lib/table.js.map +0 -1
- package/lib/tabs.js +0 -448
- package/lib/tabs.js.map +0 -1
- package/lib/typings.d.js +0 -2
- package/lib/typings.d.js.map +0 -1
- package/lib/utils/headerFooterHelpers.js +0 -165
- package/lib/utils/headerFooterHelpers.js.map +0 -1
- package/lib/utils/index.js +0 -527
- package/lib/utils/index.js.map +0 -1
- package/lib/utils/keyHelpers/accessData.js +0 -131
- package/lib/utils/keyHelpers/accessData.js.map +0 -1
- package/lib/utils/keyHelpers/acl.js +0 -90
- package/lib/utils/keyHelpers/acl.js.map +0 -1
- package/lib/utils/keyHelpers/otherHelpers.js +0 -21
- package/lib/utils/keyHelpers/otherHelpers.js.map +0 -1
- package/lib/utils/label.js +0 -103
- package/lib/utils/label.js.map +0 -1
- package/lib/versionInfo.d.ts +0 -32
- package/lib/versionInfo.d.ts.map +0 -1
- package/lib/versionInfo.js +0 -37
- package/lib/versionInfo.js.map +0 -1
- package/lib/widgets/buttons/iconLinks.js +0 -53
- package/lib/widgets/buttons/iconLinks.js.map +0 -1
- package/lib/widgets/buttons.js +0 -1306
- package/lib/widgets/buttons.js.map +0 -1
- package/lib/widgets/dragAndDrop.js +0 -194
- package/lib/widgets/dragAndDrop.js.map +0 -1
- package/lib/widgets/error.d.ts +0 -2
- package/lib/widgets/error.d.ts.map +0 -1
- package/lib/widgets/error.js +0 -46
- package/lib/widgets/error.js.map +0 -1
- package/lib/widgets/forms/autocomplete/autocompleteBar.js +0 -271
- package/lib/widgets/forms/autocomplete/autocompleteBar.js.map +0 -1
- package/lib/widgets/forms/autocomplete/autocompleteField.js +0 -258
- package/lib/widgets/forms/autocomplete/autocompleteField.js.map +0 -1
- package/lib/widgets/forms/autocomplete/autocompletePicker.js +0 -436
- package/lib/widgets/forms/autocomplete/autocompletePicker.js.map +0 -1
- package/lib/widgets/forms/autocomplete/language.js +0 -189
- package/lib/widgets/forms/autocomplete/language.js.map +0 -1
- package/lib/widgets/forms/autocomplete/publicData.js +0 -636
- package/lib/widgets/forms/autocomplete/publicData.js.map +0 -1
- package/lib/widgets/forms/basic.js +0 -254
- package/lib/widgets/forms/basic.js.map +0 -1
- package/lib/widgets/forms/comment.js +0 -54
- package/lib/widgets/forms/comment.js.map +0 -1
- package/lib/widgets/forms/fieldFunction.js +0 -52
- package/lib/widgets/forms/fieldFunction.js.map +0 -1
- package/lib/widgets/forms/fieldParams.js +0 -77
- package/lib/widgets/forms/fieldParams.js.map +0 -1
- package/lib/widgets/forms/formStyle.js +0 -44
- package/lib/widgets/forms/formStyle.js.map +0 -1
- package/lib/widgets/forms.js +0 -2045
- package/lib/widgets/forms.js.map +0 -1
- package/lib/widgets/index.js +0 -110
- package/lib/widgets/index.js.map +0 -1
- package/lib/widgets/multiSelect.js +0 -658
- package/lib/widgets/multiSelect.js.map +0 -1
- package/lib/widgets/peoplePicker.js +0 -467
- package/lib/widgets/peoplePicker.js.map +0 -1
- package/lib/widgets/widgetHelpers.js.map +0 -1
- /package/{lib → dist}/acl/access-controller.d.ts +0 -0
- /package/{lib → dist}/acl/access-controller.d.ts.map +0 -0
- /package/{lib → dist}/acl/acl-control.d.ts +0 -0
- /package/{lib → dist}/acl/acl-control.d.ts.map +0 -0
- /package/{lib → dist}/acl/acl.d.ts +0 -0
- /package/{lib → dist}/acl/acl.d.ts.map +0 -0
- /package/{lib → dist}/acl/add-agent-buttons.d.ts +0 -0
- /package/{lib → dist}/acl/add-agent-buttons.d.ts.map +0 -0
- /package/{lib → dist}/acl/index.d.ts +0 -0
- /package/{lib → dist}/acl/index.d.ts.map +0 -0
- /package/{lib → dist}/acl/types.d.ts +0 -0
- /package/{lib → dist}/acl/types.d.ts.map +0 -0
- /package/{lib → dist}/chat/keys.d.ts +0 -0
- /package/{lib → dist}/chat/keys.d.ts.map +0 -0
- /package/{lib → dist}/chat/signature.d.ts +0 -0
- /package/{lib → dist}/chat/signature.d.ts.map +0 -0
- /package/{lib → dist}/create/create.d.ts +0 -0
- /package/{lib → dist}/create/create.d.ts.map +0 -0
- /package/{lib → dist}/create/index.d.ts +0 -0
- /package/{lib → dist}/create/index.d.ts.map +0 -0
- /package/{lib → dist}/create/types.d.ts +0 -0
- /package/{lib → dist}/create/types.d.ts.map +0 -0
- /package/{lib → dist}/debug.d.ts +0 -0
- /package/{lib → dist}/footer/index.d.ts +0 -0
- /package/{lib → dist}/footer/index.d.ts.map +0 -0
- /package/{lib → dist}/header/empty-profile.d.ts +0 -0
- /package/{lib → dist}/header/empty-profile.d.ts.map +0 -0
- /package/{lib → dist}/header/index.d.ts +0 -0
- /package/{lib → dist}/header/index.d.ts.map +0 -0
- /package/{lib → dist}/iconBase.d.ts +0 -0
- /package/{lib → dist}/iconBase.d.ts.map +0 -0
- /package/{lib → dist}/icons/solid_logo.d.ts +0 -0
- /package/{lib → dist}/icons/solid_logo.d.ts.map +0 -0
- /package/{lib → dist}/icons/solid_logo.js +0 -0
- /package/{lib → dist}/log.d.ts +0 -0
- /package/{lib → dist}/login/login.d.ts +0 -0
- /package/{lib → dist}/login/login.d.ts.map +0 -0
- /package/{lib → dist}/matrix/index.d.ts +0 -0
- /package/{lib → dist}/matrix/index.d.ts.map +0 -0
- /package/{lib → dist}/matrix/matrix.d.ts +0 -0
- /package/{lib → dist}/matrix/matrix.d.ts.map +0 -0
- /package/{lib → dist}/matrix/types.d.ts +0 -0
- /package/{lib → dist}/matrix/types.d.ts.map +0 -0
- /package/{lib → dist}/media/index.d.ts +0 -0
- /package/{lib → dist}/media/index.d.ts.map +0 -0
- /package/{lib → dist}/media/media-capture.d.ts +0 -0
- /package/{lib → dist}/media/media-capture.d.ts.map +0 -0
- /package/{lib → dist}/pad.d.ts +0 -0
- /package/{lib → dist}/pad.d.ts.map +0 -0
- /package/{lib → dist}/participation.d.ts +0 -0
- /package/{lib → dist}/tabs.d.ts +0 -0
- /package/{lib → dist}/tabs.d.ts.map +0 -0
- /package/{lib → dist}/utils/headerFooterHelpers.d.ts +0 -0
- /package/{lib → dist}/utils/keyHelpers/accessData.d.ts +0 -0
- /package/{lib → dist}/utils/keyHelpers/accessData.d.ts.map +0 -0
- /package/{lib → dist}/utils/keyHelpers/acl.d.ts +0 -0
- /package/{lib → dist}/utils/keyHelpers/acl.d.ts.map +0 -0
- /package/{lib → dist}/utils/keyHelpers/otherHelpers.d.ts +0 -0
- /package/{lib → dist}/utils/keyHelpers/otherHelpers.d.ts.map +0 -0
- /package/{lib → dist}/utils/label.d.ts +0 -0
- /package/{lib → dist}/utils/label.d.ts.map +0 -0
- /package/{lib → dist}/widgets/buttons/iconLinks.d.ts +0 -0
- /package/{lib → dist}/widgets/buttons/iconLinks.d.ts.map +0 -0
- /package/{lib → dist}/widgets/buttons.d.ts +0 -0
- /package/{lib → dist}/widgets/buttons.d.ts.map +0 -0
- /package/{lib → dist}/widgets/forms/autocomplete/autocompleteBar.d.ts +0 -0
- /package/{lib → dist}/widgets/forms/autocomplete/autocompleteBar.d.ts.map +0 -0
- /package/{lib → dist}/widgets/forms/autocomplete/autocompleteField.d.ts +0 -0
- /package/{lib → dist}/widgets/forms/autocomplete/autocompleteField.d.ts.map +0 -0
- /package/{lib → dist}/widgets/forms/autocomplete/autocompletePicker.d.ts +0 -0
- /package/{lib → dist}/widgets/forms/autocomplete/autocompletePicker.d.ts.map +0 -0
- /package/{lib → dist}/widgets/forms/autocomplete/language.d.ts +0 -0
- /package/{lib → dist}/widgets/forms/autocomplete/language.d.ts.map +0 -0
- /package/{lib → dist}/widgets/forms/autocomplete/publicData.d.ts +0 -0
- /package/{lib → dist}/widgets/forms/autocomplete/publicData.d.ts.map +0 -0
- /package/{lib → dist}/widgets/forms/basic.d.ts +0 -0
- /package/{lib → dist}/widgets/forms/basic.d.ts.map +0 -0
- /package/{lib → dist}/widgets/forms/comment.d.ts +0 -0
- /package/{lib → dist}/widgets/forms/comment.d.ts.map +0 -0
- /package/{lib → dist}/widgets/forms/fieldFunction.d.ts +0 -0
- /package/{lib → dist}/widgets/forms/fieldFunction.d.ts.map +0 -0
- /package/{lib → dist}/widgets/forms/fieldParams.d.ts +0 -0
- /package/{lib → dist}/widgets/forms/fieldParams.d.ts.map +0 -0
- /package/{lib → dist}/widgets/forms/formStyle.d.ts +0 -0
- /package/{lib → dist}/widgets/widgetHelpers.d.ts +0 -0
|
@@ -0,0 +1,1280 @@
|
|
|
1
|
+
/* Buttons
|
|
2
|
+
*/
|
|
3
|
+
import { st, sym, uri, Util } from 'rdflib';
|
|
4
|
+
import { icons } from '../iconBase';
|
|
5
|
+
import ns from '../ns';
|
|
6
|
+
import { style } from '../style';
|
|
7
|
+
import * as debug from '../debug';
|
|
8
|
+
import { info } from '../log';
|
|
9
|
+
import { uploadFiles, makeDraggable, makeDropTarget } from './dragAndDrop';
|
|
10
|
+
import { store } from 'solid-logic';
|
|
11
|
+
import * as utils from '../utils';
|
|
12
|
+
import { errorMessageBlock } from './error';
|
|
13
|
+
import { addClickListenerToElement, createImageDiv, wrapDivInATR } from './widgetHelpers';
|
|
14
|
+
import { linkIcon, createLinkForURI } from './buttons/iconLinks';
|
|
15
|
+
/**
|
|
16
|
+
* UI Widgets such as buttons
|
|
17
|
+
* @packageDocumentation
|
|
18
|
+
*/
|
|
19
|
+
/* global alert */
|
|
20
|
+
const { iconBase } = icons;
|
|
21
|
+
const cancelIconURI = iconBase + 'noun_1180156.svg'; // black X
|
|
22
|
+
const checkIconURI = iconBase + 'noun_1180158.svg'; // green checkmark; Continue
|
|
23
|
+
function getStatusArea(context) {
|
|
24
|
+
let box = (context && context.statusArea) || (context && context.div) || null;
|
|
25
|
+
if (box)
|
|
26
|
+
return box;
|
|
27
|
+
let dom = context && context.dom;
|
|
28
|
+
if (!dom && typeof document !== 'undefined') {
|
|
29
|
+
dom = document;
|
|
30
|
+
}
|
|
31
|
+
if (dom) {
|
|
32
|
+
const body = dom.getElementsByTagName('body')[0];
|
|
33
|
+
box = dom.createElement('div');
|
|
34
|
+
body.insertBefore(box, body.firstElementChild);
|
|
35
|
+
if (context) {
|
|
36
|
+
context.statusArea = box;
|
|
37
|
+
}
|
|
38
|
+
return box;
|
|
39
|
+
}
|
|
40
|
+
return null;
|
|
41
|
+
}
|
|
42
|
+
/**
|
|
43
|
+
* Display an error message block
|
|
44
|
+
*/
|
|
45
|
+
export function complain(context, err) {
|
|
46
|
+
if (!err)
|
|
47
|
+
return; // only if error
|
|
48
|
+
const ele = getStatusArea(context);
|
|
49
|
+
debug.log('Complaint: ' + err);
|
|
50
|
+
if (ele)
|
|
51
|
+
ele.appendChild(errorMessageBlock((context && context.dom) || document, err));
|
|
52
|
+
else
|
|
53
|
+
alert(err);
|
|
54
|
+
}
|
|
55
|
+
/**
|
|
56
|
+
* Remove all the children of an HTML element
|
|
57
|
+
*/
|
|
58
|
+
export function clearElement(ele) {
|
|
59
|
+
while (ele.firstChild) {
|
|
60
|
+
ele.removeChild(ele.firstChild);
|
|
61
|
+
}
|
|
62
|
+
return ele;
|
|
63
|
+
}
|
|
64
|
+
/**
|
|
65
|
+
* To figure out the log URI from the full URI used to invoke the reasoner
|
|
66
|
+
*/
|
|
67
|
+
export function extractLogURI(fullURI) {
|
|
68
|
+
const logPos = fullURI.search(/logFile=/);
|
|
69
|
+
const rulPos = fullURI.search(/&rulesFile=/);
|
|
70
|
+
return fullURI.substring(logPos + 8, rulPos);
|
|
71
|
+
}
|
|
72
|
+
/**
|
|
73
|
+
* By default, converts e.g. '2020-02-19T19:35:28.557Z' to '19:35'
|
|
74
|
+
* if today is 19 Feb 2020, and to 'Feb 19' if not.
|
|
75
|
+
* @@@ TODO This needs to be changed to local time
|
|
76
|
+
* @param noTime Return a string like 'Feb 19' even if it's today.
|
|
77
|
+
*/
|
|
78
|
+
export function shortDate(str, noTime) {
|
|
79
|
+
if (!str)
|
|
80
|
+
return '???';
|
|
81
|
+
const month = [
|
|
82
|
+
'Jan',
|
|
83
|
+
'Feb',
|
|
84
|
+
'Mar',
|
|
85
|
+
'Apr',
|
|
86
|
+
'May',
|
|
87
|
+
'Jun',
|
|
88
|
+
'Jul',
|
|
89
|
+
'Aug',
|
|
90
|
+
'Sep',
|
|
91
|
+
'Oct',
|
|
92
|
+
'Nov',
|
|
93
|
+
'Dec'
|
|
94
|
+
];
|
|
95
|
+
try {
|
|
96
|
+
const nowZ = new Date().toISOString();
|
|
97
|
+
// var nowZ = $rdf.term(now).value
|
|
98
|
+
// var n = now.getTimezoneOffset() // Minutes
|
|
99
|
+
if (str.slice(0, 10) === nowZ.slice(0, 10) && !noTime) {
|
|
100
|
+
return str.slice(11, 16);
|
|
101
|
+
}
|
|
102
|
+
if (str.slice(0, 4) === nowZ.slice(0, 4)) {
|
|
103
|
+
return (month[parseInt(str.slice(5, 7), 10) - 1] +
|
|
104
|
+
' ' +
|
|
105
|
+
parseInt(str.slice(8, 10), 10));
|
|
106
|
+
}
|
|
107
|
+
return str.slice(0, 10);
|
|
108
|
+
}
|
|
109
|
+
catch (e) {
|
|
110
|
+
return 'shortdate:' + e;
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
/**
|
|
114
|
+
* Format a date and time
|
|
115
|
+
* @param date for instance `new Date()`
|
|
116
|
+
* @param format for instance '{FullYear}-{Month}-{Date}T{Hours}:{Minutes}:{Seconds}.{Milliseconds}'
|
|
117
|
+
* @returns for instance '2000-01-15T23:14:23.002'
|
|
118
|
+
*/
|
|
119
|
+
export function formatDateTime(date, format) {
|
|
120
|
+
return format
|
|
121
|
+
.split('{')
|
|
122
|
+
.map(function (s) {
|
|
123
|
+
const k = s.split('}')[0];
|
|
124
|
+
const width = { Milliseconds: 3, FullYear: 4 };
|
|
125
|
+
const d = { Month: 1 };
|
|
126
|
+
return s
|
|
127
|
+
? ('000' + (date['get' + k]() + (d[k] || 0))).slice(-(width[k] || 2)) + s.split('}')[1]
|
|
128
|
+
: '';
|
|
129
|
+
})
|
|
130
|
+
.join('');
|
|
131
|
+
}
|
|
132
|
+
/**
|
|
133
|
+
* Get a string representation of the current time
|
|
134
|
+
* @returns for instance '2000-01-15T23:14:23.002'
|
|
135
|
+
*/
|
|
136
|
+
export function timestamp() {
|
|
137
|
+
return formatDateTime(new Date(), '{FullYear}-{Month}-{Date}T{Hours}:{Minutes}:{Seconds}.{Milliseconds}');
|
|
138
|
+
}
|
|
139
|
+
/**
|
|
140
|
+
* Get a short string representation of the current time
|
|
141
|
+
* @returns for instance '23:14:23.002'
|
|
142
|
+
*/
|
|
143
|
+
export function shortTime() {
|
|
144
|
+
return formatDateTime(new Date(), '{Hours}:{Minutes}:{Seconds}.{Milliseconds}');
|
|
145
|
+
}
|
|
146
|
+
// ///////////////////// Handy UX widgets
|
|
147
|
+
/**
|
|
148
|
+
* Sets the best name we have and looks up a better one
|
|
149
|
+
*/
|
|
150
|
+
export function setName(element, x) {
|
|
151
|
+
const kb = store;
|
|
152
|
+
const findName = function (x) {
|
|
153
|
+
const name = kb.any(x, ns.vcard('fn')) ||
|
|
154
|
+
kb.any(x, ns.foaf('name')) ||
|
|
155
|
+
kb.any(x, ns.vcard('organization-name'));
|
|
156
|
+
return name ? name.value : null;
|
|
157
|
+
};
|
|
158
|
+
const name = x.sameTerm(ns.foaf('Agent')) ? 'Everyone' : findName(x);
|
|
159
|
+
element.textContent = name || utils.label(x);
|
|
160
|
+
if (!name && x.uri) {
|
|
161
|
+
if (!kb.fetcher) {
|
|
162
|
+
throw new Error('kb has no fetcher');
|
|
163
|
+
}
|
|
164
|
+
// Note this is only a fetch, not a lookUP of all sameAs etc
|
|
165
|
+
kb.fetcher.nowOrWhenFetched(x.doc(), undefined, function (_ok) {
|
|
166
|
+
element.textContent = findName(x) || utils.label(x); // had: (ok ? '' : '? ') +
|
|
167
|
+
});
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
/**
|
|
171
|
+
* Set of suitable images
|
|
172
|
+
* See also [[findImage]]
|
|
173
|
+
* @param x The thing for which we want to find an image
|
|
174
|
+
* @param kb The RDF store to look in
|
|
175
|
+
* @returns It goes looking for triples in `kb`,
|
|
176
|
+
* `(subject: x), (predicate: see list below) (object: image-url)`
|
|
177
|
+
* to find any image linked from the thing with one of the following
|
|
178
|
+
* predicates (in order):
|
|
179
|
+
* * ns.sioc('avatar')
|
|
180
|
+
* * ns.foaf('img')
|
|
181
|
+
* * ns.vcard('logo')
|
|
182
|
+
* * ns.vcard('hasPhoto')
|
|
183
|
+
* * ns.vcard('photo')
|
|
184
|
+
* * ns.foaf('depiction')
|
|
185
|
+
|
|
186
|
+
*/
|
|
187
|
+
export function imagesOf(x, kb) {
|
|
188
|
+
return kb
|
|
189
|
+
.each(x, ns.sioc('avatar'))
|
|
190
|
+
.concat(kb.each(x, ns.foaf('img')))
|
|
191
|
+
.concat(kb.each(x, ns.vcard('logo')))
|
|
192
|
+
.concat(kb.each(x, ns.vcard('hasPhoto')))
|
|
193
|
+
.concat(kb.each(x, ns.vcard('photo')))
|
|
194
|
+
.concat(kb.each(x, ns.foaf('depiction')));
|
|
195
|
+
}
|
|
196
|
+
/**
|
|
197
|
+
* Best logo or avatar or photo etc to represent someone or some group etc
|
|
198
|
+
*/
|
|
199
|
+
export const iconForClass = {
|
|
200
|
+
// Potentially extendable by other apps, panes, etc
|
|
201
|
+
// Relative URIs to the iconBase
|
|
202
|
+
'solid:AppProviderClass': 'noun_144.svg', // @@ classs name should not contain 'Class'
|
|
203
|
+
'solid:AppProvider': 'noun_15177.svg', // @@
|
|
204
|
+
'solid:Pod': 'noun_Cabinet_1434380.svg',
|
|
205
|
+
'vcard:Group': 'noun_339237.svg',
|
|
206
|
+
'vcard:Organization': 'noun_143899.svg',
|
|
207
|
+
'vcard:Individual': 'noun_15059.svg',
|
|
208
|
+
'schema:Person': 'noun_15059.svg',
|
|
209
|
+
'foaf:Person': 'noun_15059.svg',
|
|
210
|
+
'foaf:Agent': 'noun_98053.svg',
|
|
211
|
+
'acl:AuthenticatedAgent': 'noun_99101.svg',
|
|
212
|
+
'prov:SoftwareAgent': 'noun_Robot_849764.svg', // Bot
|
|
213
|
+
'vcard:AddressBook': 'noun_15695.svg',
|
|
214
|
+
'trip:Trip': 'noun_581629.svg',
|
|
215
|
+
'meeting:LongChat': 'noun_1689339.svg',
|
|
216
|
+
'meeting:Meeting': 'noun_66617.svg',
|
|
217
|
+
'meeting:Project': 'noun_1036577.svg',
|
|
218
|
+
'ui:Form': 'noun_122196.svg',
|
|
219
|
+
'rdfs:Class': 'class-rectangle.svg', // For RDF developers
|
|
220
|
+
'rdf:Property': 'property-diamond.svg',
|
|
221
|
+
'owl:Ontology': 'noun_classification_1479198.svg',
|
|
222
|
+
'wf:Tracker': 'noun_122196.svg',
|
|
223
|
+
'wf:Task': 'noun_17020_gray-tick.svg',
|
|
224
|
+
'wf:Open': 'noun_17020_sans-tick.svg',
|
|
225
|
+
'wf:Closed': 'noun_17020.svg'
|
|
226
|
+
};
|
|
227
|
+
/**
|
|
228
|
+
* Returns the origin of the URI of a NamedNode
|
|
229
|
+
*/
|
|
230
|
+
function tempSite(x) {
|
|
231
|
+
// use only while one in rdflib fails with origins 2019
|
|
232
|
+
const str = x.uri.split('#')[0];
|
|
233
|
+
const p = str.indexOf('//');
|
|
234
|
+
if (p < 0)
|
|
235
|
+
throw new Error('This URI does not have a web site part (origin)');
|
|
236
|
+
const q = str.indexOf('/', p + 2);
|
|
237
|
+
if (q < 0) {
|
|
238
|
+
// no third slash?
|
|
239
|
+
return str.slice(0) + '/'; // Add slash to a bare origin
|
|
240
|
+
}
|
|
241
|
+
else {
|
|
242
|
+
return str.slice(0, q + 1);
|
|
243
|
+
}
|
|
244
|
+
}
|
|
245
|
+
/**
|
|
246
|
+
* Find an image for this thing as a class
|
|
247
|
+
*/
|
|
248
|
+
export function findImageFromURI(x) {
|
|
249
|
+
const iconDir = iconBase;
|
|
250
|
+
// Special cases from URI scheme:
|
|
251
|
+
if (typeof x !== 'string' && x.uri) {
|
|
252
|
+
if (x.uri.split('/').length === 4 &&
|
|
253
|
+
!x.uri.split('/')[1] &&
|
|
254
|
+
!x.uri.split('/')[3]) {
|
|
255
|
+
return iconDir + 'noun_15177.svg'; // App -- this is an origin
|
|
256
|
+
}
|
|
257
|
+
// Non-HTTP URI types imply types
|
|
258
|
+
if (x.uri.startsWith('message:') || x.uri.startsWith('mid:')) {
|
|
259
|
+
// message: is apple bug-- should be mid:
|
|
260
|
+
return iconDir + 'noun_480183.svg'; // envelope noun_567486
|
|
261
|
+
}
|
|
262
|
+
if (x.uri.startsWith('mailto:')) {
|
|
263
|
+
return iconDir + 'noun_567486.svg'; // mailbox - an email desitination
|
|
264
|
+
}
|
|
265
|
+
// For HTTP(s) documents, we could look at the MIME type if we know it.
|
|
266
|
+
if (x.uri.startsWith('https:') && x.uri.indexOf('#') < 0) {
|
|
267
|
+
return tempSite(x) + 'favicon.ico'; // was x.site().uri + ...
|
|
268
|
+
// Todo: make the document icon a fallback for if the favicon does not exist
|
|
269
|
+
// todo: pick up a possible favicon for the web page itself from a link
|
|
270
|
+
// was: return iconDir + 'noun_681601.svg' // document - under solid assumptions
|
|
271
|
+
}
|
|
272
|
+
return null;
|
|
273
|
+
}
|
|
274
|
+
return iconDir + 'noun_10636_grey.svg'; // Grey Circle - some thing
|
|
275
|
+
}
|
|
276
|
+
/**
|
|
277
|
+
* Find something we have as explicit image data for the thing
|
|
278
|
+
* See also [[imagesOf]]
|
|
279
|
+
* @param thing The thing for which we want to find an image
|
|
280
|
+
* @returns The URL of a globe icon if thing equals `ns.foaf('Agent')`
|
|
281
|
+
* or `ns.rdf('Resource')`. Otherwise, it goes looking for
|
|
282
|
+
* triples in `store`,
|
|
283
|
+
* `(subject: thing), (predicate: see list below) (object: image-url)`
|
|
284
|
+
* to find any image linked from the thing with one of the following
|
|
285
|
+
* predicates (in order):
|
|
286
|
+
* * ns.sioc('avatar')
|
|
287
|
+
* * ns.foaf('img')
|
|
288
|
+
* * ns.vcard('logo')
|
|
289
|
+
* * ns.vcard('hasPhoto')
|
|
290
|
+
* * ns.vcard('photo')
|
|
291
|
+
* * ns.foaf('depiction')
|
|
292
|
+
*/
|
|
293
|
+
export function findImage(thing) {
|
|
294
|
+
const kb = store;
|
|
295
|
+
const iconDir = iconBase;
|
|
296
|
+
if (thing.sameTerm(ns.foaf('Agent')) || thing.sameTerm(ns.rdf('Resource'))) {
|
|
297
|
+
return iconDir + 'noun_98053.svg'; // Globe
|
|
298
|
+
}
|
|
299
|
+
const image = kb.any(thing, ns.sioc('avatar')) ||
|
|
300
|
+
kb.any(thing, ns.foaf('img')) ||
|
|
301
|
+
kb.any(thing, ns.vcard('logo')) ||
|
|
302
|
+
kb.any(thing, ns.vcard('hasPhoto')) ||
|
|
303
|
+
kb.any(thing, ns.vcard('photo')) ||
|
|
304
|
+
kb.any(thing, ns.foaf('depiction'));
|
|
305
|
+
return image ? image.uri : null;
|
|
306
|
+
}
|
|
307
|
+
/**
|
|
308
|
+
* Do the best you can with the data available
|
|
309
|
+
*
|
|
310
|
+
* @return {Boolean} Are we happy with this icon?
|
|
311
|
+
* Sets src AND STYLE of the image.
|
|
312
|
+
*/
|
|
313
|
+
function trySetImage(element, thing, iconForClassMap) {
|
|
314
|
+
const kb = store;
|
|
315
|
+
const explitImage = findImage(thing);
|
|
316
|
+
if (explitImage) {
|
|
317
|
+
element.setAttribute('src', explitImage);
|
|
318
|
+
return true;
|
|
319
|
+
}
|
|
320
|
+
// This is one of the classes we know about - the class itself?
|
|
321
|
+
const typeIcon = iconForClassMap[thing.uri];
|
|
322
|
+
if (typeIcon) {
|
|
323
|
+
element.setAttribute('src', typeIcon);
|
|
324
|
+
element.style = style.classIconStyle;
|
|
325
|
+
// element.style.border = '0.1em solid green;'
|
|
326
|
+
// element.style.backgroundColor = '#eeffee' // pale green
|
|
327
|
+
return true;
|
|
328
|
+
}
|
|
329
|
+
const schemeIcon = findImageFromURI(thing);
|
|
330
|
+
if (schemeIcon) {
|
|
331
|
+
element.setAttribute('src', schemeIcon);
|
|
332
|
+
return true; // happy with this -- don't look it up
|
|
333
|
+
}
|
|
334
|
+
// Do we have a generic icon for something in any class its in?
|
|
335
|
+
const types = kb.findTypeURIs(thing);
|
|
336
|
+
for (const typeURI in types) {
|
|
337
|
+
if (iconForClassMap[typeURI]) {
|
|
338
|
+
element.setAttribute('src', iconForClassMap[typeURI]);
|
|
339
|
+
return false; // maybe we can do better
|
|
340
|
+
}
|
|
341
|
+
}
|
|
342
|
+
element.setAttribute('src', iconBase + 'noun_10636_grey.svg'); // Grey Circle - some thing
|
|
343
|
+
return false; // we can do better
|
|
344
|
+
}
|
|
345
|
+
/**
|
|
346
|
+
* ToDo: Also add icons for *properties* like home, work, email, range, domain, comment,
|
|
347
|
+
*/
|
|
348
|
+
export function setImage(element, thing) {
|
|
349
|
+
const kb = store;
|
|
350
|
+
const iconForClassMap = {};
|
|
351
|
+
for (const k in iconForClass) {
|
|
352
|
+
const pref = k.split(':')[0];
|
|
353
|
+
const id = k.split(':')[1];
|
|
354
|
+
const theClass = ns[pref](id);
|
|
355
|
+
iconForClassMap[theClass.uri] = uri.join(iconForClass[k], iconBase);
|
|
356
|
+
}
|
|
357
|
+
const happy = trySetImage(element, thing, iconForClassMap);
|
|
358
|
+
if (!happy && thing.uri) {
|
|
359
|
+
if (!kb.fetcher) {
|
|
360
|
+
throw new Error('kb has no fetcher');
|
|
361
|
+
}
|
|
362
|
+
kb.fetcher.nowOrWhenFetched(thing.doc(), undefined, (ok) => {
|
|
363
|
+
if (ok) {
|
|
364
|
+
trySetImage(element, thing, iconForClassMap);
|
|
365
|
+
}
|
|
366
|
+
});
|
|
367
|
+
}
|
|
368
|
+
}
|
|
369
|
+
// If a web page, then a favicon, with a fallback to ???
|
|
370
|
+
// See, e.g., http://stackoverflow.com/questions/980855/inputting-a-default-image
|
|
371
|
+
export function faviconOrDefault(dom, x) {
|
|
372
|
+
const image = dom.createElement('img');
|
|
373
|
+
image.style = style.iconStyle;
|
|
374
|
+
const isOrigin = function (x) {
|
|
375
|
+
if (!x.uri)
|
|
376
|
+
return false;
|
|
377
|
+
const parts = x.uri.split('/');
|
|
378
|
+
return parts.length === 3 || (parts.length === 4 && parts[3] === '');
|
|
379
|
+
};
|
|
380
|
+
image.setAttribute('src', iconBase + (isOrigin(x) ? 'noun_15177.svg' : 'noun_681601.svg') // App symbol vs document
|
|
381
|
+
);
|
|
382
|
+
if (x.uri && x.uri.startsWith('https:') && x.uri.indexOf('#') < 0) {
|
|
383
|
+
const res = dom.createElement('object'); // favico with a fallback of a default image if no favicon
|
|
384
|
+
res.setAttribute('data', tempSite(x) + 'favicon.ico');
|
|
385
|
+
res.setAttribute('type', 'image/x-icon');
|
|
386
|
+
res.appendChild(image); // fallback
|
|
387
|
+
return res;
|
|
388
|
+
}
|
|
389
|
+
else {
|
|
390
|
+
setImage(image, x);
|
|
391
|
+
return image;
|
|
392
|
+
}
|
|
393
|
+
}
|
|
394
|
+
/* Two-option dialog pop-up
|
|
395
|
+
*/
|
|
396
|
+
function renderDeleteConfirmPopup(dom, refererenceElement, prompt, deleteFunction) {
|
|
397
|
+
function removePopup() {
|
|
398
|
+
refererenceElement.parentElement.removeChild(refererenceElement);
|
|
399
|
+
}
|
|
400
|
+
function removePopupAndDoDeletion() {
|
|
401
|
+
removePopup();
|
|
402
|
+
deleteFunction();
|
|
403
|
+
}
|
|
404
|
+
const popup = dom.createElement('div');
|
|
405
|
+
popup.style = style.confirmPopupStyle;
|
|
406
|
+
popup.style.position = 'absolute';
|
|
407
|
+
popup.style.top = '-1em'; // try leaving original button clear
|
|
408
|
+
popup.style.display = 'grid';
|
|
409
|
+
popup.style.gridTemplateColumns = 'auto auto';
|
|
410
|
+
const affirm = dom.createElement('div');
|
|
411
|
+
affirm.style.gridColumn = '1/2';
|
|
412
|
+
affirm.style.gridRow = '1'; // @@ sigh; TS. could pass number in fact
|
|
413
|
+
const cancel = dom.createElement('div');
|
|
414
|
+
cancel.style.gridColumn = '1/2';
|
|
415
|
+
cancel.style.gridRow = '2';
|
|
416
|
+
const xButton = cancelButton(dom, removePopup);
|
|
417
|
+
popup.appendChild(xButton);
|
|
418
|
+
xButton.style.gridColumn = '1';
|
|
419
|
+
xButton.style.gridRow = '2';
|
|
420
|
+
const cancelPrompt = popup.appendChild(dom.createElement('button'));
|
|
421
|
+
cancelPrompt.style = style.buttonStyle;
|
|
422
|
+
cancelPrompt.style.gridRow = '2';
|
|
423
|
+
cancelPrompt.style.gridColumn = '2';
|
|
424
|
+
cancelPrompt.textContent = 'Cancel'; // @@ I18n
|
|
425
|
+
const affirmIcon = button(dom, icons.iconBase + 'noun_925021.svg', 'Delete it'); // trashcan
|
|
426
|
+
popup.appendChild(affirmIcon);
|
|
427
|
+
affirmIcon.style.gridRow = '1';
|
|
428
|
+
affirmIcon.style.gridColumn = '1';
|
|
429
|
+
const sureButtonElt = popup.appendChild(dom.createElement('button'));
|
|
430
|
+
sureButtonElt.style = style.buttonStyle;
|
|
431
|
+
sureButtonElt.style.gridRow = '1';
|
|
432
|
+
sureButtonElt.style.gridColumn = '2';
|
|
433
|
+
sureButtonElt.textContent = prompt;
|
|
434
|
+
popup.appendChild(sureButtonElt);
|
|
435
|
+
affirmIcon.addEventListener('click', removePopupAndDoDeletion);
|
|
436
|
+
sureButtonElt.addEventListener('click', removePopupAndDoDeletion);
|
|
437
|
+
// xButton.addEventListener('click', removePopup)
|
|
438
|
+
cancelPrompt.addEventListener('click', removePopup);
|
|
439
|
+
return popup;
|
|
440
|
+
}
|
|
441
|
+
/**
|
|
442
|
+
* Delete button with a check you really mean it
|
|
443
|
+
* @@ Supress check if command key held down?
|
|
444
|
+
*/
|
|
445
|
+
export function deleteButtonWithCheck(dom, container, noun, deleteFunction) {
|
|
446
|
+
function createPopup() {
|
|
447
|
+
const refererenceElement = dom.createElement('div');
|
|
448
|
+
container.insertBefore(refererenceElement, deleteButton);
|
|
449
|
+
refererenceElement.style.position = 'relative'; // Needed as reference for popup
|
|
450
|
+
refererenceElement.appendChild(renderDeleteConfirmPopup(dom, refererenceElement, prompt, deleteFunction));
|
|
451
|
+
}
|
|
452
|
+
const minusIconURI = iconBase + 'noun_2188_red.svg'; // white minus in red #cc0000 circle
|
|
453
|
+
const deleteButton = dom.createElement('img');
|
|
454
|
+
deleteButton.setAttribute('src', minusIconURI);
|
|
455
|
+
deleteButton.setAttribute('style', style.smallButtonStyle); // @@tsc - would set deleteButton.style
|
|
456
|
+
deleteButton.style.float = 'right'; // Historically this has alwaus floated right
|
|
457
|
+
const prompt = 'Remove this ' + noun;
|
|
458
|
+
deleteButton.title = prompt;
|
|
459
|
+
// @@ In an ideal world, make use of hover an accessibility option
|
|
460
|
+
deleteButton.classList.add('hoverControlHide');
|
|
461
|
+
deleteButton.addEventListener('click', createPopup);
|
|
462
|
+
container.classList.add('hoverControl');
|
|
463
|
+
container.appendChild(deleteButton);
|
|
464
|
+
deleteButton.setAttribute('data-testid', 'deleteButtonWithCheck');
|
|
465
|
+
return deleteButton; // or button div? caller may change size of image
|
|
466
|
+
}
|
|
467
|
+
/* Make a button
|
|
468
|
+
*
|
|
469
|
+
* @param dom - the DOM document object
|
|
470
|
+
* @Param iconURI - the URI of the icon to use (if any)
|
|
471
|
+
* @param text - the tooltip text or possibly button contents text
|
|
472
|
+
* @param handler <function> - A handler to called when button is clicked
|
|
473
|
+
*
|
|
474
|
+
* @returns <dDomElement> - the button
|
|
475
|
+
*/
|
|
476
|
+
export function button(dom, iconURI, text, handler, options = { buttonColor: 'Primary', needsBorder: false }) {
|
|
477
|
+
const button = dom.createElement('button');
|
|
478
|
+
button.setAttribute('type', 'button');
|
|
479
|
+
// button.innerHTML = text // later, user preferences may make text preferred for some
|
|
480
|
+
if (iconURI) {
|
|
481
|
+
const img = button.appendChild(dom.createElement('img'));
|
|
482
|
+
img.setAttribute('src', iconURI);
|
|
483
|
+
img.setAttribute('style', 'width: 2em; height: 2em;'); // trial and error. 2em disappears
|
|
484
|
+
img.title = text;
|
|
485
|
+
button.setAttribute('style', style.buttonStyle);
|
|
486
|
+
}
|
|
487
|
+
else {
|
|
488
|
+
button.textContent = text.toLocaleUpperCase();
|
|
489
|
+
button.onmouseover = function () {
|
|
490
|
+
if (options.buttonColor === 'Secondary') {
|
|
491
|
+
if (options.needsBorder) {
|
|
492
|
+
button.setAttribute('style', style.secondaryButtonNoBorderHover);
|
|
493
|
+
}
|
|
494
|
+
else {
|
|
495
|
+
button.setAttribute('style', style.secondaryButtonHover);
|
|
496
|
+
}
|
|
497
|
+
}
|
|
498
|
+
else {
|
|
499
|
+
if (options.needsBorder) {
|
|
500
|
+
button.setAttribute('style', style.primaryButtonNoBorderHover);
|
|
501
|
+
}
|
|
502
|
+
else {
|
|
503
|
+
button.setAttribute('style', style.primaryButtonHover);
|
|
504
|
+
}
|
|
505
|
+
}
|
|
506
|
+
};
|
|
507
|
+
button.onmouseout = function () {
|
|
508
|
+
if (options.buttonColor === 'Secondary') {
|
|
509
|
+
if (options.needsBorder) {
|
|
510
|
+
button.setAttribute('style', style.secondaryButtonNoBorder);
|
|
511
|
+
}
|
|
512
|
+
else {
|
|
513
|
+
button.setAttribute('style', style.secondaryButton);
|
|
514
|
+
}
|
|
515
|
+
}
|
|
516
|
+
else {
|
|
517
|
+
if (options.needsBorder) {
|
|
518
|
+
button.setAttribute('style', style.primaryButtonNoBorder);
|
|
519
|
+
}
|
|
520
|
+
else {
|
|
521
|
+
button.setAttribute('style', style.primaryButton);
|
|
522
|
+
}
|
|
523
|
+
}
|
|
524
|
+
};
|
|
525
|
+
if (options.buttonColor === 'Secondary') {
|
|
526
|
+
if (options.needsBorder) {
|
|
527
|
+
button.setAttribute('style', style.secondaryButtonNoBorder);
|
|
528
|
+
}
|
|
529
|
+
else {
|
|
530
|
+
button.setAttribute('style', style.secondaryButton);
|
|
531
|
+
}
|
|
532
|
+
}
|
|
533
|
+
else {
|
|
534
|
+
if (options.needsBorder) {
|
|
535
|
+
button.setAttribute('style', style.primaryButtonNoBorder);
|
|
536
|
+
}
|
|
537
|
+
else {
|
|
538
|
+
button.setAttribute('style', style.primaryButton);
|
|
539
|
+
}
|
|
540
|
+
}
|
|
541
|
+
}
|
|
542
|
+
if (handler) {
|
|
543
|
+
button.addEventListener('click', handler, false);
|
|
544
|
+
}
|
|
545
|
+
return button;
|
|
546
|
+
}
|
|
547
|
+
/* Make a cancel button
|
|
548
|
+
*
|
|
549
|
+
* @param dom - the DOM document object
|
|
550
|
+
* @param handler <function> - A handler to called when button is clicked
|
|
551
|
+
*
|
|
552
|
+
* @returns <dDomElement> - the button
|
|
553
|
+
*/
|
|
554
|
+
export function cancelButton(dom, handler) {
|
|
555
|
+
const b = button(dom, cancelIconURI, 'Cancel', handler);
|
|
556
|
+
if (b.firstChild) { // sigh for tsc
|
|
557
|
+
b.firstChild.style.opacity = '0.3'; // Black X is too harsh: current language is grey X
|
|
558
|
+
}
|
|
559
|
+
return b;
|
|
560
|
+
}
|
|
561
|
+
/* Make a continue button
|
|
562
|
+
*
|
|
563
|
+
* @param dom - the DOM document object
|
|
564
|
+
* @param handler <function> - A handler to called when button is clicked
|
|
565
|
+
*
|
|
566
|
+
* @returns <dDomElement> - the button
|
|
567
|
+
*/
|
|
568
|
+
export function continueButton(dom, handler) {
|
|
569
|
+
return button(dom, checkIconURI, 'Continue', handler);
|
|
570
|
+
}
|
|
571
|
+
/* Grab a name for a new thing
|
|
572
|
+
*
|
|
573
|
+
* Form to get the name of a new thing before we create it
|
|
574
|
+
* @params theClass Misspelt to avoid clashing with the JavaScript keyword
|
|
575
|
+
* @returns: a promise of (a name or null if cancelled)
|
|
576
|
+
*/
|
|
577
|
+
export function askName(dom, kb, container, predicate, theClass, noun) {
|
|
578
|
+
return new Promise(function (resolve, _reject) {
|
|
579
|
+
const form = dom.createElement('div'); // form is broken as HTML behaviour can resurface on js error
|
|
580
|
+
// classLabel = utils.label(ns.vcard('Individual'))
|
|
581
|
+
predicate = predicate || ns.foaf('name'); // eg 'name' in user's language
|
|
582
|
+
noun = noun || (theClass ? utils.label(theClass) : ' '); // eg 'folder' in users's language
|
|
583
|
+
const prompt = noun + ' ' + utils.label(predicate) + ': ';
|
|
584
|
+
form.appendChild(dom.createElement('p')).textContent = prompt;
|
|
585
|
+
const namefield = dom.createElement('input');
|
|
586
|
+
namefield.setAttribute('type', 'text');
|
|
587
|
+
namefield.setAttribute('size', '100');
|
|
588
|
+
namefield.setAttribute('maxLength', '2048'); // No arbitrary limits
|
|
589
|
+
namefield.setAttribute('style', style.textInputStyle);
|
|
590
|
+
namefield.select(); // focus next user input
|
|
591
|
+
form.appendChild(namefield);
|
|
592
|
+
container.appendChild(form);
|
|
593
|
+
// namefield.focus()
|
|
594
|
+
function gotName() {
|
|
595
|
+
form.parentNode.removeChild(form);
|
|
596
|
+
resolve(namefield.value.trim());
|
|
597
|
+
}
|
|
598
|
+
namefield.addEventListener('keyup', function (e) {
|
|
599
|
+
if (e.keyCode === 13) {
|
|
600
|
+
gotName();
|
|
601
|
+
}
|
|
602
|
+
}, false);
|
|
603
|
+
form.appendChild(dom.createElement('br'));
|
|
604
|
+
form.appendChild(cancelButton(dom, function (_event) {
|
|
605
|
+
form.parentNode.removeChild(form);
|
|
606
|
+
resolve(null);
|
|
607
|
+
}));
|
|
608
|
+
form.appendChild(continueButton(dom, function (_event) {
|
|
609
|
+
gotName();
|
|
610
|
+
}));
|
|
611
|
+
namefield.focus();
|
|
612
|
+
}); // Promise
|
|
613
|
+
}
|
|
614
|
+
/**
|
|
615
|
+
* A TR to represent a draggable person, etc in a list
|
|
616
|
+
*
|
|
617
|
+
* pred is unused param at the moment
|
|
618
|
+
*/
|
|
619
|
+
export const personTR = renderAsRow; // The legacy name is used in a lot of places
|
|
620
|
+
export function renderAsRow(dom, pred, obj, options) {
|
|
621
|
+
const tr = dom.createElement('tr');
|
|
622
|
+
options = options || {};
|
|
623
|
+
// tr.predObj = [pred.uri, obj.uri] moved to acl-control
|
|
624
|
+
const td1 = tr.appendChild(dom.createElement('td'));
|
|
625
|
+
const td2 = tr.appendChild(dom.createElement('td'));
|
|
626
|
+
const td3 = tr.appendChild(dom.createElement('td'));
|
|
627
|
+
// const image = td1.appendChild(dom.createElement('img'))
|
|
628
|
+
const image = options.image || faviconOrDefault(dom, obj);
|
|
629
|
+
td1.setAttribute('style', 'vertical-align: middle; width:2.5em; padding:0.5em; height: 2.5em;');
|
|
630
|
+
td2.setAttribute('style', 'vertical-align: middle; text-align:left;');
|
|
631
|
+
td3.setAttribute('style', 'vertical-align: middle; width:2em; padding:0.5em; height: 4em;');
|
|
632
|
+
td1.appendChild(image);
|
|
633
|
+
if (options.title) {
|
|
634
|
+
td2.textContent = options.title;
|
|
635
|
+
}
|
|
636
|
+
else {
|
|
637
|
+
setName(td2, obj); // This is async
|
|
638
|
+
}
|
|
639
|
+
if (options.deleteFunction) {
|
|
640
|
+
deleteButtonWithCheck(dom, td3, options.noun || 'one', options.deleteFunction);
|
|
641
|
+
}
|
|
642
|
+
if (obj.uri) {
|
|
643
|
+
// blank nodes need not apply
|
|
644
|
+
if (options.link !== false) {
|
|
645
|
+
const anchor = td3.appendChild(linkIcon(dom, obj));
|
|
646
|
+
anchor.classList.add('HoverControlHide');
|
|
647
|
+
td3.appendChild(dom.createElement('br'));
|
|
648
|
+
}
|
|
649
|
+
if (options.draggable !== false) {
|
|
650
|
+
// default is on
|
|
651
|
+
image.setAttribute('draggable', 'false'); // Stop the image being dragged instead - just the TR
|
|
652
|
+
makeDraggable(tr, obj);
|
|
653
|
+
}
|
|
654
|
+
}
|
|
655
|
+
;
|
|
656
|
+
tr.subject = obj;
|
|
657
|
+
return tr;
|
|
658
|
+
}
|
|
659
|
+
/* A helper function for renderAsDiv
|
|
660
|
+
* creates the NameDiv for the person
|
|
661
|
+
* Note: could not move it to the helper file because they call exported functions
|
|
662
|
+
* from buttons
|
|
663
|
+
* @internal exporting this only for unit tests
|
|
664
|
+
*/
|
|
665
|
+
export function createNameDiv(dom, div, title, obj) {
|
|
666
|
+
const nameDiv = div.appendChild(dom.createElement('div'));
|
|
667
|
+
if (title) {
|
|
668
|
+
nameDiv.textContent = title;
|
|
669
|
+
}
|
|
670
|
+
else {
|
|
671
|
+
setName(nameDiv, obj); // This is async
|
|
672
|
+
}
|
|
673
|
+
}
|
|
674
|
+
/* A helper function for renderAsDiv
|
|
675
|
+
* creates the linkDiv for the person
|
|
676
|
+
* Note: could not move it to the helper file because they call exported functions
|
|
677
|
+
* from buttons
|
|
678
|
+
* @internal exporting this only for unit tests
|
|
679
|
+
*/
|
|
680
|
+
export function createLinkDiv(dom, div, obj, options) {
|
|
681
|
+
const linkDiv = div.appendChild(dom.createElement('div'));
|
|
682
|
+
linkDiv.setAttribute('style', style.linkDivStyle);
|
|
683
|
+
if (options.deleteFunction) {
|
|
684
|
+
deleteButtonWithCheck(dom, linkDiv, options.noun || 'one', options.deleteFunction);
|
|
685
|
+
}
|
|
686
|
+
if (obj.uri) {
|
|
687
|
+
// blank nodes need not apply
|
|
688
|
+
if (options.link !== false) {
|
|
689
|
+
createLinkForURI(dom, linkDiv, obj);
|
|
690
|
+
}
|
|
691
|
+
makeDraggable(div, obj);
|
|
692
|
+
}
|
|
693
|
+
}
|
|
694
|
+
/**
|
|
695
|
+
* A Div to represent a draggable person, etc in a list
|
|
696
|
+
* configurable to add an onClick listener
|
|
697
|
+
*/
|
|
698
|
+
export function renderAsDiv(dom, obj, options) {
|
|
699
|
+
const div = dom.createElement('div');
|
|
700
|
+
div.setAttribute('style', style.renderAsDivStyle);
|
|
701
|
+
options = options || {};
|
|
702
|
+
const image = options.image || faviconOrDefault(dom, obj);
|
|
703
|
+
createImageDiv(dom, div, image);
|
|
704
|
+
createNameDiv(dom, div, options.title, obj);
|
|
705
|
+
createLinkDiv(dom, div, obj, options);
|
|
706
|
+
if (options.clickable && options.onClickFunction) {
|
|
707
|
+
addClickListenerToElement(div, options.onClickFunction);
|
|
708
|
+
}
|
|
709
|
+
// to be compatible with the SolidOS table layout
|
|
710
|
+
if (options.wrapInATR) {
|
|
711
|
+
const tr = wrapDivInATR(dom, div, obj);
|
|
712
|
+
return tr;
|
|
713
|
+
}
|
|
714
|
+
return div;
|
|
715
|
+
}
|
|
716
|
+
/**
|
|
717
|
+
* Refresh a DOM tree recursively
|
|
718
|
+
*/
|
|
719
|
+
export function refreshTree(root) {
|
|
720
|
+
if (root.refresh) {
|
|
721
|
+
root.refresh();
|
|
722
|
+
return;
|
|
723
|
+
}
|
|
724
|
+
for (let i = 0; i < root.children.length; i++) {
|
|
725
|
+
refreshTree(root.children[i]);
|
|
726
|
+
}
|
|
727
|
+
}
|
|
728
|
+
/**
|
|
729
|
+
* Component that displays a list of resources, for instance
|
|
730
|
+
* the attachments of a message, or the various documents related
|
|
731
|
+
* to a meeting.
|
|
732
|
+
* Accepts dropping URLs onto it to add attachments to it.
|
|
733
|
+
*/
|
|
734
|
+
export function attachmentList(dom, subject, div, options = {}) {
|
|
735
|
+
// options = options || {}
|
|
736
|
+
const deleteAttachment = function (target) {
|
|
737
|
+
if (!kb.updater) {
|
|
738
|
+
throw new Error('kb has no updater');
|
|
739
|
+
}
|
|
740
|
+
kb.updater.update(st(subject, predicate, target, doc), [], function (uri, ok, errorBody, _xhr) {
|
|
741
|
+
if (ok) {
|
|
742
|
+
refresh();
|
|
743
|
+
}
|
|
744
|
+
else {
|
|
745
|
+
complain(undefined, 'Error deleting one: ' + errorBody);
|
|
746
|
+
}
|
|
747
|
+
});
|
|
748
|
+
};
|
|
749
|
+
function createNewRow(target) {
|
|
750
|
+
const theTarget = target;
|
|
751
|
+
const opt = { noun };
|
|
752
|
+
if (modify) {
|
|
753
|
+
opt.deleteFunction = function () {
|
|
754
|
+
deleteAttachment(theTarget);
|
|
755
|
+
};
|
|
756
|
+
}
|
|
757
|
+
return personTR(dom, predicate, target, opt);
|
|
758
|
+
}
|
|
759
|
+
const refresh = function () {
|
|
760
|
+
const things = kb.each(subject, predicate);
|
|
761
|
+
things.sort();
|
|
762
|
+
utils.syncTableToArray(attachmentTable, things, createNewRow);
|
|
763
|
+
};
|
|
764
|
+
function droppedURIHandler(uris) {
|
|
765
|
+
const ins = [];
|
|
766
|
+
uris.forEach(function (u) {
|
|
767
|
+
const target = sym(u); // Attachment needs text label to disinguish I think not icon.
|
|
768
|
+
debug.log('Dropped on attachemnt ' + u); // icon was: iconBase + 'noun_25830.svg'
|
|
769
|
+
ins.push(st(subject, predicate, target, doc));
|
|
770
|
+
});
|
|
771
|
+
if (!kb.updater) {
|
|
772
|
+
throw new Error('kb has no updater');
|
|
773
|
+
}
|
|
774
|
+
kb.updater.update([], ins, function (uri, ok, errorBody, _xhr) {
|
|
775
|
+
if (ok) {
|
|
776
|
+
refresh();
|
|
777
|
+
}
|
|
778
|
+
else {
|
|
779
|
+
complain(undefined, 'Error adding one: ' + errorBody);
|
|
780
|
+
}
|
|
781
|
+
});
|
|
782
|
+
}
|
|
783
|
+
function droppedFileHandler(files) {
|
|
784
|
+
var _a, _b;
|
|
785
|
+
uploadFiles(kb.fetcher, files, (_a = options.uploadFolder) === null || _a === void 0 ? void 0 : _a.uri, // Files
|
|
786
|
+
(_b = options.uploadFolder) === null || _b === void 0 ? void 0 : _b.uri, // Pictures
|
|
787
|
+
function (theFile, destURI) {
|
|
788
|
+
const ins = [st(subject, predicate, kb.sym(destURI), doc)];
|
|
789
|
+
if (!kb.updater) {
|
|
790
|
+
throw new Error('kb has no updater');
|
|
791
|
+
}
|
|
792
|
+
kb.updater.update([], ins, function (uri, ok, errorBody, _xhr) {
|
|
793
|
+
if (ok) {
|
|
794
|
+
refresh();
|
|
795
|
+
}
|
|
796
|
+
else {
|
|
797
|
+
complain(undefined, 'Error adding link to uploaded file: ' + errorBody);
|
|
798
|
+
}
|
|
799
|
+
});
|
|
800
|
+
});
|
|
801
|
+
}
|
|
802
|
+
const doc = options.doc || subject.doc();
|
|
803
|
+
if (options.modify === undefined)
|
|
804
|
+
options.modify = true;
|
|
805
|
+
const modify = options.modify;
|
|
806
|
+
const promptIcon = options.promptIcon || iconBase + 'noun_748003.svg'; // target
|
|
807
|
+
// const promptIcon = options.promptIcon || (iconBase + 'noun_25830.svg') // paperclip
|
|
808
|
+
const predicate = options.predicate || ns.wf('attachment');
|
|
809
|
+
const noun = options.noun || 'attachment';
|
|
810
|
+
const kb = store;
|
|
811
|
+
const attachmentOuter = div.appendChild(dom.createElement('table'));
|
|
812
|
+
attachmentOuter.setAttribute('style', 'margin-top: 1em; margin-bottom: 1em;');
|
|
813
|
+
const attachmentOne = attachmentOuter.appendChild(dom.createElement('tr'));
|
|
814
|
+
const attachmentLeft = attachmentOne.appendChild(dom.createElement('td'));
|
|
815
|
+
const attachmentRight = attachmentOne.appendChild(dom.createElement('td'));
|
|
816
|
+
const attachmentTable = attachmentRight.appendChild(dom.createElement('table'));
|
|
817
|
+
attachmentTable.appendChild(dom.createElement('tr')) // attachmentTableTop
|
|
818
|
+
;
|
|
819
|
+
attachmentOuter.refresh = refresh; // Participate in downstream changes
|
|
820
|
+
// ;(attachmentTable as any).refresh = refresh <- outer should be best?
|
|
821
|
+
refresh();
|
|
822
|
+
if (modify) {
|
|
823
|
+
// const buttonStyle = 'width; 2em; height: 2em; margin: 0.5em; padding: 0.1em;'
|
|
824
|
+
const paperclip = button(dom, promptIcon, 'Drop attachments here');
|
|
825
|
+
// paperclip.style = buttonStyle // @@ needed? default has white background
|
|
826
|
+
attachmentLeft.appendChild(paperclip);
|
|
827
|
+
const fhandler = options.uploadFolder ? droppedFileHandler : null;
|
|
828
|
+
makeDropTarget(paperclip, droppedURIHandler, fhandler); // beware missing the wire of the paparclip!
|
|
829
|
+
makeDropTarget(attachmentLeft, droppedURIHandler, fhandler); // just the outer won't do it
|
|
830
|
+
if (options.uploadFolder) { // Addd an explicit file upload button as well
|
|
831
|
+
const buttonDiv = fileUploadButtonDiv(dom, droppedFileHandler);
|
|
832
|
+
attachmentLeft.appendChild(buttonDiv);
|
|
833
|
+
// buttonDiv.children[1].style = buttonStyle
|
|
834
|
+
}
|
|
835
|
+
}
|
|
836
|
+
return attachmentOuter;
|
|
837
|
+
}
|
|
838
|
+
// /////////////////////////////////////////////////////////////////////////////
|
|
839
|
+
/**
|
|
840
|
+
* Event Handler for links within solid apps.
|
|
841
|
+
*
|
|
842
|
+
* Note that native links have constraints in Firefox, they
|
|
843
|
+
* don't work with local files for instance (2011)
|
|
844
|
+
*/
|
|
845
|
+
export function openHrefInOutlineMode(e) {
|
|
846
|
+
e.preventDefault();
|
|
847
|
+
e.stopPropagation();
|
|
848
|
+
const target = utils.getTarget(e);
|
|
849
|
+
const uri = target.getAttribute('href');
|
|
850
|
+
if (!uri)
|
|
851
|
+
return debug.log('openHrefInOutlineMode: No href found!\n');
|
|
852
|
+
const dom = window.document;
|
|
853
|
+
if (dom.outlineManager) {
|
|
854
|
+
// @@ TODO Remove the use of document as a global object
|
|
855
|
+
// TODO fix dependency cycle to solid-panes by calling outlineManager
|
|
856
|
+
;
|
|
857
|
+
dom.outlineManager.GotoSubject(store.sym(uri), true, undefined, true, undefined);
|
|
858
|
+
}
|
|
859
|
+
else if (window && window.panes && window.panes.getOutliner) {
|
|
860
|
+
// @@ TODO Remove the use of window as a global object
|
|
861
|
+
;
|
|
862
|
+
window.panes
|
|
863
|
+
.getOutliner()
|
|
864
|
+
.GotoSubject(store.sym(uri), true, undefined, true, undefined);
|
|
865
|
+
}
|
|
866
|
+
else {
|
|
867
|
+
debug.log('ERROR: Can\'t access outline manager in this config');
|
|
868
|
+
}
|
|
869
|
+
// dom.outlineManager.GotoSubject(store.sym(uri), true, undefined, true, undefined)
|
|
870
|
+
}
|
|
871
|
+
/**
|
|
872
|
+
* Make a URI in the Tabulator.org annotation store out of the URI of the thing to be annotated.
|
|
873
|
+
*
|
|
874
|
+
* @@ Todo: make it a personal preference.
|
|
875
|
+
*/
|
|
876
|
+
export function defaultAnnotationStore(subject) {
|
|
877
|
+
if (subject.uri === undefined)
|
|
878
|
+
return undefined;
|
|
879
|
+
let s = subject.uri;
|
|
880
|
+
if (s.slice(0, 7) !== 'http://')
|
|
881
|
+
return undefined;
|
|
882
|
+
s = s.slice(7); // Remove
|
|
883
|
+
const hash = s.indexOf('#');
|
|
884
|
+
if (hash >= 0)
|
|
885
|
+
s = s.slice(0, hash);
|
|
886
|
+
// Strip trailing
|
|
887
|
+
else {
|
|
888
|
+
const slash = s.lastIndexOf('/');
|
|
889
|
+
if (slash < 0)
|
|
890
|
+
return undefined;
|
|
891
|
+
s = s.slice(0, slash);
|
|
892
|
+
}
|
|
893
|
+
return store.sym('http://tabulator.org/wiki/annnotation/' + s);
|
|
894
|
+
}
|
|
895
|
+
/**
|
|
896
|
+
* Retrieve all RDF class URIs from solid-ui's RDF store
|
|
897
|
+
* @returns an object `ret` such that `Object.keys(ret)` is
|
|
898
|
+
* the list of all class URIs.
|
|
899
|
+
*/
|
|
900
|
+
export function allClassURIs() {
|
|
901
|
+
const set = {};
|
|
902
|
+
store
|
|
903
|
+
.statementsMatching(undefined, ns.rdf('type'), undefined)
|
|
904
|
+
.forEach(function (st) {
|
|
905
|
+
if (st.object.value)
|
|
906
|
+
set[st.object.value] = true;
|
|
907
|
+
});
|
|
908
|
+
store
|
|
909
|
+
.statementsMatching(undefined, ns.rdfs('subClassOf'), undefined)
|
|
910
|
+
.forEach(function (st) {
|
|
911
|
+
if (st.object.value)
|
|
912
|
+
set[st.object.value] = true;
|
|
913
|
+
if (st.subject.value)
|
|
914
|
+
set[st.subject.value] = true;
|
|
915
|
+
});
|
|
916
|
+
store
|
|
917
|
+
.each(undefined, ns.rdf('type'), ns.rdfs('Class'))
|
|
918
|
+
.forEach(function (c) {
|
|
919
|
+
if (c.value)
|
|
920
|
+
set[c.value] = true;
|
|
921
|
+
});
|
|
922
|
+
return set;
|
|
923
|
+
}
|
|
924
|
+
/**
|
|
925
|
+
* Figuring which properties we know about
|
|
926
|
+
*
|
|
927
|
+
* When the user inputs an RDF property, like for a form field
|
|
928
|
+
* or when specifying the relationship between two arbitrary things,
|
|
929
|
+
* then er can prompt them with properties the session knows about
|
|
930
|
+
*
|
|
931
|
+
* TODO: Look again by catching this somewhere. (On the kb?)
|
|
932
|
+
* TODO: move to diff module? Not really a button.
|
|
933
|
+
* @param {Store} kb The quadstore to be searched.
|
|
934
|
+
*/
|
|
935
|
+
export function propertyTriage(kb) {
|
|
936
|
+
const possibleProperties = {};
|
|
937
|
+
// if (possibleProperties === undefined) possibleProperties = {}
|
|
938
|
+
// const kb = store
|
|
939
|
+
const dp = {};
|
|
940
|
+
const op = {};
|
|
941
|
+
let no = 0;
|
|
942
|
+
let nd = 0;
|
|
943
|
+
let nu = 0;
|
|
944
|
+
const pi = kb.predicateIndex; // One entry for each pred
|
|
945
|
+
for (const p in pi) {
|
|
946
|
+
const object = pi[p][0].object;
|
|
947
|
+
if (object.termType === 'Literal') {
|
|
948
|
+
dp[p] = true;
|
|
949
|
+
nd++;
|
|
950
|
+
}
|
|
951
|
+
else {
|
|
952
|
+
op[p] = true;
|
|
953
|
+
no++;
|
|
954
|
+
}
|
|
955
|
+
} // If nothing discovered, then could be either:
|
|
956
|
+
const ps = kb.each(undefined, ns.rdf('type'), ns.rdf('Property'));
|
|
957
|
+
for (let i = 0; i < ps.length; i++) {
|
|
958
|
+
const p = ps[i].toNT();
|
|
959
|
+
if (!op[p] && !dp[p]) {
|
|
960
|
+
dp[p] = true;
|
|
961
|
+
op[p] = true;
|
|
962
|
+
nu++;
|
|
963
|
+
}
|
|
964
|
+
}
|
|
965
|
+
possibleProperties.op = op;
|
|
966
|
+
possibleProperties.dp = dp;
|
|
967
|
+
info(`propertyTriage: ${no} non-lit, ${nd} literal. ${nu} unknown.`);
|
|
968
|
+
return possibleProperties;
|
|
969
|
+
}
|
|
970
|
+
/**
|
|
971
|
+
* General purpose widgets
|
|
972
|
+
*/
|
|
973
|
+
/**
|
|
974
|
+
* A button for jumping
|
|
975
|
+
*/
|
|
976
|
+
export function linkButton(dom, object) {
|
|
977
|
+
const b = dom.createElement('button');
|
|
978
|
+
b.setAttribute('type', 'button');
|
|
979
|
+
b.textContent = 'Goto ' + utils.label(object);
|
|
980
|
+
b.addEventListener('click', function (_event) {
|
|
981
|
+
// b.parentNode.removeChild(b)
|
|
982
|
+
// TODO fix dependency cycle to solid-panes by calling outlineManager
|
|
983
|
+
;
|
|
984
|
+
dom.outlineManager.GotoSubject(object, true, undefined, true, undefined);
|
|
985
|
+
}, true);
|
|
986
|
+
return b;
|
|
987
|
+
}
|
|
988
|
+
/**
|
|
989
|
+
* A button to remove some other element from the page
|
|
990
|
+
*/
|
|
991
|
+
export function removeButton(dom, element) {
|
|
992
|
+
const b = dom.createElement('button');
|
|
993
|
+
b.setAttribute('type', 'button');
|
|
994
|
+
b.textContent = '✕'; // MULTIPLICATION X
|
|
995
|
+
b.addEventListener('click', function (_event) {
|
|
996
|
+
;
|
|
997
|
+
element.parentNode.removeChild(element);
|
|
998
|
+
}, true);
|
|
999
|
+
return b;
|
|
1000
|
+
}
|
|
1001
|
+
// Description text area
|
|
1002
|
+
//
|
|
1003
|
+
// Make a box to demand a description or display existing one
|
|
1004
|
+
//
|
|
1005
|
+
// @param dom - the document DOM for the user interface
|
|
1006
|
+
// @param kb - the graph which is the knowledge base we are working with
|
|
1007
|
+
// @param subject - a term, the subject of the statement(s) being edited.
|
|
1008
|
+
// @param predicate - a term, the predicate of the statement(s) being edited
|
|
1009
|
+
// @param store - The web document being edited
|
|
1010
|
+
// @param callbackFunction - takes (boolean ok, string errorBody)
|
|
1011
|
+
// /////////////////////////////////////// Random I/O widgets /////////////
|
|
1012
|
+
// //// Column Header Buttons
|
|
1013
|
+
//
|
|
1014
|
+
// These are for selecting different modes, sources,styles, etc.
|
|
1015
|
+
//
|
|
1016
|
+
/*
|
|
1017
|
+
buttons.headerButtons = function (dom, kb, name, words) {
|
|
1018
|
+
const box = dom.createElement('table')
|
|
1019
|
+
var i, word, s = '<tr>'
|
|
1020
|
+
box.setAttribute('style', 'width: 90%; height: 1.5em')
|
|
1021
|
+
for (i=0; i<words.length; i++) {
|
|
1022
|
+
s += '<td><input type="radio" name="' + name + '" id="' + words[i] + '" value='
|
|
1023
|
+
}
|
|
1024
|
+
box.innerHTML = s + '</tr>'
|
|
1025
|
+
|
|
1026
|
+
}
|
|
1027
|
+
*/
|
|
1028
|
+
// ////////////////////////////////////////////////////////////
|
|
1029
|
+
//
|
|
1030
|
+
// selectorPanel
|
|
1031
|
+
//
|
|
1032
|
+
// A vertical panel for selecting connections to left or right.
|
|
1033
|
+
//
|
|
1034
|
+
// @param inverse means this is the object rather than the subject
|
|
1035
|
+
//
|
|
1036
|
+
export function selectorPanel(dom, kb, type, predicate, inverse, possible, options, callbackFunction, linkCallback) {
|
|
1037
|
+
return selectorPanelRefresh(dom.createElement('div'), dom, kb, type, predicate, inverse, possible, options, callbackFunction, linkCallback);
|
|
1038
|
+
}
|
|
1039
|
+
export function selectorPanelRefresh(list, dom, kb, type, predicate, inverse, possible, options, callbackFunction, linkCallback) {
|
|
1040
|
+
const style0 = 'border: 0.1em solid #ddd; border-bottom: none; width: 95%; height: 2em; padding: 0.5em;';
|
|
1041
|
+
let selected = null;
|
|
1042
|
+
list.innerHTML = '';
|
|
1043
|
+
const refreshItem = function (box, x) {
|
|
1044
|
+
// Scope to hold item and x
|
|
1045
|
+
let item;
|
|
1046
|
+
// eslint-disable-next-line prefer-const
|
|
1047
|
+
let image;
|
|
1048
|
+
const setStyle = function () {
|
|
1049
|
+
const already = inverse
|
|
1050
|
+
? kb.each(undefined, predicate, x)
|
|
1051
|
+
: kb.each(x, predicate);
|
|
1052
|
+
iconDiv.setAttribute('class', already.length === 0 ? 'hideTillHover' : ''); // See tabbedtab.css
|
|
1053
|
+
image.setAttribute('src', options.connectIcon || iconBase + 'noun_25830.svg');
|
|
1054
|
+
image.setAttribute('title', already.length ? already.length : 'attach');
|
|
1055
|
+
};
|
|
1056
|
+
const f = index.twoLine.widgetForClass(type);
|
|
1057
|
+
// eslint-disable-next-line prefer-const
|
|
1058
|
+
item = f(dom, x);
|
|
1059
|
+
item.setAttribute('style', style0);
|
|
1060
|
+
const nav = dom.createElement('div');
|
|
1061
|
+
nav.setAttribute('class', 'hideTillHover'); // See tabbedtab.css
|
|
1062
|
+
nav.setAttribute('style', 'float:right; width:10%');
|
|
1063
|
+
const a = dom.createElement('a');
|
|
1064
|
+
a.setAttribute('href', x.uri);
|
|
1065
|
+
a.setAttribute('style', 'float:right');
|
|
1066
|
+
nav.appendChild(a).textContent = '>';
|
|
1067
|
+
box.appendChild(nav);
|
|
1068
|
+
const iconDiv = dom.createElement('div');
|
|
1069
|
+
iconDiv.setAttribute('style', (inverse ? 'float:left;' : 'float:right;') + ' width:30px;');
|
|
1070
|
+
image = dom.createElement('img');
|
|
1071
|
+
setStyle();
|
|
1072
|
+
iconDiv.appendChild(image);
|
|
1073
|
+
box.appendChild(iconDiv);
|
|
1074
|
+
item.addEventListener('click', function (event) {
|
|
1075
|
+
if (selected === item) {
|
|
1076
|
+
// deselect
|
|
1077
|
+
item.setAttribute('style', style0);
|
|
1078
|
+
selected = null;
|
|
1079
|
+
}
|
|
1080
|
+
else {
|
|
1081
|
+
if (selected)
|
|
1082
|
+
selected.setAttribute('style', style0);
|
|
1083
|
+
item.setAttribute('style', style0 + 'background-color: #ccc; color:black;');
|
|
1084
|
+
selected = item;
|
|
1085
|
+
}
|
|
1086
|
+
callbackFunction(x, event, selected === item);
|
|
1087
|
+
setStyle();
|
|
1088
|
+
}, false);
|
|
1089
|
+
image.addEventListener('click', function (event) {
|
|
1090
|
+
linkCallback(x, event, inverse, setStyle);
|
|
1091
|
+
}, false);
|
|
1092
|
+
box.appendChild(item);
|
|
1093
|
+
return box;
|
|
1094
|
+
};
|
|
1095
|
+
for (let i = 0; i < possible.length; i++) {
|
|
1096
|
+
const box = dom.createElement('div');
|
|
1097
|
+
list.appendChild(box);
|
|
1098
|
+
refreshItem(box, possible[i]);
|
|
1099
|
+
}
|
|
1100
|
+
return list;
|
|
1101
|
+
}
|
|
1102
|
+
// ###########################################################################
|
|
1103
|
+
//
|
|
1104
|
+
// Small compact views of things
|
|
1105
|
+
//
|
|
1106
|
+
export let index = {};
|
|
1107
|
+
// ///////////////////////////////////////////////////////////////////////////
|
|
1108
|
+
// We need these for anything which is a subject of an attachment.
|
|
1109
|
+
//
|
|
1110
|
+
// These should be moved to type-dependeent UI code. Related panes maybe
|
|
1111
|
+
function twoLineDefault(dom, x) {
|
|
1112
|
+
// Default
|
|
1113
|
+
const box = dom.createElement('div');
|
|
1114
|
+
box.textContent = utils.label(x);
|
|
1115
|
+
return box;
|
|
1116
|
+
}
|
|
1117
|
+
/**
|
|
1118
|
+
* Find a function that can create a widget for a given class
|
|
1119
|
+
* @param c The RDF class for which we want a widget generator function
|
|
1120
|
+
*/
|
|
1121
|
+
function twoLineWidgetForClass(c) {
|
|
1122
|
+
let widget = index.twoLine[c.uri];
|
|
1123
|
+
const kb = store;
|
|
1124
|
+
if (widget)
|
|
1125
|
+
return widget;
|
|
1126
|
+
const sup = kb.findSuperClassesNT(c);
|
|
1127
|
+
for (const cl in sup) {
|
|
1128
|
+
widget = index.twoLine[kb.fromNT(cl).uri];
|
|
1129
|
+
if (widget)
|
|
1130
|
+
return widget;
|
|
1131
|
+
}
|
|
1132
|
+
return index.twoLine[''];
|
|
1133
|
+
}
|
|
1134
|
+
/**
|
|
1135
|
+
* Display a transaction
|
|
1136
|
+
* @param x Should have attributes through triples in store:
|
|
1137
|
+
* * ns.qu('payee') -> a named node
|
|
1138
|
+
* * ns.qu('date) -> a literal
|
|
1139
|
+
* * ns.qu('amount') -> a literal
|
|
1140
|
+
*/
|
|
1141
|
+
function twoLineTransaction(dom, x) {
|
|
1142
|
+
let failed = '';
|
|
1143
|
+
const enc = function (p) {
|
|
1144
|
+
const y = store.any(x, ns.qu(p));
|
|
1145
|
+
if (!y)
|
|
1146
|
+
failed += '@@ No value for ' + p + '! ';
|
|
1147
|
+
return y ? utils.escapeForXML(y.value) : '?'; // @@@@
|
|
1148
|
+
};
|
|
1149
|
+
const box = dom.createElement('table');
|
|
1150
|
+
box.innerHTML = `
|
|
1151
|
+
<tr>
|
|
1152
|
+
<td colspan="2"> ${enc('payee')}</td>
|
|
1153
|
+
< /tr>
|
|
1154
|
+
< tr >
|
|
1155
|
+
<td>${enc('date').slice(0, 10)}</td>
|
|
1156
|
+
<td style = "text-align: right;">${enc('amount')}</td>
|
|
1157
|
+
</tr>`;
|
|
1158
|
+
if (failed) {
|
|
1159
|
+
box.innerHTML = `
|
|
1160
|
+
<tr>
|
|
1161
|
+
<td><a href="${utils.escapeForXML(x.uri)}">${utils.escapeForXML(failed)}</a></td>
|
|
1162
|
+
</tr>`;
|
|
1163
|
+
}
|
|
1164
|
+
return box;
|
|
1165
|
+
}
|
|
1166
|
+
/**
|
|
1167
|
+
* Display a trip
|
|
1168
|
+
* @param x Should have attributes through triples in store:
|
|
1169
|
+
* * ns.dc('title') -> a literal
|
|
1170
|
+
* * ns.cal('dtstart') -> a literal
|
|
1171
|
+
* * ns.cal('dtend') -> a literal
|
|
1172
|
+
*/
|
|
1173
|
+
function twoLineTrip(dom, x) {
|
|
1174
|
+
const enc = function (p) {
|
|
1175
|
+
const y = store.any(x, p);
|
|
1176
|
+
return y ? utils.escapeForXML(y.value) : '?';
|
|
1177
|
+
};
|
|
1178
|
+
const box = dom.createElement('table');
|
|
1179
|
+
box.innerHTML = `
|
|
1180
|
+
<tr>
|
|
1181
|
+
<td colspan="2">${enc(ns.dc('title'))}</td>
|
|
1182
|
+
</tr>
|
|
1183
|
+
<tr style="color: #777">
|
|
1184
|
+
<td>${enc(ns.cal('dtstart'))}</td>
|
|
1185
|
+
<td>${enc(ns.cal('dtend'))}</td>
|
|
1186
|
+
</tr>`;
|
|
1187
|
+
return box;
|
|
1188
|
+
}
|
|
1189
|
+
/**
|
|
1190
|
+
* Stick a stylesheet link the document if not already there
|
|
1191
|
+
*/
|
|
1192
|
+
export function addStyleSheet(dom, href) {
|
|
1193
|
+
const links = dom.querySelectorAll('link');
|
|
1194
|
+
for (let i = 0; i < links.length; i++) {
|
|
1195
|
+
if ((links[i].getAttribute('rel') || '') === 'stylesheet' &&
|
|
1196
|
+
(links[i].getAttribute('href') || '') === href) {
|
|
1197
|
+
return;
|
|
1198
|
+
}
|
|
1199
|
+
}
|
|
1200
|
+
const link = dom.createElement('link');
|
|
1201
|
+
link.setAttribute('rel', 'stylesheet');
|
|
1202
|
+
link.setAttribute('type', 'text/css');
|
|
1203
|
+
link.setAttribute('href', href);
|
|
1204
|
+
dom.getElementsByTagName('head')[0].appendChild(link);
|
|
1205
|
+
}
|
|
1206
|
+
// Figure (or guess) whether this is an image, etc
|
|
1207
|
+
//
|
|
1208
|
+
export function isAudio(file) {
|
|
1209
|
+
return isImage(file, 'audio');
|
|
1210
|
+
}
|
|
1211
|
+
export function isVideo(file) {
|
|
1212
|
+
return isImage(file, 'video');
|
|
1213
|
+
}
|
|
1214
|
+
/**
|
|
1215
|
+
*
|
|
1216
|
+
*/
|
|
1217
|
+
export function isImage(file, kind) {
|
|
1218
|
+
const dcCLasses = {
|
|
1219
|
+
audio: 'http://purl.org/dc/dcmitype/Sound',
|
|
1220
|
+
image: 'http://purl.org/dc/dcmitype/Image',
|
|
1221
|
+
video: 'http://purl.org/dc/dcmitype/MovingImage'
|
|
1222
|
+
};
|
|
1223
|
+
const what = kind || 'image';
|
|
1224
|
+
// See https://github.com/linkeddata/rdflib.js/blob/e367d5088c/src/formula.ts#L554
|
|
1225
|
+
//
|
|
1226
|
+
const typeURIs = store.findTypeURIs(file);
|
|
1227
|
+
// See https://github.com/linkeddata/rdflib.js/blob/d5000f/src/utils-js.js#L14
|
|
1228
|
+
// e.g.'http://www.w3.org/ns/iana/media-types/audio'
|
|
1229
|
+
const prefix = Util.mediaTypeClass(what + '/*').uri.split('*')[0];
|
|
1230
|
+
for (const t in typeURIs) {
|
|
1231
|
+
if (t.startsWith(prefix))
|
|
1232
|
+
return true;
|
|
1233
|
+
}
|
|
1234
|
+
if (dcCLasses[what] in typeURIs)
|
|
1235
|
+
return true;
|
|
1236
|
+
return false;
|
|
1237
|
+
}
|
|
1238
|
+
/**
|
|
1239
|
+
* File upload button
|
|
1240
|
+
* @param dom The DOM aka document
|
|
1241
|
+
* @param droppedFileHandler Same handler function as drop, takes array of file objects
|
|
1242
|
+
* @returns {Element} - a div with a button and a inout in it
|
|
1243
|
+
* The input is hidden, as it is uglky - the user clicks on the nice icons and fires the input.
|
|
1244
|
+
*/
|
|
1245
|
+
// See https://developer.mozilla.org/en-US/docs/Web/API/File/Using_files_from_web_applications
|
|
1246
|
+
export function fileUploadButtonDiv(dom, droppedFileHandler) {
|
|
1247
|
+
const div = dom.createElement('div');
|
|
1248
|
+
const input = div.appendChild(dom.createElement('input'));
|
|
1249
|
+
input.setAttribute('type', 'file');
|
|
1250
|
+
input.setAttribute('multiple', 'true');
|
|
1251
|
+
input.addEventListener('change', (event) => {
|
|
1252
|
+
debug.log('File drop event: ', event);
|
|
1253
|
+
if (event.files) {
|
|
1254
|
+
droppedFileHandler(event.files);
|
|
1255
|
+
}
|
|
1256
|
+
else if (event.target && event.target.files) {
|
|
1257
|
+
droppedFileHandler(event.target.files);
|
|
1258
|
+
}
|
|
1259
|
+
else {
|
|
1260
|
+
alert('Sorry no files .. internal error?');
|
|
1261
|
+
}
|
|
1262
|
+
}, false);
|
|
1263
|
+
input.style = 'display:none';
|
|
1264
|
+
const buttonElt = div.appendChild(button(dom, iconBase + 'noun_Upload_76574_000000.svg', 'Upload files', _event => {
|
|
1265
|
+
input.click();
|
|
1266
|
+
}));
|
|
1267
|
+
makeDropTarget(buttonElt, null, droppedFileHandler); // Can also just drop on button
|
|
1268
|
+
return div;
|
|
1269
|
+
}
|
|
1270
|
+
index = {
|
|
1271
|
+
line: { // Approx 80em
|
|
1272
|
+
},
|
|
1273
|
+
twoLine: {
|
|
1274
|
+
'': twoLineDefault,
|
|
1275
|
+
'http://www.w3.org/2000/10/swap/pim/qif#Transaction': twoLineTransaction,
|
|
1276
|
+
'http://www.w3.org/ns/pim/trip#Trip': twoLineTrip,
|
|
1277
|
+
widgetForClass: twoLineWidgetForClass
|
|
1278
|
+
}
|
|
1279
|
+
};
|
|
1280
|
+
//# sourceMappingURL=buttons.js.map
|