PageSourceSearch

https://quorumapp.com/ts/BayesBot.js

js quorumapp.com collected 2026-09-26 04:38:45 UTC 316,716 bytes, 6,313 lines download raw bytes

1/// <reference path="references.d.ts" />
2/// <reference path="application.ts" />
3/// <reference path="AutoSaveHelper.ts" />
4var __assign = (this && this.__assign) || function () {
5    __assign = Object.assign || function(t) {
6        for (var s, i = 1, n = arguments.length; i < n; i++) {
7            s = arguments[i];
8            for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p))
9                t[p] = s[p];
10        }
11        return t;
12    };
13    return __assign.apply(this, arguments);
14};
15var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
16    function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
17    return new (P || (P = Promise))(function (resolve, reject) {
18        function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
19        function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
20        function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
21        step((generator = generator.apply(thisArg, _arguments || [])).next());
22    });
23};
24var __generator = (this && this.__generator) || function (thisArg, body) {
25    var _ = { label: 0, sent: function() { if (t[0] & 1) throw t[1]; return t[1]; }, trys: [], ops: [] }, f, y, t, g;
26    return g = { next: verb(0), "throw": verb(1), "return": verb(2) }, typeof Symbol === "function" && (g[Symbol.iterator] = function() { return this; }), g;
27    function verb(n) { return function (v) { return step([n, v]); }; }
28    function step(op) {
29        if (f) throw new TypeError("Generator is already executing.");
30        while (g && (g = 0, op[0] && (_ = 0)), _) try {
31            if (f = 1, y && (t = op[0] & 2 ? y["return"] : op[0] ? y["throw"] || ((t = y["return"]) && t.call(y), 0) : y.next) && !(t = t.call(y, op[1])).done) return t;
32            if (y = 0, t) op = [op[0] & 2, t.value];
33            switch (op[0]) {
34                case 0: case 1: t = op; break;
35                case 4: _.label++; return { value: op[1], done: false };
36                case 5: _.label++; y = op[1]; op = [0]; continue;
37                case 7: op = _.ops.pop(); _.trys.pop(); continue;
38                default:
39                    if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) { _ = 0; continue; }
40                    if (op[0] === 3 && (!t || (op[1] > t[0] && op[1] < t[3]))) { _.label = op[1]; break; }
41                    if (op[0] === 6 && _.label < t[1]) { _.label = t[1]; t = op; break; }
42                    if (t && _.label < t[2]) { _.label = t[2]; _.ops.push(op); break; }
43                    if (t[2]) _.ops.pop();
44                    _.trys.pop(); continue;
45            }
46            op = body.call(thisArg, _);
47        } catch (e) { op = [6, e]; y = 0; } finally { f = t = 0; }
48        if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true };
49    }
50};
51import { Distiller } from './application.js';
52import { AutoSave } from './AutoSaveHelper.js';
53import { hideBayesNetStateTooltip, moveBayesNetStateTooltip, showBayesNetStateTooltip } from './BayesNet/TreeNodeMultiline.js';
54var BAYES_BOT_HEADER_OPEN = '[[';
55var BAYES_BOT_HEADER_CLOSE = ']]';
56var BAYES_BOT_CREATE_HEADER_MARKUP = 'Create a [[Bayes net]] out of a description of the future.';
57var BAYES_BOT_DEFAULT_MODEL = 'gpt-5.6-luna';
58var BAYES_BOT_MODEL_CHOICES = [
59    { id: 'gpt-5.6-luna', label: 'GPT-5.6 Luna', disabled: false },
60    { id: 'gpt-5.6-sol', label: 'GPT-5.6 Sol', disabled: false },
61    { id: 'gpt-6-astra', label: 'GPT-6 Astra', disabled: true },
62];
63function bayesBotIsoDate(value) {
64    if (value == null || value === '') {
65        return '';
66    }
67    if (value instanceof Date && !isNaN(value.getTime())) {
68        var utcMidnight = value.getUTCHours() === 0 &&
69            value.getUTCMinutes() === 0 &&
70            value.getUTCSeconds() === 0;
71        var year = utcMidnight ? value.getUTCFullYear() : value.getFullYear();
72        var month = (utcMidnight ? value.getUTCMonth() : value.getMonth()) + 1;
73        var day = utcMidnight ? value.getUTCDate() : value.getDate();
74        var pad = function (n) { return (n < 10 ? '0' + n : String(n)); };
75        return year + '-' + pad(month) + '-' + pad(day);
76    }
77    var match = String(value).trim().match(/^(\d{4}-\d{2}-\d{2})/);
78    return match ? match[1] : '';
79}
80var BAYES_BOT_BAYES_NET_INFO = '<div style="font-size:13px;line-height:1.5;font-family:Inter,-apple-system,BlinkMacSystemFont,Segoe UI,sans-serif;">'
81    + 'A probabilistic model of a forecasting question. Each node is an observable event; '
82    + 'an arrow means learning the parent should update the child. The graph stays open so later evidence can move the primary forecast.'
83    + '</div>';
84function isLegacyHormuzPlaceholder(value) {
85    var plain = stripBayesBotHeaderMarkup(String(value || '')).trim().toLowerCase();
86    return plain.indexOf('strait of hormuz') >= 0 && plain.indexOf('return to normal') >= 0;
87}
88var BAYES_BOT_AGENT_EXAMPLE_PROMPTS = [
89    'AstraBot go to this link and propose a node that will top the leaderboard. Find a very early, super diagnostic signal. It\'s hard.',
90    'hey claude go to this URL and explain how information flows through the model in plain english.',
91    'FutureSearch go to the URL below and audit the forecasts in this model.'
92];
93var BAYES_BOT_CREATE_AGENT_EXAMPLE_PROMPTS = [
94    'AstraBot, go to the following link and convert the attached Dwarkesh podcast transcript into a bayes net.',
95    'FutureSearch please render Cowen\'s argument in the attached article in bayes net form here',
96    'hey claude what\'s a good p(doom) for someone who\'s just getting into dooming? make a bayes net on this site to supp
96ort your view.'
97];
98var BAYES_BOT_AUTH_PROPOSE_MESSAGE = 'Please sign in or create an account to start contributing to this Bayes net';
99var BAYES_BOT_AUTH_CREATE_MESSAGE = 'Please sign in or create an account to generate a Bayes net';
100var BAYES_BOT_AUTH_AGENT_MESSAGE = 'Please sign in or create an account to connect your agent to this session.';
101var BAYES_BOT_AUTH_BRANCH_MESSAGE = 'Please sign in or create an account to merge or clone a branch';
102function bayesBotAgentConnectOrigin(hostname, port) {
103    var host = String(hostname || '').trim() || 'localhost';
104    var hostLower = host.toLowerCase();
105    var isLocal = hostLower === 'localhost' ||
106        hostLower === '127.0.0.1' ||
107        hostLower === '[::1]' ||
108        hostLower === '::1';
109    var rawPort = port != null ? String(port) : '';
110    if (isLocal) {
111        var portPart_1 = rawPort && rawPort !== '80' ? ':' + rawPort : '';
112        return 'http://' + host + portPart_1;
113    }
114    var portPart = rawPort && rawPort !== '443' ? ':' + rawPort : '';
115    return 'https://' + host + portPart;
116}
117function stripBayesBotHeaderMarkup(markup) {
118    return String(markup || '').split(BAYES_BOT_HEADER_OPEN).join('').split(BAYES_BOT_HEADER_CLOSE).join('');
119}
120function parseBayesBotHeaderMarkup(markup) {
121    var parts = [];
122    var remaining = String(markup || '');
123    while (remaining.length) {
124        var open_1 = remaining.indexOf(BAYES_BOT_HEADER_OPEN);
125        if (open_1 < 0) {
126            parts.push({ text: remaining, clickable: false });
127            break;
128        }
129        if (open_1 > 0) {
130            parts.push({ text: remaining.slice(0, open_1), clickable: false });
131        }
132        var close_1 = remaining.indexOf(BAYES_BOT_HEADER_CLOSE, open_1 + BAYES_BOT_HEADER_OPEN.length);
133        if (close_1 < 0) {
134            parts.push({ text: remaining.slice(open_1 + BAYES_BOT_HEADER_OPEN.length), clickable: false });
135            break;
136        }
137        parts.push({
138            text: remaining.slice(open_1 + BAYES_BOT_HEADER_OPEN.length, close_1),
139            clickable: true,
140        });
141        remaining = remaining.slice(close_1 + BAYES_BOT_HEADER_CLOSE.length);
142    }
143    return parts.filter(function (p) { return p.text.length > 0; });
144}
145function bayesBotHeaderInfoHtml(title, body) {
146    var escapeHtml = function (value) {
147        return String(value == null ? '' : value)
148            .replace(/&/g, '&amp;')
149            .replace(/</g, '&lt;')
150            .replace(/>/g, '&gt;')
151            .replace(/"/g, '&quot;')
152            .replace(/'/g, '&#39;');
153    };
154    var safeTitle = escapeHtml(title);
155    var safeBody = escapeHtml(body);
156    return '<div style="font-size:13px;line-height:1.5;font-family:Inter,-apple-system,BlinkMacSystemFont,Segoe UI,sans-serif;">'
157        + '<span style="font-family:Inter,sans-serif;font-weight:600;color:#3B82F6;">' + safeTitle + '</span> '
158        + safeBody
159        + '</div>';
160}
161var BayesBot = /** @class */ (function () {
162    function BayesBot(params) {
163        var _this = this;
164        var _a, _b;
165        this.loadingSteps = ko.observable(false);
166        this.currentStageWorkflow = ko.observable(null);
167        this.questionSet = ko.observable(null);
168        this.workflowUndefined = ko.observable(false);
169        /** BayesBot only renders bayes-net workflow elements (interactive or readonly). */
170        this.isBayesNetElement = function (element) {
171            var _a, _b;
172            if (!element) {
173                return false;
174            }
175            var view = typeof element.view === 'function' ? element.view() : element.view;
176            if (view === 'bayes-net' || view === 'bayes-net-results') {
177                return true;
178            }
179            if (element.type === 'bayes-net-readonly') {
180                return true;
181            }
182            var generalType = typeof element.generalQuestionType === 'function'
183                ? (_a = element.generalQuestionType()) === null || _a === void 0 ? void 0 : _a.value
184                : (_b = element.generalQuestionType) === null || _b === void 0 ? void 0 : _b.value;
185            return generalType === 'bayesNet';
186        };
187        this.nodeProposalText = ko.observable('');
188        this.nodeProposalSubmitting = ko.observable(false);
189        this.lastBayesBotJobId = ko.observable(null);
190        this.proposalPhase = ko.observable('idle');
191        this.submittedProposalText = ko.observable('');
192        this.proposalStatusText = ko.observable('');
193        this.displayedProposalStatusText = ko.observable('');
194        this.proposalStatusAnimating = ko.observable(false);
195        this.proposalStatusFreezeWidth = ko.observable('');
196        this.proposalFinalResponse = ko.observable('');
197        this.proposalInfoValue = ko.observable(null);
198        this.proposalLifetimeInformation = ko.observable(null);
199        this.proposalSpecNotes = ko.observableArray([]);
200        this.showCreateNetLifetimeScore = ko.observable(false);
201        this.addedSignalExplainerOpen = ko.observable(false);
202        this.addedSignalExplainerRow = ko.observable(null);
203        this.shannonExplainerOpen = ko.observable(false);
204        this.leaderboardPanelTab = ko.observable('leaderboard');
205        this.specLogRows = ko.observableArray([]);
206        this.leaderboardNodes = ko.observableArray([]);
207        this.leaderboardRankOf = ko.observable(0);
208        this.leaderboardPrimaryNodeId = ko.observable(null);
209        this.selectedCanvasNodeId = ko.observable(null);
210        this.pinnedLeaderboardNodeId = ko.observable(null);
211        this.leaderboardScrollFrame = null;
212        this.workbenchHeightPx = ko.observable(135);
213        this.proposeShare = ko.observable(0.5);
214        this.clearingNet = ko.observable(false);
215        this.showClearNetDevButton = false;
216        this.isCreateMode = ko.observable(false);
217        this.authModalOpen = ko.observable(false);
218        this.authModalMode = ko.observable('signup');
219        this.authModalContext = ko.observable('header');
220        this.authUsername = ko.observable('');
221        this.authEmail = ko.observable('');
222        this.authPassword = ko.observable('');
223        this.authError = ko.observable('');
224        this.authSubmitting = ko.observable(false);
225        this.myModelsMenuOpen = ko.observable(false);
226        this.publicModelsMenuOpen = ko.observable(false);
227        this.myModels = ko.observableArray([]);
228        this.myModelsQuery = ko.observable('');
229        this.myModelsLoading = ko.observable(false);
230        this.myModelsHasMore = ko.observable(false);
231        this.myModelsLoaded = ko.observable(false);
232        this.publicModelGroups = ko.observableArray([]);
233        this.publicModelsLoading = ko.observable(false);
234        this.publicModelsLoaded = ko.observable(false);
235        this.myModelsOffset = 0;
236        this.myModelsSearchTimer = null;
237        this.myModelsRequestToken = 0;
238        this.topMenuOpen = ko.pureComputed(function () {
239            return _this.myModelsMenuOpen() || _this.publicModelsMenuOpen();
240        });
241        this.myModelsEmptyMessage = ko.pureComputed(function () {
242            if (!_this.myModelsLoaded() || _this.myModels().length > 0 || _this.myModelsLoading()) {
243                return '';
244            }
245            var q = String(_this.myModelsQuery() || '').trim();
246            if (q) {
247                return 'No models match that search.';
248            }
249            return "You don't have any models yet.";
250        });
251        this.isTempVisitor = ko.pureComputed(function () {
252            var user = Distiller.currentUser && Distiller.currentUser();
253            if (!user) {
254                return false;
255            }
256            if (Distiller.isBayesBotTempUser) {
257                return Distiller.isBayesBotTempUser();
258            }
259            var email = user.email != null ? String(user.email) : '';
260            return !email || email.indexOf('@') < 0;
261        });
262        this.bayesBotModelChoices = BAYES_BOT_MODEL_CHOICES;
263        this.selectedBayesBotModel = ko.observable(BAYES_BOT_DEFAULT_MODEL);
264        this.bayesBotModelMenuOpen = ko.observable(false);
265        this.selectedBayesBotModelLabel = ko.pureComputed(function () {
266            var id = _this.selectedBayesBotModel();
267            for (var i = 0; i < _this.bayesBotModelChoices.length; i++) {
268                if (_this.bayesBotModelChoices[i].id === id) {
269                    return _this.bayesBotModelChoices[i].label;
270                }
271            }
272            return 'GPT-5.6 Luna';
273        });
274        this.authContextMessage = ko.pureComputed(function () {
275            var context = _this.authModalContext();
276            if (context === 'create') {
277                return BAYES_BOT_AUTH_CREATE_MESSAGE;
278            }
279            if (context === 'propose') {
280                return BAYES_BOT_AUTH_PROPOSE_MESSAGE;
281            }
282            if (context === 'agent') {
283                return BAYES_BOT_AUTH_AGENT_MESSAGE;
284            }
285            if (context === 'branch') {
286                return BAYES_BOT_AUTH_BRANCH_MESSAGE;
287            }
288            return '';
289        });
290        this.draftProvisioning = ko.observable(false);
291        this.draftProvisionError = ko.observable('');
292        this.draftNeedsReplacement = ko.observable(false);
293        this.boundCommunityId = ko.observable(null);
294        this.boundQuestionText = ko.observable('');
295        this.boundResolutionCriteria = ko.observable('');
296        this.boundResolutionDate = ko.observable('');
297        this.headerMarkup = ko.observable('');
298        this.displayedHeaderText = ko.observable('');
299        this.headerAnimating = ko.observable(false);
300        this.headerAnimationComplete = ko.observable(true);
301        this.headerInfoHtml = ko.observable('');
302        this.headerTypewriterTimer = null;
303        this.headerTypewriterToken = 0;
304        /** Canvas user. Authors edit the shared template; other visitors get a personal copy on write. */
305        this.viewerUserId = Distiller.currentUser && Distiller.currentUser()
306            ? Distiller.currentUser().userId
307            : null;
308        this.viewingSharedTemplate = true;
309        this.liveTemplateBuild = false;
310        /** True when this visitor authored the template and should edit it in place. */
311        this.ownsCanonicalTemplate = false;
312        this.authorOwnsTemplate = ko.observable(false);
313        this.personalCopyNetId = null;
314        this.cloneReady = false;
315        this.clonePromise = null;
316        this.preparingCloneForEdits = false;
317        this.sharedTemplateNetId = ko.observable(null);
318        this.canvasMountedForTemplateNetId = null;
319        this.authorBayesNetSet = ko.observable(null);
320        this.authorBayesNet = null;
321        this.bayesBotEventSource = null;
322        this.bayesBotSseUserId = null;
323        this.bayesBotSseReconnectTimer = null;
324        this.bayesBotPollTimer = null;
325        this.lifetimeInfoChart = null;
326        this.pendingBayesBotGraphEvents = [];
327        this.statusTypewriterTimer = null;
328        this.statusTypewriterToken = 0;
329        this.proposalJobSettled = false;
330        this.proposePanelTab = ko.observable('propose');
331        this.agentKey = ko.observable(null);
332        this.agentStudyBaseUrl = ko.observable(null);
333        this.agentKeyLoading = ko.observable(false);
334        this.agentConnectCopied = ko.observable(false);
335        this.displayedAgentExampleText = ko.observable('');
336        this.agentExampleAnimating = ko.observable(false);
337        this.agentActivityLive = ko.observable(false);
338        this.agentIsConnected = ko.observable(false);
339        this.agentActivitySpinning = ko.observable(false);
340        this.agentActivitySpinnerFrame = ko.observable('⣾');
341        this.agentConnectBrailleCells = ko.observableArray([]);
342        this.agentActivityLatestText = ko.observable('');
343        this.agentActivityLines = ko.observableArray([]);
344        this.agentKeyReady = null;
345        this.agentConnectCopiedTimer = null;
346        this.agentExampleTimer = null;
347        this.agentExampleToken = 0;
348        this.agentExampleIndex = 0;
349        this.agentActivitySpinnerTimer = null;
350        this.agentActivitySpinnerTick = 0;
351        this.agentTerminalStickToBottom = true;
352        this.isProposalStatusFill = ko.pureComputed(function () {
353            var phase = _this.proposalPhase();
354            return phase === 'processing' || phase === 'error' || phase === 'complete';
355        });
356        this.showProposeComposer = ko.pureComputed(function () {
357            return _this.proposePanelTab() === 'propose' && !_this.isProposalStatusFill();
358        });
359        this.showProposalStatusCard = ko.pureComputed(function () {
360            return _this.proposePanelTab() === 'propose' && !!(_this.submittedProposalText() || _this.isProposalStatusFill());
361        });
362        this.showAgentConnect = ko.pureComputed(function () {
363            return _this.proposePanelTab() === 'connect';
364        });
365        this.showConnectTabSpinner = ko.pureComputed(function () {
366            return _this.agentIsConnected() && _this.proposePanelTab() === 'connect';
367        });
368        this.proposePanelTitle = ko.pureComputed(function () {
369            return _this.isCreateMode() ? 'Describe the future' : 'Propose a node';
370        });
371        this.selectProposeTab = function () {
372            _this.exitBranchView();
373            _this.proposePanelTab('propose');
374            _this.stopAgentExampleRotation();
375        };
376        this.selectConnectTab = function () {
377            _this.exitBranchView();
378            _this.proposePanelTab('connect');
379            if (!_this.agentActivityLive()) {
380                _this.startAgentExampleRotation();
381            }
382        };
383        this.showBranchTab = ko.pureComputed(function () {
384            return !_this.isCreateMode();
385        });
386        this.showBranchList = ko.pureComputed(function () {
387            return _this.proposePanelTab() === 'branches';
388        });
389        this.branchRows = ko.observableArray([]);
390        this.selectedBranchCopyNetId = ko.observable(null);
391        this.viewingBranchCopyNetId = ko.observable(null);
392        this.canonicalLifetimeInformation = null;
393        this.branchShareCopied = ko.observable(false);
394        this.mergeModalOpen = ko.observable(false);
395        this.branchActionMode = ko.observable('merge');
396        this.mergePhase = ko.observable('choose');
397        this.mergeError = ko.observable('');
398        this.mergeStatusText = ko.observable('');
399        this.displayedMergeStatusText = ko.observable('');
400        this.mergeStatusAnimating = ko.observable(false);
401        this.mergeInFlight = ko.observable(false);
402        this.mergeBarAuthorName = ko.observable('');
403        this.scoresStatus = ko.observable('');
404        this.mergeDoneTimer = null;
405        this.branchShareCopiedTimer = null;
406        this.mergeForecastPending = false;
407        this.mergeFinishStarted = false;
408        this.mergeStatusJobId = '';
409        this.pendingMergeResult = null;
410        this.mergeStatusTypewriterToken = 0;
411        this.mergeStatusTypewriterTimer = null;
412        this.viewingBranchAuthorName = ko.pureComputed(function () {
413            var id = _this.selectedBranchCopyNetId();
414            if (id == null) {
415                return '';
416            }
417            var rows = _this.branchRows();
418            for (var i = 0; i < rows.length; i++) {
419                if (rows[i] && rows[i].copyNetId === id) {
420                    if (rows[i].isMain) {
421                        return 'Main';
422                    }
423                    return rows[i].authorName ? String(rows[i].authorName) : 'Unknown';
424                }
425            }
426            return '';
427        });
428        this.showBranchInvite = ko.pureComputed(function () {
429            if (!_this.authorOwnsTemplate()) {
430                return false;
431            }
432            var rows = _this.branchRows();
433            for (var i = 0; i < rows.length; i++) {
434                if (rows[i] && !rows[i].isMain) {
435                    return false;
436                }
437            }
438            return true;
439        });
440        this.branchCanvasBarAuthorName = ko.pureComputed(function () {
441            var viewing = _this.viewingBranchAuthorName();
442            if (viewing) {
443                return viewing;
444            }
445            return _this.mergeInFlight() ? _this.mergeBarAuthorName() : '';
446        });
447        this.branchCanvasBarVisible = ko.pureComputed(function () {
448            return !!_this.branchCanvasBarAuthorName();
449        });
450        this.showMergeBarStatus = ko.pureComputed(function () {
451            return (_this.mergeInFlight() &&
452                !_this.mergeModalOpen() &&
453                !!(_this.displayedMergeStatusText() || _this.mergeStatusAnimating() || _this.mergeStatusText()));
454        });
455        this.branchActionTitle = ko.pureComputed(function () {
456            var cloning = _this.branchActionMode() === 'clone';
457            var phase = _this.mergePhase();
458            if (phase === 'working') {
459                return cloning ? 'Cloning this branch…' : 'Merging this branch…';
460            }
461            if (phase === 'done') {
462                return cloning ? 'Cloned.' : 'Merged.';
463            }
464            return cloning ? 'Clone this branch?' : 'Merge this branch?';
465        });
466        this.branchActionExplanation = ko.pureComputed(function () {
467            if (_this.branchActionMode() === 'clone') {
468                return 'Clone gives you a perfect copy of this branch and archives the version you are on. Your current version is replaced by this copy.';
469            }
470            return 'Merge is strictly additive. It copies your current version, then adds nodes from this branch that you do not already have. It does not remove nodes that exist only on your version.';
471        });
472        this.exitBranchView = function () {
473            if (!_this.selectedBranchCopyNetId()) {
474                return;
475            }
476            _this.selectedBranchCopyNetId(null);
477            _this.viewingBranchCopyNetId(null);
478            _this.refreshBranchRowSelection();
479            _this.restoreOwnNetView();
480        };
481        this.branchShareUrl = ko.pureComputed(function () {
482            var sid = _this.resolveBoundCommunityId();
483            if (!sid) {
484                return '';
485            }
486            return window.location.origin + '?sid=' + sid;
487        });
488        this.selectBranchesTab = function () {
489            _this.proposePanelTab('branches');
490            _this.stopAgentExampleRotation();
491            _this.loadBranchList();
492            _this.scheduleBranchListWorkbenchHeight();
493        };
494        this.branchTabTooltip = ko.pureComputed(function () {
495            return _this.branchRows().length ? 'Branches' : 'Share this page';
496        });
497        this.onBranchIconPointerEnter = function (_data, event) {
498            showBayesNetStateTooltip(_this.branchTabTooltip(), event);
499        };
500        this.onLogsIconPointerEnter = function (_data, event) {
501            showBayesNetStateTooltip('Logs', event);
502        };
503        this.onWorkbenchIconPointerMove = function (_data, event) {
504            moveBayesNetStateTooltip(event);
505        };
506        this.onWorkbenchIconPointerLeave = function () {
507            hideBayesNetStateTooltip();
508        };
509        this.branchAvatarColor = function (userId) {
510            var colors = Distiller.colorsArray && Distiller.colorsArray();
511            if (!colors || !colors.length) {
512                return '#8898AA';
513            }
514            var id = parseInt(String(userId), 10) || 0;
515            if (id < colors.length) {
516                return colors[id];
517            }
518            return colors[id % colors.length];
519        };
520        this.mapBranchListRow = function (row, selectedId) {
521            var added = parseInt(String(row.addedNodeCount), 10) || 0;
522            var removed = parseInt(String(row.removedNodeCount), 10) || 0;
523            var forecasts = parseInt(String(row.addedForecastCount), 10) || 0;
524            var copyNetId = parseInt(String(row.copyNetId), 10);
525            var copyUserId = parseInt(String(row.copyUserId), 10) || 0;
526            var authorName = row.authorName ? String(row.authorName) : 'Unknown';
527            var isMain = !!row.isMain;
528            var isCurrent = !!row.isCurrent;
529            return {
530                copyNetId: copyNetId,
531                copyUserId: copyUserId,
532                authorName: authorName,
533                isMain: isMain,
534                isCurrent: isCurrent,
535                showActions: !isCurrent,
536                avatarInitials: Distiller.avatarInitials
537                    ? Distiller.avatarInitials(isMain ? 'Main' : authorName)
538                    : (isMain ? 'M' : authorName.slice(0, 2).toUpperCase()),
539                avatarColor: _this.branchAvatarColor(copyUserId),
540                addedNodeCount: added,
541                removedNodeCount: removed,
542                addedForecastCount: forecasts,
543                addedLabel: '+' + added + (added === 1 ? ' node' : ' nodes'),
544                removedLabel: '-' + removed + (removed === 1 ? ' node' : ' nodes'),
545                forecastLabel: '+' + forecasts + (forecasts === 1 ? ' forecast' : ' forecasts'),
546                mergedLabel: !isMain && row.lastMergedAt
547                    ? 'Merged on ' + _this.formatSpecLogTime(row.lastMergedAt)
548                    : '',
549                isSelected: selectedId != null && selectedId === copyNetId,
550            };
551        };
552        this.loadBranchList = function () {
553            var currentUser = Distiller.currentUser && Distiller.currentUser();
554            var userId = currentUser && currentUser.userId;
555            if (!userId || _this.isCreateMode()) {
556                return;
557            }
558            Distiller.httpGetAsync(Distiller.apiEndpoint + '/api/bayesBotBranches/' + userId
559                + '?communityId=' + encodeURIComponent(String(_this.resolveBoundCommunityId())), function (responseText) {
560                try {
561                    var parsed = typeof responseText === 'string' ? JSON.parse(responseText) : responseText;
562                    if (!parsed || parsed.ok === false) {
563                        return;
564                    }
565                    var selectedId = _this.selectedBranchCopyNetId();
566                    var rows = [];
567                    if (parsed.main && parsed.main.copyNetId) {
568                        rows.push(_this.mapBranchListRow(parsed.main, selectedId));
569                    }
570                    var branches = parsed.branches || [];
571                    for (var i = 0; i < branches.length; i++) {
572                        rows.push(_this.mapBranchListRow(branches[i], selectedId));
573                    }
574                    _this.branchRows(rows);
575                    _this.scheduleBranchListWorkbenchHeight();
576                }
577                catch (e) {
578                    console.error('BayesBot branch list parse failed:', e);
579                }
580            });
581        };
582        this.selectBranchRow = function (row) {
583            var copyNetId = row && row.copyNetId != null ? parseInt(String(row.copyNetId), 10) : NaN;
584            if (!Number.isFinite(copyNetId) || copyNetId <= 0) {
585                return;
586            }
587            if (row.isCurrent) {
588                _this.exitBranchView();
589                return;
590            }
591            if (_this.selectedBranchCopyNetId() === copyNetId) {
592                _this.exitBranchView();
593                return;
594            }
595            _this.selectedBranchCopyNetId(copyNetId);
596            _this.refreshBranchRowSelection();
597            _this.inspectBranch(copyNetId);
598        };
599        this.refreshBranchRowSelection = function () {
600            var selectedId = _this.selectedBranchCopyNetId();
601            var rows = _this.branchRows().map(function (row) { return (__assign(__assign({}, row), { isSelected: selectedId != null && row.copyNetId === selectedId })); });
602            _this.branchRows(rows);
603        };
604        this.inspectBranch = function (copyNetId) {
605            var currentUser = Distiller.currentUser && Distiller.currentUser();
606            var userId = currentUser && currentUser.userId;
607            if (!userId) {
608                return;
609            }
610            Distiller.httpGetAsync(Distiller.apiEndpoint + '/api/bayesBotBranchView/' + userId + '/' + copyNetId
611                + '?communityId=' + encodeURIComponent(String(_this.resolveBoundCommunityId())), function (responseText) {
612                try {
613                    var parsed = typeof responseText === 'string' ? JSON.parse(responseText) : responseText;
614                    if (!parsed || parsed.ok === false || !parsed.bayesNet) {
615                        return;
616                    }
617                    _this.rememberCanonicalLifetime();
618                    _this.viewingBranchCopyNetId(copyNetId);
619                    var net = _this.authorBayesNet;
620                    if (net && typeof net.load === 'function') {
621                        net.netId = copyNetId;
622                        net.load(parsed.bayesNet);
623                    }
624                    if (parsed.leaderboard && parsed.leaderboard.ok !== false) {
625                        _this.leaderboardPrimaryNodeId(parsed.leaderboard.primaryNodeId || null);
626                        _this.applyLeaderboardNodes(parsed.leaderboard.nodes || [], parsed.leaderboard.rankOf);
627                        _this.applyPrimaryNodeHighlight();
628                    }
629                    else {
630                        _this.loadLeaderboard();
631                    }
632                    if (_this.lifetimeHasChart(parsed.lifetimeInformation)) {
633                        _this.presentPersistedLifetimeInformation(parsed.lifetimeInformation);
634                    }
635                }
636                catch (e) {
637                    console.error('BayesBot branch view parse failed:', e);
638                }
639            });
640        };
641        this.lifetimeHasChart = function (lifetimeInformation) {
642            return !!(lifetimeInformation &&
643                Array.isArray(lifetimeInformation.breakpoints) &&
644                lifetimeInformation.breakpoints.length);
645        };
646        this.rememberCanonicalLifetime = function () {
647            if (_this.viewingBranchCopyNetId()) {
648                return;
649            }
650            var current = _this.proposalLifetimeInformation();
651            if (_this.lifetimeHasChart(current)) {
652                _this.canonicalLifetimeInformation = current;
653            }
654        };
655        this.restoreOwnNetView = function () {
656            _this.viewingBranchCopyNetId(null);
657            void _this.reloadAuthorBayesNetGraph();
658            _this.loadLeaderboard();
659            if (_this.lifetimeHasChart(_this.canonicalLifetimeInformation)) {
660                _this.presentPersistedLifetimeInformation(_this.canonicalLifetimeInformation);
661            }
662        };
663        this.openMergeModal = function (row, event) {
664            _this.openBranchActionModal(row, event, 'merge');
665        };
666        this.openCloneModal = function (row, event) {
667            _this.openBranchActionModal(row, event, 'clone');
668        };
669        this.openBranchActionModal = function (row, event, mode) {
670            if (event && event.stopPropagation) {
671                event.stopPropagation();
672            }
673            if (_this.isTempVisitor()) {
674                _this.openAuthModal('branch');
675                return;
676            }
677            if (_this.mergeInFlight()) {
678                _this.mergePhase('working');
679                _this.mergeModalOpen(true);
680                return;
681            }
682            if (row && row.copyNetId != null) {
683                _this.selectedBranchCopyNetId(parseInt(String(row.copyNetId), 10));
684                _this.refreshBranchRowSelection();
685            }
686            _this.branchActionMode(mode === 'clone' ? 'clone' : 'merge');
687            _this.mergePhase('choose');
688            _this.mergeError('');
689            _this.clearMergeStatusPresentation();
690            _this.mergeModalOpen(true);
691        };
692        this.closeMergeModal = function () {
693            if (_this.mergePhase() === 'done') {
694                return;
695            }
696            if (_this.mergePhase() === 'working' && _this.mergeInFlight()) {
697                _this.mergeModalOpen(false);
698                return;
699            }
700            _this.mergeModalOpen(false);
701            _this.mergePhase('choose');
702            _this.mergeError('');
703            _this.clearMergeStatusPresentation();
704        };
705        this.clearMergeStatusPresentation = function () {
706            _this.mergeStatusTypewriterToken += 1;
707            if (_this.mergeStatusTypewriterTimer) {
708                clearTimeout(_this.mergeStatusTypewriterTimer);
709                _this.mergeStatusTypewriterTimer = null;
710            }
711            _this.mergeStatusAnimating(false);
712            _this.mergeStatusText('');
713            _this.displayedMergeStatusText('');
714        };
715        this.setMergeStatusText = function (next) {
716            var target = next == null ? '' : String(next);
717            _this.mergeStatusText(target);
718            if (_this.displayedMergeStatusText() === target && !_this.mergeStatusAnimating()) {
719                return;
720            }
721            var token = ++_this.mergeStatusTypewriterToken;
722            if (_this.mergeStatusTypewriterTimer) {
723                clearTimeout(_this.mergeStatusTypewriterTimer);
724                _this.mergeStatusTypewriterTimer = null;
725            }
726            var hadText = _this.displayedMergeStatusText().length > 0;
727            _this.mergeStatusAnimating(hadText);
728            var reveal = function () {
729                if (token !== _this.mergeStatusTypewriterToken) {
730                    return;
731                }
732                _this.mergeStatusAnimating(false);
733                _this.displayedMergeStatusText(target);
734                _this.mergeStatusTypewriterTimer = null;
735            };
736            if (!hadText) {
737                reveal();
738                return;
739            }
740            var backspace = function () {
741                if (token !== _this.mergeStatusTypewriterToken) {
742                    return;
743                }
744                var shown = _this.displayedMergeStatusText();
745                if (shown.length > 0) {
746                    _this.displayedMergeStatusText(shown.slice(0, -1));
747                    _this.mergeStatusTypewriterTimer = setTimeout(backspace, 22);
748                    return;
749                }
750                _this.mergeStatusTypewriterTimer = setTimeout(reveal, 80
750);
751            };
752            backspace();
753        };
754        this.mergeResponseStillWorking = function (parsed) {
755            if (!parsed || _this.branchActionMode() === 'clone') {
756                return false;
757            }
758            return parsed.forecastMode === 'generate' || parsed.scoresStatus === 'filling_forecasts';
759        };
760        this.rememberPendingMerge = function (parsed) {
761            var prev = _this.pendingMergeResult || {};
762            _this.pendingMergeResult = Object.assign({}, prev, parsed || {});
763            if (parsed && parsed.mergeStatusJobId) {
764                _this.mergeStatusJobId = String(parsed.mergeStatusJobId);
765            }
766            _this.mergeForecastPending = true;
767        };
768        this.holdMergeForForecasts = function (parsed) {
769            _this.rememberPendingMerge(parsed);
770            _this.mergePhase('working');
771            _this.mergeInFlight(true);
772        };
773        this.endMergeFlight = function () {
774            _this.mergeInFlight(false);
775            _this.mergeForecastPending = false;
776            _this.mergeFinishStarted = false;
777            _this.mergeStatusJobId = '';
778            _this.pendingMergeResult = null;
779            _this.mergeBarAuthorName('');
780            _this.clearMergeStatusPresentation();
781        };
782        this.claimMergeFinish = function () {
783            if (!_this.mergeInFlight() || _this.mergeFinishStarted) {
784                return false;
785            }
786            _this.mergeFinishStarted = true;
787            return true;
788        };
789        this.finishSynchronousMerge = function (parsed) {
790            if (!_this.claimMergeFinish()) {
791                return;
792            }
793            _this.mergePhase('done');
794            _this.clearMergeStatusPresentation();
795            if (!_this.mergeModalOpen()) {
796                _this.endMergeFlight();
797                _this.mergePhase('choose');
798                _this.applyMergeSuccess(parsed);
799                return;
800            }
801            if (_this.mergeDoneTimer) {
802                clearTimeout(_this.mergeDoneTimer);
803            }
804            _this.mergeDoneTimer = setTimeout(function () {
805                _this.mergeDoneTimer = null;
806                _this.mergeModalOpen(false);
807                _this.mergePhase('choose');
808                _this.endMergeFlight();
809                _this.applyMergeSuccess(parsed);
810            }, 700);
811        };
812        this.completeForecastMerge = function (data) {
813            if (!_this.claimMergeFinish()) {
814                return;
815            }
816            var stashed = _this.pendingMergeResult || {};
817            var incomingLifetime = data && data.lifetimeInformation ? data.lifetimeInformation : null;
818            var stashedLifetime = stashed.lifetimeInformation && stashed.lifetimeInformation.status !== 'filling_forecasts'
819                ? stashed.lifetimeInformation
820                : null;
821            var parsed = Object.assign({}, stashed, {
822                lifetimeInformation: incomingLifetime || stashedLifetime,
823                scoresStatus: null,
824            });
825            if (!parsed.templateNetId && parsed.personalCopyNetId == null && data && data.netId != null && parsed.ownsCanonicalTemplate !== false) {
826                parsed.templateNetId = data.netId;
827            }
828            _this.mergeModalOpen(false);
829            _this.mergePhase('choose');
830            _this.mergeError('');
831            _this.scoresStatus('');
832            _this.endMergeFlight();
833            _this.applyMergeSuccess(parsed);
834        };
835        this.failForecastMerge = function (data) {
836            if (!_this.claimMergeFinish()) {
837                return;
838            }
839            _this.endMergeFlight();
840            _this.mergePhase('error');
841            _this.mergeError((data && data.message) || 'Forecasts could not be incorporated.');
842            _this.mergeModalOpen(true);
843        };
844        this.isMergeForecastJob = function (data) {
845            var jobId = data && data.jobId != null ? String(data.jobId) : '';
846            return jobId.indexOf('merge-forecasts:') === 0;
847        };
848        this.onBayesBotMergeStatusEvent = function (event) {
849            var data = _this.parseBayesBotSseData(event);
850            if (!data || (!_this.mergeInFlight() && !_this.mergeForecastPending)) {
851                return;
852            }
853            if (data.jobId) {
854                var jobId = String(data.jobId);
855                if (_this.mergeStatusJobId && _this.mergeStatusJobId !== jobId) {
856                    return;
857                }
858                _this.mergeStatusJobId = jobId;
859            }
860            if (data.phase === 'complete') {
861                _this.completeForecastMerge(data);
862                return;
863            }
864            if (data.phase === 'failed') {
865                _this.failForecastMerge(data);
866                return;
867            }
868            if (data.message) {
869                _this.setMergeStatusText(String(data.message));
870            }
871        };
872        this.applyMergeSuccess = function (parsed) {
873            _this.selectedBranchCopyNetId(null);
874            _this.viewingBranchCopyNetId(null);
875            if (parsed && parsed.lifetimeInformation) {
876                _this.presentPersistedLifetimeInformation(parsed.lifetimeInformation);
877            }
878            else if (parsed && parsed.scoresStatus) {
879                _this.scoresStatus(String(parsed.scoresStatus));
880            }
881            _this.refreshBranchRowSelection();
882            var personalCopyNetId = parsed && parsed.personalCopyNetId != null
883                ? parseInt(String(parsed.personalCopyNetId), 10)
884                : NaN;
885            if (Number.isFinite(personalCopyNetId) && personalCopyNetId > 0 && !parsed.ownsCanonicalTemplate) {
886                _this.ownsCanonicalTemplate = false;
887                _this.authorOwnsTemplate(false);
888                void _this.adoptPersonalCopyFromServer(personalCopyNetId, parsed.setId).then(function () {
889                    _this.loadBranchList();
890                    _this.loadSpecLogs(true);
891                });
892                return;
893            }
894            if (parsed && parsed.templateNetId != null) {
895                var templateNetId = parseInt(String(parsed.templateNetId), 10);
896                if (Number.isFinite(templateNetId) && templateNetId > 0) {
897                    _this.sharedTemplateNetId(templateNetId);
898                }
899            }
900            _this.restoreOwnNetView();
901            _this.loadBranchList();
902            _this.loadSpecLogs(true);
903        };
904        this.confirmMergeBranch = function (includeForecasts) {
905            var currentUser = Distiller.currentUser && Distiller.currentUser();
906            var userId = currentUser && currentUser.userId;
907            var copyNetId = _this.selectedBranchCopyNetId();
908            if (!userId || !copyNetId || _this.mergePhase() === 'working') {
909                return;
910            }
911            _this.mergePhase('working');
912            _this.mergeError('');
913            _this.mergeInFlight(true);
914            _this.mergeForecastPending = false;
915            _this.mergeFinishStarted = false;
916            _this.mergeStatusJobId = '';
917            _this.pendingMergeResult = null;
918            _this.mergeBarAuthorName(_this.viewingBranchAuthorName() || _this.mergeBarAuthorName() || '');
919            _this.setMergeStatusText('Merging…');
920            var xmlHttp = new XMLHttpRequest();
921            xmlHttp.onreadystatechange = function () {
922                if (xmlHttp.readyState !== 4) {
923                    return;
924                }
925                if (!_this.mergeInFlight()) {
926                    return;
927                }
928                var parsed = null;
929                try {
930                    parsed = xmlHttp.responseText ? JSON.parse(xmlHttp.responseText) : null;
931                }
932                catch (e) {
933                    parsed = null;
934                }
935                if (parsed && parsed.code === 'auth_required') {
936                    _this.endMergeFlight();
937                    _this.mergePhase('choose');
938                    _this.mergeModalOpen(false);
939                    _this.openAuthModal('branch');
940                    return;
941                }
942                if (xmlHttp.status === 200 && parsed && parsed.ok !== false) {
943                    if (_this.mergeResponseStillWorking(parsed)) {
944                        _this.holdMergeForForecasts(parsed);
945                        return;
946                    }
947                    _this.finishSynchronousMerge(parsed);
948                    return;
949                }
950                var dismissed = !_this.mergeModalOpen();
951                _this.endMergeFlight();
952                _this.mergePhase('error');
953                _this.mergeError((parsed && parsed.error) || (_this.branchActionMode() === 'clone' ? 'Clone failed' : 'Merge failed'));
954                if (dismissed) {
955                    _this.mergeModalOpen(true);
956                }
957            };
958            xmlHttp.onerror = function () {
959                if (!_this.mergeInFlight()) {
960                    return;
961                }
962                var dismissed = !_this.mergeModalOpen();
963                _this.endMergeFlight();
964                _this.mergePhase('error');
965                _this.mergeError(_this.branchActionMode() === 'clone' ? 'Clone failed' : 'Merge failed');
966                if (dismissed) {
967                    _this.mergeModalOpen(true);
968                }
969            };
970            var actionPath = _this.branchActionMode() === 'clone'
971                ? '/api/bayesBotCloneBranch/'
972                : '/api/bayesBotMergeBranch/';
973            xmlHttp.open('POST', Distiller.apiEndpoint + actionPath + userId, true);
974            xmlHttp.setRequestHeader('Content-Type', 'application/json;charset=UTF-8');
975            xmlHttp.send(JSON.stringify({
976                copyNetId: copyNetId,
977                includeForecasts: !!includeForecasts,
978                communityId: _this.resolveBoundCommunityId(),
979            }));
980        };
981        this.copyBranchShareUrl = function () {
982            var url = _this.branchShareUrl();
983            if (!url || !Distiller.copyToClipboard) {
984                return;
985            }
986            Distiller.copyToClipboard(url, true);
987            _this.branchShareCopied(true);
988            if (_this.branchShareCopiedTimer) {
989                clearTimeout(_this.branchShareCopiedTimer);
990            }
991            _this.branchShareCopiedTimer = setTimeout(function () {
992                _this.branchShareCopied(false);
993                _this.branchShareCopiedTimer = null;
994            }, 400);
995        };
996        this.selectLeaderboardTab = function () {
997            _this.leaderboardPanelTab('leaderboard');
998        };
999        this.selectInformationGainTab = function () {
1000            _this.leaderboardPanelTab('informationGain');
1001            var needed = _this.measureMinWorkbenchHeightForInformationGain();
1002            if (_this.workbenchHeightPx() < needed) {
1003                _this.workbenchHeightPx(_this.clampWorkbenchHeight(needed));
1004            }
1005            var self = _this;
1006            setTimeout(function () {
1007                self.renderLifetimeInformationChart();
1008            }, 50);
1009        };
1010        this.selectLogsTab = function () {
1011            _this.leaderboardPanelTab('logs');
1012            _this.loadSpecLogs(true);
1013        };
1014        this.formatSpecLogTime = function (value) {
1015            if (value == null || value === '') {
1016                return '';
1017            }
1018            var raw = value instanceof Date ? value : new Date(String(value).replace(' ', 'T'));
1019            if (isNaN(raw.getTime())) {
1020                return String(value);
1021            }
1022            return raw.toLocaleString(undefined, {
1023                month: 'short',
1024                day: 'numeric',
1025                year: 'numeric',
1026                hour: 'numeric',
1027                minute: '2-digit',
1028            });
1029        };
1030        this.scrollSpecLogsToBottom = function () {
1031            requestAnimationFrame(function () {
1032                var el = document.querySelector('.bayesBotLogsScroll');
1033                if (el) {
1034                    el.scrollTop = el.scrollHeight;
1035                }
1036            });
1037        };
1038        this.loadSpecLogs = function (scrollToBottom) {
1039            var currentUser = Distiller.currentUser && Distiller.currentUser();
1040            var userId = currentUser && currentUser.userId;
1041            if (!userId) {
1042                return;
1043            }
1044            Distiller.httpGetAsync(Distiller.apiEndpoint + '/api/bayesBotLogs/' + userId
1045                + '?communityId=' + encodeURIComponent(String(_this.resolveBoundCommunityId() || '')), function (responseText) {
1046                try {
1047                    var parsed = typeof responseText === 'string' ? JSON.parse(responseText) : responseText;
1048                    if (!parsed || parsed.ok === false) {
1049                        return;
1050                    }
1051                    var rows = (parsed.logs || []).map(function (row) { return ({
1052                        id: row && row.id != null ? row.id : null,
1053                        text: row && row.text != null ? String(row.text) : '',
1054                        timeLabel: _this.formatSpecLogTime(row && row.timestamp),
1055                    }); });
1056                    _this.specLogRows(rows);
1057                    if (scrollToBottom) {
1058                        _this.scrollSpecLogsToBottom();
1059                    }
1060                }
1061                catch (e) {
1062                    console.error('BayesBot logs parse failed:', e);
1063                }
1064            });
1065        };
1066        this.readLifetimeChartMinHeightPx = function () {
1067            var wrap = document.querySelector('.bayesBotLifetimeChartWrap');
1068            if (wrap) {
1069                var minH = parseFloat(window.getComputedStyle(wrap).minHeight);
1070                if (Number.isFinite(minH) && minH > 0) {
1071                    return minH;
1072                }
1073            }
1074            return BayesBot.LIFETIME_CHART_MIN_HEIGHT_PX;
1075        };
1076        this.readInformationGainChromePx = function () {
1077            var row = document.querySelector('.bayesBotWorkbenchRow');
1078            var infoPanel = null;
1079            if (row) {
1080                var panels = row.querySelectorAll('.bayesBotPanel');
1081                for (var i = 0; i < panels.length; i++) {
1082                    if (!panels[i].classList.contains('bayesBotProposePanel')) {
1083                        infoPanel = panels[i];
1084                        break;
1085                    }
1086                }
1087            }
1088            // Tab padding 12+10, ~16px line box, 1px tablist border; body pad 8+8; panel border 1+1.
1089            if (!infoPanel) {
1090                return 12 + 10 + 16 + 1 + 8 + 8 + 1 + 1;
1091            }
1092            var panelStyle = window.getComputedStyle(infoPanel);
1093            var borderY = (parseFloat(panelStyle.borderTopWidth) || 0) +
1094                (parseFloat(panelStyle.borderBottomWidth) || 0);
1095            var tabs = infoPanel.querySelector('.bayesBotProposeTabs');
1096            var tabH = tabs ? tabs.getBoundingClientRect().height : 12 + 10 + 16 + 1;
1097            var body = infoPanel.querySelector('.bayesBotInformationGainBody');
1098            var padY = 8 + 8;
1099            if (body) {
1100                var bodyStyle = window.getComputedStyle(body);
1101                padY =
1102                    (parseFloat(bodyStyle.paddingTop) || 0) +
1103                        (parseFloat(bodyStyle.paddingBottom) || 0);
1104            }
1105            return tabH + padY + borderY;
1106        };
1107        this.measureMinWorkbenchHeightForInformationGain = function () {
1108            return Math.ceil(_this.readLifetimeChartMinHeightPx() + _this.readInformationGainChromePx());
1109        };
1110        this.scheduleBranchListWorkbenchHeight = function () {
1111            var self = _this;
1112            var attempts = 0;
1113            var tick = function () {
1114                if (self.proposePanelTab() !== 'branches') {
1115                    return;
1116                }
1117                if (!self.branchRowsAreMeasurable() && attempts < 30) {
1118                    attempts += 1;
1119                    requestAnimationFrame(tick);
1120                    return;
1121                }
1122                self.ensureBranchListWorkbenchHeight();
1123            };
1124            requestAnimationFrame(tick);
1125        };
1126        this.branchRowsAreMeasurable = function () {
1127            var expected = _this.branchRows().length;
1128            if (expected <= 0) {
1129                return false;
1130            }
1131            var rows = document.querySelectorAll('.bayesBotBranchRow');
1132            if (rows.length < Math.min(expected, 2)) {
1133                return false;
1134            }
1135            return rows[0].getBoundingClientRect().height > 0;
1136        };
1137        this.ensureBranchListWorkbenchHeight = function () {
1138            if (_this.proposePanelTab() !== 'branches') {
1139                return;
1140            }
1141            var needed = _this.measureMinWorkbenchHeightForBranchList();
1142            if (_this.workbenchHeightPx() < needed) {
1143                _this.workbenchHeightPx(_this.clampWorkbenchHeight(needed));
1144            }
1145        };
1146        this.measureMinWorkbenchHeightForBranchList = function () {
1147            var rows = document.querySelectorAll('.bayesBotBranchRow');
1148            var visibleCount = Math.min(rows.length, 2);
1149            if (visibleCount < 1) {
1150                return _this.workbenchHeightPx();
1151            }
1152            var rowBlock = 0;
1153            for (var i = 0; i < visibleCount; i++) {
1154                var rowHeight = rows[i].offsetHeight;
1155                if (!(rowHeight > 0)) {
1156                    return _this.workbenchHeightPx();
1157                }
1158                rowBlock += rowHeight;
1159            }
1160            var panel = document.querySelector('.bayesBotProposePanel');
1161            var scroll = panel ? panel.querySelector('.bayesBotBranchScroll') : null;
1162            if (!panel || !scroll) {
1163                return rowBlock + BayesBot.BRANCH_ROW_HEIGHT_PX;
1164            }
1165            var chrome = Math.max(0, panel.offsetHeight - scroll.clientHeight);
1166            return Math.ceil(chrome + rowBlock);
1167        };
1168        this.ensureCreateNetWorkbenchHeight = function () {
1169            _this.workbenchHeightPx(_this.clampWorkbenchHeight(_this.measureMinWorkbenchHeightForInformationGain()));
1170        };
1171        this.agentExampleTypingDelay = function (ch) {
1172            if (ch === '.' || ch === '?' || ch === '!') {
1173                return 160 + Math.random() * 140;
1174            }
1175            if (ch === ',' || ch === ';') {
1176                return 80 + Math.random() * 70;
1177            }
1178            if (ch === '\n') {
1179                return 200 + Math.random() * 90;
1180            }
1181            if (ch === ' ') {
1182                return 36 + Math.random() * 40;
1183            }
1184            if (Math.random() < 0.05) {
1185                return 120 + Math.random() * 90;
1186            }
1187            return 20 + Math.random() * 32;
1188        };
1189        this.isAgentTerminalNearBottom = function (el) {
1190            return el.scrollHeight - el.scrollTop - el.clientHeight <= 32;
1191        };
1192        this.onAgentConnectTerminalScroll = function (_data, event) {
1193            var el = event && event.currentTarget;
1194            if (!el) {
1195                return true;
1196            }
1197            _this.agentTerminalStickToBottom = _this.isAgentTerminalNearBottom(el);
1198            return true;
1199        };
1200        this.scrollAgentExampleTerminal = function () {
1201            if (!_this.agentTerminalStickToBottom) {
1202                return;
1203            }
1204            var el = document.querySelector('.bayesBotAgentConnectTerminal');
1205            if (!el) {
1206                return;
1207            }
1208            requestAnimationFrame(function () {
1209                if (!_this.agentTerminalStickToBottom) {
1210                    return;
1211                }
1212                el.scrollTop = el.scrollHeight;
1213            });
1214        };
1215        this.stopAgentExampleRotation = function () {
1216            _this.agentExampleToken += 1;
1217            if (_this.agentExampleTimer) {
1218                clearTimeout(_this.agentExampleTimer);
1219                _this.agentExampleTimer = null;
1220            }
1221            _this.agentExampleAnimating(false);
1222            _this.displayedAgentExampleText('');
1223        };
1224        this.startAgentExampleRotation = function () {
1225            var token = ++_this.agentExampleToken;
1226            if (_this.agentExampleTimer) {
1227                clearTimeout(_this.agentExampleTimer);
1228                _this.agentExampleTimer = null;
1229            }
1230            _this.displayedAgentExampleText('');
1231            _this.agentExampleAnimating(true);
1232            var typeExample = function () {
1233                if (token !== _this.agentExampleToken) {
1234                    return;
1235                }
1236                var prompts = _this.isCreateMode()
1237                    ? BAYES_BOT_CREATE_AGENT_EXAMPLE_PROMPTS
1238                    : BAYES_BOT_AGENT_EXAMPLE_PROMPTS;
1239                var target = prompts[_this.agentExampleIndex % prompts.length];
1240                var i = 0;
1241                _this.displayedAgentExampleText('');
1242                _this.agentExampleAnimating(true);
1243                _this.scrollAgentExampleTerminal();
1244                var typeNext = function () {
1245                    if (token !== _this.agentExampleToken) {
1246                        return;
1247                    }
1248                    if (i >= target.length) {
1249                        _this.agentExampleAnimating(true);
1250                        _this.scrollAgentExampleTerminal();
1251                        _this.agentExampleTimer = setTimeout(function () {
1252                            if (token !== _this.agentExampleToken) {
1253                                return;
1254                            }
1255                            _this.agentExampleIndex = (_this.agentExampleIndex + 1) % prompts.length;
1256                            typeExample();
1257                        }, 3400);
1258                        return;
1259                    }
1260                    i += 1;
1261                    _this.displayedAgentExampleText(target.slice(0, i)
1261);
1262                    _this.scrollAgentExampleTerminal();
1263                    _this.agentExampleTimer = setTimeout(typeNext, _this.agentExampleTypingDelay(target.charAt(i - 1)));
1264                };
1265                typeNext();
1266            };
1267            typeExample();
1268        };
1269        this.startAgentActivitySpinner = function () {
1270            _this.agentActivitySpinning(true);
1271            _this.ensureAgentSpinnerRunning();
1272        };
1273        this.ensureAgentSpinnerRunning = function () {
1274            if (_this.agentActivitySpinnerTimer) {
1275                return;
1276            }
1277            _this.agentActivitySpinnerTick = 0;
1278            _this.tickAgentSpinner();
1279            _this.agentActivitySpinnerTimer = setInterval(function () {
1280                _this.agentActivitySpinnerTick += 1;
1281                _this.tickAgentSpinner();
1282            }, 140);
1283        };
1284        this.tickAgentSpinner = function () {
1285            var frames = BayesBot.AGENT_ACTIVITY_SPINNER_FRAMES;
1286            var tick = _this.agentActivitySpinnerTick;
1287            _this.agentActivitySpinnerFrame(frames[tick % frames.length]);
1288            var n = BayesBot.BRAILLE_CELL_COUNT;
1289            var last = n - 1;
1290            var period = last * 2;
1291            var t = period === 0 ? 0 : tick % period;
1292            var pos = t <= last ? t : period - t;
1293            var cells = [];
1294            for (var i = 0; i < n; i++) {
1295                cells.push(i === pos);
1296            }
1297            _this.agentConnectBrailleCells(cells);
1298        };
1299        this.stopAgentActivitySpinner = function () {
1300            _this.agentActivitySpinning(false);
1301            if (_this.agentIsConnected()) {
1302                _this.ensureAgentSpinnerRunning();
1303                return;
1304            }
1305            if (_this.agentActivitySpinnerTimer) {
1306                clearInterval(_this.agentActivitySpinnerTimer);
1307                _this.agentActivitySpinnerTimer = null;
1308            }
1309        };
1310        this.markAgentConnected = function () {
1311            if (!_this.agentIsConnected()) {
1312                _this.agentIsConnected(true);
1313            }
1314            _this.ensureAgentSpinnerRunning();
1315        };
1316        this.showAgentActivityTerminal = function () {
1317            _this.stopAgentExampleRotation();
1318            _this.proposePanelTab('connect');
1319            if (!_this.agentActivityLive()) {
1320                _this.agentTerminalStickToBottom = true;
1321            }
1322            _this.agentActivityLive(true);
1323            _this.scrollAgentExampleTerminal();
1324        };
1325        this.appendAgentActivityLine = function (text) {
1326            var next = String(text || '').trim();
1327            if (!next) {
1328                return;
1329            }
1330            _this.showAgentActivityTerminal();
1331            _this.stopAgentActivitySpinner();
1332            var current = String(_this.agentActivityLatestText() || '').trim();
1333            if (current) {
1334                _this.agentActivityLines.push(current);
1335            }
1336            _this.agentActivityLatestText(next);
1337            _this.scrollAgentExampleTerminal();
1338        };
1339        this.resetCanvasObservationsToDefault = function () {
1340            var net = _this.authorBayesNet;
1341            if (net && typeof net.clearAllObservations === 'function') {
1342                net.clearAllObservations();
1343            }
1344        };
1345        this.selectCanvasNodeFromActivity = function (data) {
1346            var net = _this.authorBayesNet;
1347            if (!net || !data) {
1348                return;
1349            }
1350            var liveId = _this.resolveCanvasNodeId(data.nodeId, data.nodeTitle);
1351            if (!liveId) {
1352                return;
1353            }
1354            if (typeof net.setPathFocusNodeId === 'function') {
1355                net.setPathFocusNodeId(liveId);
1356            }
1357            if (typeof net.selectNodeById === 'function') {
1358                net.selectNodeById(liveId);
1359            }
1360        };
1361        this.applyAgentObservationFromActivity = function (data) {
1362            var net = _this.authorBayesNet;
1363            if (!net || !data) {
1364                return;
1365            }
1366            if (data.clearAll) {
1367                if (typeof net.clearAllObservations === 'function') {
1368                    net.clearAllObservations();
1369                }
1370                return;
1371            }
1372            var liveId = _this.resolveCanvasNodeId(data.nodeId, data.nodeTitle);
1373            if (!liveId) {
1374                return;
1375            }
1376            if (data.answerId != null && typeof net.applyObservation === 'function') {
1377                net.applyObservation(liveId, data.answerId);
1378                return;
1379            }
1380            if (typeof net.clearObservation === 'function') {
1381                net.clearObservation(liveId);
1382            }
1383        };
1384        this.applyAgentForecastFromActivity = function (data) {
1385            if (!data) {
1386                return;
1387            }
1388            var forecastType = String(data.forecastType || '');
1389            if (forecastType === 'conditional' || (Array.isArray(data.rows) && data.rows.length)) {
1390                _this.applyBayesBotGraphEvent({
1391                    type: 'conditional',
1392                    nodeId: data.nodeId,
1393                    rows: data.rows || [],
1394                });
1395                return;
1396            }
1397            if (forecastType === 'prior' || (Array.isArray(data.values) && data.values.length)) {
1398                _this.applyBayesBotGraphEvent({
1399                    type: 'prior',
1400                    nodeId: data.nodeId,
1401                    values: data.values || [],
1402                });
1403            }
1404        };
1405        this.applyAgentGraphMutationFromActivity = function (data) {
1406            if (!data) {
1407                return;
1408            }
1409            var mutationType = String(data.mutationType || '');
1410            if (mutationType === 'nodeCreated') {
1411                _this.applyBayesBotGraphEvent({
1412                    type: 'node',
1413                    node: data.node,
1414                    parentNodeId: data.parentNodeId,
1415                });
1416                _this.selectCanvasNodeFromActivity(data);
1417                return;
1418            }
1419            if (mutationType === 'nodeDeleted' || (data.nodeId != null && !data.parentNodeId && !data.childNodeId)) {
1420                _this.applyBayesBotGraphEvent({
1421                    type: 'nodeDeleted',
1422                    nodeId: data.nodeId,
1423                });
1424                return;
1425            }
1426            if (mutationType === 'connectionRemoved') {
1427                _this.applyBayesBotGraphEvent({
1428                    type: 'connectionRemoved',
1429                    parentNodeId: data.parentNodeId,
1430                    childNodeId: data.childNodeId,
1431                });
1432                return;
1433            }
1434            if (mutationType === 'connection') {
1435                _this.applyBayesBotGraphEvent({
1436                    type: 'connection',
1437                    parentNodeId: data.parentNodeId,
1438                    childNodeId: data.childNodeId,
1439                });
1440            }
1441        };
1442        this.adoptAgentJobFromActivity = function (data) {
1443            if (data && data.jobId) {
1444                var incoming = String(data.jobId);
1445                var current = _this.lastBayesBotJobId();
1446                if (incoming !== String(current || '')) {
1447                    _this.proposalJobSettled = false;
1448                }
1449                _this.lastBayesBotJobId(incoming);
1450            }
1451            if (_this.isCreateMode()) {
1452                _this.liveTemplateBuild = true;
1453                _this.ensureCreateCanvasMounted(data);
1454                _this.ensureCreateNetWorkbenchHeight();
1455            }
1456        };
1457        this.onBayesBotAgentActivityEvent = function (event) {
1458            var data = _this.parseBayesBotSseData(event);
1459            if (!data || !data.kind) {
1460                return;
1461            }
1462            _this.markAgentConnected();
1463            _this.adoptAgentJobFromActivity(data);
1464            var kind = String(data.kind);
1465            if (kind === 'connecting') {
1466                _this.showAgentActivityTerminal();
1467                _this.agentActivityLines([]);
1468                _this.agentActivityLatestText('');
1469                _this.startAgentActivitySpinner();
1470                _this.resetCanvasObservationsToDefault();
1471                return;
1472            }
1473            if (kind === 'connected') {
1474                _this.resetCanvasObservationsToDefault();
1475                _this.appendAgentActivityLine(data.message || 'agent is connected.');
1476                return;
1477            }
1478            if (kind === 'proposal_status') {
1479                _this.appendAgentActivityLine(data.message || 'processing proposal...');
1480                return;
1481            }
1482            if (kind === 'proposal_complete') {
1483                _this.appendAgentActivityLine(data.message || 'proposal finished.');
1484                var lines = Array.isArray(data.lines) ? data.lines : [];
1485                for (var i = 0; i < lines.length; i++) {
1486                    _this.appendAgentActivityLine(String(lines[i]));
1487                }
1488                return;
1489            }
1490            if (kind === 'simulate') {
1491                _this.applyAgentObservationFromActivity(data);
1492            }
1493            if (kind === 'forecast') {
1494                _this.applyAgentForecastFromActivity(data);
1495                _this.selectCanvasNodeFromActivity(data);
1496            }
1497            if (kind === 'graph') {
1498                _this.applyAgentGraphMutationFromActivity(data);
1499            }
1500            if (kind === 'select') {
1501                _this.selectCanvasNodeFromActivity(data);
1502            }
1503            _this.appendAgentActivityLine(data.message || kind);
1504        };
1505        this.rewriteUrlToCurrentOrigin = function (raw) {
1506            var trimmed = String(raw || '').trim();
1507            if (!trimmed || typeof window === 'undefined' || !window.location) {
1508                return trimmed;
1509            }
1510            var origin = bayesBotAgentConnectOrigin(window.location.hostname, window.location.port);
1511            try {
1512                var parsed = new URL(trimmed, origin);
1513                return origin + parsed.pathname + parsed.search + parsed.hash;
1514            }
1515            catch (e) {
1516                return trimmed;
1517            }
1518        };
1519        this.resolvePublicStudyUrl = function () {
1520            var fromStudy = Distiller.studyInView && Distiller.studyInView();
1521            var studyLink = fromStudy && fromStudy.publicLink && fromStudy.publicLink();
1522            var studyUrl = studyLink && studyLink.url && studyLink.url();
1523            if (studyUrl) {
1524                return _this.rewriteUrlToCurrentOrigin(studyUrl);
1525            }
1526            var fromCommunity = Distiller.currentCommunity && Distiller.currentCommunity();
1527            var communityLink = fromCommunity && fromCommunity.publicLink && fromCommunity.publicLink();
1528            var communityUrl = communityLink && communityLink.url && communityLink.url();
1529            if (communityUrl) {
1530                return _this.rewriteUrlToCurrentOrigin(communityUrl);
1531            }
1532            if (_this.agentStudyBaseUrl()) {
1533                return _this.rewriteUrlToCurrentOrigin(_this.agentStudyBaseUrl());
1534            }
1535            try {
1536                var loc = new URL(window.location.href);
1537                var study = loc.searchParams.get('study');
1538                var sid = loc.searchParams.get('sid');
1539                if (study && sid) {
1540                    return (bayesBotAgentConnectOrigin(loc.hostname, loc.port) +
1541                        '/?&study=' +
1542                        encodeURIComponent(study) +
1543                        '&sid=' +
1544                        encodeURIComponent(sid) +
1545                        '&');
1546                }
1547            }
1548            catch (e) {
1549                // fall through
1550            }
1551            return '';
1552        };
1553        this.agentConnectUrl = ko.pureComputed(function () {
1554            var key = _this.agentKey();
1555            var base = _this.resolvePublicStudyUrl();
1556            if (!key || !base) {
1557                return '';
1558            }
1559            var trimmed = String(base);
1560            var joiner = /[?&]$/.test(trimmed) ? '' : (trimmed.indexOf('?') >= 0 ? '&' : '?');
1561            return trimmed + joiner + 'agent=' + encodeURIComponent(key);
1562        });
1563        this.copyAgentConnectUrl = function () {
1564            var url = _this.agentConnectUrl();
1565            if (!url || !Distiller.copyToClipboard) {
1566                return;
1567            }
1568            Distiller.copyToClipboard(url, true);
1569            _this.agentConnectCopied(true);
1570            if (_this.agentConnectCopiedTimer) {
1571                clearTimeout(_this.agentConnectCopiedTimer);
1572            }
1573            _this.agentConnectCopiedTimer = setTimeout(function () {
1574                _this.agentConnectCopied(false);
1575                _this.agentConnectCopiedTimer = null;
1576            }, 400);
1577        };
1578        this.ensureAgentKey = function () {
1579            if (_this.agentKeyReady) {
1580                return _this.agentKeyReady;
1581            }
1582            var currentUser = Distiller.currentUser && Distiller.currentUser();
1583            var userId = currentUser && currentUser.userId;
1584            if (!userId) {
1585                return Promise.resolve();
1586            }
1587            _this.agentKeyLoading(true);
1588            var endpoint = Distiller.apiEndpoint + '/api/getOrCreateAgentKey/' + userId;
1589            _this.agentKeyReady = fetch(endpoint, { credentials: 'same-origin' })
1590                .then(function (res) { return res.json(); })
1591                .then(function (response) {
1592                if (response && response.ok && response.key) {
1593                    _this.agentKey(response.key);
1594                    if (response.studyUrl) {
1595                        _this.agentStudyBaseUrl(response.studyUrl);
1596                    }
1597                }
1598                else {
1599                    console.error('BayesBot ensureAgentKey: unexpected response', response);
1600                }
1601            })
1602                .catch(function (e) {
1603                console.error('BayesBot ensureAgentKey failed:', e);
1604            })
1605                .then(function () {
1606                _this.agentKeyLoading(false);
1607            });
1608            return _this.agentKeyReady;
1609        };
1610        this.proposePlaceholder = ko.pureComputed(function () {
1611            return _this.isCreateMode()
1612                ? 'Paste a description of the future…'
1613                : 'Describe an early update signal…';
1614        });
1615        this.headerParts = ko.pureComputed(function () {
1616            return parseBayesBotHeaderMarkup(_this.headerMarkup());
1617        });
1618        this.resolveBoundCommunityId = function () {
1619            var bound = _this.boundCommunityId();
1620            if (bound) {
1621                return bound;
1622            }
1623            var community = Distiller.currentCommunity && Distiller.currentCommunity();
1624            if (community && typeof community.communityId === 'function' && community.communityId()) {
1625                return community.communityId();
1626            }
1627            var user = Distiller.currentUser && Distiller.currentUser();
1628            if (user && user.communityId != null) {
1629                var raw = typeof user.communityId === 'function'
1630                    ? user.communityId()
1631                    : user.communityId;
1632                var parsed = parseInt(String(raw), 10);
1633                if (Number.isFinite(parsed) && parsed > 0) {
1634                    return parsed;
1635                }
1636            }
1637            return 661;
1638        };
1639        this.showHeaderInfo = function (_part, event) {
1640            Distiller.showInfo(_this.headerInfoHtml(), event);
1641        };
1642        this.presentCreateHeader = function () {
1643            _this.isCreateMode(true);
1644            _this.ensureCreateNetWorkbenchHeight();
1645            _this.applyHeaderMarkupImmediate(BAYES_BOT_CREATE_HEADER_MARKUP, BAYES_BOT_BAYES_NET_INFO);
1646        };
1647        this.headerForDisplay = function (markup, infoHtml) {
1648            if (!isLegacyHormuzPlaceholder(markup)) {
1649                return { markup: markup, infoHtml: infoHtml };
1650            }
1651            _this.isCreateMode(true);
1652            return { markup: BAYES_BOT_CREATE_HEADER_MARKUP, infoHtml: BAYES_BOT_BAYES_NET_INFO };
1653        };
1654        this.applyHeaderMarkupImmediate = function (markup, infoHtml) {
1655            var shown = _this.headerForDisplay(markup, infoHtml);
1656            markup = shown.markup;
1657            infoHtml = shown.infoHtml;
1658            _this.headerTypewriterToken += 1;
1659            if (_this.headerTypewriterTimer) {
1660                clearTimeout(_this.headerTypewriterTimer);
1661                _this.headerTypewriterTimer = null;
1662            }
1663            _this.headerMarkup(markup);
1664            _this.displayedHeaderText(stripBayesBotHeaderMarkup(markup));
1665            _this.headerInfoHtml(infoHtml);
1666            _this.headerAnimating(false);
1667            _this.headerAnimationComplete(true);
1668        };
1669        this.animateHeaderMarkup = function (markup, infoHtml) {
1670            var shown = _this.headerForDisplay(markup, infoHtml);
1671            markup = shown.markup;
1672            infoHtml = shown.infoHtml;
1673            var token = ++_this.headerTypewriterToken;
1674            if (_this.headerTypewriterTimer) {
1675                clearTimeout(_this.headerTypewriterTimer);
1676                _this.headerTypewriterTimer = null;
1677            }
1678            var targetPlain = stripBayesBotHeaderMarkup(markup);
1679            _this.headerAnimationComplete(false);
1680            _this.headerAnimating(true);
1681            _this.headerInfoHtml(infoHtml);
1682            var reveal = function () {
1683                if (token !== _this.headerTypewriterToken) {
1684                    return;
1685                }
1686                _this.displayedHeaderText(targetPlain);
1687                _this.headerMarkup(markup);
1688                _this.headerAnimating(false);
1689                _this.headerAnimationComplete(true);
1690                _this.headerTypewriterTimer = null;
1691            };
1692            var typeForward = function () {
1693                if (token !== _this.headerTypewriterToken) {
1694                    return;
1695                }
1696                var shown = _this.displayedHeaderText();
1697                if (shown.length >= targetPlain.length) {
1698                    reveal();
1699                    return;
1700                }
1701                _this.displayedHeaderText(targetPlain.slice(0, shown.length + 1));
1702                _this.headerTypewriterTimer = setTimeout(typeForward, 18);
1703            };
1704            var backspace = function () {
1705                if (token !== _this.headerTypewriterToken) {
1706                    return;
1707                }
1708                var shown = _this.displayedHeaderText();
1709                if (shown.length > 0) {
1710                    _this.displayedHeaderText(shown.slice(0, -1));
1711                    _this.headerTypewriterTimer = setTimeout(backspace, 16);
1712                    return;
1713                }
1714                _this.headerTypewriterTimer = setTimeout(typeForward, 80);
1715            };
1716            if (!_this.displayedHeaderText()) {
1717                typeForward();
1718                return;
1719            }
1720            backspace();
1721        };
1722        this.unwrapBayesBotValue = function (value) {
1723            return typeof value === 'function' ? value() : value;
1724        };
1725        this.makeEmptyAuthorSet = function (setId, setName, type) {
1726            return {
1727                setId: ko.observable(setId),
1728                setName: ko.observable(setName || 'Bayes set'),
1729                setDescription: ko.observable(null),
1730                quillDelta: ko.observable(null),
1731                questions: ko.observableArray([]),
1732                isSelected: ko.observable(false),
1733                images: ko.observableArray([]),
1734                discussionQuestions: ko.observableArray([]),
1735                surveyQuestions: ko.observableArray([]),
1736                hiddenQuestions: ko.observableArray([]),
1737                type: ko.observable(type != null ? type : 16),
1738                parentId: ko.observable(null),
1739                priority: ko.observable(null),
1740                setTags: ko.observableArray([]),
1741                userStage: ko.observable(null),
1742                pages: ko.observableArray([]),
1743                subsets: ko.observableArray([]),
1744                isLive: ko.observable(1),
1745            };
1746        };
1747        this.resolveBayesSetIdFromPage = function () {
1748            var pageSet = _this.questionSet();
1749            if (!pageSet) {
1750                return null;
1751            }
1752            var pageType = _this.unwrapBayesBotValue(pageSet.type);
1753            var pageSetId = _this.unwrapBayesBotValue(pageSet.setId);
1754            if (pageType === 16 && pageSetId) {
1755                return pageSetId;
1756            }
1757            var pages = pageSet.pages && typeof pageSet.pages === 'function' ? pageSet.pages() : [];
1758            for (var p = 0; p < (pages || []).length; p++) {
1759                var workflows = pages[p] && pages[p].stageWorkflows && typeof pages[p].stageWorkflows === 'function'
1760                    ? pages[p].stageWorkflows()
1761                    : [];
1762                for (var w = 0; w < (workflows || []).length; w++) {
1763                    var elements = workflows[w] && workflows[w].elements && typeof workflows[w].elements === 'function'
1764                        ? workflows[w].elements()
1765                        : [];
1766                    for (var i = 0; i < (elements || []).length; i++) {
1767                        var el = elements[i];
1768                        if (!_this.isBayesNetElement(el)) {
1769                            continue;
1770                        }
1771                        var fromFlag = _this.unwrapBayesBotValue(el.bayesNetSetId);
1772                        if (fromFlag) {
1773                            return fromFlag;
1774                        }
1775                        var nestedSet = _this.unwrapBayesBotValue(el.set);
1776                        if (nestedSet && typeof nestedSet === 'object') {
1777                            var nestedId = _this.unwrapBayesBotValue(nestedSet.setId);
1778                            if (nestedId) {
1779                                return nestedId;
1780                            }
1781                        }
1782                        else if (nestedSet) {
1783                            return nestedSet;
1784                        }
1785                    }
1786                }
1787            }
1788            return null;
1789        };
1790        this.mountCanvasImmediately = function () {
1791            var templateNetId = parseInt(String(_this.sharedTemplateNetId()), 10);
1792            if (!Number.isFinite(templateNetId) || templateNetId <= 0) {
1793                return;
1794            }
1795            var setId = _this.resolveBayesSetIdFromPage() || 'bayes-bot-canvas';
1796            var existing = _this.authorBayesNetSet();
1797            if (existing &&
1798                existing.setId &&
1799                String(existing.setId()) === String(setId) &&
1800                _this.canvasMountedForTemplateNetId === templateNetId) {
1801                return;
1802            }
1803            _this.publishAuthorBayesNetSet(setId, 'Bayes set', 16);
1804        };
1805        this.publishAuthorBayesNetSet = function (setId, setName, type) {
1806            var templateNetId = parseInt(String(_this.sharedTemplateNetId()), 10);
1807            _this.canvasMountedForTemplateNetId =
1808                Number.isFinite(templateNetId) && templateNetId > 0 ? templateNetId : null;
1809            _this.authorBayesNet = null;
1810            // Drop the previous component so Knockout reads templateNetId on a new mount.
1811            _this.authorBayesNetSet(null);
1812            _this.authorBayesNetSet(_this.makeEmptyAuthorSet(setId, setName, type));
1813        };
1814        this.mountAuthorBayesNetFromModel = function (data) {
1815            if (!data || !data.setId) {
1816                return;
1817            }
1818            if (data.communityId != null) {
1819                _this.boundCommunityId(parseInt(String(data.communityId), 10));
1820            }
1821            if (data.questionText && !isLegacyHormuzPlaceholder(data.questionText)) {
1822                _this.boundQuestionText(String(data.questionText));
1823            }
1824            if (data.resolutionCriteria && !isLegacyHormuzPlaceholder(data.resolutionCriteria)) {
1825                _this.boundResolutionCriteria(String(data.resolutionCriteria));
1826            }
1827            if (data.resolutionDate) {
1828                _this.boundResolutionDate(bayesBotIsoDate(data.resolutionDate) || _this.boundResolutionDate());
1829            }
1830            var headerSource = data.headerMarkup || data.questionText;
1831            if (headerSource && !_this.isCreateMode()) {
1832                var infoHtml = data.resolutionCriteria
1833                    ? bayesBotHeaderInfoHtml('Resolves Yes', String(data.resolutionCriteria))
1834                    : _this.headerInfoHtml();
1835                _this.applyHeaderMarkupImmediate(String(headerSource), infoHtml);
1836            }
1837            _this.sharedTemplateNetId(data.templateNetId != null ? parseInt(String(data.templateNetId), 10) : null);
1838            if (data.ownsCanonicalTemplate) {
1839                _this.ownsCanonicalTemplate = true;
1840                _this.authorOwnsTemplate(true);
1841            }
1842            if (data.copyNetId != null && !_this.ownsCanonicalTemplate) {
1843                var existingCopy = parseInt(String(data.copyNetId), 10);
1844                if (Number.isFinite(existingCopy) && existingCopy > 0) {
1845                    _this.personalCopyNetId = existingCopy;
1846                }
1847            }
1848            if (data.primaryNodeId || data.endStateNodeId) {
1849                var primaryId = parseInt(String(data.primaryNodeId || data.endStateNodeId), 10);
1850                if (Number.isFinite(primaryId) && primaryId > 0) {
1851                    _this.leaderboardPrimaryNodeId(primaryId);
1852                }
1853            }
1854            var templateNetId = _this.sharedTemplateNetId();
1855            var existing = _this.authorBayesNetSet();
1856            if (existing &&
1857                existing.setId &&
1858                String(existing.setId()) === String(data.setId) &&
1859                _this.canvasMountedForTemplateNetId === templateNetId) {
1860                return;
1861            }
1862            _this.publishAuthorBayesNetSet(data.setId, data.setName, data.type != null ? data.type : 16);
1863        };
1864        this.ensureCreateCanvasMounted = function (data) {
1865            _this.liveTemplateBuild = true;
1866            _this.ownsCanonicalTemplate = true;
1867            _this.authorOwnsTemplate(true);
1868            var currentUser = Distiller.currentUser && Distiller.currentUser();
1869            if (currentUser && currentUser.userId) {
1870                _this.viewerUserId = currentUser.userId;
1871            }
1872            if (data && (data.templateNetId || data.netId)) {
1873                var netId = parseInt(String(data.templateNetId || data.netId), 10);
1874                if (Number.isFinite(netId) && netId > 0) {
1875                    _this.sharedTemplateNetId(netId);
1876                }
1877            }
1878            if (data && (data.setId || data.bayesSetId)) {
1879                _this.mountAuthorBayesNetFromModel(__assign(__assign({}, data), { setId: data.setId || data.bayesSetId, setName: data.setName || 'Bayes set', type: data.type != null ? data.type : 16 }));
1880                return;
1881            }
1882            if (_this.authorBayesNetSet()) {
1883                return;
1884            }
1885            void _this.loadAuthorBayesNet();
1886        };
1887        this.parseSidFromUrl = function () {
1888            try {
1889                var raw = new URLSearchParams(window.location.search).get('sid');
1890                var parsed = parseInt(String(raw), 10);
1891                if (Number.isFinite(parsed) && parsed > 0) {
1892                    return parsed;
1893                }
1894            }
1895            catch (e) {
1896                /* ignore */
1897            }
1898            return null;
1899        };
1900        this.isDraftCreateUrl = function () {
1901            try {
1902                return new URLSearchParams(window.location.search).get('bayesBotDraft') 
1902=== '1';
1903            }
1904            catch (e) {
1905                return false;
1906            }
1907        };
1908        this.provisionDraftStudy = function () { return __awaiter(_this, void 0, void 0, function () {
1909            var navigating, e_1;
1910            return __generator(this, function (_a) {
1911                switch (_a.label) {
1912                    case 0:
1913                        this.ensureCreateNetWorkbenchHeight();
1914                        this.draftProvisioning(true);
1915                        this.draftProvisionError('');
1916                        this.draftNeedsReplacement(false);
1917                        navigating = false;
1918                        _a.label = 1;
1919                    case 1:
1920                        _a.trys.push([1, 3, 4, 5]);
1921                        return [4 /*yield*/, Distiller.provisionFreshBayesBotLanding({ forceNew: true })];
1922                    case 2:
1923                        _a.sent();
1924                        navigating = true;
1925                        return [3 /*break*/, 5];
1926                    case 3:
1927                        e_1 = _a.sent();
1928                        console.error('BayesBot provision draft failed:', e_1);
1929                        this.draftProvisionError(e_1 && e_1.message
1930                            ? String(e_1.message)
1931                            : 'Could not create the BayesBot study.');
1932                        return [3 /*break*/, 5];
1933                    case 4:
1934                        if (!navigating) {
1935                            this.draftProvisioning(false);
1936                            if (Distiller.endBayesBotBoot) {
1937                                Distiller.endBayesBotBoot();
1938                            }
1939                        }
1940                        return [7 /*endfinally*/];
1941                    case 5: return [2 /*return*/];
1942                }
1943            });
1944        }); };
1945        this.initializeDraftStudy = function () { return __awaiter(_this, void 0, void 0, function () {
1946            var currentUser, userId, communityId, agentKeyPromise, response, data, isMissingDraft, activeJob, e_2;
1947            return __generator(this, function (_a) {
1948                switch (_a.label) {
1949                    case 0:
1950                        currentUser = Distiller.currentUser && Distiller.currentUser();
1951                        userId = currentUser && currentUser.userId;
1952                        communityId = this.resolveBoundCommunityId();
1953                        if (!userId || !communityId) {
1954                            this.draftProvisionError('The BayesBot study identity is incomplete.');
1955                            if (Distiller.isBayesBotUrl && Distiller.isBayesBotUrl()) {
1956                                this.presentCreateHeader();
1957                            }
1958                            if (Distiller.endBayesBotBoot) {
1959                                Distiller.endBayesBotBoot();
1960                            }
1961                            return [2 /*return*/];
1962                        }
1963                        if (Distiller.beginBayesBotBoot) {
1964                            Distiller.beginBayesBotBoot();
1965                        }
1966                        this.draftProvisioning(true);
1967                        this.draftProvisionError('');
1968                        this.draftNeedsReplacement(false);
1969                        agentKeyPromise = this.ensureAgentKey();
1970                        _a.label = 1;
1971                    case 1:
1972                        _a.trys.push([1, 9, 10, 11]);
1973                        return [4 /*yield*/, fetch(Distiller.apiEndpoint +
1974                                '/api/bayesBotDraftState/' +
1975                                userId +
1976                                '/' +
1977                                communityId, { credentials: 'same-origin' })];
1978                    case 2:
1979                        response = _a.sent();
1980                        return [4 /*yield*/, response.json()];
1981                    case 3:
1982                        data = _a.sent();
1983                        isMissingDraft = (data && data.ok === true && data.found === false) ||
1984                            (response.status === 400 && data && data.error === 'Bayes Bot draft not found');
1985                        if (!isMissingDraft) return [3 /*break*/, 5];
1986                        this.isCreateMode(false);
1987                        this.clearDraftCreateUrl();
1988                        return [4 /*yield*/, this.loadAuthorBayesNet()];
1989                    case 4:
1990                        _a.sent();
1991                        this.loadLeaderboard();
1992                        if (!stripBayesBotHeaderMarkup(this.headerMarkup() || '').trim()) {
1993                            this.presentCreateHeader();
1994                        }
1995                        return [2 /*return*/];
1996                    case 5:
1997                        if (!response.ok || !data || data.ok === false) {
1998                            throw new Error((data && data.error) || 'draft state unavailable');
1999                        }
2000                        if (data.agentKey) {
2001                            this.agentKey(String(data.agentKey));
2002                        }
2003                        if (data.studyUrl) {
2004                            this.agentStudyBaseUrl(String(data.studyUrl));
2005                        }
2006                        if (!(data.status === 'complete')) return [3 /*break*/, 7];
2007                        this.isCreateMode(false);
2008                        this.ownsCanonicalTemplate = true;
2009                        this.authorOwnsTemplate(true);
2010                        this.clearDraftCreateUrl();
2011                        return [4 /*yield*/, this.loadAuthorBayesNet()];
2012                    case 6:
2013                        _a.sent();
2014                        this.loadLeaderboard();
2015                        this.presentPersistedLifetimeInformation(data.lifetimeInformation || this.lifetimeInformationFromJob(data.activeJob));
2016                        return [3 /*break*/, 8];
2017                    case 7:
2018                        if (data.status === 'building') {
2019                            this.isCreateMode(true);
2020                            this.ensureCreateNetWorkbenchHeight();
2021                            activeJob = data.activeJob || null;
2022                            this.applyPersistedBuildStatus(__assign(__assign({}, (data.buildStatus || {})), (activeJob
2023                                ? {
2024                                    jobId: activeJob.jobId,
2025                                    status: activeJob.status,
2026                                    progress: activeJob.progress,
2027                                }
2028                                : {})));
2029                            if (!stripBayesBotHeaderMarkup(this.headerMarkup() || '').trim() || isLegacyHormuzPlaceholder(this.headerMarkup())) {
2030                                this.presentCreateHeader();
2031                            }
2032                            this.ensureCreateCanvasMounted(data);
2033                        }
2034                        else if (data.status === 'failed') {
2035                            this.presentCreateHeader();
2036                            this.draftNeedsReplacement(true);
2037                            this.draftProvisionError((data.buildStatus && data.buildStatus.error) ||
2038                                'This draft could not be completed. Start a new Bayes Bot study to try again.');
2039                        }
2040                        else if (data.status === 'draft' || data.createMode) {
2041                            this.presentCreateHeader();
2042                            this.ensureCreateCanvasMounted(data);
2043                        }
2044                        _a.label = 8;
2045                    case 8: return [3 /*break*/, 11];
2046                    case 9:
2047                        e_2 = _a.sent();
2048                        console.error('BayesBot initialize draft failed:', e_2);
2049                        this.draftProvisionError(e_2 && e_2.message
2050                            ? String(e_2.message)
2051                            : 'Could not load the BayesBot draft.');
2052                        if ((Distiller.isBayesBotUrl && Distiller.isBayesBotUrl()) ||
2053                            this.isDraftCreateUrl()) {
2054                            this.presentCreateHeader();
2055                        }
2056                        return [3 /*break*/, 11];
2057                    case 10:
2058                        this.draftProvisioning(false);
2059                        this.connectBayesBotSse();
2060                        void agentKeyPromise;
2061                        if (Distiller.endBayesBotBoot) {
2062                            Distiller.endBayesBotBoot();
2063                        }
2064                        return [7 /*endfinally*/];
2065                    case 11: return [2 /*return*/];
2066                }
2067            });
2068        }); };
2069        this.retryDraftInitialization = function () {
2070            if (_this.draftNeedsReplacement()) {
2071                void _this.provisionReplacementDraft();
2072                return;
2073            }
2074            if (_this.parseSidFromUrl()) {
2075                void _this.initializeDraftStudy();
2076            }
2077            else {
2078                void _this.provisionDraftStudy();
2079            }
2080        };
2081        this.provisionReplacementDraft = function () { return __awaiter(_this, void 0, void 0, function () {
2082            var currentUser, userId, communityId, handingOff, responseText, data, e_3;
2083            return __generator(this, function (_a) {
2084                switch (_a.label) {
2085                    case 0:
2086                        currentUser = Distiller.currentUser && Distiller.currentUser();
2087                        userId = currentUser && currentUser.userId;
2088                        communityId = this.resolveBoundCommunityId();
2089                        if (!userId || !communityId) {
2090                            this.draftProvisionError('The BayesBot study identity is incomplete.');
2091                            if (Distiller.endBayesBotBoot) {
2092                                Distiller.endBayesBotBoot();
2093                            }
2094                            return [2 /*return*/];
2095                        }
2096                        if (Distiller.beginBayesBotBoot) {
2097                            Distiller.beginBayesBotBoot();
2098                        }
2099                        this.draftProvisioning(true);
2100                        handingOff = false;
2101                        _a.label = 1;
2102                    case 1:
2103                        _a.trys.push([1, 3, 4, 5]);
2104                        return [4 /*yield*/, Distiller.httpPostJsonAsync(Distiller.apiEndpoint +
2105                                '/api/bayesBotNewDraft/' +
2106                                userId +
2107                                '/' +
2108                                communityId, {})];
2109                    case 2:
2110                        responseText = _a.sent();
2111                        data = typeof responseText === 'string' ? JSON.parse(responseText) : responseText;
2112                        if (!data || data.ok === false || !data.studyUrl) {
2113                            throw new Error((data && data.error) || 'new draft unavailable');
2114                        }
2115                        this.rememberCreatedStudyMembership(data);
2116                        this.applyCreatedStudyToUrl(data);
2117                        Distiller.enterProvisionedBayesBotStudy(data);
2118                        handingOff = true;
2119                        return [3 /*break*/, 5];
2120                    case 3:
2121                        e_3 = _a.sent();
2122                        this.draftProvisionError(e_3 && e_3.message
2123                            ? String(e_3.message)
2124                            : 'Could not create a replacement draft.');
2125                        return [3 /*break*/, 5];
2126                    case 4:
2127                        if (!handingOff) {
2128                            this.draftProvisioning(false);
2129                            if (Distiller.endBayesBotBoot) {
2130                                Distiller.endBayesBotBoot();
2131                            }
2132                        }
2133                        return [7 /*endfinally*/];
2134                    case 5: return [2 /*return*/];
2135                }
2136            });
2137        }); };
2138        this.rememberCreatedStudyMembership = function (data) {
2139            if (Distiller.rememberBayesBotStudyMembership) {
2140                Distiller.rememberBayesBotStudyMembership(data);
2141                return;
2142            }
2143            var communityId = data && data.communityId != null
2144                ? parseInt(String(data.communityId), 10)
2145                : data && data.sid != null
2146                    ? parseInt(String(data.sid), 10)
2147                    : NaN;
2148            var currentUserId = data && data.currentUserId != null
2149                ? parseInt(String(data.currentUserId), 10)
2150                : data && data.studyUserId != null
2151                    ? parseInt(String(data.studyUserId), 10)
2152                    : data && data.userId != null
2153                        ? parseInt(String(data.userId), 10)
2154                        : NaN;
2155            if (!Number.isFinite(communityId) || communityId <= 0 || !Number.isFinite(currentUserId) || currentUserId <= 0) {
2156                console.error('BayesBot rememberCreatedStudyMembership: missing communityId or currentUserId', data);
2157                return;
2158            }
2159            var params = [];
2160            try {
2161                if (localStorage.quorumParams && Distiller.isJsonString && Distiller.isJsonString(localStorage.quorumParams)) {
2162                    var parsed = JSON.parse(localStorage.quorumParams);
2163                    if (Array.isArray(parsed)) {
2164                        params = parsed;
2165                    }
2166                }
2167            }
2168            catch (e) {
2169                params = [];
2170            }
2171            var already = params.some(function (row) { return row && String(row.communityId) === String(communityId); });
2172            if (!already) {
2173                // community.type stays null — do not mark this as the parent admin membership.
2174                params.push({ communityId: communityId, currentUserId: currentUserId, type: null });
2175                try {
2176                    localStorage.setItem('quorumParams', JSON.stringify(params));
2177                }
2178                catch (e) {
2179                    console.error('BayesBot rememberCreatedStudyMembership: failed to write quorumParams', e);
2180                }
2181            }
2182            if (Distiller.memberships && typeof Distiller.memberships === 'function') {
2183                var existing = Distiller.memberships();
2184                if (Array.isArray(existing) && !existing.some(function (row) { return row && String(row.
2184communityId) === String(communityId); })) {
2185                    if (typeof Distiller.memberships.push === 'function') {
2186                        Distiller.memberships.push({ communityId: communityId, currentUserId: currentUserId, type: null });
2187                    }
2188                }
2189            }
2190        };
2191        this.applyCreatedStudyToUrl = function (data) {
2192            if (Distiller.applyBayesBotStudyToUrl) {
2193                Distiller.applyBayesBotStudyToUrl(data);
2194                return;
2195            }
2196            var communityId = data && data.communityId != null
2197                ? parseInt(String(data.communityId), 10)
2198                : data && data.sid != null
2199                    ? parseInt(String(data.sid), 10)
2200                    : NaN;
2201            if (!Number.isFinite(communityId) || communityId <= 0) {
2202                return;
2203            }
2204            try {
2205                var url = new URL(window.location.href);
2206                url.searchParams.set('sid', String(communityId));
2207                var linkKey = data && data.linkKey != null ? String(data.linkKey) : '';
2208                if (linkKey) {
2209                    url.searchParams.set('study', linkKey);
2210                }
2211                url.searchParams.set('bayesBotDraft', '1');
2212                var next = url.toString();
2213                if (next !== window.location.href) {
2214                    window.history.replaceState({ path: next }, '', next);
2215                    if (Distiller.urlRevision) {
2216                        var revision = Distiller.urlRevision;
2217                        revision(revision() + 1);
2218                    }
2219                }
2220            }
2221            catch (e) {
2222                console.error('BayesBot applyCreatedStudyToUrl failed:', e);
2223            }
2224        };
2225        this.clearDraftCreateUrl = function () {
2226            try {
2227                var url = new URL(window.location.href);
2228                if (!url.searchParams.has('bayesBotDraft')) {
2229                    return;
2230                }
2231                url.searchParams.delete('bayesBotDraft');
2232                window.history.replaceState({ path: url.toString() }, '', url.toString());
2233                if (Distiller.urlRevision) {
2234                    var revision = Distiller.urlRevision;
2235                    revision(revision() + 1);
2236                }
2237            }
2238            catch (e) {
2239                console.error('BayesBot clear draft URL failed:', e);
2240            }
2241        };
2242        this.applyPersistedBuildStatus = function (buildStatus) {
2243            if (!buildStatus || typeof buildStatus !== 'object') {
2244                return;
2245            }
2246            if (buildStatus.jobId) {
2247                _this.lastBayesBotJobId(String(buildStatus.jobId));
2248            }
2249            if (buildStatus.questionText && !isLegacyHormuzPlaceholder(buildStatus.questionText)) {
2250                _this.boundQuestionText(String(buildStatus.questionText));
2251            }
2252            if (buildStatus.resolutionCriteria && !isLegacyHormuzPlaceholder(buildStatus.resolutionCriteria)) {
2253                _this.boundResolutionCriteria(String(buildStatus.resolutionCriteria));
2254            }
2255            if (buildStatus.resolutionDate) {
2256                _this.boundResolutionDate(bayesBotIsoDate(buildStatus.resolutionDate) || _this.boundResolutionDate());
2257            }
2258            if (buildStatus.headerMarkup) {
2259                var criteria = buildStatus.resolutionCriteria
2260                    ? String(buildStatus.resolutionCriteria)
2261                    : _this.boundResolutionCriteria();
2262                _this.applyHeaderMarkupImmediate(String(buildStatus.headerMarkup), criteria ? bayesBotHeaderInfoHtml('Resolves Yes', criteria) : _this.headerInfoHtml());
2263            }
2264            if (buildStatus.lifetimeInformation) {
2265                var lifetimeStatus = buildStatus.lifetimeInformation.status;
2266                var hasChart = Array.isArray(buildStatus.lifetimeInformation.breakpoints) &&
2267                    buildStatus.lifetimeInformation.breakpoints.length;
2268                if (hasChart ||
2269                    lifetimeStatus === 'awaiting_author_forecasts' ||
2270                    lifetimeStatus === 'filling_forecasts') {
2271                    _this.presentPersistedLifetimeInformation(buildStatus.lifetimeInformation);
2272                }
2273            }
2274            var status = String(buildStatus.status || '');
2275            if (status !== 'processing' && status !== 'queued') {
2276                return;
2277            }
2278            _this.proposalJobSettled = false;
2279            _this.nodeProposalSubmitting(true);
2280            _this.liveTemplateBuild = true;
2281            _this.proposalPhase('processing');
2282            var message = buildStatus.message || 'Working…';
2283            _this.proposalStatusText(message);
2284            _this.displayedProposalStatusText(message);
2285            _this.setProposalStatusText(message);
2286            if (buildStatus.jobId) {
2287                _this.pollBayesBotJobUntilDone(String(buildStatus.jobId));
2288            }
2289        };
2290        this.clearProposalStatusAnimation = function () {
2291            _this.statusTypewriterToken += 1;
2292            if (_this.statusTypewriterTimer) {
2293                clearTimeout(_this.statusTypewriterTimer);
2294                _this.statusTypewriterTimer = null;
2295            }
2296            _this.proposalStatusAnimating(false);
2297            _this.proposalStatusFreezeWidth('');
2298        };
2299        this.measureProposalStatusRowWidth = function () {
2300            var el = document.querySelector('.bayesBotProposalCard.isStatusFill .bayesBotProposalStatus');
2301            if (!el) {
2302                return 0;
2303            }
2304            var width = el.getBoundingClientRect().width;
2305            var parent = el.parentElement;
2306            var max = parent ? parent.getBoundingClientRect().width : width;
2307            return max > 0 ? Math.min(width, max) : width;
2308        };
2309        this.freezeProposalStatusRowWidth = function () {
2310            var width = _this.measureProposalStatusRowWidth();
2311            if (width > 0) {
2312                _this.proposalStatusFreezeWidth(Math.ceil(width) + 'px');
2313            }
2314        };
2315        this.lockProposalStatusRowToContent = function () {
2316            _this.proposalStatusFreezeWidth('');
2317            _this.freezeProposalStatusRowWidth();
2318        };
2319        this.setProposalStatusText = function (next, onDone) {
2320            var target = next == null ? '' : String(next);
2321            _this.proposalStatusText(target);
2322            if (_this.displayedProposalStatusText() === target && !_this.proposalStatusAnimating()) {
2323                if (onDone) {
2324                    onDone();
2325                }
2326                return;
2327            }
2328            _this.animateProposalStatusText(target, onDone);
2329        };
2330        this.animateProposalStatusText = function (target, onDone) {
2331            var token = ++_this.statusTypewriterToken;
2332            if (_this.statusTypewriterTimer) {
2333                clearTimeout(_this.statusTypewriterTimer);
2334                _this.statusTypewriterTimer = null;
2335            }
2336            var hadText = _this.displayedProposalStatusText().length > 0;
2337            if (hadText) {
2338                _this.freezeProposalStatusRowWidth();
2339            }
2340            _this.proposalStatusAnimating(hadText);
2341            var reveal = function () {
2342                if (token !== _this.statusTypewriterToken) {
2343                    return;
2344                }
2345                _this.proposalStatusAnimating(false);
2346                _this.displayedProposalStatusText(target);
2347                _this.proposalStatusFreezeWidth('');
2348                _this.freezeProposalStatusRowWidth();
2349                _this.statusTypewriterTimer = null;
2350                if (onDone) {
2351                    onDone();
2352                }
2353            };
2354            if (!hadText) {
2355                reveal();
2356                return;
2357            }
2358            var backspace = function () {
2359                if (token !== _this.statusTypewriterToken) {
2360                    return;
2361                }
2362                var shown = _this.displayedProposalStatusText();
2363                if (shown.length > 0) {
2364                    _this.displayedProposalStatusText(shown.slice(0, -1));
2365                    _this.statusTypewriterTimer = setTimeout(backspace, 22);
2366                    return;
2367                }
2368                _this.statusTypewriterTimer = setTimeout(reveal, hadText ? 80 : 0);
2369            };
2370            backspace();
2371        };
2372        this.lifetimeInformationFromJob = function (job) {
2373            if (!job) {
2374                return null;
2375            }
2376            if (job.results && job.results.lifetimeInformation) {
2377                return job.results.lifetimeInformation;
2378            }
2379            return job.lifetimeInformation || null;
2380        };
2381        this.presentPersistedLifetimeInformation = function (lifetimeInformation) {
2382            if (!lifetimeInformation) {
2383                return;
2384            }
2385            if (!_this.viewingBranchCopyNetId()) {
2386                _this.canonicalLifetimeInformation = lifetimeInformation;
2387            }
2388            var status = lifetimeInformation && lifetimeInformation.status;
2389            if (status === 'awaiting_author_forecasts' || status === 'filling_forecasts') {
2390                _this.scoresStatus(String(status));
2391            }
2392            else if (_this.lifetimeHasChart(lifetimeInformation)) {
2393                _this.scoresStatus('');
2394            }
2395            _this.proposalLifetimeInformation(lifetimeInformation);
2396            var self = _this;
2397            setTimeout(function () {
2398                if (self.leaderboardPanelTab() === 'informationGain') {
2399                    self.renderLifetimeInformationChart();
2400                }
2401            }, 50);
2402        };
2403        this.lifetimeScoreNetIdOnScreen = function () {
2404            var viewingBranch = _this.viewingBranchCopyNetId();
2405            if (viewingBranch) {
2406                return viewingBranch;
2407            }
2408            if (!_this.viewingSharedTemplate && _this.personalCopyNetId) {
2409                var copyId = parseInt(String(_this.personalCopyNetId), 10);
2410                if (Number.isFinite(copyId) && copyId > 0) {
2411                    return copyId;
2412                }
2413            }
2414            var templateId = _this.sharedTemplateNetId();
2415            return templateId != null ? templateId : null;
2416        };
2417        this.dismissProposalResult = function () {
2418            var wasError = _this.proposalPhase() === 'error';
2419            var prior = _this.submittedProposalText();
2420            _this.clearProposalStatusAnimation();
2421            _this.clearBayesBotJobPoll();
2422            _this.proposalPhase('idle');
2423            _this.submittedProposalText('');
2424            _this.proposalStatusText('');
2425            _this.displayedProposalStatusText('');
2426            _this.proposalStatusFreezeWidth('');
2427            _this.proposalFinalResponse('');
2428            _this.proposalInfoValue(null);
2429            _this.proposalSpecNotes([]);
2430            _this.showCreateNetLifetimeScore(false);
2431            _this.closeAddedSignalExplainer();
2432            _this.closeShannonExplainer();
2433            _this.nodeProposalSubmitting(false);
2434            if (wasError && prior) {
2435                _this.nodeProposalText(prior);
2436            }
2437        };
2438        // STRIPE THEME: remove this dispose + the classList.add in the constructor to revert.
2439        this.dispose = function () {
2440            if (Distiller.bayesBotViewModel === _this) {
2441                Distiller.bayesBotViewModel = null;
2442            }
2443            document.body.classList.remove('bayesBotStripeTheme');
2444            _this.closeLeaderboardScorePanel();
2445            _this.closeShannonExplainer();
2446            _this.clearProposalStatusAnimation();
2447            _this.destroyLifetimeInformationChart();
2448            _this.clearBayesBotJobPoll();
2449            _this.headerTypewriterToken += 1;
2450            if (_this.headerTypewriterTimer) {
2451                clearTimeout(_this.headerTypewriterTimer);
2452                _this.headerTypewriterTimer = null;
2453            }
2454            _this.stopAgentExampleRotation();
2455            if (_this.leaderboardScrollFrame != null && typeof cancelAnimationFrame === 'function') {
2456                cancelAnimationFrame(_this.leaderboardScrollFrame);
2457                _this.leaderboardScrollFrame = null;
2458            }
2459        };
2460        this.onAuthorBayesNetReady = function (net) {
2461            _this.authorBayesNet = net;
2462            if (net && typeof net.setOnSelectionChange === 'function') {
2463                net.setOnSelectionChange(function (nodeId) {
2464                    _this.selectedCanvasNodeId(nodeId != null ? Number(nodeId) : null);
2465                });
2466            }
2467            _this.applyPrimaryNodeHighlight();
2468            if (!_this.ownsCanonicalTemplate &&
2469                _this.personalCopyNetId &&
2470                _this.viewingSharedTemplate &&
2471                !_this.viewingBranchCopyNetId()) {
2472                void _this.adoptPersonalCopyFromServer(_this.personalCopyNetId);
2473            }
2474            var queued = _this.pendingBayesBotGraphEvents.slice();
2475            _this.pendingBayesBotGraphEvents = [];
2476            for (var i = 0; i < queued.length; i++) {
2477                _this.applyBayesBotGraphEvent(queued[i]);
2478            }
2479        };
2480        this.applyPrimaryNodeHighlight = function () {
2481            var net = _this.authorBayesNet;
2482            if (!net || typeof net.setPrimaryNodeId !== 'function') {
2483                return;
2484            }
2485            var primaryId = _this.resolvePrimaryNodeId();
2486            if (primaryId) {
2487                _this.leaderboardPrimaryNodeId(primaryId);
2488            }
2489            net.setPrimaryNodeId(primaryId);
2490        };
2491        this.applyBayesBotGraphEvent = function (data) {
2492            if (!data || !data.type) {
2493                return;
2494            }
2495            if (!_this.authorBayesNet) {
2496                _this.pendingBayesBotGraphEvents.push(data);
2497                return;
2498            }
2499            if (_this.viewingSharedTemplate && !_this.liveTemplateBuild && !_this.ownsCanonicalTemplate && !_this.agentActivityLive()) {
2500                if (data.type !== 'prior' &&
2501                    data.type !== 'conditional' &&
2502                    data.type !== 'node' &&
2503                    data.type !== 'nodeDeleted' &&
2504                    data.type !== 'connectionRemoved' &&
2505                    data.type !== 'connection') {
2506                    _this.pendingBayesBotGraphEvents.push(data);
2507                    return;
2508                }
2509            }
2510            if (data.type === 'node' && data.node && typeof _this.authorBayesNet.applyRemoteCreatedNode === 'function') {
2511                var applied = _this.authorBayesNet.applyRemoteCreatedNode(data.node, data.parentNodeId);
2512                if (!applied) {
2513                    _this.pendingBayesBotGraphEvents.push(data);
2514                    return;
2515                }
2516                if (!_this.leaderboardPrimaryNodeId() && data.node.id != null) {
2517                    _this.leaderboardPrimaryNodeId(parseInt(String(data.node.id), 10));
2518                }
2519                _this.applyPrimaryNodeHighlight();
2520                return;
2521            }
2522            if (data.type === 'nodeDeleted' && typeof _this.authorBayesNet.applyRemoteDeletedNode === 'function') {
2523                _this.authorBayesNet.applyRemoteDeletedNode(data.nodeId);
2524                return;
2525            }
2526            if (data.type === 'connection' && typeof _this.authorBayesNet.applyRemoteConnection === 'function') {
2527                _this.authorBayesNet.applyRemoteConnection(data.parentNodeId, data.childNodeId);
2528                _this.applyPrimaryNodeHighlight();
2529                return;
2530            }
2531            if (data.type === 'connectionRemoved' && typeof _this.authorBayesNet.applyRemoteDeletedConnection === 'function') {
2532                _this.authorBayesNet.applyRemoteDeletedConnection(data.parentNodeId, data.childNodeId);
2533                return;
2534            }
2535            if (data.type === 'prior' && typeof _this.authorBayesNet.applyRemotePriorForecast === 'function') {
2536                _this.authorBayesNet.applyRemotePriorForecast(data.nodeId, data.values || []);
2537                return;
2538            }
2539            if (data.type === 'conditional' && typeof _this.authorBayesNet.applyRemoteConditionalForecast === 'function') {
2540                _this.authorBayesNet.applyRemoteConditionalForecast(data.nodeId, data.rows || []);
2541            }
2542        };
2543        this.parseBayesBotSseData = function (event) {
2544            if (!event || event.data == null) {
2545                return null;
2546            }
2547            try {
2548                return typeof event.data === 'string' ? JSON.parse(event.data) : event.data;
2549            }
2550            catch (e) {
2551                console.error('BayesBot SSE parse failed:', e);
2552                return null;
2553            }
2554        };
2555        this.isCurrentBayesBotJob = function (data) {
2556            if (!data) {
2557                return false;
2558            }
2559            var current = _this.lastBayesBotJobId();
2560            if (!current) {
2561                return true;
2562            }
2563            return !data.jobId || String(data.jobId) === String(current);
2564        };
2565        this.onBayesBotStatusEvent = function (event) {
2566            var data = _this.parseBayesBotSseData(event);
2567            if (_this.isMergeForecastJob(data)) {
2568                if ((_this.mergeInFlight() || _this.mergeForecastPending) && data && data.message) {
2569                    var jobId = String(data.jobId);
2570                    if (!_this.mergeStatusJobId || _this.mergeStatusJobId === jobId) {
2571                        _this.mergeStatusJobId = jobId;
2572                        _this.setMergeStatusText(String(data.message));
2573                    }
2574                }
2575                return;
2576            }
2577            if (!_this.isCurrentBayesBotJob(data) || _this.proposalPhase() !== 'processing') {
2578                return;
2579            }
2580            if (data && data.message) {
2581                _this.setProposalStatusText(data.message);
2582            }
2583        };
2584        this.onBayesBotNodeCreatedEvent = function (event) {
2585            var data = _this.parseBayesBotSseData(event);
2586            if (!data || !data.node) {
2587                return;
2588            }
2589            if (!_this.isCurrentBayesBotJob(data) && !_this.agentActivityLive()) {
2590                return;
2591            }
2592            _this.liveTemplateBuild = true;
2593            if (!_this.authorBayesNetSet()) {
2594                _this.ensureCreateCanvasMounted(data);
2595            }
2596            _this.applyBayesBotGraphEvent({
2597                type: 'node',
2598                node: data.node,
2599                parentNodeId: data.parentNodeId,
2600            });
2601        };
2602        this.onBayesBotNodeDeletedEvent = function (event) {
2603            var data = _this.parseBayesBotSseData(event);
2604            if (!data) {
2605                return;
2606            }
2607            if (!_this.isCurrentBayesBotJob(data) && !_this.agentActivityLive()) {
2608                return;
2609            }
2610            _this.applyBayesBotGraphEvent({
2611                type: 'nodeDeleted',
2612                nodeId: data.nodeId,
2613            });
2614        };
2615        this.onBayesBotConnectionAddedEvent = function (event) {
2616            var data = _this.parseBayesBotSseData(event);
2617            if (!data) {
2618                return;
2619            }
2620            if (!_this.isCurrentBayesBotJob(data) && !_this.agentActivityLive()) {
2621                return;
2622            }
2623            _this.applyBayesBotGraphEvent({
2624                type: 'connection',
2625                parentNodeId: data.parentNodeId,
2626                childNodeId: data.childNodeId,
2627            });
2628        };
2629        this.onBayesBotConnectionRemovedEvent = function (event) {
2630            var data = _this.parseBayesBotSseData(event);
2631            if (!data) {
2632                return;
2633            }
2634            if (!_this.isCurrentBayesBotJob(data) && !_this.agentActivityLive()) {
2635                return;
2636            }
2637            _this.applyBayesBotGraphEvent({
2638                type: 'connectionRemoved',
2639                parentNodeId: data.parentNodeId,
2640                childNodeId: data.childNodeId,
2641            });
2642        };
2643        this.onBayesBotPriorForecastEvent = function (event) {
2644            var data = _this.parseBayesBotSseData(event);
2645            if (!data || _this.isMergeForecastJob(data)) {
2646                return;
2647            }
2648            if (!_this.isCurrentBayesBotJob(data) && !_this.agentActivityLive()) {
2649                return;
2650            }
2651            _this.applyBayesBotGraphEvent({
2652                type: 'prior',
2653                nodeId: data.nodeId,
2654                values: data.values || [],
2655            });
2656        };
2657        this.onBayesBotConditionalForecastEvent = function (event) {
2658            var data = _this.parseBayesBotSseData(event);
2659            if (!data || _this.isMergeForecastJob(data)) {
2660                return;
2661            }
2662            if (!_this.isCurrentBayesBotJob(data) && !_this.agentActivityLive()) {
2663                return;
2664            }
2665            _this.applyBayesBotGraphEvent({
2666                type: 'conditional',
2667                nodeId: data.nodeId,
2668                rows: data.rows || [],
2669            });
2670        };
2671        this.shouldAcceptBayesBotComplete = function (data) {
2672            if (!_this.isCurrentBayesBotJob(data) || !data || _this.proposalJobSettled) {
2673                return false;
2674            }
2675            if (_this.proposalPhase() === 'processing') {
2676                return true;
2677            }
2678            var currentJobId = _this.lastBayesBotJobId();
2679            if (currentJobId && String(data.jobId || '') === String(currentJobId)) {
2680                return true;
2681            }
2682            return !currentJobId && !!(_this.isCreateMode() || _this.agentActivityLive());
2683        };
2684        this.onBayesBotCompleteEvent = function (event) {
2685            var data = _this.parseBayesBotSseData(event);
2686            if (!_this.shouldAcceptBayesBotComplete(data)) {
2687                return;
2688            }
2689            _this.proposalJobSettled = true;
2690            var wasCreateMode = _this.isCreateMode();
2691            var createJobOk = wasCreateMode &&
2692                data.status !== 'error' &&
2693                data.draftStatus !== 'failed';
2694            var isHardFailure = !createJobOk && (data.status === 'error' || data.status === 'partial');
2695            _this.liveTemplateBuild = false;
2696            _this.proposalFinalResponse(data.responseText || data.error || '');
2697            _this.proposalInfoValue(data.infoValue || null);
2698            var incomingNotes = Array.isArray(data.specNotes)
2699                ? data.specNotes
2700                : Array.isArray(data.warnings)
2701                    ? data.warnings
2702                    : [];
2703            _this.proposalSpecNotes(incomingNotes.map(function (row) { return String(row || '').trim(); }).filter(function (row) { return !!row; }));
2704            if (_this.leaderboardPanelTab() === 'logs') {
2705                _this.loadSpecLogs(true);
2706            }
2707            if (data.lifetimeInformation &&
2708                Array.isArray(data.lifetimeInformation.breakpoints) &&
2709                data.lifetimeInformation.breakpoints.length) {
2710                _this.presentPersistedLifetimeInformation(data.lifetimeInformation);
2711            }
2712            _this.showCreateNetLifetimeScore(!!wasCreateMode);
2713            _this.applyLeaderboardFromInfoValue(data.infoValue);
2714            _this.nodeProposalSubmitting(false);
2715            _this.clearBayesBotJobPoll();
2716            if (isHardFailure) {
2717                _this.proposalPhase('error');
2718                _this.setProposalStatusText(data.message || 'Something went wrong');
2719                if (wasCreateMode) {
2720                    _this.applyHeaderMarkupImmediate(BAYES_BOT_CREATE_HEADER_MARKUP, BAYES_BOT_BAYES_NET_INFO);
2721                    _this.draftNeedsReplacement(true);
2722                    _this.draftProvisionError(data.error ||
2723                        data.message ||
2724                        'The model could not be built. Start a new draft to retry.');
2725                }
2726                return;
2727            }
2728            _this.clearProposalStatusAnimation();
2729            _this.proposalStatusText('');
2730            _this.displayedProposalStatusText('');
2731            _this.proposalStatusFreezeWidth('');
2732            _this.proposalPhase('complete');
2733            if (wasCreateMode) {
2734                _this.isCreateMode(false);
2735            }
2736            _this.clearDraftCreateUrl();
2737            if (wasCreateMode) {
2738                _this.ownsCanonicalTemplate = true;
2739                _this.authorOwnsTemplate(true);
2740            }
2741            if (data.copyNetId && !_this.ownsCanonicalTemplate && !_this.isTempVisitor()) {
2742                void _this.adoptPersonalCopyFromServer(data.copyNetId, data.setId).then(function (loadedCopy) {
2743                    if (!_this.authorBayesNetSet()) {
2744                        void _this.loadAuthorBayesNet();
2745                        return;
2746                    }
2747                    if (!loadedCopy) {
2748                        void _this.reloadAuthorBayesNetGraph();
2749                    }
2750                });
2751            }
2752            else if (!_this.authorBayesNetSet()) {
2753                void _this.loadAuthorBayesNet();
2754            }
2755            else {
2756                void _this.reloadAuthorBayesNetGraph();
2757            }
2758            _this.loadLeaderboard();
2759        };
2760        this.clearBayesBotJobPoll = function () {
2761            if (_this.bayesBotPollTimer) {
2762                clearTimeout(_this.bayesBotPollTimer);
2763                _this.bayesBotPollTimer = null;
2764            }
2765        };
2766        this.collectFinalResponseFromJob = function (job) {
2767            if (!job) {
2768                return '';
2769            }
2770            if (job.results && job.results.invalid && job.results.message) {
2771                return String(job.results.message);
2772            }
2773            var infoValueSummary = job.results && job.results.infoValueSummary && String(job.results.infoValueSummary).trim()
2774                ? String(job.results.infoValueSummary).trim()
2775                : '';
2776            if (infoValueSummary) {
2777                return infoValueSummary;
2778            }
2779            var tasks = job.results && (job.results.tasks || job.results.lanes)
2780                ? job.results.tasks || job.results.lanes
2781                : [];
2782            var answers = [];
2783            for (var i = 0; i < tasks.length; i++) {
2784                if (tasks[i] && tasks[i].answer && String(tasks[i].answer).trim()) {
2785                    answers.push(String(tasks[i].answer).trim());
2786                }
2787            }
2788            if (answers.length) {
2789                return answers.join('\n\n');
2790            }
2791            if (job.error) {
2792                return String(job.error);
2793            }
2794            return 'Proposal processed.';
2795        };
2796        this.formatCompactRank = function (rank, rankOf) {
2797            if (rank != null && rankOf > 0) {
2798                return '#' + rank;
2799            }
2800            if (rank != null) {
2801                return '#' + rank;
2802            }
2803            return '—';
2804        };
2805        this.applyLeaderboardNodes = function (nodes, rankOf) {
2806            var list = Array.isArray(nodes) ? nodes.slice() : [];
2807            var ranked = list.filter(function (n) { return n && !n.isEndState; });
2808            ranked.sort(function (a, b) {
2809                var ar = a.rank != null ? a.rank : Number.MAX_SAFE_INTEGER;
2810                var br = b.rank != null ? b.rank : Number.MAX_SAFE_INTEGER;
2811                if (ar !== br) {
2812                    return ar - br;
2813                }
2814                return String(a.title || '').localeCompare(String(b.title || ''));
2815            });
2816            _this.leaderboardNodes(ranked);
2817            var inferred = rankOf != null && rankOf > 0
2818                ? rankOf
2819                : ranked.reduce(function (max, n) {
2820                    var r = n && n.rank != null ? n.rank : 0;
2821                    return r > max ? r : max;
2822                }, ranked.length);
2823            _this.leaderboardRankOf(inferred);
2824        };
2825        this.applyLeaderboardFromInfoValue = function (info) {
2826            if (!info || !Array.isArray(info.nodes) || !info.nodes.length) {
2827                _this.loadLeaderboard();
2828                return;
2829            }
2830            if (info.endStateNodeId) {
2831                _this.leaderboardPrimaryNodeId(info.endStateNodeId);
2832            }
2833            _this.applyLeaderboardNodes(info.nodes, info.rankOf);
2834            _this.applyPrimaryNodeHighlight();
2835        };
2836        this.loadLeaderboard = function () {
2837            var currentUser = Distiller.currentUser && Distiller.currentUser();
2838            var userId = currentUser && currentUser.userId;
2839            if (!userId) {
2840                return;
2841            }
2842            var onScreenNetId = _this.lifetimeScoreNetIdOnScreen();
2843            Distiller.httpGetAsync(Distiller.apiEndpoint + '/api/bayesBotLeaderboard/' + userId
2844                + '?communityId=' + encodeURIComponent(String(_this.resolveBoundCommunityId()))
2845                + (onScreenNetId
2846                    ? '&netId=' + encodeURIComponent(String(onScreenNetId))
2847                    : ''), function (responseText) {
2848                try {
2849                    var parsed = typeof responseText === 'string' ? JSON.parse(responseText) : responseText;
2850                    if (!parsed || parsed.ok === false) {
2851                        return;
2852                    }
2853                    _this.scoresStatus(parsed.scoresStatus ? String(parsed.scoresStatus) : '');
2854                    _this.leaderboardPrimaryNodeId(parsed.primaryNodeId || null);
2855                    _this.applyLeaderboardNodes(parsed.nodes || [], parsed.rankOf);
2856                    _this.applyPrimaryNodeHighlight();
2857                }
2858                catch (e) {
2859                    console.error('BayesBot leaderboard parse failed:', e);
2860                }
2861            });
2862        };
2863        this.leaderboardRows = ko.pureComputed(function () {
2864            var rankOf = _this.leaderboardRankOf() || 0;
2865            var pinnedId = _this.pinnedLeaderboardNodeId();
2866            var selectedId = pinnedId != null ? pinnedId : _this.selectedCanvasNodeId();
2867            return _this.leaderboardNodes().map(function (node) { return ({
2868                nodeId: node.nodeId,
2869                title: node.title || 'Untitled node',
2870                isNew: !!node.isNew,
2871                isSelected: _this.canvasSelectionMatchesRow(selectedId, node),
2872                metric: _this.enrichLeaderboardMetric(node),
2873                usefulnessScore: _this.formatUsefulnessScore(node.informationValue),
2874                overall: _this.formatCompactRank(node.rank, rankOf),
2875                overallFull: _this.formatInfoValueRank(node.rank, rankOf),
2876                diagnosticity: _this.formatCompactRank(node.diagnosticityRank, rankOf),
2877                diagnosticityFull: _this.formatInfoValueRank(node.diagnosticityRank, rankOf),
2878                timeliness: _this.formatCompactRank(node.timelinessRank, rankOf),
2879                timelinessFull: _this.formatInfoValueRank(node.timelinessRank, rankOf),
2880            }); });
2881        });
2882        this.formatUsefulnessScore = function (value) {
2883            if (value == null || !isFinite(Number(value))) {
2884                return '—';
2885            }
2886            return Number(value).toFixed(2);
2887        };
2888        this.formatAddedSignal = function (value) {
2889            if (value == null || !isFinite(Number(value))) {
2890                return '—';
2891            }
2892            return Number(value).toFixed(2);
2893        };
2894        this.enrichLeaderboardMetric = function (node) {
2895            if (!node) {
2896                return node;
2897            }
2898            var metric = Object.assign({}, node);
2899            if (metric.diagnosticityDeltaPrc == null && metric.impactScore != null && isFinite(Number(metric.impactScore))) {
2900                metric.diagnosticityDeltaPrc = Math.round(Number(metric.impactScore) * 1000) / 10;
2901            }
2902            return metric;
2903        };
2904        this.formatInfoValueRank = function (rank, rankOf) {
2905            if (rank != null && rankOf > 0) {
2906                return '#' + rank + ' of ' + rankOf;
2907            }
2908            return 'unranked';
2909        };
2910        this.proposalInfoValueRows = ko.pureComputed(function () {
2911            var info = _this.proposalInfoValue();
2912            var highlighted = info && Array.isArray(info.highlighted) ? info.highlighted : [];
2913            var many = highlighted.length > 1;
2914            return highlighted.map(function (node) {
2915                var rankOf = node.rankOf != null ? node.rankOf : (info && info.rankOf) || 0;
2916                return {
2917                    metric: node,
2918                    who: many ? '"' + (node.title || 'This node') + '"' : 'Your node',
2919                    overall: _this.formatInfoValueRank(node.rank, rankOf),
2920                    diagnosticity: _this.formatInfoValueRank(node.diagnosticityRank, rankOf),
2921                    timeliness: _this.formatInfoValueRank(node.timelinessRank, rankOf),
2922                    addedSignalLabel: _this.formatAddedSignal(node.addedSignal),
2923                };
2924            });
2925        });
2926        this.missingForecastLeaderboardMessage = ko.pureComputed(function () {
2927            return _this.authorOwnsTemplate()
2928                ? 'You must provide missing forecasts before we can provide a node leaderboard.'
2929                : 'The author of this model must provide missing forecasts before we can provide a node leaderboard.';
2930        });
2931        this.missingForecastInformationMessage = ko.pureComputed(function () {
2932            return _this.authorOwnsTemplate()
2933                ? 'You must provide missing forecasts before we can calculate expected information gain over time.'
2934                : 'The author of this model must provide missing forecasts before we can calculate expected information gain over time.';
2935        });
2936        this.hasLifetimeChart = ko.pureComputed(function () {
2937            var result = _this.proposalLifetimeInformation();
2938            return !!(result && Array.isArray(result.breakpoints) && result.breakpoints.length);
2939        });
2940        this.formatLeadBeforeResolution = function (timeMs, endMs) {
2941            if (timeMs == null || endMs == null || !isFinite(timeMs) || !isFinite(endMs)) {
2942                return 'before resolution';
2943            }
2944            var ms = endMs - timeMs;
2945            if (ms <= 0) {
2946                return 'at resolution';
2947            }
2948            var hours = ms / 3600000;
2949            var days = hours / 24;
2950            var weeks = days / 7;
2951            var months = days / 30.437;
2952            var years = days / 365.2425;
2953            var formatCount = function (n, unit) {
2954                var rounded = n >= 10 ? Math.round(n) : Math.round(n * 10) / 10;
2955                var shown = Math.abs(rounded - Math.round(rounded)) < 0.05 ? String(Math.round(rounded)) : rounded.toFixed(1);
2956                var plural = Number(shown) === 1 ? unit : unit + 's';
2957                return shown + ' ' + plural + ' before resolution';
2958            };
2959            if (years >= 1) {
2960                return formatCount(years, 'year');
2961            }
2962            if (months >= 1) {
2963                return formatCount(months, 'month');
2964            }
2965            if (weeks >= 1) {
2966                return formatCount(weeks, 'week');
2967            }
2968            if (days >= 1) {
2969                return formatCount(days, 'day');
2970            }
2971            return formatCount(Math.max(hours, 1), 'hour');
2972        };
2973        this.lifetimeTimepointRows = ko.pureComputed(function () {
2974            var result = _this.proposalLifetimeInformation();
2975            var breakpoints = _this.lifetimeChartBreakpoints(result);
2976            var endMs = result && result.endTimeMs != null
2977                ? Number(result.endTimeMs)
2978                : breakpoints.length
2979                    ? breakpoints[breakpoints.length - 1].timeMs
2980                    : null;
2981            var rows = [];
2982            for (var i = 0; i < breakpoints.length; i++) {
2983                var bp = breakpoints[i];
2984                if (!bp || bp.isStart) {
2985                    continue;
2986                }
2987                var pct = Math.round(Number(bp.normalizedInformation || 0) * 100);
2988                rows.push({
2989                    label: _this.formatUtcLongDate(bp.timeMs) +
2990                        ' (' +
2991                        _this.formatLeadBeforeResolution(bp.timeMs, endMs) +
2992                        '): ' +
2993                        pct +
2994                        '%',
2995                });
2996            }
2997            return rows;
2998        });
2999        this.shannonBitsLabel = ko.pureComputed(function () {
3000            var result = _this.proposalLifetimeInformation();
3001            var bits = result && result.h0Bits != null ? Number(result.h0Bits) : NaN;
3002            if (!isFinite(bits)) {
3003                return 'an unknown number of bits of uncertainty';
3004            }
3005            return (Math.round(bits * 100) / 100).toFixed(2) + ' bits of uncertainty';
3006        });
3007        this.shannonExplainerTerms = ko.pureComputed(function () {
3008            var result = _this.proposalLifetimeInformation();
3009            var states = result && Array.isArray(result.h0States) ? result.h0States : [];
3010            return states.map(function (state) {
3011                var p = Number(state && state.probability);
3012                var name = state && state.name ? String(state.name) : 'State';
3013                if (!(p > 0)) {
3014                    return { name: name, line: 'P = 0 (does not contribute)' };
3015                }
3016                var term = -(p * (Math.log(p) / Math.log(2)));
3017                return {
3018                    name: name,
3019                    line: 'P = ' +
3020                        p.toFixed(2) +
3021                        ', −p log₂(p) = ' +
3022                        (Math.round(term * 100) / 100).toFixed(2),
3023                };
3024            });
3025        });
3026        this.shannonExplainerTotal = ko.pureComputed(function () {
3027            var result = _this.proposalLifetimeInformation();
3028            var bits = result && result.h0Bits != null ? Number(result.h0Bits) : NaN;
3029            if (!isFinite(bits)) {
3030                return 'H₀ is unavailable for this net.';
3031            }
3032            return '= ' + (Math.round(bits * 100) / 100).toFixed(2) + ' bits';
3033        });
3034        this.showShannonExplainer = function (_data, event) {
3035            if (event && typeof event.preventDefault === 'function') {
3036                event.preventDefault();
3037                event.stopPropagation();
3038            }
3039            _this.shannonExplainerOpen(true);
3040        };
3041        this.closeShannonExplainer = function () {
3042            _this.shannonExplainerOpen(false);
3043        };
3044        this.lifetimeInformationView = ko.pureComputed(function () {
3045            if (_this.proposalPhase() === 'processing' && _this.showCreateNetLifetimeScore()) {
3046                return {
3047                    kind: 'loading',
3048                    headline: 'Measuring lifetime information…',
3049                    explanation: '',
3050                    rawAreaLabel: '',
3051                    warningLines: [],
3052                    estimated: false,
3053                    showChart: false,
3054                    percentText: '',
3055                };
3056            }
3057            if (!_this.showCreateNetLifetimeScore()) {
3058                return null;
3059            }
3060            var result = _this.proposalLifetimeInformation();
3061            if (!result) {
3062                if (_this.proposalPhase() === 'complete') {
3063                    return {
3064                        kind: 'loading',
3065                        headline: 'Measuring lifetime information…',
3066                        explanation: '',
3067                        rawAreaLabel: '',
3068                        warningLines: [],
3069                        estimated: false,
3070                        showChart: false,
3071                        percentText: '',
3072                    };
3073                }
3074                return null;
3075            }
3076            return _this.mapLifetimeInformationUi(result);
3077        });
3078        this.mapLifetimeInformationUi = function (result) {
3079            var exclusions = (result && result.exclusions) || {};
3080            var warningLines = [];
3081            var undated = exclusions.undatedCount || 0;
3082            var post = exclusions.postPrimaryCount || 0;
3083            if (undated > 0) {
3084                warningLines.push(undated === 1
3085                    ? '1 node was excluded because it has no resolution date.'
3086                    : String(undated) + ' nodes were excluded because they have no resolution date.');
3087            }
3088            if (post > 0) {
3089                warningLines.push(post === 1
3090                    ? '1 node resolves at or after the primary question and does not contribute early-warning value.'
3091                    : String(post) +
3092                        ' nodes resolve at or after the primary question and do not contribute early-warning value.');
3093            }
3094            if (result && result.calculationWarning) {
3095                warningLines.push(String(result.calculationWarning));
3096            }
3097            if (!result || result.status === 'error') {
3098                return {
3099                    kind: 'error',
3100                    headline: 'Lifetime Information Score could not be calculated.',
3101                    explanation: (result && result.errorMessage) ||
3102                        'Something went wrong while measuring expected information.',
3103                    rawAreaLabel: '',
3104                    warningLines: warningLines,
3105                    estimated: false,
3106                    showChart: false,
3107                    percentText: '',
3108                };
3109            }
3110            if (result.status === 'missingPrimaryDate') {
3111                return {
3112                    kind: 'missingPrimaryDate',
3113                    headline: 'Lifetime Information Score unavailable: the primary question needs a future resolution date.',
3114                    explanation: '',
3115                    rawAreaLabel: '',
3116                    warningLines: warningLines,
3117                    estimated: false,
3118                    showChart: false,
3119                    percentText: '',
3120                };
3121            }
3122            if (result.status === 'alreadyCertain') {
3123                return {
3124                    kind: 'alreadyCertain',
3125                    headline: 'Not applicable: the primary outcome is already certain.',
3126                    explanation: 'There is no remaining uncertainty for the net to eliminate.',
3127                    rawAreaLabel: 'Information-time value: 0 bit-days',
3128                    warningLines: warningLines,
3129                    estimated: false,
3130                    showChart: false,
3131                    percentText: '',
3132                };
3133            }
3134            var percent = result.lifetimeInformationPercent != null && isFinite(Number(result.lifetimeInformationPercent))
3135                ? Number(result.lifetimeInformationPercent)
3136                : 0;
3137            var percentText = String(Math.round(percent)) + '%';
3138            var unit = result.displayUnit || 'bit-days';
3139            var rawValue = result.rawAreaDisplayValue != null && isFinite(Number(result.rawAreaDisplayValue))
3140                ? Number(result.rawAreaDisplayValue)
3141                : 0;
3142            var rawRounded = Math.round(rawValue * 10) / 10;
3143            var rawLabel = Math.abs(rawRounded - Math.round(rawRounded)) < 0.05
3144                ? String(Math.round(rawRounded))
3145                : rawRounded.toFixed(1);
3146            return {
3147                kind: result.status === 'noEligibleNodes' ? 'noEligibleNodes' : 'score',
3148                headline: 'Lifetime Information Score: ' + percentText,
3149                explanation: 'This Bayes net is expected to eliminate an average of ' +
3150                    percentText +
3151                    " of the primary question's current uncertainty across its remaining lifetime. Information that becomes available earlier contributes more to the score. The shaded area is the Lifetime Information Score.",
3152                rawAreaLabel: 'Information-time value: ' + rawLabel + ' ' + unit,
3153                warningLines: warningLines,
3154                estimated: result.exact === false,
3155                showChart: true,
3156                percentText: percentText,
3157            };
3158        };
3159        this.formatUtcLongDate = function (timeMs) {
3160            var months = [
3161                'January', 'February', 'March', 'April', 'May', 'June',
3162                'July', 'August', 'September', 'October', 'November', 'December',
3163            ];
3164            var d = new Date(timeMs);
3165            return months[d.getUTCMonth()] + ' ' + d.getUTCDate() + ', ' + d.getUTCFullYear();
3166        };
3167        this.destroyLifetimeInformationChart = function () {
3168            if (_this.lifetimeInfoChart && typeof _this.lifetimeInfoChart.destroy === 'function') {
3169                _this.lifetimeInfoChart.destroy();
3170            }
3171            _this.lifetimeInfoChart = null;
3172        };
3173        this.renderLifetimeInformationChart = function () {
3174            _this.destroyLifetimeInformationChart();
3175            if (_this.leaderboardPanelTab() !== 'informationGain') {
3176                return;
3177            }
3178            var result = _this.proposalLifetimeInformation();
3179            if (!result || !Array.isArray(result.breakpoints) || !result.breakpoints.length) {
3180                return;
3181            }
3182            var canvas = document.querySelector('.bayesBotInformationGainBody .bayesBotLifetimeChartCanvas');
3183            if (!canvas) {
3184                return;
3185            }
3186            var ChartLib = window.Chart;
3187            if (!ChartLib) {
3188                return;
3189            }
3190            var breakpoints = _this.lifetimeChartBreakpoints(result);
3191            var ticks = Array.isArray(result.chartTicks) ? result.chartTicks : [];
3192            var labeledTicks = ticks.length > 1 ? ticks.slice(1) : ticks;
3193            var startMs = result.startTimeMs != null ? result.startTimeMs : breakpoints[0].timeMs;
3194            var endMs = result.endTimeMs != null
3195                ? result.endTimeMs
3196                : breakpoints[breakpoints.length - 1].timeMs;
3197            var axisEndMs = _this.lifetimeChartAxisEndMs(startMs, endMs, ticks);
3198            var dataPoints = breakpoints.map(function (bp) { return ({
3199                x: bp.timeMs,
3200                y: Number(bp.normalizedInformation || 0) * 100,
3201            }); });
3202            var self = _this;
3203            var ctx = canvas.getContext('2d');
3204            _this.lifetimeInfoChart = new ChartLib(ctx, {
3205                type: 'line',
3206                data: {
3207                    datasets: [
3208                        {
3209                            label: 'Expected Information Gain',
3210                            data: dataPoints,
3211                            // Chart.js 3: 'before' holds y until the next event, then jumps.
3212                            // 'after' applies the next y immediately, which painted the first
3213                            // resolution's height from t0.
3214                            stepped: 'before',
3215                            fill: 'origin',
3216                            clip: false,
3217                            borderColor: '#2563EB',
3218                            backgroundColor: 'rgba(37, 99, 235, 0.18)',
3219                            borderWidth: 2,
3220                            tension: 0,
3221                            pointRadius: function (ctx) {
3222                                var bp = breakpoints[ctx.dataIndex];
3223                                if (!bp) {
3224                                    return 0;
3225                                }
3226                                if (bp.isPrimaryResolution) {
3227                                    return 6;
3228                                }
3229                                if (bp.resolvingNodeIds && bp.resolvingNodeIds.length) {
3230                                    return 4;
3231                                }
3232                                if (bp.isStart) {
3233                                    return 3;
3234                                }
3235                                return 2;
3236                            },
3237                            pointHoverRadius: 7,
3238                            pointHitRadius: 10,
3239                            pointBackgroundColor: function (ctx) {
3240                                var bp = breakpoints[ctx.dataIndex];
3241                                if (bp && bp.isPrimaryResolution) {
3242                                    return '#2563EB';
3243                                }
3244                                return '#06B6D4';
3245                            },
3246                            pointBorderColor: '#172554',
3247                            pointBorderWidth: 1,
3248                        },
3249                    ],
3250                },
3251                options: {
3252                    responsive: true,
3253                    maintainAspectRatio: false,
3254                    animation: false,
3255                    interaction: { mode: 'point', intersect: true },
3256                    onHover: function (evt, elements) {
3257                        var canvasEl = evt && evt.native && evt.native.target;
3258                        if (canvasEl && canvasEl.style) {
3259                            canvasEl.style.cursor = elements && elements.length ? 'pointer' : 'default';
3260                        }
3261                    },
3262                    onClick: function (evt, elements) {
3263                        if (!elements || !elements.length) {
3264                            return;
3265                        }
3266                        var idx = elements[0].index;
3267                        var bp = breakpoints[idx];
3268                        if (!bp) {
3269                            return;
3270                        }
3271                        var native = (evt && evt.native) || evt;
3272                        self.showLifetimeTimepointModal(bp, native);
3273                    },
3274                    plugins: {
3275                        filler: { propagate: false },
3276                        legend: { display: false },
3277                        tooltip: {
3278                            enabled: true,
3279                            mode: 'point',
3280                            intersect: true,
3281                            displayColors: false,
3282                            callbacks: {
3283                                title: function (items) {
3284                                    if (!items || !items.length) {
3285                                        return '';
3286                                    }
3287                                    var x = items[0].parsed && items[0].parsed.x;
3288                                    var bp = self.closestLifetimeBreakpoint(breakpoints, x);
3289                                    return bp ? self.formatUtcLongDate(bp.timeMs) : '';
3290                                },
3291                                label: function (item) {
3292                                    var x = item && item.parsed && item.parsed.x;
3293                                    var bp = self.closestLifetimeBreakpoint(breakpoints, x);
3294                                    if (!bp) {
3295                                        return '';
3296                                    }
3297                                    var pct = Math.round(Number(bp.normalizedInformation || 0) * 100);
3298                                    return pct + '% information gained';
3299                                },
3300                                afterBody: function () {
3301                                    return ['Click for details'];
3302                                },
3303                            },
3304                        },
3305                    },
3306                    scales: {
3307                        x: {
3308                            type: 'linear',
3309                            min: startMs,
3310                            max: axisEndMs,
3311                            title: { display: false },
3312                            afterBuildTicks: function (axis) {
3313                                if (!labeledTicks.length) {
3314                                    return;
3315                                }
3316                                axis.ticks = labeledTicks.map(function (tick) {
3317                                    return { value: tick.timeMs };
3318                                });
3319                            },
3320                            ticks: {
3321                                autoSkip: false,
3322                                maxRotation: 45,
3323                                minRotation: 0,
3324                                callback: function (value) {
3325                                    var n = Number(value);
3326                                    for (var i = 0; i < labeledTicks.length; i++) {
3327                                        if (Math.abs(labeledTicks[i].timeMs - n) < 1) {
3328                                            return labeledTicks[i].label;
3329                                        }
3330                                    }
3331                                    return '';
3332                                },
3333                            },
3334                        },
3335                        y: {
3336                            min: 0,
3337                            max: 108,
3338                            title: {
3339                                display: true,
3340                                text: 'Expected Information Gain',
3341                                color: '#64748B',
3342                                font: { size: 11 },
3343                            },
3344                            afterBuildTicks: function (axis) {
3345                                axis.ticks = [0, 20, 40, 60, 80, 100].map(function (value) {
3346                                    return { value: value };
3347                                });
3348                            },
3349                            ticks: {
3350                                callback: function (value) {
3351                                    var n = Number(value);
3352                                    if (!isFinite(n) || n > 100) {
3353                                        return '';
3354                                    }
3355                                    return String(n) + '%';
3356                                },
3357                            },
3358                        },
3359                    },
3360                },
3361            });
3362        };
3363        this.lifetimeChartTickScale = function (startMs, endMs) {
3364            var horizonDays = (endMs - startMs) / 86400000;
3365            var daysPerYear = 365.2425;
3366            if (horizonDays < 90) {
3367                return {
3368                    unit: 'day',
3369                    step: horizonDays <= 16 ? 1 : Math.max(1, Math.ceil(horizonDays / 10)),
3370                };
3371            }
3372            if (horizonDays < 2 * daysPerYear) {
3373                if (horizonDays < 180) {
3374                    return { unit: 'week', step: horizonDays < 120 ? 1 : 2 };
3375                }
3376                return { unit: 'month', step: 1 };
3377            }
3378            if (horizonDays < 8 * daysPerYear) {
3379                return { unit: 'month', step: horizonDays < 4 * daysPerYear ? 3 : 6 };
3380            }
3381            return { unit: 'year', step: 1 };
3382        };
3383        this.addLifetimeAxisPad = function (timeMs, unit, step) {
3384            var d = new Date(timeMs);
3385            var y = d.getUTCFullYear();
3386            var m = d.getUTCMonth();
3387            var day = d.getUTCDate();
3388            if (unit === 'day') {
3389                return Date.UTC(y, m, day + step);
3390            }
3391            if (unit === 'week') {
3392                return Date.UTC(y, m, day + step * 7);
3393            }
3394            if (unit === 'month') {
3395                return Date.UTC(y, m + step, day);
3396            }
3397            return Date.UTC(y + step, m, day);
3398        };
3399        this.lifetimeChartAxisEndMs = function (startMs, endMs, ticks) {
3400            if (ticks && ticks.length >= 3) {
3401                var gap = Number(ticks[ticks.length - 1].timeMs) - Number(ticks[ticks.length - 2].timeMs);
3402                if (gap > 0 && gap < endMs - startMs) {
3403                    return endMs + gap;
3404                }
3405            }
3406            var scale = _this.lifetimeChartTickScale(startMs, endMs);
3407            return _this.addLifetimeAxisPad(endMs, scale.unit, scale.step);
3408        };
3409        this.lifetimeChartBreakpoints = function (result) {
3410            var rows = result && Array.isArray(result.breakpoints) ? result.breakpoints : [];
3411            var h0 = result && result.h0Bits != null && isFinite(Number(result.h0Bits))
3412                ? Number(result.h0Bits)
3413                : null;
3414            return rows.map(function (bp) {
3415                if (!bp || !bp.isPrimaryResolution) {
3416                    return bp;
3417                }
3418                return __assign(__assign({}, bp), { expectedEntropyBits: 0, informationBits: h0 != null ? h0 : bp.informationBits, normalizedInformation: 1 });
3419            });
3420        };
3421        this.closestLifetimeBreakpoint = function (breakpoints, timeMs) {
3422            if (!breakpoints || !breakpoints.length || timeMs == null || !isFinite(Number(timeMs))) {
3423                return null;
3424            }
3425            var best = breakpoints[0];
3426            var bestD = Math.abs(best.timeMs - timeMs);
3427            for (var i = 1; i < breakpoints.length; i++) {
3428                var d = Math.abs(breakpoints[i].timeMs - timeMs);
3429                if (d < bestD) {
3430                    best = breakpoints[i];
3431                    bestD = d;
3432                }
3433            }
3434            return best;
3435        };
3436        this.formatPercentagePoints = function (value) {
3437            if (value == null || !isFinite(Number(value))) {
3438                return 'an unknown amount';
3439            }
3440            var n = Math.round(Number(value) * 10) / 10;
3441            var label = Math.abs(n - Math.round(n)) < 0.05 ? String(Math.round(n)) : n.toFixed(1);
3442            return label + ' percentage point' + (Number(label) === 1 ? '' : 's');
3443        };
3444        this.formatLeadDays = function (days) {
3445            if (days == null || !isFinite(Number(days))) {
3446                return null;
3447            }
3448            var n = Math.round(Number(days));
3449            return String(n) + ' day' + (Math.abs(n) === 1 ? '' : 's');
3450        };
3451        this.wrapInfoHtml = function (html) {
3452            return '<div style="font-size:13px;line-height:1.5;font-family:Inter,-apple-system,BlinkMacSystemFont,Segoe UI,sans-serif;">' + html + '</div>';
3453            // ORIGINAL: return '<div style="font-size:12px;line-height:1.45;">
3453' + html + '</div>';
3454        };
3455        this.resolvePrimaryNodeId = function () {
3456            var net = _this.authorBayesNet;
3457            var nodes = net && net.nodes ? net.nodes : [];
3458            var liveIds = {};
3459            for (var i = 0; i < nodes.length; i++) {
3460                liveIds[Number(nodes[i].id)] = true;
3461            }
3462            var stored = _this.leaderboardPrimaryNodeId();
3463            if (stored && liveIds[Number(stored)]) {
3464                return Number(stored);
3465            }
3466            var question = String(_this.boundQuestionText() || '').trim().toLowerCase();
3467            var header = stripBayesBotHeaderMarkup(_this.headerMarkup() || '').trim().toLowerCase();
3468            for (var i = 0; i < nodes.length; i++) {
3469                var title = String(nodes[i].title || '').trim().toLowerCase();
3470                if (!title) {
3471                    continue;
3472                }
3473                if (question && (question.indexOf(title) >= 0 || title.indexOf(question) >= 0)) {
3474                    return Number(nodes[i].id);
3475                }
3476                if (header && (header.indexOf(title) >= 0 || title.indexOf(header) >= 0)) {
3477                    return Number(nodes[i].id);
3478                }
3479            }
3480            var bestId = null;
3481            var bestCount = 0;
3482            var incoming = {};
3483            for (var i = 0; i < nodes.length; i++) {
3484                incoming[Number(nodes[i].id)] = incoming[Number(nodes[i].id)] || 0;
3485                var children = nodes[i].childrenIds || [];
3486                for (var c = 0; c < children.length; c++) {
3487                    var childId = Number(children[c]);
3488                    incoming[childId] = (incoming[childId] || 0) + 1;
3489                }
3490            }
3491            var keys = Object.keys(incoming);
3492            for (var i = 0; i < keys.length; i++) {
3493                var id = Number(keys[i]);
3494                if (incoming[id] > bestCount) {
3495                    bestCount = incoming[id];
3496                    bestId = id;
3497                }
3498            }
3499            return bestCount > 0 ? bestId : (stored || null);
3500        };
3501        this.liveDiagnosticityDeltaPrc = function (signalNodeId) {
3502            var net = _this.authorBayesNet;
3503            var primaryId = _this.resolvePrimaryNodeId();
3504            var swingFn = net && typeof net.expectedObservationSwingPrc === 'function'
3505                ? net.expectedObservationSwingPrc
3506                : net && typeof net.maxObservationSwingPrc === 'function'
3507                    ? net.maxObservationSwingPrc
3508                    : null;
3509            if (!net || !primaryId || !signalNodeId || !swingFn) {
3510                return null;
3511            }
3512            var swing = swingFn(signalNodeId, primaryId);
3513            return swing != null && isFinite(Number(swing)) ? Number(swing) : null;
3514        };
3515        this.diagnosticityInfoHtml = function (node) {
3516            var live = _this.liveDiagnosticityDeltaPrc(node && node.nodeId);
3517            var delta = live != null
3518                ? _this.formatPercentagePoints(live)
3519                : node && node.diagnosticityDeltaPrc != null
3520                    ? _this.formatPercentagePoints(node.diagnosticityDeltaPrc)
3521                    : 'an unknown amount';
3522            return _this.wrapInfoHtml('How much is the forecast on the primary question expected to move when this node resolves?' +
3523                '<br><br>Learning this node\'s outcome is expected to move a probability on that question by <span style="font-family:Inter,sans-serif;font-weight:600;color:#3B82F6;">' +
3524                delta +
3525                '</span>. Unlikely states only count in proportion to how likely they are.');
3526        };
3527        this.timelinessInfoHtml = function (node) {
3528            var intro = 'How far in advance will we know the outcome to this question? The further in advance we get a signal from your node, the better!';
3529            var days = node ? _this.formatLeadDays(node.leadDays) : null;
3530            var resolutionDate = node && node.resolutionDate ? String(node.resolutionDate) : '';
3531            var todayUtc = new Date().toISOString().slice(0, 10);
3532            if (resolutionDate && resolutionDate <= todayUtc) {
3533                return _this.wrapInfoHtml(intro +
3534                    '<br><br>This node resolved on <span style="font-family:Inter,sans-serif;font-weight:600;color:#3B82F6;">' +
3535                    resolutionDate +
3536                    '</span>, so it no longer scores for timeliness.');
3537            }
3538            if (days == null) {
3539                if (resolutionDate) {
3540                    return _this.wrapInfoHtml(intro +
3541                        '<br><br>This node is set to resolve on <span style="font-family:Inter,sans-serif;font-weight:600;color:#3B82F6;">' +
3542                        resolutionDate +
3543                        '</span>.');
3544                }
3545                return _this.wrapInfoHtml(intro + '<br><br>We could not read a resolution date for this node, so timeliness could not be scored.');
3546            }
3547            if (node.leadDays <= 0) {
3548                return _this.wrapInfoHtml(intro +
3549                    '<br><br>This node is set to resolve on or after the primary question, so it does not provide an early signal.');
3550            }
3551            return _this.wrapInfoHtml(intro +
3552                '<br><br>We\'ll know this node\'s outcome <span style="font-family:Inter,sans-serif;font-weight:600;color:#3B82F6;">' +
3553                days +
3554                '</span> before the primary question resolves.');
3555        };
3556        this.showDiagnosticityInfo = function (row, event) {
3557            Distiller.showInfo(_this.diagnosticityInfoHtml(row && row.metric), event);
3558        };
3559        this.showTimelinessInfo = function (row, event) {
3560            Distiller.showInfo(_this.timelinessInfoHtml(row && row.metric), event);
3561        };
3562        this.showAddedSignalExplainer = function (row, event) {
3563            if (event) {
3564                event.preventDefault();
3565                event.stopPropagation();
3566            }
3567            Distiller.visibleInfo(null);
3568            _this.addedSignalExplainerRow(row || null);
3569            _this.addedSignalExplainerOpen(true);
3570        };
3571        this.closeAddedSignalExplainer = function () {
3572            _this.addedSignalExplainerOpen(false);
3573            _this.addedSignalExplainerRow(null);
3574        };
3575        this.resolveCanvasNodeId = function (nodeId, title) {
3576            var net = _this.authorBayesNet;
3577            var parsed = nodeId != null ? parseInt(String(nodeId), 10) : NaN;
3578            if (net && typeof net.resolveLiveNodeId === 'function' && Number.isFinite(parsed)) {
3579                var live = net.resolveLiveNodeId(parsed);
3580                if (live) {
3581                    return live;
3582                }
3583            }
3584            var nodes = net && net.nodes ? net.nodes : [];
3585            if (Number.isFinite(parsed) && nodes.some(function (n) { return Number(n.id) === parsed; })) {
3586                return parsed;
3587            }
3588            var needle = String(title || '').trim().toLowerCase();
3589            if (needle) {
3590                for (var i = 0; i < nodes.length; i++) {
3591                    if (String(nodes[i].title || '').trim().toLowerCase() === needle) {
3592                        return Number(nodes[i].id);
3593                    }
3594                }
3595            }
3596            return Number.isFinite(parsed) ? parsed : null;
3597        };
3598        this.canvasSelectionMatchesRow = function (liveId, node) {
3599            if (liveId == null || !node) {
3600                return false;
3601            }
3602            var resolved = _this.resolveCanvasNodeId(node.nodeId, node.title);
3603            return resolved != null && Number(resolved) === Number(liveId);
3604        };
3605        this.scheduleLeaderboardScrollToSelection = function () {
3606            if (_this.selectedCanvasNodeId() == null && _this.pinnedLeaderboardNodeId() == null) {
3607                return;
3608            }
3609            var run = function () {
3610                _this.leaderboardScrollFrame = null;
3611                _this.scrollLeaderboardToSelectedRow();
3612            };
3613            if (typeof requestAnimationFrame === 'function') {
3614                if (_this.leaderboardScrollFrame != null) {
3615                    cancelAnimationFrame(_this.leaderboardScrollFrame);
3616                }
3617                _this.leaderboardScrollFrame = requestAnimationFrame(function () {
3618                    _this.leaderboardScrollFrame = requestAnimationFrame(run);
3619                });
3620                return;
3621            }
3622            setTimeout(run, 0);
3623        };
3624        this.scrollLeaderboardToSelectedRow = function () {
3625            var root = typeof document === 'undefined' ? null : document.querySelector('.bayesBotRoot');
3626            var scroller = root && root.querySelector('.bayesBotLeaderboardScroll');
3627            var row = scroller && scroller.querySelector('tr.is-canvas-selected');
3628            if (!scroller || !row) {
3629                return;
3630            }
3631            var scrollerRect = scroller.getBoundingClientRect();
3632            var rowRect = row.getBoundingClientRect();
3633            var rowCenter = rowRect.top - scrollerRect.top + scroller.scrollTop + rowRect.height / 2;
3634            var maxScroll = Math.max(0, scroller.scrollHeight - scroller.clientHeight);
3635            var nextTop = Math.max(0, Math.min(maxScroll, rowCenter - scroller.clientHeight / 2));
3636            if (Math.abs(nextTop - scroller.scrollTop) < 2) {
3637                return;
3638            }
3639            if (typeof scroller.scrollTo === 'function') {
3640                scroller.scrollTo({ top: nextTop, behavior: 'smooth' });
3641            }
3642            else {
3643                scroller.scrollTop = nextTop;
3644            }
3645        };
3646        this.applyPinnedPathFocus = function () {
3647            var net = _this.authorBayesNet;
3648            if (!net || typeof net.setPathFocusNodeId !== 'function') {
3649                return;
3650            }
3651            net.setPathFocusNodeId(_this.pinnedLeaderboardNodeId());
3652        };
3653        this.hoverLeaderboardRow = function (row) {
3654            var net = _this.authorBayesNet;
3655            if (!net || typeof net.setPathFocusNodeId !== 'function') {
3656                return;
3657            }
3658            var liveId = _this.resolveCanvasNodeId(row && row.nodeId, row && row.title);
3659            net.setPathFocusNodeId(liveId);
3660        };
3661        this.clearLeaderboardHover = function () {
3662            if (_this.pinnedLeaderboardNodeId() != null) {
3663                _this.applyPinnedPathFocus();
3664                return;
3665            }
3666            var net = _this.authorBayesNet;
3667            if (!net || typeof net.setPathFocusNodeId !== 'function') {
3668                return;
3669            }
3670            net.setPathFocusNodeId(null);
3671        };
3672        this.closeLeaderboardScorePanel = function () {
3673            _this.pinnedLeaderboardNodeId(null);
3674            _this.applyPinnedPathFocus();
3675            Distiller.visibleInfo(null);
3676            var modal = Distiller.modalInView && Distiller.modalInView();
3677            if (modal &&
3678                (modal.name === 'bayes-bot-node-score-modal' || modal.name === 'bayes-bot-timepoint-modal')) {
3679                Distiller.modalInView(null);
3680            }
3681        };
3682        this.showLifetimeTimepointModal = function (breakpoint, event) {
3683            if (!breakpoint) {
3684                return;
3685            }
3686            Distiller.visibleInfo(null);
3687            var params = {
3688                breakpoint: breakpoint,
3689                dateLabel: _this.formatUtcLongDate(breakpoint.timeMs),
3690                percentLabel: Math.round(Number(breakpoint.normalizedInformation || 0) * 100) + '%',
3691                resolvingNames: Array.isArray(breakpoint.resolvingNodeNames)
3692                    ? breakpoint.resolvingNodeNames.slice()
3693                    : [],
3694                isPrimaryResolution: !!breakpoint.isPrimaryResolution,
3695                isStart: !!breakpoint.isStart,
3696                entropyRemainingLabel: breakpoint.expectedEntropyBits != null
3697                    ? Number(breakpoint.expectedEntropyBits).toFixed(2) + ' bits'
3698                    : '—',
3699                informationAvailableLabel: breakpoint.informationBits != null
3700                    ? Number(breakpoint.informationBits).toFixed(2) + ' bits'
3701                    : '—',
3702                anchorX: event && typeof event.clientX === 'number' ? event.clientX : 80,
3703                anchorY: event && typeof event.clientY === 'number' ? event.clientY : 80,
3704            };
3705            var open = function () {
3706                Distiller.modalInView({
3707                    name: 'bayes-bot-timepoint-modal',
3708                    params: params,
3709                });
3710            };
3711            if (Distiller.modalInView()) {
3712                Distiller.modalInView(null);
3713                setTimeout(open, 0);
3714                return;
3715            }
3716            open();
3717        };
3718        this.showLeaderboardNodeModal = function (row, event) {
3719            if (event) {
3720                event.preventDefault();
3721                event.stopPropagation();
3722            }
3723            var liveId = _this.resolveCanvasNodeId(row && row.nodeId, row && row.title);
3724            if (_this.pinnedLeaderboardNodeId() != null && Number(_this.pinnedLeaderboardNodeId()) === Number(liveId)) {
3725                _this.closeLeaderboardScorePanel();
3726                return;
3727            }
3728            _this.pinnedLeaderboardNodeId(liveId);
3729            _this.applyPinnedPathFocus();
3730            Distiller.visibleInfo(null);
3731            Distiller.modalInView({
3732                name: 'bayes-bot-node-score-modal',
3733                params: {
3734                    row: row,
3735                    anchorX: event && typeof event.clientX === 'number' ? event.clientX : 80,
3736                    anchorY: event && typeof event.clientY === 'number' ? event.clientY : 80,
3737                    diagnosticityInfoHtml: _this.diagnosticityInfoHtml,
3738                    timelinessInfoHtml: _this.timelinessInfoHtml,
3739                    onClose: function () {
3740                        _this.pinnedLeaderboardNodeId(null);
3741                        _this.applyPinnedPathFocus();
3742                    },
3743                },
3744            });
3745        };
3746        this.workbenchGridColumns = ko.pureComputed(function () {
3747            var share = Math.max(0.18, Math.min(0.82, _this.proposeShare()));
3748            return 'minmax(160px, ' + share + 'fr) 20px minmax(160px, ' + (1 - share) + 'fr)';
3749        });
3750        this.workbenchHeightStyle = ko.pureComputed(function () { return _this.workbenchHeightPx() + 'px'; });
3751        this.workbenchColDrag = null;
3752        this.workbenchRowDrag = null;
3753        this.clampWorkbenchHeight = function (height) {
3754            var minH = 100;
3755            var maxH = Math.max(minH, Math.min(640, Math.round(window.innerHeight * 0.7)));
3756            return Math.max(minH, Math.min(maxH, Math.round(height)));
3757        };
3758        this.startWorkbenchColResize = function (_data, event) {
3759            var target = event.currentTarget;
3760            var row = target && target.parentElement;
3761            if (!row) {
3762                return true;
3763            }
3764            event.preventDefault();
3765            event.stopPropagation();
3766            var usable = Math.max(1, row.clientWidth - 20);
3767            _this.workbenchColDrag = {
3768                pointerId: event.pointerId,
3769                startX: event.clientX,
3770                startShare: _this.proposeShare(),
3771                usableWidth: usable,
3772            };
3773            _this.workbenchRowDrag = null;
3774            if (target.setPointerCapture) {
3775                target.setPointerCapture(event.pointerId);
3776            }
3777            document.body.classList.add('bayesBotWorkbenchResizing');
3778            document.body.classList.add('bayesBotWorkbenchResizingCol');
3779            return false;
3780        };
3781        this.startWorkbenchRowResize = function (_data, event) {
3782            var target = event.currentTarget;
3783            event.preventDefault();
3784            event.stopPropagation();
3785            _this.workbenchRowDrag = {
3786                pointerId: event.pointerId,
3787                startY: event.clientY,
3788                startHeight: _this.workbenchHeightPx(),
3789            };
3790            _this.workbenchColDrag = null;
3791            if (target.setPointerCapture) {
3792                target.setPointerCapture(event.pointerId);
3793            }
3794            document.body.classList.add('bayesBotWorkbenchResizing');
3795            document.body.classList.add('bayesBotWorkbenchResizingRow');
3796            return false;
3797        };
3798        this.onWorkbenchResizeMove = function (_data, event) {
3799            if (_this.workbenchColDrag && event.pointerId === _this.workbenchColDrag.pointerId) {
3800                var minPx = 160;
3801                var maxPx = Math.max(minPx, _this.workbenchColDrag.usableWidth - minPx);
3802                var startPx = _this.workbenchColDrag.startShare * _this.workbenchColDrag.usableWidth;
3803                var nextPx = Math.max(minPx, Math.min(maxPx, startPx + (event.clientX - _this.workbenchColDrag.startX)));
3804                _this.proposeShare(nextPx / _this.workbenchColDrag.usableWidth);
3805            }
3806            if (_this.workbenchRowDrag && event.pointerId === _this.workbenchRowDrag.pointerId) {
3807                _this.workbenchHeightPx(_this.clampWorkbenchHeight(_this.workbenchRowDrag.startHeight + (event.clientY - _this.workbenchRowDrag.startY)));
3808            }
3809            return true;
3810        };
3811        this.endWorkbenchResize = function (_data, event) {
3812            if (event && _this.workbenchColDrag && event.pointerId !== _this.workbenchColDrag.pointerId) {
3813                return true;
3814            }
3815            if (event && _this.workbenchRowDrag && event.pointerId !== _this.workbenchRowDrag.pointerId) {
3816                return true;
3817            }
3818            _this.workbenchColDrag = null;
3819            _this.workbenchRowDrag = null;
3820            document.body.classList.remove('bayesBotWorkbenchResizing');
3821            document.body.classList.remove('bayesBotWorkbenchResizingCol');
3822            document.body.classList.remove('bayesBotWorkbenchResizingRow');
3823            return true;
3824        };
3825        this.pollBayesBotJobUntilDone = function (jobId) {
3826            _this.clearBayesBotJobPoll();
3827            var tick = function () {
3828                if (_this.proposalPhase() !== 'processing' || _this.lastBayesBotJobId() !== jobId) {
3829                    return;
3830                }
3831                var currentUser = Distiller.currentUser && Distiller.currentUser();
3832                var userId = currentUser && currentUser.userId;
3833                if (!userId) {
3834                    return;
3835                }
3836                Distiller.httpGetAsync(Distiller.apiEndpoint + '/api/bayesBotJob/' + userId + '/' + jobId, function (responseText) {
3837                    if (_this.proposalPhase() !== 'processing' || _this.lastBayesBotJobId() !== jobId) {
3838                        return;
3839                    }
3840                    try {
3841                        var parsed = typeof responseText === 'string' ? JSON.parse(responseText) : responseText;
3842                        if (!parsed || parsed.ok === false) {
3843                            _this.bayesBotPollTimer = setTimeout(tick, 5000);
3844                            return;
3845                        }
3846                        var terminal = parsed.status === 'complete' ||
3847                            parsed.status === 'error' ||
3848                            parsed.status === 'partial';
3849                        if (terminal) {
3850                            _this.onBayesBotCompleteEvent({
3851                                data: JSON.stringify({
3852                                    jobId: parsed.jobId || jobId,
3853                                    status: parsed.status,
3854                                    message: parsed.status === 'error'
3855                                        ? 'Something went wrong'
3856                                        : parsed.status === 'partial'
3857                                            ? 'Finished with some issues'
3858                                            : 'Done',
3859                                    responseText: _this.collectFinalResponseFromJob(parsed),
3860                                    error: parsed.error,
3861                                    warnings: parsed.results && Array.isArray(parsed.results.warnings)
3862                                        ? parsed.results.warnings
3863                                        : [],
3864                                    specNotes: parsed.results && Array.isArray(parsed.results.specNotes)
3865                                        ? parsed.results.specNotes
3866                                        : parsed.results && Array.isArray(parsed.results.warnings)
3867                                            ? parsed.results.warnings
3868                                            : [],
3869                                    draftStatus: parsed.results && parsed.results.draftStatus
3870                                        ? parsed.results.draftStatus
3871                                        : null,
3872                                    primaryNodeId: parsed.results && parsed.results.primaryNodeId != null
3873                                        ? parsed.results.primaryNodeId
3874                                        : null,
3875                                    infoValue: parsed.results && parsed.results.infoValue
3876                                        ? parsed.results.infoValue
3877                                        : null,
3878                                    lifetimeInformation: parsed.results && parsed.results.lifetimeInformation
3879                                        ? parsed.results.lifetimeInformation
3880                                        : null,
3881                                    copyNetId: parsed.copyNetId || null,
3882                                    setId: parsed.setId || null,
3883                                    templateNetId: parsed.templateNetId || null,
3884                                }),
3885                            });
3886                            return;
3887                        }
3888                    }
3889                    catch (e) {
3890                        /* keep polling */
3891                    }
3892                    _this.bayesBotPollTimer = setTimeout(tick, 5000);
3893                });
3894            };
3895            _this.bayesBotPollTimer = setTimeout(tick, 8000);
3896        };
3897        this.attachBayesBotSseListeners = function (es) {
3898            es.addEventListener('bayesBotStatus', _this.onBayesBotStatusEvent);
3899            es.addEventListener('bayesBotNodeCreated', _this.onBayesBotNodeCreatedEvent);
3900            es.addEventListener('bayesBotNodeDeleted', _this.onBayesBotNodeDeletedEvent);
3901            es.addEventListener('bayesBotConnectionAdded', _this.onBayesBotConnectionAddedEvent);
3902            es.addEventListener('bayesBotConnectionRemoved', _this.onBayesBotConnectionRemovedEvent);
3903            es.addEventListener('bayesBotPriorForecast', _this.onBayesBotPriorForecastEvent);
3904            es.addEventListener('bayesBotConditionalForecast', _this.onBayesBotConditionalForecastEvent);
3905            es.addEventListener('bayesBotComplete', _this.onBayesBotCompleteEvent);
3906            es.addEventListener('bayesBotHeader', _this.onBayesBotHeaderEvent);
3907            es.addEventListener('bayesBotStudyReady', _this.onBayesBotStudyReadyEvent);
3908            es.addEventListener('bayesBotCloneReady', _this.onBayesBotCloneReadyEvent);
3909            es.addEventListener('bayesBotLeaderboard', _this.onBayesBotLeaderboardEvent);
3910            es.addEventListener('bayesBotLifetimeInformation', _this.onBayesBotLifetimeInformationEvent);
3911            es.addEventListener('bayesBotAgentActivity', _this.onBayesBotAgentActivityEvent);
3912            es.addEventListener('bayesBotAuthRequired', _this.onBayesBotAuthRequiredEvent);
3913            es.addEventListener('bayesBotBranchMerged', _this.onBayesBotBranchMergedEvent);
3914            es.addEventListener('bayesBotMergeStatus', _this.onBayesBotMergeStatusEvent);
3915            es.onerror = function () {
3916                if (es.readyState !== EventSource.CLOSED) {
3917                    return;
3918                }
3919                try {
3920                    es.close();
3921                }
3922                catch (e) {
3923                    /* ignore */
3924                }
3925                if (_this.bayesBotEventSource === es) {
3926                    _this.bayesBotEventSource = null;
3927                }
3928                if (_this.bayesBotSseReconnectTimer) {
3929                    return;
3930                }
3931                _this.bayesBotSseReconnectTimer = setTimeout(function () {
3932                    _this.bayesBotSseReconnectTimer = null;
3933                    _this.connectBayesBotSse();
3934                }, 2500);
3935            };
3936        };
3937        this.onBayesBotHeaderEvent = function (event) {
3938            var data = _this.parseBayesBotSseData(event);
3939            if (!_this.isCurrentBayesBotJob(data) || !data) {
3940                return;
3941            }
3942            var markup = data.headerMarkup != null ? String(data.headerMarkup) : '';
3943            if (!markup) {
3944                return;
3945            }
3946            var criteria = data.resolutionCriteria != null ? String(data.resolutionCriteria) : '';
3947            if (data.questionText && !isLegacyHormuzPlaceholder(data.questionText)) {
3948                _this.boundQuestionText(String(data.questionText));
3949            }
3950            if (criteria && !isLegacyHormuzPlaceholder(criteria)) {
3951                _this.boundResolutionCriteria(criteria);
3952            }
3953            if (data.resolutionDate) {
3954                _this.boundResolutionDate(bayesBotIsoDate(data.resolutionDate) || _this.boundResolutionDate());
3955            }
3956            var infoHtml = criteria
3957                ? bayesBotHeaderInfoHtml('Resolves Yes', criteria)
3958                : _this.headerInfoHtml();
3959            _this.animateHeaderMarkup(markup, infoHtml);
3960        };
3961        this.onBayesBotStudyReadyEvent = function (event) {
3962            var data = _this.parseBayesBotSseData(event);
3963            if (!_this.isCurrentBayesBotJob(data) || !data) {
3964                return;
3965            }
3966            _this.ensureCreateCanvasMounted(data);
3967        };
3968        this.onBayesBotLeaderboardEvent = function (event) {
3969            var data = _this.parseBayesBotSseData(event);
3970            if (!data) {
3971                return;
3972            }
3973            var fromMerge = data.reason === 'merge';
3974            if (!fromMerge && !_this.isCurrentBayesBotJob(data) && !_this.agentActivityLive()) {
3975                return;
3976            }
3977            if (data.scoresStatus) {
3978                _this.scoresStatus(String(data.scoresStatus));
3979            }
3980            else if (fromMerge) {
3981                _this.scoresStatus('');
3982            }
3983            _this.leaderboardPrimaryNodeId(data.primaryNodeId || null);
3984            _this.applyLeaderboardNodes(data.nodes || [], data.rankOf);
3985            _this.applyPrimaryNodeHighlight();
3986        };
3987        this.onBayesBotLifetimeInformationEvent = function (event) {
3988            var data = _this.parseBayesBotSseData(event);
3989            if (!data) {
3990                return;
3991            }
3992            var lifetime = data.lifetimeInformation || null;
3993            if (!lifetime) {
3994                return;
3995            }
3996            if (data.netId != null) {
3997                var incoming = parseInt(String(data.netId), 10);
3998                var onScreen = _this.lifetimeScoreNetIdOnScreen();
3999                if (Number.isFinite(incoming) &&
4000                    incoming > 0 &&
4001                    onScreen != null &&
4002                    incoming !== onScreen) {
4003                    return;
4004                }
4005            }
4006            _this.presentPersistedLifetimeInformation(lifetime);
4007        };
4008        this.connectBayesBotSse = function () {
4009            var currentUser = Distiller.currentUser && Distiller.currentUser();
4010            var userId = currentUser && currentUser.userId;
4011            if (!userId) {
4012                return;
4013            }
4014            if (_this.bayesBotEventSource && _this.bayesBotSseUserId === userId) {
4015                return;
4016            }
4017            if (_this.bayesBotEventSource) {
4018                try {
4019                    _this.bayesBotEventSource.close();
4020                }
4021                catch (e) {
4022                    /* ignore */
4023                }
4024                _this.bayesBotEventSource = null;
4025            }
4026            _this.bayesBotSseUserId = userId;
4027            var requestedUserId = userId;
4028            fetch(Distiller.apiEndpoint + '/api/get-bayes-bot-sse-token/' + userId, {
4029                method: 'GET',
4030                headers: { 'Content-Type': 'application/json' },
4031                credentials: 'same-origin',
4032            })
4033                .then(function (response) {
4034                if (!response.ok) {
4035                    throw new Error('HTTP error! status: ' + response.status);
4036                }
4037                return response.json();
4038            })
4039                .then(function (data) {
4040                var current = Distiller.currentUser && Distiller.currentUser();
4041                if (!current || current.userId !== requestedUserId) {
4042                    return;
4043                }
4044                if (_this.bayesBotEventSource) {
4045                    return;
4046                }
4047                var token = data && data.token;
4048                if (!token) {
4049                    throw new Error('missing SSE token');
4050                }
4051                var es = new EventSource(Distiller.apiEndpoint + '/api/events?token=' 
4051+ token);
4052                _this.bayesBotEventSource = es;
4053                _this.attachBayesBotSseListeners(es);
4054            })
4055                .catch(function (error) {
4056                console.error('Error establishing BayesBot SSE connection:', error);
4057                if (!_this.bayesBotSseReconnectTimer) {
4058                    _this.bayesBotSseReconnectTimer = setTimeout(function () {
4059                        _this.bayesBotSseReconnectTimer = null;
4060                        _this.connectBayesBotSse();
4061                    }, 4000);
4062                }
4063            });
4064        };
4065        this.remountAuthorBayesNet = function () {
4066            _this.authorBayesNet = null;
4067            _this.pendingBayesBotGraphEvents = [];
4068            _this.authorBayesNetSet(null);
4069            _this.loadAuthorBayesNet();
4070            _this.loadLeaderboard();
4071        };
4072        this.onBayesBotCloneReadyEvent = function (event) {
4073            if (_this.ownsCanonicalTemplate || _this.isTempVisitor()) {
4074                return;
4075            }
4076            var data = _this.parseBayesBotSseData(event);
4077            if (!data || !data.copyNetId) {
4078                return;
4079            }
4080            if (data.ownsCanonicalTemplate) {
4081                _this.ownsCanonicalTemplate = true;
4082                _this.authorOwnsTemplate(true);
4083                return;
4084            }
4085            _this.adoptPersonalCopyFromServer(data.copyNetId, data.setId);
4086        };
4087        this.flushQueuedBayesBotGraphEvents = function () {
4088            var queued = _this.pendingBayesBotGraphEvents.slice();
4089            _this.pendingBayesBotGraphEvents = [];
4090            for (var i = 0; i < queued.length; i++) {
4091                _this.applyBayesBotGraphEvent(queued[i]);
4092            }
4093        };
4094        this.adoptPersonalCopyFromServer = function (copyNetIdRaw, setIdRaw) { return __awaiter(_this, void 0, void 0, function () {
4095            var copyNetId, currentUser, userId, setId, responseText, parsed, graph, nodes, e_4;
4096            return __generator(this, function (_a) {
4097                switch (_a.label) {
4098                    case 0:
4099                        copyNetId = parseInt(String(copyNetIdRaw), 10);
4100                        if (!Number.isFinite(copyNetId) || copyNetId <= 0) {
4101                            return [2 /*return*/, false];
4102                        }
4103                        currentUser = Distiller.currentUser && Distiller.currentUser();
4104                        userId = currentUser && currentUser.userId;
4105                        setId = setIdRaw != null
4106                            ? setIdRaw
4107                            : (this.authorBayesNetSet() && this.authorBayesNetSet().setId
4108                                ? this.authorBayesNetSet().setId()
4109                                : null);
4110                        if (!userId || !setId) {
4111                            this.personalCopyNetId = copyNetId;
4112                            return [2 /*return*/, false];
4113                        }
4114                        _a.label = 1;
4115                    case 1:
4116                        _a.trys.push([1, 3, , 4]);
4117                        return [4 /*yield*/, new Promise(function (resolve) {
4118                                Distiller.httpGetAsync(Distiller.apiEndpoint + '/api/bayesNets/' + userId + '/' + setId + '/' + copyNetId, resolve);
4119                            })];
4120                    case 2:
4121                        responseText = _a.sent();
4122                        parsed = typeof responseText === 'string' ? JSON.parse(responseText) : responseText;
4123                        graph = parsed && parsed.bayesNet ? parsed.bayesNet : null;
4124                        nodes = graph && graph.nodes ? graph.nodes : [];
4125                        this.personalCopyNetId = copyNetId;
4126                        this.viewingSharedTemplate = false;
4127                        this.cloneReady = true;
4128                        this.liveTemplateBuild = false;
4129                        if (this.authorBayesNet && graph && typeof this.authorBayesNet.load === 'function') {
4130                            this.authorBayesNet.netId = copyNetId;
4131                            this.authorBayesNet.load(graph);
4132                            if (this.authorBayesNet.primaryNodeId) {
4133                                this.leaderboardPrimaryNodeId(this.authorBayesNet.primaryNodeId);
4134                            }
4135                            this.applyPrimaryNodeHighlight();
4136                        }
4137                        else if (this.authorBayesNet && typeof this.authorBayesNet.adoptCopiedNodeIds === 'function') {
4138                            this.applyPrimaryNodeHighlight();
4139                            this.authorBayesNet.adoptCopiedNodeIds(nodes, copyNetId);
4140                            if (this.authorBayesNet.primaryNodeId) {
4141                                this.leaderboardPrimaryNodeId(this.authorBayesNet.primaryNodeId);
4142                            }
4143                            this.applyPrimaryNodeHighlight();
4144                        }
4145                        this.flushQueuedBayesBotGraphEvents();
4146                        this.loadLeaderboard();
4147                        return [2 /*return*/, true];
4148                    case 3:
4149                        e_4 = _a.sent();
4150                        console.error('BayesBot adopt personal copy failed:', e_4);
4151                        this.personalCopyNetId = copyNetId;
4152                        return [2 /*return*/, false];
4153                    case 4: return [2 /*return*/];
4154                }
4155            });
4156        }); };
4157        this.ensurePersonalCopy = function (opts) {
4158            if (_this.isTempVisitor()) {
4159                var templateNetId = _this.sharedTemplateNetId();
4160                return Promise.resolve(templateNetId || 0);
4161            }
4162            if (_this.ownsCanonicalTemplate) {
4163                var templateNetId = _this.sharedTemplateNetId();
4164                if (templateNetId) {
4165                    return Promise.resolve(templateNetId);
4166                }
4167            }
4168            if (_this.cloneReady && _this.personalCopyNetId && !_this.viewingSharedTemplate) {
4169                return Promise.resolve(_this.personalCopyNetId);
4170            }
4171            if (_this.clonePromise) {
4172                if (!opts || !opts.silent) {
4173                    _this.preparingCloneForEdits = true;
4174                    _this.setProposalStatusText('Preparing clone for editing…');
4175                }
4176                return _this.clonePromise;
4177            }
4178            var showPreparing = !opts || !opts.silent;
4179            if (showPreparing && !_this.cloneReady) {
4180                _this.preparingCloneForEdits = true;
4181                _this.setProposalStatusText('Preparing clone for editing…');
4182            }
4183            _this.clonePromise = (function () { return __awaiter(_this, void 0, void 0, function () {
4184                var currentUser, userId, responseText, parsed, templateNetId;
4185                return __generator(this, function (_a) {
4186                    switch (_a.label) {
4187                        case 0:
4188                            currentUser = Distiller.currentUser && Distiller.currentUser();
4189                            userId = currentUser && currentUser.userId;
4190                            if (!userId) {
4191                                throw new Error('missing current user');
4192                            }
4193                            if (!(this.personalCopyNetId && this.authorBayesNet && !this.viewingSharedTemplate)) return [3 /*break*/, 2];
4194                            return [4 /*yield*/, this.adoptPersonalCopyFromServer(this.personalCopyNetId, this.authorBayesNetSet() && this.authorBayesNetSet().setId
4195                                    ? this.authorBayesNetSet().setId()
4196                                    : null)];
4197                        case 1:
4198                            _a.sent();
4199                            if (this.cloneReady && !this.viewingSharedTemplate) {
4200                                return [2 /*return*/, this.personalCopyNetId];
4201                            }
4202                            _a.label = 2;
4203                        case 2: return [4 /*yield*/, Distiller.httpPostJsonAsync(Distiller.apiEndpoint + '/api/bayesBotEnsureCopy/' + userId, { communityId: this.resolveBoundCommunityId() })];
4204                        case 3:
4205                            responseText = _a.sent();
4206                            parsed = typeof responseText === 'string' ? JSON.parse(responseText) : responseText;
4207                            if (parsed && parsed.ownsCanonicalTemplate) {
4208                                this.ownsCanonicalTemplate = true;
4209                                this.authorOwnsTemplate(true);
4210                                if (parsed.templateNetId != null) {
4211                                    templateNetId = parseInt(String(parsed.templateNetId), 10);
4212                                    if (Number.isFinite(templateNetId) && templateNetId > 0) {
4213                                        this.sharedTemplateNetId(templateNetId);
4214                                    }
4215                                }
4216                                return [2 /*return*/, this.sharedTemplateNetId() || parseInt(String(parsed.copyNetId), 10)];
4217                            }
4218                            if (!parsed || parsed.ok === false || !parsed.copyNetId) {
4219                                throw new Error((parsed && parsed.error) || 'failed to prepare clone');
4220                            }
4221                            return [4 /*yield*/, this.adoptPersonalCopyFromServer(parsed.copyNetId, parsed.setId)];
4222                        case 4:
4223                            _a.sent();
4224                            return [2 /*return*/, parseInt(String(parsed.copyNetId), 10)];
4225                    }
4226                });
4227            }); })();
4228            return _this.clonePromise.then(function (copyNetId) {
4229                _this.preparingCloneForEdits = false;
4230                _this.clonePromise = null;
4231                return copyNetId;
4232            }, function (err) {
4233                _this.preparingCloneForEdits = false;
4234                _this.clonePromise = null;
4235                throw err;
4236            });
4237        };
4238        this.clearNetForDev = function () { return __awaiter(_this, void 0, void 0, function () {
4239            var currentUser, userId, confirmed, responseText, parsed, e_5;
4240            return __generator(this, function (_a) {
4241                switch (_a.label) {
4242                    case 0:
4243                        if (!this.showClearNetDevButton || this.clearingNet()) {
4244                            return [2 /*return*/];
4245                        }
4246                        currentUser = Distiller.currentUser && Distiller.currentUser();
4247                        userId = currentUser && currentUser.userId;
4248                        if (!userId) {
4249                            console.error('BayesBot clear net: missing current user');
4250                            return [2 /*return*/];
4251                        }
4252                        confirmed = window.confirm('Clear the shared net down to the primary node and rebuild your copy? Local development only.');
4253                        if (!confirmed) {
4254                            return [2 /*return*/];
4255                        }
4256                        this.clearingNet(true);
4257                        _a.label = 1;
4258                    case 1:
4259                        _a.trys.push([1, 3, 4, 5]);
4260                        return [4 /*yield*/, Distiller.httpPostJsonAsync(Distiller.apiEndpoint + '/api/bayesBotClearNet/' + userId, { communityId: this.resolveBoundCommunityId() })];
4261                    case 2:
4262                        responseText = _a.sent();
4263                        parsed = typeof responseText === 'string' ? JSON.parse(responseText) : responseText;
4264                        if (!parsed || parsed.ok === false) {
4265                            console.error('BayesBot clear net failed:', parsed);
4266                            window.alert((parsed && parsed.error) || 'Failed to clear net');
4267                            return [2 /*return*/];
4268                        }
4269                        this.dismissProposalResult();
4270                        this.leaderboardNodes([]);
4271                        this.leaderboardRankOf(0);
4272                        this.leaderboardPrimaryNodeId(parsed.primaryNodeId || null);
4273                        this.remountAuthorBayesNet();
4274                        return [3 /*break*/, 5];
4275                    case 3:
4276                        e_5 = _a.sent();
4277                        console.error('BayesBot clear net failed:', e_5);
4278                        window.alert('Failed to clear net');
4279                        return [3 /*break*/, 5];
4280                    case 4:
4281                        this.clearingNet(false);
4282                        return [7 /*endfinally*/];
4283                    case 5: return [2 /*return*/];
4284                }
4285            });
4286        }); };
4287        /**
4288         * Load the shared template Bayes set for the bound (or current) BayesBot study.
4289         */
4290        this.reloadAuthorBayesNetGraph = function () {
4291            var set = _this.authorBayesNetSet();
4292            var net = _this.authorBayesNet;
4293            var userId = _this.viewerUserId
4294                || (Distiller.currentUser && Distiller.currentUser() && Distiller.currentUser().userId);
4295            if (!set || !net || !userId || typeof net.load !== 'function') {
4296                return _this.loadAuthorBayesNet();
4297            }
4298            var setId = set.setId && set.setId();
4299            if (!setId) {
4300                return _this.loadAuthorBayesNet();
4301            }
4302            if (!_this.viewingSharedTemplate && _this.personalCopyNetId) {
4303                var copyNetId_1 = _this.personalCopyNetId;
4304                return new Promise(function (resolve) {
4305                    Distiller.httpGetAsync(Distiller.apiEndpoint + '/api/bayesNets/' + userId + '/' + setId + '/' + copyNetId_1, function (responseText) {
4306                        try {
4307                            var parsed = typeof responseText === 'string' ? JSON.parse(responseText) : responseText;
4308                            var graph = parsed && parsed.bayesNet;
4309                            if (graph) {
4310                                net.netId = copyNetId_1;
4311                                net.load(graph);
4312                                _this.flushQueuedBayesBotGraphEvents();
4313                            }
4314                        }
4315                        catch (e) {
4316                            console.error('BayesBot reload copy graph failed:', e);
4317                        }
4318                        resolve();
4319                    });
4320                });
4321            }
4322            var query = ['viewSharedTemplate=1'];
4323            var templateNetId = _this.sharedTemplateNetId();
4324            if (templateNetId) {
4325                query.push('templateNetId=' + encodeURIComponent(String(templateNetId)));
4326            }
4327            return new Promise(function (resolve) {
4328                Distiller.httpGetAsync(Distiller.apiEndpoint + '/api/bayesNets/' + userId + '/' + setId + '?' + query.join('&'), function (responseText) {
4329                    try {
4330                        var parsed = typeof responseText === 'string' ? JSON.parse(responseText) : responseText;
4331                        var graph = parsed && parsed.bayesNets && parsed.bayesNets[0];
4332                        if (graph) {
4333                            net.load(graph);
4334                            _this.flushQueuedBayesBotGraphEvents();
4335                        }
4336                    }
4337                    catch (e) {
4338                        console.error('BayesBot reload graph failed:', e);
4339                    }
4340                    resolve();
4341                });
4342            });
4343        };
4344        this.loadAuthorBayesNet = function () {
4345            var currentUser = Distiller.currentUser && Distiller.currentUser();
4346            var userId = currentUser && currentUser.userId;
4347            if (!userId) {
4348                console.error('BayesBot loadAuthorBayesNet: missing current user');
4349                _this.loadingSteps(false);
4350                return Promise.resolve();
4351            }
4352            _this.viewerUserId = userId;
4353            var communityId = _this.resolveBoundCommunityId();
4354            return new Promise(function (resolve) {
4355                Distiller.httpGetAsync(Distiller.apiEndpoint + '/api/getBayesBotModel/' + userId
4356                    + '?communityId=' + encodeURIComponent(String(communityId)), function (responseText) {
4357                    try {
4358                        var response = typeof responseText === 'string' ? JSON.parse(responseText) : responseText;
4359                        if (response && !response.err && response.data && response.data.setId) {
4360                            _this.mountAuthorBayesNetFromModel(response.data);
4361                            _this.applyPersistedBuildStatus(response.data.buildStatus);
4362                            var lifetime = !_this.viewingSharedTemplate && response.data.copyLifetimeInformation
4363                                ? response.data.copyLifetimeInformation
4364                                : response.data.lifetimeInformation;
4365                            var usable = lifetime &&
4366                                Array.isArray(lifetime.breakpoints) &&
4367                                lifetime.breakpoints.length
4368                                ? lifetime
4369                                : response.data.lifetimeInformation;
4370                            if (usable) {
4371                                _this.presentPersistedLifetimeInformation(usable);
4372                            }
4373                        }
4374                        else {
4375                            console.error('BayesBot loadAuthorBayesNet: unexpected response', responseText);
4376                        }
4377                    }
4378                    catch (e) {
4379                        console.error('BayesBot loadAuthorBayesNet failed:', e);
4380                    }
4381                    finally {
4382                        _this.loadingSteps(false);
4383                        resolve();
4384                    }
4385                });
4386            });
4387        };
4388        this.toggleBayesBotModelMenu = function () {
4389            if (_this.requireSignedInForComposer()) {
4390                return;
4391            }
4392            var next = !_this.bayesBotModelMenuOpen();
4393            _this.bayesBotModelMenuOpen(next);
4394            if (next) {
4395                _this.placeBayesBotModelMenu();
4396            }
4397        };
4398        this.placeBayesBotModelMenu = function () {
4399            if (!_this.bayesBotModelMenuOpen()) {
4400                return;
4401            }
4402            var trigger = document.querySelector('.bayesBotModelPickerButton');
4403            var menu = document.querySelector('.bayesBotModelMenu');
4404            if (!trigger || !menu) {
4405                return;
4406            }
4407            var rect = trigger.getBoundingClientRect();
4408            menu.style.top = rect.top + 'px';
4409            menu.style.left = rect.left + 'px';
4410            menu.style.width = rect.width + 'px';
4411        };
4412        this.selectBayesBotModel = function (choice) {
4413            if (choice && choice.disabled) {
4414                return;
4415            }
4416            if (!choice || _this.requireSignedInForComposer()) {
4417                _this.bayesBotModelMenuOpen(false);
4418                return;
4419            }
4420            _this.bayesBotModelMenuOpen(false);
4421            if (choice.id === _this.selectedBayesBotModel()) {
4422                return;
4423            }
4424            _this.selectedBayesBotModel(choice.id);
4425            void _this.saveBayesBotUserModel(choice.id);
4426        };
4427        this.loadBayesBotUserModel = function () { return __awaiter(_this, void 0, void 0, function () {
4428            var currentUser, userId, response, data_1, known, e_6;
4429            return __generator(this, function (_a) {
4430                switch (_a.label) {
4431                    case 0:
4432                        if (this.isTempVisitor()) {
4433                            this.selectedBayesBotModel(BAYES_BOT_DEFAULT_MODEL);
4434                            this.bayesBotModelMenuOpen(false);
4435                            return [2 /*return*/];
4436                        }
4437                        currentUser = Distiller.currentUser && Distiller.currentUser();
4438                        userId = currentUser && currentUser.userId;
4439                        if (!userId) {
4440                            return [2 /*return*/];
4441                        }
4442                        _a.label = 1;
4443                    case 1:
4444                        _a.trys.push([1, 4, , 5]);
4445                        return [4 /*yield*/, fetch(Distiller.apiEndpoint + '/api/bayesBotUserModel/' + userId, { credentials: 'same-origin' })];
4446                    case 2:
4447                        response = _a.sent();
4448                        return [4 /*yield*/, response.json()];
4449                    case 3:
4450                        data_1 = _a.sent();
4451                        if (!response.ok || !data_1 || data_1.ok === false || !data_1.model) {
4452                            return [2 /*return*/];
4453                        }
4454                        known = this.bayesBotModelChoices.some(function (choice) { return choice.id === data_1.model && !choice.disabled; });
4455                        if (known) {
4456                            this.selectedBayesBotModel(data_1.model);
4457                        }
4458                        return [3 /*break*/, 5];
4459                    case 4:
4460                        e_6 = _a.sent();
4461                        console.error('BayesBot model preference load failed', e_6);
4462                        return [3 /*break*/, 5];
4463                    case 5: return [2 /*return*/];
4464                }
4465            });
4466        }); };
4467        this.saveBayesBotUserModel = function (model) { return __awaiter(_this, void 0, void 0, function () {
4468            var currentUser, userId, e_7;
4469            return __generator(this, function (_a) {
4470                switch (_a.label) {
4471                    case 0:
4472                        currentUser = Distiller.currentUser && Distiller.currentUser();
4473                        userId = currentUser && currentUser.userId;
4474                        if (!userId) {
4475                            return [2 /*return*/];
4476                        }
4477                        _a.label = 1;
4478                    case 1:
4479                        _a.trys.push([1, 3, , 4]);
4480                        return [4 /*yield*/, Distiller.httpPostJsonAsync(Distiller.apiEndpoint + '/api/bayesBotUserModel/' + userId, { model: model })];
4481                    case 2:
4482                        _a.sent();
4483                        return [3 /*break*/, 4];
4484                    case 3:
4485                        e_7 = _a.sent();
4486                        console.error('BayesBot model preference save failed', e_7);
4487                        return [3 /*break*/, 4];
4488                    case 4: return [2 /*return*/];
4489                }
4490            });
4491        }); };
4492        this.submitComposer = function () {
4493            if (_this.requireSignedInForComposer()) {
4494                return;
4495            }
4496            if (_this.isCreateMode()) {
4497                _this.submitNetProposal();
4498                return;
4499            }
4500            _this.submitNodeProposal();
4501        };
4502        this.requireSignedInForComposer = function () {
4503            if (!_this.isTempVisitor()) {
4504                return false;
4505            }
4506            _this.openAuthModal(_this.isCreateMode() ? 'create' : 'propose');
4507            return true;
4508        };
4509        this.openAuthModal = function (context) {
4510            _this.authModalContext(context || 'header');
4511            if (_this.authModalMode() !== 'signin') {
4512                _this.authModalMode('signup');
4513            }
4514            _this.authError('');
4515            _this.authModalOpen(true);
4516        };
4517        this.closeTopMenus = function () {
4518            _this.myModelsMenuOpen(false);
4519            _this.publicModelsMenuOpen(false);
4520        };
4521        this.openTopMenuAuth = function () {
4522            _this.closeTopMenus();
4523            _this.openAuthModal('header');
4524        };
4525        this.bayesBotHostAlreadyIdentifies = function () {
4526            try {
4527                var hostAndPath = (window.location.hostname + window.location.pathname).toLowerCase();
4528                return hostAndPath.indexOf('bayesbot') > -1;
4529            }
4530            catch (e) {
4531                return false;
4532            }
4533        };
4534        this.freshBayesBotHref = function () {
4535            var origin = window.location.origin;
4536            if (_this.bayesBotHostAlreadyIdentifies()) {
4537                return origin + '/';
4538            }
4539            return origin + '/?bayesBot';
4540        };
4541        this.communityBayesBotHref = function (communityId) {
4542            var origin = window.location.origin;
4543            var sid = encodeURIComponent(String(communityId));
4544            if (_this.bayesBotHostAlreadyIdentifies()) {
4545                return origin + '/?sid=' + sid;
4546            }
4547            return origin + '/?bayesBot&sid=' + sid;
4548        };
4549        this.openNewModel = function () {
4550            window.location.assign(_this.freshBayesBotHref());
4551        };
4552        this.openBayesBotCommunity = function (model) {
4553            var communityId = model && model.communityId != null
4554                ? parseInt(String(model.communityId), 10)
4555                : NaN;
4556            if (!Number.isFinite(communityId) || communityId <= 0) {
4557                return;
4558            }
4559            window.location.assign(_this.communityBayesBotHref(communityId));
4560        };
4561        this.togglePublicModelsMenu = function () {
4562            var next = !_this.publicModelsMenuOpen();
4563            _this.myModelsMenuOpen(false);
4564            _this.publicModelsMenuOpen(next);
4565            if (next) {
4566                void _this.loadPublicModels();
4567            }
4568        };
4569        this.toggleMyModelsMenu = function () {
4570            var next = !_this.myModelsMenuOpen();
4571            _this.publicModelsMenuOpen(false);
4572            _this.myModelsMenuOpen(next);
4573            if (next) {
4574                void _this.loadMyModels(true);
4575            }
4576        };
4577        this.onMyModelsQueryInput = function () {
4578            if (_this.myModelsSearchTimer) {
4579                clearTimeout(_this.myModelsSearchTimer);
4580            }
4581            _this.myModelsSearchTimer = setTimeout(function () {
4582                void _this.loadMyModels(true);
4583            }, 200);
4584        };
4585        this.loadMoreMyModels = function () {
4586            if (_this.myModelsLoading() || !_this.myModelsHasMore()) {
4587                return;
4588            }
4589            void _this.loadMyModels(false);
4590        };
4591        this.loadMyModels = function (reset) { return __awaiter(_this, void 0, void 0, function () {
4592            var currentUser, userId, token, offset, q, response, data, models, e_8;
4593            return __generator(this, function (_a) {
4594                switch (_a.label) {
4595                    case 0:
4596                        currentUser = Distiller.currentUser && Distiller.currentUser();
4597                        userId = currentUser && currentUser.userId;
4598                        if (reset) {
4599                            this.myModelsOffset = 0;
4600                            this.myModels([]);
4601                            this.myModelsHasMore(false);
4602                            this.myModelsLoaded(false);
4603                        }
4604                        if (!userId) {
4605                            this.myModelsLoading(false);
4606                            this.myModelsLoaded(true);
4607                            this.myModelsHasMore(false);
4608                            return [2 /*return*/];
4609                        }
4610                        token = ++this.myModelsRequestToken;
4611                        offset = this.myModelsOffset;
4612                        q = String(this.myModelsQuery() || '').trim();
4613                        this.myModelsLoading(true);
4614                        _a.label = 1;
4615                    case 1:
4616                        _a.trys.push([1, 4, 5, 6]);
4617                        return [4 /*yield*/, fetch(Distiller.apiEndpoint +
4618                                '/api/bayesBotMyModels/' +
4619                                userId +
4620                                '?q=' +
4621                                encodeURIComponent(q) +
4622                                '&offset=' +
4623                                encodeURIComponent(String(offset)) +
4624                                '&limit=10', { credentials: 'same-origin' })];
4625                    case 2:
4626                        response = _a.sent();
4627                        return [4 /*yield*/, response.json()];
4628                    case 3:
4629                        data = _a.sent();
4630                        if (token !== this.myModelsRequestToken) {
4631                            return [2 /*return*/];
4632                        }
4633                        if (!response.ok || !data || data.ok === false) {
4634                            this.myModelsHasMore(false);
4635                            return [2 /*return*/];
4636                        }
4637                        models = Array.isArray(data.models) ? data.models : [];
4638                        if (reset) {
4639                            this.myModels(models);
4640                        }
4641                        else {
4642                            this.myModels(this.myModels().concat(models));
4643                        }
4644                        this.myModelsOffset = offset + models.length;
4645                        this.myModelsHasMore(!!data.hasMore);
4646                        return [3 /*break*/, 6];
4647                    case 4:
4648                        e_8 = _a.sent();
4649                        if (token !== this.myModelsRequestToken) {
4650                            return [2 /*return*/];
4651                        }
4652                        console.error('BayesBot my models failed:', e_8);
4653                        this.myModelsHasMore(false);
4654                        return [3 /*break*/, 6];
4655                    case 5:
4656                        if (token === this.myModelsRequestToken) {
4657                            this.myModelsLoading(false);
4658                            this.myModelsLoaded(true);
4659                        }
4660                        return [7 /*endfinally*/];
4661                    case 6: return [2 /*return*/];
4662                }
4663            });
4664        }); };
4665        this.loadPublicModels = function () { return __awaiter(_this, void 0, void 0, function () {
4666            var response, data, e_9;
4667            return __generator(this, function (_a) {
4668                switch (_a.label) {
4669                    case 0:
4670                        this.publicModelsLoading(true);
4671                        _a.label = 1;
4672                    case 1:
4673                        _a.trys.push([1, 4, 5, 6]);
4674                        return [4 /*yield*/, fetch(Distiller.apiEndpoint + '/api/bayesBotPublicModels', { credentials: 'same-origin' })];
4675                    case 2:
4676                        response = _a.sent();
4677                        return [4 /*yield*/, response.json()];
4678                    case 3:
4679                        data = _a.sent();
4680                        if (!response.ok || !data || data.ok === false) {
4681                            this.publicModelGroups([]);
4682                            return [2 /*return*/];
4683                        }
4684                        this.publicModelGroups(Array.isArray(data.groups) ? data.groups : []);
4685                        return [3 /*break*/, 6];
4686                    case 4:
4687                        e_9 = _a.sent();
4688                        console.error('BayesBot public models failed:', e_9);
4689                        this.publicModelGroups([]);
4690                        return [3 /*break*/, 6];
4691                    case 5:
4692                        this.publicModelsLoading(false);
4693                        this.publicModelsLoaded(true);
4694                        return [7 /*endfinally*/];
4695                    case 6: return [2 /*return*/];
4696                }
4697            });
4698        }); };
4699        this.closeAuthModal = function () {
4700            _this.authModalOpen(false);
4701            _this.authSubmitting(false);
4702            _this.authError('');
4703        };
4704        this.showAuthSignIn = function () {
4705            _this.authModalMode('signin');
4706            _this.authError('');
4707        };
4708        this.showAuthSignUp = function () {
4709            _this.authModalMode('signup');
4710            _this.authError('');
4711        };
4712        this.onBayesBotAuthRequiredEvent = function (event) {
4713            if (!_this.isTempVisitor()) {
4714                return;
4715            }
4716            var data = _this.parseBayesBotSseData(event);
4717            var reason = data && data.reason != null ? String(data.reason) : 'agent';
4718            _this.openAuthModal(reason === 'create' || reason === 'propose' ? reason : 'agent');
4719        };
4720        this.onBayesBotBranchMergedEvent = function (event) {
4721            var data = _this.parseBayesBotSseData(event);
4722            if (_this.mergeInFlight()) {
4723                if (_this.mergeResponseStillWorking(data)) {
4724                    _this.holdMergeForForecasts(data);
4725                    return;
4726                }
4727                if (!_this.mergeModalOpen()) {
4728                    _this.finishSynchronousMerge(data);
4729                }
4730                return;
4731            }
4732            var personalCopyNetId = data && data.personalCopyNetId != null
4733                ? parseInt(String(data.personalCopyNetId), 10)
4734                : NaN;
4735            if (Number.isFinite(personalCopyNetId) && personalCopyNetId > 0 && data.ownsCanonicalTemplate !== true) {
4736                _this.ownsCanonicalTemplate = false;
4737                _this.authorOwnsTemplate(false);
4738                _this.selectedBranchCopyNetId(null);
4739                _this.viewingBranchCopyNetId(null);
4740                _this.mergeModalOpen(false);
4741                if (data.lifetimeInformation) {
4742                    _this.presentPersistedLifetimeInformation(data.lifetimeInformation);
4743                }
4744                else if (data.scoresStatus) {
4745                    _this.scoresStatus(String(data.scoresStatus));
4746                }
4747                void _this.adoptPersonalCopyFromServer(personalCopyNetId, data.setId).then(function () {
4748                    _this.loadSpecLogs(true);
4749                    if (_this.proposePanelTab() === 'branches') {
4750                        _this.loadBranchList();
4751                    }
4752                });
4753                return;
4754            }
4755            if (data && data.templateNetId != null) {
4756                var templateNetId = parseInt(String(data.templateNetId), 10);
4757                if (Number.isFinite(templateNetId) && templateNetId > 0) {
4758                    _this.sharedTemplateNetId(templateNetId);
4759                }
4760            }
4761            if (data && data.lifetimeInformation) {
4762                _this.presentPersistedLifetimeInformation(data.lifetimeInformation);
4763            }
4764            else if (data && data.scoresStatus) {
4765                _this.scoresStatus(String(data.scoresStatus));
4766            }
4767            _this.selectedBranchCopyNetId(null);
4768            _this.viewingBranchCopyNetId(null);
4769            _this.mergeModalOpen(false);
4770            _this.refreshBranchRowSelection();
4771            _this.restoreOwnNetView();
4772            _this.loadSpecLogs(true);
4773            if (_this.proposePanelTab() === 'branches') {
4774                _this.loadBranchList();
4775            }
4776        };
4777        this.submitAuthModal = function () { return __awaiter(_this, void 0, void 0, function () {
4778            var currentUser, userId, email, password, username, previousUserId, path, response, parsed, identitySwitched, e_10;
4779            return __generator(this, function (_a) {
4780                switch (_a.label) {
4781                    case 0:
4782                        if (this.authSubmitting()) {
4783                            return [2 /*return*/];
4784                        }
4785                        currentUser = Distiller.currentUser && Distiller.currentUser();
4786                        userId = currentUser && currentUser.userId;
4787                        if (!userId) {
4788                            this.authError('Missing current user.');
4789                            return [2 /*return*/];
4790                        }
4791                        email = (this.authEmail() || '').trim();
4792                        password = this.authPassword() || '';
4793                        username = (this.authUsername() || '').trim();
4794                        if (this.authModalMode() === 'signup') {
4795                            if (!username) {
4796                                this.authError('Please enter a username.');
4797                                return [2 /*return*/];
4798                            }
4799                        }
4800                        if (!email || email.indexOf('@') < 0) {
4801                            this.authError('Please enter a valid email address.');
4802                            return [2 /*return*/];
4803                        }
4804                        if (!password) {
4805                            this.authError('Please enter a password.');
4806                            return [2 /*return*/];
4807                        }
4808                        this.authSubmitting(true);
4809                        this.authError('');
4810                        previousUserId = userId;
4811                        path = this.authModalMode() === 'signin' ? 'bayesBotSignin' : 'bayesBotSignup';
4812                        _a.label = 1;
4813                    case 1:
4814                        _a.trys.push([1, 5, , 6]);
4815                        return [4 /*yield*/, fetch(Distiller.apiEndpoint + '/api/' + path + '/' + userId, {
4816                                method: 'POST',
4817                                headers: { 'Content-Type': 'application/json' },
4818                                credentials: 'same-origin',
4819                                body: JSON.stringify({
4820                                    username: username,
4821                                    email: email,
4822                                    password: password,
4823                                    urlParams: Distiller.getUrlMetaParamsToSave ? Distiller.getUrlMetaParamsToSave() : [],
4824                                }),
4825                            })];
4826                    case 2:
4827                        response = _a.sent();
4828                        return [4 /*yield*/, response.json()];
4829                    case 3:
4830                        parsed = _a.sent();
4831                        if (parsed && parsed.code === 'email_exists') {
4832                            this.authModalMode('signin');
4833                            this.authError('An account with this email already exists. Sign in below.');
4834                            this.authSubmitting(false);
4835                            return [2 /*return*/];
4836                        }
4837                        if (!parsed || parsed.message !== 'successful login') {
4838                            this.authError(parsed && parsed.alertMessage
4839                                ? parsed.alertMessage
4840                                : (parsed && parsed.error) || 'Could not complete that request.');
4841                            this.authSubmitting(false);
4842                            return [2 /*return*/];
4843                        }
4844                        identitySwitched = parsed.userId != previousUserId;
4845                        if (Distiller.hydrateBayesBotLoginResponse) {
4846                            Distiller.hydrateBayesBotLoginResponse(parsed);
4847                        }
4848                        this.authPassword('');
4849                        this.closeAuthModal();
4850                        return [4 /*yield*/, this.onBayesBotAuthChanged(identitySwitched)];
4851                    case 4:
4852                        _a.sent();
4853                        return [3 /*break*/, 6];
4854                    case 5:
4855                        e_10 = _a.sent();
4856                        console.error('BayesBot auth modal failed:', e_10);
4857                        this.authError('Could not complete that request.');
4858                        this.authSubmitting(false);
4859                        return [3 /*break*/, 6];
4860                    case 6: return [2 /*return*/];
4861                }
4862            });
4863        }); };
4864        this.onBayesBotAuthChanged = function (identitySwitched) { return __awaiter(_this, void 0, void 0, function () {
4865            var currentUser, agentKeyPromise, e_11, e_12, e_13;
4866            return __generator(this, function (_a) {
4867                switch (_a.label) {
4868                    case 0:
4869                        currentUser = Distiller.currentUser && Distiller.currentUser();
4870                        this.viewerUserId = currentUser && currentUser.userId ? currentUser.userId : this.viewerUserId;
4871                        if (!identitySwitched) {
4872                            return [2 /*return*/];
4873                        }
4874                        this.agentKey(null);
4875                        this.agentKeyReady = null;
4876                        this.cloneReady = false;
4877                        this.clonePromise = null;
4878                        this.personalCopyNetId = null;
4879                        this.connectBayesBotSse();
4880                        _a.label = 1;
4881                    case 1:
4882                        _a.trys.push([1, 3, , 4]);
4883                        agentKeyPromise = this.ensureAgentKey();
4884                        return [4 /*yield*/, agentKeyPromise];
4885                    case 2:
4886                        _a.sent();
4887                        return [3 /*break*/, 4];
4888                    case 3:
4889                        e_11 = _a.sent();
4890                        console.error('BayesBot auth identity switch: agent key failed', e_11);
4891                        return [3 /*break*/, 4];
4892                    case 4:
4893                        _a.trys.push([4, 9, , 10]);
4894                        if (!this.isCreateMode()) return [3 /*break*/, 6];
4895                        return [4 /*yield*/, this.initializeDraftStudy()];
4896                    case 5:
4897                        _a.sent();
4898                        return [3 /*break*/, 8];
4899                    case 6: return [4 /*yield*/, this.loadAuthorBayesNet()];
4900                    case 7:
4901                        _a.sent();
4902                        _a.label = 8;
4903                    case 8: return [3 /*break*/, 10];
4904                    case 9:
4905                        e_12 = _a.sent();
4906                        console.error('BayesBot auth identity switch: model reload failed', e_12);
4907                        return [3 /*break*/, 10];
4908                    case 10:
4909                        _a.trys.push([10, 12, , 13]);
4910                        return [4 /*yield*/, this.loadBayesBotUserModel()];
4911                    case 11:
4912                        _a.sent();
4913                        return [3 /*break*/, 13];
4914                    case 12:
4915                        e_13 = _a.sent();
4916                        console.error('BayesBot auth identity switch: model preference failed', e_13);
4917                        return [3 /*break*/, 13];
4918                    case 13: return [2 /*return*/];
4919                }
4920            });
4921        }); };
4922        this.submitNetProposal = function () { return __awaiter(_this, void 0, void 0, function () {
4923            var text, currentUser, userId, responseText, parsed, e_14;
4924            return __generator(this, function (_a) {
4925                switch (_a.label) {
4926                    case 0:
4927                        text = (this.nodeProposalText() || '').trim();
4928                        if (!text || this.nodeProposalSubmitting()) {
4929                            return [2 /*return*/];
4930                        }
4931                        currentUser = Distiller.currentUser && Distiller.currentUser();
4932                        userId = currentUser && currentUser.userId;
4933                        if (!userId) {
4934                            console.error('BayesBot create-net submit: missing current user');
4935                            return [2 /*return*/];
4936                        }
4937                        this.liveTemplateBuild = true;
4938                        this.ensureCreateCanvasMounted();
4939                        this.nodeProposalSubmitting(true);
4940                        this.proposalJobSettled = false;
4941                        this.submittedProposalText(text);
4942                        this.proposalFinalResponse('');
4943                        this.proposalInfoValue(null);
4944                        this.proposalPhase('processing');
4945                        this.setProposalStatusText('Reading the description…');
4946                        this.nodeProposalText('');
4947                        _a.label = 1;
4948                    case 1:
4949                        _a.trys.push([1, 3, , 4]);
4950                        this.connectBayesBotSse();
4951                        return [4 /*yield*/, Distiller.httpPostJsonAsync(Distiller.apiEndpoint + '/api/bayesBotCreateNet/' + userId, { text: text, communityId: this.resolveBoundCommunityId(), model: this.selectedBayesBotModel() })];
4952                    case 2:
4953                        responseText = _a.sent();
4954                        parsed = typeof responseText === 'string' ? JSON.parse(responseText) : responseText;
4955                        if (parsed && parsed.jobId) {
4956                            this.lastBayesBotJobId(parsed.jobId);
4957                            this.pollBayesBotJobUntilDone(parsed.jobId);
4958                            if (parsed.progress || parsed.status) {
4959                                this.setProposalStatusText(parsed.message || 'Reading the description…');
4960                            }
4961                        }
4962                        else {
4963                            console.error('BayesBot create-net unexpected response:', responseText);
4964                            this.proposalJobSettled = true;
4965                            this.proposalPhase('error');
4966                            this.setProposalStatusText('Something went wrong');
4967                            this.proposalFinalResponse('The description could not be submitted.');
4968                            this.nodeProposalSubmitting(false);
4969                        }
4970                        return [3 /*break*/, 4];
4971                    case 3:
4972                        e_14 = _a.sent();
4973                        console.error('BayesBot create-net submit failed:', e_14);
4974                        this.proposalJobSettled = true;
4975                        this.proposalPhase('error');
4976                        this.setProposalStatusText('Something went wrong');
4977                        this.proposalFinalResponse('The description could not be submitted.');
4978                        this.nodeProposalSubmitting(false);
4979                        return [3 /*break*/, 4];
4980                    case 4: return [2 /*return*/];
4981                }
4982            });
4983        }); };
4984        this.submitNodeProposal = function () { return __awaiter(_this, void 0, void 0, function () {
4985            var text, currentUser, userId, alreadyCloned, e_15, payload, responseText, parsed, queuedLabel, e_16;
4986            return __generator(this, function (_a) {
4987                switch (_a.label) {
4988                    case 0:
4989                        text = (this.nodeProposalText() || '').trim();
4990                        if (!text || this.nodeProposalSubmitting()) {
4991                            return [2 /*return*/];
4992                        }
4993                        currentUser = Distiller.currentUser && Distiller.currentUser();
4994                        userId = currentUser && currentUser.userId;
4995                        if (!userId) {
4996                            console.error('BayesBot submit: missing current user');
4997                            return [2 /*return*/];
4998                        }
4999                        this.connectBayesBotSse();
5000                        this.nodeProposalSubmitting(true);
5001                        this.proposalJobSettled = false;
5002                        this.submittedProposalText(text);
5003                        this.proposalFinalResponse('');
5004                        this.proposalInfoValue(null);
5005                        this.proposalPhase('processing');
5006                        this.nodeProposalText('');
5007                        _a.label = 1;
5008                    case 1:
5009                        _a.trys.push([1, 4, , 5]);
5010                        alreadyCloned = this.cloneReady && this.personalCopyNetId && !this.viewingSharedTemplate;
5011                        if (!(!this.ownsCanonicalTemplate && !alreadyCloned)) return [3 /*break*/, 3];
5012                        this.setProposalStatusText('Preparing clone for editing…');
5013                        return [4 /*yield*/, this.ensurePersonalCopy({ silent: false })];
5014                    case 2:
5015                        _a.sent();
5016                        _a.label = 3;
5017                    case 3: return [3 /*break*/, 5];
5018                    case 4:
5019                        e_15 = _a.sent();
5020                        console.error('BayesBot prepare clone failed:', e_15);
5021                        this.proposalJobSettled = true;
5022                        this.proposalPhase('error');
5023                        this.setProposalStatusText('Something went wrong');
5024                        this.proposalFinalResponse('Could not prepare your copy of the model.');
5025                        this.nodeProposalSubmitting(false);
5026                        return [2 /*return*/];
5027                    case 5:
5028                        this.setProposalStatusText('Processing proposal');
5029                        payload = {
5030                            text: text,
5031                            questionText: this.boundQuestionText() || '',
5032                            resolutionCriteria: this.boundResolutionCriteria() || '',
5033                            communityId: this.resolveBoundCommunityId(),
5034                            model: this.selectedBayesBotModel(),
5035                        };
5036                        _a.label = 6;
5037                    case 6:
5038                        _a.trys.push([6, 8, , 9]);
5039                        return [4 /*yield*/, Distiller.httpPostJsonAsync(Distiller.apiEndpoint + '/api/bayesBotSubmit/' 
5039+ userId, payload)];
5040                    case 7:
5041                        responseText = _a.sent();
5042                        parsed = typeof responseText === 'string' ? JSON.parse(responseText) : responseText;
5043                        if (parsed && parsed.jobId) {
5044                            this.lastBayesBotJobId(parsed.jobId);
5045                            this.pollBayesBotJobUntilDone(parsed.jobId);
5046                            if (parsed.progress || parsed.status) {
5047                                queuedLabel = parsed.status === 'queued' || parsed.progress === 'queued'
5048                                    ? 'Processing proposal'
5049                                    : this.proposalStatusText();
5050                                this.setProposalStatusText(queuedLabel);
5051                            }
5052                        }
5053                        else {
5054                            console.error('BayesBot submit unexpected response:', responseText);
5055                            this.proposalJobSettled = true;
5056                            this.proposalPhase('error');
5057                            this.setProposalStatusText('Something went wrong');
5058                            this.proposalFinalResponse('The proposal could not be submitted.');
5059                            this.nodeProposalSubmitting(false);
5060                        }
5061                        return [3 /*break*/, 9];
5062                    case 8:
5063                        e_16 = _a.sent();
5064                        console.error('BayesBot submit failed:', e_16);
5065                        this.proposalJobSettled = true;
5066                        this.proposalPhase('error');
5067                        this.setProposalStatusText('Something went wrong');
5068                        this.proposalFinalResponse('The proposal could not be submitted.');
5069                        this.nodeProposalSubmitting(false);
5070                        return [3 /*break*/, 9];
5071                    case 9: return [2 /*return*/];
5072                }
5073            });
5074        }); };
5075        this.onNodeProposalKeydown = function (_data, event) {
5076            if (event.key === 'Enter' && (event.metaKey || event.ctrlKey)) {
5077                event.preventDefault();
5078                _this.submitComposer();
5079                return false;
5080            }
5081            return true;
5082        };
5083        this.getQuillOptions = function (purpose, index, isReadOnly, parentIndex) {
5084            var outerContainerId = ko.observable('outerQuillContainerFor_' + index);
5085            if (purpose === 'description') {
5086                outerContainerId = ko.observable('outerQuillContainerFor_description');
5087            }
5088            if (purpose === 'edit-about') {
5089                outerContainerId = ko.observable('outerQuillContainerFor_edit-about');
5090            }
5091            var atIndex = ko.observable(index);
5092            var idParam = atIndex;
5093            if (parentIndex || parentIndex === 0) {
5094                atIndex = ko.observable(parentIndex);
5095            }
5096            var options = {
5097                isReadOnly: true,
5098                outerContainerId: outerContainerId,
5099                atIndex: atIndex,
5100                idParam: idParam,
5101            };
5102            if (!isReadOnly) {
5103                options = {
5104                    placeholder: 'This is a text block. Add static content here.',
5105                    isReadOnly: isReadOnly,
5106                    outerContainerId: outerContainerId,
5107                    toolbarOptions: [
5108                        ['bold', 'italic', 'underline', 'strike', 'blockquote'], // toggled buttons
5109                        //  ['blockquote', 'code-block'],
5110                        // [{ 'header': 1 }, { 'header': 2 }],               // custom button values
5111                        [{ 'list': 'ordered' }, { 'list': 'bullet' }],
5112                        [{ 'script': 'sub' }, { 'script': 'super' }], // superscript/subscript
5113                        [{ 'indent': '-1' }, { 'indent': '+1' }], // outdent/indent
5114                        [{ 'size': ['small', false, 'large', 'huge'] }], // custom dropdown
5115                        //    [{ 'header': [1, 2, 3, 4, 5, 6, false] }],
5116                        [{ 'color': [] }, { 'background': [] }], // dropdown with defaults from theme
5117                        // [{ 'font': []}],
5118                        [{ 'font': ['montserrat', 'arvo'] }],
5119                        [{ 'align': [] }],
5120                        ['clean', 'link', 'image', 'video']
5121                    ],
5122                    atIndex: atIndex,
5123                    idParam: idParam,
5124                };
5125                return options;
5126            }
5127            if (purpose === 'description') {
5128                options.placeholder = "This text is featured at the top of the page. If you are using this page to ask a forecasting question, this description should contain that forecasting question (and any background you want to include -- links, images, whatever!).";
5129            }
5130            else if (purpose === 'question') {
5131                options.placeholder = "Write your question here (optional - if your page description contains all the information your users will need, then you can leave this blank).";
5132            }
5133            else if (purpose === 'graph') {
5134                options.placeholder = "Describe your graph here (optional).";
5135            }
5136            else if (purpose === 'comment') {
5137                options.placeholder = "Describe the comments that will appear here (optional).";
5138            }
5139            else if (purpose === 'edit-about') {
5140                options.placeholder = "This is the first page participants see when they register for your study. Describe what they should expect to do here. Add images, videos, as needed!";
5141            }
5142            return options;
5143        };
5144        this.updateQuestionWithValues = function (question, values) {
5145            if (!values) {
5146                return question;
5147            }
5148            var keys = Object.keys(values);
5149            if (question.text()) {
5150                for (var i = 0; i < keys.length; i++) {
5151                    var key = keys[i];
5152                    var value = values[key];
5153                    question.text(question.text().replace(new RegExp(_this.escapeRegExp(key), 'g'), value));
5154                }
5155            }
5156            if (question.quillDelta()) {
5157                var quillDelta = question.quillDelta();
5158                if (typeof quillDelta !== 'string') {
5159                    quillDelta = JSON.stringify(quillDelta);
5160                }
5161                for (var i = 0; i < keys.length; i++) {
5162                    var key = keys[i];
5163                    var value = values[key];
5164                    quillDelta = quillDelta.replace(new RegExp(_this.escapeRegExp(key), 'g'), value);
5165                }
5166                question.quillDelta(JSON.parse(quillDelta));
5167            }
5168            if (question.answers().length > 0) {
5169                for (var i = 0; i < question.answers().length; i++) {
5170                    var text = question.answers()[i].text();
5171                    var defaultValue = question.answers()[i].defaultValue();
5172                    if (text) {
5173                        for (var i_1 = 0; i_1 < keys.length; i_1++) {
5174                            var key = keys[i_1];
5175                            var value = values[key];
5176                            text = text.replace(new RegExp(_this.escapeRegExp(key), 'g'), value);
5177                        }
5178                        question.answers()[i].text(text);
5179                    }
5180                    if (defaultValue) {
5181                        for (var i_2 = 0; i_2 < keys.length; i_2++) {
5182                            var key = keys[i_2];
5183                            var value = values[key];
5184                            defaultValue = defaultValue.replace(new RegExp(_this.escapeRegExp(key), 'g'), value);
5185                        }
5186                        question.answers()[i].defaultValue(defaultValue);
5187                    }
5188                }
5189            }
5190            return question;
5191        };
5192        this.updateTextWithValues = function (text, values) {
5193            if (!values || !text) {
5194                return text;
5195            }
5196            var keys = Object.keys(values);
5197            for (var i = 0; i < keys.length; i++) {
5198                var key = keys[i];
5199                var value = values[key];
5200                text = text.replace(new RegExp(_this.escapeRegExp(key), 'g'), value);
5201            }
5202            return text;
5203        };
5204        this.elementWithOriginalValues = function (element, deepClone) {
5205            element.text(deepClone.text);
5206            element.quillDelta(deepClone.quillDelta);
5207            if (element.variableValues && element.variableValues()) {
5208                element.variableValues(JSON.parse(JSON.stringify(deepClone.variableValues)));
5209            }
5210            if (element.question && element.question()) {
5211                element.question().text(deepClone.question.text);
5212                element.question().quillDelta(deepClone.question.quillDelta);
5213                if (element.question().answers && element.question().answers()) {
5214                    for (var i = 0; i < element.question().answers().length; i++) {
5215                        element.question().answers()[i].text(deepClone.question.answers[i].text);
5216                    }
5217                }
5218                if (element.question().timepointQuestions && element.question().timepointQuestions()) {
5219                    for (var i = 0; i < element.question().timepointQuestions().length; i++) {
5220                        element.question().timepointQuestions()[i].text(deepClone.question.timepointQuestions[i].text);
5221                        if (element.question().timepointQuestions()[i].answers && element.question().timepointQuestions()[i].answers()) {
5222                            for (var a = 0; a < element.question().timepointQuestions()[i].answers().length; a++) {
5223                                element.question().timepointQuestions()[i].answers()[a].text(deepClone.question.timepointQuestions[i].answers[a].text);
5224                            }
5225                        }
5226                    }
5227                }
5228            }
5229            if (element.subElements && element.subElements().length > 0) {
5230                for (var i = 0; i < element.subElements().length; i++) {
5231                    var subElement = _this.elementWithOriginalValues(element.subElements()[i], deepClone.subElements[i]);
5232                    element.subElements()[i] = subElement;
5233                }
5234            }
5235            return element;
5236        };
5237        this.showNextButton = ko.observable(false);
5238        this.showNextButtonComputed = ko.computed(function () {
5239            var _a;
5240            if (!_this.currentStageWorkflow()) {
5241                _this.showNextButton(false);
5242                return;
5243            }
5244            var questionsToExclude = [];
5245            //IF THE ADMIN EXPLICITLY ADDED THE NEXT BUTTON, ALWAYS SHOW IT 
5246            if (_this.currentStageWorkflow().elements().find(function (a) { return a.hasOwnProperty('buttonType') && a.buttonType().value === 'nextPage' && !a.isAutoAdded; })) {
5247                if (Distiller.reevaluatingPathFor().length > 0) {
5248                    _this.showNextButton(false);
5249                    return;
5250                }
5251                _this.showNextButton(true);
5252                return;
5253            }
5254            var _loop_1 = function (i) {
5255                if (_this.currentStageWorkflow().elements()[i].type === 'question' && _this.currentStageWorkflow().elements()[i].question()) {
5256                    if (!_this.currentStageWorkflow().elements()[i].generalQuestionType()) {
5257                        //this is an edge case where they've added a question element but haven't defined the type 
5258                        questionsToExclude.push(_this.currentStageWorkflow().elements()[i].question().questionId());
5259                    }
5260                    else if (Distiller.showHideIfPayload().find(function (a) { return a.workflowIndex === i; })) {
5261                        if (Distiller.showHideIfPayload().find(function (a) { return a.workflowIndex === i && a.action === 'showIf' && a.status() === 0; })) {
5262                            questionsToExclude.push(_this.currentStageWorkflow().elements()[i].question().questionId());
5263                            for (var t = 0; t < _this.currentStageWorkflow().elements()[i].question().timepointQuestions().length; t++) {
5264                                questionsToExclude.push(_this.currentStageWorkflow().elements()[i].question().timepointQuestions()[t].questionId());
5265                            }
5266                        }
5267                        else if (Distiller.showHideIfPayload().find(function (a) { return a.workflowIndex === i && a.action === 'hideIf' && a.status() === 1; })) {
5268                            questionsToExclude.push(_this.currentStageWorkflow().elements()[i].question().questionId());
5269                            for (var t = 0; t < _this.currentStageWorkflow().elements()[i].question().timepointQuestions().length; t++) {
5270                                questionsToExclude.push(_this.currentStageWorkflow().elements()[i].question().timepointQuestions()[t].questionId());
5271                            }
5272                        }
5273                    }
5274                }
5275            };
5276            for (var i = 0; i < _this.currentStageWorkflow().elements().length; i++) {
5277                _loop_1(i);
5278            }
5279            if (!_this.questionSet() || Distiller.autoSaving() || Distiller.reevaluatingPathFor().length > 0) {
5280                _this.showNextButton(false);
5281                return;
5282            }
5283            //if they have completed the page AND there are more pages to show
5284            if (Distiller.questionsOnPath().filter(function (a) { var _a; return a.setId === ((_a = _this.questionSet()) === null || _a === void 0 ? void 0 : _a.setId()) && questionsToExclude.indexOf(a.questionId) < 0; }).filter(function (a) { return !a.isComplete(); }).length === 0
5285                && _this.questionSet().setId() !== ((_a = Distiller.questionSets()[Distiller.questionSets().length - 1]) === null || _a === void 0 ? void 0 : _a.setId())) {
5286                _this.showNextButton(true);
5287                return;
5288            }
5289            _this.showNextButton(false);
5290            return;
5291        });
5292        this.settingVariables = ko.observable(false);
5293        this.subscribeToQuestionReference = function (subscribableRow) {
5294            var questionReferences = subscribableRow.questionSubstrings;
5295            var _loop_2 = function (i) {
5296                var questionId = Distiller.extractNumberAfterPrefix(questionReferences[i], 'QID');
5297                var answerId = Distiller.extractNumberAfterPrefix(questionReferences[i], 'AID');
5298                if (!Distiller.variableValueSubscriptions().find(function (a) { return a.questionId === questionId && a.answerId === answerId && a.variableName === subscribableRow.variableName && a.variableValue === subscribableRow.variableValue; })) {
5299                    var question = Distiller.allQuestions().find(function (a) { return a.questionId() === parseInt(questionId); });
5300                    if (question) {
5301                        var row_1 = {
5302                            questionId: questionId,
5303                            answerId: answerId,
5304                            variableName: subscribableRow.variableName,
5305                            variableValue: subscribableRow.variableValue,
5306                            subscription: null
5307                        };
5308                        row_1.subscription = question.savedCurrentForecast.subscribe(function (a) {
5309                            _this.setVariableValuesClientSide(row_1, a);
5310                        });
5311                        Distiller.variableValueSubscriptions.push(row_1);
5312                    }
5313                }
5314            };
5315            for (var i = 0; i < questionReferences.length; i++) {
5316                _loop_2(i);
5317            }
5318        };
5319        this.updateVariablesTimeout = null;
5320        this.indexesToUpdate = ko.observableArray([]);
5321        this.highlightRequired = ko.observable(false);
5322        this.setVariableValuesClientSide = function (subscriptionRow, savedCurrentForecast) {
5323            if (_this.updateVariablesTimeout) {
5324                clearTimeout(_this.updateVariablesTimeout);
5325            }
5326            // let requestId = Distiller.fakeNumericId(Distiller.userPathMetadataRequestIds());
5327            //Distiller.userPathMetadataRequestIds.push(requestId);
5328            var updateableElements = Distiller.updatableElements().filter(function (a) { return a.setId === _this.questionSet().setId(); });
5329            var indexesToUpdate = [];
5330            for (var i = 0; i < updateableElements.length; i++) {
5331                var elementRow = updateableElements[i];
5332                var element = elementRow.deepClone;
5333                if ((element.text && element.text.indexOf(subscriptionRow.variableName) > -1)
5334                    || (element.quillDelta && JSON.stringify(element.quillDelta).indexOf(subscriptionRow.variableName) > -1)) {
5335                    indexesToUpdate.push(elementRow.elementIndex);
5336                }
5337                //updatable elements are PLAIN JS 
5338                if (element.question) {
5339                    if ((element.question.text && element.question.text.indexOf(subscriptionRow.variableName) > -1)
5340                        || (element.question.quillDelta && JSON.stringify(element.question.quillDelta).indexOf(subscriptionRow.variableName) > -1)) {
5341                        indexesToUpdate.push(elementRow.elementIndex);
5342                    }
5343                    for (var j = 0; j < element.question.timepointQuestions.length; j++) {
5344                        if ((element.question.timepointQuestions[j].text && element.question.timepointQuestions[j].text.indexOf(subscriptionRow.variableName) > -1)
5345                            || (element.question.timepointQuestions[j].quillDelta && JSON.stringify(element.question.timepointQuestions[j].quillDelta).indexOf(subscriptionRow.variableName) > -1)) {
5346                            indexesToUpdate.push(elementRow.elementIndex);
5347                        }
5348                    }
5349                    for (var j = 0; j < element.question.answers.length; j++) {
5350                        if ((element.question.answers[j].text && element.question.answers[j].text.indexOf(subscriptionRow.variableName) > -1)
5351                            || (element.question.answers[j].quillDelta && JSON.stringify(element.question.answers[j].quillDelta).indexOf(subscriptionRow.variableName) > -1)) {
5352                            indexesToUpdate.push(elementRow.elementIndex);
5353                        }
5354                    }
5355                }
5356            }
5357            var self = _this;
5358            if (indexesToUpdate.length > 0) {
5359                for (var i = 0; i < indexesToUpdate.length; i++) {
5360                    if (_this.indexesToUpdate().indexOf(indexesToUpdate[i]) < 0) {
5361                        _this.indexesToUpdate.push(indexesToUpdate[i]);
5362                    }
5363                }
5364                _this.updateVariablesTimeout = setTimeout(function () {
5365                    self.updateVariablesTimeout = null;
5366                    _this.setVariableValues(_this.indexesToUpdate());
5367                    _this.indexesToUpdate([]);
5368                }, 100);
5369            }
5370            else if (_this.indexesToUpdate().length > 0) {
5371                _this.updateVariablesTimeout = setTimeout(function () {
5372                    self.updateVariablesTimeout = null;
5373                    _this.setVariableValues(_this.indexesToUpdate());
5374                    _this.indexesToUpdate([]);
5375                }, 100);
5376            }
5377        };
5378        this.setVariableValues = function (indexesToUpdate) { return __awaiter(_this, void 0, void 0, function () {
5379            var startingVariableValues, _loop_3, this_1, i, i, questionReferences, allVariableValueKeys, _loop_4, this_2, i, uniqueQuestionReferences, arrayOfUniqueReferences, questionReferenceValues, questionRefKeys, i, keys, v, q, text, expressionReferences, i, keys, v, expressionSubstrings, uniqueExpressionReferences, arrayOfUniqueExpressionReferences, expressionReferenceValues, i, expression, expressionValue, expressionRefKeys, i, keys, v, q, text, i, variableValues, newText, quillDelta, j, k, paramValue, newParamValue, question, variableValuesToSave, _loop_5, this_3, i;
5380            var _this = this;
5381            return __generator(this, function (_a) {
5382                switch (_a.label) {
5383                    case 0:
5384                        startingVariableValues = [];
5385                        if (indexesToUpdate && indexesToUpdate.length > 0) {
5386                            _loop_3 = function (i) {
5387                                var variableNames = Object.keys(this_1.currentStageWorkflow().elements()[indexesToUpdate[i]].variableValues());
5388                                var _loop_6 = function (v) {
5389                                    if (!startingVariableValues.find(function (a) { return a.name === variableNames[v]; })) {
5390                                        startingVariableValues.push({
5391                                            name: variableNames[v],
5392                                            value: this_1.currentStageWorkflow().elements()[indexesToUpdate[i]].variableValues()[variableNames[v]]
5393                                        });
5394                                    }
5395                                };
5396                                for (var v = 0; v < variableNames.length; v++) {
5397                                    _loop_6(v);
5398                                }
5399                            };
5400                            this_1 = this;
5401                            for (i = 0; i < indexesToUpdate.length; i++) {
5402                                _loop_3(i);
5403                            }
5404                        }
5405                        if (!indexesToUpdate) {
5406                            for (i = 0; i < Distiller.variableValueSubscriptions().length; i++) {
5407                                Distiller.variableValueSubscriptions()[i].subscription.dispose();
5408                            }
5409                            Distiller.variableValueSubscriptions([]);
5410                        }
5411                        //  Distiller.updatableElements([]);
5411 //new june 9 2025 clear out updatable elements
5412                        if (!indexesToUpdate) {
5413                            this.settingVariables(true);
5414                        }
5415                        questionReferences = [];
5416                        allVariableValueKeys = [];
5417                        _loop_4 = function (i) {
5418                            if (!indexesToUpdate || indexesToUpdate.indexOf(i) > -1) {
5419                                if (this_2.currentStageWorkflow().elements()[i].variableValues && this_2.currentStageWorkflow().elements()[i].variableValues()) {
5420                                    //if this element is updatable, we need to replace its text, questionText, and answerOption text with the 
5421                                    //values from the deep clone so that the rest of the question reference script below will apply
5422                                    if (Distiller.updatableElements().find(function (a) { return a.setId === _this.questionSet().setId() && a.elementIndex === i; })) {
5423                                        this_2.currentStageWorkflow().elements()[i] = this_2.elementWithOriginalValues(this_2.currentStageWorkflow().elements()[i], Distiller.updatableElements().find(function (a) { return a.setId === _this.questionSet().setId() && a.elementIndex === i; }).deepClone);
5424                                    }
5425                                    var keys = Object.keys(this_2.currentStageWorkflow().elements()[i].variableValues());
5426                                    allVariableValueKeys.push.apply(allVariableValueKeys, keys);
5427                                    for (var v = 0; v < keys.length; v++) {
5428                                        var questionSubstrings = Distiller.extractQuestionSubstrings(this_2.currentStageWorkflow().elements()[i].variableValues()[keys[v]]);
5429                                        if (questionSubstrings.length > 0) {
5430                                            var row = {
5431                                                variableName: keys[v],
5432                                                variableValue: this_2.currentStageWorkflow().elements()[i].variableValues()[keys[v]],
5433                                                questionSubstrings: questionSubstrings
5434                                            };
5435                                            this_2.subscribeToQuestionReference(row);
5436                                        }
5437                                        questionReferences.push.apply(questionReferences, questionSubstrings);
5438                                    }
5439                                    //clone elements with question references so we can update them
5440                                    if (questionReferences.length > 0 && !Distiller.updatableElements().find(function (a) { return a.setId === _this.questionSet().setId() && a.elementIndex === i; })) {
5441                                        var elementReference = {
5442                                            setId: this_2.questionSet().setId(),
5443                                            elementIndex: i,
5444                                            deepClone: Distiller.deepClone(ko.toJS(this_2.currentStageWorkflow().elements()[i]))
5445                                        };
5446                                        Distiller.updatableElements.push(elementReference);
5447                                    }
5448                                }
5449                            }
5450                        };
5451                        this_2 = this;
5452                        //extract references to questions 
5453                        //get reference:values pairs from server 
5454                        //replace reference within value
5455                        //do math if applicable
5456                        for (i = 0; i < this.currentStageWorkflow().elements().length; i++) {
5457                            _loop_4(i);
5458                        }
5459                        uniqueQuestionReferences = new Set(questionReferences);
5460                        arrayOfUniqueReferences = Array.from(uniqueQuestionReferences);
5461                        if (!(questionReferences.length > 0)) return [3 /*break*/, 2];
5462                        return [4 /*yield*/, this.getQuestionReferenceValues(arrayOfUniqueReferences)];
5463                    case 1:
5464                        questionReferenceValues = _a.sent();
5465                        if (!questionReferenceValues.err) {
5466                            questionRefKeys = Object.keys(questionReferenceValues);
5467                            for (i = 0; i < this.currentStageWorkflow().elements().length; i++) {
5468                                if (!indexesToUpdate || indexesToUpdate.indexOf(i) > -1) {
5469                                    if (this.currentStageWorkflow().elements()[i].variableValues && this.currentStageWorkflow().elements()[i].variableValues()) {
5470                                        keys = Object.keys(this.currentStageWorkflow().elements()[i].variableValues());
5471                                        for (v = 0; v < keys.length; v++) {
5472                                            for (q = 0; q < questionRefKeys.length; q++) {
5473                                                text = this.currentStageWorkflow().elements()[i].variableValues()[keys[v]];
5474                                                text = text.replace(new RegExp(this.escapeRegExp(questionRefKeys[q]), 'g'), questionReferenceValues[questionRefKeys[q]]);
5475                                                this.currentStageWorkflow().elements()[i].variableValues()[keys[v]] = text;
5476                                            }
5477                                        }
5478                                    }
5479                                }
5480                            }
5481                        }
5482                        _a.label = 2;
5483                    case 2:
5484                        expressionReferences = [];
5485                        //check for $e{} and extract contents 
5486                        for (i = 0; i < this.currentStageWorkflow().elements().length; i++) {
5487                            if (!indexesToUpdate || indexesToUpdate.indexOf(i) > -1) {
5488                                if (this.currentStageWorkflow().elements()[i].variableValues && this.currentStageWorkflow().elements()[i].variableValues()) {
5489                                    keys = Object.keys(this.currentStageWorkflow().elements()[i].variableValues());
5490                                    for (v = 0; v < keys.length; v++) {
5491                                        expressionSubstrings = this.extractExpressionSubstrings(this.currentStageWorkflow().elements()[i].variableValues()[keys[v]]);
5492                                        expressionReferences.push.apply(expressionReferences, expressionSubstrings);
5493                                    }
5494                                }
5495                            }
5496                        }
5497                        if (expressionReferences.length > 0) {
5498                            uniqueExpressionReferences = new Set(expressionReferences);
5499                            arrayOfUniqueExpressionReferences = Array.from(uniqueExpressionReferences);
5500                            expressionReferenceValues = {};
5501                            for (i = 0; i < arrayOfUniqueExpressionReferences.length; i++) {
5502                                expression = arrayOfUniqueExpressionReferences[i];
5503                                expressionValue = null;
5504                                try {
5505                                    expressionValue = this.evaluateMathExpression(expression);
5506                                }
5507                                catch (error) {
5508                                    console.log("error evaluating math expression", error);
5509                                    expressionValue = 'ERROR';
5510                                }
5511                                expressionReferenceValues[expression] = expressionValue;
5512                            }
5513                            expressionRefKeys = Object.keys(expressionReferenceValues);
5514                            for (i = 0; i < this.currentStageWorkflow().elements().length; i++) {
5515                                if (!indexesToUpdate || indexesToUpdate.indexOf(i) > -1) {
5516                                    if (this.currentStageWorkflow().elements()[i].variableValues && this.currentStageWorkflow().elements()[i].variableValues()) {
5517                                        keys = Object.keys(this.currentStageWorkflow().elements()[i].variableValues());
5518                                        for (v = 0; v < keys.length; v++) {
5519                                            for (q = 0; q < expressionRefKeys.length; q++) {
5520                                                text = this.currentStageWorkflow().elements()[i].variableValues()[keys[v]];
5521                                                text = text.replace(new RegExp(this.escapeRegExp(expressionRefKeys[q]), 'g'), expressionReferenceValues[expressionRefKeys[q]]);
5522                                                this.currentStageWorkflow().elements()[i].variableValues()[keys[v]] = text;
5523                                            }
5524                                        }
5525                                    }
5526                                }
5527                            }
5528                        }
5529                        //evaluate math 
5530                        for (i = 0; i < this.currentStageWorkflow().elements().length; i++) {
5531                            if (!indexesToUpdate || indexesToUpdate.indexOf(i) > -1) {
5532                                if (this.currentStageWorkflow().elements()[i].variableValues) {
5533                                    variableValues = this.currentStageWorkflow().elements()[i].variableValues();
5534                                    if (this.currentStageWorkflow().elements()[i].text) {
5535                                        newText = this.updateTextWithValues(this.currentStageWorkflow().elements()[i].text(), variableValues);
5536                                        this.currentStageWorkflow().elements()[i].text(newText);
5537                                    }
5538                                    if (this.currentStageWorkflow().elements()[i].quillDelta) {
5539                                        quillDelta = this.currentStageWorkflow().elements()[i].quillDelta();
5540                                        if (typeof quillDelta !== 'string') {
5541                                            quillDelta = JSON.stringify(quillDelta);
5542                                        }
5543                                        quillDelta = this.updateTextWithValues(quillDelta, variableValues);
5544                                        this.currentStageWorkflow().elements()[i].quillDelta(JSON.parse(quillDelta));
5545                                        //WHAT THE FUCK IS WRONG WITH THIS 
5546                                        this.currentStageWorkflow().elements()[i].quillDelta.valueHasMutated();
5547                                    }
5548                                    if (this.currentStageWorkflow().elements()[i].uiParams && this.currentStageWorkflow().elements()[i].uiParams() && this.currentStageWorkflow().elements()[i].uiParams().length > 0) {
5549                                        for (j = 0; j < this.currentStageWorkflow().elements()[i].uiParams().length; j++) {
5550                                            if (this.currentStageWorkflow().elements()[i].uiParams()[j].hasOwnProperty('customParams') && this.currentStageWorkflow().elements()[i].uiParams()[j].customParams && this.currentStageWorkflow().elements()[i].uiParams()[j].customParams().length > 0) {
5551                                                for (k = 0; k < this.currentStageWorkflow().elements()[i].uiParams()[j].customParams().length; k++) {
5552                                                    paramValue = this.currentStageWorkflow().elements()[i].uiParams()[j].customParams()[k].value(
5552);
5553                                                    newParamValue = this.updateTextWithValues(paramValue, variableValues);
5554                                                    this.currentStageWorkflow().elements()[i].uiParams()[j].customParams()[k].value(newParamValue);
5555                                                }
5556                                            }
5557                                        }
5558                                    }
5559                                    if (this.currentStageWorkflow().elements()[i].question && this.currentStageWorkflow().elements()[i].question()) {
5560                                        question = this.currentStageWorkflow().elements()[i].question();
5561                                        question = this.updateQuestionWithValues(question, variableValues);
5562                                        this.currentStageWorkflow().elements()[i].question(question);
5563                                    }
5564                                    /*  if (i === 0) {
5565                                          console.log("variableValues for element i = ", i, this.currentStageWorkflow().elements()[0].variableValues());
5566                                      } */
5567                                }
5568                            }
5569                        }
5570                        variableValuesToSave = [];
5571                        if (indexesToUpdate && indexesToUpdate.length > 0) {
5572                            _loop_5 = function (i) {
5573                                var variableNames = Object.keys(this_3.currentStageWorkflow().elements()[indexesToUpdate[i]].variableValues());
5574                                var _loop_7 = function (v) {
5575                                    if (!variableValuesToSave.find(function (a) { return a.name === variableNames[v]; })
5576                                        && this_3.currentStageWorkflow().elements()[indexesToUpdate[i]].variableValues()[variableNames[v]] !== startingVariableValues.find(function (a) { return a.name === variableNames[v]; }).value) {
5577                                        variableValuesToSave.push({
5578                                            name: variableNames[v],
5579                                            value: this_3.currentStageWorkflow().elements()[indexesToUpdate[i]].variableValues()[variableNames[v]]
5580                                        });
5581                                    }
5582                                };
5583                                for (var v = 0; v < variableNames.length; v++) {
5584                                    _loop_7(v);
5585                                }
5586                            };
5587                            this_3 = this;
5588                            for (i = 0; i < indexesToUpdate.length; i++) {
5589                                _loop_5(i);
5590                            }
5591                        }
5592                        if (!(variableValuesToSave.length > 0)) return [3 /*break*/, 4];
5593                        return [4 /*yield*/, Distiller.updateUserPathMetadata(variableValuesToSave, 'path')];
5594                    case 3:
5595                        _a.sent();
5596                        _a.label = 4;
5597                    case 4:
5598                        this.settingVariables(false);
5599                        return [2 /*return*/];
5600                }
5601            });
5602        }); };
5603        this.getQuestionsByTabForTabController = function (tabControllerElementIndex) {
5604            var _a, _b;
5605            var elements = (_a = _this.currentStageWorkflow()) === null || _a === void 0 ? void 0 : _a.elements();
5606            if (!elements || tabControllerElementIndex < 0 || tabControllerElementIndex >= elements.length) {
5607                return {};
5608            }
5609            var tabController = elements[tabControllerElementIndex];
5610            if (!(tabController === null || tabController === void 0 ? void 0 : tabController.tabs)) {
5611                return {};
5612            }
5613            var tabIds = tabController.tabs().map(function (a) { return a.tabId; });
5614            var questionsByTab = {};
5615            for (var i = 0; i < tabIds.length; i++) {
5616                questionsByTab[tabIds[i]] = [];
5617            }
5618            for (var i = 0; i < elements.length; i++) {
5619                var element = elements[i];
5620                if (element.type === 'question' && element.tabId && tabIds.indexOf(element.tabId) > -1 && ((_b = element.question) === null || _b === void 0 ? void 0 : _b.call(element))) {
5621                    questionsByTab[element.tabId].push(element.question().questionId());
5622                }
5623            }
5624            return questionsByTab;
5625        };
5626        this.hiddenTabIds = ko.pureComputed(function () {
5627            var _a, _b, _c, _d;
5628            var hiddenTabIds = [];
5629            var _loop_8 = function (i) {
5630                if (this_4.currentStageWorkflow().elements()[i].type === 'tab-controller' || this_4.currentStageWorkflow().elements()[i].type === 'detail-expander') {
5631                    if (((_a = Distiller.showHideIfPayload().find(function (a) { return a.workflowIndex === i && a.action === 'showIf'; })) === null || _a === void 0 ? void 0 : _a.status()) !== undefined
5632                        && ((_b = Distiller.showHideIfPayload().find(function (a) { return a.workflowIndex === i && a.action === 'showIf'; })) === null || _b === void 0 ? void 0 : _b.status()) !== 1) {
5633                        hiddenTabIds.push.apply(hiddenTabIds, this_4.currentStageWorkflow().elements()[i].tabs().map(function (a) { return a.tabId; }));
5634                    }
5635                    if (((_c = Distiller.showHideIfPayload().find(function (a) { return a.workflowIndex === i && a.action === 'hideIf'; })) === null || _c === void 0 ? void 0 : _c.status()) !== undefined
5636                        && ((_d = Distiller.showHideIfPayload().find(function (a) { return a.workflowIndex === i && a.action === 'hideIf'; })) === null || _d === void 0 ? void 0 : _d.status()) === 1) {
5637                        hiddenTabIds.push.apply(hiddenTabIds, this_4.currentStageWorkflow().elements()[i].tabs().map(function (a) { return a.tabId; }));
5638                    }
5639                }
5640            };
5641            var this_4 = this;
5642            for (var i = 0; i < this.currentStageWorkflow().elements().length; i++) {
5643                _loop_8(i);
5644            }
5645            return hiddenTabIds;
5646        }, this);
5647        this.allTabs = ko.pureComputed(function () {
5648            var tabControllers = this.currentStageWorkflow().elements().filter(function (a) { return a.hasOwnProperty('tabs'); });
5649            if (tabControllers.length > 0) {
5650                for (var i = 0; i < tabControllers.length; i++) {
5651                    return tabControllers.map(function (a) { return a.tabs(); }).flat().map(function (a) { return a.tabId; });
5652                }
5653            }
5654            return [];
5655        }, this);
5656        this.isInsideDetailExpander = function (tabId) {
5657            var _a;
5658            if (!tabId) {
5659                return false;
5660            }
5661            var elements = (_a = _this.currentStageWorkflow()) === null || _a === void 0 ? void 0 : _a.elements();
5662            var parent = elements === null || elements === void 0 ? void 0 : elements.find(function (a) { return a.hasOwnProperty('tabs') && a.tabs().find(function (b) { return b.tabId === tabId; }); });
5663            return (parent === null || parent === void 0 ? void 0 : parent.type) === 'detail-expander';
5664        };
5665        this.selectedTabs = ko.pureComputed(function () {
5666            var selectedTabIdsToReturn = [];
5667            var tabControllers = this.currentStageWorkflow().elements().filter(function (a) { return a.hasOwnProperty('tabs'); });
5668            if (tabControllers.length > 0) {
5669                for (var _i = 0, tabControllers_1 = tabControllers; _i < tabControllers_1.length; _i++) {
5670                    var tabController = tabControllers_1[_i];
5671                    //if it DOES NOT have a parent tab controller, or if it DOES have a parent tab controller and it's selected,
5672                    //add its selected tab to the selected tabs
5673                    if (!tabController.tabId || (tabController.tabId && (selectedTabIdsToReturn.indexOf(tabController.tabId) > -1) || this.allTabs().indexOf(tabController.tabId) < 0)) {
5674                        if (tabController.tabs().find(function (b) { return b.isSelected(); })) {
5675                            selectedTabIdsToReturn.push(tabController.tabs().find(function (b) { return b.isSelected(); }).tabId);
5676                        }
5677                    }
5678                }
5679                return selectedTabIdsToReturn;
5680            }
5681            return [];
5682        }, this);
5683        this.showingWorkflowModal = ko.observable(null);
5684        this.referenceValueForModal = ko.observable(null);
5685        this.firstMatrixQuestions = ko.observableArray([]);
5686        this.confirmMatrixQuestion = ko.observable(null);
5687        this.confirmHighEstimate = function () {
5688            /*  for (let i = 0; i < this.firstMatrixQuestions().length; i++) {
5689                  this.firstMatrixQuestions()[i].answers()[1].currentUserForecast(1);
5690              } */
5691            _this.showingWorkflowModal(null);
5692        };
5693        this.reviseHighEstimate = function () {
5694            if (_this.confirmMatrixQuestion()) {
5695                _this.confirmMatrixQuestion().answers()[0].currentUserForecast(0);
5696                _this.showingWorkflowModal(null);
5697            }
5698        };
5699        this.setupBindings = function () {
5700            ko.bindingHandlers.viewerJsSrc = {
5701                update: function (element, valueAccessor) {
5702                    var value = ko.unwrap(valueAccessor()); // Get the current value of the observable
5703                    if (value) {
5704                        element.src = "/ts/Vendors/ViewerJS/#/../../../api/file/" + value;
5705                    }
5706                }
5707            };
5708        };
5709        var self = this;
5710        // STRIPE THEME: remove this classList.add to revert page chrome.
5711        document.body.classList.add('bayesBotStripeTheme');
5712        Distiller.bayesBotViewModel = this;
5713        this.setupBindings();
5714        document.addEventListener('click', function (event) {
5715            if (!_this.bayesBotModelMenuOpen()) {
5716                return;
5717            }
5718            var target = event.target;
5719            if (target && target.closest && target.closest('.bayesBotModelPicker')) {
5720                return;
5721            }
5722            _this.bayesBotModelMenuOpen(false);
5723        });
5724        var repositionModelMenu = function () { return _this.placeBayesBotModelMenu(); };
5725        document.addEventListener('scroll', repositionModelMenu, true);
5726        window.addEventListener('resize', repositionModelMenu);
5727        void this.loadBayesBotUserModel();
5728        this.selectedCanvasNodeId.subscribe(function () { return _this.scheduleLeaderboardScrollToSelection(); });
5729        this.leaderboardPanelTab.subscribe(function () {
5730            if (_this.leaderboardPanelTab() === 'informationGain') {
5731                var self_1 = _this;
5732                setTimeout(function () {
5733                    self_1.renderLifetimeInformationChart();
5734                }, 50);
5735            }
5736        });
5737        this.proposalLifetimeInformation.subscribe(function () {
5738            if (_this.leaderboardPanelTab() === 'informationGain') {
5739                var self_2 = _this;
5740                setTimeout(function () {
5741                    self_2.renderLifetimeInformationChart();
5742                }, 50);
5743            }
5744        });
5745        var preloadBranches = function () {
5746            if (_this.showBranchTab()) {
5747                _this.loadBranchList();
5748            }
5749        };
5750        this.authorOwnsTemplate.subscribe(preloadBranches);
5751        this.isCreateMode.subscribe(preloadBranches);
5752        self.loadingSteps(false);
5753        var urlSid = this.parseSidFromUrl();
5754        var bayesBotPage = !!(Distiller.isBayesBotUrl && Distiller.isBayesBotUrl());
5755        var landingCreate = bayesBotPage && !urlSid;
5756        var draftCreate = !!urlSid && this.isDraftCreateUrl();
5757        // sid on Bayes Bot is not "published." The URL flag is only an
5758        // optimistic create hint. Draft vs live is decided by draft-state:
5759        // this user owns a draft (including still-building) or they do not.
5760        var draftLifecycleCandidate = !!urlSid && (bayesBotPage ||
5761            !!(Distiller.isBayesBotView && Distiller.isBayesBotView()));
5762        this.isCreateMode(landingCreate || draftCreate);
5763        if (landingCreate || draftCreate) {
5764            this.ensureCreateNetWorkbenchHeight();
5765        }
5766        if (urlSid) {
5767            this.boundCommunityId(urlSid);
5768        }
5769        if (landingCreate || draftCreate) {
5770            this.applyHeaderMarkupImmediate(BAYES_BOT_CREATE_HEADER_MARKUP, BAYES_BOT_BAYES_NET_INFO);
5771        }
5772        if (params && params.questionSet) {
5773            this.questionSet(params.questionSet);
5774        }
5775        this.mountCanvasImmediately();
5776        Distiller.showHideIfPayload([]);
5777        if (this.questionSet() && this.questionSet().pages && this.questionSet().pages().length > 0) {
5778            if (this.questionSet().pages()[0].stageWorkflows().length > 0) {
5779                if (this.questionSet().pages()[0].stageWorkflows().find(function (a) { return a.stage() == Distiller.currentUser().stageNumber(); })) {
5780                    //set the current stage workflow = to the stage workflow that matches the current user's stage number
5781                    this.currentStageWorkflow(this.questionSet().pages()[0].stageWorkflows().find(function (a) { return a.stage() == Distiller.currentUser().stageNumber(); }));
5782                }
5783                else if (this.questionSet().pages()[0].stageWorkflows().filter(function (a) { return a.stage() < Distiller.currentUser().stageNumber(); })) {
5784                    //if there's not an exact match, present the last stage workflow that is less than the current user's stage number
5785                    var latestStageWorkflow = this.questionSet().pages()[0].stageWorkflows().filter(function (a) { return a.stage() < Distiller.currentUser().stageNumber(); })[this.questionSet().pages()[0].stageWorkflows().filter(function (a) { return a.stage() < Distiller.currentUser().stageNumber(); }).length - 1];
5786                    this.currentStageWorkflow(latestStageWorkflow);
5787                }
5788                else {
5789                    this.workflowUndefined(true);
5790                }
5791            }
5792            else {
5793                this.workflowUndefined(true);
5794            }
5795        }
5796        if (this.currentStageWorkflow()) {
5797            this.setVariableValues();
5798            if (this.currentStageWorkflow().elements().filter(function (a) { return a.type === 'question'; }).length > 0) {
5799                AutoSave.addAutoSaveNumericQuestions(this.currentStageWorkflow().elements().filter(function (a) { return a.type === 'question' && a.question && a.question(); }));
5800            }
5801            // if menuItemsVisible is false, we need to add a next button if they don't already have one
5802            var existingNextButton = this.currentStageWorkflow().elements().find(function (a) { return a.type === 'button' && a.buttonType() && a.buttonType().value === 'nextPage'; });
5803            if (!existingNextButton && Distiller.currentStageFeatures().leftMenuNav !== 'visible') {
5804                //add this even if there isn't a next set because 
5805                //the next set can get added by a conditional branch 
5806                var newElement = {
5807                    type: 'button',
5808                    view: ko.observable('button-view'),
5809                    text: ko.observable(null),
5810                    isUploadingPresentation: ko.observable(false),
5811                    presentationUploadError: ko.observable(null),
5812                    presentationUrl: ko.observable(null),
5813                    quillDelta: ko.observable(null),
5814                    uiParams: ko.observableArray([]),
5815                    subElements: ko.observableArray([]),
5816                    buttonType: ko.observable({ value: 'nextPage' }),
5817                    buttonHTML: ko.observable('Next page'),
5818                    buttonLink: ko.observable(null),
5819                    buttonParams: ko.observableArray([]),
5820                    isAutoAdded: 1
5821                };
5822                this.currentStageWorkflow().elements.push(newElement);
5823            }
5824            var hasShowIfLogic = this.currentStageWorkflow().elements().some(function (a) { return a.uiParams().some(function (b) { return (b.value === 'showIf' || b.value === 'hideIf') && b.apply(); }); });
5825            if (hasShowIfLogic) {
5826                var showHideIfPayload = [];
5827                var timeSpentThresholds_1 = [];
5828                //build {workflowIndex: index, action: 'showIf'/'hideIf', filtersJson: filtersJson} object
5829                for (var i = 0; i < this.currentStageWorkflow().elements().length; i++) {
5830                    if (this.currentStageWorkflow().elements()[i].uiParams().find(function (a) { return a.value === 'showIf' && a.apply(); })) {
5831                        var validationQueryData = this.getValidationQuery(this.currentStageWorkflow().elements()[i].uiParams().find(function (a) { return a.value === 'showIf'; }).filtersJson());
5832                        var row = {
5833                            questionId: (_a = this.currentStageWorkflow().elements()[i].question()) === null || _a === void 0 ? void 0 : _a.questionId(),
5834                            elementType: this.currentStageWorkflow().elements()[i].type,
5835                            workflowIndex: i,
5836                            action: 'showIf',
5837                            filtersJson: this.currentStageWorkflow().elements()[i].uiParams().find(function (a) { return a.value === 'showIf'; }).filtersJson(),
5838                            status: ko.observable('loading'),
5839                            validationQuery: validationQueryData === null || validationQueryData === void 0 ? void 0 : validationQueryData.query,
5840                            validationQuestionIds: validationQueryData === null || validationQueryData === void 0 ? void 0 : validationQueryData.questionIds,
5841                            scrollTo: this.currentStageWorkflow().elements()[i].uiParams().find(function (a) { return a.value === 'showIf'; }).scrollTo
5842                        };
5843                        if (!row.filtersJson) {
5844                            continue;
5845                        }
5846                        var thresholds = Distiller.extractTimeSpentThresholds(row.filtersJson);
5847                        if (thresholds.length > 0) {
5848                            timeSpentThresholds_1.push.apply(timeSpentThresholds_1, thresholds);
5849                        }
5850                        showHideIfPayload.push(row);
5851                    }
5852                    if (this.currentStageWorkflow().elements()[i].uiParams().find(function (a) { return a.value === 'hideIf' && a.apply(); })) {
5853                        var validationQueryData = this.getValidationQuery(this.currentStageWorkflow().elements()[i].uiParams().find(function (a) { return a.value === 'hideIf'; }).filtersJson());
5854                        var row = {
5855                            questionId: (_b = this.currentStageWorkflow().elements()[i].question()) === null || _b === void 0 ? void 0 : _b.questionId(),
5856                            elementType: this.currentStageWorkflow().elements()[i].type,
5857                            workflowIndex: i,
5858                            action: 'hideIf',
5859                            filtersJson: this.currentStageWorkflow().elements()[i].uiParams().find(function (a) { return a.value === 'hideIf'; }).filtersJson(),
5860                            status: ko.observable('loading'),
5861                            validationQuery: validationQueryData.query,
5862                            validationQuestionIds: validationQueryData.questionIds,
5863                        };
5864                        if (!row.filtersJson) {
5865                            continue;
5866                        }
5867                        var thresholds = Distiller.extractTimeSpentThresholds(row.filtersJson);
5868                        if (thresholds.length > 0) {
5869                            timeSpentThresholds_1.push.apply(timeSpentThresholds_1, thresholds);
5870                        }
5871                        showHideIfPayload.push(row);
5872                    }
5873                }
5874                var uniqueThresholds = timeSpentThresholds_1.filter(function (item, index) {
5875                    return timeSpentThresholds_1.indexOf(item) === index;
5876                });
5877                uniqueThresholds = uniqueThresholds.filter(function (a) { return a >= Distiller.currentUser().totalTimeSpent(); });
5878                Distiller.showHideIfTimeSpentThresholds(uniqueThresholds);
5879                Distiller.showHideIfPayload(showHideIfPayload);
5880                Distiller.evaluateShowHideIf();
5881            }
5882            //pavel modal 
5883            var matrixTableElements_1 = this.currentStageWorkflow().elements().filter(function (a) { return a.hasOwnProperty('generalQuestionType') && a.generalQuestionType() && a.generalQuestionType().value === 'matrixTable'; });
5884            if (hasShowIfLogic
5885                && matrixTableElements_1.length > 1
5886                && matrixTableElements_1[0].uiParams().find(function (a) { return a.type === 'validation' && (a.value === 'orderRightMatrix' /* || a.value === 'orderLeftMatrix'*/); })
5887                && matrixTableElements_1[0].question().answers().length === 2) {
5888                var confirmElement_1 = this.currentStageWorkflow().elements().find(function (a) { return a.generalQuestionType() && a.generalQuestionType().value === 'multipleChoice' && a.uiParams().find(function (b) { return b.value === 'showIf' && b.apply(); }); });
5889                if (confirmElement_1) {
5890                    confirmElement_1.question().answers()[0].currentUserForec
5890ast.subscribe(function (newValue) {
5891                        if (newValue === 1) {
5892                            if (matrixTableElements_1[0].question().answers()[1].currentUserForecast() === 1) {
5893                                var match = matrixTableElements_1[0].question().name().match(/-?\d+(\.\d+)?/);
5894                                var firstNumber = match ? parseFloat(match[0]) : null;
5895                                if (!isNaN(firstNumber)) {
5896                                    _this.referenceValueForModal(firstNumber);
5897                                    var firstMatrixQuestions = [matrixTableElements_1[0].question()];
5898                                    for (var i = 0; i < matrixTableElements_1[0].question().timepointQuestions().length; i++) {
5899                                        firstMatrixQuestions.push(matrixTableElements_1[0].question().timepointQuestions()[i]);
5900                                    }
5901                                    _this.firstMatrixQuestions(firstMatrixQuestions);
5902                                    _this.confirmMatrixQuestion(confirmElement_1.question());
5903                                    _this.showingWorkflowModal('menuElicitation1');
5904                                }
5905                            }
5906                        }
5907                    });
5908                }
5909            }
5910            //   if (hasShowIfLogic && this.currentStageWorkflow().elements().find(a => a.generalQuestionType === 'matrixTable')
5911            /*  let urlParams = new URLSearchParams(window.location.search);
5912              let devIndices: any = urlParams.get('devIndices');
5913              if (devIndices && Distiller.isJsonString(devIndices)) {
5914                  devIndices = JSON.parse(devIndices);
5915                  Distiller.devIndices(devIndices);
5916              } */
5917        }
5918        this.mountCanvasImmediately();
5919        var afterAgentKey = function () {
5920            if (!_this.isCreateMode()) {
5921                _this.loadAuthorBayesNet();
5922                _this.loadLeaderboard();
5923            }
5924            _this.connectBayesBotSse();
5925        };
5926        if (landingCreate) {
5927            if (!Distiller.bayesBotLandingProvisionPromise &&
5928                !Distiller.bayesBotLandingHandoff) {
5929                void this.provisionDraftStudy();
5930            }
5931        }
5932        else if (draftCreate || draftLifecycleCandidate) {
5933            void this.initializeDraftStudy();
5934        }
5935        else {
5936            var timeout = new Promise(function (resolve) {
5937                setTimeout(resolve, 4000);
5938            });
5939            Promise.race([this.ensureAgentKey(), timeout]).then(afterAgentKey).catch(afterAgentKey);
5940        }
5941        var loggedInBody = document.getElementById('loggedInBody2');
5942        if (loggedInBody) {
5943            loggedInBody.scrollTop = 0;
5944        }
5945        window.scrollTo(0, 0);
5946        if (this.currentStageWorkflow()) {
5947        }
5948        //cache whether they're scrolled to bottom here. it should start as false on load of the page. 
5949        if (Distiller.setOfVisibleQuestion() && Distiller.setOfVisibleQuestion().highlightRequired()) {
5950            Distiller.scrolledToBottom.subscribe(function (newValue) {
5951                if (newValue) {
5952                    _this.highlightRequired(true);
5953                }
5954            });
5955        }
5956    }
5957    BayesBot.prototype.escapeRegExp = function (string) {
5958        return string.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
5959    };
5960    BayesBot.prototype.waitForAutosave = function () {
5961        return __awaiter(this, void 0, void 0, function () {
5962            var _this = this;
5963            return __generator(this, function (_a) {
5964                return [2 /*return*/, new Promise(function (resolve, reject) { return __awaiter(_this, void 0, void 0, function () {
5965                        return __generator(this, function (_a) {
5966                            if (!Distiller.autoSaving()) {
5967                                resolve('done');
5968                            }
5969                            else {
5970                                Distiller.autoSaving.subscribe(function (newValue) {
5971                                    if (!newValue) {
5972                                        resolve('done');
5973                                    }
5974                                });
5975                            }
5976                            return [2 /*return*/];
5977                        });
5978                    }); })];
5979            });
5980        });
5981    };
5982    BayesBot.prototype.getQuestionReferenceValues = function (questionReferences) {
5983        return __awaiter(this, void 0, void 0, function () {
5984            var self;
5985            var _this = this;
5986            return __generator(this, function (_a) {
5987                self = this;
5988                return [2 /*return*/, new Promise(function (resolve, reject) { return __awaiter(_this, void 0, void 0, function () {
5989                        return __generator(this, function (_a) {
5990                            switch (_a.label) {
5991                                case 0:
5992                                    if (!Distiller.autoSaving()) return [3 /*break*/, 2];
5993                                    return [4 /*yield*/, self.waitForAutosave()];
5994                                case 1:
5995                                    _a.sent();
5996                                    _a.label = 2;
5997                                case 2:
5998                                    Distiller.httpSendAsync(Distiller.apiEndpoint + "/api/getQuestionReferenceValues/" + Distiller.currentUser().userId, JSON.stringify(questionReferences), function (response) {
5999                                        response = JSON.parse(response);
6000                                        if (response.err) {
6001                                            alert("Oops! Something went wrong when we tried to construct these questions. Please email [email protected] to report this error right away.");
6002                                            resolve(response);
6003                                            return;
6004                                        }
6005                                        resolve(response.data);
6006                                    });
6007                                    return [2 /*return*/];
6008                            }
6009                        });
6010                    }); })];
6011            });
6012        });
6013    };
6014    BayesBot.prototype.extractExpressionSubstrings = function (str) {
6015        var regex = /\$e{[^}]*}/g;
6016        return str.match(regex) || [];
6017    };
6018    BayesBot.prototype.evaluateMathExpression = function (expression) {
6019        // Remove leading "$e{" and trailing "}" if they exist
6020        expression = expression.replace(/^\$e\{|\}$/g, '');
6021        var self = this;
6022        // Dynamically evaluate mathematical functions within an expression
6023        function evaluateFunctions(exp) {
6024            // Function to evaluate arguments before applying the outer function
6025            function evaluateArgsAndApplyFunction(args, applyFunction) {
6026                var argsArray = self.splitArgsConsideringNesting(
6026args);
6027                var evaluatedArgs = argsArray.map(function (arg) { return evaluateFunctions(arg); });
6028                // Apply the function to the evaluated arguments
6029                return applyFunction(evaluatedArgs).toString();
6030            }
6031            // Iteratively apply patterns to replace mathematical expressions
6032            var modifiedExp = exp, matched;
6033            do {
6034                matched = false;
6035                var hasFunctionsOrCommas = /max|min|sqrt|abs|round|,/.test(modifiedExp);
6036                if (!hasFunctionsOrCommas) {
6037                    matched = true;
6038                    try {
6039                        //fix for Bridget june 6 2025 
6040                        if (isOnlyMultiplication(modifiedExp)) {
6041                            var numberOfDecimalPlaces = sumDecimalPlaces(modifiedExp);
6042                            var result = eval(modifiedExp);
6043                            return parseFloat(result.toFixed(numberOfDecimalPlaces));
6044                        }
6045                        return eval(modifiedExp).toString();
6046                    }
6047                    catch (error) {
6048                        return 'Error: ' + error.message;
6049                    }
6050                }
6051                // Handle max() with proper nested parentheses support
6052                while (modifiedExp.includes('max(')) {
6053                    var maxStart = modifiedExp.indexOf('max(');
6054                    if (maxStart === -1)
6055                        break;
6056                    var depth = 1;
6057                    var i = maxStart + 4; // Start after 'max('
6058                    var args = '';
6059                    while (depth > 0 && i < modifiedExp.length) {
6060                        var char = modifiedExp[i];
6061                        if (char === '(')
6062                            depth++;
6063                        else if (char === ')')
6064                            depth--;
6065                        if (depth > 0)
6066                            args += char;
6067                        i++;
6068                    }
6069                    var fullMatch = 'max(' + args + ')';
6070                    matched = true;
6071                    var replacement = evaluateArgsAndApplyFunction(args, function (evaluatedArgs) { return Math.max.apply(Math, evaluatedArgs.map(parseFloat)); });
6072                    // Replace only the first occurrence to avoid infinite loops
6073                    modifiedExp = modifiedExp.substring(0, maxStart) + replacement + modifiedExp.substring(maxStart + fullMatch.length);
6074                }
6075                // Handle min() with proper nested parentheses support
6076                while (modifiedExp.includes('min(')) {
6077                    var minStart = modifiedExp.indexOf('min(');
6078                    if (minStart === -1)
6079                        break;
6080                    var depth = 1;
6081                    var i = minStart + 4; // Start after 'min('
6082                    var args = '';
6083                    while (depth > 0 && i < modifiedExp.length) {
6084                        var char = modifiedExp[i];
6085                        if (char === '(')
6086                            depth++;
6087                        else if (char === ')')
6088                            depth--;
6089                        if (depth > 0)
6090                            args += char;
6091                        i++;
6092                    }
6093                    var fullMatch = 'min(' + args + ')';
6094                    matched = true;
6095                    var replacement = evaluateArgsAndApplyFunction(args, function (evaluatedArgs) { return Math.min.apply(Math, evaluatedArgs.map(parseFloat)); });
6096                    // Replace only the first occurrence to avoid infinite loops
6097                    modifiedExp = modifiedExp.substring(0, minStart) + replacement + modifiedExp.substring(minStart + fullMatch.length);
6098                }
6099                // Handle sqrt() with proper nested parentheses support
6100                while (modifiedExp.includes('sqrt(')) {
6101                    var sqrtStart = modifiedExp.indexOf('sqrt(');
6102                    if (sqrtStart === -1)
6103                        break;
6104                    var depth = 1;
6105                    var i = sqrtStart + 5; // Start after 'sqrt('
6106                    var args = '';
6107                    while (depth > 0 && i < modifiedExp.length) {
6108                        var char = modifiedExp[i];
6109                        if (char === '(')
6110                            depth++;
6111                        else if (char === ')')
6112                            depth--;
6113                        if (depth > 0)
6114                            args += char;
6115                        i++;
6116                    }
6117                    var fullMatch = 'sqrt(' + args + ')';
6118                    matched = true;
6119                    var replacement = evaluateArgsAndApplyFunction(args, function (evaluatedArgs) { return Math.sqrt(evaluatedArgs[0]); });
6120                    // Replace only the first occurrence to avoid infinite loops
6121                    modifiedExp = modifiedExp.substring(0, sqrtStart) + replacement + modifiedExp.substring(sqrtStart + fullMatch.length);
6122                }
6123                // Handle abs() with proper nested parentheses support
6124                while (modifiedExp.includes('abs(')) {
6125                    var absStart = modifiedExp.indexOf('abs(');
6126                    if (absStart === -1)
6127                        break;
6128                    var depth = 1;
6129                    var i = absStart + 4; // Start after 'abs('
6130                    var args = '';
6131                    while (depth > 0 && i < modifiedExp.length) {
6132                        var char = modifiedExp[i];
6133                        if (char === '(')
6134                            depth++;
6135                        else if (char === ')')
6136                            depth--;
6137                        if (depth > 0)
6138                            args += char;
6139                        i++;
6140                    }
6141                    var fullMatch = 'abs(' + args + ')';
6142                    matched = true;
6143                    var replacement = evaluateArgsAndApplyFunction(args, function (evaluatedArgs) { return Math.abs(evaluatedArgs[0]); });
6144                    // Replace only the first occurrence to avoid infinite loops
6145                    modifiedExp = modifiedExp.substring(0, absStart) + replacement + modifiedExp.substring(absStart + fullMatch.length);
6146                }
6147                // Handle round() with proper nested parentheses support
6148                while (modifiedExp.includes('round(')) {
6149                    var roundStart = modifiedExp.indexOf('round(');
6150                    if (roundStart === -1)
6151                        break;
6152                    var depth = 1;
6153                    var i = roundStart + 6; // Start after 'round('
6154                    var args = '';
6155                    while (depth > 0 && i < modifiedExp.length) {
6156                        var char = modifiedExp[i];
6157                        if (char === '(')
6158                            depth++;
6159                        else if (char === ')')
6160                            depth--;
6161                        if (depth > 0)
6162                            args += char;
6163                        i++;
6164                    }
6165                    var fullMatch = 'round(' + args + ')';
6166                    matched = true;
6167                    // Split the arguments considering nesting, and then evaluate them
6168                    var arrayOfArgs = self.splitArgsConsideringNesting(args).map(function (arg) { return evaluateFunctions(arg); });
6169                    var replacement = void 0;
6170                    if (arrayOfArgs.length > 1) {
6171                        // Now, correctly parse the number to round and the decimal places
6172                        var numToRound = parseFloat(arrayOfArgs[0]);
6173                        var decimalPlaces = parseInt(arrayOfArgs[1]);
6174                        var roundedValue = Number(numToRound.toFixed(decimalPlaces));
6175                        replacement = roundedValue.toString();
6176                    }
6177                    else {
6178                        // If there's only one argument, round it to the nearest integer
6179                        var numToRound = parseFloat(arrayOfArgs[0]);
6180                        replacement = Math.round(numToRound).toString();
6181                    }
6182                    // Replace only the first occurrence to avoid infinite loops
6183                    modifiedExp = modifiedExp.substring(0, roundStart) + replacement + modifiedExp.substring(roundStart + fullMatch.length);
6184                }
6185            } while (matched);
6186            return modifiedExp;
6187        }
6188        // Replace "^" with "**" for exponentiation
6189        expression = expression.replace(/\^/g, '**');
6190        // Dynamically evaluate functions within the expression
6191        expression = evaluateFunctions(expression);
6192        function isOnlyMultiplication(expr) {
6193            // Must only contain numbers, decimal points, whitespace, and single * operators
6194            // Disallow **, /, +, -, %, etc.
6195            var invalidOperators = /[+\-/()%]|[*]{2,}/;
6196            var validCharacters = /^[\d.\s*]+$/;
6197            return validCharacters.test(expr) && !invalidOperators.test(expr);
6198        }
6199        function sumDecimalPlaces(expr) {
6200            // Match all numbers, including those like .5, 0.123, 5., 10.0, etc.
6201            var numberPattern = /(?:\d*\.\d+|\d+\.)/g;
6202            var totalDecimals = 0;
6203            var matches = expr.match(numberPattern);
6204            if (!matches)
6205                return 0;
6206            for (var _i = 0, matches_1 = matches; _i < matches_1.length; _i++) {
6207                var numStr = matches_1[_i];
6208                var decimalPart = numStr.split('.')[1];
6209                if (decimalPart) {
6210                    totalDecimals += decimalPart.length;
6211                }
6212            }
6213            return totalDecimals;
6214        }
6215        try {
6216            // Use eval to evaluate the simplified expression
6217            var result = eval(expression);
6218            // Check if the result is a finite number
6219            if (typeof result === 'number' && isFinite(result)) {
6220                return result;
6221            }
6222            else {
6223                throw new Error('Invalid result');
6224            }
6225        }
6226        catch (error) {
6227            return 'Error: ' + error.message;
6228        }
6229    };
6230    BayesBot.prototype.splitArgsConsideringNesting = function (args) {
6231        var argsArray = [];
6232        var currentArg = '';
6233        var depth = 0; // Depth of nested parentheses
6234        for (var i = 0; i < args.length; i++) {
6235            var char = args[i];
6236            if (char === '(')
6237                depth++;
6238            if (char === ')')
6239                depth--;
6240            // If we encounter a comma and are not inside nested parentheses, split here
6241            if (char === ',' && depth === 0) {
6242                argsArray.push(currentArg.trim());
6243                currentArg = '';
6244            }
6245            else {
6246                currentArg += char;
6247            }
6248        }
6249        // Add the last argument (or only argument if no commas were found) to the array
6250        if (currentArg !== '')
6251            argsArray.push(currentArg.trim());
6252        return argsArray;
6253    };
6254    BayesBot.prototype.getValidationQuery = function (query) {
6255        var response = {
6256            query: undefined,
6257            questionIds: []
6258        };
6259        if (!query) {
6260            return undefined;
6261        }
6262        for (var i = 0; i < query.rules.length; i++) {
6263            // console.log("rule "  + JSON.stringify(rule));
6264            if (query.rules[i].id === 'submission' && query.rules[i].value && (query.rules[i].value.includes('_valid') || query.rules[i].value.includes('_invalid'))) {
6265                var params = [];
6266                if (Distiller.isJsonString(query.rules[i].value) && Array.isArray(JSON.parse(query.rules[i].value))) {
6267                    params = JSON.parse(query.rules[i].value);
6268                }
6269                else {
6270                    params = query.rules[i].value.split('.');
6271                }
6272                var evaluatingFor = 'valid_submission';
6273                if (query.rules[i].value.includes('_invalid')) {
6274                    evaluatingFor = 'invalid_submission';
6275                }
6276                if (params.length > 1) {
6277                    var newQuestionId = parseInt(params[1]);
6278                    if (response.questionIds.indexOf(newQuestionId) < 0) {
6279                        response.questionIds.push(newQuestionId);
6280                    }
6281                    query.rules[i].isValid = ko.observable(-1); //default to incomplete 
6282                    query.rules[i].evaluatingFor = evaluatingFor;
6283                }
6284            }
6285            else if (query.rules[i].rules && Array.isArray(query.rules[i].rules)) {
6286                var validationSubqueryData = this.getValidationQuery(query.rules[i]);
6287                for (var q = 0; q < validationSubqueryData.questionIds.length; q++) {
6288                    if (response.questionIds.indexOf(validationSubqueryData.questionIds[q]) < 0) {
6289                        response.questionIds.push(validationSubqueryData.questionIds[q]);
6290                    }
6291                }
6292                query.rules[i] = validationSubqueryData.query;
6293            }
6294        }
6295        response.query = query;
6296        return response;
6297    };
6298    BayesBot.LIFETIME_CHART_MIN_HEIGHT_PX = 200;
6299    BayesBot.BRANCH_ROW_HEIGHT_PX = 39;
6300    BayesBot.AGENT_ACTIVITY_SPINNER_FRAMES = ['⣾', '⣽', '⣻', '⢿', '⡿', '⣟', '⣯', '⣷'];
6301    BayesBot.BRAILLE_CELL_COUNT = 9;
6302    BayesBot.isLocalhostDev = function () {
6303        var href = String((typeof window !== 'undefined' && window.location && window.location.href) || '');
6304        var host = String((typeof window !== 'undefined' && window.location && window.location.hostname) || '').toLowerCase();
6305        return (host === 'localhost' ||
6306            host === '127.0.0.1' ||
6307            host.indexOf('localhost') >= 0 ||
6308            href.indexOf('localhost') > -1 ||
6309            href.indexOf('127.0.0.1') > -1);
6310    };
6311    return BayesBot;
6312}());
6313export default BayesBot;

Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.