UNPKG

solid-panes

Version:

Solid-compatible Panes: applets and views for the mashlib and databrowser

509 lines (468 loc) 16.1 kB
/* Attachment Pane ** ** - Attach a document to a thing ** - View attachments ** - Look at all unattached Supporting Documents. ** - Drag a document onto the pane to attach it @@ ** ** */ import * as UI from 'solid-ui' import * as $rdf from 'rdflib' export default { icon: UI.icons.iconBase + 'noun_25830.svg', // noun_25830 name: 'attachments', // Does the subject deserve an attachments pane? // // In this case we will render any thing which is in any subclass of // certain classes, or also the certain classes themselves, as a // triage tool for correlating many attachees with attachments. // We also offer the pane for anything of any class which just has an attachment already. // label: function (subject, context) { const kb = context.session.store const t = kb.findTypeURIs(subject) const QU = $rdf.Namespace('http://www.w3.org/2000/10/swap/pim/qif#') const WF = $rdf.Namespace('http://www.w3.org/2005/01/wf/flow#') if ( t['http://www.w3.org/ns/pim/trip#Trip'] || // If in any subclass subject.uri === 'http://www.w3.org/ns/pim/trip#Trip' || t['http://www.w3.org/2005/01/wf/flow#Task'] || t['http://www.w3.org/2000/10/swap/pim/qif#Transaction'] || // subject.uri == 'http://www.w3.org/2000/10/swap/pim/qif#Transaction' || QU('Transaction') in kb.findSuperClassesNT(subject) || kb.holds(subject, WF('attachment')) ) { return 'attachments' } return null }, render: function (subject, context) { const dom = context.dom const kb = context.session.store const WF = $rdf.Namespace('http://www.w3.org/2005/01/wf/flow#') const QU = $rdf.Namespace('http://www.w3.org/2000/10/swap/pim/qif#') // //////////////////////////////////////////////////////////////////////////// const complain = function complain (message) { const pre = dom.createElement('pre') pre.setAttribute('style', 'background-color: pink') div.appendChild(pre) pre.appendChild(dom.createTextNode(message)) } // Where can we write about this thing? // // Returns term for document or null const findStore = function (kb, subject) { if (kb.updater.editable(subject.doc(), kb)) return subject.doc() const store = kb.any(subject.doc(), QU('annotationStore')) return store } const div = dom.createElement('div') const esc = UI.utils.escapeForXML div.setAttribute('class', 'attachPane') div.innerHTML = '<h1>' + esc(UI.utils.label(subject, true)) + ' attachments</h1>' // const predicate = WF('attachment') const range = QU('SupportingDocument') let subjects, multi const options = {} let currentMode = 0 // 0 -> Show all; 1 -> Attached; 2 -> unattached let currentSubject = null let currentObject = null const objectType = QU('SupportingDocument') // Find all members of the class which we know about // and sort them by an appropriate property. @@ Move to library // const getSortKeySimple = function (_c) { const uriMap = { 'http://www.w3.org/2005/01/wf/flow#Task': 'http://purl.org/dc/elements/1.1/created', 'http://www.w3.org/ns/pim/trip#Trip': 'http://www.w3.org/2002/12/cal/ical#dtstart', 'http://www.w3.org/2000/10/swap/pim/qif#Transaction': 'http://www.w3.org/2000/10/swap/pim/qif#date', 'http://www.w3.org/2000/10/swap/pim/qif#SupportingDocument': 'http://purl.org/dc/elements/1.1/date' } const uri = uriMap[subject.uri] return uri ? kb.sym(uri) : kb.any(subject, UI.ns.ui('sortBy')) } const getSortKey = function (c) { let k = getSortKeySimple(c.uri) if (k) return k const sup = kb.findSuperClassesNT(c) for (const cl in sup) { // note unordered -- could be closest first k = getSortKeySimple(kb.fromNT(cl).uri) if (k) return k } return undefined // failure } const getMembersAndSort = function (subject) { const sortBy = getSortKey(subject) let u, x, key const uriHash = kb.findMemberURIs(subject) const pairs = [] const subjects = [] for (u in uriHash) { // @@ TODO: Write away the need for exception on next line if (uriHash.hasOwnProperty(u)) { x = kb.sym(u) if (sortBy) { key = kb.any(x, sortBy) if (!key) { // complain("No key "+key+" sortby "+sortBy+" for "+x) key = '8888-12-31' } else { key = key.value } } else { complain('No sortby ' + sortBy + ' for ' + x) key = '9999-12-31' } // key = (sortBy && kb.any(x, sortBy)) || kb.literal("9999-12-31") // Undated appear future // if (!key) complain("Sort: '"+key+"' No "+sortBy+" for "+x) // Assume just not in this year pairs.push([key, x]) } } pairs.sort() pairs.reverse() // @@ Descending order .. made a toggle? for (let i = 0; i < pairs.length; i++) { subjects.push(pairs[i][1]) } return subjects } // Set up a triage of many class members against documents or just one if ( subject.uri === 'http://www.w3.org/ns/pim/trip#Trip' || QU('Transaction') in kb.findSuperClassesNT(subject) // subject.uri == 'http://www.w3.org/2000/10/swap/pim/qif#Transaction' ) { multi = true subjects = getMembersAndSort(subject) } else { currentSubject = subject currentMode = 1 // Show attached only. subjects = [subject] multi = false } // var store = findStore(kb, subject) // if (!store) complain("There is no annotation store for: "+subject.uri) // var objects = kb.each(undefined, ns.rdf('type'), range) const objects = getMembersAndSort(range) if (!objects) complain('objects:' + objects.length) const deselectObject = function () { currentObject = null preview.innerHTML = '' } const showFiltered = function (mode) { const filtered = mode === 0 ? objects : getFiltered() // eslint-enable UI.widgets.selectorPanelRefresh( objectList, dom, kb, objectType, predicate, true, filtered, options, showObject, linkClicked ) if (filtered.length === 1) { currentObject = filtered[0] showObject(currentObject, null, true) // @@ (Sure?) if only one select it. } else { deselectObject() } function getFiltered () { return mode === 1 ? currentSubject === null ? objects.filter(y => !!kb.holds(undefined, predicate, y)) : objects.filter(y => !!kb.holds(currentSubject, predicate, y)) : objects.filter(y => kb.each(undefined, predicate, y).length === 0) } } const setAttachment = function (x, y, value, refresh) { if (kb.holds(x, predicate, y) === value) return const verb = value ? 'attach' : 'detach' // complain("Info: starting to "+verb+" " + y.uri + " to "+x.uri+ ":\n") const linkDone3 = function (uri, ok, body) { if (ok) { // complain("Success "+verb+" "+y.uri+" to "+x.uri+ ":\n"+ body) refresh() } else { complain( 'Error: Unable to ' + verb + ' ' + y.uri + ' to ' + x.uri + ':\n' + body ) } } const store = findStore(kb, x) if (!store) { complain('There is no annotation store for: ' + x.uri) } else { const sts = [$rdf.st(x, predicate, y, store)] if (value) { kb.updater.update([], sts, linkDone3) } else { kb.updater.update(sts, [], linkDone3) } } } const linkClicked = function (x, event, inverse, refresh) { let s, o if (inverse) { // Objectlist if (!currentSubject) { complain('No subject for the link has been selected') return } else { s = currentSubject o = x } } else { // Subjectlist if (!currentObject) { complain('No object for the link has been selected') return } else { s = x o = currentObject } } setAttachment(s, o, !kb.holds(s, predicate, o), refresh) // @@ toggle } // When you click on a subject, filter the objects connected to the subject in Mode 1 const showSubject = function (x, event, selected) { if (selected) { currentSubject = x } else { currentSubject = null if (currentMode === 1) deselectObject() } // If all are displayed, refresh would just annoy: if (currentMode !== 0) showFiltered(currentMode) // Refresh the objects } if (multi) { const subjectList = UI.widgets.selectorPanel( dom, kb, subject, predicate, false, subjects, options, showSubject, linkClicked ) subjectList.setAttribute( 'style', 'background-color: white; width: 25em; height: 100%; padding: 0 em; overflow:scroll; float:left' ) div.appendChild(subjectList) } const showObject = function (x, event, selected) { if (!selected) { deselectObject() preview.innerHTML = '' // Clean out what is there // complain("Show object "+x.uri) return } currentObject = x try { /* var dispalyable = function (kb, x) { var cts = kb.fetcher.getHeader(x, 'content-type') if (cts) { var displayables = ['text/html', 'image/png', 'application/pdf'] for (var j = 0; j < cts.length; j++) { for (var k = 0; k < displayables.length; k++) { if (cts[j].indexOf(displayables[k]) >= 0) { return true } } } } return false } */ preview.innerHTML = 'Loading ....' if (x.uri) { kb.fetcher .load(x.uri) .then(() => { const outliner = context.getOutliner(dom) const display = outliner.propertyTable(x) // ,table, pane preview.innerHTML = '' preview.appendChild(display) }) .catch(err => { preview.textContent = 'Error loading ' + x.uri + ': ' + err }) } /* if (dispalyable(kb, x) || x.uri.slice(-4) == ".pdf" || x.uri.slice(-4) == ".png" || x.uri.slice(-5) == ".html" || x.uri.slice(-5) == ".jpeg") { // @@@@@@ MAJOR KLUDGE! use metadata after HEAD preview.innerHTML = '<iframe height="100%" width="100%"src="' + x.uri + '">' + x.uri + '</iframe>' } else { } */ } catch (e) { preview.innerHTML = '<span style="background-color: pink;">' + 'Error:' + e + '</span>' // @@ enc } } div.setAttribute( 'style', 'background-color: white; width:40cm; height:20cm;' ) const headerButtons = function (dom, labels, intial, callback) { const head = dom.createElement('table') let current = intial head.setAttribute( 'style', 'float: left; width: 30em; padding: 0.5em; height: 1.5em; background-color: #ddd; color: #444; font-weight: bold' ) const tr = dom.createElement('tr') const style0 = 'border-radius: 0.6em; text-align: center;' const style1 = style0 + 'background-color: #ccc; color: black;' head.appendChild(tr) const setStyles = function () { for (i = 0; i < labels.length; i++) { buttons[i].setAttribute('style', i === current ? style1 : style0) } } let i, b const buttons = [] for (i = 0; i < labels.length; i++) { b = buttons[i] = dom.createElement('td') b.textContent = labels[i] tr.appendChild(buttons[i]) const listen = function (b, i) { b.addEventListener('click', function (_e) { current = i setStyles() callback(i) }) } listen(b, i) } setStyles() return head } const setMode = function (mode) { if (mode !== currentMode) { currentMode = mode deselectObject() showFiltered(mode) } } const wrapper = dom.createElement('div') wrapper.setAttribute( 'style', ' width: 30em; height: 100%; padding: 0em; float:left;' ) // wrapper.appendChild(head) div.appendChild(wrapper) wrapper.appendChild( headerButtons( dom, ['all', 'attached', 'not attached'], currentMode, setMode ) ) const objectList = UI.widgets.selectorPanel( dom, kb, objectType, predicate, true, objects, options, showObject, linkClicked ) objectList.setAttribute( 'style', 'background-color: #ffe; width: 30em; height: 100%; padding: 0em; overflow:scroll;' ) // float:left wrapper.appendChild(objectList) // objectList.insertBefore(head, objectList.firstChild) const preview = dom.createElement('div') preview.setAttribute( 'style', /* background-color: black; */ 'padding: 0em; margin: 0; height: 100%; overflow:scroll;' ) div.appendChild(preview) showFiltered(currentMode) if (subjects.length > 0 && multi) { const stores = {} for (let k = 0; k < subjects.length; k++) { const store = findStore(kb, subjects[k]) if (store) stores[store.uri] = subjects[k] // if (!store) complain("No store for "+subjects[k].uri) } for (const storeURI in stores) { // var store = findStore(kb,subjects[subjectList.length-1]) const store = kb.sym(storeURI) const mintBox = dom.createElement('div') mintBox.setAttribute( 'style', 'clear: left; width: 20em; margin-top:2em; background-color:#ccc; border-radius: 1em; padding: 1em; font-weight: bold;' ) mintBox.textContent = '+ New ' + UI.utils.label(subject) mintBox.textContent += ' in ' + UI.utils.label(store) const storeLab = dom.createElement('span') storeLab.setAttribute( 'style', 'font-weight: normal; font-size: 80%; color: #777;' ) storeLab.textContent = storeURI mintBox.appendChild(dom.createElement('br')) mintBox.appendChild(storeLab) /* var mintButton = dom.createElement('img') mintBox.appendChild(mintButton) mintButton.setAttribute('src', ...); @@ Invokes master handler */ mintBox.addEventListener( 'click', function (_event) { const thisForm = UI.widgets.promptForNew( dom, kb, subject, predicate, subject, null, store, function (ok, _body) { if (!ok) { // callback(ok, body); // @@ if ok, need some form of refresh of the select for the new thing } else { // Refresh @@ } } ) try { div.insertBefore(thisForm, mintBox.nextSibling) // Sigh no insertAfter } catch (e) { div.appendChild(thisForm) } // var newObject = thisForm.AJAR_subject }, false ) div.appendChild(mintBox) } } // if (!me) complain("(You do not have your Web Id set. Set your Web ID to make changes.)") return div } } // pane object // ends