mirror of https://github.com/gorhill/uBlock.git
sync feature (#80): draft
This commit is contained in:
parent
a7712116a7
commit
690421aead
|
@ -53,12 +53,14 @@ vAPI.app.restart = function() {
|
||||||
chrome.runtime.reload();
|
chrome.runtime.reload();
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/******************************************************************************/
|
||||||
/******************************************************************************/
|
/******************************************************************************/
|
||||||
|
|
||||||
// chrome.storage.local.get(null, function(bin){ console.debug('%o', bin); });
|
// chrome.storage.local.get(null, function(bin){ console.debug('%o', bin); });
|
||||||
|
|
||||||
vAPI.storage = chrome.storage.local;
|
vAPI.storage = chrome.storage.local;
|
||||||
|
|
||||||
|
/******************************************************************************/
|
||||||
/******************************************************************************/
|
/******************************************************************************/
|
||||||
|
|
||||||
// https://github.com/gorhill/uMatrix/issues/234
|
// https://github.com/gorhill/uMatrix/issues/234
|
||||||
|
@ -101,6 +103,7 @@ vAPI.browserSettings = {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/******************************************************************************/
|
||||||
/******************************************************************************/
|
/******************************************************************************/
|
||||||
|
|
||||||
vAPI.tabs = {};
|
vAPI.tabs = {};
|
||||||
|
@ -499,6 +502,7 @@ vAPI.tabs.injectScript = function(tabId, details, callback) {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/******************************************************************************/
|
||||||
/******************************************************************************/
|
/******************************************************************************/
|
||||||
|
|
||||||
// Must read: https://code.google.com/p/chromium/issues/detail?id=410868#c8
|
// Must read: https://code.google.com/p/chromium/issues/detail?id=410868#c8
|
||||||
|
@ -534,6 +538,7 @@ vAPI.setIcon = function(tabId, iconStatus, badge) {
|
||||||
chrome.browserAction.setIcon({ tabId: tabId, path: iconPaths }, onIconReady);
|
chrome.browserAction.setIcon({ tabId: tabId, path: iconPaths }, onIconReady);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/******************************************************************************/
|
||||||
/******************************************************************************/
|
/******************************************************************************/
|
||||||
|
|
||||||
vAPI.messaging = {
|
vAPI.messaging = {
|
||||||
|
@ -751,6 +756,7 @@ vAPI.messaging.broadcast = function(message) {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/******************************************************************************/
|
||||||
/******************************************************************************/
|
/******************************************************************************/
|
||||||
|
|
||||||
vAPI.net = {};
|
vAPI.net = {};
|
||||||
|
@ -875,6 +881,7 @@ vAPI.net.registerListeners = function() {
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/******************************************************************************/
|
||||||
/******************************************************************************/
|
/******************************************************************************/
|
||||||
|
|
||||||
vAPI.contextMenu = {
|
vAPI.contextMenu = {
|
||||||
|
@ -890,12 +897,14 @@ vAPI.contextMenu = {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/******************************************************************************/
|
||||||
/******************************************************************************/
|
/******************************************************************************/
|
||||||
|
|
||||||
vAPI.lastError = function() {
|
vAPI.lastError = function() {
|
||||||
return chrome.runtime.lastError;
|
return chrome.runtime.lastError;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/******************************************************************************/
|
||||||
/******************************************************************************/
|
/******************************************************************************/
|
||||||
|
|
||||||
// This is called only once, when everything has been loaded in memory after
|
// This is called only once, when everything has been loaded in memory after
|
||||||
|
@ -947,6 +956,7 @@ vAPI.onLoadAllCompleted = function() {
|
||||||
chrome.tabs.query({ url: '<all_urls>' }, bindToTabs);
|
chrome.tabs.query({ url: '<all_urls>' }, bindToTabs);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/******************************************************************************/
|
||||||
/******************************************************************************/
|
/******************************************************************************/
|
||||||
|
|
||||||
vAPI.punycodeHostname = function(hostname) {
|
vAPI.punycodeHostname = function(hostname) {
|
||||||
|
@ -957,6 +967,196 @@ vAPI.punycodeURL = function(url) {
|
||||||
return url;
|
return url;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/******************************************************************************/
|
||||||
|
/******************************************************************************/
|
||||||
|
|
||||||
|
vAPI.cloud = (function() {
|
||||||
|
var chunkCountPerFetch = 16; // Must be a power of 2
|
||||||
|
|
||||||
|
// Mind chrome.storage.sync.MAX_ITEMS (512 at time of writing)
|
||||||
|
var maxChunkCountPerItem = Math.floor(512 * 0.75) & ~(chunkCountPerFetch - 1);
|
||||||
|
|
||||||
|
// Mind chrome.storage.sync.QUOTA_BYTES_PER_ITEM (8192 at time of writing)
|
||||||
|
var maxChunkSize = Math.floor(chrome.storage.sync.QUOTA_BYTES_PER_ITEM * 0.75);
|
||||||
|
|
||||||
|
// Mind chrome.storage.sync.QUOTA_BYTES_PER_ITEM (8192 at time of writing)
|
||||||
|
var maxStorageSize = chrome.storage.sync.QUOTA_BYTES;
|
||||||
|
|
||||||
|
var options = {
|
||||||
|
deviceName: ''
|
||||||
|
};
|
||||||
|
|
||||||
|
var getDeviceName = function() {
|
||||||
|
// Assign a permanent user-friendly id to this uBlock instance if one does
|
||||||
|
// not exist. This will allow to have some sort of identifier for a user
|
||||||
|
// to possibly identify the source of cloud data.
|
||||||
|
var name = window.localStorage.getItem('deviceName') || '';
|
||||||
|
if ( name !== '' ) {
|
||||||
|
return name;
|
||||||
|
}
|
||||||
|
|
||||||
|
return window.navigator.platform;
|
||||||
|
};
|
||||||
|
|
||||||
|
// This is used to find out a rough count of how many chunks exists:
|
||||||
|
// We "poll" at specific index in order to get a rough idea of how
|
||||||
|
// large is the stored string.
|
||||||
|
// This allows reading a single item with only 2 sync operations -- a
|
||||||
|
// good thing given chrome.storage.syncMAX_WRITE_OPERATIONS_PER_MINUTE
|
||||||
|
// and chrome.storage.syncMAX_WRITE_OPERATIONS_PER_HOUR.
|
||||||
|
var getCoarseChunkCount = function(dataKey, callback) {
|
||||||
|
var bin = {};
|
||||||
|
for ( var i = 0; i < maxChunkCountPerItem; i += 16 ) {
|
||||||
|
bin[dataKey + i.toString()] = '';
|
||||||
|
}
|
||||||
|
|
||||||
|
chrome.storage.sync.get(bin, function(bin) {
|
||||||
|
if ( chrome.runtime.lastError ) {
|
||||||
|
callback(0, chrome.runtime.lastError.message);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Could loop backward... let's assume for now
|
||||||
|
// maxChunkCountPerItem could be something else than a
|
||||||
|
// multiple of 16.
|
||||||
|
var chunkCount = 0;
|
||||||
|
for ( var i = 0; i < maxChunkCountPerItem; i += 16 ) {
|
||||||
|
if ( bin[dataKey + i.toString()] === '' ) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
chunkCount = i + 16;
|
||||||
|
}
|
||||||
|
|
||||||
|
callback(chunkCount);
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
var deleteChunks = function(dataKey, start) {
|
||||||
|
var keys = [];
|
||||||
|
|
||||||
|
// No point in deleting more than:
|
||||||
|
// - The max number of chunks per item
|
||||||
|
// - The max number of chunks per storage limit
|
||||||
|
var n = Math.min(
|
||||||
|
maxChunkCountPerItem,
|
||||||
|
Math.ceil(maxStorageSize / maxChunkSize)
|
||||||
|
);
|
||||||
|
for ( var i = start; i < n; i++ ) {
|
||||||
|
keys.push(dataKey + i.toString());
|
||||||
|
}
|
||||||
|
chrome.storage.sync.remove(keys);
|
||||||
|
};
|
||||||
|
|
||||||
|
var start = function(/* dataKeys */) {
|
||||||
|
};
|
||||||
|
|
||||||
|
var push = function(dataKey, data, callback) {
|
||||||
|
var item = JSON.stringify({
|
||||||
|
'source': getDeviceName(),
|
||||||
|
'tstamp': Date.now(),
|
||||||
|
'data': data
|
||||||
|
});
|
||||||
|
|
||||||
|
// Chunkify taking into account QUOTA_BYTES_PER_ITEM:
|
||||||
|
// https://developer.chrome.com/extensions/storage#property-sync
|
||||||
|
// "The maximum size (in bytes) of each individual item in sync
|
||||||
|
// "storage, as measured by the JSON stringification of its value
|
||||||
|
// "plus its key length."
|
||||||
|
var bin = {};
|
||||||
|
var chunkCount = Math.ceil(item.length / maxChunkSize);
|
||||||
|
for ( var i = 0; i < chunkCount; i++ ) {
|
||||||
|
bin[dataKey + i.toString()] = item.substr(i * maxChunkSize, maxChunkSize);
|
||||||
|
}
|
||||||
|
bin[dataKey + i.toString()] = ''; // Sentinel
|
||||||
|
|
||||||
|
chrome.storage.sync.set(bin, function() {
|
||||||
|
var errorStr;
|
||||||
|
if ( chrome.runtime.lastError ) {
|
||||||
|
errorStr = chrome.runtime.lastError.message;
|
||||||
|
}
|
||||||
|
callback(errorStr);
|
||||||
|
|
||||||
|
// Remove potentially unused trailing chunks
|
||||||
|
deleteChunks(dataKey, chunkCount);
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
var pull = function(dataKey, callback) {
|
||||||
|
var assembleChunks = function(bin) {
|
||||||
|
if ( chrome.runtime.lastError ) {
|
||||||
|
callback(null, chrome.runtime.lastError.message);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Assemble chunks into a single string.
|
||||||
|
var json = [], jsonSlice;
|
||||||
|
var i = 0;
|
||||||
|
for (;;) {
|
||||||
|
jsonSlice = bin[dataKey + i.toString()];
|
||||||
|
if ( jsonSlice === '' ) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
json.push(jsonSlice);
|
||||||
|
i += 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
var entry = null;
|
||||||
|
try {
|
||||||
|
entry = JSON.parse(json.join(''));
|
||||||
|
} catch(ex) {
|
||||||
|
}
|
||||||
|
callback(entry);
|
||||||
|
};
|
||||||
|
|
||||||
|
var fetchChunks = function(coarseCount, errorStr) {
|
||||||
|
if ( coarseCount === 0 || typeof errorStr === 'string' ) {
|
||||||
|
callback(null, errorStr);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
var bin = {};
|
||||||
|
for ( var i = 0; i < coarseCount; i++ ) {
|
||||||
|
bin[dataKey + i.toString()] = '';
|
||||||
|
}
|
||||||
|
|
||||||
|
chrome.storage.sync.get(bin, assembleChunks);
|
||||||
|
};
|
||||||
|
|
||||||
|
getCoarseChunkCount(dataKey, fetchChunks);
|
||||||
|
};
|
||||||
|
|
||||||
|
var getOptions = function(callback) {
|
||||||
|
if ( typeof callback !== 'function' ) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
callback(options);
|
||||||
|
};
|
||||||
|
|
||||||
|
var setOptions = function(details, callback) {
|
||||||
|
if ( typeof details !== 'object' || details === null ) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ( typeof details.deviceName === 'string' ) {
|
||||||
|
window.localStorage.setItem('deviceName', details.deviceName);
|
||||||
|
}
|
||||||
|
|
||||||
|
if ( typeof callback === 'function' ) {
|
||||||
|
callback(options);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
return {
|
||||||
|
start: start,
|
||||||
|
push: push,
|
||||||
|
pull: pull,
|
||||||
|
getOptions: getOptions,
|
||||||
|
setOptions: setOptions
|
||||||
|
};
|
||||||
|
})();
|
||||||
|
|
||||||
|
/******************************************************************************/
|
||||||
/******************************************************************************/
|
/******************************************************************************/
|
||||||
|
|
||||||
})();
|
})();
|
||||||
|
|
|
@ -2807,6 +2807,7 @@ vAPI.contextMenu.remove = function() {
|
||||||
this.onCommand = null;
|
this.onCommand = null;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/******************************************************************************/
|
||||||
/******************************************************************************/
|
/******************************************************************************/
|
||||||
|
|
||||||
var optionsObserver = {
|
var optionsObserver = {
|
||||||
|
@ -2849,6 +2850,7 @@ var optionsObserver = {
|
||||||
|
|
||||||
optionsObserver.register();
|
optionsObserver.register();
|
||||||
|
|
||||||
|
/******************************************************************************/
|
||||||
/******************************************************************************/
|
/******************************************************************************/
|
||||||
|
|
||||||
vAPI.lastError = function() {
|
vAPI.lastError = function() {
|
||||||
|
@ -2875,6 +2877,7 @@ vAPI.onLoadAllCompleted = function() {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/******************************************************************************/
|
||||||
/******************************************************************************/
|
/******************************************************************************/
|
||||||
|
|
||||||
// Likelihood is that we do not have to punycode: given punycode overhead,
|
// Likelihood is that we do not have to punycode: given punycode overhead,
|
||||||
|
@ -2894,6 +2897,120 @@ vAPI.punycodeURL = function(url) {
|
||||||
return url;
|
return url;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/******************************************************************************/
|
||||||
|
/******************************************************************************/
|
||||||
|
|
||||||
|
vAPI.cloud = (function() {
|
||||||
|
var extensionBranchPath = 'extensions.' + location.host;
|
||||||
|
var cloudBranchPath = extensionBranchPath + '.cloudStorage';
|
||||||
|
|
||||||
|
var options = {
|
||||||
|
deviceName: ''
|
||||||
|
};
|
||||||
|
|
||||||
|
var getDeviceName = function() {
|
||||||
|
var name = '';
|
||||||
|
|
||||||
|
// User-supplied device name.
|
||||||
|
var branch = Services.prefs.getBranch(extensionBranchPath + '.');
|
||||||
|
try {
|
||||||
|
name = branch.getCharPref('deviceName');
|
||||||
|
} catch(ex) {
|
||||||
|
}
|
||||||
|
options.deviceName = name;
|
||||||
|
if ( name !== '' ) {
|
||||||
|
return name;
|
||||||
|
}
|
||||||
|
|
||||||
|
// No name: try to use device name specified by user in Preferences.
|
||||||
|
branch = Services.prefs.getBranch('services.sync.client.');
|
||||||
|
try {
|
||||||
|
name = branch.getCharPref('name');
|
||||||
|
} catch(ex) {
|
||||||
|
}
|
||||||
|
if ( name !== '' ) {
|
||||||
|
return name;
|
||||||
|
}
|
||||||
|
|
||||||
|
// No name: use os/cpu.
|
||||||
|
return window.navigator.platform || window.navigator.oscpu;
|
||||||
|
};
|
||||||
|
|
||||||
|
var start = function(dataKeys) {
|
||||||
|
var extensionBranch = Services.prefs.getBranch(extensionBranchPath + '.');
|
||||||
|
var syncBranch = Services.prefs.getBranch('services.sync.prefs.sync.');
|
||||||
|
|
||||||
|
// Mark config entries as syncable
|
||||||
|
var dataKey;
|
||||||
|
for ( var i = 0; i < dataKeys.length; i++ ) {
|
||||||
|
dataKey = dataKeys[i];
|
||||||
|
if ( extensionBranch.prefHasUserValue('cloudStorage.' + dataKey) === false ) {
|
||||||
|
extensionBranch.setCharPref('cloudStorage.' + dataKey, '');
|
||||||
|
}
|
||||||
|
syncBranch.setBoolPref(cloudBranchPath + '.' + dataKey, true);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
var push = function(datakey, data, callback) {
|
||||||
|
var branch = Services.prefs.getBranch(cloudBranchPath + '.');
|
||||||
|
var bin = {
|
||||||
|
'source': getDeviceName(),
|
||||||
|
'tstamp': Date.now(),
|
||||||
|
'data': data
|
||||||
|
};
|
||||||
|
branch.setCharPref(datakey, JSON.stringify(bin));
|
||||||
|
if ( typeof callback === 'function' ) {
|
||||||
|
callback();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
var pull = function(datakey, callback) {
|
||||||
|
var result = null;
|
||||||
|
var branch = Services.prefs.getBranch(cloudBranchPath + '.');
|
||||||
|
try {
|
||||||
|
var json = branch.getCharPref(datakey);
|
||||||
|
if ( typeof json === 'string' ) {
|
||||||
|
result = JSON.parse(json);
|
||||||
|
}
|
||||||
|
} catch(ex) {
|
||||||
|
}
|
||||||
|
callback(result);
|
||||||
|
};
|
||||||
|
|
||||||
|
var getOptions = function(callback) {
|
||||||
|
if ( typeof callback !== 'function' ) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
callback(options);
|
||||||
|
};
|
||||||
|
|
||||||
|
var setOptions = function(details, callback) {
|
||||||
|
if ( typeof details !== 'object' || details === null ) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
var branch = Services.prefs.getBranch(extensionBranchPath + '.');
|
||||||
|
|
||||||
|
if ( typeof details.deviceName === 'string' ) {
|
||||||
|
branch.setCharPref('deviceName', details.deviceName);
|
||||||
|
}
|
||||||
|
|
||||||
|
if ( typeof callback === 'function' ) {
|
||||||
|
callback(options);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
return {
|
||||||
|
start: start,
|
||||||
|
push: push,
|
||||||
|
pull: pull,
|
||||||
|
getOptions: getOptions,
|
||||||
|
setOptions: setOptions
|
||||||
|
};
|
||||||
|
})();
|
||||||
|
|
||||||
|
/******************************************************************************/
|
||||||
/******************************************************************************/
|
/******************************************************************************/
|
||||||
|
|
||||||
})();
|
})();
|
||||||
|
|
|
@ -5,11 +5,14 @@
|
||||||
<title>uBlock — Your filters</title>
|
<title>uBlock — Your filters</title>
|
||||||
<link rel="stylesheet" type="text/css" href="css/common.css">
|
<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/dashboard-common.css">
|
||||||
|
<link rel="stylesheet" type="text/css" href="css/cloud-ui.css">
|
||||||
<link rel="stylesheet" type="text/css" href="css/1p-filters.css">
|
<link rel="stylesheet" type="text/css" href="css/1p-filters.css">
|
||||||
</head>
|
</head>
|
||||||
|
|
||||||
<body>
|
<body>
|
||||||
|
|
||||||
|
<div id="cloudWidget" class="hide" data-cloud-entry="myFiltersPane"></div>
|
||||||
|
|
||||||
<p data-i18n="1pFormatHint"></p>
|
<p data-i18n="1pFormatHint"></p>
|
||||||
<p><button id="userFiltersApply" class="important" type="button" disabled="true" data-i18n="1pApplyChanges"></button></p>
|
<p><button id="userFiltersApply" class="important" type="button" disabled="true" data-i18n="1pApplyChanges"></button></p>
|
||||||
<textarea class="userFilters" id="userFilters" dir="auto" spellcheck="false"></textarea>
|
<textarea class="userFilters" id="userFilters" dir="auto" spellcheck="false"></textarea>
|
||||||
|
@ -22,6 +25,7 @@
|
||||||
<script src="js/udom.js"></script>
|
<script src="js/udom.js"></script>
|
||||||
<script src="js/i18n.js"></script>
|
<script src="js/i18n.js"></script>
|
||||||
<script src="js/dashboard-common.js"></script>
|
<script src="js/dashboard-common.js"></script>
|
||||||
|
<script src="js/cloud-ui.js"></script>
|
||||||
<script src="js/1p-filters.js"></script>
|
<script src="js/1p-filters.js"></script>
|
||||||
|
|
||||||
</body>
|
</body>
|
||||||
|
|
|
@ -5,22 +5,27 @@
|
||||||
<title>uBlock — Ubiquitous rules</title>
|
<title>uBlock — Ubiquitous rules</title>
|
||||||
<link rel="stylesheet" type="text/css" href="css/common.css">
|
<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/dashboard-common.css">
|
||||||
|
<link rel="stylesheet" type="text/css" href="css/cloud-ui.css">
|
||||||
<link rel="stylesheet" type="text/css" href="css/3p-filters.css">
|
<link rel="stylesheet" type="text/css" href="css/3p-filters.css">
|
||||||
</head>
|
</head>
|
||||||
|
|
||||||
<body>
|
<body>
|
||||||
|
|
||||||
<button id="buttonApply" class="important disabled" data-i18n="3pApplyChanges"></button>
|
<div id="cloudWidget" class="hide" data-cloud-entry="tpFiltersPane"></div>
|
||||||
<ul id="options">
|
|
||||||
<li><input type="checkbox" id="autoUpdate"><label data-i18n="3pAutoUpdatePrompt1" for="autoUpdate"></label> 
|
<div>
|
||||||
<button class="important disabled" id="buttonUpdate" data-i18n="3pUpdateNow"></button>
|
<button id="buttonApply" class="important disabled" data-i18n="3pApplyChanges"></button>
|
||||||
<button id="buttonPurgeAll" class="custom disabled" data-i18n="3pPurgeAll"></button>
|
<ul id="options">
|
||||||
<li><input type="checkbox" id="parseCosmeticFilters"><label data-i18n="3pParseAllABPHideFiltersPrompt1" for="parseCosmeticFilters"></label>
|
<li><input type="checkbox" id="autoUpdate"><label data-i18n="3pAutoUpdatePrompt1" for="autoUpdate"></label> 
|
||||||
<button class="whatisthis"></button>
|
<button class="important disabled" id="buttonUpdate" data-i18n="3pUpdateNow"></button>
|
||||||
<div class="whatisthis-expandable para" data-i18n="3pParseAllABPHideFiltersInfo"></div>
|
<button id="buttonPurgeAll" class="custom disabled" data-i18n="3pPurgeAll"></button>
|
||||||
<li><p id="listsOfBlockedHostsPrompt"></p>
|
<li><input type="checkbox" id="parseCosmeticFilters"><label data-i18n="3pParseAllABPHideFiltersPrompt1" for="parseCosmeticFilters"></label>
|
||||||
</ul>
|
<button class="whatisthis"></button>
|
||||||
<ul id="lists"></ul>
|
<div class="whatisthis-expandable para" data-i18n="3pParseAllABPHideFiltersInfo"></div>
|
||||||
|
<li><p id="listsOfBlockedHostsPrompt"></p>
|
||||||
|
</ul>
|
||||||
|
<ul id="lists"></ul>
|
||||||
|
</div>
|
||||||
|
|
||||||
<div id="externalListsDiv">
|
<div id="externalListsDiv">
|
||||||
<p data-i18n="3pExternalListsHint" style="margin: 0 0 0.25em 0; font-size: 13px;"></p>
|
<p data-i18n="3pExternalListsHint" style="margin: 0 0 0.25em 0; font-size: 13px;"></p>
|
||||||
|
@ -58,6 +63,7 @@
|
||||||
<script src="js/udom.js"></script>
|
<script src="js/udom.js"></script>
|
||||||
<script src="js/i18n.js"></script>
|
<script src="js/i18n.js"></script>
|
||||||
<script src="js/dashboard-common.js"></script>
|
<script src="js/dashboard-common.js"></script>
|
||||||
|
<script src="js/cloud-ui.js"></script>
|
||||||
<script src="js/3p-filters.js"></script>
|
<script src="js/3p-filters.js"></script>
|
||||||
|
|
||||||
</body>
|
</body>
|
||||||
|
|
|
@ -175,6 +175,10 @@
|
||||||
"message":"Color-blind friendly",
|
"message":"Color-blind friendly",
|
||||||
"description":"English: Color-blind friendly"
|
"description":"English: Color-blind friendly"
|
||||||
},
|
},
|
||||||
|
"settingsCloudStorageEnabledPrompt":{
|
||||||
|
"message":"Enable cloud storage support",
|
||||||
|
"description": ""
|
||||||
|
},
|
||||||
"settingsAdvancedUserPrompt":{
|
"settingsAdvancedUserPrompt":{
|
||||||
"message":"I am an advanced user (<a href='https:\/\/github.com\/gorhill\/uBlock\/wiki\/Advanced-user-features'>Required reading<\/a>)",
|
"message":"I am an advanced user (<a href='https:\/\/github.com\/gorhill\/uBlock\/wiki\/Advanced-user-features'>Required reading<\/a>)",
|
||||||
"description":"English: "
|
"description":"English: "
|
||||||
|
@ -591,6 +595,26 @@
|
||||||
"message": "Permanently",
|
"message": "Permanently",
|
||||||
"description": "English: Permanently"
|
"description": "English: Permanently"
|
||||||
},
|
},
|
||||||
|
"cloudPush": {
|
||||||
|
"message": "Export to cloud storage",
|
||||||
|
"description": ""
|
||||||
|
},
|
||||||
|
"cloudPull": {
|
||||||
|
"message": "Import from cloud storage",
|
||||||
|
"description": ""
|
||||||
|
},
|
||||||
|
"cloudNoData": {
|
||||||
|
"message": "...\n...",
|
||||||
|
"description": ""
|
||||||
|
},
|
||||||
|
"cloudDeviceNamePrompt": {
|
||||||
|
"message": "This device name:",
|
||||||
|
"description": ""
|
||||||
|
},
|
||||||
|
"genericSubmit": {
|
||||||
|
"message": "Submit",
|
||||||
|
"description": ""
|
||||||
|
},
|
||||||
"dummy":{
|
"dummy":{
|
||||||
"message":"This entry must be the last one",
|
"message":"This entry must be the last one",
|
||||||
"description":"so we dont need to deal with comma for last entry"
|
"description":"so we dont need to deal with comma for last entry"
|
||||||
|
|
|
@ -97,10 +97,8 @@ button.custom:hover {
|
||||||
}
|
}
|
||||||
#buttonApply {
|
#buttonApply {
|
||||||
display: initial;
|
display: initial;
|
||||||
margin: 1em 0;
|
|
||||||
position: fixed;
|
position: fixed;
|
||||||
right: 1em;
|
right: 1em;
|
||||||
top: 0;
|
|
||||||
z-index: 10;
|
z-index: 10;
|
||||||
}
|
}
|
||||||
body[dir=rtl] #buttonApply {
|
body[dir=rtl] #buttonApply {
|
||||||
|
|
|
@ -5,11 +5,14 @@
|
||||||
<title>uBlock — Dynamic filtering rules</title>
|
<title>uBlock — Dynamic filtering rules</title>
|
||||||
<link rel="stylesheet" type="text/css" href="css/common.css">
|
<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/dashboard-common.css">
|
||||||
|
<link rel="stylesheet" type="text/css" href="css/cloud-ui.css">
|
||||||
<link rel="stylesheet" type="text/css" href="css/dyna-rules.css">
|
<link rel="stylesheet" type="text/css" href="css/dyna-rules.css">
|
||||||
</head>
|
</head>
|
||||||
|
|
||||||
<body>
|
<body>
|
||||||
|
|
||||||
|
<div id="cloudWidget" class="hide" data-cloud-entry="myRulesPane"></div>
|
||||||
|
|
||||||
<p data-i18n="rulesHint"></p>
|
<p data-i18n="rulesHint"></p>
|
||||||
<p data-i18n="rulesFormatHint"></p>
|
<p data-i18n="rulesFormatHint"></p>
|
||||||
<div id="diff">
|
<div id="diff">
|
||||||
|
@ -51,6 +54,7 @@
|
||||||
<script src="js/udom.js"></script>
|
<script src="js/udom.js"></script>
|
||||||
<script src="js/i18n.js"></script>
|
<script src="js/i18n.js"></script>
|
||||||
<script src="js/dashboard-common.js"></script>
|
<script src="js/dashboard-common.js"></script>
|
||||||
|
<script src="js/cloud-ui.js"></script>
|
||||||
<script src="js/dyna-rules.js"></script>
|
<script src="js/dyna-rules.js"></script>
|
||||||
|
|
||||||
</body>
|
</body>
|
||||||
|
|
|
@ -40,10 +40,8 @@ var messager = vAPI.messaging.channel('1p-filters.js');
|
||||||
// This is to give a visual hint that the content of user blacklist has changed.
|
// This is to give a visual hint that the content of user blacklist has changed.
|
||||||
|
|
||||||
function userFiltersChanged() {
|
function userFiltersChanged() {
|
||||||
uDom('#userFiltersApply').prop(
|
uDom.nodeFromId('userFiltersApply').disabled =
|
||||||
'disabled',
|
uDom('#userFilters').val().trim() === cachedUserFilters;
|
||||||
uDom('#userFilters').val().trim() === cachedUserFilters
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/******************************************************************************/
|
/******************************************************************************/
|
||||||
|
@ -54,7 +52,7 @@ function renderUserFilters() {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
cachedUserFilters = details.content.trim();
|
cachedUserFilters = details.content.trim();
|
||||||
uDom('#userFilters').val(details.content);
|
uDom.nodeFromId('userFilters').value = details.content;
|
||||||
};
|
};
|
||||||
messager.send({ what: 'readUserFilters' }, onRead);
|
messager.send({ what: 'readUserFilters' }, onRead);
|
||||||
}
|
}
|
||||||
|
@ -160,16 +158,31 @@ var userFiltersApplyHandler = function() {
|
||||||
|
|
||||||
/******************************************************************************/
|
/******************************************************************************/
|
||||||
|
|
||||||
uDom.onLoad(function() {
|
var getCloudData = function() {
|
||||||
// Handle user interaction
|
return uDom.nodeFromId('userFilters').value;
|
||||||
uDom('#importUserFiltersFromFile').on('click', startImportFilePicker);
|
};
|
||||||
uDom('#importFilePicker').on('change', handleImportFilePicker);
|
|
||||||
uDom('#exportUserFiltersToFile').on('click', exportUserFiltersToFile);
|
|
||||||
uDom('#userFilters').on('input', userFiltersChanged);
|
|
||||||
uDom('#userFiltersApply').on('click', userFiltersApplyHandler);
|
|
||||||
|
|
||||||
renderUserFilters();
|
var setCloudData = function(data) {
|
||||||
});
|
if ( typeof data !== 'string' ) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
uDom.nodeFromId('userFilters').value = data;
|
||||||
|
userFiltersChanged();
|
||||||
|
};
|
||||||
|
|
||||||
|
self.cloud.onPush = getCloudData;
|
||||||
|
self.cloud.onPull = setCloudData;
|
||||||
|
|
||||||
|
/******************************************************************************/
|
||||||
|
|
||||||
|
// Handle user interaction
|
||||||
|
uDom('#importUserFiltersFromFile').on('click', startImportFilePicker);
|
||||||
|
uDom('#importFilePicker').on('change', handleImportFilePicker);
|
||||||
|
uDom('#exportUserFiltersToFile').on('click', exportUserFiltersToFile);
|
||||||
|
uDom('#userFilters').on('input', userFiltersChanged);
|
||||||
|
uDom('#userFiltersApply').on('click', userFiltersApplyHandler);
|
||||||
|
|
||||||
|
renderUserFilters();
|
||||||
|
|
||||||
/******************************************************************************/
|
/******************************************************************************/
|
||||||
|
|
||||||
|
|
|
@ -495,7 +495,7 @@ var externalListsChangeHandler = function() {
|
||||||
/******************************************************************************/
|
/******************************************************************************/
|
||||||
|
|
||||||
var externalListsApplyHandler = function() {
|
var externalListsApplyHandler = function() {
|
||||||
externalLists = uDom('#externalLists').val();
|
externalLists = uDom.nodeFromId('externalLists').value;
|
||||||
messager.send({
|
messager.send({
|
||||||
what: 'userSettings',
|
what: 'userSettings',
|
||||||
name: 'externalLists',
|
name: 'externalLists',
|
||||||
|
@ -520,21 +520,69 @@ var groupEntryClickHandler = function() {
|
||||||
|
|
||||||
/******************************************************************************/
|
/******************************************************************************/
|
||||||
|
|
||||||
uDom.onLoad(function() {
|
var getCloudData = function() {
|
||||||
uDom('#autoUpdate').on('change', autoUpdateCheckboxChanged);
|
var bin = {
|
||||||
uDom('#parseCosmeticFilters').on('change', cosmeticSwitchChanged);
|
parseCosmeticFilters: uDom.nodeFromId('parseCosmeticFilters').checked,
|
||||||
uDom('#buttonApply').on('click', buttonApplyHandler);
|
selectedLists: [],
|
||||||
uDom('#buttonUpdate').on('click', buttonUpdateHandler);
|
externalLists: externalLists
|
||||||
uDom('#buttonPurgeAll').on('click', buttonPurgeAllHandler);
|
};
|
||||||
uDom('#lists').on('change', '.listEntry > input', onListCheckboxChanged);
|
|
||||||
uDom('#lists').on('click', 'span.purge', onPurgeClicked);
|
|
||||||
uDom('#externalLists').on('input', externalListsChangeHandler);
|
|
||||||
uDom('#externalListsApply').on('click', externalListsApplyHandler);
|
|
||||||
uDom('#lists').on('click', '.groupEntry > span', groupEntryClickHandler);
|
|
||||||
|
|
||||||
renderFilterLists();
|
var lis = uDom('#lists .listEntry'), li;
|
||||||
renderExternalLists();
|
var i = lis.length;
|
||||||
});
|
while ( i-- ) {
|
||||||
|
li = lis.at(i);
|
||||||
|
if ( li.descendants('input').prop('checked') ) {
|
||||||
|
bin.selectedLists.push(li.descendants('a').attr('data-listkey'));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return bin;
|
||||||
|
};
|
||||||
|
|
||||||
|
var setCloudData = function(data) {
|
||||||
|
if ( typeof data !== 'object' || data === null ) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
var checked = data.parseCosmeticFilters === true;
|
||||||
|
uDom.nodeFromId('parseCosmeticFilters').checked = checked;
|
||||||
|
listDetails.cosmetic = checked;
|
||||||
|
|
||||||
|
var lis = uDom('#lists .listEntry'), li, input, listKey;
|
||||||
|
var i = lis.length;
|
||||||
|
while ( i-- ) {
|
||||||
|
li = lis.at(i);
|
||||||
|
input = li.descendants('input');
|
||||||
|
listKey = li.descendants('a').attr('data-listkey');
|
||||||
|
checked = data.selectedLists.indexOf(listKey) !== -1;
|
||||||
|
input.prop('checked', checked);
|
||||||
|
listDetails.available[listKey].off = !checked;
|
||||||
|
}
|
||||||
|
|
||||||
|
uDom.nodeFromId('externalLists').value = data.externalLists || '';
|
||||||
|
|
||||||
|
renderWidgets();
|
||||||
|
externalListsChangeHandler();
|
||||||
|
};
|
||||||
|
|
||||||
|
self.cloud.onPush = getCloudData;
|
||||||
|
self.cloud.onPull = setCloudData;
|
||||||
|
|
||||||
|
/******************************************************************************/
|
||||||
|
|
||||||
|
uDom('#autoUpdate').on('change', autoUpdateCheckboxChanged);
|
||||||
|
uDom('#parseCosmeticFilters').on('change', cosmeticSwitchChanged);
|
||||||
|
uDom('#buttonApply').on('click', buttonApplyHandler);
|
||||||
|
uDom('#buttonUpdate').on('click', buttonUpdateHandler);
|
||||||
|
uDom('#buttonPurgeAll').on('click', buttonPurgeAllHandler);
|
||||||
|
uDom('#lists').on('change', '.listEntry > input', onListCheckboxChanged);
|
||||||
|
uDom('#lists').on('click', 'span.purge', onPurgeClicked);
|
||||||
|
uDom('#externalLists').on('input', externalListsChangeHandler);
|
||||||
|
uDom('#externalListsApply').on('click', externalListsApplyHandler);
|
||||||
|
uDom('#lists').on('click', '.groupEntry > span', groupEntryClickHandler);
|
||||||
|
|
||||||
|
renderFilterLists();
|
||||||
|
renderExternalLists();
|
||||||
|
|
||||||
/******************************************************************************/
|
/******************************************************************************/
|
||||||
|
|
||||||
|
|
|
@ -234,21 +234,39 @@ var editCancelHandler = function() {
|
||||||
|
|
||||||
/******************************************************************************/
|
/******************************************************************************/
|
||||||
|
|
||||||
uDom.onLoad(function() {
|
var getCloudData = function() {
|
||||||
// Handle user interaction
|
return rulesFromHTML('#diff .left li');
|
||||||
uDom('#importButton').on('click', startImportFilePicker);
|
};
|
||||||
uDom('#importFilePicker').on('change', handleImportFilePicker);
|
|
||||||
uDom('#exportButton').on('click', exportUserRulesToFile);
|
|
||||||
|
|
||||||
uDom('#revertButton').on('click', revertHandler);
|
var setCloudData = function(data) {
|
||||||
uDom('#commitButton').on('click', commitHandler);
|
if ( typeof data !== 'string' ) {
|
||||||
uDom('#editEnterButton').on('click', editStartHandler);
|
return;
|
||||||
uDom('#diff > .pane.right > .rulesContainer').on('dblclick', editStartHandler);
|
}
|
||||||
uDom('#editStopButton').on('click', editStopHandler);
|
var request = {
|
||||||
uDom('#editCancelButton').on('click', editCancelHandler);
|
'what': 'setSessionRules',
|
||||||
|
'rules': data
|
||||||
|
};
|
||||||
|
messager.send(request, renderRules);
|
||||||
|
};
|
||||||
|
|
||||||
messager.send({ what: 'getRules' }, renderRules);
|
self.cloud.onPush = getCloudData;
|
||||||
});
|
self.cloud.onPull = setCloudData;
|
||||||
|
|
||||||
|
/******************************************************************************/
|
||||||
|
|
||||||
|
// Handle user interaction
|
||||||
|
uDom('#importButton').on('click', startImportFilePicker);
|
||||||
|
uDom('#importFilePicker').on('change', handleImportFilePicker);
|
||||||
|
uDom('#exportButton').on('click', exportUserRulesToFile);
|
||||||
|
|
||||||
|
uDom('#revertButton').on('click', revertHandler);
|
||||||
|
uDom('#commitButton').on('click', commitHandler);
|
||||||
|
uDom('#editEnterButton').on('click', editStartHandler);
|
||||||
|
uDom('#diff > .pane.right > .rulesContainer').on('dblclick', editStartHandler);
|
||||||
|
uDom('#editStopButton').on('click', editStopHandler);
|
||||||
|
uDom('#editCancelButton').on('click', editCancelHandler);
|
||||||
|
|
||||||
|
messager.send({ what: 'getRules' }, renderRules);
|
||||||
|
|
||||||
/******************************************************************************/
|
/******************************************************************************/
|
||||||
|
|
||||||
|
|
|
@ -33,28 +33,31 @@
|
||||||
/******************************************************************************/
|
/******************************************************************************/
|
||||||
|
|
||||||
// Helper to deal with the i18n'ing of HTML files.
|
// Helper to deal with the i18n'ing of HTML files.
|
||||||
|
vAPI.i18n.render = function(context) {
|
||||||
|
uDom('[data-i18n]', context).forEach(function(elem) {
|
||||||
|
elem.html(vAPI.i18n(elem.attr('data-i18n')));
|
||||||
|
});
|
||||||
|
|
||||||
uDom('[data-i18n]').forEach(function(elem) {
|
uDom('[title]', context).forEach(function(elem) {
|
||||||
elem.html(vAPI.i18n(elem.attr('data-i18n')));
|
var title = vAPI.i18n(elem.attr('title'));
|
||||||
});
|
if ( title ) {
|
||||||
|
elem.attr('title', title);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
uDom('[title]').forEach(function(elem) {
|
uDom('[placeholder]', context).forEach(function(elem) {
|
||||||
var title = vAPI.i18n(elem.attr('title'));
|
elem.attr('placeholder', vAPI.i18n(elem.attr('placeholder')));
|
||||||
if ( title ) {
|
});
|
||||||
elem.attr('title', title);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
uDom('[placeholder]').forEach(function(elem) {
|
uDom('[data-i18n-tip]', context).forEach(function(elem) {
|
||||||
elem.attr('placeholder', vAPI.i18n(elem.attr('placeholder')));
|
elem.attr(
|
||||||
});
|
'data-tip',
|
||||||
|
vAPI.i18n(elem.attr('data-i18n-tip')).replace(/<br>/g, '\n').replace(/\n{3,}/g, '\n\n')
|
||||||
|
);
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
uDom('[data-i18n-tip]').forEach(function(elem) {
|
vAPI.i18n.render();
|
||||||
elem.attr(
|
|
||||||
'data-tip',
|
|
||||||
vAPI.i18n(elem.attr('data-i18n-tip')).replace(/<br>/g, '\n').replace(/\n{3,}/g, '\n\n')
|
|
||||||
);
|
|
||||||
});
|
|
||||||
|
|
||||||
/******************************************************************************/
|
/******************************************************************************/
|
||||||
|
|
||||||
|
|
|
@ -671,6 +671,68 @@ vAPI.messaging.listen('element-picker.js', onMessage);
|
||||||
/******************************************************************************/
|
/******************************************************************************/
|
||||||
/******************************************************************************/
|
/******************************************************************************/
|
||||||
|
|
||||||
|
// cloud-ui.js
|
||||||
|
|
||||||
|
(function() {
|
||||||
|
|
||||||
|
'use strict';
|
||||||
|
|
||||||
|
/******************************************************************************/
|
||||||
|
|
||||||
|
var µb = µBlock;
|
||||||
|
|
||||||
|
/******************************************************************************/
|
||||||
|
|
||||||
|
var onMessage = function(request, sender, callback) {
|
||||||
|
// Async
|
||||||
|
switch ( request.what ) {
|
||||||
|
case 'cloudGetOptions':
|
||||||
|
vAPI.cloud.getOptions(function(options) {
|
||||||
|
options.enabled = µb.userSettings.cloudStorageEnabled === true;
|
||||||
|
callback(options);
|
||||||
|
});
|
||||||
|
return;
|
||||||
|
|
||||||
|
case 'cloudSetOptions':
|
||||||
|
vAPI.cloud.setOptions(request.options, callback);
|
||||||
|
return;
|
||||||
|
|
||||||
|
case 'cloudPull':
|
||||||
|
return vAPI.cloud.pull(request.datakey, callback);
|
||||||
|
|
||||||
|
case 'cloudPush':
|
||||||
|
return vAPI.cloud.push(request.datakey, request.data, callback);
|
||||||
|
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Sync
|
||||||
|
var response;
|
||||||
|
|
||||||
|
switch ( request.what ) {
|
||||||
|
// For when cloud storage is disabled.
|
||||||
|
case 'cloudPull':
|
||||||
|
// fallthrough
|
||||||
|
case 'cloudPush':
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
return vAPI.messaging.UNHANDLED;
|
||||||
|
}
|
||||||
|
|
||||||
|
callback(response);
|
||||||
|
};
|
||||||
|
|
||||||
|
vAPI.messaging.listen('cloud-ui.js', onMessage);
|
||||||
|
|
||||||
|
/******************************************************************************/
|
||||||
|
|
||||||
|
})();
|
||||||
|
|
||||||
|
/******************************************************************************/
|
||||||
|
/******************************************************************************/
|
||||||
|
|
||||||
// 3p-filters.js
|
// 3p-filters.js
|
||||||
|
|
||||||
(function() {
|
(function() {
|
||||||
|
|
|
@ -186,6 +186,12 @@ var onUserSettingsReceived = function(details) {
|
||||||
changeUserSettings('colorBlindFriendly', this.checked);
|
changeUserSettings('colorBlindFriendly', this.checked);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
uDom('#cloud-storage-enabled')
|
||||||
|
.prop('checked', details.cloudStorageEnabled === true)
|
||||||
|
.on('change', function(){
|
||||||
|
changeUserSettings('cloudStorageEnabled', this.checked);
|
||||||
|
});
|
||||||
|
|
||||||
uDom('#advanced-user-enabled')
|
uDom('#advanced-user-enabled')
|
||||||
.prop('checked', details.advancedUserEnabled === true)
|
.prop('checked', details.advancedUserEnabled === true)
|
||||||
.on('change', function(){
|
.on('change', function(){
|
||||||
|
|
|
@ -66,6 +66,13 @@ var onAllReady = function() {
|
||||||
// for launch time.
|
// for launch time.
|
||||||
µb.assets.remoteFetchBarrier -= 1;
|
µb.assets.remoteFetchBarrier -= 1;
|
||||||
|
|
||||||
|
vAPI.cloud.start([
|
||||||
|
'tpFiltersPane',
|
||||||
|
'myFiltersPane',
|
||||||
|
'myRulesPane',
|
||||||
|
'whitelistPane'
|
||||||
|
]);
|
||||||
|
|
||||||
//quickProfiler.stop(0);
|
//quickProfiler.stop(0);
|
||||||
|
|
||||||
vAPI.onLoadAllCompleted();
|
vAPI.onLoadAllCompleted();
|
||||||
|
|
|
@ -96,10 +96,7 @@
|
||||||
/******************************************************************************/
|
/******************************************************************************/
|
||||||
|
|
||||||
µBlock.saveWhitelist = function() {
|
µBlock.saveWhitelist = function() {
|
||||||
var bin = {
|
this.keyvalSetOne('netWhitelist', this.stringFromWhitelist(this.netWhitelist));
|
||||||
'netWhitelist': this.stringFromWhitelist(this.netWhitelist)
|
|
||||||
};
|
|
||||||
vAPI.storage.set(bin);
|
|
||||||
this.netWhitelistModifyTime = Date.now();
|
this.netWhitelistModifyTime = Date.now();
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -156,7 +153,7 @@
|
||||||
/******************************************************************************/
|
/******************************************************************************/
|
||||||
|
|
||||||
µBlock.saveUserFilters = function(content, callback) {
|
µBlock.saveUserFilters = function(content, callback) {
|
||||||
return this.assets.put(this.userFiltersPath, content, callback);
|
this.assets.put(this.userFiltersPath, content, callback);
|
||||||
};
|
};
|
||||||
|
|
||||||
/******************************************************************************/
|
/******************************************************************************/
|
||||||
|
|
|
@ -41,7 +41,7 @@ var reUnwantedChars = /[\x00-\x09\x0b\x0c\x0e-\x1f!"$'()<>{}|\\^\[\]`~]/;
|
||||||
/******************************************************************************/
|
/******************************************************************************/
|
||||||
|
|
||||||
var whitelistChanged = function() {
|
var whitelistChanged = function() {
|
||||||
var s = uDom('#whitelist').val().trim();
|
var s = uDom.nodeFromId('whitelist').value.trim();
|
||||||
var bad = reUnwantedChars.test(s);
|
var bad = reUnwantedChars.test(s);
|
||||||
uDom('#whitelistApply').prop(
|
uDom('#whitelistApply').prop(
|
||||||
'disabled',
|
'disabled',
|
||||||
|
@ -55,7 +55,7 @@ var whitelistChanged = function() {
|
||||||
var renderWhitelist = function() {
|
var renderWhitelist = function() {
|
||||||
var onRead = function(whitelist) {
|
var onRead = function(whitelist) {
|
||||||
cachedWhitelist = whitelist;
|
cachedWhitelist = whitelist;
|
||||||
uDom('#whitelist').val(cachedWhitelist);
|
uDom.nodeFromId('whitelist').value = whitelist;
|
||||||
};
|
};
|
||||||
messager.send({ what: 'getWhitelist' }, onRead);
|
messager.send({ what: 'getWhitelist' }, onRead);
|
||||||
whitelistChanged();
|
whitelistChanged();
|
||||||
|
@ -122,15 +122,30 @@ var whitelistApplyHandler = function() {
|
||||||
|
|
||||||
/******************************************************************************/
|
/******************************************************************************/
|
||||||
|
|
||||||
uDom.onLoad(function() {
|
var getCloudData = function() {
|
||||||
uDom('#importWhitelistFromFile').on('click', startImportFilePicker);
|
return uDom.nodeFromId('whitelist').value;
|
||||||
uDom('#importFilePicker').on('change', handleImportFilePicker);
|
};
|
||||||
uDom('#exportWhitelistToFile').on('click', exportWhitelistToFile);
|
|
||||||
uDom('#whitelist').on('input', whitelistChanged);
|
|
||||||
uDom('#whitelistApply').on('click', whitelistApplyHandler);
|
|
||||||
|
|
||||||
renderWhitelist();
|
var setCloudData = function(data) {
|
||||||
});
|
if ( typeof data !== 'string' ) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
uDom.nodeFromId('whitelist').value = data;
|
||||||
|
whitelistChanged();
|
||||||
|
};
|
||||||
|
|
||||||
|
self.cloud.onPush = getCloudData;
|
||||||
|
self.cloud.onPull = setCloudData;
|
||||||
|
|
||||||
|
/******************************************************************************/
|
||||||
|
|
||||||
|
uDom('#importWhitelistFromFile').on('click', startImportFilePicker);
|
||||||
|
uDom('#importFilePicker').on('change', handleImportFilePicker);
|
||||||
|
uDom('#exportWhitelistToFile').on('click', exportWhitelistToFile);
|
||||||
|
uDom('#whitelist').on('input', whitelistChanged);
|
||||||
|
uDom('#whitelistApply').on('click', whitelistApplyHandler);
|
||||||
|
|
||||||
|
renderWhitelist();
|
||||||
|
|
||||||
/******************************************************************************/
|
/******************************************************************************/
|
||||||
|
|
||||||
|
|
|
@ -15,6 +15,7 @@
|
||||||
<li><input id="icon-badge" type="checkbox"><label data-i18n="settingsIconBadgePrompt" for="icon-badge"></label>
|
<li><input id="icon-badge" type="checkbox"><label data-i18n="settingsIconBadgePrompt" for="icon-badge"></label>
|
||||||
<li><input id="context-menu-enabled" type="checkbox"><label data-i18n="settingsContextMenuPrompt" for="context-menu-enabled"></label>
|
<li><input id="context-menu-enabled" type="checkbox"><label data-i18n="settingsContextMenuPrompt" for="context-menu-enabled"></label>
|
||||||
<li><input id="color-blind-friendly" type="checkbox"><label data-i18n="settingsColorBlindPrompt" for="color-blind-friendly"></label>
|
<li><input id="color-blind-friendly" type="checkbox"><label data-i18n="settingsColorBlindPrompt" for="color-blind-friendly"></label>
|
||||||
|
<li><input id="cloud-storage-enabled" type="checkbox"><label data-i18n="settingsCloudStorageEnabledPrompt" for="cloud-storage-enabled"></label>
|
||||||
<li><input id="advanced-user-enabled" type="checkbox"><label data-i18n="settingsAdvancedUserPrompt" for="advanced-user-enabled"></label>
|
<li><input id="advanced-user-enabled" type="checkbox"><label data-i18n="settingsAdvancedUserPrompt" for="advanced-user-enabled"></label>
|
||||||
<li class="subgroup"><span data-i18n="3pGroupPrivacy"></span><ul>
|
<li class="subgroup"><span data-i18n="3pGroupPrivacy"></span><ul>
|
||||||
<li><input id="prefetching-disabled" type="checkbox"><label data-i18n="settingsPrefetchingDisabledPrompt" for="prefetching-disabled"></label> <a class="fa info" href="https://wikipedia.org/wiki/Link_prefetching#Issues_and_criticisms" target="_blank"></a>
|
<li><input id="prefetching-disabled" type="checkbox"><label data-i18n="settingsPrefetchingDisabledPrompt" for="prefetching-disabled"></label> <a class="fa info" href="https://wikipedia.org/wiki/Link_prefetching#Issues_and_criticisms" target="_blank"></a>
|
||||||
|
|
|
@ -5,11 +5,14 @@
|
||||||
<title>uBlock — Whitelist</title>
|
<title>uBlock — Whitelist</title>
|
||||||
<link rel="stylesheet" type="text/css" href="css/common.css">
|
<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/dashboard-common.css">
|
||||||
|
<link rel="stylesheet" type="text/css" href="css/cloud-ui.css">
|
||||||
<link rel="stylesheet" type="text/css" href="css/whitelist.css">
|
<link rel="stylesheet" type="text/css" href="css/whitelist.css">
|
||||||
</head>
|
</head>
|
||||||
|
|
||||||
<body>
|
<body>
|
||||||
|
|
||||||
|
<div id="cloudWidget" class="hide" data-cloud-entry="whitelistPane"></div>
|
||||||
|
|
||||||
<p data-i18n="whitelistPrompt"></p>
|
<p data-i18n="whitelistPrompt"></p>
|
||||||
<p><button id="whitelistApply" class="important" type="button" disabled="true" data-i18n="whitelistApply"></button></p>
|
<p><button id="whitelistApply" class="important" type="button" disabled="true" data-i18n="whitelistApply"></button></p>
|
||||||
<textarea id="whitelist" dir="auto" spellcheck="false"></textarea>
|
<textarea id="whitelist" dir="auto" spellcheck="false"></textarea>
|
||||||
|
@ -23,6 +26,7 @@
|
||||||
<script src="js/udom.js"></script>
|
<script src="js/udom.js"></script>
|
||||||
<script src="js/i18n.js"></script>
|
<script src="js/i18n.js"></script>
|
||||||
<script src="js/dashboard-common.js"></script>
|
<script src="js/dashboard-common.js"></script>
|
||||||
|
<script src="js/cloud-ui.js"></script>
|
||||||
<script src="js/whitelist.js"></script>
|
<script src="js/whitelist.js"></script>
|
||||||
|
|
||||||
</body>
|
</body>
|
||||||
|
|
Loading…
Reference in New Issue