@@ -429,6 +429,14 @@ public class FilterHttpResource { | |||||
return ok(ruleEngine.flushCaches()); | return ok(ruleEngine.flushCaches()); | ||||
} | } | ||||
@DELETE @Path(EP_MATCHERS) | |||||
@Produces(APPLICATION_JSON) | |||||
public Response flushMatchers(@Context ContainerRequest request) { | |||||
final Account caller = userPrincipal(request); | |||||
if (!caller.admin()) return forbidden(); | |||||
return ok(ruleEngine.flushMatchers()); | |||||
} | |||||
@POST @Path(EP_APPLY+"/{requestId}") | @POST @Path(EP_APPLY+"/{requestId}") | ||||
@Consumes(MediaType.WILDCARD) | @Consumes(MediaType.WILDCARD) | ||||
@Produces(MediaType.WILDCARD) | @Produces(MediaType.WILDCARD) | ||||
@@ -214,14 +214,14 @@ public class BubbleBlockRuleDriver extends TrafficAnalyticsRuleDriver | |||||
return FilterMatchDecision.abort_not_found; // block this request | return FilterMatchDecision.abort_not_found; // block this request | ||||
case allow: default: | case allow: default: | ||||
subDecision = checkRefererAndShowStats(decisionType, filter, account, device, extraLog, app, site, prefix, bubbleBlockConfig); | |||||
subDecision = checkRefererAndShowStats(decisionType, filter, account, device, extraLog, app, site, prefix); | |||||
if (subDecision != null) return subDecision; | if (subDecision != null) return subDecision; | ||||
if (log.isInfoEnabled()) log.info(prefix+"decision is ALLOW"); | if (log.isInfoEnabled()) log.info(prefix+"decision is ALLOW"); | ||||
else if (extraLog) log.error(prefix+"decision is ALLOW"); | else if (extraLog) log.error(prefix+"decision is ALLOW"); | ||||
return FilterMatchDecision.no_match; | return FilterMatchDecision.no_match; | ||||
case filter: | case filter: | ||||
subDecision = checkRefererAndShowStats(decisionType, filter, account, device, extraLog, app, site, prefix, bubbleBlockConfig); | |||||
subDecision = checkRefererAndShowStats(decisionType, filter, account, device, extraLog, app, site, prefix); | |||||
if (subDecision != null) return subDecision; | if (subDecision != null) return subDecision; | ||||
final List<BlockSpec> specs = decision.getSpecs(); | final List<BlockSpec> specs = decision.getSpecs(); | ||||
if (empty(specs)) { | if (empty(specs)) { | ||||
@@ -237,8 +237,8 @@ public class BubbleBlockRuleDriver extends TrafficAnalyticsRuleDriver | |||||
return FilterMatchDecision.abort_not_found; | return FilterMatchDecision.abort_not_found; | ||||
} | } | ||||
} | } | ||||
if (log.isInfoEnabled()) log.info(prefix+"decision was FILTER but no URL-blocks and inPageBlocks are disabled (returning no_match)"); | |||||
else if (extraLog) log.error(prefix+"decision was FILTER but no URL-blocks and inPageBlocks are disabled (returning no_match)"); | |||||
if (log.isInfoEnabled()) log.info(prefix+"decision was FILTER but no URL-blocks and both inPageBlocks and showStats are disabled (returning no_match)"); | |||||
else if (extraLog) log.error(prefix+"decision was FILTER but no URL-blocks and both inPageBlocks and showStats are disabled (returning no_match)"); | |||||
return FilterMatchDecision.no_match; | return FilterMatchDecision.no_match; | ||||
} | } | ||||
if (log.isInfoEnabled()) log.info(prefix+"decision is FILTER (returning match)"); | if (log.isInfoEnabled()) log.info(prefix+"decision is FILTER (returning match)"); | ||||
@@ -248,7 +248,8 @@ public class BubbleBlockRuleDriver extends TrafficAnalyticsRuleDriver | |||||
} | } | ||||
} | } | ||||
public FilterMatchDecision checkRefererAndShowStats(BlockDecisionType decisionType, FilterMatchersRequest filter, Account account, Device device, boolean extraLog, String app, String site, String prefix, BubbleBlockConfig bubbleBlockConfig) { | |||||
public FilterMatchDecision checkRefererAndShowStats(BlockDecisionType decisionType, FilterMatchersRequest filter, Account account, Device device, boolean extraLog, String app, String site, String prefix) { | |||||
prefix += "(checkRefererAndShowStats): "; | |||||
if (filter.hasReferer()) { | if (filter.hasReferer()) { | ||||
final FilterMatchDecision refererDecision = checkRefererDecision(filter, account, device, app, site, prefix); | final FilterMatchDecision refererDecision = checkRefererDecision(filter, account, device, app, site, prefix); | ||||
if (refererDecision != null && refererDecision.isAbort()) { | if (refererDecision != null && refererDecision.isAbort()) { | ||||
@@ -261,6 +262,9 @@ public class BubbleBlockRuleDriver extends TrafficAnalyticsRuleDriver | |||||
if (log.isInfoEnabled()) log.info(prefix+"decision was "+decisionType+" but showStats=true, returning match"); | if (log.isInfoEnabled()) log.info(prefix+"decision was "+decisionType+" but showStats=true, returning match"); | ||||
else if (extraLog) log.error(prefix+"decision was "+decisionType+" but showStats=true, returning match"); | else if (extraLog) log.error(prefix+"decision was "+decisionType+" but showStats=true, returning match"); | ||||
return FilterMatchDecision.match; | return FilterMatchDecision.match; | ||||
} else { | |||||
if (log.isInfoEnabled()) log.info(prefix+"decision was "+decisionType+" but showStats=false, returning null"); | |||||
else if (extraLog) log.error(prefix+"decision was "+decisionType+" but showStats=false, returning null"); | |||||
} | } | ||||
return null; | return null; | ||||
} | } | ||||
@@ -419,7 +423,7 @@ public class BubbleBlockRuleDriver extends TrafficAnalyticsRuleDriver | |||||
final AppDataDAO dataDAO = configuration.getBean(AppDataDAO.class); | final AppDataDAO dataDAO = configuration.getBean(AppDataDAO.class); | ||||
log.info("priming app="+app.getName()); | log.info("priming app="+app.getName()); | ||||
dataDAO.findByAccountAndAppAndAndKeyPrefix(account.getUuid(), app.getUuid(), PREFIX_APPDATA_HIDE_STATS) | dataDAO.findByAccountAndAppAndAndKeyPrefix(account.getUuid(), app.getUuid(), PREFIX_APPDATA_HIDE_STATS) | ||||
.forEach(data -> deviceIdService.setBlockStatsForFqdn(account, fqdnFromKey(data.getKey()), false)); | |||||
.forEach(data -> deviceIdService.setBlockStatsForFqdn(account, fqdnFromKey(data.getKey()), !Boolean.parseBoolean(data.getData()))); | |||||
} | } | ||||
@Override public Function<AppData, AppData> createCallback(Account account, | @Override public Function<AppData, AppData> createCallback(Account account, | ||||
@@ -437,7 +441,7 @@ public class BubbleBlockRuleDriver extends TrafficAnalyticsRuleDriver | |||||
deviceIdService.unsetBlockStatsForFqdn(account, fqdn); | deviceIdService.unsetBlockStatsForFqdn(account, fqdn); | ||||
} else { | } else { | ||||
log.info(prefix+"setting fqdn: "+fqdn); | log.info(prefix+"setting fqdn: "+fqdn); | ||||
deviceIdService.setBlockStatsForFqdn(account, fqdn, false); | |||||
deviceIdService.setBlockStatsForFqdn(account, fqdn, !Boolean.parseBoolean(data.getData())); | |||||
} | } | ||||
} else { | } else { | ||||
throw invalidEx("err.fqdn.invalid", "not a valid FQDN: "+fqdn, fqdn); | throw invalidEx("err.fqdn.invalid", "not a valid FQDN: "+fqdn, fqdn); | ||||
@@ -1 +1 @@ | |||||
bubble.version=Adventure 1.0.6 | |||||
bubble.version=Adventure 1.0.7 |
@@ -30,15 +30,38 @@ | |||||
.then(response => response.text()) | .then(response => response.text()) | ||||
.then(data => set_func(data)) | .then(data => set_func(data)) | ||||
.catch((error) => { | .catch((error) => { | ||||
console.error('Error getting link message ('+messages+'): '+error); | |||||
console.error('Error getting link message ('+link+'): '+error); | |||||
}); | }); | ||||
} | } | ||||
{{JS_PREFIX}}_app_entry_div = function () { | |||||
const entry = document.createElement('div'); | |||||
entry.style.all = 'revert'; | |||||
entry.style.fontSize = 'small'; | |||||
return entry; | |||||
} | |||||
{{JS_PREFIX}}_app_header_element = function () { | |||||
const header = document.createElement('strong'); | |||||
header.style.all = 'revert'; | |||||
header.style.fontSize = 'medium'; | |||||
return header; | |||||
} | |||||
{{JS_PREFIX}}_app_header = function (title, defaultTitle, formatFunc) { | |||||
const labelText = {{PAGE_PREFIX}}_msg_or_default({{JS_PREFIX}}_messages, title, defaultTitle); | |||||
const label = {{JS_PREFIX}}_app_header_element(); | |||||
label.appendChild(document.createTextNode(formatFunc ? formatFunc(labelText) : labelText)); | |||||
return label; | |||||
} | |||||
{{JS_PREFIX}}_app_title_span = function (defaultName) { | {{JS_PREFIX}}_app_title_span = function (defaultName) { | ||||
const appTitleSpan = document.createElement('span'); | const appTitleSpan = document.createElement('span'); | ||||
appTitleSpan.style.fontSize = 'medium'; | |||||
appTitleSpan.style.fontSize = 'large'; | |||||
const appImage = document.createElement('img'); | const appImage = document.createElement('img'); | ||||
appImage.src = {{JS_PREFIX}}_asset_img_url('icon'); | appImage.src = {{JS_PREFIX}}_asset_img_url('icon'); | ||||
appImage.style.all = 'revert'; | |||||
appImage.style.width = '16px'; | |||||
appImage.width = 16; | appImage.width = 16; | ||||
const appTitle = document.createElement('strong'); | const appTitle = document.createElement('strong'); | ||||
const appName = {{PAGE_PREFIX}}_msg_or_default({{JS_PREFIX}}_messages, 'web_appName', defaultName); | const appName = {{PAGE_PREFIX}}_msg_or_default({{JS_PREFIX}}_messages, 'web_appName', defaultName); | ||||
@@ -48,10 +71,18 @@ | |||||
return appTitleSpan; | return appTitleSpan; | ||||
} | } | ||||
function {{JS_PREFIX}}_create_button(labelKey, labelDefault, onclick, labelFormat) { | |||||
const label = {{PAGE_PREFIX}}_msg_or_default({{JS_PREFIX}}_messages, labelKey, labelDefault); | |||||
function {{JS_PREFIX}}_create_button_element() { | |||||
const btn = document.createElement('button'); | const btn = document.createElement('button'); | ||||
btn.style.color = 'black'; | |||||
btn.style.fontSize = 'x-small'; | btn.style.fontSize = 'x-small'; | ||||
btn.style.border = 'revert'; | |||||
btn.style.background = 'revert'; | |||||
return btn; | |||||
} | |||||
function {{JS_PREFIX}}_create_button(labelKey, labelDefault, onclick, labelFormat) { | |||||
const label = {{PAGE_PREFIX}}_msg_or_default({{JS_PREFIX}}_messages, labelKey, labelDefault); | |||||
const btn = {{JS_PREFIX}}_create_button_element(); | |||||
btn.addEventListener('click', onclick); | btn.addEventListener('click', onclick); | ||||
btn.appendChild(document.createTextNode(labelFormat ? labelFormat(label) : label)); | btn.appendChild(document.createTextNode(labelFormat ? labelFormat(label) : label)); | ||||
return btn; | return btn; | ||||
@@ -97,6 +128,16 @@ if (typeof {{PAGE_PREFIX}}_icon_status === 'undefined') { | |||||
return msg && msg in messages ? messages[msg] : def; | return msg && msg in messages ? messages[msg] : def; | ||||
} | } | ||||
{{PAGE_PREFIX}}_get_fqdn_tld = function () { | |||||
const host = window.location.hostname; | |||||
const fqdnParts = host.split('.'); | |||||
switch (fqdnParts.length) { | |||||
case 0: return host; | |||||
case 1: return host; | |||||
default: return fqdnParts[fqdnParts.length-2] + '.' + fqdnParts[fqdnParts.length-1]; | |||||
} | |||||
} | |||||
{{PAGE_PREFIX}}_log = function (data) { | {{PAGE_PREFIX}}_log = function (data) { | ||||
const logData = JSON.stringify(data); | const logData = JSON.stringify(data); | ||||
const requestOptions = { | const requestOptions = { | ||||
@@ -31,13 +31,23 @@ function {{JS_PREFIX}}_show_app_details(ev) { | |||||
detailsDiv.removeChild(detailsDiv.lastChild); | detailsDiv.removeChild(detailsDiv.lastChild); | ||||
} | } | ||||
detailsDiv.style.display = 'block'; | detailsDiv.style.display = 'block'; | ||||
detailsDiv.appendChild({{JS_PREFIX}}_app_title_span('BlockParty!')); | |||||
const titleSpan = {{JS_PREFIX}}_app_title_span('BlockParty!'); | |||||
const topButtons = document.createElement('span'); | |||||
const topCloseButton = {{JS_PREFIX}}_create_button('web_close', 'close', function (e) { | |||||
e.stopPropagation(); | |||||
e.preventDefault(); | |||||
{{JS_PREFIX}}_hide_app_details(); | |||||
return false; | |||||
}); | |||||
topButtons.style.float = 'right'; | |||||
topButtons.appendChild(topCloseButton); | |||||
titleSpan.appendChild(topButtons); | |||||
detailsDiv.appendChild(titleSpan); | |||||
detailsDiv.appendChild(document.createElement('hr')); | detailsDiv.appendChild(document.createElement('hr')); | ||||
// add rows for blocked stuff... | // add rows for blocked stuff... | ||||
const adsAndTrackersLabel = {{PAGE_PREFIX}}_msg_or_default({{JS_PREFIX}}_messages, 'web_adsAndTrackers', 'Blocked Ads/Trackers'); | |||||
const adsAndTrackersHeader = document.createElement('strong'); | |||||
adsAndTrackersHeader.appendChild(document.createTextNode(adsAndTrackersLabel)); | |||||
const adsAndTrackersHeader = {{JS_PREFIX}}_app_header('web_adsAndTrackers', 'Blocked Ads/Trackers'); | |||||
detailsDiv.appendChild(adsAndTrackersHeader); | detailsDiv.appendChild(adsAndTrackersHeader); | ||||
if ({{JS_PREFIX}}_last_stats.blocks.length === 0) { | if ({{JS_PREFIX}}_last_stats.blocks.length === 0) { | ||||
const emptyLabel = {{PAGE_PREFIX}}_msg_or_default({{JS_PREFIX}}_messages, 'web_noAdsOrTrackers', '(no blocks)'); | const emptyLabel = {{PAGE_PREFIX}}_msg_or_default({{JS_PREFIX}}_messages, 'web_noAdsOrTrackers', '(no blocks)'); | ||||
@@ -46,26 +56,25 @@ function {{JS_PREFIX}}_show_app_details(ev) { | |||||
} else { | } else { | ||||
for (let i = 0; i < {{JS_PREFIX}}_last_stats.blocks.length; i++) { | for (let i = 0; i < {{JS_PREFIX}}_last_stats.blocks.length; i++) { | ||||
const entry = {{JS_PREFIX}}_last_stats.blocks[i]; | const entry = {{JS_PREFIX}}_last_stats.blocks[i]; | ||||
const entryDiv = document.createElement('div'); | |||||
const entryDiv = {{JS_PREFIX}}_app_entry_div(); | |||||
const entryText = document.createTextNode(entry.fqdn + ': ' + entry.count); | const entryText = document.createTextNode(entry.fqdn + ': ' + entry.count); | ||||
entryDiv.appendChild(entryText); | entryDiv.appendChild(entryText); | ||||
detailsDiv.appendChild(entryDiv); | detailsDiv.appendChild(entryDiv); | ||||
} | } | ||||
} | } | ||||
detailsDiv.appendChild(document.createElement('hr')); | detailsDiv.appendChild(document.createElement('hr')); | ||||
const fqdn = window.location.hostname; | |||||
const hideStatsButton = {{JS_PREFIX}}_create_button('web_hideStats', 'Hide BlockParty for '+fqdn, function (e) { | |||||
const domain = {{PAGE_PREFIX}}_get_fqdn_tld(); | |||||
const hideStatsButton = {{JS_PREFIX}}_create_button('web_hideStats', 'Hide BlockParty for ', function (e) { | |||||
// write appdata | // write appdata | ||||
const hide_show_stats_url = '/__bubble/api/filter/data/{{BUBBLE_DATA_ID}}/write'; | const hide_show_stats_url = '/__bubble/api/filter/data/{{BUBBLE_DATA_ID}}/write'; | ||||
const requestOptions = { | const requestOptions = { | ||||
method: 'POST', | method: 'POST', | ||||
body: JSON.stringify({key: 'hideStats_'+fqdn, data: 'false'}) | |||||
body: JSON.stringify({key: 'hideStats_'+domain, data: 'true'}) | |||||
}; | }; | ||||
fetch(hide_show_stats_url, requestOptions) | fetch(hide_show_stats_url, requestOptions) | ||||
.then(() => window.location.reload()); | .then(() => window.location.reload()); | ||||
//.then(() => window.location.reload()); | |||||
return false; | return false; | ||||
}, label => label + fqdn); | |||||
}, label => label + domain); | |||||
detailsDiv.appendChild(hideStatsButton); | detailsDiv.appendChild(hideStatsButton); | ||||
detailsDiv.appendChild(document.createElement('hr')); | detailsDiv.appendChild(document.createElement('hr')); | ||||
@@ -330,11 +330,13 @@ function {{JS_PREFIX}}_hide_app_details() { | |||||
} | } | ||||
} | } | ||||
function {{JS_PREFIX}}_create_expandable_header(listsHeaderText, entryClassName, detailsDiv, stateVar) { | |||||
const listsHeaderLink = document.createElement('a'); | |||||
const listsHeader = document.createElement('strong'); | |||||
function {{JS_PREFIX}}_create_expandable_header(messageKey, defaultMessage, entryClassName, detailsDiv, stateVar) { | |||||
const listsHeaderText = {{PAGE_PREFIX}}_msg_or_default({{JS_PREFIX}}_messages, messageKey, defaultMessage); | |||||
const listsHeader = {{JS_PREFIX}}_app_header_element(); | |||||
const listsExpandControl = document.createElement('span'); | const listsExpandControl = document.createElement('span'); | ||||
listsExpandControl.appendChild(document.createTextNode('[+] ')); | listsExpandControl.appendChild(document.createTextNode('[+] ')); | ||||
const listsHeaderLink = document.createElement('a'); | |||||
const listsCollapseControl = document.createElement('span'); | const listsCollapseControl = document.createElement('span'); | ||||
listsCollapseControl.appendChild(document.createTextNode('[-] ')); | listsCollapseControl.appendChild(document.createTextNode('[-] ')); | ||||
listsHeader.appendChild({{JS_PREFIX}}_expand_state[stateVar] ? listsCollapseControl : listsExpandControl); | listsHeader.appendChild({{JS_PREFIX}}_expand_state[stateVar] ? listsCollapseControl : listsExpandControl); | ||||
@@ -392,7 +394,6 @@ function {{JS_PREFIX}}_show_app_details() { | |||||
detailsDiv.style.scrollTop = 0; | detailsDiv.style.scrollTop = 0; | ||||
const titleSpan = {{JS_PREFIX}}_app_title_span('ShadowBan'); | const titleSpan = {{JS_PREFIX}}_app_title_span('ShadowBan'); | ||||
const topButtons = document.createElement('span'); | const topButtons = document.createElement('span'); | ||||
if ({{JS_PREFIX}}_unblocked_needs_refresh) { | if ({{JS_PREFIX}}_unblocked_needs_refresh) { | ||||
@@ -420,8 +421,7 @@ function {{JS_PREFIX}}_show_app_details() { | |||||
detailsDiv.appendChild(document.createElement('hr')); | detailsDiv.appendChild(document.createElement('hr')); | ||||
const recentBlocksDiv = document.createElement('div'); | const recentBlocksDiv = document.createElement('div'); | ||||
const recentBlocksText = {{PAGE_PREFIX}}_msg_or_default({{JS_PREFIX}}_messages, 'web_recentBlocks', 'Recent Blocks'); | |||||
const recentBlocksHeader = {{JS_PREFIX}}_create_expandable_header(recentBlocksText, '{{JS_PREFIX}}_recent_entry', detailsDiv, 'recent'); | |||||
const recentBlocksHeader = {{JS_PREFIX}}_create_expandable_header('web_recentBlocks', 'Recent Blocks', '{{JS_PREFIX}}_recent_entry', detailsDiv, 'recent'); | |||||
recentBlocksDiv.appendChild(recentBlocksHeader); | recentBlocksDiv.appendChild(recentBlocksHeader); | ||||
detailsDiv.appendChild(recentBlocksDiv); | detailsDiv.appendChild(recentBlocksDiv); | ||||
@@ -432,7 +432,7 @@ function {{JS_PREFIX}}_show_app_details() { | |||||
let totalBlocks = 0; | let totalBlocks = 0; | ||||
if (keywordTallyKeys.length === 0 && authorTallyKeys.length === 0) { | if (keywordTallyKeys.length === 0 && authorTallyKeys.length === 0) { | ||||
const noRecentBlocksText = {{PAGE_PREFIX}}_msg_or_default({{JS_PREFIX}}_messages, 'web_noRecentBlocks', '(empty)'); | const noRecentBlocksText = {{PAGE_PREFIX}}_msg_or_default({{JS_PREFIX}}_messages, 'web_noRecentBlocks', '(empty)'); | ||||
const entryDiv = document.createElement('div'); | |||||
const entryDiv = {{JS_PREFIX}}_app_entry_div(); | |||||
entryDiv.className = '{{JS_PREFIX}}_recent_entry'; | entryDiv.className = '{{JS_PREFIX}}_recent_entry'; | ||||
entryDiv.style.display = {{JS_PREFIX}}_expand_state['recent'] ? 'block': 'none'; | entryDiv.style.display = {{JS_PREFIX}}_expand_state['recent'] ? 'block': 'none'; | ||||
entryDiv.appendChild(document.createTextNode(noRecentBlocksText)); | entryDiv.appendChild(document.createTextNode(noRecentBlocksText)); | ||||
@@ -448,7 +448,7 @@ function {{JS_PREFIX}}_show_app_details() { | |||||
keywordTallyKeys.forEach(keyword => { | keywordTallyKeys.forEach(keyword => { | ||||
const count = keywordTally[keyword]; | const count = keywordTally[keyword]; | ||||
totalBlocks += count; | totalBlocks += count; | ||||
const entryDiv = document.createElement('div'); | |||||
const entryDiv = {{JS_PREFIX}}_app_entry_div(); | |||||
entryDiv.className = '{{JS_PREFIX}}_recent_entry'; | entryDiv.className = '{{JS_PREFIX}}_recent_entry'; | ||||
entryDiv.style.display = {{JS_PREFIX}}_expand_state['recent'] ? 'block': 'none'; | entryDiv.style.display = {{JS_PREFIX}}_expand_state['recent'] ? 'block': 'none'; | ||||
const em = document.createElement('em'); | const em = document.createElement('em'); | ||||
@@ -465,7 +465,7 @@ function {{JS_PREFIX}}_show_app_details() { | |||||
authorTallyKeys.forEach(author => { | authorTallyKeys.forEach(author => { | ||||
const count = authorTally[author]; | const count = authorTally[author]; | ||||
totalBlocks += count; | totalBlocks += count; | ||||
const entryDiv = document.createElement('div'); | |||||
const entryDiv = {{JS_PREFIX}}_app_entry_div(); | |||||
entryDiv.className = '{{JS_PREFIX}}_recent_entry'; | entryDiv.className = '{{JS_PREFIX}}_recent_entry'; | ||||
entryDiv.style.display = 'none'; | entryDiv.style.display = 'none'; | ||||
entryDiv.appendChild(document.createTextNode(author + ': ' + count)); | entryDiv.appendChild(document.createTextNode(author + ': ' + count)); | ||||
@@ -473,13 +473,14 @@ function {{JS_PREFIX}}_show_app_details() { | |||||
}); | }); | ||||
} | } | ||||
} | } | ||||
const summaryHeader = document.createElement('strong'); | |||||
const summaryLabel = {{PAGE_PREFIX}}_msg_or_default({{JS_PREFIX}}_messages, 'web_signalNoiseRatio', 'signal/noise'); | |||||
const totalAllowed = {{PAGE_PREFIX}}_allow_tally; | const totalAllowed = {{PAGE_PREFIX}}_allow_tally; | ||||
const fullTotal = totalAllowed + totalBlocks; | const fullTotal = totalAllowed + totalBlocks; | ||||
const ratio = fullTotal === 0 ? 0 : 100.0 * (totalAllowed / fullTotal); | const ratio = fullTotal === 0 ? 0 : 100.0 * (totalAllowed / fullTotal); | ||||
const eqSign = totalBlocks === 0 ? '=' : '≈'; | const eqSign = totalBlocks === 0 ? '=' : '≈'; | ||||
summaryHeader.appendChild(document.createTextNode(summaryLabel + ': ' + totalAllowed + '/' + totalBlocks + ' '+eqSign+' ' + ratio.toLocaleString('{{ACCOUNT_LOCALE_HYPHEN}}', { maximumSignificantDigits: 3 }) + '%')); | |||||
const summaryHeader = {{JS_PREFIX}}_app_header('web_signalNoiseRatio', 'signal/noise', | |||||
label => label + ': ' + totalAllowed + '/' + totalBlocks + ' '+eqSign+' ' + ratio.toLocaleString('{{ACCOUNT_LOCALE_HYPHEN}}', { maximumSignificantDigits: 3 }) + '%'); | |||||
recentBlocksDiv.appendChild(summaryHeader); | recentBlocksDiv.appendChild(summaryHeader); | ||||
detailsDiv.appendChild(document.createElement('hr')); | detailsDiv.appendChild(document.createElement('hr')); | ||||
@@ -497,12 +498,11 @@ function {{JS_PREFIX}}_show_app_details() { | |||||
return a.localeCompare(b, '{{ACCOUNT_LANG}}', {'sensitivity': 'base'}); | return a.localeCompare(b, '{{ACCOUNT_LANG}}', {'sensitivity': 'base'}); | ||||
}); | }); | ||||
} | } | ||||
const usersHeaderText = {{PAGE_PREFIX}}_msg_or_default({{JS_PREFIX}}_messages, 'web_blockedUsers', 'Block Users'); | |||||
const usersHeader = {{JS_PREFIX}}_create_expandable_header(usersHeaderText, '{{JS_PREFIX}}_user_entry', detailsDiv, 'users'); | |||||
const usersHeader = {{JS_PREFIX}}_create_expandable_header('web_blockedUsers', 'Block Users', '{{JS_PREFIX}}_user_entry', detailsDiv, 'users'); | |||||
detailsDiv.appendChild(usersHeader); | detailsDiv.appendChild(usersHeader); | ||||
if (blocks === null || blocks.length === 0) { | if (blocks === null || blocks.length === 0) { | ||||
const emptyMessage = {{PAGE_PREFIX}}_msg_or_default({{JS_PREFIX}}_messages, 'web_noUsersBlocked', '(empty)'); | const emptyMessage = {{PAGE_PREFIX}}_msg_or_default({{JS_PREFIX}}_messages, 'web_noUsersBlocked', '(empty)'); | ||||
const entryDiv = document.createElement('div'); | |||||
const entryDiv = {{JS_PREFIX}}_app_entry_div(); | |||||
entryDiv.className = '{{JS_PREFIX}}_user_entry'; | entryDiv.className = '{{JS_PREFIX}}_user_entry'; | ||||
entryDiv.style.display = {{JS_PREFIX}}_expand_state['users'] ? 'block': 'none'; | entryDiv.style.display = {{JS_PREFIX}}_expand_state['users'] ? 'block': 'none'; | ||||
const entryText = document.createTextNode(emptyMessage); | const entryText = document.createTextNode(emptyMessage); | ||||
@@ -511,7 +511,7 @@ function {{JS_PREFIX}}_show_app_details() { | |||||
} else { | } else { | ||||
for (let i = 0; i < blocks.length; i++) { | for (let i = 0; i < blocks.length; i++) { | ||||
const entry = blocks[i]; | const entry = blocks[i]; | ||||
const entryDiv = document.createElement('div'); | |||||
const entryDiv = {{JS_PREFIX}}_app_entry_div(); | |||||
entryDiv.className = '{{JS_PREFIX}}_user_entry'; | entryDiv.className = '{{JS_PREFIX}}_user_entry'; | ||||
entryDiv.style.display = {{JS_PREFIX}}_expand_state['users'] ? 'block': 'none'; | entryDiv.style.display = {{JS_PREFIX}}_expand_state['users'] ? 'block': 'none'; | ||||
const entryText = document.createTextNode(entry); | const entryText = document.createTextNode(entry); | ||||
@@ -523,12 +523,11 @@ function {{JS_PREFIX}}_show_app_details() { | |||||
detailsDiv.appendChild(document.createElement('hr')); | detailsDiv.appendChild(document.createElement('hr')); | ||||
if ({{JS_PREFIX}}_supports_keywords) { | if ({{JS_PREFIX}}_supports_keywords) { | ||||
const keywordsHeaderText = {{PAGE_PREFIX}}_msg_or_default({{JS_PREFIX}}_messages, 'web_blockedKeywords', 'Block Keywords'); | |||||
const keywordsHeader = {{JS_PREFIX}}_create_expandable_header(keywordsHeaderText, '{{JS_PREFIX}}_keyword_entry', detailsDiv, 'keywords'); | |||||
const keywordsHeader = {{JS_PREFIX}}_create_expandable_header('web_blockedKeywords', 'Block Keywords', '{{JS_PREFIX}}_keyword_entry', detailsDiv, 'keywords'); | |||||
detailsDiv.appendChild(keywordsHeader); | detailsDiv.appendChild(keywordsHeader); | ||||
if (keywords === null || keywords.length === 0) { | if (keywords === null || keywords.length === 0) { | ||||
const emptyMessage = {{PAGE_PREFIX}}_msg_or_default({{JS_PREFIX}}_messages, 'web_noKeywordsBlocked', '(empty)'); | const emptyMessage = {{PAGE_PREFIX}}_msg_or_default({{JS_PREFIX}}_messages, 'web_noKeywordsBlocked', '(empty)'); | ||||
const entryDiv = document.createElement('div'); | |||||
const entryDiv = {{JS_PREFIX}}_app_entry_div(); | |||||
entryDiv.className = '{{JS_PREFIX}}_keyword_entry'; | entryDiv.className = '{{JS_PREFIX}}_keyword_entry'; | ||||
entryDiv.style.display = {{JS_PREFIX}}_expand_state['keyword'] ? 'block': 'none'; | entryDiv.style.display = {{JS_PREFIX}}_expand_state['keyword'] ? 'block': 'none'; | ||||
const entryText = document.createTextNode(emptyMessage); | const entryText = document.createTextNode(emptyMessage); | ||||
@@ -537,7 +536,7 @@ function {{JS_PREFIX}}_show_app_details() { | |||||
} else { | } else { | ||||
for (let i = 0; i < keywords.length; i++) { | for (let i = 0; i < keywords.length; i++) { | ||||
const entry = keywords[i]; | const entry = keywords[i]; | ||||
const entryDiv = document.createElement('div'); | |||||
const entryDiv = {{JS_PREFIX}}_app_entry_div(); | |||||
entryDiv.className = '{{JS_PREFIX}}_keyword_entry'; | entryDiv.className = '{{JS_PREFIX}}_keyword_entry'; | ||||
entryDiv.style.display = {{JS_PREFIX}}_expand_state['keyword'] ? 'block': 'none'; | entryDiv.style.display = {{JS_PREFIX}}_expand_state['keyword'] ? 'block': 'none'; | ||||
const entryText = document.createTextNode(entry); | const entryText = document.createTextNode(entry); | ||||
@@ -571,8 +570,7 @@ function {{JS_PREFIX}}_show_app_details() { | |||||
const listKeys = Object.keys({{JS_PREFIX}}_messages).filter(k => k.startsWith('web_kwlist_') && !k.endsWith('_url')); | const listKeys = Object.keys({{JS_PREFIX}}_messages).filter(k => k.startsWith('web_kwlist_') && !k.endsWith('_url')); | ||||
if (listKeys.length > 0) { | if (listKeys.length > 0) { | ||||
const listsHeaderText = {{PAGE_PREFIX}}_msg_or_default({{JS_PREFIX}}_messages, 'web_blockedKeywordLists', 'Keyword Lists'); | |||||
const listsHeader = {{JS_PREFIX}}_create_expandable_header(listsHeaderText, '{{JS_PREFIX}}_keylist_entry', detailsDiv, 'lists'); | |||||
const listsHeader = {{JS_PREFIX}}_create_expandable_header('web_blockedKeywordLists', 'Keyword Lists', '{{JS_PREFIX}}_keylist_entry', detailsDiv, 'lists'); | |||||
detailsDiv.appendChild(listsHeader); | detailsDiv.appendChild(listsHeader); | ||||
listKeys.forEach(listKey => { | listKeys.forEach(listKey => { | ||||
@@ -582,7 +580,7 @@ function {{JS_PREFIX}}_show_app_details() { | |||||
console.log('URL not defined for list: '+listKey); | console.log('URL not defined for list: '+listKey); | ||||
return; | return; | ||||
} | } | ||||
const entryDiv = document.createElement('div'); | |||||
const entryDiv = {{JS_PREFIX}}_app_entry_div(); | |||||
entryDiv.className = '{{JS_PREFIX}}_keylist_entry'; | entryDiv.className = '{{JS_PREFIX}}_keylist_entry'; | ||||
const entryLink = document.createElement('span'); | const entryLink = document.createElement('span'); | ||||
entryLink.onclick = function () { | entryLink.onclick = function () { | ||||