@@ -1329,6 +1329,213 @@ export function makeDescription (
13291329 return group
13301330}
13311331
1332+ /** Make SELECT element to select options
1333+ //
1334+ // @param subject - a term, the subject of the statement(s) being edited.
1335+ // @param predicate - a term, the predicate of the statement(s) being edited
1336+ // @param possible - a list of terms, the possible value the object can take
1337+ // @param options.multiple - Boolean - Whether more than one at a time is allowed
1338+ // @param options.nullLabel - a string to be displayed as the
1339+ // option for none selected (for non multiple)
1340+ // @param options.mint - User may create thing if this sent to the prompt string eg "New foo"
1341+ // @param options.subForm - If mint, then the form to be used for minting the new thing
1342+ // @param dataDoc - The web document being edited
1343+ // @param callbackFunction - takes (boolean ok, string errorBody)
1344+ */
1345+ export function makeSelectForClassifierOptions (
1346+ dom ,
1347+ kb ,
1348+ subject ,
1349+ predicate ,
1350+ possible ,
1351+ options ,
1352+ dataDoc ,
1353+ callbackFunction
1354+ ) {
1355+ log . debug ( 'Select list length now ' + possible . length )
1356+ let n = 0
1357+ const uris = { } // Count them
1358+ const editable = kb . updater . editable ( dataDoc . uri )
1359+
1360+ for ( let i = 0 ; i < possible . length ; i ++ ) {
1361+ const sub = possible [ i ] // @@ Maybe; make this so it works with blank nodes too
1362+ if ( ! sub . uri ) debug . warn ( `makeSelectForClassifierOptions: option does not have an uri: ${ sub } , with predicate: ${ predicate } ` )
1363+ if ( ! sub . uri || sub . uri in uris ) continue
1364+ uris [ sub . uri ] = true
1365+ n ++
1366+ } // uris is now the set of possible options
1367+ if ( n === 0 && ! options . mint ) {
1368+ return errorMessageBlock (
1369+ dom ,
1370+ "Can't do selector with no options, subject= " +
1371+ subject +
1372+ ' property = ' +
1373+ predicate +
1374+ '.'
1375+ )
1376+ }
1377+
1378+ log . debug ( 'makeSelectForClassifierOptions: dataDoc=' + dataDoc )
1379+ let actual
1380+ const getActual = function ( ) {
1381+ actual = { }
1382+ if ( predicate . sameTerm ( ns . rdf ( 'type' ) ) ) {
1383+ actual = kb . findTypeURIs ( subject )
1384+ } else {
1385+ kb . each ( subject , predicate , null , dataDoc ) . forEach ( function ( x ) {
1386+ actual [ x . uri ] = true
1387+ } )
1388+ }
1389+ return actual
1390+ }
1391+ actual = getActual ( )
1392+
1393+ const onChange = function ( _e ) {
1394+ select . disabled = true // until data written back - gives user feedback too
1395+ const ds = [ ]
1396+ let is = [ ]
1397+ const removeValue = function ( t ) {
1398+ if ( kb . holds ( subject , predicate , t , dataDoc ) ) {
1399+ ds . push ( $rdf . st ( subject , predicate , t , dataDoc ) )
1400+ }
1401+ }
1402+ let newObject
1403+ for ( let i = 0 ; i < select . options . length ; i ++ ) {
1404+ const opt = select . options [ i ]
1405+ if ( opt . selected && opt . AJAR_mint ) {
1406+ if ( options . mintClass ) {
1407+ const thisForm = promptForNew (
1408+ dom ,
1409+ kb ,
1410+ subject ,
1411+ predicate ,
1412+ options . mintClass ,
1413+ null ,
1414+ dataDoc ,
1415+ function ( ok , body ) {
1416+ if ( ! ok ) {
1417+ callbackFunction ( ok , body , { change : 'new' } ) // @@ if ok, need some form of refresh of the select for the new thing
1418+ }
1419+ }
1420+ )
1421+ select . parentNode . appendChild ( thisForm )
1422+ newObject = thisForm . AJAR_subject
1423+ } else {
1424+ newObject = newThing ( dataDoc )
1425+ }
1426+ is . push ( $rdf . st ( subject , predicate , newObject , dataDoc ) )
1427+ if ( options . mintStatementsFun ) {
1428+ is = is . concat ( options . mintStatementsFun ( newObject ) )
1429+ }
1430+ }
1431+ if ( ! opt . AJAR_uri ) continue // a prompt or mint
1432+ if ( opt . selected && ! ( opt . AJAR_uri in actual ) ) {
1433+ // new class
1434+ is . push ( $rdf . st ( subject , predicate , kb . sym ( opt . AJAR_uri ) , dataDoc ) )
1435+ }
1436+ if ( ! opt . selected && opt . AJAR_uri in actual ) {
1437+ // old class
1438+ removeValue ( kb . sym ( opt . AJAR_uri ) )
1439+ }
1440+ if ( opt . selected ) select . currentURI = opt . AJAR_uri
1441+ }
1442+ let sel = select . subSelect // All subclasses must also go
1443+ while ( sel && sel . currentURI ) {
1444+ removeValue ( kb . sym ( sel . currentURI ) )
1445+ sel = sel . subSelect
1446+ }
1447+ sel = select . superSelect // All superclasses are redundant
1448+ while ( sel && sel . currentURI ) {
1449+ removeValue ( kb . sym ( sel . currentURI ) )
1450+ sel = sel . superSelect
1451+ }
1452+ function doneNew ( ok , _body ) {
1453+ callbackFunction ( ok , { widget : 'select' , event : 'new' } )
1454+ }
1455+ log . info ( 'makeSelectForClassifierOptions: data doc = ' + dataDoc )
1456+ kb . updater . update ( ds , is , function ( uri , ok , body ) {
1457+ actual = getActual ( ) // refresh
1458+ if ( ok ) {
1459+ select . disabled = false // data written back
1460+ if ( newObject ) {
1461+ const fn = fieldFunction ( dom , options . subForm )
1462+ fn (
1463+ dom ,
1464+ select . parentNode ,
1465+ { } ,
1466+ newObject ,
1467+ options . subForm ,
1468+ dataDoc ,
1469+ doneNew
1470+ )
1471+ }
1472+ } else {
1473+ return select . parentNode . appendChild ( errorMessageBlock ( dom , 'Error updating data in select: ' + body ) )
1474+ }
1475+ if ( callbackFunction ) callbackFunction ( ok , { widget : 'select' , event : 'change' } )
1476+ } )
1477+ }
1478+
1479+ const select = dom . createElement ( 'select' )
1480+ select . setAttribute ( 'style' , style . formSelectSTyle )
1481+ if ( options . multiple ) select . setAttribute ( 'multiple' , 'true' )
1482+ select . currentURI = null
1483+
1484+ select . refresh = function ( ) {
1485+ actual = getActual ( ) // refresh
1486+ for ( let i = 0 ; i < select . children . length ; i ++ ) {
1487+ const option = select . children [ i ]
1488+ if ( option . AJAR_uri ) {
1489+ option . selected = option . AJAR_uri in actual
1490+ }
1491+ }
1492+ select . disabled = false // unlocked any conflict we had got into
1493+ }
1494+
1495+ for ( const uri in uris ) {
1496+ const c = kb . sym ( uri )
1497+ const option = dom . createElement ( 'option' )
1498+ if ( options . disambiguate ) {
1499+ option . appendChild ( dom . createTextNode ( utils . labelWithOntology ( c , true ) ) ) // Init. cap
1500+ } else {
1501+ option . appendChild ( dom . createTextNode ( utils . label ( c , true ) ) ) // Init.
1502+ }
1503+ const backgroundColor = kb . any (
1504+ c ,
1505+ kb . sym ( 'http://www.w3.org/ns/ui#backgroundColor' )
1506+ )
1507+ if ( backgroundColor ) {
1508+ option . setAttribute (
1509+ 'style' ,
1510+ 'background-color: ' + backgroundColor . value + '; '
1511+ )
1512+ }
1513+ option . AJAR_uri = uri
1514+ if ( uri in actual ) {
1515+ option . setAttribute ( 'selected' , 'true' )
1516+ select . currentURI = uri
1517+ // dump("Already in class: "+ uri+"\n")
1518+ }
1519+ select . appendChild ( option )
1520+ }
1521+ if ( editable && options . mint ) {
1522+ const mint = dom . createElement ( 'option' )
1523+ mint . appendChild ( dom . createTextNode ( options . mint ) )
1524+ mint . AJAR_mint = true // Flag it
1525+ select . insertBefore ( mint , select . firstChild )
1526+ }
1527+ if ( select . currentURI == null && ! options . multiple ) {
1528+ const prompt = dom . createElement ( 'option' )
1529+ prompt . appendChild ( dom . createTextNode ( options . nullLabel ) )
1530+ select . insertBefore ( prompt , select . firstChild )
1531+ prompt . selected = true
1532+ }
1533+ if ( editable ) {
1534+ select . addEventListener ( 'change' , onChange , false )
1535+ }
1536+ return select
1537+ } // makeSelectForClassifierOptions
1538+
13321539/** Make SELECT element to select options
13331540//
13341541// @param subject - a term, the subject of the statement(s) being edited.
@@ -1503,20 +1710,41 @@ export function makeSelectForCategory (
15031710) {
15041711 const du = kb . any ( category , ns . owl ( 'disjointUnionOf' ) )
15051712 let subs
1713+ let multiple = false
15061714 if ( ! du ) {
15071715 subs = kb . each ( undefined , ns . rdfs ( 'subClassOf' ) , category )
1716+ multiple = true
15081717 } else {
15091718 subs = du . elements
15101719 }
15111720 log . debug ( 'Select list length ' + subs . length )
1512-
1513- return makeSelectForOptions (
1721+ if ( subs . length === 0 ) {
1722+ return errorMessageBlock (
1723+ dom ,
1724+ "Can't do " +
1725+ ( multiple ? 'multiple ' : '' ) +
1726+ 'selector with no subclasses of category: ' +
1727+ category
1728+ )
1729+ }
1730+ if ( subs . length === 1 ) {
1731+ return errorMessageBlock (
1732+ dom ,
1733+ "Can't do " +
1734+ ( multiple ? 'multiple ' : '' ) +
1735+ 'selector with only 1 subclass of category: ' +
1736+ category +
1737+ ':' +
1738+ subs [ 1 ]
1739+ )
1740+ }
1741+ return makeSelectForClassifierOptions (
15141742 dom ,
15151743 kb ,
15161744 subject ,
15171745 ns . rdf ( 'type' ) ,
15181746 subs ,
1519- { nullLabel : '* Select type *' } ,
1747+ { multiple , nullLabel : '* Select type *' } ,
15201748 dataDoc ,
15211749 callbackFunction
15221750 )
0 commit comments