Add global music player, invoicing/payment system, account subpages, mail account admin, and the qualityiron.vegas Jet 2.0 port

Jet interpreter (Tag.cpp, __call.cpp, __imap*.cpp/h, __smtpsend.cpp/h,
__imapemptyfolder.cpp/h - new): assorted parser/tag fixes found across
this work - a stray literal-tag-in-comment and backslash-quote parser
corruption class, a dangling-pointer fix, a __call.cpp fd leak, IMAP
folder-management tags, multi-account/per-host IMAP support, and a
smtpsend Message-ID fix.

barant.com site (jetc/barant-site/):
- A site-wide persistent music player (header band, playlist/queue,
  stereo FFT EQ, session-persisted state) fed by new library/
  ianslibrary/music song-list subpages.
- /account split into info/phones/invoices subpages; a full invoicing
  and Square payment system (create/edit/copy/pay/email invoice);
  admin invoice management and mail-account management subpages.
- /services split into overview/hosting/siptrunking/documents
  subpages, retiring the old standalone pages.
- SIPPHONE.tag: a Jet 2.0 WebRTC softphone tag.
- main.css/spa.js/email.js: the SPA loading indicator, the music
  player band styling, and assorted fixes accumulated alongside this
  work.

qualityiron-site/: the qualityiron.vegas job/contractor/customer/
document management app, ported from its original Jet 1.4 codebase to
Jet 2.0 as barant.coms APIServers second multi-tenant site.

wcwc/wcwc.jet: the WCWC site Jet source (wcwc.jtc build output not
included).

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
Brad ArantandClaude Sonnet 5 committed 2026-10-06 17:42:01 +00:00
1 parent 905fbafa5a
commit b8f7793773
406 files changed
+15803 -587

No files matched your search

+28
View File
@@ -1,6 +1,7 @@
(function() {
var content = document.getElementById('content');
var form = document.getElementById('mainform');
var loadingIndicator = document.getElementById('spa-loading-indicator');
// Lives outside #content (a sibling in the persistent shell, next to
// the menu bar/form) - a fragment swap never touches it on its own,
// so a per-page side menu would otherwise stay frozen on whichever
@@ -220,6 +221,12 @@
function navigate(url, push) {
var sep = url.indexOf('?') === -1 ? '?' : '&';
content.classList.add('spa-fading');
// Opacity transition alone (see the CSS) means a fetch that resolves
// quickly never gets the indicator past a barely-visible opacity
// before this removes the class again - exactly the "do not flash
// this for a fast load" behavior wanted, with no separate delay
// timer needed to achieve it.
if (loadingIndicator) loadingIndicator.classList.add('spa-loading-visible');
Promise.all([
fetch(url + sep + '_fragment=1', { credentials: 'same-origin' }).then(function(r) { return r.text(); }),
delay(180)
@@ -233,6 +240,7 @@
syncActiveTab(finalUrl.split('?')[0]);
}
content.classList.remove('spa-fading');
if (loadingIndicator) loadingIndicator.classList.remove('spa-loading-visible');
});
}
@@ -254,6 +262,7 @@
var subpage = fd.get('SUBPAGE') || '';
var url = formPrefix + '/' + page + (subpage ? '/' + subpage : '');
content.classList.add('spa-fading');
if (loadingIndicator) loadingIndicator.classList.add('spa-loading-visible');
Promise.all([
fetch(url + '?_fragment=1', {
method: 'POST',
@@ -271,6 +280,7 @@
syncActiveTab(finalUrl.split('?')[0]);
}
content.classList.remove('spa-fading');
if (loadingIndicator) loadingIndicator.classList.remove('spa-loading-visible');
});
});
}
@@ -279,6 +289,24 @@
navigate(window.location.pathname + window.location.search, false);
});
// Instant visual feedback on a side-menu click - without this, the
// active-item highlight only updated once applyResponse() swapped in
// the new #_spa_panelmenu markup after the fetch finished, which read
// as unresponsive (the click did nothing visible until the whole
// subpage had already loaded). That server-rendered swap is still the
// authoritative source of truth and will correct this on arrival (e.g.
// if a click ends up redirected somewhere else entirely), so this is
// purely an optimistic bridge, not a second source of truth.
if (panelMenu) {
panelMenu.addEventListener('click', function(e) {
var a = e.target.closest('a');
if (!a || !panelMenu.contains(a)) return;
var current = panelMenu.querySelector('.submenuitem-active');
if (current) current.classList.remove('submenuitem-active');
a.classList.add('submenuitem-active');
});
}
// Drag-to-resize for the side menu (#panelmenu) against the main
// content column - the resizer bar (#panelmenu-resizer) lives in the
// persistent shell right alongside panelMenu itself (see its own