ZDDC/archive/js/url-state.js
2026-06-11 13:32:31 -05:00

224 lines
8 KiB
JavaScript

(function() {
'use strict';
// URL state sync module for ZDDC Archive
// Default values for URL params
var DEFAULT_SORT_FIELD = 'trackingNumber';
var DEFAULT_SORT_DIRECTION = 'asc';
var DEFAULT_ENABLED_TYPES = ['issued', 'received'];
// Map URL param names to state paths
var PARAM_MAP = {
sort: 'sortField',
dir: 'sortDirection',
tn: 'columnFilters.trackingNumber',
ti: 'columnFilters.title',
rv: 'columnFilters.revisions',
types: 'enabledFolderTypes',
gf: 'groupingFilter',
tf: 'transmittalFilter',
projects: 'projectFilter',
show: 'visibleProjects'
};
// Serialize current state to URL query string
function serialize() {
var params = new URLSearchParams();
// Sort field
if (window.app.sortField !== DEFAULT_SORT_FIELD) {
params.set('sort', window.app.sortField);
}
// Sort direction
if (window.app.sortDirection !== DEFAULT_SORT_DIRECTION) {
params.set('dir', window.app.sortDirection);
}
// Column filters
if (window.app.columnFilters.trackingNumber !== '') {
params.set('tn', window.app.columnFilters.trackingNumber);
}
if (window.app.columnFilters.title !== '') {
params.set('ti', window.app.columnFilters.title);
}
if (window.app.columnFilters.revisions !== '') {
params.set('rv', window.app.columnFilters.revisions);
}
// Folder types (only if different from default [issued, received])
var enabledTypes = Array.from(window.app.enabledFolderTypes).sort();
var defaultTypes = DEFAULT_ENABLED_TYPES.slice().sort();
if (JSON.stringify(enabledTypes) !== JSON.stringify(defaultTypes)) {
params.set('types', enabledTypes.join(','));
}
// Grouping filter
if (window.app.groupingFilter !== '') {
params.set('gf', window.app.groupingFilter);
}
// Transmittal filter
if (window.app.transmittalFilter !== '') {
params.set('tf', window.app.transmittalFilter);
}
// Project filter — always preserved if set (for shareable URLs)
if (window.app.projectFilter && window.app.projectFilter.size > 0) {
params.set('projects', Array.from(window.app.projectFilter).join(','));
}
// Visibility filter (project picker). Emit only when it's a strict subset
// of projectFilter — the common "everything visible" case keeps URLs clean.
if (window.app.visibleProjects && window.app.projectFilter
&& window.app.projectFilter.size > 0) {
var pfSize = window.app.projectFilter.size;
var vp = Array.from(window.app.visibleProjects).filter(function(n) {
return window.app.projectFilter.has(n);
});
if (vp.length < pfSize) {
params.set('show', vp.slice().sort().join(','));
}
}
// Build query string
var qs = params.toString();
return qs ? '?' + qs : '';
}
// Push state to URL without triggering popstate
function push() {
var result = serialize();
if (result === location.search) {
return;
}
try {
history.replaceState(null, '', location.pathname + result);
} catch (e) {
// Silently swallow errors (e.g., file:// protocol restrictions)
}
}
// Restore state from URL query string
function restore() {
var params = new URLSearchParams(location.search);
// Restore sort field
if (params.has('sort')) {
var sortValue = params.get('sort');
if (sortValue === 'trackingNumber' || sortValue === 'title') {
window.app.sortField = sortValue;
}
}
// Restore sort direction
if (params.has('dir')) {
var dirValue = params.get('dir');
if (dirValue === 'asc' || dirValue === 'desc') {
window.app.sortDirection = dirValue;
}
}
// Restore column filters with AST parsing
if (params.has('tn')) {
var tnValue = params.get('tn');
window.app.columnFilters.trackingNumber = tnValue;
window.app.columnFilterASTs.trackingNumber = zddc.filter.parse(tnValue);
}
if (params.has('ti')) {
var tiValue = params.get('ti');
window.app.columnFilters.title = tiValue;
window.app.columnFilterASTs.title = zddc.filter.parse(tiValue);
}
if (params.has('rv')) {
var rvValue = params.get('rv');
window.app.columnFilters.revisions = rvValue;
window.app.columnFilterASTs.revisions = zddc.filter.parse(rvValue);
}
// Restore folder types
if (params.has('types')) {
var typesValue = params.get('types');
var typeValues = typesValue.split(',').map(function(t) { return t.trim(); });
// Validate against app.FOLDER_TYPE_NAMES
var validTypes = typeValues.filter(function(t) {
return window.app.FOLDER_TYPE_NAMES.indexOf(t) !== -1;
});
window.app.enabledFolderTypes = new Set(validTypes);
}
// Restore grouping filter
if (params.has('gf')) {
window.app.groupingFilter = params.get('gf');
}
// Restore transmittal filter
if (params.has('tf')) {
window.app.transmittalFilter = params.get('tf');
}
// Restore project filter
if (params.has('projects')) {
var projValue = params.get('projects');
var projNames = projValue.split(',').map(function(p) { return p.trim(); }).filter(Boolean);
window.app.projectFilter = new Set(projNames);
}
// Restore visibility filter. autoConnectHttpSource will intersect against
// projectFilter / availableProjects after the project list resolves, so
// dropping bogus names is handled there. We just parse here.
if (params.has('show')) {
var showValue = params.get('show');
var showNames = showValue.split(',').map(function(p) { return p.trim(); }).filter(Boolean);
window.app.visibleProjects = new Set(showNames);
}
// Update DOM inputs to reflect restored values
updateFilterInputs();
}
// Update DOM filter inputs to match restored state
function updateFilterInputs() {
// Column filter inputs
document.querySelectorAll('.column-filter[data-filter-field]').forEach(function(input) {
var field = input.getAttribute('data-filter-field');
var filterValue = window.app.columnFilters[field] || '';
input.value = filterValue;
if (filterValue !== '') {
input.classList.add('filter-active');
} else {
input.classList.remove('filter-active');
}
});
// Grouping filter
var groupingFilterEl = document.getElementById('groupingFilter');
if (groupingFilterEl) {
groupingFilterEl.value = window.app.groupingFilter;
if (window.app.groupingFilter !== '') {
groupingFilterEl.classList.add('filter-active');
} else {
groupingFilterEl.classList.remove('filter-active');
}
}
// Transmittal filter
var transmittalFilterEl = document.getElementById('transmittalFilter');
if (transmittalFilterEl) {
transmittalFilterEl.value = window.app.transmittalFilter;
if (window.app.transmittalFilter !== '') {
transmittalFilterEl.classList.add('filter-active');
} else {
transmittalFilterEl.classList.remove('filter-active');
}
}
}
// Register module
window.app.modules.urlState = {
serialize: serialize,
push: push,
restore: restore
};
})();