solid-panes
Version:
Solid-compatible Panes: applets and views for the mashlib and databrowser
1,218 lines (1,012 loc) • 79.1 kB
JavaScript
"use strict";
function _typeof(obj) { "@babel/helpers - typeof"; if (typeof Symbol === "function" && typeof Symbol.iterator === "symbol") { _typeof = function _typeof(obj) { return typeof obj; }; } else { _typeof = function _typeof(obj) { return obj && typeof Symbol === "function" && obj.constructor === Symbol && obj !== Symbol.prototype ? "symbol" : typeof obj; }; } return _typeof(obj); }
// Original author: kennyluck
//
// Kenny's Notes:
/* places to generate SPARQL update: clearInputAndSave() pasteFromClipboard()->insertTermTo();
undetermined statement generated formUndetStat()
->fillInRequest()
ontological issues
temporarily using the tabont namespace
clipboard: 'predicates' 'objects' 'all'(internal)
request: 'from' 'to' 'message' 'Request'
*/
var UI = require('solid-ui');
var $rdf = UI.rdf;
var panes = require('pane-registry');
var UserInputFormula; // Formula to store references of user's work
var TempFormula; // Formula to store incomplete triples (Requests),
// temporarily disjoint with kb to avoid bugs
module.exports = function UserInput(outline) {
var kb = UI.store;
var myDocument = outline.document; // is this ok?
// UI.log.warn("myDocument when it's set is "+myDocument.location);
this.menuId = 'predicateMenu1';
/* //namespace information, as a subgraph of the knowledge base, is built in showMenu
this.namespaces={};
for (var name in UI.ns) {
this.namespaces[name] = UI.ns[name]('').uri;
}
var NameSpaces=this.namespaces;
*/
// hq, print and trim functions
var qp = function qp(str) {
console.log(str + '\n');
}; // var tabont = UI.ns.tabont;
// var foaf = UI.ns.foaf
var rdf = UI.ns.rdf; // var RDFS = UI.ns.rdfs
// var OWL = UI.ns.owl
// var dc = UI.ns.dc
// var rss = UI.ns.rss
// var contact = UI.ns.contact
// var mo = UI.ns.mo
var bibo = UI.rdf.Namespace('http://purl.org/ontology/bibo/'); // hql for pubsPane
// var dcterms = UI.rdf.Namespace('http://purl.org/dc/terms/')
var dcelems = UI.rdf.Namespace('http://purl.org/dc/elements/1.1/');
var movedArrow = false; // hq
// var updateService=new updateCenter(kb);
if (!UserInputFormula) {
UserInputFormula = new UI.rdf.Formula();
UserInputFormula.superFormula = kb; // UserInputFormula.registerFormula("Your Work");
}
if (!TempFormula) TempFormula = new UI.rdf.IndexedFormula(); // Use RDFIndexedFormula so add returns the statement
TempFormula.name = 'TempFormula';
if (!UI.store.updater) UI.store.updater = new UI.rdf.UpdateManager(kb);
return {
// updateService: updateService,
sparqler: UI.store.updater,
lastModified: null,
// the last <input> being modified, .isNew indicates whether it's a new input
lastModifiedStat: null,
// the last statement being modified
statIsInverse: false,
// whether the statement is an inverse
/**
* Triggering Events: event entry points, should be called only from outline.js but not anywhere else
* in userinput.js, should be as short as possible, function names to be discussed
*/
// Called when the blue cross under the default pane is clicked.
// Add a new row to a property list ( P and O)
addNewPredicateObject: function addNewPredicateObject(e) {
if (UI.utils.getTarget(e).className !== 'bottom-border-active') return;
var This = outline.UserInput;
var target = UI.utils.getTarget(e); // UI.log.warn(ancestor(target,'TABLE').textContent);
var insertTr = myDocument.createElement('tr');
UI.utils.ancestor(target, 'DIV').insertBefore(insertTr, UI.utils.ancestor(target, 'TR'));
var tempTr = myDocument.createElement('tr');
var reqTerm1 = This.generateRequest('(TBD)', tempTr, true);
insertTr.appendChild(tempTr.firstChild);
var reqTerm2 = This.generateRequest('(Enter text or drag an object onto this field)', tempTr, false);
insertTr.appendChild(tempTr.firstChild); // there should be an elegant way of doing this
// Take the why of the last TR and write to it.
if (UI.utils.ancestor(target, 'TR').previousSibling && // there is a previous predicate/object line
UI.utils.ancestor(target, 'TR').previousSibling.AJAR_statement) {
var preStat = UI.utils.ancestor(target, 'TR').previousSibling.AJAR_statement; // This should always(?) input a non-inverse statement
This.formUndetStat(insertTr, preStat.subject, reqTerm1, reqTerm2, preStat.why, false);
} else {
// no previous row: write to the document defining the subject
var subject = UI.utils.getAbout(kb, UI.utils.ancestor(target.parentNode.parentNode, 'TD'));
var doc = kb.sym(UI.rdf.Util.uri.docpart(subject.uri));
This.formUndetStat(insertTr, subject, reqTerm1, reqTerm2, doc, false);
}
outline.walk('moveTo', insertTr.firstChild);
UI.log.info('addNewPredicateObject: selection = ' + outline.getSelection().map(function (item) {
return item.textContent;
}).join(', '));
this.startFillInText(outline.getSelection()[0]);
},
// Called when a blue cross on a predicate is clicked
// tr.AJAR_inverse stores whether the clicked predicate is an inverse one
// tr.AJAR_statement (an incomplete statement in TempFormula) stores the destination(why), now
// determined by the preceding one (is this good?)
addNewObject: function addNewObject(e) {
var predicateTd = UI.utils.getTarget(e).parentNode.parentNode; // var predicateTerm = UI.utils.getAbout(kb, predicateTd)
var isInverse = predicateTd.parentNode.AJAR_inverse; // var titleTerm=UI.utils.getAbout(kb,UI.utils.ancestor(predicateTd.parentNode,'TD'));
// set pseudo lastModifiedStat here
this.lastModifiedStat = predicateTd.parentNode.AJAR_statement;
var insertTr = this.appendToPredicate(predicateTd);
var reqTerm = this.generateRequest(' (Error) ', insertTr, false);
var preStat = insertTr.previousSibling.AJAR_statement;
if (!isInverse) {
this.formUndetStat(insertTr, preStat.subject, preStat.predicate, reqTerm, preStat.why, false);
} else {
this.formUndetStat(insertTr, reqTerm, preStat.predicate, preStat.object, preStat.why, true);
}
outline.walk('moveTo', insertTr.lastChild);
this.startFillInText(insertTr.lastChild); // this.statIsInverse=false;
},
// Called when delete is pressed
Delete: function Delete(selectedTd) {
this.deleteTriple(selectedTd, false);
},
// Called when enter is pressed
Enter: function Enter(selectedTd) {
this.literalModification(selectedTd);
},
// Called when a selected cell is clicked again
Click: function Click(e) {
var target = UI.utils.getTarget(e);
if (UI.utils.getTerm(target).termType !== 'Literal') return;
this.literalModification(target); // this prevents the generated inputbox to be clicked again
e.preventDefault();
e.stopPropagation();
},
// Called when paste is called (Ctrl+v)
pasteFromClipboard: function pasteFromClipboard(address, selectedTd) {
function termFrom(fromCode) {
var term = outline.clipboard[fromCode].shift();
if (term === null) {
UI.log.warn('no more element in clipboard!');
return;
}
switch (fromCode) {
case 'predicates':
case 'objects':
var allArray = outline.clipboard.all;
for (var i = 0; true; i++) {
if (term.sameTerm(allArray[i])) {
allArray.splice(i, 1);
break;
}
}
break;
case 'all':
throw new Error('hostorical code not understood - what is theCollection?');
/*
var isObject = term.sameTerm(theCollection('objects').elements[0])
isObject ? outline.clipboard.objecs.shift() : outline.clipboard.predicates.shift() // drop the corresponding term
return [term, isObject]
break
*/
}
return term;
}
var term;
switch (selectedTd.className) {
case 'undetermined selected':
term = selectedTd.nextSibling ? termFrom('predicates') : termFrom('objects');
if (!term) return;
break;
case 'pred selected':
// paste objects into this predicate
term = termFrom('objects');
if (!term) return;
break;
case 'selected':
// header <TD>, undetermined generated
var returnArray = termFrom('all');
if (!returnArray) return;
term = returnArray[0];
this.insertTermTo(selectedTd, term, returnArray[1]);
return;
}
this.insertTermTo(selectedTd, term);
},
/**
* Intermediate Processing:
*/
// a general entry point for any event except Click&Enter(goes to literalModification)
// do a little inference to pick the right input box
startFillInText: function startFillInText(selectedTd) {
switch (this.whatSortOfEditCell(selectedTd)) {
case 'DatatypeProperty-like':
// this.clearMenu();
// selectedTd.className='';
UI.utils.emptyNode(selectedTd);
this.lastModified = this.createInputBoxIn(selectedTd, ' (Please Input) ');
this.lastModified.isNew = false;
this.lastModified.select();
break;
case 'predicate':
// the goal is to bring back all the menus (with autocomplete functionality
// this.performAutoCompleteEdit(selectedTd,['PredicateAutoComplete',
// this.choiceQuery('SuggestPredicateByDomain')]);
this.performAutoCompleteEdit(selectedTd, 'PredicateAutoComplete');
break;
case 'ObjectProperty-like':
case 'no-idea':
// menu should be either function that
this.performAutoCompleteEdit(selectedTd, 'GeneralAutoComplete');
/*
//<code time="original">
emptyNode(selectedTd);
this.lastModified=this.createInputBoxIn(selectedTd,"");
this.lastModified.select();
this.lastModified.addEventListener('keypress',this.AutoComplete,false);
//this pops up the autocomplete menu
this.AutoComplete(1);
//</code>
*/
}
},
literalModification: function literalModification(selectedTd) {
UI.log.debug('entering literal Modification with ' + selectedTd + selectedTd.textContent); // var This=outline.UserInput;
if (selectedTd.className.indexOf(' pendingedit') !== -1) {
UI.log.warn('The node you attempted to edit has a request still pending.\n' + 'Please wait for the request to finish (the text will turn black)\n' + 'before editing this node again.');
return true;
}
var target = selectedTd;
var about = this.getStatementAbout(target); // timbl - to avoid alert from random clicks
if (!about) return;
try {
var obj = UI.utils.getTerm(target);
var trNode = UI.utils.ancestor(target, 'TR');
} catch (e) {
UI.log.warn('userinput.js: ' + e + UI.utils.getAbout(kb, selectedTd));
UI.log.error(target + ' getStatement Error:' + e);
}
try {
var tdNode = trNode.lastChild;
} catch (e) {
UI.log.error(e + '@' + target);
} // seems to be a event handling problem of firefox3
/*
if (e.type!='keypress'&&(selectedTd.className=='undetermined selected'||selectedTd.className=='undetermined')){
this.Refill(e,selectedTd);
return;
}
*/
// ignore clicking trNode.firstChild (be careful for <div> or <span>)
// if (e.type!='keypress'&&target!=tdNode && UI.utils.ancestor(target,'TD')!=tdNode) return;
if (obj.termType === 'Literal') {
tdNode.removeChild(tdNode.firstChild); // remove the text
if (obj.value.match('\n')) {
// match a line feed and require <TEXTAREA>
var textBox = myDocument.createElement('textarea');
textBox.appendChild(myDocument.createTextNode(obj.value));
textBox.setAttribute('rows', (obj.value.match(/\n/g).length + 1).toString()); // g is for global(??)
textBox.setAttribute('cols', '100'); // should be the size of <TD>
textBox.setAttribute('class', 'textinput');
tdNode.appendChild(textBox);
this.lastModified = textBox;
} else {
this.lastModified = this.createInputBoxIn(tdNode, obj.value);
}
this.lastModified.isNew = false; // Kenny: What should be expected after you click a editable text element?
// Choice 1
this.lastModified.select(); // Choice 2 - direct the key cursor to where you click (failed attempt)
// --------------------------------------------------------------------------
// duplicate the event so user can edit without clicking twice
// var e2=myDocument.createEvent("MouseEvents");
// e2.initMouseEvent("click",true,true,window,0,0,0,0,0,false,false,false,false,0,null);
// inputBox.dispatchEvent(e2);
// ---------------------------------------------------------------------------
}
return true; // this is not a valid modification
},
/**
* UIs: input event handlers, menu generation
*/
performAutoCompleteEdit: function performAutoCompleteEdit(selectedTd, menu) {
UI.utils.emptyNode(selectedTd);
qp('perform AutoCompleteEdit. THIS IS=' + this);
this.lastModified = this.createInputBoxIn(selectedTd, '');
this.lastModified.select();
this.lastModified.addEventListener('keypress', this.getAutoCompleteHandler(menu), false);
/* keypress!?
This is what I hate about UI programming.
I shall write something about this but not now.
*/
// this pops up the autocomplete menu
// Pops up the menu even though no keypress has occurred
// 1 is a dummy variable for the "enterEvent"
this.getAutoCompleteHandler(menu)(1);
},
backOut: function backOut() {
this.deleteTriple(this.lastModified.parentNode, true);
this.lastModified = null;
},
clearMenu: function clearMenu() {
var menu = myDocument.getElementById(this.menuID);
if (menu) {
menu.parentNode.removeChild(menu); // emptyNode(menu);
}
},
/* goes here when either this is a literal or escape from menu and then input text */
clearInputAndSave: function clearInputAndSave(e) {
if (!this.lastModified) return;
if (!this.lastModified.isNew) {
try {
var obj = this.getStatementAbout(this.lastModified).object;
} catch (e) {
return;
}
}
var s = this.lastModifiedStat; // when 'isNew' this is set at addNewObject()
if (this.lastModified.value !== this.lastModified.defaultValue) {
if (this.lastModified.value === '') {
// ToDo: remove this
this.lastModified.value = this.lastModified.defaultValue;
this.clearInputAndSave();
return;
} else if (this.lastModified.isNew) {
s = new UI.rdf.Statement(s.subject, s.predicate, kb.literal(this.lastModified.value), s.why); // TODO: DEFINE ERROR CALLBACK
var defaultpropview = this.views.defaults[s.predicate.uri];
var trCache = UI.utils.ancestor(this.lastModified, 'TR');
try {
UI.store.updater.update([], [s], function (uri, success, errorBody) {
if (!success) {
UI.log.error('Error occurs while inserting ' + s + '\n\n' + errorBody + '\n'); // UI.log.warn("Error occurs while inserting "+s+'\n\n'+errorBody);
outline.UserInput.deleteTriple(trCache.lastChild, true);
}
});
} catch (e) {
UI.log.error('Error inserting fact ' + s + ':\n\t' + e + '\n');
return;
}
s = kb.add(s.subject, s.predicate, kb.literal(this.lastModified.value), s.why);
} else {
if (this.statIsInverse) {
UI.log.error("Invalid Input: a literal can't be a subject in RDF/XML");
this.backOut();
return;
}
var s1, s2, s3;
switch (obj.termType) {
case 'Literal':
// generate path and nailing from current values
// TODO: DEFINE ERROR CALLBACK
var valueCache = this.lastModified.value;
trCache = UI.utils.ancestor(this.lastModified, 'TR');
var oldValue = this.lastModified.defaultValue;
s2 = $rdf.st(s.subject, s.predicate, kb.literal(this.lastModified.value), s.why);
try {
UI.store.updater.update([s], [s2], function (uri, success, errorBody) {
if (success) {
obj.value = valueCache;
} else {
UI.log.warn('Error occurs while editing ' + s + '\n\n' + errorBody);
trCache.lastChild.textContent = oldValue;
}
trCache.lastChild.className = trCache.lastChild.className.replace(/ pendingedit/g, '');
});
} catch (e) {
UI.log.warn('Error occurs while editing ' + s + ':\n\t' + e);
return;
} // obj.value=this.lastModified.value;
// UserInputFormula.statements.push(s);
break;
case 'BlankNode':
// a request refill with text
// var newStat
var textTerm = kb.literal(this.lastModified.value, ''); // <Feature about="labelChoice">
if (s.predicate.termType === 'Collection') {
// case: add triple ????????? Weird - tbl
var selectedPredicate = s.predicate.elements[0]; // @@ TBL elements is a list on the predicate??
if (kb.any(undefined, selectedPredicate, textTerm)) {
if (!e) {
// keyboard
var tdNode = this.lastModified.parentNode;
e = {};
e.pageX = UI.utils.findPos(tdNode)[0];
e.pageY = UI.utils.findPos(tdNode)[1] + tdNode.clientHeight;
}
this.showMenu(e, 'DidYouMeanDialog', undefined, {
dialogTerm: kb.any(undefined, selectedPredicate, textTerm),
bnodeTerm: s.subject
});
} else {
s1 = UI.utils.ancestor(UI.utils.ancestor(this.lastModified, 'TR').parentNode, 'TR').AJAR_statement;
s2 = $rdf.st(s.subject, selectedPredicate, textTerm, s.why);
var type = kb.the(s.subject, rdf('type'));
s3 = kb.anyStatementMatching(s.subject, rdf('type'), type, s.why); // TODO: DEFINE ERROR CALLBACK
// because the table is repainted, so...
trCache = UI.utils.ancestor(UI.utils.ancestor(this.lastModified, 'TR'), 'TD').parentNode;
try {
UI.store.updater.update([], [s1, s2, s3], function (uri, success, errorBody) {
if (!success) {
console.log('Error occurs while editing ' + s1 + '\n\n' + errorBody);
outline.UserInput.deleteTriple(trCache.lastChild, true); // @@@@ This
}
});
} catch (e) {
console.log('Error occurs while editing ' + s1 + ':\n\t' + e);
return;
}
kb.remove(s);
kb.add(s.subject, selectedPredicate, textTerm, s.why); // was: newStat =
// a subtle bug occurs here, if foaf:nick hasn't been dereferneced,
// this add will cause a repainting
}
var enclosingTd = UI.utils.ancestor(this.lastModified.parentNode.parentNode, 'TD');
var defaultPane = panes.byName('default'); // @@ check
outline.outlineExpand(enclosingTd, s.subject, {
pane: defaultPane,
already: true
});
outline.walk('right', outline.focusTd); // </Feature>
} else {
this.fillInRequest('object', this.lastModified.parentNode, kb.literal(this.lastModified.value));
return; // The new Td is already generated by fillInRequest, so it's done.
}
break;
}
}
} else if (this.lastModified.isNew) {
// generate 'Request', there is no way you can input ' (Please Input) '
var trNode = UI.utils.ancestor(this.lastModified, 'TR');
var reqTerm = this.generateRequest('(To be determined. Re-type of drag an object onto this field)');
var preStat = trNode.previousSibling.AJAR_statement; // the statement of the same predicate
this.formUndetStat(trNode, preStat.subject, preStat.predicate, reqTerm, preStat.why, false); // this why being the same as the previous statement
this.lastModified = null; // UI.log.warn("test .isNew)");
return;
} else if (s.predicate.termType === 'Collection') {
kb.removeMany(s.subject);
var upperTr = UI.utils.ancestor(UI.utils.ancestor(this.lastModified, 'TR').parentNode, 'TR');
preStat = upperTr.AJAR_statement;
reqTerm = this.generateRequest('(To be determined. Re-type of drag an object onto this field)');
this.formUndetStat(upperTr, preStat.subject, preStat.predicate, reqTerm, preStat.why, false);
outline.replaceTD(outline.outlineObjectTD(reqTerm, defaultpropview), upperTr.lastChild);
this.lastModified = null;
return;
} else if (this.statIsInverse) {
/*
if ((s.object.termType === 'BlankNode' && !this.statIsInverse) ||
s.subject.termType === 'BlankNode' && this.statIsInverse) {
this.backOut()
return
*/
if (s.subject.termType === 'BlankNode') {
this.backOut();
return;
}
} else {
if (s.object.termType === 'BlankNode') {
this.backOut();
return;
}
} // case modified - literal modification only(for now).
trNode = UI.utils.ancestor(this.lastModified, 'TR'); // var defaultpropview = this.views.defaults[s.predicate.uri]
if (!this.statIsInverse) {
// this is for an old feature
// outline.replaceTD(outline.outlineObjectTD(s.object, defaultpropview),trNode.lastChild);
outline.replaceTD(outline.outlineObjectTD(kb.literal(this.lastModified.value), defaultpropview), trNode.lastChild);
} else {
outline.replaceTD(outline.outlineObjectTD(s.subject, defaultpropview), trNode.lastChild);
}
if (this.lastModified.value !== this.lastModified.defaultValue) {
trNode.lastChild.className += ' pendingedit';
} // trNode.AJAR_statement=s;//you don't have to set AJAR_inverse because it's not changed
// This is going to be painful when predicate-edit allowed
this.lastModified = null;
},
/* deletes the triple corresponding to selectedTd, remove that Td. */
deleteTriple: function deleteTriple(selectedTd, isBackOut) {
// ToDo: complete deletion of a node
UI.log.debug('deleteTriple entered'); // allow a pending node to be deleted if it's a backout sent by SPARQL update callback
if (!isBackOut && selectedTd.className.indexOf(' pendingedit') !== -1) {
console.log('The node you attempted to edit has a request still pending.\n' + 'Please wait for the request to finish (the text will turn black)\n' + 'before editing this node again.');
outline.walk('up');
return;
}
var removedTr; // var afterTr
var s = this.getStatementAbout(selectedTd);
if (!isBackOut && !kb.whether(s.object, rdf('type'), UI.ns.link('Request')) && // Better to check whether provenance is internal?
!kb.whether(s.predicate, rdf('type'), UI.ns.link('Request')) && !kb.whether(s.subject, rdf('type'), UI.ns.link('Request'))) {
UI.log.debug('about to send SPARQLUpdate');
try {
UI.store.updater.update([s], [], function (uri, success, errorBody) {
if (success) {
removefromview();
} else {
// removedTr.AJAR_statement=kb.add(s.subject,s.predicate,s.object,s.why);
console.log('Error occurs while deleting ' + s + '\n\n' + errorBody);
selectedTd.className = selectedTd.className.replace(/ pendingedit/g, '');
}
});
selectedTd.className += ' pendingedit';
} catch (e) {
UI.log.error(e);
UI.log.warn('Error deleting statement ' + s + ':\n\t' + e);
return;
}
UI.log.debug('SPARQLUpdate sent');
} else {// removal of an undetermined statement associated with pending TRs
// TempFormula.remove(s);
}
UI.log.debug('about to remove ' + s);
UI.log.debug('removed');
outline.walk('up');
removedTr = selectedTd.parentNode; // afterTr = removedTr.nextSibling
function removefromview() {
var trIterator;
for (trIterator = removedTr; trIterator.childNodes.length === 1; trIterator = trIterator.previousSibling) {
;
}
if (trIterator === removedTr) {
var theNext = trIterator.nextSibling;
if (theNext.nextSibling && theNext.childNodes.length === 1) {
var predicateTd = trIterator.firstChild;
predicateTd.setAttribute('rowspan', parseInt(predicateTd.getAttribute('rowspan')) - 1);
theNext.insertBefore(trIterator.firstChild, theNext.firstChild);
}
removedTr.parentNode.removeChild(removedTr);
} else {
// !DisplayOptions["display:block on"].enabled){
predicateTd = trIterator.firstChild;
predicateTd.setAttribute('rowspan', parseInt(predicateTd.getAttribute('rowspan')) - 1);
removedTr.parentNode.removeChild(removedTr);
}
}
if (isBackOut) removefromview();
},
/* clipboard principle: copy wildly, paste carefully
ToDoS:
1. register Subcollection?
2. copy from more than one selectedTd: 1.sequece 2.collection
3. make a clipboard class?
*/
clipboardInit: function clipboardInit() {
outline.clipboard = {};
outline.clipboard.objects = [];
outline.clipboard.predicates = [];
outline.clipboard.all = [];
},
copyToClipboard: function copyToClipboard(address, selectedTd) {
/*
var clip = Components.classes["@mozilla.org/widget/clipboard;1"].getService(Components.interfaces.nsIClipboard);
if (!clip) return false;
var clipid = Components.interfaces.nsIClipboard;
var trans = Components.classes["@mozilla.org/widget/transferable;1"].createInstance(Components.interfaces.nsITransferable);
if (!trans) return false;
var copytext = "Tabulator!!";
var str = Components.classes["@mozilla.org/supports-string;1"].
createInstance(Components.interfaces.nsISupportsString);
if (!str) return false;
str.data = copytext;
trans.addDataFlavor("text/x-moz-url");
trans.setTransferData("text/x-mox-url", str, copytext.length * 2);
clip.setData(trans, null, clipid.kGlobalClipboard);
*/
var term = UI.utils.getTerm(selectedTd);
switch (selectedTd.className) {
case 'selected': // table header
case 'obj selected':
// var objects = outline.clipboard.objects
outline.clipboard.objects.unshift(term);
break;
case 'pred selected':
case 'pred internal selected':
outline.clipboard.predicates.unshift(term);
}
outline.clipboard.all.unshift(term);
},
insertTermTo: function insertTermTo(selectedTd, term, isObject) {
switch (selectedTd.className) {
case 'undetermined selected':
var defaultpropview = this.views.defaults[selectedTd.parentNode.AJAR_statement.predicate.uri];
this.fillInRequest(selectedTd.nextSibling ? 'predicate' : 'object', selectedTd, term);
break;
case 'pred selected':
// paste objects into this predicate
var insertTr = this.appendToPredicate(selectedTd);
var preStat = selectedTd.parentNode.AJAR_statement;
defaultpropview = this.views.defaults[preStat.predicate.uri];
insertTr.appendChild(outline.outlineObjectTD(term, defaultpropview)); // modify store and update here
var isInverse = selectedTd.parentNode.AJAR_inverse;
if (!isInverse) {
insertTr.AJAR_statement = kb.add(preStat.subject, preStat.predicate, term, preStat.why);
} else {
insertTr.AJAR_statemnet = kb.add(term, preStat.predicate, preStat.object, preStat.why);
}
try {
UI.store.updater.update([], [insertTr.AJAR_statement], function (uri, success, errorBody) {
if (!success) {
UI.log.error('userinput.js (pred selected): Fail trying to insert statement ' + insertTr.AJAR_statement + ': ' + errorBody);
}
});
} catch (e) {
UI.log.error('Exception trying to insert statement ' + insertTr.AJAR_statement + ': ' + UI.utils.stackString(e));
return;
}
insertTr.AJAR_inverse = isInverse;
UserInputFormula.statements.push(insertTr.AJAR_statement);
break;
case 'selected':
// header <TD>, undetermined generated
var paneDiv = UI.utils.ancestor(selectedTd, 'TABLE').lastChild;
var newTr = paneDiv.insertBefore(myDocument.createElement('tr'), paneDiv.lastChild); // var titleTerm=UI.utils.getAbout(kb,UI.utils.ancestor(newTr,'TD'));
preStat = newTr.previousSibling.AJAR_statement;
if (typeof isObject === 'undefined') isObject = true;
if (isObject) {
// object inserted
this.formUndetStat(newTr, preStat.subject, this.generateRequest('(TBD)', newTr, true), term, preStat.why, false); // defaultpropview temporaily not dealt with
newTr.appendChild(outline.outlineObjectTD(term));
outline.walk('moveTo', newTr.firstChild);
this.startFillInText(newTr.firstChild);
} else {
// predicate inserted
// existing predicate not expected
var reqTerm = this.generateRequest('(To be determined. Re-type of drag an object onto this field)', newTr);
this.formUndetStat(newTr, preStat.subject, term, reqTerm, preStat.why, false);
newTr.insertBefore(outline.outlinePredicateTD(term, newTr, false, false), newTr.firstChild);
outline.walk('moveTo', newTr.lastChild);
this.startFillInText(newTr.lastChild);
}
break;
}
},
Refill: function Refill(e, selectedTd) {
UI.log.info('Refill' + selectedTd.textContent);
var isPredicate = selectedTd.nextSibling;
var predicateQuery;
if (isPredicate) {
// predicateTd
if (selectedTd.nextSibling.className === 'undetermined') {
/* Make set of proprties to propose for a predicate.
The naive approach is to take those which have a class
of the subject as their domain. But in fact we must offer anything which
is not explicitly excluded, by having a domain disjointWith a
class of the subject. */
/* SELECT ?pred
WHERE{
?pred a rdf:Property.
?pred rdfs:domain subjectClass.
}
*/
/* SELECT ?pred ?class
WHERE{
?pred a rdf:Property.
subjectClass owl:subClassOf ?class.
?pred rdfs:domain ?class.
}
*/
/* SELECT ?pred
WHERE{
subject a ?subjectClass.
?pred rdfs:domain ?subjectClass.
}
*/
var subject = UI.utils.getAbout(kb, UI.utils.ancestor(selectedTd, 'TABLE').parentNode);
var subjectClass = kb.any(subject, rdf('type'));
var sparqlText = [];
var endl = '.\n';
sparqlText[0] = 'SELECT ?pred WHERE{\n?pred ' + rdf('type') + rdf('Property') + '.\n' + '?pred ' + UI.ns.rdfs('domain') + subjectClass + '.}'; // \n is required? SPARQL parser bug?
sparqlText[1] = 'SELECT ?pred ?class\nWHERE{\n' + '?pred ' + rdf('type') + rdf('Property') + '.\n' + subjectClass + UI.ns.rdfs('subClassOf') + ' ?class.\n' + '?pred ' + UI.ns.rdfs('domain') + ' ?class.\n}';
sparqlText[2] = 'SELECT ?pred WHERE{\n' + subject + rdf('type') + kb.variable('subjectClass') + endl + kb.variable('pred') + UI.ns.rdfs('domain') + kb.variable('subjectClass') + endl + '}';
predicateQuery = sparqlText.map($rdf.SPARQLToQuery);
} else {
// ------selector
/* SELECT ?pred
WHERE{
?pred a rdf:Property.
?pred rdfs:domain subjectClass.
?pred rdfs:range objectClass.
}
*/
// Candidate
/* SELECT ?pred
WHERE{
subject a ?subjectClass.
object a ?objectClass.
?pred rdfs:domain ?subjectClass.
?pred rdfs:range ?objectClass.
*/
subject = UI.utils.getAbout(kb, UI.utils.ancestor(selectedTd, 'TABLE').parentNode);
subjectClass = kb.any(subject, rdf('type'));
var object = selectedTd.parentNode.AJAR_statement.object; // var objectClass = (object.termType === 'Literal') ? UI.ns.rdfs('Literal') : kb.any(object, rdf('type'))
// var sparqlText="SELECT ?pred WHERE{\n?pred "+rdf('type')+rdf('Property')+".\n"+
// "?pred "+UI.ns.rdfs('domain')+subjectClass+".\n"+
// "?pred "+UI.ns.rdfs('range')+objectClass+".\n}"; // \n is required? SPARQL parser bug?
sparqlText = 'SELECT ?pred WHERE{' + subject + rdf('type') + '?subjectClass' + '.\n' + object + rdf('type') + '?objectClass' + '.\n' + '?pred ' + UI.ns.rdfs('domain') + '?subjectClass' + '.\n' + '?pred ' + UI.ns.rdfs('range') + '?objectClass' + '.\n}'; // \n is required? SPARQL parser bug?
predicateQuery = $rdf.SPARQLToQuery(sparqlText);
} // -------presenter
// ToDo: how to sort selected predicates?
this.showMenu(e, 'GeneralPredicateChoice', predicateQuery, {
isPredicate: isPredicate,
selectedTd: selectedTd
});
} else {
// objectTd
var predicateTerm = selectedTd.parentNode.AJAR_statement.predicate;
if (kb.whether(predicateTerm, rdf('type'), UI.ns.owl('DatatypeProperty')) || predicateTerm.termType === 'Collection' || kb.whether(predicateTerm, UI.ns.rdfs('range'), UI.ns.rdfs('Literal'))) {
selectedTd.className = '';
UI.utils.emptyNode(selectedTd);
this.lastModified = this.createInputBoxIn(selectedTd, ' (Please Input) ');
this.lastModified.isNew = false;
this.lastModified.select();
} // show menu for rdf:type
if (selectedTd.parentNode.AJAR_statement.predicate.sameTerm(rdf('type'))) {
var _sparqlText = 'SELECT ?class WHERE{?class ' + rdf('type') + UI.ns.rdfs('Class') + '.}'; // I should just use kb.each
var classQuery = $rdf.SPARQLToQuery(_sparqlText);
this.showMenu(e, 'TypeChoice', classQuery, {
isPredicate: isPredicate,
selectedTd: selectedTd
});
}
}
},
// This is where pubsPane.js comes in, with: outline.UserInput.getAutoCompleteHandler("JournalTAC")(e);
getAutoCompleteHandler: function getAutoCompleteHandler(mode) {
qp('\n\n***** In getAutoCompleteHandler ****** mode = ' + mode);
if (mode === 'PredicateAutoComplete') {
mode = 'predicate';
} else if (mode !== 'JournalTAC') {
// hq // why? -tim - not 'predicate' below
mode = 'all';
}
var InputBox;
if (mode === 'JournalTAC') {
// hq // Better to pass in InputBox as a param
InputBox = myDocument.getElementById('inpid_journal_title');
} else {
InputBox = this.lastModified || outline.getSelection()[0].firstChild;
}
qp('InputBox=' + InputBox); // hq
qp('InputBox.value=' + InputBox.value); // hq
return function (enterEvent) {
qp('ENTER EVENT=' + enterEvent); // Firefox 2.0.0.6 makes this not working? 'this' becomes [object HTMLInputElement]
// but not [wrapped ...]
// var InputBox=(typeof enterEvent=='object')?this:this.lastModified;//'this' is the <input> element
qp('1. outside (if eneterEvent)');
var e = {};
var tdNode = InputBox.parentNode;
if (!mode) mode = tdNode.nextSibling ? 'predicate' : 'all';
e.pageX = UI.utils.findPos(tdNode)[0];
e.pageY = UI.utils.findPos(tdNode)[1] + tdNode.clientHeight;
qp('epX=' + e.pageX + ', epY=' + e.pageY + ', mode=' + mode);
var menu = myDocument.getElementById(outline.UserInput.menuID);
function setHighlightItem(item) {
if (!item) return; // do not make changes
if (menu.lastHighlight) menu.lastHighlight.className = '';
menu.lastHighlight = item;
menu.lastHighlight.className = 'activeItem';
outline.showURI(UI.utils.getAbout(kb, menu.lastHighlight));
}
if (enterEvent) {
// either the real event of the pseudo number passed by OutlineKeypressPanel
qp('2. in (if enterEvent). with type = ' + _typeof(enterEvent));
var newText = InputBox.value;
if (_typeof(enterEvent) === 'object') {
qp('3. in typeof enterEvent is object, will switch to keys, arrows, etc. keycode = ' + enterEvent.keyCode);
enterEvent.stopPropagation();
if (menu && !menu.lastHighlight) {
// this ensures the following operation valid
setHighlightItem(menu.firstChild.firstChild);
}
switch (enterEvent.keyCode) {
case 13: // enter
case 9:
// tab
qp('handler: Enter or Tab');
if (!menu) {
outline.UserInput.clearInputAndSave();
return;
}
if (!menu.lastHighlight) {
if (mode === 'JournalTAC') {
outline.UserInput.clearMenu();
qp('no lastH');
return 'no lastH';
}
return;
} // warning?
if (menu.lastHighlight.tagName === 'INPUT') {
switch (menu.lastHighlight.value) {
case 'New...':
qp('subcase New');
outline.UserInput.createNew();
break;
case 'GiveURI':
qp('subcase GiveURI');
outline.UserInput.inputURI();
break;
}
} else {
// pubsPane Stuff:
if (mode === 'JournalTAC') {
qp('movedArrow? ' + movedArrow); // Enter only works if arrows have been moved
if (movedArrow && menu.lastHighlight) {
// Get the title from the DOM
// tr, th, div, innerHTML
var jtitle = menu.lastHighlight.firstChild.firstChild.innerHTML; // tr, th, td, innerHTML
var juri = menu.lastHighlight.firstChild.nextSibling.innerHTML; // clearing out the < and > from juri
juri = juri.slice(4, -4);
return ['gotdptitle', jtitle, juri];
} // If doesn't qualify to be autocomplete, return this random string, since pubsPane checks for "gotdptitle"
return 'asGivenTxt';
}
var inputTerm = UI.utils.getAbout(kb, menu.lastHighlight);
var fillInType = mode === 'predicate' ? 'predicate' : 'object';
outline.UserInput.clearMenu();
outline.UserInput.fillInRequest(fillInType, InputBox.parentNode, inputTerm); // if (outline.UserInput.fillInRequest(fillInType,InputBox.parentNode,inputTerm))
// outline.UserInput.clearMenu();
}
qp('outside');
return;
case 38:
// up
qp('handler: Arrow UP');
movedArrow = true; // hq
if (newText === '' && menu.lastHighlight.tagName === 'TR' && !menu.lastHighlight.previousSibling) {
setHighlightItem(menu.firstChild.firstChild);
} else {
setHighlightItem(menu.lastHighlight.previousSibling);
}
return "I'm a little Arrow Up";
case 40:
// down
qp('handler: Arrow Down');
movedArrow = true; // hq
if (menu.lastHighlight.tagName === 'INPUT') {
setHighlightItem(menu.childNodes[1].firstChild);
} else {
setHighlightItem(menu.lastHighlight.nextSibling);
}
return "I'm a little Down Arrow";
case 37: // left
case 39:
// right
qp('handler: Arrow left, right');
if (menu.lastHighlight.tagName === 'INPUT') {
if (enterEvent.keyCode === 37) {
setHighlightItem(menu.lastHighlight.previousSibling);
} else {
setHighlightItem(menu.lastHighlight.nextSibling);
}
}
return;
case 8:
// backspace
qp('handler: Backspace');
newText = newText.slice(0, -1);
break;
case 27:
// esc to enter literal
qp('handler: Esc');
if (!menu) {
outline.UserInput.backOut();
return;
}
outline.UserInput.clearMenu(); // Not working? I don't know.
// InputBox.removeEventListener('keypress',outline.UserInput.Autocomplete,false);
return;
// break
default:
qp('handler: Default');
movedArrow = false; // hq
// we need this because it is keypress, seeAlso performAutoCompleteEdit
qp('oldtext=' + newText);
newText += String.fromCharCode(enterEvent.charCode);
qp('charcodent=' + enterEvent.charCode);
qp('strcharcod=' + String.fromCharCode(enterEvent.charCode));
console.log('DEFAULT txtstr=' + newText + '\n');
// hq
}
} // endif typeof(event) === object
// UI.log.warn(InputBox.choices.length);
// for(i=0;InputBox.choices[i].label<newText;i++); //O(n) ToDo: O(log n)
if (mode === 'all') {
qp('generalAC after switch, newText=' + newText + 'mode is all');
outline.UserInput.clearMenu(); // outline.UserInput.showMenu(e,'GeneralAutoComplete',undefined,{'isPredicate':false,'selectedTd':tdNode,'choices':InputBox.choices, 'index':i});
outline.UserInput.showMenu(e, 'GeneralAutoComplete', undefined, {
inputText: newText,
selectedTd: tdNode
});
if (newText.length === 0) outline.UserInput.WildCardButtons();
} else if (mode === 'predicate') {
qp('predicateAC after switch, newText=' + newText + 'mode is predicate');
outline.UserInput.clearMenu();
outline.UserInput.showMenu(e, 'PredicateAutoComplete', undefined, {
inputText: newText,
isPredicate: true,
selectedTd: tdNode
});
} else if (mode === 'JournalTAC') {
// hq
qp('JouralTAC after switch, newText=' + newText);
outline.UserInput.clearMenu(); // Goto showMenu
outline.UserInput.showMenu(e, 'JournalTitleAutoComplete', undefined, {
inputText: newText
}, 'orderisuseless');
}
menu = myDocument.getElementById(outline.UserInput.menuID);
if (!menu) {
qp('No menu element. Do not show menu.');
return;
}
qp('at end of handler\n^^^^^^^^^^^^^^^^^\n\n');
setHighlightItem(menu.firstChild.firstChild);
outline.showURI(UI.utils.getAbout(kb, menu.lastHighlight));
return 'nothing to return';
}
}; // end of return function
},
// Add the buttons which allow the suer to craete a new object
// Or reference an exiting one with a URI.
//
WildCardButtons: function WildCardButtons() {
var menuDiv = myDocument.getElementById(outline.UserInput.menuID);
var div = menuDiv.insertBefore(myDocument.createElement('div'), menuDiv.firstChild);
var input1 = div.appendChild(myDocument.createElement('input'));
var input2 = div.appendChild(myDocument.createElement('input'));
input1.type = 'button';
input1.value = 'New...';
input2.type = 'button';
input2.value = 'Know its URI';
function highlightInput(e) {
// same as the one in newMenu()
var menu = myDocument.getElementById(outline.UserInput.menuID);
if (menu.lastHighlight) menu.lastHighlight.className = '';
menu.lastHighlight = UI.utils.ancestor(UI.utils.getTarget(e), 'INPUT');
if (!menu.lastHighlight) return; // mouseover <TABLE>
menu.lastHighlight.className = 'activeItem';
}
div.addEventListener('mouseover', highlightInput, false);
input1.addEventListener('click', this.createNew, false);
input2.addEventListener('click', this.inputURI, false);
},
// ToDo: shrink rows when \n+backspace
Keypress: function Keypress(e) {
if (e.keyCode === 13) {
if (outline.targetOf(e).tagName !== 'TEXTAREA') {
this.clearInputAndSave();
} else {
// <TEXTAREA>
var preRows = parseInt(this.lastModified.getAttribute('rows'));
this.lastModified.setAttribute('rows', (preRows + 1).toString());
e.stopPropagation();
}
} // Remark by Kenny: If the user wants to input more lines into an one-line-only blank.
// Direct him/her to a new blank (how?)
},
Mousedown: function Mousedown(e) {
qp('MOUSING DOWN'); // temporary key ctrl+s or q for swiching mode
// This was in HCIOptions "right click to switch mode":
window.addEventListener('keypress', function (e) {
if (e.ctrlKey && (e.charCode === 115 || e.charCode === 113)) {
UserInput.switchMode();
}
}, false);
window.addEventListener('mousedown', UserInput.Mousedown, false);
document.getElementById('outline').oncontextmenu = function () {
return false;
};
if (e.button === 2) {
// right click
UserInput.switchMode();
if (e) {
e.preventDefault();
e.stopPropagation();
}
}
},
Mouseover: function Mouseover(e) {
this.className = 'bottom-border-active';
if (this._tabulatorMode === 1) {
switch (UI.utils.getTarget(e).tagName) {
case 'TD':
var preTd = UI.utils.getTarget(e);
if (preTd.className === 'pred') preTd.style.cursor = 'copy';
break;
// Uh...I think I might have to give up this
case 'DIV':
var border = UI.utils.getTarget(e);
if (UI.utils.getTarget(e).className === 'bottom-border') {
border.style.borderColor = 'rgb(100%,65%,0%)';
border.style.cursor = 'copy';
}
break;
default:
}
}
},
Mouseout: function Mouseout(e) {
this.className = 'bottom-border';
if (this._tabulatorMode === 1) {
var border = UI.utils.getTarget(e);
if (UI.utils.getTarget(e).className === 'bottom-border') {
border.style.borderColor = 'transparent';
border.style.cursor = 'auto';
}
}
},
/**
* Utilities
*/
whatSortOfEditCell: function whatSortOfEditCell(selectedTd) {
if (selectedTd.nextSibling) return 'predicate';
var predicateTerm = this.getStatementAbout(selectedTd).predicate; // var predicateTerm=selectedTd.parentNode.AJAR_statement.predicate;
if (kb.whether(predicateTerm, UI.ns.rdf('type'), UI.ns.owl('DatatypeProperty')) || kb.whether(predicateTerm, UI.ns.rdfs('range'), UI.ns.rdfs('Literal')) || predicateTerm.termType === 'Collection') {
return 'DatatypeProperty-like';
} else if (kb.