diff --git a/background.js b/background.js index 0144aa9..ccecec9 100644 --- a/background.js +++ b/background.js @@ -501,9 +501,6 @@ function checkTab(id, isBeforeNav, isRepeat) { // Get current time/date let timedate = new Date(now * 1000); - // Get override end time - let overrideEndTime = gOptions["oret"]; - gTabs[id].secsLeft = Infinity; gTabs[id].secsLeftSet = 0; gTabs[id].showTimer = false; @@ -645,9 +642,12 @@ function checkTab(id, isBeforeNav, isRepeat) { // Check lockdown condition let lockdown = (timedata[4] > now); - // Check override condition + // Check override condition (per-set override stored in timedata[8]) + // Note: allowOverride is checked at activation time, not here, so active overrides + // persist until end time even if allowOverride is later disabled (matching lockdown behavior) + let overrideEndTime = timedata[8]; let override = (prevOverride || !isInternalPage) && (overrideEndTime > now) - && allowOverride && (allowOverLock || !lockdown); + && (allowOverLock || !lockdown); // Determine whether this page should now be blocked let doBlock = lockdown @@ -1055,14 +1055,21 @@ function updateIcon() { // Get current time in seconds let now = Math.floor(Date.now() / 1000) + (gClockOffset * 60); - // Get override end time - let overrideEndTime = gOptions["oret"]; + // Check if any block set has active override (per-set override stored in timedata[8]) + let anyOverrideActive = false; + for (let set = 1; set <= gNumSets; set++) { + let timedata = gOptions[`timedata${set}`]; + if (timedata && timedata[8] > now) { + anyOverrideActive = true; + break; + } + } // Change icon only if override status has changed - if (!gOverrideIcon && overrideEndTime > now) { + if (!gOverrideIcon && anyOverrideActive) { browser.action.setIcon({ path: OVERRIDE_ICON }); gOverrideIcon = true; - } else if (gOverrideIcon && overrideEndTime <= now) { + } else if (gOverrideIcon && !anyOverrideActive) { browser.action.setIcon({ path: DEFAULT_ICON }); gOverrideIcon = false; } @@ -1340,19 +1347,25 @@ function cancelLockdown(set) { saveTimeData(); } -// Apply override +// Apply override for specified sets // -function applyOverride(endTime) { - //log("applyOverride: " + endTime); +function applyOverride(sets, endTime) { + //log("applyOverride: " + sets + " " + endTime); - if (!gGotOptions) { + if (!gGotOptions || !sets || sets.length == 0) { return; } let options = {}; - // Set override end time - options["oret"] = gOptions["oret"] = endTime; + // Apply override to each set (only if it doesn't reduce any current override) + for (let set of sets) { + if (set >= 1 && set <= gNumSets) { + if (endTime > gOptions[`timedata${set}`][8]) { + gOptions[`timedata${set}`][8] = endTime; + } + } + } if (endTime) { // Get current time in seconds @@ -1386,6 +1399,22 @@ function applyOverride(endTime) { function (error) { warn("Cannot set options: " + error); } ); + saveTimeData(); + updateIcon(); +} + +// Cancel override for specified set +// +function cancelOverride(set) { + //log("cancelOverride: " + set); + + if (!gGotOptions || set < 1 || set > gNumSets) { + return; + } + + gOptions[`timedata${set}`][8] = 0; + + saveTimeData(); updateIcon(); } @@ -1629,7 +1658,10 @@ function handleCommand(command) { break; case "lb-cancel-override": - applyOverride(0); + // Cancel override for all sets + for (let set = 1; set <= gNumSets; set++) { + cancelOverride(set); + } break; case "lb-add-sites": @@ -1716,7 +1748,15 @@ function handleMessage(message, sender, sendResponse) { case "override": // Override requested - applyOverride(message.endTime); + if (!message.endTime) { + // Override canceled - cancel all overrides + for (let set = 1; set <= gNumSets; set++) { + cancelOverride(set); + } + } else { + // Override requested for specified sets + applyOverride(message.sets, message.endTime); + } break; case "password": diff --git a/common.js b/common.js index 667a412..fc082ed 100644 --- a/common.js +++ b/common.js @@ -106,6 +106,8 @@ const GENERAL_OPTIONS = { orlps: { type: "number", def: 0, id: null }, // default: no override limit period start time orlc: { type: "number", def: 0, id: null }, // default: no override limit count oret: { type: "number", def: 0, id: null }, // default: no override end time + orHours: { type: "string", def: "", id: null }, // default: blank (for UI state persistence) + orMins: { type: "string", def: "", id: null }, // default: blank (for UI state persistence) warnSecs: { type: "string", def: "", id: "warnSecs" }, // default: no warning warnImmediate: { type: "boolean", def: true, id: "warnImmediate" }, // default: warn only for immediate block contextMenu: { type: "boolean", def: true, id: "contextMenu" }, // default: enabled @@ -181,6 +183,7 @@ function cleanOptions(options) { // timedata[5] = rollover time for current period (secs) // timedata[6] = rollover time for next period (secs) // timedata[7] = start time for next rollover period (secs since epoch) +// timedata[8] = end time for override (secs since epoch) // function cleanTimeData(options) { let numSets = +options["numSets"]; @@ -189,8 +192,8 @@ function cleanTimeData(options) { for (let set = 1; set <= numSets; set++) { let timedata = options[`timedata${set}`]; if (!Array.isArray(timedata)) { - timedata = [now, 0, 0, 0, 0, 0, 0, 0]; - } else while (timedata.length < 8) { + timedata = [now, 0, 0, 0, 0, 0, 0, 0, 0]; + } else while (timedata.length < 9) { timedata.push(0); } options[`timedata${set}`] = timedata; diff --git a/override.html b/override.html index 15b72ab..3b7f91f 100644 --- a/override.html +++ b/override.html @@ -23,7 +23,48 @@
LeechBlock Override

- Allow blocking to be suspended for minute(s) +
+

+ + + + +      + + + + +
+

+
+

+
+

+

+ + +

+

@@ -46,10 +87,16 @@

Please enter a non-zero duration for the override.

+
+

Please select the sites to override.

+
+
+

No block sets have "Allow temporary override" enabled.

+

You can enable this in Options for each block set.

+

Override activated. Blocking will be suspended until .

-

Note: Override will have no effect because you have not selected
"Allow temporary override for these sites" for any block sets.

- +

Override will be applied to sites in:

diff --git a/override.js b/override.js index e7046dc..d20d925 100644 --- a/override.js +++ b/override.js @@ -13,6 +13,9 @@ function warn(message) { console.warn("[LBNG] " + message); } function getElement(id) { return document.getElementById(id); } +var gStorage = browser.storage.local; + +var gFormHTML; var gAccessConfirmed = false; var gAccessHashCode; var gClockOffset; @@ -21,13 +24,36 @@ var gOverrideMins; var gOverrideLimit = false; var gOverrideLimitPeriod; var gOverrideLimitLeft; -var gOverrideSetNames = []; +var gEligibleSets = []; var gClockTimeOpts; -// Initialize form +// Initialize form (with specified eligible block sets) // -function initForm() { - //log("initForm"); +function initForm(eligibleSets) { + //log("initForm: " + eligibleSets); + + // Reset form to original HTML + $("#form").html(gFormHTML); + + gEligibleSets = eligibleSets; + + // Clear the blockSets container and add checkboxes only for eligible sets + $("#blockSets").empty(); + + // Get HTML template for first checkbox + let blockSetHTML = `

+ + +

`; + + // Create checkboxes only for eligible sets + for (let i = 0; i < eligibleSets.length; i++) { + let set = eligibleSets[i]; + let setHTML = blockSetHTML + .replace(/(Block Set) 1/g, `$1 ${set}`) + .replace(/(id|for)="(\w+)1"/g, `$1="$2${set}"`); + $("#blockSets").append(setHTML); + } // Set up JQuery UI widgets $("#activate").button(); @@ -44,19 +70,16 @@ function initializePage() { browser.storage.local.get("sync").then(onGotSync, onError); function onGotSync(options) { - if (options["sync"]) { - browser.storage.sync.get().then(onGot, onError); - } else { - browser.storage.local.get().then(onGot, onError); - } + gStorage = options["sync"] + ? browser.storage.sync + : browser.storage.local; + + gStorage.get().then(onGot, onError); } function onGot(options) { cleanOptions(options); - // Initialize form - initForm(); - setTheme(options["theme"]); // Get clock time format @@ -92,16 +115,46 @@ function initializePage() { } } - // Get list of sets to override + // Get list of eligible sets (those with allowOverride enabled) let numSets = +options["numSets"]; + let eligibleSets = []; for (let set = 1; set <= numSets; set++) { if (options[`allowOverride${set}`]) { - let setName = options[`setName${set}`]; - if (setName) { - gOverrideSetNames.push(`Block Set ${set} (${setName})`); - } else { - gOverrideSetNames.push(`Block Set ${set}`); - } + eligibleSets.push(set); + } + } + + // Check if any sets are eligible + if (eligibleSets.length == 0) { + $("#alertNoEligibleSets").dialog("open"); + return; + } + + // Initialize form with eligible sets + initForm(eligibleSets); + + // Restore previous hours/mins from storage + let orHours = options["orHours"]; + if (orHours > 0) { + getElement("hours").value = orHours; + } + + let orMins = options["orMins"]; + if (orMins > 0) { + getElement("mins").value = orMins; + } + + // Restore previously selected sets from storage + for (let set of eligibleSets) { + let override = options[`override${set}`]; + if (override) { + getElement(`blockSet${set}`).checked = override; + } + + // Append custom set name to check box label (if specified) + let setName = options[`setName${set}`]; + if (setName) { + getElement(`blockSetLabel${set}`).innerText += ` (${setName})`; } } @@ -121,10 +174,10 @@ function closePage() { browser.runtime.sendMessage({ type: "close" }); } -// Set focus to minutes input field +// Set focus to hours input field // -function focusMins() { - $("#mins").focus(); +function focusHours() { + $("#hours").focus(); } // Confirm access to override @@ -174,11 +227,13 @@ function confirmAccess(options) { $("#promptAccessCodeInput").focus(); } else if (gOverrideMins) { // Override duration already specified in General options - activateOverride(); + // But still need to show form for set selection + $("#form").show(); + setTimeout(focusHours, 100); } else { // Override duration not specified in General options $("#form").show(); - setTimeout(focusMins, 100); + setTimeout(focusHours, 100); } } @@ -250,36 +305,71 @@ function resizePromptInputHeight(numLines) { // Activate override // function activateOverride() { - // Get duration from form if not already specified - if (!gOverrideMins) { - gOverrideMins = $("#mins").val(); - if (!gOverrideMins || !checkPosIntFormat(gOverrideMins)) { - gOverrideMins = ""; - $("#mins").val(""); - $("#alertNoDuration").dialog("open"); - return; - } + //log("activateOverride"); + + // Get duration from form + let hours = getElement("hours").value; + let mins = getElement("mins").value; + let duration; + + if (gOverrideMins) { + // Override duration specified in General options + duration = gOverrideMins * 60; + } else { + // Calculate duration from hours + mins + duration = hours * 3600 + mins * 60; + } + + if (!duration || duration < 0) { + $("#alertNoDuration").dialog("open"); + return; } // Calculate end time for override - let endTime = Math.floor(Date.now() / 1000) + (gClockOffset * 60) + (gOverrideMins * 60); + let endTime = Math.floor(Date.now() / 1000) + (gClockOffset * 60) + duration; + + // Collect selected sets + let selectedSets = []; + let selectedSetNames = []; + for (let set of gEligibleSets) { + let selected = getElement(`blockSet${set}`).checked; + if (selected) { + selectedSets.push(set); + // Build list of selected set names for confirmation + let label = getElement(`blockSetLabel${set}`).innerText; + selectedSetNames.push(label); + } + } - // Request override + if (selectedSets.length == 0) { + $("#alertNoSets").dialog("open"); + return; + } + + // Request override for selected sets (single message) let message = { type: "override", - endTime: endTime + endTime: endTime, + sets: selectedSets }; browser.runtime.sendMessage(message); + // Save options for next time + let options = {}; + options["orHours"] = hours; + options["orMins"] = mins; + for (let set of gEligibleSets) { + options[`override${set}`] = getElement(`blockSet${set}`).checked; + } + gStorage.set(options).catch( + function (error) { warn("Cannot set options: " + error); } + ); + if (gOverrideConfirm) { // Show confirmation dialog - endTime = new Date(endTime * 1000); - $("#alertOverrideEndTime").html(endTime.toLocaleTimeString(undefined, gClockTimeOpts)); - if (gOverrideSetNames.length > 0) { - $("#alertOverrideNoSets").hide(); - $("#alertOverrideSets").show(); - $("#alertOverrideSetList").html(""); - } + let endTimeDate = new Date(endTime * 1000); + $("#alertOverrideEndTime").html(endTimeDate.toLocaleTimeString(undefined, gClockTimeOpts)); + $("#alertOverrideSetList").html(""); if (gOverrideLimit) { $("#alertOverrideLimit").show(); $("#alertLimitLeft").html(gOverrideLimitLeft - 1); @@ -301,13 +391,8 @@ function initAccessControlPrompt(prompt) { let input = $(`#${prompt}Input`); if (hashCode32(input.val()) == gAccessHashCode) { gAccessConfirmed = true; - if (gOverrideMins) { - // Slight delay to allow focus to pass to new dialog - setTimeout(activateOverride, 100); - } else { - $("#form").show(); - setTimeout(focusMins, 100); - } + $("#form").show(); + setTimeout(focusHours, 100); $(`#${prompt}`).dialog("close"); } else { input.val(""); @@ -341,6 +426,9 @@ function initAccessControlPrompt(prompt) { /*** STARTUP CODE BEGINS HERE ***/ +// Save original HTML of form +gFormHTML = $("#form").html(); + // Initialize alert dialogs $("div[id^='alert']").dialog({ autoOpen: false, @@ -353,6 +441,9 @@ $("div[id^='alert']").dialog({ $("#alertLimitReached").dialog({ close: function (event, ui) { closePage(); } }); +$("#alertNoEligibleSets").dialog({ + close: function (event, ui) { closePage(); } +}); $("#alertOverrideActivated").dialog({ close: function (event, ui) { closePage(); } });