ir_anthology_ui_v3/
├── index.html # Main HTML with Tailwind CSS
├── style.css # Custom table styles
├── PROGRESS.md # Development progress notes
├── ABSTRACT.md # High-level overview
├── js/
│ ├── app.js # Main entry point
│ ├── state.js # State & URL sync
│ ├── queryBuilder.js # SPARQL query generation
│ ├── fetchService.js # API communication
│ ├── tableView.js # Dynamic table rendering
│ ├── search.js # Search handling
│ ├── filterChips.js # Filter chip display
│ └── settings.js # Settings panel
└── .github/workflows/
└── deploy.yml # GitHub Pages deployment
{
entity: 'Author', // Current entity type
filters: { // Active filters as arrays
'Author': ['James', 'Smith'],
'Year': ['2024']
},
sort_by: 'Publication', // Column to sort by
order: 'desc', // 'asc' or 'desc'
page: 1, // Current page
limit: 50 // Results per### Valid Entity page
}Types
['Author', 'Venue', 'Publication', 'Year', '2020s', '2010s', '2000s', 'Pre2000s']| State Key | URL Parameter |
|---|---|
| entity | ?entity=Author |
| sort_by | &sort_by=Publication |
| order | &order=desc |
| page | &page=1 |
| limit | &limit=50 |
| filters | &filter_Author=James&filter_Author=Smith |
Purpose: Main entry point that ties all modules together.
Key Responsibilities:
- Initializes all modules
- Sets up event listeners for state changes
- Handles data loading
- Manages infinite scroll
Important Functions:
async function init()- Initializes the application
- Sets up event listeners
- Loads initial data
async function loadData(keepScrollPosition)- Fetches data from SPARQL endpoint
- Renders table and filter chips
- Handles scroll position
function setupInfiniteScroll()- Creates intersection observer
- Loads more data when reaching end
Purpose: Central state management with URL synchronization.
Key Functions:
function getInitialState()- Reads URL parameters on page load
- Converts filter params to arrays
- Returns state object
function setState(newState, pushToHistory = true)- Merges new state with current state
- Updates URL (pushState or replaceState)
- Dispatches
stateChangedevent
function buildURLFromState(state)- Converts state to URL query string
- Handles array-based filters
- Returns URL path
Events:
- Listens to
popstatefor browser back/forward navigation
Purpose: Dynamically builds SPARQL queries based on state.
Key Functions:
function buildQuery(state, filterMode = 'label')- Main function to generate SPARQL
- Replaces placeholders: $ENTITY_TYPE, $FILTERS, $ORDER, $LIMIT, $OFFSET
function getLabelVar(entityType)- Returns SPARQL label variable
- Example:
'Author'→'?author_label'
function getUriVar(entityType)- Returns SPARQL URI variable
- Example:
'Author'→'?author_URI'
function buildFilters(filters, filterMode = 'label')- Generates FILTER clauses
- Uses CONTAINS for partial matching
- Example output:
FILTER(CONTAINS(LCASE(?author_label), LCASE("james")) || CONTAINS(LCASE(?author_label), LCASE("smith")))function buildOrder(sort_by, order)- Generates ORDER BY clause
- Example:
'Publication', 'desc'→ORDER BY DESC(?Publication)
Base Query Template: The module contains a SPARQL query that:
- Groups publications by entity (Author, Venue, Year, etc.)
- Counts related publications, venues, authors, years
- Supports decade groupings (2020s, 2010s, 2000s, Pre2000s)
Purpose: Communicates with SPARQL endpoint.
Key Variables:
let SPARQL_ENDPOINT = 'http://webislab33.medien.uni-weimar.de:7018/sparql/';Key Functions:
async function fetchEntities(sparqlQuery)- Sends GET request to SPARQL endpoint
- Returns JSON response
async function fetchWithCount(sparqlQuery)- Fetches main data + count query
- Returns
{ data, total }
function setEndpoint(endpoint)- Updates the SPARQL endpoint URL
Purpose: Dynamic table rendering based on SPARQL response.
Key Constants:
const HIDDEN_COLUMNS = ['URI']; // Columns not displayed
const CLICKABLE_COLUMNS = ['Entity', 'Publication', 'Venue', 'Author', 'Year', '2020s', '2010s', '2000s', 'Pre2000s'];Key Functions:
function renderTable(data, state)- Main rendering function
- Reads
data.head.varsfor columns - Calls renderTableHead and renderTableBody
function renderTableHead(vars, state)- Creates
<thead>from variables - Adds entity change and sort buttons
- Sets data attributes for interactivity
function renderTableBody(vars, bindings, state)- Creates
<tbody>from bindings - Sets data attributes on cells:
data-entity-column: Column namedata-entity-type: Current entity typedata-entity-title: Entity label from Entity column
Event Handlers:
function handleEntityChange(varName)- Called when clicking column header text
- Updates entity in state
function handleSortClick(columnName)- Called when clicking sort arrow
- Toggles asc/desc order
function handleCellClick(event)- Called when clicking table cell
- Uses
data-entity-columnto update entity - Uses
data-entity-typeas filter key - Uses
data-entity-titleas filter value
Purpose: Handles search input.
Key Functions:
function initSearch()- Sets up Enter key listener
function handleSearch(input, searchInside)- Parses comma-separated values
- Uses
data-search-insideas filter key - Adds to filters and triggers update
function updateSearchInside(entityType)- Updates
data-search-insideattribute on search input
Purpose: Displays active filters as removable chips.
Key Functions:
function renderFilterChips(filters)- Renders each filter key in a row
- Values displayed as chips with remove buttons
function removeFilterValue(filterKey, filterIndex)- Removes specific value from filter array
- Dispatches state update
Purpose: Settings panel for SPARQL endpoint configuration.
Key Functions:
function initSettings()- Toggles settings panel visibility
- Handles endpoint input save
User Action
↓
Event Handler (tableView, search, etc.)
↓
window.dispatchEvent('updateState')
↓
setState() in state.js
↓
Update window.AppState
↓
Update URL (pushState)
↓
Dispatch 'stateChanged' event
↓
loadData() in app.js
↓
buildQuery() in queryBuilder.js
↓
fetchWithCount() in fetchService.js
↓
SPARQL Endpoint
↓
renderTable() in tableView.js
↓
Render Filter Chips
↓
Update UI
Dispatched from modules to update state:
window.dispatchEvent(new CustomEvent('updateState', {
detail: {
entity: 'Year', // Optional: change entity
filters: { Author: ['x'] }, // Optional: update filters
sort_by: 'Year', // Optional: change sort
order: 'asc', // Optional: change order
page: 1, // Optional: change page
limit: 50 // Optional: change limit
}
}));Fired after state is updated (triggers data reload):
document.addEventListener('stateChanged', async (event) => {
const currentState = event.detail;
await loadData(false endpointChanged Event
F);
});###ired when user saves new SPARQL endpoint:
window.addEventListener('endpointChanged', (event) => {
setEndpoint(event.detail.endpoint);
loadData(false);
});- Click gear icon
- Enter new endpoint URL
- Click "Save Settings"
- Data reloads from new endpoint
- Add to
VALID_ENTITIESarray inqueryBuilder.js - The dynamic label/URI variables are auto-generated
Edit BASE_SPARQL_TEMPLATE in queryBuilder.js:
const BASE_SPARQL_TEMPLATE = `
PREFIX ex: <https://ir.webis.de/kg/>
SELECT ...
WHERE {
VALUES ?entityType { "$ENTITY_TYPE" }
...
$FILTERS
...
}
GROUP BY ...
$ORDER
LIMIT $LIMIT
OFFSET $OFFSET
`;Placeholders that get replaced:
$ENTITY_TYPE- Current entity (Author, Venue, etc.)$FILTERS- Generated FILTER clauses$ORDER- ORDER BY clause$LIMIT- Results limit$OFFSET- Pagination offset
- Dynamic Columns: Table reads
head.varsfrom SPARQL response, so any SELECT changes are automatically reflected - Array-based Filters: Multiple values per filter key supported
- Case-insensitive Search: Uses
LCASE()andCONTAINS()in SPARQL - URL Sync: All state persisted in URL for shareability
- ES Modules: No bundler needed, works directly in browser
Empty Results:
- Check browser console for SPARQL errors
- Verify endpoint is accessible
CORS Errors:
- Endpoint must allow cross-origin requests
- Consider using a CORS proxy
Filters Not Working:
- Check console for filter generation
- Verify filter key matches entity type