UNPKG

solid-panes

Version:

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

1,031 lines (1,020 loc) • 46.2 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.default = void 0; var _solidLogic = require("solid-logic"); var UI = _interopRequireWildcard(require("solid-ui")); var $rdf = _interopRequireWildcard(require("rdflib")); function _interopRequireWildcard(e, t) { if ("function" == typeof WeakMap) var r = new WeakMap(), n = new WeakMap(); return (_interopRequireWildcard = function (e, t) { if (!t && e && e.__esModule) return e; var o, i, f = { __proto__: null, default: e }; if (null === e || "object" != typeof e && "function" != typeof e) return f; if (o = t ? n : r) { if (o.has(e)) return o.get(e); o.set(e, f); } for (const t in e) "default" !== t && {}.hasOwnProperty.call(e, t) && ((i = (o = Object.defineProperty) && Object.getOwnPropertyDescriptor(e, t)) && (i.get || i.set) ? o(f, t, i) : f[t] = e[t]); return f; })(e, t); } /* Microblog pane Charles McKenzie <charles2@mit.edu> */ /* global alert */ var _default = exports.default = { icon: UI.icons.originalIconBase + 'microblog/microblog.png', name: 'microblogPane', label: function (subject) { if (_solidLogic.store.whether(subject, UI.ns.rdf('type'), UI.ns.foaf('Person'))) { return 'Microblog'; } else { return null; } }, render: function (s, doc) { //* ********************************************** // NAMESPACES SECTION //* ********************************************** const SIOC = $rdf.Namespace('http://rdfs.org/sioc/ns#'); const SIOCt = $rdf.Namespace('http://rdfs.org/sioc/types#'); const FOAF = $rdf.Namespace('http://xmlns.com/foaf/0.1/'); const terms = $rdf.Namespace('http://purl.org/dc/terms/'); const RDF = UI.ns.rdf; const kb = _solidLogic.store; const charCount = 140; const sf = _solidLogic.store.fetcher; //* ********************************************** // BACK END //* ********************************************** const sparqlUpdater = kb.updater; // ---------------------------------------------- // FOLLOW LIST // store the URIs of followed users for // dereferencing the @replies // ---------------------------------------------- const FollowList = function (user) { this.userlist = {}; this.uris = {}; const myFollows = kb.each(kb.sym(user), SIOC('follows')); for (const mf in myFollows) { this.add(kb.any(myFollows[mf], SIOC('id')), myFollows[mf].uri); } }; FollowList.prototype.add = function (user, uri) { // add a user to the follows store if (this.userlist[user]) { if (!(uri in this.uris)) { this.userlist[user].push(uri); this.uris[uri] = ''; } } else { this.userlist[user] = [uri]; } }; FollowList.prototype.selectUser = function (user) { // check if a user is in the follows list. if (this.userlist[user]) { return [this.userlist[user].length === 1, this.userlist[user]]; } else { // user does not follow any users with this nick return [false, []]; } }; // ---------------------------------------------- // FAVORITES // controls the list of favorites. // constructor expects a user as uri. // ---------------------------------------------- const Favorites = function (user) { this.favorites = {}; this.favoritesURI = ''; if (!user) { // TODO is this even useful? return; } this.user = user.split('#')[0]; const created = kb.each(kb.sym(user), SIOC('creator_of')); for (const c in created) { if (kb.whether(created[c], RDF('type'), SIOCt('FavouriteThings'))) { this.favoritesURI = created[c]; const favs = kb.each(created[c], SIOC('container_of')); for (const f in favs) { this.favorites[favs[f]] = ''; } break; } } }; Favorites.prototype.favorited = function (post) { /* Favorited- returns true if the post is a favorite false otherwise */ return kb.sym(post) in this.favorites; }; Favorites.prototype.add = function (post, callback) { const batch = new $rdf.Statement(this.favoritesURI, SIOC('container_of'), kb.sym(post), kb.sym(this.user)); sparqlUpdater.insert_statement(batch, function (a, success, c) { if (success) { kb.add(batch.subject, batch.predicate, batch.object, batch.why); } callback(a, success, c); }); }; Favorites.prototype.remove = function (post, callback) { const batch = new $rdf.Statement(this.favoritesURI, SIOC('container_of'), kb.sym(post), kb.sym(this.user)); sparqlUpdater.delete_statement(batch, function (a, success, c) { if (success) { kb.add(batch.subject, batch.predicate, batch.object, batch.why); } callback(a, success, c); }); }; // ---------------------------------------------- // MICROBLOG // store the uri's of followed users for // dereferencing the @replies. // ---------------------------------------------- const Microblog = function (kb) { this.kb = kb; // attempt to fetch user account from local preferences if just // in case the user's foaf was not writable. add it to the store // this will probably need to change. const theUser = _solidLogic.authn.currentUser(); if (theUser) { let theAccount = UI.preferences.get('acct'); if (theAccount) { theAccount = kb.sym(theAccount); } if (theUser && theAccount) { kb.add(theUser, FOAF('holdsAccount'), theAccount, theUser.uri.split('#')[0]); } } }; Microblog.prototype.getUser = function (uri) { const User = {}; User.name = kb.any(uri, SIOC('name')) ? kb.any(uri, SIOC('name')) : ''; User.avatar = kb.any(uri, SIOC('avatar')) ? kb.any(uri, SIOC('avatar')) : ''; User.id = kb.any(uri, SIOC('id')); User.sym = uri; return User; }; Microblog.prototype.getPost = function (uri) { const Post = {}; // date ---------- let postLink = new Date(kb.anyValue(uri, terms('created'))); let h = postLink.getHours(); const a = h > 12 ? ' PM' : ' AM'; h = h > 12 ? h - 12 : h; let m = postLink.getMinutes(); m = m < 10 ? '0' + m : m; const mo = ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec']; const da = ['Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat']; const ds = da[postLink.getDay()] + ' ' + postLink.getDate() + ' ' + mo[postLink.getMonth()] + ' ' + postLink.getFullYear(); postLink = h + ':' + m + a + ' on ' + ds; Post.date = postLink; // --------- Post.mentions = ''; Post.message = String(kb.any(uri, SIOC('content'))); Post.creator = kb.any(uri, SIOC('has_creator')); Post.uri = ''; return Post; }; Microblog.prototype.gen_random_uri = function (base) { // generate random uri const uriNonce = base + '#n' + Math.floor(Math.random() * 10e9); return kb.sym(uriNonce); }; Microblog.prototype.statusUpdate = function (statusMsg, callback, replyTo, meta) { const myUserURI = this.getMyURI(); const myUser = kb.sym(myUserURI.split('#')[0]); const newPost = this.gen_random_uri(myUser.uri); const microlist = kb.each(kb.sym(myUserURI), SIOC('creator_of')); let micro; for (const microlistelement in microlist) { if (kb.whether(microlist[microlistelement], RDF('type'), SIOCt('Microblog')) && !kb.whether(microlist[microlistelement], SIOC('topic'), kb.sym(this.getMyURI()))) { micro = microlist[microlistelement]; break; } } // generate new post const batch = [new $rdf.Statement(newPost, RDF('type'), SIOCt('MicroblogPost'), myUser), new $rdf.Statement(newPost, SIOC('has_creator'), kb.sym(myUserURI), myUser), new $rdf.Statement(newPost, SIOC('content'), statusMsg, myUser), new $rdf.Statement(newPost, terms('created'), new Date(), myUser), new $rdf.Statement(micro, SIOC('container_of'), newPost, myUser)]; // message replies if (replyTo) { batch.push(new $rdf.Statement(newPost, SIOC('reply_of'), kb.sym(replyTo), myUser)); } // @replies, #hashtags, !groupReplies for (const r in meta.recipients) { batch.push(new $rdf.Statement(newPost, SIOC('topic'), kb.sym(meta.recipients[r]), myUser)); batch.push(new $rdf.Statement(kb.any(), SIOC('container_of'), newPost, myUser)); const mblogs = kb.each(kb.sym(meta.recipients[r]), SIOC('creator_of')); for (const mbl in mblogs) { if (kb.whether(mblogs[mbl], SIOC('topic'), kb.sym(meta.recipients[r]))) { const replyBatch = new $rdf.Statement(mblogs[mbl], SIOC('container_of'), newPost, kb.sym(meta.recipients[r].split('#')[0])); sparqlUpdater.insert_statement(replyBatch); } } } sparqlUpdater.insert_statement(batch, function (a, b, c) { callback(a, b, c, batch); }); }; Microblog.prototype.getMyURI = function () { const me = _solidLogic.authn.currentUser(); console.log(me); const myMicroblog = kb.any(kb.sym(me), FOAF('holdsAccount')); console.log('\n\n' + myMicroblog); return myMicroblog ? myMicroblog.uri : false; }; Microblog.prototype.generateNewMB = function (id, name, avatar, loc) { const host = loc + '/' + id; const rememberMicroblog = function () { UI.preferences.set('acct', host + '#' + id); }; const cbgenUserMB = function (a, success, c, d) { if (success) { alert('Microblog generated at ' + host + '#' + id + 'please add <b>' + host + '</b> to your foaf.'); // mbCancelNewMB() @@TBD // assume the foaf is not writable and store the microblog to the // preferences for later retrieval. // this will probably need to change. rememberMicroblog(); for (const triple in d) { kb.add(d[triple].subject, d[triple].predicate, d[triple].object, d[triple].why); } } }; const genUserMB = [ // user new $rdf.Statement(kb.sym(host + '#' + id), RDF('type'), SIOC('User'), kb.sym(host)), new $rdf.Statement(kb.sym(host + '#' + id), SIOC('creator_of'), kb.sym(host + '#mb'), kb.sym(host)), new $rdf.Statement(kb.sym(host + '#' + id), SIOC('creator_of'), kb.sym(host + '#mbn'), kb.sym(host)), new $rdf.Statement(kb.sym(host + '#' + id), SIOC('creator_of'), kb.sym(host + '#fav'), kb.sym(host)), new $rdf.Statement(kb.sym(host + '#' + id), SIOC('name'), name, kb.sym(host)), new $rdf.Statement(kb.sym(host + '#' + id), SIOC('id'), id, kb.sym(host)), new $rdf.Statement(kb.sym(host + '#' + id), RDF('label'), id, kb.sym(host)), new $rdf.Statement(s, FOAF('holdsAccount'), kb.sym(host + '#' + id), kb.sym(host)), // microblog new $rdf.Statement(kb.sym(host + '#mb'), RDF('type'), SIOCt('Microblog'), kb.sym(host)), new $rdf.Statement(kb.sym(host + '#mb'), SIOC('has_creator'), kb.sym(host + '#' + id), kb.sym(host)), // notification microblog new $rdf.Statement(kb.sym(host + '#mbn'), RDF('type'), SIOCt('Microblog'), kb.sym(host)), new $rdf.Statement(kb.sym(host + '#mbn'), SIOC('topic'), kb.sym(host + '#' + id), kb.sym(host)), new $rdf.Statement(kb.sym(host + '#mbn'), SIOC('has_creator'), kb.sym(host + '#' + id), kb.sym(host)), // favorites container new $rdf.Statement(kb.sym(host + '#fav'), RDF('type'), SIOCt('FavouriteThings'), kb.sym(host)), new $rdf.Statement(kb.sym(host + '#fav'), SIOC('has_creator'), kb.sym(host + '#' + id), kb.sym(host))]; if (avatar) { // avatar optional genUserMB.push(new $rdf.Statement(kb.sym(host + '#' + id), SIOC('avatar'), kb.sym(avatar), kb.sym(host))); } sparqlUpdater.insert_statement(genUserMB, cbgenUserMB); }; const mb = new Microblog(kb); const myFavorites = new Favorites(mb.getMyURI()); const myFollowList = new FollowList(mb.getMyURI()); //* ********************************************** // FRONT END FUNCTIONALITY //* ********************************************** // ---------------------------------------------- // PANE // User Interface for the Microblog Pane // ---------------------------------------------- const Pane = function (s, doc, microblogPane) { const TabManager = function (doc) { this.tablist = {}; this.doc = doc; this.tabView = doc.createElement('ul'); this.tabView.className = 'tabslist'; }; TabManager.prototype.create = function (id, caption, view, isDefault) { const tab = this.doc.createElement('li'); tab.innerHTML = caption; if (isDefault) { tab.className = 'active'; } tab.id = id; const change = this.change; const tablist = this.tablist; tab.addEventListener('click', function (evt) { change(evt.target.id, tablist, doc); }, false); this.tablist[id] = { view: view.id, tab }; this.tabView.appendChild(tab); }; TabManager.prototype.getTabView = function () { return this.tabView; }; TabManager.prototype.change = function (id, tablist, doc) { for (const tab in tablist) { if (tab === id) { tablist[id].tab.className = 'active'; doc.getElementById(tablist[id].view).className += ' active'; } else { const view = doc.getElementById(tablist[tab].view); view.className = view.className.replace(/\w*active\w*/, ''); tablist[tab].tab.className = tablist[id].tab.className.replace(/\w*active\w*/, ''); } } }; this.microblogPane = microblogPane; const accounts = kb.each(s, FOAF('holdsAccount')); let account; for (const a in accounts) { if (kb.whether(accounts[a], RDF('type'), SIOC('User')) && kb.whether(kb.any(accounts[a], SIOC('creator_of')), RDF('type'), SIOCt('Microblog'))) { account = accounts[a]; break; } } this.Ifollow = kb.whether(kb.sym(mb.getMyURI()), SIOC('follows'), account); const resourceType = kb.any(s, RDF('type')); if (resourceType.uri === SIOCt('Microblog').uri || resourceType.uri === SIOCt('MicroblogPost').uri) { this.thisIsMe = kb.any(s, SIOC('has_creator')).uri === mb.getMyURI(); } else if (resourceType.uri === SIOC('User').uri) { this.thisIsMe = s.uri === mb.getMyURI(); } else if (resourceType.uri === FOAF('Person').uri) { const me = _solidLogic.authn.currentUser(); const meUri = me && me.uri; this.thisIsMe = s.uri === meUri; } else { this.thisIsMe = false; } this.Tab = new TabManager(doc); }; Pane.prototype.notify = function (messageString) { const xmsg = doc.createElement('li'); xmsg.className = 'notify'; xmsg.innerHTML = messageString; doc.getElementById('notify-container').appendChild(xmsg); setTimeout(function () { doc.getElementById('notify-container').removeChild(xmsg); // delete xmsg; }, 4000); }; Pane.prototype.header = function (s, doc) { const that = this; function lsFollowUser() { const myUser = kb.sym(mb.getMyURI()); // var Ifollow = that.Ifollow const username = that.creator.name; const mbconfirmFollow = function (uri, success, _msg) { if (success === true) { if (!that.Ifollow) { // prevent duplicate entries from being added to kb (because that was happening) if (!kb.whether(followMe.subject, followMe.predicate, followMe.object, followMe.why)) { kb.add(followMe.subject, followMe.predicate, followMe.object, followMe.why); } } else { kb.removeMany(followMe.subject, followMe.predicate, followMe.object, followMe.why); } console.log(that.Ifollow); that.Ifollow = !that.Ifollow; xfollowButton.disabled = false; console.log(that.Ifollow); const followButtonLabel = that.Ifollow ? 'Unfollow ' : 'Follow '; const doFollow = that.Ifollow ? 'now follow ' : 'no longer follow '; xfollowButton.value = followButtonLabel + username; that.notify('You ' + doFollow + username + '.'); } }; const followMe = new $rdf.Statement(myUser, SIOC('follows'), that.creator.sym, myUser); xfollowButton.disabled = true; xfollowButton.value = 'Updating...'; if (!that.Ifollow) { sparqlUpdater.insert_statement(followMe, mbconfirmFollow); } else { sparqlUpdater.delete_statement(followMe, mbconfirmFollow); } } const notify = function (messageString) { const xmsg = doc.createElement('li'); xmsg.className = 'notify'; xmsg.innerHTML = messageString; doc.getElementById('notify-container').appendChild(xmsg); setTimeout(function () { doc.getElementById('notify-container').removeChild(xmsg); // delete xmsg; }, 4000); }; const mbCancelNewMB = function (_evt) { xupdateContainer.removeChild(xupdateContainer.childNodes[xupdateContainer.childNodes.length - 1]); xcreateNewMB.disabled = false; }; const lsCreateNewMB = function (_evt) { // disable the create new microblog button. // then prefills the information. xcreateNewMB.disabled = true; const xcmb = doc.createElement('div'); const xcmbName = doc.createElement('input'); if (kb.whether(s, FOAF('name'))) { // handle use of FOAF:NAME xcmbName.value = kb.any(s, FOAF('name')); } else { // handle use of family and given name xcmbName.value = kb.any(s, FOAF('givenname')) ? kb.any(s, FOAF('givenname')) + ' ' : ''; xcmbName.value += kb.any(s, FOAF('family_name')) ? kb.any(s, FOAF('givenname')) : ''; xcmbName.value = kb.any(s, FOAF('givenname')) + ' ' + kb.any(s, FOAF('family_name')); } const xcmbId = doc.createElement('input'); xcmbId.value = kb.any(s, FOAF('nick')) ? kb.any(s, FOAF('nick')) : ''; const xcmbAvatar = doc.createElement('input'); if (kb.whether(s, FOAF('img'))) { // handle use of img xcmbAvatar.value = kb.any(s, FOAF('img')).uri; } else { // otherwise try depiction xcmbAvatar.value = kb.any(s, FOAF('depiction')) ? kb.any(s, FOAF('depiction')).uri : ''; } let workspace; // = kb.any(s,WORKSPACE) //TODO - ADD URI FOR WORKSPACE DEFINITION const xcmbWritable = doc.createElement('input'); xcmbWritable.value = workspace || 'http://dig.csail.mit.edu/2007/wiki/sandbox'; // @@@ xcmb.innerHTML = ` <form class ="createNewMB" id="createNewMB"> <p id="xcmbname"><span class="">Name: </span></p> <p id="xcmbid">Id: </p> <p id="xcmbavatar">Avatar: </p> <p id="xcmbwritable">Host my microblog at: </p> <input type="button" id="mbCancel" value="Cancel" /> <input type="submit" id="mbCreate" value="Create!" /> </form> `; xupdateContainer.appendChild(xcmb); doc.getElementById('xcmbname').appendChild(xcmbName); doc.getElementById('xcmbid').appendChild(xcmbId); doc.getElementById('xcmbavatar').appendChild(xcmbAvatar); doc.getElementById('xcmbwritable').appendChild(xcmbWritable); doc.getElementById('mbCancel').addEventListener('click', mbCancelNewMB, false); doc.getElementById('createNewMB').addEventListener('submit', function () { mb.generateNewMB(xcmbId.value, xcmbName.value, xcmbAvatar.value, xcmbWritable.value); }, false); xcmbName.focus(); }; const mbSubmitPost = function () { const meta = { recipients: [] }; // user has selected a microblog to post to if (mb.getMyURI()) { // let myUser = kb.sym(mb.getMyURI()) // submission callback const cbconfirmSubmit = function (uri, success, responseText, d) { if (success === true) { for (const triple in d) { kb.add(d[triple].subject, d[triple].predicate, d[triple].object, d[triple].why); } xupdateSubmit.disabled = false; xupdateStatus.value = ''; mbLetterCount(); notify('Microblog Updated.'); if (that.thisIsMe) { doc.getElementById('postNotificationList').insertBefore(that.generatePost(d[0].subject), doc.getElementById('postNotificationList').childNodes[0]); } } else { notify('There was a problem submitting your post.'); } }; const words = xupdateStatus.value.split(' '); const mbUpdateWithReplies = function () { xupdateSubmit.disabled = true; xupdateSubmit.value = 'Updating...'; mb.statusUpdate(xupdateStatus.value, cbconfirmSubmit, xinReplyToContainer.value, meta); }; for (const word in words) { if (words[word].match(/@\w+/)) { const atUser = words[word].replace(/\W/g, ''); const recipient = myFollowList.selectUser(atUser); if (recipient[0] === true) { meta.recipients.push(recipient[1][0]); } else if (recipient[1].length > 1) { // if multiple users allow the user to choose const xrecipients = doc.createElement('select'); const xrecipientsSubmit = doc.createElement('input'); xrecipientsSubmit.type = 'button'; xrecipientsSubmit.value = 'Continue'; xrecipientsSubmit.addEventListener('click', function () { meta.recipients.push(recipient[1][xrecipients.value]); mbUpdateWithReplies(); xrecipients.parentNode.removeChild(xrecipientsSubmit); xrecipients.parentNode.removeChild(xrecipients); }, false); const recipChoice = function (recip, c) { const name = kb.any(kb.sym(recip), SIOC('name')); const choice = doc.createElement('option'); choice.value = c; choice.innerHTML = name; return choice; }; for (const r in recipient[1]) { xrecipients.appendChild(recipChoice(recipient[1][r], r)); } xupdateContainer.appendChild(xrecipients); xupdateContainer.appendChild(xrecipientsSubmit); return; } else { // no users known or self reference. if (String(kb.any(kb.sym(mb.getMyURI()), SIOC('id'))).toLowerCase() === atUser.toLowerCase()) { meta.recipients.push(mb.getMyURI()); } else { notify('You do not follow ' + atUser + '. Try following ' + atUser + ' before mentioning them.'); return; } } } /* else if(words[word].match(/\#\w+/)){ //hashtag } else if(words[word].match(/\!\w+/)){ //usergroup } */ } mbUpdateWithReplies(); } else { notify('Please set your microblog first.'); } }; const mbLetterCount = function () { xupdateStatusCounter.innerHTML = charCount - xupdateStatus.value.length; xupdateStatusCounter.style.color = charCount - xupdateStatus.value.length < 0 ? '#c33' : ''; if (xupdateStatus.value.length === 0) { xinReplyToContainer.value = ''; xupdateSubmit.value = 'Send'; } }; // reply viewer const xviewReply = doc.createElement('ul'); xviewReply.className = 'replyView'; xviewReply.addEventListener('click', function () { xviewReply.className = 'replyView'; }, false); this.xviewReply = xviewReply; const headerContainer = doc.createElement('div'); headerContainer.className = 'header-container'; // ---create status update box--- const xnotify = doc.createElement('ul'); xnotify.id = 'notify-container'; xnotify.className = 'notify-container'; this.xnotify = xnotify; const xupdateContainer = doc.createElement('form'); xupdateContainer.className = 'update-container'; xupdateContainer.innerHTML = '<h3>What are you up to?</h3>'; let xinReplyToContainer; let xupdateStatus; let xupdateStatusCounter; let xupdateSubmit; let xcreateNewMB; if (mb.getMyURI()) { xinReplyToContainer = doc.createElement('input'); xinReplyToContainer.id = 'xinReplyToContainer'; xinReplyToContainer.type = 'hidden'; xupdateStatus = doc.createElement('textarea'); xupdateStatus.id = 'xupdateStatus'; xupdateStatusCounter = doc.createElement('span'); xupdateStatusCounter.appendChild(doc.createTextNode(charCount)); xupdateStatus.cols = 30; xupdateStatus.addEventListener('keyup', mbLetterCount, false); xupdateStatus.addEventListener('focus', mbLetterCount, false); xupdateSubmit = doc.createElement('input'); xupdateSubmit.id = 'xupdateSubmit'; xupdateSubmit.type = 'submit'; xupdateSubmit.value = 'Send'; xupdateContainer.appendChild(xinReplyToContainer); xupdateContainer.appendChild(xupdateStatusCounter); xupdateContainer.appendChild(xupdateStatus); xupdateContainer.appendChild(xupdateSubmit); xupdateContainer.addEventListener('submit', mbSubmitPost, false); } else { const xnewUser = doc.createTextNode('Hi, it looks like you don\'t have a microblog, ' + ' would you like to create one? '); xcreateNewMB = doc.createElement('input'); xcreateNewMB.type = 'button'; xcreateNewMB.value = 'Create a new Microblog'; xcreateNewMB.addEventListener('click', lsCreateNewMB, false); xupdateContainer.appendChild(xnewUser); xupdateContainer.appendChild(xcreateNewMB); } headerContainer.appendChild(xupdateContainer); const subheaderContainer = doc.createElement('div'); subheaderContainer.className = 'subheader-container'; // user header // this.creator const creators = kb.each(s, FOAF('holdsAccount')); let creator; for (const c in creators) { if (kb.whether(creators[c], RDF('type'), SIOC('User')) && kb.whether(kb.any(creators[c], SIOC('creator_of')), RDF('type'), SIOCt('Microblog'))) { creator = creators[c]; // var mb = kb.sym(creator.uri.split("#")[0]); // store.fetcher.refresh(mb); break; // TODO add support for more than one microblog in same foaf } } let xfollowButton; if (creator) { this.creator = mb.getUser(creator); // ---display avatar, if available --- if (this.creator.avatar !== '') { const avatar = doc.createElement('img'); avatar.src = this.creator.avatar.uri; subheaderContainer.appendChild(avatar); } // ---generate name --- const userName = doc.createElement('h1'); userName.className = 'fn'; userName.appendChild(doc.createTextNode(this.creator.name + ' (' + this.creator.id + ')')); subheaderContainer.appendChild(userName); // ---display follow button--- if (!this.thisIsMe && mb.getMyURI()) { xfollowButton = doc.createElement('input'); xfollowButton.setAttribute('type', 'button'); const followButtonLabel = this.Ifollow ? 'Unfollow ' : 'Follow '; xfollowButton.value = followButtonLabel + this.creator.name; xfollowButton.addEventListener('click', lsFollowUser, false); subheaderContainer.appendChild(xfollowButton); } // user header end // header tabs const xtabsList = this.Tab.getTabView(); headerContainer.appendChild(subheaderContainer); headerContainer.appendChild(xtabsList); } return headerContainer; }; Pane.prototype.generatePost = function (post, _me) { /* generatePost - Creates and formats microblog posts post - symbol of the uri the post in question */ const that = this; const viewPost = function (uris) { const xviewReply = that.xviewReply; for (let i = 0; i < xviewReply.childNodes.length; i++) { xviewReply.removeChild(xviewReply.childNodes[0]); } const xcloseContainer = doc.createElement('li'); xcloseContainer.className = 'closeContainer'; const xcloseButton = doc.createElement('span'); xcloseButton.innerHTML = '&#215;'; xcloseButton.className = 'closeButton'; xcloseContainer.appendChild(xcloseButton); xviewReply.appendChild(xcloseContainer); for (const uri in uris) { xviewReply.appendChild(that.generatePost(kb.sym(uris[uri]), this.thisIsMe, 'view')); } xviewReply.className = 'replyView-active'; that.microblogPane.appendChild(xviewReply); }; // container for post const xpost = doc.createElement('li'); xpost.className = 'post'; xpost.setAttribute('id', String(post.uri).split('#')[1]); const Post = mb.getPost(post); // username text // var uname = kb.any(kb.any(post, SIOC('has_creator')), SIOC('id')) const uholdsaccount = kb.any(undefined, FOAF('holdsAccount'), kb.any(post, SIOC('has_creator'))); const xuname = doc.createElement('a'); xuname.href = uholdsaccount.uri; xuname.className = 'userLink'; const xunameText = doc.createTextNode(mb.getUser(Post.creator).id); xuname.appendChild(xunameText); // user image const xuavatar = doc.createElement('img'); xuavatar.src = mb.getUser(Post.creator).avatar.uri; xuavatar.className = 'postAvatar'; // post content const xpostContent = doc.createElement('blockquote'); let postText = Post.message; // post date const xpostLink = doc.createElement('a'); xpostLink.className = 'postLink'; xpostLink.addEventListener('click', function () { viewPost([post.uri]); }, false); xpostLink.id = 'post_' + String(post.uri).split('#')[1]; xpostLink.setAttribute('content', post.uri); xpostLink.setAttribute('property', 'permalink'); const postLink = doc.createTextNode(Post.date ? Post.date : 'post date unknown'); xpostLink.appendChild(postLink); // LINK META DATA (MENTIONS, HASHTAGS, GROUPS) const mentions = kb.each(post, SIOC('topic')); const tags = {}; for (const mention in mentions) { sf.lookUpThing(mentions[mention]); const id = kb.any(mentions[mention], SIOC('id')); tags['@' + id] = mentions[mention]; } const postTags = postText.match(/(@|#|!)\w+/g); const postFunction = function () { const p = postTags.pop(); return tags[p] ? kb.any(undefined, FOAF('holdsAccount'), tags[p]).uri : p; }; for (const t in tags) { const person = t.replace(/@/, ''); const replacePerson = RegExp('(@|!|#)(' + person + ')'); postText = postText.replace(replacePerson, '$1<a href="' + postFunction() + '">$2</a>'); } xpostContent.innerHTML = postText; // in reply to logic // This has the potential to support a post that replies to many messages. const inReplyTo = kb.each(post, SIOC('reply_of')); const xreplyTo = doc.createElement('span'); let theReply; for (const reply in inReplyTo) { theReply = String(inReplyTo[reply]).replace(/<|>/g, ''); const genReplyTo = function () { const reply = doc.createElement('a'); reply.innerHTML = ', <b>in reply to</b>'; reply.addEventListener('click', function () { viewPost([post.uri, theReply]); return false; }, false); return reply; }; xreplyTo.appendChild(genReplyTo()); } // END LINK META DATA // add the reply to and delete buttons to the interface const mbReplyTo = function () { const id = mb.getUser(Post.creator).id; const xupdateStatus = doc.getElementById('xupdateStatus'); const xinReplyToContainer = doc.getElementById('xinReplyToContainer'); const xupdateSubmit = doc.getElementById('xupdateSubmit'); xupdateStatus.value = '@' + id + ' '; xupdateStatus.focus(); xinReplyToContainer.value = post.uri; xupdateSubmit.value = 'Reply'; }; let xconfirmDeletionDialog; const mbDeletePost = function (evt) { const lsconfirmNo = function () { doc.getElementById('notify-container').removeChild(xconfirmDeletionDialog); evt.target.disabled = false; }; const lsconfirmYes = function () { reallyDelete(); doc.getElementById('notify-container').removeChild(xconfirmDeletionDialog); }; evt.target.disabled = true; xconfirmDeletionDialog = doc.createElement('li'); xconfirmDeletionDialog.className = 'notify conf'; xconfirmDeletionDialog.innerHTML += '<p>Are you sure you want to delete this post?</p>'; xconfirmDeletionDialog.addEventListener('keyup', function (evt) { if (evt.keyCode === 27) { lsconfirmNo(); } }, false); const confirmyes = doc.createElement('input'); confirmyes.type = 'button'; confirmyes.className = 'confirm'; confirmyes.value = 'Delete'; confirmyes.addEventListener('click', lsconfirmYes, false); const confirmno = doc.createElement('input'); confirmno.type = 'button'; confirmno.className = 'confirm'; confirmno.value = 'Cancel'; confirmno.addEventListener('click', lsconfirmNo, false); xconfirmDeletionDialog.appendChild(confirmno); xconfirmDeletionDialog.appendChild(confirmyes); doc.getElementById('notify-container').appendChild(xconfirmDeletionDialog); confirmno.focus(); const reallyDelete = function () { // callback after deletion const mbconfirmDeletePost = function (a, success) { if (success) { that.notify('Post deleted.'); // update the ui to reflect model changes. const deleteThisNode = evt.target.parentNode; deleteThisNode.parentNode.removeChild(deleteThisNode); kb.removeMany(deleteMe); } else { that.notify('Oops, there was a problem, please try again'); evt.target.disabled = true; } }; // delete references to post const deleteContainerOf = function (a, success) { if (success) { const deleteContainer = kb.statementsMatching(undefined, SIOC('container_of'), kb.sym(doc.getElementById('post_' + evt.target.parentNode.id).getAttribute('content'))); sparqlUpdater.batch_delete_statement(deleteContainer, mbconfirmDeletePost); } else { that.notify('Oops, there was a problem, please try again'); evt.target.disabled = false; } }; // delete attributes of post evt.target.disabled = true; const deleteMe = kb.statementsMatching(kb.sym(doc.getElementById('post_' + evt.target.parentNode.id).getAttribute('content'))); sparqlUpdater.batch_delete_statement(deleteMe, deleteContainerOf); }; }; let themaker; let xreplyButton; let xdeleteButton; if (mb.getMyURI()) { // If the microblog in question does not belong to the user, // display the delete post and reply to post buttons. themaker = kb.any(post, SIOC('has_creator')); if (mb.getMyURI() !== themaker.uri) { xreplyButton = doc.createElement('input'); xreplyButton.type = 'button'; xreplyButton.value = 'reply'; xreplyButton.className = 'reply'; xreplyButton.addEventListener('click', mbReplyTo, false); } else { xdeleteButton = doc.createElement('input'); xdeleteButton.type = 'button'; xdeleteButton.value = 'Delete'; xdeleteButton.className = 'reply'; xdeleteButton.addEventListener('click', mbDeletePost, false); } } const mbFavorite = function (evt) { const nid = evt.target.parentNode.id; const favpost = doc.getElementById('post_' + nid).getAttribute('content'); xfavorite.className += ' ing'; const cbFavorite = function (a, success, _c, _d) { if (success) { xfavorite.className = xfavorite.className.split(' ')[1] === 'ed' ? 'favorit' : 'favorit ed'; } }; if (!myFavorites.favorited(favpost)) { myFavorites.add(favpost, cbFavorite); } else { myFavorites.remove(favpost, cbFavorite); } }; const xfavorite = doc.createElement('a'); xfavorite.innerHTML = '&#9733;'; xfavorite.addEventListener('click', mbFavorite, false); if (myFavorites.favorited(post.uri)) { xfavorite.className = 'favorit ed'; } else { xfavorite.className = 'favorit'; } // build xpost.appendChild(xuavatar); xpost.appendChild(xpostContent); if (mb.getMyURI()) { xpost.appendChild(xfavorite); if (mb.getMyURI() !== themaker.uri) { xpost.appendChild(xreplyButton); } else { xpost.appendChild(xdeleteButton); } } xpost.appendChild(xuname); xpost.appendChild(xpostLink); if (inReplyTo !== '') { xpost.appendChild(xreplyTo); } return xpost; }; Pane.prototype.generatePostList = function (gmbPosts) { /* generatePostList - Generate the posts and display their results on the interface. */ const postList = doc.createElement('ul'); const postlist = {}; const datelist = []; for (const post in gmbPosts) { const postDate = kb.any(gmbPosts[post], terms('created')); if (postDate) { datelist.push(postDate); postlist[postDate] = this.generatePost(gmbPosts[post], this.thisIsMe); } } datelist.sort().reverse(); for (const d in datelist) { postList.appendChild(postlist[datelist[d]]); } return postList; }; Pane.prototype.followsView = function () { const getFollowed = function (user) { let userid = kb.any(user, SIOC('id')); const follow = doc.createElement('li'); follow.className = 'follow'; userid = userid || user.uri; let fol = kb.any(undefined, FOAF('holdsAccount'), user); fol = fol ? fol.uri : user.uri; follow.innerHTML = '<a href="' + fol + '">' + userid + '</a>'; return follow; }; const xfollows = doc.createElement('div'); xfollows.id = 'xfollows'; xfollows.className = 'followlist-container view-container'; if (this.creator && kb.whether(this.creator.sym, SIOC('follows'))) { const creatorFollows = kb.each(this.creator.sym, SIOC('follows')); const xfollowsList = doc.createElement('ul'); for (const thisPerson in creatorFollows) { xfollowsList.appendChild(getFollowed(creatorFollows[thisPerson])); } xfollows.appendChild(xfollowsList); } this.Tab.create('tab-follows', 'Follows', xfollows, false); return xfollows; }; Pane.prototype.streamView = function (s, doc) { const postContainer = doc.createElement('div'); postContainer.id = 'postContainer'; postContainer.className = 'post-container view-container active'; let mbPosts = []; if (kb.whether(s, FOAF('name')) && kb.whether(s, FOAF('holdsAccount'))) { sf.lookUpThing(kb.any(s, FOAF('holdsAccount'))); const follows = kb.each(kb.any(s, FOAF('holdsAccount')), SIOC('follows')); for (const f in follows) { sf.lookUpThing(follows[f]); // look up people user follows const smicroblogs = kb.each(follows[f], SIOC('creator_of')); // get the follows microblogs for (const smb in smicroblogs) { sf.lookUpThing(smicroblogs[smb]); if (kb.whether(smicroblogs[smb], SIOC('topic'), follows[f])) { continue; } else { mbPosts = mbPosts.concat(kb.each(smicroblogs[smb], SIOC('container_of'))); } } } } if (mbPosts.length > 0) { const postList = this.generatePostList(mbPosts); // generate stream postList.id = 'postList'; postList.className = 'postList'; postContainer.appendChild(postList); } this.Tab.create('tab-stream', 'By Follows', postContainer, true); return postContainer; }; Pane.prototype.notificationsView = function (s, doc) { const postNotificationContainer = doc.createElement('div'); postNotificationContainer.id = 'postNotificationContainer'; postNotificationContainer.className = 'notification-container view-container'; const postMentionContainer = doc.createElement('div'); postMentionContainer.id = 'postMentionContainer'; postMentionContainer.className = 'mention-container view-container'; let mbnPosts = []; let mbmPosts = []; // get mbs that I am the creator of. const theUser = kb.any(s, FOAF('holdsAccount')); const user = kb.any(theUser, SIOC('id')); const microblogs = kb.each(theUser, SIOC('creator_of')); for (const mbm in microblogs) { sf.lookUpThing(microblogs[mbm]); if (kb.whether(microblogs[mbm], SIOC('topic'), theUser)) { mbmPosts = mbmPosts.concat(kb.each(microblogs[mbm], SIOC('container_of'))); } else { if (kb.whether(microblogs[mbm], RDF('type'), SIOCt('Microblog'))) { mbnPosts = mbnPosts.concat(kb.each(microblogs[mbm], SIOC('container_of'))); } } } const postNotificationList = this.generatePostList(mbnPosts); postNotificationList.id = 'postNotificationList'; postNotificationList.className = 'postList'; postNotificationContainer.appendChild(postNotificationList); const postMentionList = this.generatePostList(mbmPosts); postMentionList.id = 'postMentionList'; postMentionList.className = 'postList'; postMentionContainer.appendChild(postMentionList); this.postMentionContainer = postMentionContainer; this.postNotificationContainer = postNotificationContainer; this.Tab.create('tab-by-user', 'By ' + user, postNotificationContainer, false); this.Tab.create('tab-at-user', '@' + user, postMentionContainer, false); }; Pane.prototype.build = function () { const microblogPane = this.microblogPane; this.headerContainer = this.header(s, doc); this.postContainer = this.streamView(s, doc); this.notificationsView(s, doc); this.xfollows = this.followsView(); microblogPane.className = 'ppane'; microblogPane.appendChild(this.xviewReply); microblogPane.appendChild(this.xnotify); microblogPane.appendChild(this.headerContainer); if (this.xfollows !== undefined) { microblogPane.appendChild(this.xfollows); } microblogPane.appendChild(this.postContainer); microblogPane.appendChild(this.postNotificationContainer); microblogPane.appendChild(this.postMentionContainer); }; const microblogpane = doc.createElement('div'); // var getusersfollows = function(uri){ // var follows = new Object(); // var followsa = {follows:0, matches:0}; // var accounts = kb.each(s, FOAF("holdsAccount")); // //get all of the accounts that a person holds // for (var acct in accounts){ // var account = accounts[acct].uri; // var act = kb.each(kb.sym(account),SIOC("follows")); // for (var a in act){ // var thisuri = act[a].uri.split("#")[0]; // if (!follows[thisuri]){followsa.follows+=1;} // follows[thisuri] =true; // } // } // // var buildPaneUI = function(uri){ // followsa.matches = (follows[uri]) ? followsa.matches+1: followsa.matches; // console.log(follows.toSource()); // if(followsa.follows == followsa.matches ){ const ppane = new Pane(s, doc, microblogpane); ppane.build(); // return false; // } // else{ // return true; // } // } // sf.addCallback('done',buildPaneUI); // sf.addCallback('fail',buildPaneUI); // //fetch each of the followers // for (var f in follows){ // sf.refresh(kb.sym(f)); // } // }(s); return microblogpane; } };