aboutsummaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
authorgorhill <rhill@raymondhill.net>2015-08-12 12:17:39 -0400
committergorhill <rhill@raymondhill.net>2015-08-12 12:17:39 -0400
commitf338c28cd64f5ae6d8a24d99bafb295a98e65083 (patch)
treefff8964e98a9a6488dd2fef4f58b41a1bedc42db /src
parent09790f30a2ad6915fe8effd06d6c76da5e3d445f (diff)
downloaduBlock-f338c28cd64f5ae6d8a24d99bafb295a98e65083.zip
uBlock-f338c28cd64f5ae6d8a24d99bafb295a98e65083.tar.gz
uBlock-f338c28cd64f5ae6d8a24d99bafb295a98e65083.tar.bz2
support append from cloud storage + uniformize buttons visual in dashboard
Diffstat (limited to 'src')
-rw-r--r--src/1p-filters.html15
-rw-r--r--src/3p-filters.html7
-rw-r--r--src/_locales/en/messages.json4
-rw-r--r--src/css/3p-filters.css22
-rw-r--r--src/css/common.css32
-rw-r--r--src/dyna-rules.html14
-rw-r--r--src/js/1p-filters.js28
-rw-r--r--src/js/cloud-ui.js6
-rw-r--r--src/js/dashboard-common.js83
-rw-r--r--src/js/dyna-rules.js7
-rw-r--r--src/js/whitelist.js42
-rw-r--r--src/settings.html6
-rw-r--r--src/whitelist.html15
13 files changed, 198 insertions, 83 deletions
diff --git a/src/1p-filters.html b/src/1p-filters.html
index 83be070..0c5d01c 100644
--- a/src/1p-filters.html
+++ b/src/1p-filters.html
@@ -14,11 +14,16 @@
<div id="cloudWidget" class="hide" data-cloud-entry="myFiltersPane"></div>
<p data-i18n="1pFormatHint"></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>
-<p><button id="importUserFiltersFromFile" data-i18n="1pImport"></button> &emsp;
- <button id="exportUserFiltersToFile" data-i18n="1pExport"></button>
- <input id="importFilePicker" type="file" accept="text/plain" class="hiddenFileInput"></p>
+<p>
+ <button id="userFiltersApply" class="custom important" type="button" disabled="true" data-i18n="1pApplyChanges"></button>&ensp;
+ <button id="userFiltersRevert" class="custom" type="button" disabled="true" data-i18n="genericRevert"></button>
+</p>
+<p><textarea class="userFilters" id="userFilters" dir="auto" spellcheck="false"></textarea></p>
+<p>
+ <button id="importUserFiltersFromFile" class="custom" data-i18n="1pImport"></button>&ensp;
+ <button id="exportUserFiltersToFile" class="custom" data-i18n="1pExport"></button>
+ <input id="importFilePicker" type="file" accept="text/plain" class="hiddenFileInput">
+</p>
<script src="js/vapi-common.js"></script>
<script src="js/vapi-client.js"></script>
diff --git a/src/3p-filters.html b/src/3p-filters.html
index f19acfb..b62bc46 100644
--- a/src/3p-filters.html
+++ b/src/3p-filters.html
@@ -17,7 +17,7 @@
<button id="buttonApply" class="important disabled" data-i18n="3pApplyChanges"></button>
<ul id="options">
<li><input type="checkbox" id="autoUpdate"><label data-i18n="3pAutoUpdatePrompt1" for="autoUpdate"></label>&ensp;
- <button class="important disabled" id="buttonUpdate" data-i18n="3pUpdateNow"></button>
+ <button class="custom important disabled" id="buttonUpdate" data-i18n="3pUpdateNow"></button>
<button id="buttonPurgeAll" class="custom disabled" data-i18n="3pPurgeAll"></button>
<li><input type="checkbox" id="parseCosmeticFilters"><label data-i18n="3pParseAllABPHideFiltersPrompt1" for="parseCosmeticFilters"></label>
<button class="whatisthis"></button>
@@ -29,8 +29,9 @@
<div id="externalListsDiv">
<p data-i18n="3pExternalListsHint" style="margin: 0 0 0.25em 0; font-size: 13px;"></p>
-<textarea id="externalLists" dir="ltr" spellcheck="false"></textarea>
-<p style="margin: 0.25em 0 0 0"><button id="externalListsApply" class="important" disabled="true" data-i18n="3pExternalListsApply"></button></p>
+<p style="margin: 0.25em 0 0 0">
+ <textarea id="externalLists" dir="ltr" spellcheck="false"></textarea>
+ <button id="externalListsApply" class="custom important" disabled="true" data-i18n="3pExternalListsApply"></button></p>
</div>
<div id="busyOverlay">
diff --git a/src/_locales/en/messages.json b/src/_locales/en/messages.json
index 0fabac3..7c432fe 100644
--- a/src/_locales/en/messages.json
+++ b/src/_locales/en/messages.json
@@ -615,6 +615,10 @@
"message": "Submit",
"description": "for generic 'submit' buttons"
},
+ "genericRevert": {
+ "message": "Revert",
+ "description": "for generic 'revert' buttons"
+ },
"dummy":{
"message":"This entry must be the last one",
"description":"so we dont need to deal with comma for last entry"
diff --git a/src/css/3p-filters.css b/src/css/3p-filters.css
index 2533cdf..09942e1 100644
--- a/src/css/3p-filters.css
+++ b/src/css/3p-filters.css
@@ -73,28 +73,6 @@ li.listEntry > a:nth-of-type(3) {
.dim {
opacity: 0.5;
}
-/* I designed the button with: http://charliepark.org/bootstrap_buttons/ */
-button.custom {
- padding: 0.6em 1em;
- border: 1px solid transparent;
- border-color: #80b3ff #80b3ff hsl(216, 100%, 75%);
- border-radius: 3px;
- background-color: hsl(216, 100%, 75%);
- background-image: linear-gradient(#a8cbff, #80b3ff);
- background-repeat: repeat-x;
- color: #222;
- opacity: 0.8;
- }
-button.custom.disabled {
- border-color: #dddddd #dddddd hsl(36, 0%, 85%);
- background-color: hsl(36, 0%, 72%);
- background-image: linear-gradient(#f2f2f2, #dddddd);
- color: #aaa;
- pointer-events: none;
- }
-button.custom:hover {
- opacity: 1.0;
- }
#buttonApply {
display: initial;
position: fixed;
diff --git a/src/css/common.css b/src/css/common.css
index 5fea2eb..29b3e2c 100644
--- a/src/css/common.css
+++ b/src/css/common.css
@@ -17,6 +17,30 @@ body {
color: black;
font: 14px/1.3 sans-serif;
}
+/* I designed the button with: http://charliepark.org/bootstrap_buttons/ */
+button.custom {
+ padding: 0.6em 1em;
+ border: 1px solid transparent;
+ border-color: #ccc #ccc #bbb #bbb;
+ border-radius: 3px;
+ background-color: hsl(216, 0%, 75%);
+ background-image: linear-gradient(#f2f2f2, #dddddd);
+ background-repeat: repeat-x;
+ color: #000;
+ opacity: 0.8;
+ }
+button.custom.disabled,
+button.custom[disabled] {
+ border-color: #ddd #ddd hsl(36, 0%, 85%);
+ background-color: hsl(36, 0%, 72%);
+ background-image: linear-gradient(#f2f2f2, #dddddd);
+ color: #666;
+ opacity: 0.6;
+ pointer-events: none;
+ }
+button.custom:hover {
+ opacity: 1.0;
+ }
button.important {
padding: 0.6em 1em;
border: 1px solid transparent;
@@ -28,14 +52,6 @@ button.important {
color: #222;
opacity: 0.8;
}
-button.important[disabled],
-button.important.disabled {
- border-color: #dddddd #dddddd hsl(36, 0%, 85%);
- background-color: hsl(36, 0%, 72%);
- background-image: linear-gradient(#f2f2f2, #dddddd);
- color: #888;
- pointer-events: none;
- }
button.important:hover {
opacity: 1.0;
}
diff --git a/src/dyna-rules.html b/src/dyna-rules.html
index 3ae22cf..cb81f99 100644
--- a/src/dyna-rules.html
+++ b/src/dyna-rules.html
@@ -19,8 +19,8 @@
<div class="pane left">
<div class="ruleActions">
<h2 data-i18n="rulesPermanentHeader"></h2>
- <button type="button" id="exportButton" data-i18n="rulesExport"></button>
- <button type="button" id="revertButton" data-i18n="rulesRevert"></button>
+ <button type="button" class="custom" id="exportButton" data-i18n="rulesExport"></button>
+ <button type="button" class="custom" id="revertButton" data-i18n="rulesRevert"></button>
</div>
<div class="rulesContainer">
<ul></ul>
@@ -29,11 +29,11 @@
<div class="pane right">
<div class="ruleActions">
<h2 data-i18n="rulesTemporaryHeader"></h2>
- <button type="button" id="commitButton" data-i18n="rulesCommit"></button>
- <button type="button" id="editEnterButton" data-i18n="rulesEdit"></button>
- <button type="button" id="editStopButton" data-i18n="rulesEditSave"></button>
- <button type="button" id="editCancelButton" data-i18n="rulesEditDiscard"></button>
- <button type="button" id="importButton" data-i18n="rulesImport"></button>
+ <button type="button" class="custom" id="commitButton" data-i18n="rulesCommit"></button>
+ <button type="button" class="custom" id="editEnterButton" data-i18n="rulesEdit"></button>
+ <button type="button" class="custom" id="editStopButton" data-i18n="rulesEditSave"></button>
+ <button type="button" class="custom" id="editCancelButton" data-i18n="rulesEditDiscard"></button>
+ <button type="button" class="custom" id="importButton" data-i18n="rulesImport"></button>
</div>
<div class="rulesContainer">
<textarea spellcheck="false"></textarea>
diff --git a/src/js/1p-filters.js b/src/js/1p-filters.js
index 6182e9c..358f023 100644
--- a/src/js/1p-filters.js
+++ b/src/js/1p-filters.js
@@ -19,7 +19,7 @@
Home: https://github.com/gorhill/uBlock
*/
-/* global vAPI, uDom */
+/* global vAPI, uDom, uBlockDashboard */
/******************************************************************************/
@@ -40,8 +40,9 @@ var messager = vAPI.messaging.channel('1p-filters.js');
// This is to give a visual hint that the content of user blacklist has changed.
function userFiltersChanged() {
- uDom.nodeFromId('userFiltersApply').disabled =
- uDom('#userFilters').val().trim() === cachedUserFilters;
+ var changed = uDom.nodeFromId('userFilters').value.trim() !== cachedUserFilters;
+ uDom.nodeFromId('userFiltersApply').disabled = !changed;
+ uDom.nodeFromId('userFiltersRevert').disabled = !changed;
}
/******************************************************************************/
@@ -53,6 +54,7 @@ function renderUserFilters() {
}
cachedUserFilters = details.content.trim();
uDom.nodeFromId('userFilters').value = details.content;
+ userFiltersChanged();
};
messager.send({ what: 'readUserFilters' }, onRead);
}
@@ -133,14 +135,14 @@ var exportUserFiltersToFile = function() {
.replace('{{datetime}}', now.toLocaleString())
.replace(/ +/g, '_');
vAPI.download({
- 'url': 'data:text/plain;charset=utf-8,' + encodeURIComponent(val),
+ 'url': 'data:text/plain;charset=utf-8,' + encodeURIComponent(val + '\n'),
'filename': filename
});
};
/******************************************************************************/
-var userFiltersApplyHandler = function() {
+var applyChanges = function() {
var onWritten = function(details) {
if ( details.error ) {
return;
@@ -156,17 +158,26 @@ var userFiltersApplyHandler = function() {
messager.send(request, onWritten);
};
+var revertChanges = function() {
+ uDom.nodeFromId('userFilters').value = cachedUserFilters + '\n';
+ userFiltersChanged();
+};
+
/******************************************************************************/
var getCloudData = function() {
return uDom.nodeFromId('userFilters').value;
};
-var setCloudData = function(data) {
+var setCloudData = function(data, append) {
if ( typeof data !== 'string' ) {
return;
}
- uDom.nodeFromId('userFilters').value = data;
+ var textarea = uDom.nodeFromId('userFilters');
+ if ( append ) {
+ data = uBlockDashboard.mergeNewLines(textarea.value, data);
+ }
+ textarea.value = data;
userFiltersChanged();
};
@@ -180,7 +191,8 @@ 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);
+uDom('#userFiltersApply').on('click', applyChanges);
+uDom('#userFiltersRevert').on('click', revertChanges);
renderUserFilters();
diff --git a/src/js/cloud-ui.js b/src/js/cloud-ui.js
index 9bca1fb..f129dc7 100644
--- a/src/js/cloud-ui.js
+++ b/src/js/cloud-ui.js
@@ -110,9 +110,9 @@ var pushData = function() {
/******************************************************************************/
-var pullData = function() {
+var pullData = function(ev) {
if ( typeof self.cloud.onPull === 'function' ) {
- self.cloud.onPull(self.cloud.data);
+ self.cloud.onPull(self.cloud.data, ev.shiftKey);
}
};
@@ -196,4 +196,6 @@ messager.send({ what: 'cloudGetOptions' }, onInitialize);
/******************************************************************************/
+// https://www.youtube.com/watch?v=aQFp67VoiDA
+
})();
diff --git a/src/js/dashboard-common.js b/src/js/dashboard-common.js
index ef24457..e34e18a 100644
--- a/src/js/dashboard-common.js
+++ b/src/js/dashboard-common.js
@@ -24,7 +24,86 @@
/******************************************************************************/
-uDom.onLoad(function() {
+self.uBlockDashboard = self.uBlockDashboard || {};
+
+/******************************************************************************/
+
+// Helper for client panes:
+// Remove literal duplicate lines from a set based on another set.
+
+self.uBlock.mergeNewLines = function(text, newText) {
+ var lineBeg, textEnd, lineEnd;
+ var line, hash, bucket;
+
+ // Step 1: build dictionary for existing lines.
+ var fromDict = Object.create(null);
+ lineBeg = 0;
+ textEnd = text.length;
+ while ( lineBeg < textEnd ) {
+ lineEnd = text.indexOf('\n', lineBeg);
+ if ( lineEnd === -1 ) {
+ lineEnd = text.indexOf('\r', lineBeg);
+ if ( lineEnd === -1 ) {
+ lineEnd = textEnd;
+ }
+ }
+ line = text.slice(lineBeg, lineEnd).trim();
+ lineBeg = lineEnd + 1;
+ if ( line.length === 0 ) {
+ continue;
+ }
+ hash = line.slice(0, 8);
+ bucket = fromDict[hash];
+ if ( bucket === undefined ) {
+ fromDict[hash] = line;
+ } else if ( typeof bucket === 'string' ) {
+ fromDict[hash] = [bucket, line];
+ } else /* if ( Array.isArray(bucket) ) */ {
+ bucket.push(line);
+ }
+ }
+
+ // Step 2: use above dictionary to filter out duplicate lines.
+ var out = [ '' ];
+ lineBeg = 0;
+ textEnd = newText.length;
+ while ( lineBeg < textEnd ) {
+ lineEnd = newText.indexOf('\n', lineBeg);
+ if ( lineEnd === -1 ) {
+ lineEnd = newText.indexOf('\r', lineBeg);
+ if ( lineEnd === -1 ) {
+ lineEnd = textEnd;
+ }
+ }
+ line = newText.slice(lineBeg, lineEnd).trim();
+ lineBeg = lineEnd + 1;
+ if ( line.length === 0 ) {
+ if ( out[out.length - 1] !== '' ) {
+ out.push('');
+ }
+ continue;
+ }
+ bucket = fromDict[line.slice(0, 8)];
+ if ( bucket === undefined ) {
+ out.push(line);
+ continue;
+ }
+ if ( typeof bucket === 'string' && line !== bucket ) {
+ out.push(line);
+ continue;
+ }
+ if ( bucket.indexOf(line) === -1 ) {
+ out.push(line);
+ /* continue; */
+ }
+ }
+
+ return text.trim() + '\n' + out.join('\n');
+};
+
+/******************************************************************************/
+
+(function() {
// Open links in the proper window
uDom('a').attr('target', '_blank');
uDom('a[href*="dashboard.html"]').attr('target', '_parent');
@@ -34,4 +113,4 @@ uDom.onLoad(function() {
.descendants('.whatisthis-expandable')
.toggleClass('whatisthis-expanded');
});
-});
+})();
diff --git a/src/js/dyna-rules.js b/src/js/dyna-rules.js
index fa880f9..c017652 100644
--- a/src/js/dyna-rules.js
+++ b/src/js/dyna-rules.js
@@ -158,7 +158,7 @@ function exportUserRulesToFile() {
.replace('{{datetime}}', now.toLocaleString())
.replace(/ +/g, '_');
vAPI.download({
- 'url': 'data:text/plain,' + encodeURIComponent(rulesFromHTML('#diff .left li')),
+ 'url': 'data:text/plain,' + encodeURIComponent(rulesFromHTML('#diff .left li') + '\n'),
'filename': filename,
'saveAs': true
});
@@ -238,10 +238,13 @@ var getCloudData = function() {
return rulesFromHTML('#diff .left li');
};
-var setCloudData = function(data) {
+var setCloudData = function(data, append) {
if ( typeof data !== 'string' ) {
return;
}
+ if ( append ) {
+ data = rulesFromHTML('#diff .right li') + '\n' + data;
+ }
var request = {
'what': 'setSessionRules',
'rules': data
diff --git a/src/js/whitelist.js b/src/js/whitelist.js
index 0c7329e..96a733d 100644
--- a/src/js/whitelist.js
+++ b/src/js/whitelist.js
@@ -19,7 +19,7 @@
Home: https://github.com/gorhill/uBlock
*/
-/* global vAPI, uDom */
+/* global vAPI, uDom, uBlockDashboard */
/******************************************************************************/
@@ -41,24 +41,24 @@ var reUnwantedChars = /[\x00-\x09\x0b\x0c\x0e-\x1f!"$'()<>{}|\\^\[\]`~]/;
/******************************************************************************/
var whitelistChanged = function() {
- var s = uDom.nodeFromId('whitelist').value.trim();
+ var textarea = uDom.nodeFromId('whitelist');
+ var s = textarea.value.trim();
+ var changed = s === cachedWhitelist;
var bad = reUnwantedChars.test(s);
- uDom('#whitelistApply').prop(
- 'disabled',
- s === cachedWhitelist || bad
- );
- uDom('#whitelist').toggleClass('bad', bad);
+ uDom.nodeFromId('whitelistApply').disabled = changed || bad;
+ uDom.nodeFromId('whitelistRevert').disabled = changed;
+ textarea.classList.toggle('bad', bad);
};
/******************************************************************************/
var renderWhitelist = function() {
var onRead = function(whitelist) {
- cachedWhitelist = whitelist;
- uDom.nodeFromId('whitelist').value = whitelist;
+ cachedWhitelist = whitelist.trim();
+ uDom.nodeFromId('whitelist').value = cachedWhitelist + '\n';
+ whitelistChanged();
};
messager.send({ what: 'getWhitelist' }, onRead);
- whitelistChanged();
};
/******************************************************************************/
@@ -104,15 +104,15 @@ var exportWhitelistToFile = function() {
.replace('{{datetime}}', now.toLocaleString())
.replace(/ +/g, '_');
vAPI.download({
- 'url': 'data:text/plain;charset=utf-8,' + encodeURIComponent(val),
+ 'url': 'data:text/plain;charset=utf-8,' + encodeURIComponent(val + '\n'),
'filename': filename
});
};
/******************************************************************************/
-var whitelistApplyHandler = function() {
- cachedWhitelist = uDom('#whitelist').val().trim();
+var applyChanges = function() {
+ cachedWhitelist = uDom.nodeFromId('whitelist').value.trim();
var request = {
what: 'setWhitelist',
whitelist: cachedWhitelist
@@ -120,17 +120,26 @@ var whitelistApplyHandler = function() {
messager.send(request, renderWhitelist);
};
+var revertChanges = function() {
+ uDom.nodeFromId('whitelist').value = cachedWhitelist + '\n';
+ whitelistChanged();
+};
+
/******************************************************************************/
var getCloudData = function() {
return uDom.nodeFromId('whitelist').value;
};
-var setCloudData = function(data) {
+var setCloudData = function(data, append) {
if ( typeof data !== 'string' ) {
return;
}
- uDom.nodeFromId('whitelist').value = data;
+ var textarea = uDom.nodeFromId('whitelist');
+ if ( append ) {
+ data = uBlockDashboard.mergeNewLines(textarea.value.trim(), data);
+ }
+ textarea.value = data.trim() + '\n';
whitelistChanged();
};
@@ -143,7 +152,8 @@ 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);
+uDom('#whitelistApply').on('click', applyChanges);
+uDom('#whitelistRevert').on('click', revertChanges);
renderWhitelist();
diff --git a/src/settings.html b/src/settings.html
index e518813..25bd7d6 100644
--- a/src/settings.html
+++ b/src/settings.html
@@ -39,11 +39,11 @@
</div>
<div style="margin: 2.5em 1em;">
- <p><button type="button" id="export" data-i18n="aboutBackupDataButton"></button>&ensp;
- <button type="button" id="import" data-i18n="aboutRestoreDataButton"></button>
+ <p><button class="custom" type="button" id="export" data-i18n="aboutBackupDataButton"></button>&ensp;
+ <button class="custom" type="button" id="import" data-i18n="aboutRestoreDataButton"></button>
<input id="restoreFilePicker" type="file" accept="text/plain" class="hiddenFileInput">
<p>
- <p><button type="button" id="reset" data-i18n="aboutResetDataButton"></button>
+ <p><button class="custom" type="button" id="reset" data-i18n="aboutResetDataButton"></button>
</div>
<script src="js/vapi-common.js"></script>
diff --git a/src/whitelist.html b/src/whitelist.html
index 8d4555a..1be0893 100644
--- a/src/whitelist.html
+++ b/src/whitelist.html
@@ -14,11 +14,16 @@
<div id="cloudWidget" class="hide" data-cloud-entry="whitelistPane"></div>
<p data-i18n="whitelistPrompt"></p>
-<p><button id="whitelistApply" class="important" type="button" disabled="true" data-i18n="whitelistApply"></button></p>
-<textarea id="whitelist" dir="auto" spellcheck="false"></textarea>
-<p><button id="importWhitelistFromFile" data-i18n="whitelistImport"></button> &emsp;
- <button id="exportWhitelistToFile" data-i18n="whitelistExport"></button>
- <input id="importFilePicker" type="file" accept="text/plain" class="hiddenFileInput"></p>
+<p>
+ <button id="whitelistApply" class="custom important" type="button" disabled="true" data-i18n="whitelistApply"></button>&ensp;
+ <button id="whitelistRevert" class="custom" type="button" disabled="true" data-i18n="genericRevert"></button>
+</p>
+<p><textarea id="whitelist" dir="auto" spellcheck="false"></textarea></p>
+<p>
+ <button id="importWhitelistFromFile" class="custom" data-i18n="whitelistImport"></button>&ensp;
+ <button id="exportWhitelistToFile" class="custom" data-i18n="whitelistExport"></button>
+ <input id="importFilePicker" type="file" accept="text/plain" class="hiddenFileInput">
+</p>
<script src="lib/punycode.js"></script>
<script src="js/vapi-common.js"></script>