gorhill
10 years ago
19 changed files with 89 additions and 848 deletions
-
6platform/chromium/vapi-client.js
-
4platform/chromium/vapi-common.js
-
13platform/firefox/vapi-background.js
-
6platform/firefox/vapi-client.js
-
6platform/firefox/vapi-common.js
-
61src/css/info.css
-
119src/info.html
-
2src/js/assets.js
-
4src/js/async.js
-
4src/js/browsercache.js
-
46src/js/contentscript-end.js
-
417src/js/info.js
-
4src/js/logger-ui.js
-
4src/js/logger.js
-
108src/js/messaging.js
-
7src/js/pagestats.js
-
2src/js/popup.js
-
28src/js/tab.js
-
86src/js/traffic.js
@ -1,61 +0,0 @@ |
|||
select { |
|||
max-width: 20em; |
|||
} |
|||
#stats div div,#lists div div { |
|||
padding: 0 1em 0 0; |
|||
display: inline-block; |
|||
text-align: right; |
|||
} |
|||
#requestsFilters button { |
|||
font-size: 15px; |
|||
} |
|||
#requestsFilters label { |
|||
font-size: 13px; |
|||
padding-right: 0.5em; |
|||
white-space: nowrap; |
|||
} |
|||
#requests-log { |
|||
background-color: white; |
|||
border: 1px inset #eee; |
|||
direction: ltr; |
|||
font: 11px monospace; |
|||
margin: 0; |
|||
overflow: scroll; |
|||
padding: 0; |
|||
width: calc(100% - 1.5em); |
|||
} |
|||
#requestsTable { |
|||
border-collapse: collapse; |
|||
} |
|||
#requests-log tr { |
|||
margin: 0; |
|||
border: 0; |
|||
padding: 0; |
|||
color: #070; |
|||
} |
|||
#requests-log tr.ro { |
|||
color: gray; |
|||
} |
|||
#requests-log tr:hover { |
|||
background-color: #eee; |
|||
} |
|||
#requests-log tr.blocked-true { |
|||
color: #c00; |
|||
} |
|||
#requests-log tr:first-child { |
|||
font-weight: bold; |
|||
background-color: #eee; |
|||
} |
|||
#requests-log tr > td { |
|||
padding: 1px 0.75em 1px 0; |
|||
white-space: nowrap; |
|||
} |
|||
#requests-log tr > td:nth-of-type(2) { |
|||
text-align: right; |
|||
} |
|||
.type-main_frame { |
|||
font-weight: bold; |
|||
} |
|||
tr.unused { |
|||
display: none; |
|||
} |
@ -1,119 +0,0 @@ |
|||
<!DOCTYPE html> |
|||
<html> |
|||
<head> |
|||
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8"> |
|||
<title>µMatrix — Info</title> |
|||
<link rel="stylesheet" type="text/css" href="css/common.css"> |
|||
<link rel="stylesheet" type="text/css" href="css/dashboard-common.css"> |
|||
<link rel="stylesheet" type="text/css" href="css/info.css"> |
|||
</head> |
|||
|
|||
<body> |
|||
|
|||
<h2 data-i18n="statsPageGenericStats" id="generic-stats"></h2> |
|||
<div> |
|||
<ul> |
|||
<li id="statsPageCookieHeadersFoiled"> |
|||
<li id="statsPageRefererHeadersFoiled"> |
|||
<li id="statsPageHyperlinkAuditingFoiled"> |
|||
<li id="statsPageCookiesRemoved"> |
|||
<li id="statsPageLocalStoragesCleared"> |
|||
<li id="statsPageBrowserCacheCleared"> |
|||
</ul> |
|||
</div> |
|||
|
|||
<h2 data-i18n="statsPageDetailedStats" id="detailed-stats"></h2> |
|||
<div> |
|||
<select id="selectPageUrlsTemplate" style="display: none;"> |
|||
<option value="" data-i18n="statsPageDetailedAllPages"></option> |
|||
<option value="http://behind-the-scene/" data-i18n="statsPageDetailedBehindTheScenePage"></option> |
|||
</select> |
|||
<select id="selectPageUrls"> |
|||
</select> |
|||
|
|||
<h3 data-i18n="statsPageOverview"></h3> |
|||
<div id="stats"> |
|||
<div style="white-space:nowrap;"> |
|||
|
|||
<div> |
|||
<span data-i18n="statsPageRequests"></span><br> |
|||
<span data-i18n="statsPageAll"></span><br> |
|||
<span data-i18n="statsPagePages"></span><br> |
|||
<span data-i18n="statsPageCookies"></span><br> |
|||
<span data-i18n="statsPageCSS"></span><br> |
|||
<span data-i18n="statsPageImages"></span><br> |
|||
<span data-i18n="statsPagePlugins"></span><br> |
|||
<span data-i18n="statsPageScripts"></span><br> |
|||
<span data-i18n="statsPageXHRs"></span><br> |
|||
<span data-i18n="statsPageFrames"></span><br> |
|||
<span data-i18n="statsPageOthers"></span><br> |
|||
</div> |
|||
<div style="color:#c00"> |
|||
<span data-i18n="statsPageBlocked"></span><br> |
|||
<span id="blockedAllCount"></span><br> |
|||
<span id="blockedMainFrameCount"></span><br> |
|||
<span id="blockedCookieCount"></span><br> |
|||
<span id="blockedStylesheetCount"></span><br> |
|||
<span id="blockedImageCount"></span><br> |
|||
<span id="blockedObjectCount"></span><br> |
|||
<span id="blockedScriptCount"></span><br> |
|||
<span id="blockedXHRCount"></span><br> |
|||
<span id="blockedSubFrameCount"></span><br> |
|||
<span id="blockedOtherCount"></span><br> |
|||
</div> |
|||
<div style="color:#070"> |
|||
<span data-i18n="statsPageAllowed"></span><br> |
|||
<span id="allowedAllCount"></span><br> |
|||
<span id="allowedMainFrameCount"></span><br> |
|||
<span id="allowedCookieCount"></span><br> |
|||
<span id="allowedStylesheetCount"></span><br> |
|||
<span id="allowedImageCount"></span><br> |
|||
<span id="allowedObjectCount"></span><br> |
|||
<span id="allowedScriptCount"></span><br> |
|||
<span id="allowedXHRCount"></span><br> |
|||
<span id="allowedSubFrameCount"></span><br> |
|||
<span id="allowedOtherCount"></span><br> |
|||
</div> |
|||
</div> |
|||
</div> |
|||
|
|||
|
|||
<h3 data-i18n="statsPageDetailed"></h3> |
|||
<div id="requests"> |
|||
<div><span data-i18n="statsPageLogSizePrompt1"></span> <input id="max-logged-requests" type="text" value="50" size="3"> <span data-i18n="statsPageLogSizePrompt2"></span> |
|||
<button class="whatisthis"></button> |
|||
<p class="whatisthis-expandable para" data-i18n="statsPageLogSizeHelp"></p> |
|||
<p id="requestsFilters"> |
|||
<button id="refreshRequests" class="fa" type="button"></button> |
|||
<button id="clearRequests" class="fa" type="button"></button> |
|||
<label><input id="show-blocked" type="checkbox" checked value="1"><span style="color:#c00" data-i18n="statsPageBlocked"></span></label> |
|||
<label><input id="show-allowed" type="checkbox" checked value="1"><span style="color:#070" data-i18n="statsPageAllowed"></span></label> |
|||
<label><input id="show-doc" type="checkbox" checked value="1"><span data-i18n="statsPagePages"></span></label> |
|||
<label><input id="show-cookie" type="checkbox" checked value="1"><span data-i18n="statsPageCookies"></span></label> |
|||
<label><input id="show-css" type="checkbox" checked value="1"><span data-i18n="statsPageCSS"></span></label> |
|||
<label><input id="show-image" type="checkbox" checked value="1"><span data-i18n="statsPageImages"></span></label> |
|||
<label><input id="show-plugin" type="checkbox" checked value="1"><span data-i18n="statsPagePlugins"></span></label> |
|||
<label><input id="show-script" type="checkbox" checked value="1"><span data-i18n="statsPageScripts"></span></label> |
|||
<label><input id="show-xhr" type="checkbox" checked value="1"><span data-i18n="statsPageXHRs"></span></label> |
|||
<label><input id="show-frame" type="checkbox" checked value="1"><span data-i18n="statsPageFrames"></span></label> |
|||
<label><input id="show-other" type="checkbox" checked value="1"><span data-i18n="statsPageOthers"></span></label> |
|||
</p> |
|||
</div> |
|||
<div id="requests-log" style="overflow-y:hidden"> |
|||
<table id="requestsTable"> |
|||
<tr class="ro"><td>when<td>what<td><td>where</tr> |
|||
<tr class="ro" id="requestRowTemplate"><td><td><td><a href="" style="display:none"><a></a><td></tr> |
|||
</table> |
|||
</div> |
|||
</div> |
|||
|
|||
</div> <!-- end of detailed stats --> |
|||
|
|||
<script src="js/vapi-common.js"></script> |
|||
<script src="js/vapi-client.js"></script> |
|||
<script src="js/udom.js"></script> |
|||
<script src="js/i18n.js"></script> |
|||
<script src="js/dashboard-common.js"></script> |
|||
<script src="js/info.js"></script> |
|||
</body> |
|||
</html> |
@ -1,417 +0,0 @@ |
|||
/******************************************************************************* |
|||
|
|||
µMatrix - a Chromium browser extension to black/white list requests. |
|||
Copyright (C) 2014 Raymond Hill |
|||
|
|||
This program is free software: you can redistribute it and/or modify |
|||
it under the terms of the GNU General Public License as published by |
|||
the Free Software Foundation, either version 3 of the License, or |
|||
(at your option) any later version. |
|||
|
|||
This program is distributed in the hope that it will be useful, |
|||
but WITHOUT ANY WARRANTY; without even the implied warranty of |
|||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
|||
GNU General Public License for more details. |
|||
|
|||
You should have received a copy of the GNU General Public License |
|||
along with this program. If not, see {http://www.gnu.org/licenses/}.
|
|||
|
|||
Home: https://github.com/gorhill/uMatrix
|
|||
*/ |
|||
|
|||
/* global vAPI, uDom */ |
|||
|
|||
/******************************************************************************/ |
|||
|
|||
(function() { |
|||
|
|||
'use strict'; |
|||
|
|||
/******************************************************************************/ |
|||
|
|||
var messager = vAPI.messaging.channel('info.js'); |
|||
|
|||
var targetTabId = null; |
|||
var maxRequests = 500; |
|||
var cachedUserSettings = {}; |
|||
|
|||
/******************************************************************************/ |
|||
|
|||
// Get a list of latest net requests
|
|||
|
|||
function updateRequestData(callback) { |
|||
var onResponseReceived = function(r) { |
|||
var requests = []; |
|||
for ( var pageURL in r ) { |
|||
if ( r.hasOwnProperty(pageURL) === false ) { |
|||
continue; |
|||
} |
|||
requests = requests.concat(r[pageURL]); |
|||
} |
|||
requests = requests |
|||
.sort(function(a,b){return b.when-a.when;}) |
|||
.slice(0, maxRequests); |
|||
callback(requests); |
|||
}; |
|||
var request = { |
|||
what: 'getRequestLogs', |
|||
tabId: targetTabId |
|||
}; |
|||
messager.send(request, onResponseReceived); |
|||
} |
|||
|
|||
/******************************************************************************/ |
|||
|
|||
function clearRequestData() { |
|||
var request = { |
|||
what: 'clearRequestLogs', |
|||
tabId: targetTabId |
|||
}; |
|||
messager.send(request); |
|||
} |
|||
|
|||
/******************************************************************************/ |
|||
|
|||
function renderNumber(value) { |
|||
if ( isNaN(value) ) { |
|||
return '0'; |
|||
} |
|||
return value.toLocaleString(); |
|||
} |
|||
|
|||
/******************************************************************************/ |
|||
|
|||
function renderNumbers(set) { |
|||
var keys = Object.keys(set); |
|||
var i = keys.length; |
|||
var key; |
|||
while ( i-- ) { |
|||
key = keys[i]; |
|||
uDom(key).text(renderNumber(set[key])); |
|||
} |
|||
} |
|||
|
|||
/******************************************************************************/ |
|||
|
|||
var renderLocalized = function(id, map) { |
|||
var uElem = uDom('#' + id); |
|||
var msg = vAPI.i18n(id); |
|||
for ( var k in map ) { |
|||
if ( map.hasOwnProperty(k) === false ) { |
|||
continue; |
|||
} |
|||
msg = msg.replace('{{' + k + '}}', map[k]); |
|||
} |
|||
uElem.html(msg); |
|||
}; |
|||
|
|||
/******************************************************************************/ |
|||
|
|||
function renderPageUrls() { |
|||
var onResponseReceived = function(result) { |
|||
var i, n; |
|||
var select = uDom('#selectPageUrls'); |
|||
|
|||
// Remove whatever was put there in a previous call
|
|||
uDom('#selectPageUrls > option').remove(); |
|||
var builtinOptions = uDom('#selectPageUrlsTemplate > option'); |
|||
n = builtinOptions.length; |
|||
for ( i = 0; i < n; i++ ) { |
|||
option = builtinOptions.at(i).clone(); |
|||
if ( option.val() === targetTabId ) { |
|||
option.attr('selected', true); |
|||
} |
|||
select.append(option); |
|||
} |
|||
|
|||
var entries = result.pageURLs.sort(); |
|||
var entry, pageURL, option; |
|||
n = entries.length; |
|||
for ( i = 0; i < n; i++ ) { |
|||
entry = entries[i]; |
|||
// Behind-the-scene entry is always present, no need to recreate it
|
|||
if ( entry.pageURL === result.behindTheSceneURL ) { |
|||
continue; |
|||
} |
|||
option = uDom('<option>'); |
|||
option.val(entry.tabId); |
|||
option.text(entry.pageURL); |
|||
if ( entry.pageURL === targetTabId ) { |
|||
option.attr('selected', true); |
|||
} |
|||
select.append(option); |
|||
} |
|||
// Deselect whatever is currently selected
|
|||
//uDom('#selectPageUrls > option:selected').prop('selected', false);
|
|||
// Select whatever needs to be selected
|
|||
//uDom('#selectPageUrls > option[value="'+targetUrl+'"]').prop('selected', true);
|
|||
}; |
|||
messager.send({ what: 'getPageURLs' }, onResponseReceived); |
|||
} |
|||
|
|||
/******************************************************************************/ |
|||
|
|||
function renderStats() { |
|||
var onResponseReceived = function(r) { |
|||
if ( !r.pageNetStats ) { |
|||
targetTabId = null; |
|||
} |
|||
|
|||
var requestStats = targetTabId ? r.pageNetStats : r.globalNetStats; |
|||
var blockedStats = requestStats.blocked; |
|||
var allowedStats = requestStats.allowed; |
|||
|
|||
renderLocalized('statsPageCookieHeadersFoiled', { count: renderNumber(r.cookieHeaderFoiledCounter) }); |
|||
renderLocalized('statsPageRefererHeadersFoiled', { count: renderNumber(r.refererHeaderFoiledCounter) }); |
|||
renderLocalized('statsPageHyperlinkAuditingFoiled', { count: renderNumber(r.hyperlinkAuditingFoiledCounter) }); |
|||
renderLocalized('statsPageCookiesRemoved', { count: renderNumber(r.cookieRemovedCounter) }); |
|||
renderLocalized('statsPageLocalStoragesCleared', { count: renderNumber(r.localStorageRemovedCounter) }); |
|||
renderLocalized('statsPageBrowserCacheCleared', { count: renderNumber(r.browserCacheClearedCounter) }); |
|||
|
|||
renderNumbers({ |
|||
'#blockedAllCount': requestStats.blocked.all, |
|||
'#blockedMainFrameCount': blockedStats.doc, |
|||
'#blockedCookieCount': blockedStats.cookie, |
|||
'#blockedStylesheetCount': blockedStats.css, |
|||
'#blockedImageCount': blockedStats.image, |
|||
'#blockedObjectCount': blockedStats.plugin, |
|||
'#blockedScriptCount': blockedStats.script, |
|||
'#blockedXHRCount': blockedStats.xhr, |
|||
'#blockedSubFrameCount': blockedStats.frame, |
|||
'#blockedOtherCount': blockedStats.other, |
|||
'#allowedAllCount': allowedStats.all, |
|||
'#allowedMainFrameCount': allowedStats.doc, |
|||
'#allowedCookieCount': allowedStats.cookie, |
|||
'#allowedStylesheetCount': allowedStats.css, |
|||
'#allowedImageCount': allowedStats.image, |
|||
'#allowedObjectCount': allowedStats.plugin, |
|||
'#allowedScriptCount': allowedStats.script, |
|||
'#allowedXHRCount': allowedStats.xhr, |
|||
'#allowedSubFrameCount': allowedStats.frame, |
|||
'#allowedOtherCount': allowedStats.other |
|||
}); |
|||
|
|||
// because some i18n messages may contain links
|
|||
uDom('a').attr('target', '_blank'); |
|||
}; |
|||
|
|||
messager.send({ |
|||
what: 'getStats', |
|||
tabId: targetTabId |
|||
}, |
|||
onResponseReceived |
|||
); |
|||
} |
|||
|
|||
/******************************************************************************/ |
|||
|
|||
function renderRequestRow(row, request) { |
|||
row.attr('id', ''); |
|||
row.css('display', ''); |
|||
row.removeClass(); |
|||
if ( request.block !== false ) { |
|||
row.addClass('blocked-true'); |
|||
} else { |
|||
row.addClass('blocked-false'); |
|||
} |
|||
row.addClass('type-' + request.type); |
|||
var cells = row.descendants('td'); |
|||
|
|||
// when
|
|||
var when = new Date(request.when); |
|||
cells.at(0).text(when.toLocaleTimeString()); |
|||
|
|||
// request type
|
|||
cells.at(1).text(request.type); |
|||
|
|||
// Well I got back full control since not using Tempo.js, I can now
|
|||
// generate smarter hyperlinks, that is, not hyperlinking fake
|
|||
// request URLs, which are recognizable with their curly braces inside.
|
|||
var a = cells.at(2).descendants('a'); |
|||
if ( request.url.search('{') < 0 ) { |
|||
a.attr('href', request.url); |
|||
a.css('display', ''); |
|||
} else { |
|||
a.css('display', 'none'); |
|||
} |
|||
|
|||
// request URL
|
|||
cells.at(3).text(request.url); |
|||
} |
|||
|
|||
/******************************************************************************/ |
|||
|
|||
var renderRequests = function(requests) { |
|||
var table = uDom('#requestsTable'); |
|||
var i, row; |
|||
var rowTemplate = table.descendants('#requestRowTemplate').first(); |
|||
|
|||
// Reuse whatever rows is already in there.
|
|||
var rows = table.descendants('tr:not(.ro)'); |
|||
var n = Math.min(requests.length, rows.length); |
|||
for ( i = 0; i < n; i++ ) { |
|||
renderRequestRow(rows.at(i), requests[i]); |
|||
} |
|||
|
|||
// Unhide reused rows
|
|||
rows.subset(0, n).removeClass('unused'); |
|||
|
|||
// Hide extra rows
|
|||
rows.subset(n).addClass('unused'); |
|||
|
|||
// Create new rows to receive what is left
|
|||
n = requests.length; |
|||
for ( ; i < n; i++ ) { |
|||
row = rowTemplate.clone(); |
|||
renderRequestRow(row, requests[i]); |
|||
row.insertBefore(rowTemplate); |
|||
} |
|||
|
|||
syncWithFilters(); |
|||
}; |
|||
|
|||
/******************************************************************************/ |
|||
|
|||
var updateRequests = function() { |
|||
updateRequestData(renderRequests); |
|||
}; |
|||
|
|||
/******************************************************************************/ |
|||
|
|||
var clearRequests = function() { |
|||
clearRequestData(); |
|||
renderRequests([]); |
|||
}; |
|||
|
|||
/******************************************************************************/ |
|||
|
|||
function changeUserSettings(name, value) { |
|||
cachedUserSettings[name] = value; |
|||
messager.send({ |
|||
what: 'userSettings', |
|||
name: name, |
|||
value: value |
|||
}); |
|||
} |
|||
|
|||
/******************************************************************************/ |
|||
|
|||
function changeValueHandler(elem, setting, min, max) { |
|||
var oldVal = cachedUserSettings[setting]; |
|||
var newVal = Math.round(parseFloat(elem.val())); |
|||
if ( typeof newVal !== 'number' ) { |
|||
newVal = oldVal; |
|||
} else { |
|||
newVal = Math.max(newVal, min); |
|||
newVal = Math.min(newVal, max); |
|||
} |
|||
elem.val(newVal); |
|||
if ( newVal !== oldVal ) { |
|||
changeUserSettings(setting, newVal); |
|||
} |
|||
} |
|||
|
|||
/******************************************************************************/ |
|||
|
|||
function changeFilterHandler() { |
|||
// Save new state of filters in user settings
|
|||
// Initialize request filters as per user settings:
|
|||
// https://github.com/gorhill/httpswitchboard/issues/49
|
|||
var statsFilters = cachedUserSettings.statsFilters; |
|||
uDom('input[id^="show-"][type="checkbox"]').forEach(function(input) { |
|||
statsFilters[input.attr('id')] = !!input.prop('checked'); |
|||
}); |
|||
changeUserSettings('statsFilters', statsFilters); |
|||
|
|||
syncWithFilters(); |
|||
} |
|||
|
|||
/******************************************************************************/ |
|||
|
|||
// Synchronize list of net requests with filter states
|
|||
|
|||
function syncWithFilters() { |
|||
var blocked = ['blocked','allowed']; |
|||
var type = ['doc','cookie','css','image','plugin','script','xhr','frame','other']; |
|||
var i = blocked.length; |
|||
var j; |
|||
var display, selector; |
|||
while ( i-- ) { |
|||
j = type.length; |
|||
while ( j-- ) { |
|||
display = uDom('#show-' + blocked[i]).prop('checked') && |
|||
uDom('#show-' + type[j]).prop('checked') ? '' : 'none'; |
|||
selector = '.blocked-' + (blocked[i] === 'blocked') + '.type-' + type[j]; |
|||
uDom(selector).css('display', display); |
|||
} |
|||
} |
|||
} |
|||
|
|||
/******************************************************************************/ |
|||
|
|||
var renderTransientTimer; |
|||
|
|||
function renderTransientData(internal) { |
|||
// This is in case this function is not called from timeout event
|
|||
if ( internal && renderTransientTimer ) { |
|||
clearTimeout(renderTransientTimer); |
|||
} |
|||
renderPageUrls(); |
|||
renderStats(); |
|||
renderTransientTimer = setTimeout(renderTransientData, 10000); // every 10s
|
|||
} |
|||
|
|||
/******************************************************************************/ |
|||
|
|||
function targetTabIdChangeHandler() { |
|||
targetTabId = this[this.selectedIndex].value; |
|||
renderStats(); |
|||
updateRequests(); |
|||
} |
|||
|
|||
/******************************************************************************/ |
|||
|
|||
function prepareToDie() { |
|||
changeValueHandler(uDom('#max-logged-requests'), 'maxLoggedRequests', 0, 999); |
|||
} |
|||
|
|||
/******************************************************************************/ |
|||
|
|||
var installEventHandlers = function() { |
|||
uDom('#refreshRequests').on('click', updateRequests); |
|||
uDom('#clearRequests').on('click', clearRequests); |
|||
uDom('input[id^="show-"][type="checkbox"]').on('change', changeFilterHandler); |
|||
uDom('#selectPageUrls').on('change', targetTabIdChangeHandler); |
|||
uDom('#max-logged-requests').on('change', function(){ changeValueHandler(uDom(this), 'maxLoggedRequests', 0, 999); }); |
|||
|
|||
// https://github.com/gorhill/httpswitchboard/issues/197
|
|||
window.addEventListener('beforeunload', prepareToDie); |
|||
}; |
|||
|
|||
/******************************************************************************/ |
|||
|
|||
uDom.onLoad(function(){ |
|||
// Initialize request filters as per user settings:
|
|||
// https://github.com/gorhill/httpswitchboard/issues/49
|
|||
var onResponseReceived = function(userSettings) { |
|||
// cache a copy
|
|||
cachedUserSettings = userSettings; |
|||
// init ui as per user settings
|
|||
uDom('#max-logged-requests').val(userSettings.maxLoggedRequests); |
|||
var statsFilters = userSettings.statsFilters; |
|||
uDom('input[id^="show-"][type="checkbox"]').forEach(function(input) { |
|||
var filter = statsFilters[input.attr('id')]; |
|||
input.prop('checked', filter === undefined || filter === true); |
|||
}); |
|||
|
|||
installEventHandlers(); |
|||
}; |
|||
messager.send({ what: 'getUserSettings' }, onResponseReceived); |
|||
|
|||
renderTransientData(true); |
|||
updateRequests(); |
|||
}); |
|||
|
|||
/******************************************************************************/ |
|||
|
|||
})(); |
Write
Preview
Loading…
Cancel
Save
Reference in new issue