Private
Public Access
209 lines
8.6 KiB
JavaScript
209 lines
8.6 KiB
JavaScript
'use strict';
|
|
var WizardPhase = {
|
|
STARTING: 'starting',
|
|
WAITING: 'waiting',
|
|
MEASURING: 'measuring',
|
|
VALIDATING: 'validating',
|
|
COMMITTING: 'committing'
|
|
};
|
|
var wizard = null;
|
|
var video = document.getElementById('calibrationVideo');
|
|
|
|
function wizardFinish(error) {
|
|
if (!wizard) return;
|
|
clearInterval(wizard.timer);
|
|
wizard = null;
|
|
window.remoteNavigationScope = null;
|
|
video.pause();
|
|
video.style.display = 'none';
|
|
if (typeof video.removeAttribute === 'function') {
|
|
video.removeAttribute('src');
|
|
video.load();
|
|
}
|
|
document.getElementById('cancelCalibration').disabled = true;
|
|
document.getElementById('calibrate').disabled = false;
|
|
document.getElementById('validateCalibration').disabled = false;
|
|
call('calibrationSession', function(r) {
|
|
if (r.returnValue === false) document.getElementById('error').textContent = r.errorText;
|
|
else call('status', display);
|
|
}, {
|
|
enabled: false
|
|
});
|
|
call('calibrationResult', function() {}, {
|
|
message: error || document.getElementById('wizardStatus').textContent
|
|
});
|
|
if (error) document.getElementById('wizardStatus').textContent = error;
|
|
document.getElementById('calibrate').focus();
|
|
}
|
|
|
|
function wizardSample() {
|
|
if (!wizard || wizard.busy) return;
|
|
var session = wizard;
|
|
if (Date.now() - wizard.started > 100000) {
|
|
wizardFinish('Calibration timed out. Saved profile unchanged.');
|
|
return;
|
|
}
|
|
if (wizard.phase !== WizardPhase.MEASURING) return;
|
|
var t = video.currentTime;
|
|
if (t >= 68) {
|
|
try {
|
|
var measured = calibrationFit(calibrationAverage(wizard.baseline), wizard.patches.map(calibrationAverage), wizard.model);
|
|
profile = 'custom';
|
|
profileDirty = true;
|
|
showProfile();
|
|
document.getElementById('customProfile').value = JSON.stringify(measured, null, 2);
|
|
document.getElementById('wizardStatus').textContent = 'Measured ' + measured.points.length + ' edge regions. Select Save profile, then Validate profile to check all four colored edges. The previous saved profile is still active.';
|
|
wizard.busy = true;
|
|
wizard.phase = WizardPhase.COMMITTING;
|
|
call('calibrationDraft', function(r) {
|
|
if (wizard === session) wizardFinish(r.returnValue === false ? r.errorText : undefined);
|
|
}, {
|
|
profile: 'custom',
|
|
model: measured.model,
|
|
points: measured.points
|
|
});
|
|
} catch (e) {
|
|
wizardFinish(e.message);
|
|
}
|
|
return;
|
|
}
|
|
var step = t < 4 ? -1 : Math.floor((t - 4) / 2),
|
|
local = t < 4 ? t : (t - 4) % 2;
|
|
if (step < 0 && (t < .8 || t > 3.2) || step >= 0 && (local < .65 || local > 1.75)) return;
|
|
wizard.busy = true;
|
|
call('status', function(s) {
|
|
if (wizard !== session) return;
|
|
wizard.busy = false;
|
|
if (s.returnValue === false || !s.calibrating) {
|
|
wizardFinish(s.errorText || 'Calibration session ended. Saved profile unchanged.');
|
|
return;
|
|
}
|
|
// Ignore duplicates, held output and replies crossing a stimulus boundary.
|
|
var current = video.currentTime,
|
|
nowStep = current < 4 ? -1 : Math.floor((current - 4) / 2);
|
|
if (nowStep !== step || s.sampleSequence === wizard.sequence || s.sampleAgeMs < 0 || s.sampleAgeMs > 350 || !s.colorValid || !Array.isArray(s.regions) || s.regions.length !== 16) return;
|
|
wizard.sequence = s.sampleSequence;
|
|
(step < 0 ? wizard.baseline : wizard.patches[step]).push(calibrationLuma(s.regions));
|
|
document.getElementById('wizardStatus').textContent = step < 0 ? 'Measuring black reference...' : 'Measuring edge patch ' + (step + 1) + ' of 32...';
|
|
});
|
|
}
|
|
|
|
function wizardStart(validation) {
|
|
if (wizard) return;
|
|
if (typeof showPanel === 'function') showPanel('calibration', false);
|
|
if (targetDirty || profileDirty) {
|
|
document.getElementById('wizardStatus').textContent = 'Save receiver and profile edits before starting.';
|
|
return;
|
|
}
|
|
document.getElementById('calibrate').disabled = true;
|
|
document.getElementById('validateCalibration').disabled = true;
|
|
document.getElementById('cancelCalibration').disabled = false;
|
|
window.remoteNavigationScope = ['cancelCalibration'];
|
|
wizard = {
|
|
phase: WizardPhase.STARTING,
|
|
validation: validation,
|
|
started: Date.now(),
|
|
busy: false,
|
|
sequence: -1,
|
|
model: '',
|
|
baseline: [],
|
|
patches: Array.from({
|
|
length: 32
|
|
}, function() {
|
|
return [];
|
|
})
|
|
};
|
|
var session = wizard;
|
|
document.getElementById('wizardStatus').textContent = 'Starting full-screen capture. Keep the app visible; do not change zoom or aspect ratio.';
|
|
call('calibrationSession', function(r) {
|
|
if (wizard !== session) return;
|
|
if (r.returnValue === false) {
|
|
wizardFinish(r.errorText);
|
|
return;
|
|
}
|
|
// Start the video plane before waiting for histogram data. Without another
|
|
// running video, waiting first would prevent calibration from ever starting.
|
|
video.src = 'calibration.mp4';
|
|
if (typeof video.load === 'function') video.load();
|
|
video.style.display = 'block';
|
|
var warmup = video.play();
|
|
if (warmup && warmup.catch) warmup.catch(function() {
|
|
if (wizard !== session) return;
|
|
wizardFinish('The TV could not play the calibration video. Saved profile unchanged.');
|
|
});
|
|
wizard.phase = WizardPhase.WAITING;
|
|
call('start', function() {
|
|
if (wizard === session) waitCapture();
|
|
});
|
|
}, {
|
|
enabled: true
|
|
});
|
|
|
|
function waitCapture() {
|
|
if (wizard !== session) return;
|
|
call('status', function(s) {
|
|
if (wizard !== session) return;
|
|
if (s.running && s.activeSource !== 'PicCap' && s.tvModel && s.colorValid) {
|
|
wizard.phase = validation ? WizardPhase.VALIDATING : WizardPhase.MEASURING;
|
|
wizard.model = s.tvModel;
|
|
video.currentTime = validation ? 68 : 0;
|
|
video.style.display = 'block';
|
|
document.getElementById('cancelCalibration').focus();
|
|
var playing = video.play();
|
|
if (playing && playing.catch) playing.catch(function() {
|
|
if (wizard !== session) return;
|
|
wizardFinish('The TV could not play the calibration video. Saved profile unchanged.');
|
|
});
|
|
wizard.timer = setInterval(wizardSample, 250);
|
|
} else if (Date.now() - wizard.started < 15000) setTimeout(waitCapture, 500);
|
|
else wizardFinish('No fresh full-screen histogram capture or TV model. Saved profile unchanged.');
|
|
});
|
|
}
|
|
}
|
|
document.getElementById('calibrate').onclick = function() {
|
|
wizardStart(false);
|
|
};
|
|
document.getElementById('validateCalibration').onclick = function() {
|
|
wizardStart(true);
|
|
};
|
|
document.getElementById('cancelCalibration').onclick = function() {
|
|
wizardFinish('Calibration cancelled. Saved profile unchanged.');
|
|
};
|
|
video.addEventListener('ended', function() {
|
|
if (wizard && wizard.validation) wizardFinish('Validation finished. Top-left red, top-right green, bottom-right blue, bottom-left yellow. Check matching LED positions.');
|
|
else wizardSample();
|
|
});
|
|
video.addEventListener('error', function() {
|
|
wizardFinish('Calibration video playback failed. Saved profile unchanged.');
|
|
});
|
|
document.addEventListener('keydown', function(e) {
|
|
if (wizard && (e.keyCode === 461 || e.key === 'Escape')) {
|
|
e.preventDefault();
|
|
wizardFinish('Calibration cancelled. Saved profile unchanged.');
|
|
}
|
|
});
|
|
window.addEventListener('pagehide', function() {
|
|
wizardFinish();
|
|
});
|
|
// An explicit launch action also supports starting the TV's guided test remotely.
|
|
function wizardLaunch(raw) {
|
|
try {
|
|
var params = typeof raw === 'string' ? JSON.parse(raw) : raw;
|
|
if (params && params.action === 'calibrate') wizardStart(false);
|
|
else if (params && params.action === 'repairRoot') {
|
|
showPanel('overview');
|
|
repairRoot(function(ok) {
|
|
if (ok) call('status', display);
|
|
});
|
|
}
|
|
} catch (e) {
|
|
document.getElementById('wizardStatus').textContent = 'Invalid launch parameters.';
|
|
}
|
|
}
|
|
if (typeof PalmSystem !== 'undefined' && PalmSystem.launchParams) setTimeout(function() {
|
|
wizardLaunch(PalmSystem.launchParams);
|
|
}, 1000);
|
|
window.addEventListener('webOSRelaunch', function() {
|
|
if (typeof PalmSystem !== 'undefined') wizardLaunch(PalmSystem.launchParams);
|
|
});
|