@@ -8,20 +8,28 @@ import { renderAutocompleteControl } from './autocompletBar'
88import { NamedNode , BlankNode , Variable , st } from 'rdflib'
99
1010/**
11- * Render a autocomplete form field
12- *
13- * The same function is used for many similar one-value fields, with different
14- * regexps used to validate.
15- *
16- * @param dom The HTML Document object aka Document Object Model
17- * @param container If present, the created widget will be appended to this
18- * @param already A hash table of (form, subject) kept to prevent recursive forms looping
19- * @param subject The thing about which the form displays/edits data
20- * @param form The form or field to be rendered
21- * @param doc The web document in which the data is
22- * @param callbackFunction Called when data is changed?
23- *
24- * @returns The HTML widget created
11+ * Render a autocomplete form field
12+ *
13+ * Teh autocomplete form searches for an iobject in a definitive public database,
14+ * and allows the user to search for it by name, displaying a list of objects whose names match
15+ * the input to date, and letting the user either click on one of the list,
16+ * or just go on untill there is only one. The process then returns two values,
17+ * the URiI of the object and its name.
18+ *
19+ * @param dom The HTML Document object aka Document Object Model
20+ * @param container If present, the created widget will be appended to this
21+ * @param already A hash table of (form, subject) kept to prevent recursive forms looping
22+ * @param subject The thing about which the form displays/edits data
23+ * @param form The form or field to be rendered
24+ * @param doc The web document in which the data is
25+ * @param callbackFunction Called when data is changed so other parts can be refreshed.
26+ *
27+ * Form properties:
28+ * @param ui:property The property to store the object itself
29+ * @param ui:labelProperty The property used to store the name of the object
30+ * @param ui:categoory The class of objects to be searched, if fixed (else dep on class of subject)
31+ *
32+ * @returns The HTML widget created
2533 */
2634// eslint-disable-next-line complexity
2735export function autocompleteField (
@@ -34,15 +42,15 @@ export function autocompleteField (
3442 callbackFunction : ( ok : boolean , errorMessage : string ) => void
3543) : HTMLElement {
3644 async function addOneIdAndRefresh ( result , _name ) {
37- const ds = kb . statementsMatching ( subject , property as any ) // remove any multiple values
45+ const deletables = kb . statementsMatching ( subject , property as any , null , null ) // remove any multiple values in any doc
3846
39- let is = ds . map ( statement => st ( statement . subject , statement . predicate , result , statement . why ) ) // can include >1 doc
40- if ( is . length === 0 ) {
47+ let insertables = deletables . map ( statement => st ( statement . subject , statement . predicate , result , statement . why ) ) // can include >1 doc
48+ if ( insertables . length === 0 ) {
4149 // or none
42- is = [ st ( subject , property as any , result , doc ) ]
50+ insertables = [ st ( subject , property as any , result , doc ) ]
4351 }
4452 try {
45- await kb . updater . updateMany ( ds , is )
53+ await kb . updater . updateMany ( deletables , insertables )
4654 } catch ( err ) {
4755 callbackFunction ( false , err )
4856 box . appendChild ( widgets . errorMessageBlock ( dom , 'Autocomplete form data write error:' + err ) )
@@ -66,70 +74,87 @@ export function autocompleteField (
6674
6775 const property = kb . any ( form , ns . ui ( 'property' ) )
6876 if ( ! property ) {
69- box . appendChild (
77+ return box . appendChild (
7078 dom . createTextNode ( 'Error: No property given for autocomplete field: ' + form )
7179 )
72- return box
7380 }
81+ const labelProperty = kb . any ( form , ns . ui ( 'labelProperty' ) ) || ns . schema ( 'name' )
82+
7483 const searchClass = kb . any ( form , ns . ui ( 'searchClass' ) )
7584 if ( ! searchClass ) {
7685 return box . appendChild (
7786 dom . createTextNode ( 'Error: No searchClass given for autocomplete field: ' + form )
7887 )
7988 }
80- const endPoint = kb . any ( form , ns . ui ( 'endPoint' ) )
81- if ( ! endPoint ) {
89+ // Parse the data source into query options
90+
91+ const dataSource = kb . any ( form , ns . ui ( 'dataSource' ) )
92+ if ( ! dataSource ) {
8293 return box . appendChild (
83- dom . createTextNode ( 'Error: No SPARQL endPoint given for autocomplete field: ' + form )
94+ dom . createTextNode ( 'Error: No data source given for autocomplete field: ' + form )
8495 )
8596 }
97+ const queryParams = { } // @@ const?
98+ queryParams . targetClass = kb . any ( dataSource , ns . ui ( 'targetClass' ) , null , dataSource . doc ( ) ) // Different ontology?
99+ queryParams . name = kb . anyJS ( dataSource , ns . schema ( 'name' ) , null , dataSource . doc ( ) ) // Different ontology?
100+ queryParams . logo = kb . anyJS ( dataSource , ns . schema ( 'logo' ) , null , dataSource . doc ( ) ) // Different ontology?
101+
102+ if ( ! queryParams . targetClass ) {
103+ queryParams . targetClass = kb . any ( subject , ns . rdf ( 'type' ) ) // @@ be more selective of which class if many
104+ }
105+ const endPoint = kb . any ( dataSource , ns . ui ( 'endPoint' ) , null , dataSource . doc ( ) )
106+ if ( endPoint ) { // SPARQL
107+ queryParams . endpoint = endPoint
108+
109+ queryParams . searchByNameQuery = kb . the ( dataSource , ns . ui ( 'searchByNameQuery' ) , null , dataSource . doc ( ) )
110+ if ( ! queryParams . searchByNameQuery ) {
111+ return box . appendChild (
112+ dom . createTextNode ( 'Error: No searchByNameQuery given for data Source: ' + form ) )
113+ }
114+ queryParams . insitituteDetailsQuery = kb . any ( dataSource , ns . ui ( 'insitituteDetailsQuery' ) , null , dataSource . doc ( ) )
115+ } else {
116+ return box . appendChild (
117+ dom . createTextNode ( 'Error: No SPARQL endPoint given for autocomplete field: ' + form ) )
118+ }
86119 const queryTemplate = kb . any ( form , ns . ui ( 'queryTemplate' ) )
87120 if ( ! queryTemplate ) {
88- box . appendChild (
121+ return box . appendChild (
89122 dom . createTextNode ( 'Error: No queryTemplate given for autocomplete field: ' + form )
90123 )
91- return box
92124 }
93125 // It can be cleaner to just remove empty fields if you can't edit them anyway
94126 const suppressEmptyUneditable = kb . anyJS ( form , ns . ui ( 'suppressEmptyUneditable' ) , null , formDoc )
95127 const editable = kb . updater . editable ( ( doc as NamedNode ) . uri )
96- lhs . appendChild ( widgets . fieldLabel ( dom , property as any , form ) )
97- const uri = widgets . mostSpecificClassURI ( form )
98- let params = widgets . fieldParams [ uri ]
99- if ( params === undefined ) params = { } // non-bottom field types can do this
100- // const theStyle = params.style || style.textInputStyle
101- const klass = kb . the ( form , ns . ui ( 'category' ) , null , formDoc )
102- /*
103- { label: string;
104- logo: string;
105- searchByNameQuery?: string;
106- searchByNameURI?: string;
107- insitituteDetailsQuery?: string;
108- endPoint?: string;
109- class: object
110- }
111- */
112-
113- const searchByNameQuery = kb . the ( form , ns . ui ( 'searchByNameQuery' ) , null , formDoc )
114- const queryParams = {
115- label : 'from form' ,
116- logo : '' ,
117- class : klass ,
118- endPoint : endPoint . uri ,
119- searchByNameQuery
120- }
121128
122129 const options = { // cancelButton?: HTMLElement,
123130 // acceptButton?: HTMLElement,
124- class : klass ,
131+ targetClass : dataSource . targetClass , // @@ simplify?
125132 queryParams
126133 }
134+ let obj = kb . any ( subject , property as any , undefined , doc )
135+ if ( ! obj ) {
136+ obj = kb . any ( form , ns . ui ( 'default' ) )
137+ if ( obj ) {
138+ options . currentObject = obj
139+ options . currentName = kb . the ( obj , labelProperty , null , form . doc ( ) )
140+ } else { // No data or default. Should we suprress the whole field?
141+ if ( suppressEmptyUneditable && ! editable ) {
142+ box . style . display = 'none' // clutter removal
143+ return box
144+ }
145+ }
146+ } else { // get object and name from target data:
147+ options . currentObject = obj
148+ options . currentName = kb . the ( obj , labelProperty , null , subject . doc ( ) )
149+ }
127150
128- rhs . appendChild ( await renderAutocompleteControl ( dom , subject , options , addOneIdAndRefresh ) )
151+ lhs . appendChild ( widgets . fieldLabel ( dom , property as any , form ) )
129152
130- // @@ set existing value is any
131- // renderAutoComplete(dom, options, addOneIdAndRefresh).then(acWiget => rhs.appendChild(acWiget))
153+ // const searchByNameQuery = kb.the(form, ns.ui('searchByNameQuery'), null, formDoc)
132154
155+ rhs . appendChild ( await renderAutocompleteControl ( dom , subject , options , addOneIdAndRefresh ) )
156+
157+ /*
133158 const field = dom.createElement('input')
134159 ;(field as any).style = style.textInputStyle // Do we have to override length etc?
135160 rhs.appendChild(field)
@@ -144,28 +169,7 @@ export function autocompleteField (
144169 field.setAttribute('maxLength', maxLength ? '' + maxLength : '4096')
145170
146171 doc = doc || widgets.fieldStore(subject, property as any, doc)
147-
148- let obj = kb . any ( subject , property as any , undefined , doc )
149- if ( ! obj ) {
150- obj = kb . any ( form , ns . ui ( 'default' ) )
151- }
152- if ( obj ) {
153- field . value = obj . value || ''
154- }
155- field . setAttribute ( 'style' , style )
156- if ( ! kb . updater ) {
157- throw new Error ( 'kb has no updater' )
158- }
159- if ( ! editable ) {
160- field . readOnly = true // was: disabled. readOnly is better
161- ; ( field as any ) . style = style . textInputStyleUneditable
162- // backgroundColor = textInputBackgroundColorUneditable
163- if ( suppressEmptyUneditable && field . value === '' ) {
164- box . style . display = 'none' // clutter
165- }
166- return box
167- }
168-
172+ */
169173 return box
170174}
171175
0 commit comments