UNPKG

solid-panes

Version:

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

944 lines (815 loc) • 36.2 kB
"use strict"; /* Scheduler Pane ** ** */ /* global alert */ var UI = require('solid-ui'); var $rdf = UI.rdf; var ns = UI.ns; // @@ Give other combos too-- see schedule ontology var possibleAvailabilities = [ns.sched('No'), ns.sched('Maybe'), ns.sched('Yes')]; module.exports = { icon: UI.icons.iconBase + 'noun_346777.svg', // @@ better? name: 'schedule', audience: [ns.solid('PowerUser')], // Does the subject deserve an Scheduler pane? label: function label(subject, context) { var kb = context.session.store; var t = kb.findTypeURIs(subject); if (t['http://www.w3.org/ns/pim/schedule#SchedulableEvent']) { return 'Scheduling poll'; } return null; // No under other circumstances }, // Mint a new Schedule poll mintClass: ns.sched('SchedulableEvent'), mintNew: function mintNew(context, options) { return new Promise(function (resolve, reject) { var ns = UI.ns; var kb = context.session.store; var newBase = options.newBase; var thisInstance = options.useExisting || $rdf.sym(options.newBase + 'index.ttl#this'); var complainIfBad = function complainIfBad(ok, body) { if (ok) return; console.log('Error in Schedule Pane: Error constructing new scheduler: ' + body); reject(new Error(body)); }; // ////////////////////// Accesss control // Two variations of ACL for this app, public read and public read/write // In all cases owner has read write control var genACLtext = function genACLtext(docURI, aclURI, allWrite) { var g = $rdf.graph(); var auth = $rdf.Namespace('http://www.w3.org/ns/auth/acl#'); var a = g.sym(aclURI + '#a1'); var acl = g.sym(aclURI); var doc = g.sym(docURI); g.add(a, UI.ns.rdf('type'), auth('Authorization'), acl); g.add(a, auth('accessTo'), doc, acl); g.add(a, auth('agent'), me, acl); g.add(a, auth('mode'), auth('Read'), acl); g.add(a, auth('mode'), auth('Write'), acl); g.add(a, auth('mode'), auth('Control'), acl); a = g.sym(aclURI + '#a2'); g.add(a, UI.ns.rdf('type'), auth('Authorization'), acl); g.add(a, auth('accessTo'), doc, acl); g.add(a, auth('agentClass'), ns.foaf('Agent'), acl); g.add(a, auth('mode'), auth('Read'), acl); if (allWrite) { g.add(a, auth('mode'), auth('Write'), acl); } return $rdf.serialize(acl, g, aclURI, 'text/turtle'); }; /* var setACL3 = function (docURI, allWrite, callbackFunction) { var aclText = genACLtext(docURI, aclDoc.uri, allWrite) return UI.acl.setACL(docURI, aclText, callbackFunction) } */ var setACL2 = function setACL2(docURI, allWrite, callbackFunction) { var aclDoc = kb.any(kb.sym(docURI), kb.sym('http://www.iana.org/assignments/link-relations/acl')); // @@ check that this get set by web.js if (aclDoc) { // Great we already know where it is var aclText = genACLtext(docURI, aclDoc.uri, allWrite); return fetcher.webOperation('PUT', aclDoc.uri, { data: aclText, contentType: 'text/turtle' }).then(function (_result) { return callbackFunction(true); })["catch"](function (err) { callbackFunction(false, err.message); }); } else { return fetcher.load(docURI)["catch"](function (err) { callbackFunction(false, 'Getting headers for ACL: ' + err); }).then(function () { var aclDoc = kb.any(kb.sym(docURI), kb.sym('http://www.iana.org/assignments/link-relations/acl')); if (!aclDoc) { // complainIfBad(false, "No Link rel=ACL header for " + docURI) throw new Error('No Link rel=ACL header for ' + docURI); } var aclText = genACLtext(docURI, aclDoc.uri, allWrite); return fetcher.webOperation('PUT', aclDoc.uri, { data: aclText, contentType: 'text/turtle' }); }).then(function (_result) { return callbackFunction(true); })["catch"](function (err) { callbackFunction(false, err.message); }); } }; // Body of mintNew var fetcher = kb.fetcher; var updater = kb.updater; var me = options.me || UI.authn.currentUser(); if (!me) { console.log('MUST BE LOGGED IN'); alert('NOT LOGGED IN'); return; } var base = thisInstance.dir().uri; var newDetailsDoc, newInstance; // , newIndexDoc if (options.useExisting) { newInstance = options.useExisting; newBase = thisInstance.dir().uri; newDetailsDoc = newInstance.doc(); // newIndexDoc = null if (options.newBase) { throw new Error('mint new scheduler: Illegal - have both new base and existing event'); } } else { newDetailsDoc = kb.sym(newBase + 'details.ttl'); // newIndexDoc = kb.sym(newBase + 'index.html') newInstance = kb.sym(newDetailsDoc.uri + '#event'); } var newResultsDoc = kb.sym(newBase + 'results.ttl'); var toBeCopied = options.noIndexHTML ? {} : [{ local: 'index.html', contentType: 'text/html' }]; var agenda = []; // @@ This needs some form of visible progress bar for (var f = 0; f < toBeCopied.length; f++) { var item = toBeCopied[f]; var fun = function copyItem(item) { agenda.push(function () { var newURI = newBase + item.local; console.log('Copying ' + base + item.local + ' to ' + newURI); var setThatACL = function setThatACL() { setACL2(newURI, false, function (ok, message) { if (!ok) { complainIfBad(ok, 'FAILED to set ACL ' + newURI + ' : ' + message); console.log('FAILED to set ACL ' + newURI + ' : ' + message); } else { agenda.shift()(); // beware too much nesting } }); }; kb.fetcher.webCopy(base + item.local, newBase + item.local, item.contentType).then(function () { return UI.authn.checkUser(); }).then(function (webId) { me = webId; setThatACL(); })["catch"](function (err) { console.log('FAILED to copy ' + base + item.local + ' : ' + err.message); complainIfBad(false, 'FAILED to copy ' + base + item.local + ' : ' + err.message); }); }); }; fun(item); } agenda.push(function createDetailsFile() { kb.add(newInstance, ns.rdf('type'), ns.sched('SchedulableEvent'), newDetailsDoc); if (me) { kb.add(newInstance, ns.dc('author'), me, newDetailsDoc); // Who is sending the invitation? kb.add(newInstance, ns.foaf('maker'), me, newDetailsDoc); // Uneditable - wh is allowed to edit this? } kb.add(newInstance, ns.dc('created'), new Date(), newDetailsDoc); kb.add(newInstance, ns.sched('resultsDocument'), newDetailsDoc); updater.put(newDetailsDoc, kb.statementsMatching(undefined, undefined, undefined, newDetailsDoc), 'text/turtle', function (uri2, ok, message) { if (ok) { agenda.shift()(); } else { complainIfBad(ok, 'FAILED to save new scheduler at: ' + newDetailsDoc + ' : ' + message); console.log('FAILED to save new scheduler at: ' + newDetailsDoc + ' : ' + message); } }); }); agenda.push(function () { kb.fetcher.webOperation('PUT', newResultsDoc.uri, { data: '', contentType: 'text/turtle' }).then(function () { agenda.shift()(); })["catch"](function (err) { complainIfBad(false, 'Failed to initialize empty results file: ' + err.message); }); }); agenda.push(function () { setACL2(newResultsDoc.uri, true, function (ok, body) { complainIfBad(ok, 'Failed to set Read-Write ACL on results file: ' + body); if (ok) agenda.shift()(); }); }); agenda.push(function () { setACL2(newDetailsDoc.uri, false, function (ok, body) { complainIfBad(ok, 'Failed to set read ACL on configuration file: ' + body); if (ok) agenda.shift()(); }); }); agenda.push(function () { // give the user links to the new app console.log('Finished minting new scheduler'); options.newInstance = newInstance; resolve(options); }); agenda.shift()(); // Created new data files. }); // promise }, // mintNew // Render one meeting schedule poll render: function render(subject, context) { var dom = context.dom; var kb = context.session.store; var ns = UI.ns; var invitation = subject; var appPathSegment = 'app-when-can-we.w3.org'; // how to allocate this string and connect to // //////////////////////////////////////////// var fetcher = kb.fetcher; var updater = kb.updater; var waitingForLogin = false; var thisInstance = subject; var detailsDoc = subject.doc(); var baseDir = detailsDoc.dir(); var base = baseDir.uri; var resultsDoc = $rdf.sym(base + 'results.ttl'); // var formsURI = base + 'forms.ttl' // We can't in fact host stuff from there because of CORS var formsURI = 'https://solid.github.io/solid-panes/schedule/formsForSchedule.ttl'; var form1 = kb.sym(formsURI + '#form1'); var form2 = kb.sym(formsURI + '#form2'); var form3 = kb.sym(formsURI + '#form3'); var formText = require('./formsForSchedule.js'); $rdf.parse(formText, kb, formsURI, 'text/turtle'); // Load forms directly var inputStyle = 'background-color: #eef; padding: 0.5em; border: .5em solid white; font-size: 100%'; // font-size: 120% var buttonIconStyle = 'width: 1.8em; height: 1.8em;'; // Utility functions var complainIfBad = function complainIfBad(ok, message) { if (!ok) { div.appendChild(UI.widgets.errorMessageBlock(dom, message, 'pink')); } }; var clearElement = function clearElement(ele) { while (ele.firstChild) { ele.removeChild(ele.firstChild); } return ele; }; var refreshCellColor = function refreshCellColor(cell, value) { var bg = kb.any(value, UI.ns.ui('backgroundColor')); if (bg) { cell.setAttribute('style', 'padding: 0.3em; text-align: center; background-color: ' + bg + ';'); } }; var me; UI.authn.checkUser().then(function (webId) { me = webId; if (logInOutButton) { logInOutButton.refresh(); } if (webId && waitingForLogin) { waitingForLogin = false; showAppropriateDisplay(); } }); console.log('me: ' + me); // @@ curently not actually used elsewhere // ////////////////////////////// Reproduction: spawn a new instance // // Viral growth path: user of app decides to make another instance // var newInstanceButton = function newInstanceButton() { var b = UI.authn.newAppInstance(dom, { noun: 'scheduler' }, initializeNewInstanceInWorkspace); b.firstChild.setAttribute('style', inputStyle); return b; }; // newInstanceButton // /////////////////////// Create new document files for new instance of app var initializeNewInstanceInWorkspace = function initializeNewInstanceInWorkspace(ws) { var newBase = kb.any(ws, ns.space('uriPrefix')); if (!newBase) { newBase = ws.uri.split('#')[0]; } else { newBase = newBase.value; } if (newBase.slice(-1) !== '/') { $rdf.log.error(appPathSegment + ': No / at end of uriPrefix ' + newBase); // @@ paramater? newBase = newBase + '/'; } var now = new Date(); newBase += appPathSegment + '/id' + now.getTime() + '/'; // unique id initializeNewInstanceAtBase(thisInstance, newBase); }; var initializeNewInstanceAtBase = function initializeNewInstanceAtBase(thisInstance, newBase) { var options = { thisInstance: thisInstance, newBase: newBase }; this.mintNew(context, options).then(function (options) { var p = div.appendChild(dom.createElement('p')); p.setAttribute('style', 'font-size: 140%;'); p.innerHTML = "Your <a href='" + options.newInstance.uri + "'><b>new scheduler</b></a> is ready to be set up. " + "<br/><br/><a href='" + options.newInstance.uri + "'>Say when you what days work for you.</a>"; })["catch"](function (error) { complainIfBad(false, 'Error createing new scheduler at ' + options.newInstance + ': ' + error); }); }; // /////////////////////// var getForms = function getForms() { console.log('getforms()'); getDetails(); /* fetcher.nowOrWhenFetched(formsURI, undefined, function (ok, body) { console.log('getforms() ok? ' + ok) if (!ok) return complainIfBad(ok, body) getDetails() }) */ }; var getDetails = function getDetails() { console.log('getDetails()'); // Looking for blank screen hang-up fetcher.nowOrWhenFetched(detailsDoc.uri, undefined, function (ok, body) { console.log('getDetails() ok? ' + ok); if (!ok) return complainIfBad(ok, body); showAppropriateDisplay(); }); }; var showAppropriateDisplay = function showAppropriateDisplay() { console.log('showAppropriateDisplay()'); UI.authn.checkUser().then(function (webId) { if (!webId) { return showSignon(); } // On gh-pages, the turtle will not load properly (bad mime type) // but we can trap it as being a non-editable server. if (!kb.updater.editable(detailsDoc.uri, kb) || kb.holds(subject, ns.rdf('type'), ns.wf('TemplateInstance'))) { // This is read-only example e.g. on github pages, etc showBootstrap(div); return; } var ready = kb.any(subject, ns.sched('ready')); if (!ready) { showForms(); } else { // no editing not author getResults(); } }); }; var showSignon = function showSignon() { clearElement(naviMain); var signonContext = { div: div, dom: dom }; UI.authn.logIn(signonContext).then(function (context) { me = context.me; waitingForLogin = false; // untested showAppropriateDisplay(); }); }; var showBootstrap = function showBootstrap() { var div = clearElement(naviMain); div.appendChild(UI.authn.newAppInstance(dom, { noun: 'poll' }, initializeNewInstanceInWorkspace)); div.appendChild(dom.createElement('hr')); // @@ var p = div.appendChild(dom.createElement('p')); p.textContent = 'Where would you like to store the data for the poll? ' + 'Give the URL of the directory where you would like the data stored.'; var baseField = div.appendChild(dom.createElement('input')); baseField.setAttribute('type', 'text'); baseField.size = 80; // really a string baseField.label = 'base URL'; baseField.autocomplete = 'on'; div.appendChild(dom.createElement('br')); // @@ var button = div.appendChild(dom.createElement('button')); button.setAttribute('style', inputStyle); button.textContent = 'Start new poll at this URI'; button.addEventListener('click', function (_e) { var newBase = baseField.value; if (newBase.slice(-1) !== '/') { newBase += '/'; } initializeNewInstanceAtBase(thisInstance, newBase); }); }; // ///////////// The forms to configure the poll var doneButton = dom.createElement('button'); var showForms = function showForms() { clearElement(naviCenter); // Remove refresh button if nec var div = naviMain; var wizard = true; var currentSlide = 0; var gotDoneButton = false; if (wizard) { var forms = [form1, form2, form3]; var slides = []; currentSlide = 0; for (var f = 0; f < forms.length; f++) { var slide = dom.createElement('div'); UI.widgets.appendForm(document, slide, {}, subject, forms[f], detailsDoc, complainIfBad); slides.push(slide); } var refresh = function refresh() { clearElement(naviMain).appendChild(slides[currentSlide]); if (currentSlide === 0) { b1.setAttribute('disabled', ''); } else { b1.removeAttribute('disabled'); } if (currentSlide === slides.length - 1) { b2.setAttribute('disabled', ''); if (!gotDoneButton) { // Only expose at last slide seen naviCenter.appendChild(emailButton); // could also check data shape naviCenter.appendChild(doneButton); // could also check data shape gotDoneButton = true; } } else { b2.removeAttribute('disabled'); } }; var b1 = clearElement(naviLeft).appendChild(dom.createElement('button')); b1.setAttribute('style', inputStyle); b1.textContent = '<- go back'; b1.addEventListener('click', function (_e) { if (currentSlide > 0) { currentSlide -= 1; refresh(); } }, false); var b2 = clearElement(naviRight).appendChild(dom.createElement('button')); b2.setAttribute('style', inputStyle); b2.textContent = 'continue ->'; b2.addEventListener('click', function (_e) { if (currentSlide < slides.length - 1) { currentSlide += 1; refresh(); } }, false); refresh(); } else { // not wizard one big form // @@@ create the initial config doc if not exist var table = div.appendChild(dom.createElement('table')); UI.widgets.appendForm(document, table, {}, subject, form1, detailsDoc, complainIfBad); UI.widgets.appendForm(document, table, {}, subject, form2, detailsDoc, complainIfBad); UI.widgets.appendForm(document, table, {}, subject, form3, detailsDoc, complainIfBad); naviCenter.appendChild(doneButton); // could also check data shape } // @@@ link config to results var insertables = []; insertables.push($rdf.st(subject, ns.sched('availabilityOptions'), ns.sched('YesNoMaybe'), detailsDoc)); insertables.push($rdf.st(subject, ns.sched('ready'), new Date(), detailsDoc)); insertables.push($rdf.st(subject, ns.sched('results'), resultsDoc, detailsDoc)); // @@ also link in results doneButton.setAttribute('style', inputStyle); doneButton.textContent = 'Go to poll'; doneButton.addEventListener('click', function (_e) { if (kb.any(subject, ns.sched('ready'))) { // already done getResults(); naviRight.appendChild(emailButton); } else { naviRight.appendChild(emailButton); kb.updater.update([], insertables, function (uri, success, errorBody) { if (!success) { complainIfBad(success, errorBody); } else { // naviRight.appendChild(emailButton) getResults(); } }); } }, false); var emailButton = dom.createElement('button'); emailButton.setAttribute('style', inputStyle); var emailIcon = emailButton.appendChild(dom.createElement('img')); emailIcon.setAttribute('src', UI.icons.iconBase + 'noun_480183.svg'); // noun_480183.svg emailIcon.setAttribute('style', buttonIconStyle); // emailButton.textContent = 'email invitations' emailButton.addEventListener('click', function (_e) { var title = kb.anyValue(subject, ns.cal('summary')) || kb.anyValue(subject, ns.dc('title')) || ''; var mailto = 'mailto:' + kb.each(subject, ns.sched('invitee')).map(function (who) { var mbox = kb.any(who, ns.foaf('mbox')); return mbox ? mbox.uri.replace('mailto:', '') : ''; }).join(',') + '?subject=' + encodeURIComponent(title + '-- When can we meet?') + '&body=' + encodeURIComponent(title + '\n\nWhen can you?\n\nSee ' + subject + '\n'); // @@ assumed there is a data browser console.log('Mail: ' + mailto); window.location.href = mailto; }, false); }; // showForms // Ask for each day, what times .. @@ to be added some time /* var setTimesOfDay = function () { var i, j, x, y, slot, cell, day var insertables = [] var possibleDays = kb.each(invitation, ns.sched('option')) .map(function (opt) {return kb.any(opt, ns.cal('dtstart'))}) var cellLookup = [] var slots = kb.each(invitation, ns.sched('slot')) if (slots.length === 0) { for (i = 0; i < 2; i++) { slot = UI.widgets.newThing(detailsDoc) insertables.push($rdf.st(invitation, ns.sched('slot'), slot)) insertables.push($rdf.st(slot, ns.rdfs('label'), 'slot ' + (i + 1))) for (j = 0; j < possibleDays.length; j++) { day - possibleDays[j] x = kb.any(slot, ns.rdfs('label')) y = kb.any(day, ns.cal('dtstart')) cell = UI.widgets.newThing(detailsDoc) cellLookup[x.toNT() + y.toNT()] = cell insertables.push($rdf.st(slot, ns.sched('cell'), cell)) insertables.push($rdf.st(cell, ns.sched('day'), possibleDays[j])) } } } var query = new $rdf.Query('TimesOfDay') var v = {}['day', 'label', 'value', 'slot', 'cell'].map(function (x) { query.vars.push(v[x] = $rdf.variable(x)) }) query.pat.add(invitation, ns.sched('slot'), v.slot) query.pat.add(v.slot, ns.rdfs('label'), v.label) query.pat.add(v.slot, ns.sched('cell'), v.cell) query.pat.add(v.cell, ns.sched('timeOfDay'), v.value) query.pat.add(v.cell, ns.sched('day'), v.day) var options = {} options.set_x = kb.each(subject, ns.sched('slot')) // @@@@@ option -> dtstart in future options.set_x = options.set_x.map(function (opt) { return kb.any(opt, ns.rdfs('label')) }) options.set_y = kb.each(subject, ns.sched('option')); // @@@@@ option -> dtstart in future options.set_y = options.set_y.map(function (opt) { return kb.any(opt, ns.cal('dtstart')) }) var possibleTimes = kb.each(invitation, ns.sched('option')) .map(function (opt) { return kb.any(opt, ns.cal('dtstart')) }) var displayTheMatrix = function () { var matrix = div.appendChild(UI.matrix.matrixForQuery( dom, query, v.time, v.author, v.value, options, function () {})) matrix.setAttribute('class', 'matrix') var refreshButton = dom.createElement('button') refreshButton.setAttribute('style', inputStyle) refreshButton.textContent = 'refresh' refreshButton.addEventListener('click', function (e) { refreshButton.disabled = true UI.store.fetcher.nowOrWhenFetched(subject.doc(), undefined, function (ok, body) { if (!ok) { console.log('Cant refresh matrix' + body) } else { matrix.refresh() refreshButton.disabled = false } }) }, false) clearElement(naviCenter) naviCenter.appendChild(refreshButton) } var dataPointForNT = [] var doc = resultsDoc options.set_y = options.set_y.filter(function (z) { return (! z.sameTerm(me)) }) options.set_y.push(me) // Put me on the end options.cellFunction = function (cell, x, y, value) { // var point = cellLookup[x.toNT() + y.toNT()] if (y.sameTerm(me)) { var callbackFunction = function () { refreshCellColor(cell, value); }; // @@ may need that var selectOptions = {} var predicate = ns.sched('timeOfDay') var cellSubject = dataPointForNT[x.toNT()] var selector = UI.widgets.makeSelectForOptions(dom, kb, cellSubject, predicate, possibleAvailabilities, selectOptions, resultsDoc, callbackFunction) cell.appendChild(selector) } else if (value !== null) { cell.textContent = UI.utils.label(value) } } var responses = kb.each(invitation, ns.sched('response')) var myResponse = null responses.map(function (r) { if (kb.holds(r, ns.dc('author'), me)) { myResponse = r } }) var id = UI.widgets.newThing(doc).uri if (myResponse === null) { myResponse = $rdf.sym(id + '_response') insertables.push($rdf.st(invitation, ns.sched('response'), myResponse, doc)) insertables.push($rdf.st(myResponse, ns.dc('author'), me, doc)) } else { var dps = kb.each(myResponse, ns.sched('cell')) dps.map(function (dataPoint) { var time = kb.any(dataPoint, ns.cal('dtstart')) dataPointForNT[time.toNT()] = dataPoint }) } for (let j = 0; j < possibleTimes.length; j++) { if (dataPointForNT[possibleTimes[j].toNT()]) continue var dataPoint = $rdf.sym(id + '_' + j) insertables.push($rdf.st(myResponse, ns.sched('cell'), dataPoint, doc)) insertables.push($rdf.st(dataPoint, ns.cal('dtstart'), possibleTimes[j], doc)) // @@ dataPointForNT[possibleTimes[j].toNT()] = dataPoint } if (insertables.length) { UI.store.updater.update([], insertables, function (uri, success, errorBody) { if (!success) { complainIfBad(success, errorBody) } else { displayTheMatrix() } }) } else { // no insertables displayTheMatrix() } } */ // end setTimesOfDay // Read or create empty results file var getResults = function getResults() { fetcher.nowOrWhenFetched(resultsDoc.uri, function (ok, body, response) { if (!ok) { if (response.status === 404) { // / Check explicitly for 404 error console.log('Initializing details file ' + resultsDoc); updater.put(resultsDoc, [], 'text/turtle', function (uri2, ok, message) { if (ok) { clearElement(naviMain); showResults(); } else { complainIfBad(ok, 'FAILED to create results file at: ' + resultsDoc.uri + ' : ' + message); console.log('FAILED to craete results file at: ' + resultsDoc.uri + ' : ' + message); } }); } else { // Other error, not 404 -- do not try to overwite the file complainIfBad(ok, 'FAILED to read results file: ' + body); } } else { // Happy read clearElement(naviMain); showResults(); } }); }; var showResults = function showResults() { // Now the form for responsing to the poll // // div.appendChild(dom.createElement('hr')) // var invitation = subject var title = kb.any(invitation, ns.cal('summary')); var comment = kb.any(invitation, ns.cal('comment')); var location = kb.any(invitation, ns.cal('location')); var div = naviMain; if (title) div.appendChild(dom.createElement('h3')).textContent = title; if (location) { div.appendChild(dom.createElement('address')).textContent = location.value; } if (comment) { div.appendChild(dom.createElement('p')).textContent = comment.value; } var author = kb.any(invitation, ns.dc('author')); if (author) { var authorName = kb.any(author, ns.foaf('name')); if (authorName) { div.appendChild(dom.createElement('p')).textContent = authorName; } } var query = new $rdf.Query('Responses'); var v = {}; var vs = ['time', 'author', 'value', 'resp', 'cell']; vs.map(function (x) { query.vars.push(v[x] = $rdf.variable(x)); }); query.pat.add(invitation, ns.sched('response'), v.resp); query.pat.add(v.resp, ns.dc('author'), v.author); query.pat.add(v.resp, ns.sched('cell'), v.cell); query.pat.add(v.cell, ns.sched('availabilty'), v.value); query.pat.add(v.cell, ns.cal('dtstart'), v.time); // Sort by by person @@@ var options = {}; options.set_x = kb.each(subject, ns.sched('option')); // @@@@@ option -> dtstart in future options.set_x = options.set_x.map(function (opt) { return kb.any(opt, ns.cal('dtstart')); }); options.set_y = kb.each(subject, ns.sched('response')); options.set_y = options.set_y.map(function (resp) { return kb.any(resp, ns.dc('author')); }); var possibleTimes = kb.each(invitation, ns.sched('option')).map(function (opt) { return kb.any(opt, ns.cal('dtstart')); }); var displayTheMatrix = function displayTheMatrix() { var matrix = div.appendChild(UI.matrix.matrixForQuery(dom, query, v.time, v.author, v.value, options, function () {})); matrix.setAttribute('class', 'matrix'); var refreshButton = dom.createElement('button'); refreshButton.setAttribute('style', inputStyle); // refreshButton.textContent = 'refresh' // noun_479395.svg var refreshIcon = dom.createElement('img'); refreshIcon.setAttribute('src', UI.icons.iconBase + 'noun_479395.svg'); refreshIcon.setAttribute('style', buttonIconStyle); refreshButton.appendChild(refreshIcon); refreshButton.addEventListener('click', function (_e) { refreshButton.disabled = true; kb.fetcher.refresh(resultsDoc, function (ok, body) { if (!ok) { console.log('Cant refresh matrix' + body); } else { matrix.refresh(); refreshButton.disabled = false; } }); }, false); clearElement(naviCenter); naviCenter.appendChild(refreshButton); }; // @@ Give other combos too-- see schedule ontology // var possibleAvailabilities = [ SCHED('No'), SCHED('Maybe'), SCHED('Yes') ] // var me = UI.authn.currentUser() var dataPointForNT = []; var loginContext = { div: naviCenter, dom: dom }; UI.authn.logIn(loginContext).then(function (context) { var me = context.me; var doc = resultsDoc; options.set_y = options.set_y.filter(function (z) { return !z.sameTerm(me); }); options.set_y.push(me); // Put me on the end options.cellFunction = function (cell, x, y, value) { if (value !== null) { kb.fetcher.nowOrWhenFetched(value.uri.split('#')[0], undefined, function (ok, _error) { if (ok) refreshCellColor(cell, value); }); } if (y.sameTerm(me)) { var callbackFunction = function callbackFunction() { refreshCellColor(cell, value); }; // @@ may need that var selectOptions = {}; var predicate = ns.sched('availabilty'); var cellSubject = dataPointForNT[x.toNT()]; var selector = UI.widgets.makeSelectForOptions(dom, kb, cellSubject, predicate, possibleAvailabilities, selectOptions, resultsDoc, callbackFunction); cell.appendChild(selector); } else if (value !== null) { cell.textContent = UI.utils.label(value); } }; var responses = kb.each(invitation, ns.sched('response')); var myResponse = null; responses.map(function (r) { if (kb.holds(r, ns.dc('author'), me)) { myResponse = r; } }); var insertables = []; // list of statements to be stored var id = UI.widgets.newThing(doc).uri; if (myResponse === null) { myResponse = $rdf.sym(id + '_response'); insertables.push($rdf.st(invitation, ns.sched('response'), myResponse, doc)); insertables.push($rdf.st(myResponse, ns.dc('author'), me, doc)); } else { var dps = kb.each(myResponse, ns.sched('cell')); dps.map(function (dataPoint) { var time = kb.any(dataPoint, ns.cal('dtstart')); dataPointForNT[time.toNT()] = dataPoint; }); } for (var j = 0; j < possibleTimes.length; j++) { if (dataPointForNT[possibleTimes[j].toNT()]) continue; var dataPoint = $rdf.sym(id + '_' + j); insertables.push($rdf.st(myResponse, ns.sched('cell'), dataPoint, doc)); insertables.push($rdf.st(dataPoint, ns.cal('dtstart'), possibleTimes[j], doc)); // @@ dataPointForNT[possibleTimes[j].toNT()] = dataPoint; } if (insertables.length) { kb.updater.update([], insertables, function (uri, success, errorBody) { if (!success) { complainIfBad(success, errorBody); } else { displayTheMatrix(); } }); } else { // no insertables displayTheMatrix(); } }); // If I made this in the first place, allow me to edit it. // @@ optionally -- allows others to if according to original var instanceCreator = kb.any(subject, ns.foaf('maker')); // owner? if (!instanceCreator || instanceCreator.sameTerm(me)) { var editButton = dom.createElement('button'); editButton.setAttribute('style', inputStyle); // editButton.textContent = '(Modify the poll)' // noun_344563.svg var editIcon = dom.createElement('img'); editIcon.setAttribute('src', UI.icons.iconBase + 'noun_344563.svg'); editIcon.setAttribute('style', buttonIconStyle); editButton.appendChild(editIcon); editButton.addEventListener('click', function (_e) { clearElement(div); showForms(); }, false); clearElement(naviLeft); naviLeft.appendChild(editButton); } // div.appendChild(editButton) clearElement(naviRight); naviRight.appendChild(newInstanceButton()); }; // showResults var div = dom.createElement('div'); var structure = div.appendChild(dom.createElement('table')); // @@ make responsive style structure.setAttribute('style', 'background-color: white; min-width: 40em; min-height: 13em;'); var naviLoginoutTR = structure.appendChild(dom.createElement('tr')); naviLoginoutTR.appendChild(dom.createElement('td')); naviLoginoutTR.appendChild(dom.createElement('td')); naviLoginoutTR.appendChild(dom.createElement('td')); var logInOutButton = null; /* var logInOutButton = UI.authn.loginStatusBox(dom, setUser) // floating divs lead to a mess // logInOutButton.setAttribute('style', 'float: right') // float the beginning of the end naviLoginout3.appendChild(logInOutButton) logInOutButton.setAttribute('style', 'margin-right: 0em;') */ var naviTop = structure.appendChild(dom.createElement('tr')); var naviMain = naviTop.appendChild(dom.createElement('td')); naviMain.setAttribute('colspan', '3'); var naviMenu = structure.appendChild(dom.createElement('tr')); naviMenu.setAttribute('class', 'naviMenu'); naviMenu.setAttribute('style', ' text-align: middle; vertical-align: middle; padding-top: 4em; '); // naviMenu.setAttribute('style', 'margin-top: 3em;') var naviLeft = naviMenu.appendChild(dom.createElement('td')); var naviCenter = naviMenu.appendChild(dom.createElement('td')); var naviRight = naviMenu.appendChild(dom.createElement('td')); getForms(); return div; } // render }; // property list // ends //# sourceMappingURL=schedulePane.js.map