diff options
author | gorhill <rhill@raymondhill.net> | 2015-08-12 12:17:39 -0400 |
---|---|---|
committer | gorhill <rhill@raymondhill.net> | 2015-08-12 12:17:39 -0400 |
commit | f338c28cd64f5ae6d8a24d99bafb295a98e65083 (patch) | |
tree | fff8964e98a9a6488dd2fef4f58b41a1bedc42db /src | |
parent | 09790f30a2ad6915fe8effd06d6c76da5e3d445f (diff) | |
download | uBlock-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.html | 15 | ||||
-rw-r--r-- | src/3p-filters.html | 7 | ||||
-rw-r--r-- | src/_locales/en/messages.json | 4 | ||||
-rw-r--r-- | src/css/3p-filters.css | 22 | ||||
-rw-r--r-- | src/css/common.css | 32 | ||||
-rw-r--r-- | src/dyna-rules.html | 14 | ||||
-rw-r--r-- | src/js/1p-filters.js | 28 | ||||
-rw-r--r-- | src/js/cloud-ui.js | 6 | ||||
-rw-r--r-- | src/js/dashboard-common.js | 83 | ||||
-rw-r--r-- | src/js/dyna-rules.js | 7 | ||||
-rw-r--r-- | src/js/whitelist.js | 42 | ||||
-rw-r--r-- | src/settings.html | 6 | ||||
-rw-r--r-- | src/whitelist.html | 15 |
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>   - <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>  + <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>  + <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>  - <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>  - <button type="button" id="import" data-i18n="aboutRestoreDataButton"></button> + <p><button class="custom" type="button" id="export" data-i18n="aboutBackupDataButton"></button>  + <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>   - <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>  + <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>  + <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> |