src/outline/userInput.js
// 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')
const $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
const 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
) {
const 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)
const 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'
)
const 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'))
) {
const 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 (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 (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 (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.whether(predicateTerm, rdf('type'), UI.ns.owl('ObjectProperty'))
) {
return 'ObjectProperty-like'
} else {
return 'no-idea'
}
},
getStatementAbout: function getStatementAbout (something) {
// var trNode=something.parentNode;
var trNode = UI.utils.ancestor(something, 'TR')
if (!trNode) {
throw new Error('No ancestor TR for the TD we clicked on:' + something)
}
try {
var statement = trNode.AJAR_statement
} catch (e) {
throw new Error(
'No AJAR_statement!' +
something +
something.textContent +
' has ancestor ' +
trNode
) // was commented out @@
// throw 'TR not a statement TR' // was commented out @@
}
// Set last modified here, I am not sure this will be ok.
this.lastModifiedStat = trNode.AJAR_statement
this.statIsInverse = trNode.AJAR_inverse
return statement
},
createInputBoxIn: function createInputBoxIn (tdNode, defaultText) {
function UpAndDown (e) {
if (e.keyCode === 38 || e.keyCode === 40) {
outline.OutlinerKeypressPanel(e)
outline.UserInput.clearInputAndSave()
}
}
UI.log.info(
'myDocument in createInputBoxIn is now ' + myDocument.location
)
UI.log.info('outline.document is now ' + outline.document.location)
var inputBox = myDocument.createElement('input')
inputBox.setAttribute('value', defaultText)
inputBox.setAttribute('class', 'textinput')
// inputBox.setAttribute('size','100');//should be the size of <TD>
if (tdNode.className !== 'undetermined selected') {
inputBox.setAttribute('size', '100') // should be the size of <TD>
inputBox.addEventListener('keypress', UpAndDown, false)
}
tdNode.appendChild(inputBox)
return inputBox
},
// called when 'New...' is clicked(eventlistener) or enter is pressed while 'New...' is highlighted
createNew: function createNew (_e) {
outline.UserInput.clearMenu()
var selectedTd = outline.getSelection()[0]
var targetdoc = selectedTd.parentNode.AJAR_statement.why
var newTerm = kb.nextSymbol(targetdoc)
outline.UserInput.fillInRequest('object', selectedTd, newTerm)
// selection is changed
outline.outlineExpand(outline.getSelection()[0], newTerm)
},
inputURI: function inputURI (_e) {
var This = outline.UserInput
This.clearMenu()
var selectedTd = outline.getSelection()[0]
UI.utils.emptyNode(selectedTd)
var tiptext = ' (Type a URI) '
This.lastModified = This.createInputBoxIn(selectedTd, tiptext)
This.lastModified.select()
function typeURIhandler (e) {
e.stopPropagation()
switch (e.keyCode) {
case 13: // enter
case 9: // tab
// this is input box
if (this.value !== tiptext) {
var newuri = this.value // @@ Removed URI "fixup" code
This.fillInRequest('object', selectedTd, kb.sym(newuri))
}
}
}
This.lastModified.addEventListener('keypress', typeURIhandler, false)
/*
if (false && UI.isExtension){
var selectedTd = outline.getSelection()[0];
emptyNode(selectedTd);
var textbox = myDocument.createElementNS(kXULNS,'textbox');
textbox.setAttribute('type','autocomplete');
textbox.setAttribute('autocompletesearch','history');
selectedTd.appendChild(textbox);
urlbar = gURLBar.cloneNode(false);
selectedTd.appendChild(urlbar);
urlbar.mController = gURLBar.mController;
}
*/
},
appendToPredicate: function appendToPredicate (predicateTd) {
var isEnd = false
var trIterator
try {
for (
trIterator = predicateTd.parentNode.nextSibling;
trIterator.childNodes.length === 1 && trIterator.AJAR_statement;
// number of nodes as condition, also beware of toggle Trs that don't have AJAR_statement
trIterator = trIterator.nextSibling
) {}
} catch (e) {
isEnd = true
}
// if(!isEnd && HCIoptions["bottom insert highlights"].enabled) trIterator=trIterator.previousSibling;
var insertTr = myDocument.createElement('tr')
// style stuff, I'll have to investigate appendPropertyTRs() somehow
insertTr.style.colspan = '1'
insertTr.style.display = 'block'
insertTr.style.display = ''
if (predicateTd.hasAttribute('rowspan')) {
predicateTd.setAttribute(
'rowspan',
parseInt(predicateTd.getAttribute('rowspan')) + 1
)
}
if (!predicateTd.hasAttribute('rowspan')) {
predicateTd.setAttribute('rowspan', '2')
}
if (!isEnd) {
trIterator.parentNode.insertBefore(insertTr, trIterator)
} else {
var table = predicateTd.parentNode.parentNode
if (table.className === 'defaultPane') {
table.insertBefore(insertTr, table.lastChild)
} else {
table.appendChild(insertTr)
}
}
return insertTr
},
bnode2symbol: function bnode2symbol (bnode, symbol) {
kb.copyTo(bnode, symbol, ['two-direction', 'delete'])
},
generateRequest: function generateRequest (
tipText,
trNew,
isPredicate,
notShow
) {
var trNode
if (!notShow) {
if (trNew) {
trNode = trNew
} else {
trNode = UI.utils.ancestor(this.lastModified, 'TR')
}
UI.utils.emptyNode(trNode)
}
// create the undetermined term
// Choice 1:
// var reqTerm=kb.literal("TBD");
// this is troblesome since RDFIndexedFormula does not allow me to add <x> <y> "TBD". twice
// Choice 2: Use a variable.
// Agreed. Kenny wonders whether there is RDF/XML representation of a variable.
// labelPriority[UI.ns.link('message').uri] = 20;
// We must get rid of this clutter in the store. "OK, will be stroed in a seperate formula to avoid bugs", Kenny says
var tp = TempFormula
var reqTerm = tp.bnode()
tp.add(reqTerm, UI.ns.rdf('type'), UI.ns.link('Request'))
if (tipText.length < 10) {
tp.add(reqTerm, UI.ns.link('message'), tp.literal(tipText))
} else {
tp.add(reqTerm, UI.ns.link('message'), tp.literal(tipText))
}
tp.add(reqTerm, UI.ns.link('to'), tp.literal('The User'))
tp.add(reqTerm, UI.ns.link('from'), tp.literal('The User'))
// append the undetermined td
if (!notShow) {
var newNode
if (isPredicate) {
newNode = trNode.appendChild(
outline.outlinePredicateTD(reqTerm, trNode, false, false)
)
} else {
newNode = trNode.appendChild(outline.outlineObjectTD(reqTerm))
}
newNode.className = 'undetermined'
newNode.textContent = tipText
}
return reqTerm
},
showMenu: function showMenu (
e,
menuType,
inputQuery,
extraInformation,
_order
) {
// ToDo:order, make a class?
UI.log.info('myDocument is now ' + myDocument.location)
UI.log.info('outline.doucment is now ' + outline.document.location)
var This = this
var menu = myDocument.createElement('div')
qp('\n**** In showMenu, menuType = ' + menuType + '\n')
if (extraInformation) {
for (var x in extraInformation) {
console.log('\t extra ' + x + ': ' + extraInformation[x] + '\n')
}
}
console.log('CREATED MENU\n') // hq
menu.id = this.menuID
menu.className = 'outlineMenu'
// menu.addEventListener('click',false);
menu.style.top = e.pageY + 'px'
menu.style.left = e.pageX + 'px'
/// /For pubsPane
// This is for setting the location of the dropdown menu, because
// JournalTitleAutoComplete is called with a keypress, and not mouse actions
// Get Offset of an HTML element
var getOffset = function getOffset (el) {
var _lf = 0
var _tp = 0
var oldlf = 0
var oldtp = 0
var newlf = 0
var newtp = 0
// repeatedly get ancestor's positions
// TODO: STILL a small offset/bug
while (el && !isNaN(el.offsetLeft) && !isNaN(el.offsetTop)) {
newlf = el.offsetLeft
newtp = el.offsetTop
// only change if the new parent's offset is different
if (newlf !== oldlf) {
_lf += el.offsetLeft - el.scrollLeft
}
if (newtp !== oldtp) {
_tp += el.offsetTop - el.scrollTop
}
oldlf = newlf
oldtp = newtp
el = el.parentNode
}
// there is a constant offset
return { top: _tp + 54, left: _lf - 38 }
}
// Change the position of menu in pubsPane's journal Title AC
if (menuType === 'JournalTitleAutoComplete') {
// hql
var loc = getOffset(myDocument.getElementById('inpid_journal_title'))
loc.left -= myDocument.getElementById('inpid_journal_title').scrollTop
menu.style.top = loc.top + 'px'
menu.style.left = loc.left + 'px'
}
console.log(
'menu at top=' + menu.style.top + ' left=' + menu.style.left + '\n'
) // hql
// \\\\\\\hql
myDocument.body.appendChild(menu)
var table = menu.appendChild(myDocument.createElement('table'))
menu.lastHighlight = null
function highlightTr (e) {
if (menu.lastHighlight) menu.lastHighlight.className = ''
menu.lastHighlight = UI.utils.ancestor(UI.utils.getTarget(e), 'TR')
if (!menu.lastHighlight) return // mouseover <TABLE>
menu.lastHighlight.className = 'activeItem'
}
table.addEventListener('mouseover', highlightTr, false)
// setting for action after selecting item
switch (menuType) {
case 'DidYouMeanDialog':
var selectItem = function selectItem (e) {
qp('DID YOU MEAN SELECT ITEM!!!!!')
var target = UI.utils.ancestor(UI.utils.getTarget(e), 'TR')
if (target.childNodes.length === 2 && target.nextSibling) {
// Yes
kb.add(bnodeTerm, IDpredicate, IDterm) // used to connect the two
outline.UserInput.clearMenu()
} else if (target.childNodes.length === 2) {
outline.UserInput.clearMenu()
}
}
break
case 'LimitedPredicateChoice':
var clickedTd = extraInformation.clickedTd
selectItem = function selectItem (e) {
qp('LIMITED P SELECT ITEM!!!!')
var selectedPredicate = UI.utils.getAbout(kb, UI.utils.getTarget(e))
var predicateChoices =
clickedTd.parentNode.AJAR_statement.predicate.elements
for (var i = 0; i < predicateChoices.length; i++) {
if (predicateChoices[i].sameTerm(selectedPredicate)) {
predicateChoices.unshift(predicateChoices.splice(i, 1)[0])
}
}
outline.UserInput.clearMenu()
// refresh the choice
var tr = clickedTd.parentNode
var newTd = outline.outlinePredicateTD(
tr.AJAR_statement.predicate,
tr
)
tr.insertBefore(newTd, clickedTd)
tr.removeChild(clickedTd)
This.lastModified.select()
}
break
case 'PredicateAutoComplete':
case 'GeneralAutoComplete':
case 'GeneralPredicateChoice':
case 'JournalTitleAutoComplete': // hql
case 'TypeChoice':
// Clickable menu
var isPredicate = extraInformation.isPredicate
var selectedTd = extraInformation.selectedTd
selectItem = function selectItem (e) {
qp('WOOHOO')
var inputTerm = UI.utils.getAbout(kb, UI.utils.getTarget(e))
qp('GENERAL SELECT ITEM!!!!!!=' + inputTerm)
qp('target=' + UI.utils.getTarget(e))
if (isPredicate) {
qp('1')
if (
outline.UserInput.fillInRequest(
'predicate',
selectedTd,
inputTerm
)
) {
qp('2')
outline.UserInput.clearMenu()
}
} else {
qp('3')
// thisInput.fillInRequest('object',selectedTd,inputTerm); //why is this not working?
if (
outline.UserInput.fillInRequest('object', selectedTd, inputTerm)
) {
qp('4')
outline.UserInput.clearMenu()
}
}
}
break
default:
throw new Error('userinput: unexpected mode')
}
// hq: this line makes the menu clickable
table.addEventListener('click', selectItem, false)
// Add Items to the list
// build NameSpaces here from knowledge base
var NameSpaces = {}
// for each (ontology in ontologies)
kb.each(undefined, UI.ns.rdf('type'), UI.ns.owl('Ontology')).map(
function (ontology) {
var label = UI.utils.label(ontology)
if (!label) return
// this is like extracting metadata from URI. Maybe it's better not to take the abbrevs.
var match = label.value.match(/\((.+?)\)/)
if (match) {
NameSpaces[match[1]] = ontology.uri
} else {
NameSpaces[label.value] = ontology.uri
}
}
)
function addMenuItem (predicate) {
if (table.firstChild && table.firstChild.className === 'no-suggest') {
table.removeChild(table.firstChild)
}
var Label = UI.utils.predicateLabelForXML(predicate, false)
// Label = Label.slice(0,1).toUpperCase() + Label.slice(1);
if (!predicate.uri) return // bnode
var theNamespace = '??'
for (var name in NameSpaces) {
UI.log.debug(NameSpaces[name])
if (UI.rdf.Util.string_startswith(predicate.uri, NameSpaces[name])) {
theNamespace = name
break
}
}
var tr = table.appendChild(myDocument.createElement('tr'))
tr.setAttribute('about', predicate)
var th = tr.appendChild(myDocument.createElement('th'))
th.appendChild(myDocument.createElement('div')).appendChild(
myDocument.createTextNode(Label)
)
tr.appendChild(myDocument.createElement('td')).appendChild(
myDocument.createTextNode(theNamespace.toUpperCase())
)
}
function addPredicateChoice (selectedQuery) {
return function (bindings) {
var predicate = bindings[selectedQuery.vars[0]]
addMenuItem(predicate)
}
}
function clearMenu (e) {
This.clearMenu()
e.stopPropagation()
}
var inputText
switch (menuType) {
case 'DidYouMeanDialog':
var dialogTerm = extraInformation.dialogTerm
var bnodeTerm = extraInformation.bnodeTerm
// have to do style instruction passing
menu.style.width = 'auto'
var h1 = table.appendChild(myDocument.createElement('tr'))
var h1th = h1.appendChild(myDocument.createElement('th'))
h1th.appendChild(myDocument.createTextNode('Did you mean...'))
var plist = kb.statementsMatching(dialogTerm)
var i
for (i = 0; i < plist.length; i++) {
if (
kb.whether(
plist[i].predicate,
rdf('type'),
UI.ns.owl('InverseFunctionalProperty')
)
) {
break
}
}
var IDpredicate = plist[i].predicate
var IDterm = kb.any(dialogTerm, plist[i].predicate)
var text =
UI.utils.label(dialogTerm) +
' who has ' +
UI.utils.label(IDpredicate) +
' ' +
IDterm +
'?'
var h2 = table.appendChild(myDocument.createElement('tr'))
var h2th = h2.appendChild(myDocument.createElement('th'))
h2th.appendChild(myDocument.createTextNode(text))
h1th.setAttribute('colspan', '2')
h2th.setAttribute('colspan', '2')
var ans1 = table.appendChild(myDocument.createElement('tr'))
ans1
.appendChild(myDocument.createElement('th'))
.appendChild(myDocument.createTextNode('Yes'))
ans1
.appendChild(myDocument.createElement('td'))
.appendChild(myDocument.createTextNode('BOOLEAN'))
var ans2 = table.appendChild(myDocument.createElement('tr'))
ans2
.appendChild(myDocument.createElement('th'))
.appendChild(myDocument.createTextNode('No'))
ans2
.appendChild(myDocument.createElement('td'))
.appendChild(myDocument.createTextNode('BOOLEAN'))
break
case 'PredicateAutoComplete': // Prompt user for possible relationships for new data
inputText = extraInformation.inputText
/* The labeller functionality code ahs been lost or dropped -- reinstate this? */
// @@ TODO: Write away the need for exception on next line
// eslint-disable-next-line no-case-declarations
const predicates = outline.labeller.searchAdv(
inputText,
undefined,
'predicate'
)
// @@ TODO: Write away the need for exception on next line
// eslint-disable-next-line no-case-declarations
let results = [] // @@ fixme
for (let i = 0; i < predicates.length; i++) {
var tempQuery = {}
tempQuery.vars = []
tempQuery.vars.push('Kenny')
var tempBinding = {}
tempBinding.Kenny = kb.fromNT(predicates[i].NT)
try {
addPredicateChoice(tempQuery)(tempBinding)
} catch (e) {
throw new Error("I'll deal with bnodes later...[Kenny]" + e)
} // I'll deal with bnodes later...
}
// @@ TODO: Write away the need for exception on next line
// eslint-disable-next-line no-case-declarations
let entries = results[0]
if (entries.length === 0) {
console.log('cm length 0\n') // hq
this.clearMenu()
return
}
for (let i = 0; i < entries.length && i < 10; i++) {
// do not show more than 30 items
addMenuItem(entries[i][1])
}
break
case 'GeneralAutoComplete':
inputText = extraInformation.inputText
try {
results = outline.labeller.search(inputText)
} catch (e) {
console.log(
'GeneralAutoComplete: debug me ' +
extraInformation.selectedTd.textContent +
'\n' +
e +
'\n'
)
}
entries = results[0] // [label, subject,priority]
var types = results[1]
if (entries.length === 0) {
console.log('cm length 0\n') // hq
this.clearMenu()
return
}
for (let i = 0; i < entries.length && i < 10; i++) {
// do not show more than 30 items
// console.log("\nGEN ENTRIES["+i+"] = "+entries[i]+"\n");//hq
var thisNT = entries[i][1].toNT()
// console.log("thisNT="+thisNT+"\n");
var tr = table.appendChild(myDocument.createElement('tr'))
tr.setAttribute('about', thisNT)
var th = tr.appendChild(myDocument.createElement('th'))
th.appendChild(myDocument.createElement('div')).appendChild(
myDocument.createTextNode(entries[i][0])
)
// var theTerm = entries[i][1]
// var type=theTerm?kb.any(kb.fromNT(thisNT),rdf('type')):undefined;
var type = types[i]
var typeLabel = type ? UI.utils.label(type) : ''
tr.appendChild(myDocument.createElement('td')).appendChild(
myDocument.createTextNode(typeLabel)
)
}
/* var choices=extraInformation.choices;
var index=extraInformation.index;
for (var i=index-10;i<index+20;i++){ //show 30 items
if (i<0) i=0;
if (i==choices.length) break;
var thisNT=choices[i].NT;
var tr=table.appendChild(myDocument.createElement('tr'));
tr.setAttribute('about',thisNT);
var th=tr.appendChild(myDocument.createElement('th'))
th.appendChild(myDocument.createElement('div')).appendChild(myDocument.createTextNode(choices[i].label));
var theTerm=kb.fromNT(thisNT);
var type=theTerm?kb.any(kb.fromNT(thisNT),rdf('type')):undefined;
var typeLabel=type?label(type):"";
tr.appendChild(myDocument.createElement('td')).appendChild(myDocument.createTextNode(typeLabel));
}
*/
break
case 'JournalTitleAutoComplete': // hql
// HEART OF JOURNAL TITLE AUTOCOMPLETE
// extraInformatin is from above getAutoCompleteHandler
inputText = extraInformation.inputText
console.log('testing searching text= ' + inputText + ' =====\n')
console.log('\n===start JournalTitleAutoComplete\n')
// Gets all the URI's with type Journal in the knowledge base
var juris = kb.each(undefined, rdf('type'), bibo('Journal'))
var matchedtitle = [] // debugging display before inserts into menu
for (let i = 0; i < juris.length; i++) {
var juri = juris[i]
var jtitle = kb.each(juri, dcelems('title'), undefined)
var jtstr = jtitle + ''
var matchstr = inputText.toLowerCase()
var jTitleLowerCase = jtstr.toLowerCase()
// If the inputText as a whole is contained in a journal title
if (jTitleLowerCase.search(matchstr) !== -1) {
qp('FOUND A Journal Title Match!!!!!!')
matchedtitle.push(jtitle)
// Add it as a row to the menu:
// === Title, URI ==
const tr = table.appendChild(myDocument.createElement('tr'))
tr.setAttribute('about', 'journalTitle')
const th = tr.appendChild(myDocument.createElement('th'))
th.appendChild(myDocument.createElement('div')).appendChild(
myDocument.createTextNode(jtitle)
)
tr.appendChild(myDocument.createElement('td')).appendChild(
myDocument.createTextNode(juri)
)
}
}
console.log('matched: ' + matchedtitle + '\n')
console.log("\\\\done showMenu's JTAutocomplete\n")
break
case 'LimitedPredicateChoice':
var choiceTerm = UI.utils.getAbout(kb, extraInformation.clickedTd)
// because getAbout relies on kb.fromNT, which does not deal with
// the 'Collection' termType. This termType is ambiguous anyway.
choiceTerm.termType = 'Collection'
var choices = kb.each(choiceTerm, UI.ns.link('element'))
for (let i = 0; i < choices.length; i++) {
addMenuItem(choices[i])
}
break
default:
tr = table.appendChild(myDocument.createElement('tr'))
tr.className = 'no-suggest'
th = tr.appendChild(myDocument.createElement('th'))
th.appendChild(myDocument.createElement('div')).appendChild(
myDocument.createTextNode(
'No suggested choices. Try to type instead.'
)
)
tr.appendChild(myDocument.createElement('td')).appendChild(
myDocument.createTextNode('OK')
)
// var This = this
tr.addEventListener('click', clearMenu, 'false')
var nullFetcher = function () {}
switch (inputQuery.constructor.name) {
case 'Array':
for (let i = 0; i < inputQuery.length; i++) {
kb.query(
inputQuery[i],
addPredicateChoice(inputQuery[i]),
nullFetcher
)
}
break
case 'undefined':
throw new Error('addPredicateChoice: query is not defined')
// break
default:
kb.query(inputQuery, addPredicateChoice(inputQuery), nullFetcher)
}
}
}, // funciton showMenu
/* When a blank is filled. This happens even for blue-cross editing. */
fillInRequest: function fillInRequest (type, selectedTd, inputTerm) {
var tr = selectedTd.parentNode
var stat
var isInverse
stat = tr.AJAR_statement
isInverse = tr.AJAR_inverse
var reqTerm = type === 'object' ? stat.object : stat.predicate
var newStat
var doNext = false
// RDF Event
var eventhandler
if (kb.any(reqTerm, UI.ns.link('onfillin'))) {
/* 2017 -- Not sure what is supposed to happen here -- timbl @@@@
eventhandler = function(subject) {
return kb.any(reqTerm, UI.ns.link('onfillin')).value)
}
*/
}
if (type === 'predicate') {
// ToDo: How to link two things with an inverse relationship
const newTd = outline.outlinePredicateTD(inputTerm, tr, false, false)
if (selectedTd.nextSibling.className !== 'undetermined') {
var s = new UI.rdf.Statement(
stat.subject,
inputTerm,
stat.object,
stat.why
)
try {
UI.store.updater.update([], [s], function (
uri,
success,
errorBody
) {
if (success) {
newStat = kb.anyStatementMatching(
stat.subject,
inputTerm,
stat.object,
stat.why
)
tr.AJAR_statement = newStat
newTd.className = newTd.className.replace(/ pendingedit/g, '')
} else {
// outline.UserInput.deleteTriple(newTd,true);
// Warn the user that the write has failed.
UI.log.warn(
'Failure occurs (#2) while inserting ' +
tr.AJAR_statement +
'\n\n' +
errorBody
)
}
})
} catch (e) {
UI.log.error(e)
// Warn the user that the write has failed.
UI.log.warn(
'Error when insert (#2) of statement ' + s + ':\n\t' + e
)
return
}
newTd.className += ' pendingedit'
this.lastModified = null
} else {
this.formUndetStat(
tr,
stat.subject,
inputTerm,
stat.object,
stat.why,
false
)
outline.walk('right')
doNext = true
}
outline.replaceTD(newTd, selectedTd)
TempFormula.remove(stat)
} else if (type === 'object') {
// Object value has been edited
const newTd = outline.outlineObjectTD(inputTerm)
outline.replaceTD(newTd, selectedTd)
if (
!selectedTd.previousSibling ||
selectedTd.previousSibling.className !== 'undetermined'
) {
let s
if (!isInverse) {
s = new UI.rdf.Statement(
stat.subject,
stat.predicate,
inputTerm,
stat.why
)
} else {
s = new UI.rdf.Statement(
inputTerm,
stat.predicate,
stat.object,
stat.why
)
}
try {
UI.store.updater.update([], [s], function (
uri,
success,
_errorBody
) {
UI.log.info(
'@@ usinput.js (object) callback ok=' +
success +
' for statement:' +
s +
'\n '
)
var newStats
if (success) {
newTd.className = newTd.className.replace(/ pendingedit/g, '') // User feedback
if (!isInverse) {
newStats = kb.statementsMatching(
stat.subject,
stat.predicate,
inputTerm,
stat.why
)
} else {
newStats = kb.statementsMatching(
inputTerm,
stat.predicate,
stat.object,
stat.why
)
}
if (!newStats.length) {
UI.log.error("userinput.js 1711: Can't find statememt!")
}
tr.AJAR_statement = newStats[0]
} else {
UI.log.warn(
'userinput.js (object): Fail trying to insert statement ' + s
)
// outline.UserInput.deleteTriple(newTd,true);
}
})
} catch (e) {
// outline.UserInput.deleteTriple(newTd,true);
UI.log.error(
'userinput.js (object): exception trying to insert statement ' +
s +
': ' +
UI.utils.stackString(e)
)
UI.log.warn('Error trying to insert statement ' + s + ':\n' + e)
return
}
this.lastModified = null
newTd.className += ' pendingedit'
} else {
// ?this.formUndetStat(tr...)
outline.walk('left')
doNext = true
}
// removal of the undetermined statement
TempFormula.remove(stat)
}
// do not throw away user's work even update fails
UserInputFormula.statements.push(newStat)
if (eventhandler) eventhandler(stat.subject)
if (doNext) {
this.startFillInText(outline.getSelection()[0])
} else {
return true
} // can clearMenu
},
formUndetStat: function formUndetStat (
trNode,
subject,
predicate,
object,
why,
inverse
) {
trNode.AJAR_inverse = inverse
trNode.AJAR_statement = TempFormula.add(subject, predicate, object, why)
return trNode.AJAR_statement
}
}
}