mirror of https://github.com/gorhill/uBlock.git
make use of CodeMirror for Whitelist pane
This commit is contained in:
parent
42a05746e5
commit
9715d1e8b9
|
@ -5,36 +5,7 @@ div > p:last-child {
|
|||
margin-bottom: 0;
|
||||
}
|
||||
#whitelist {
|
||||
border: 1px solid gray;
|
||||
height: 60vh;
|
||||
margin: 0;
|
||||
padding: 1px;
|
||||
position: relative;
|
||||
resize: vertical;
|
||||
}
|
||||
#whitelist.invalid {
|
||||
border-color: red;
|
||||
}
|
||||
#whitelist textarea {
|
||||
border: none;
|
||||
box-sizing: border-box;
|
||||
height: 100%;
|
||||
padding: 0.4em;
|
||||
resize: none;
|
||||
text-align: left;
|
||||
white-space: pre;
|
||||
width: 100%;
|
||||
}
|
||||
#whitelist textarea + div {
|
||||
background-color: red;
|
||||
bottom: 0;
|
||||
color: white;
|
||||
display: none;
|
||||
padding: 2px 4px;
|
||||
pointer-events: none;
|
||||
position: absolute;
|
||||
right: 0;
|
||||
}
|
||||
#whitelist.invalid textarea + div {
|
||||
display: block;
|
||||
}
|
||||
|
|
|
@ -135,7 +135,11 @@ var onMessage = function(request, sender, callback) {
|
|||
break;
|
||||
|
||||
case 'getWhitelist':
|
||||
response = µb.stringFromWhitelist(µb.netWhitelist);
|
||||
response = {
|
||||
whitelist: µb.stringFromWhitelist(µb.netWhitelist),
|
||||
reBadHostname: µb.reWhitelistBadHostname.source,
|
||||
reHostnameExtractor: µb.reWhitelistHostnameExtractor.source
|
||||
};
|
||||
break;
|
||||
|
||||
case 'launchElementPicker':
|
||||
|
@ -985,10 +989,6 @@ var onMessage = function(request, sender, callback) {
|
|||
resetUserData();
|
||||
break;
|
||||
|
||||
case 'validateWhitelistString':
|
||||
response = µb.validateWhitelistString(request.raw);
|
||||
break;
|
||||
|
||||
case 'writeHiddenSettings':
|
||||
µb.hiddenSettings = µb.hiddenSettingsFromString(request.content);
|
||||
µb.saveHiddenSettings();
|
||||
|
|
|
@ -219,7 +219,7 @@ var matchBucket = function(url, hostname, bucket, start) {
|
|||
}
|
||||
// Plain hostname
|
||||
else if ( line.indexOf('/') === -1 ) {
|
||||
if ( reInvalidHostname.test(line) ) {
|
||||
if ( this.reWhitelistBadHostname.test(line) ) {
|
||||
key = '#';
|
||||
directive = '# ' + line;
|
||||
} else {
|
||||
|
@ -242,7 +242,7 @@ var matchBucket = function(url, hostname, bucket, start) {
|
|||
// label (or else it would be just impossible to make an efficient
|
||||
// dict.
|
||||
else {
|
||||
matches = reHostnameExtractor.exec(line);
|
||||
matches = this.reWhitelistHostnameExtractor.exec(line);
|
||||
if ( !matches || matches.length !== 2 ) {
|
||||
key = '#';
|
||||
directive = '# ' + line;
|
||||
|
@ -266,27 +266,8 @@ var matchBucket = function(url, hostname, bucket, start) {
|
|||
return whitelist;
|
||||
};
|
||||
|
||||
µBlock.validateWhitelistString = function(s) {
|
||||
var lineIter = new this.LineIterator(s), line;
|
||||
while ( !lineIter.eot() ) {
|
||||
line = lineIter.next().trim();
|
||||
if ( line === '' ) { continue; }
|
||||
if ( line.startsWith('#') ) { continue; } // Comment
|
||||
if ( line.indexOf('/') === -1 ) { // Plain hostname
|
||||
if ( reInvalidHostname.test(line) ) { return false; }
|
||||
continue;
|
||||
}
|
||||
if ( line.length > 2 && line.startsWith('/') && line.endsWith('/') ) { // Regex-based
|
||||
try { new RegExp(line.slice(1, -1)); } catch(ex) { return false; }
|
||||
continue;
|
||||
}
|
||||
if ( reHostnameExtractor.test(line) === false ) { return false; } // URL
|
||||
}
|
||||
return true;
|
||||
};
|
||||
|
||||
var reInvalidHostname = /[^a-z0-9.\-\[\]:]/,
|
||||
reHostnameExtractor = /([a-z0-9\[][a-z0-9.\-]*[a-z0-9\]])(?::[\d*]+)?\/(?:[^\x00-\x20\/]|$)[^\x00-\x20]*$/;
|
||||
µBlock.reWhitelistBadHostname = /[^a-z0-9.\-\[\]:]/;
|
||||
µBlock.reWhitelistHostnameExtractor = /([a-z0-9\[][a-z0-9.\-]*[a-z0-9\]])(?::[\d*]+)?\/(?:[^\x00-\x20\/]|$)[^\x00-\x20]*$/;
|
||||
|
||||
/******************************************************************************/
|
||||
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
/*******************************************************************************
|
||||
|
||||
uBlock Origin - a browser extension to block requests.
|
||||
Copyright (C) 2014-2016 Raymond Hill
|
||||
Copyright (C) 2014-2018 Raymond Hill
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
|
@ -19,79 +19,94 @@
|
|||
Home: https://github.com/gorhill/uBlock
|
||||
*/
|
||||
|
||||
/* global uDom, uBlockDashboard */
|
||||
|
||||
/******************************************************************************/
|
||||
|
||||
(function() {
|
||||
/* global CodeMirror, uDom, uBlockDashboard */
|
||||
|
||||
'use strict';
|
||||
|
||||
/******************************************************************************/
|
||||
|
||||
(function() {
|
||||
|
||||
/******************************************************************************/
|
||||
|
||||
CodeMirror.defineMode("ubo-whitelist-directives", function() {
|
||||
var reComment = /^\s*#/,
|
||||
reRegex = /^\/.+\/$/;
|
||||
|
||||
return {
|
||||
token: function(stream) {
|
||||
var line = stream.string.trim();
|
||||
stream.skipToEnd();
|
||||
if ( reBadHostname === undefined ) {
|
||||
return null;
|
||||
}
|
||||
if ( reComment.test(line) ) {
|
||||
return 'comment';
|
||||
}
|
||||
if ( line.indexOf('/') === -1 ) {
|
||||
return reBadHostname.test(line) ? 'error' : null;
|
||||
}
|
||||
if ( reRegex.test(line) ) {
|
||||
try {
|
||||
new RegExp(line.slice(1, -1));
|
||||
} catch(ex) {
|
||||
return 'error';
|
||||
}
|
||||
return null;
|
||||
}
|
||||
return reHostnameExtractor.test(line) ? null : 'error';
|
||||
}
|
||||
};
|
||||
});
|
||||
|
||||
var reBadHostname,
|
||||
reHostnameExtractor;
|
||||
|
||||
/******************************************************************************/
|
||||
|
||||
var messaging = vAPI.messaging,
|
||||
cachedWhitelist = '';
|
||||
cachedWhitelist = '',
|
||||
noopFunc = function(){};
|
||||
|
||||
/******************************************************************************/
|
||||
|
||||
var getTextareaNode = function() {
|
||||
var me = getTextareaNode,
|
||||
node = me.theNode;
|
||||
if ( node === undefined ) {
|
||||
node = me.theNode = uDom.nodeFromSelector('#whitelist textarea');
|
||||
var cmEditor = new CodeMirror(
|
||||
document.getElementById('whitelist'),
|
||||
{
|
||||
autofocus: true,
|
||||
inputStyle: 'contenteditable',
|
||||
lineNumbers: true,
|
||||
lineWrapping: true,
|
||||
styleActiveLine: true
|
||||
}
|
||||
return node;
|
||||
};
|
||||
|
||||
var setErrorNodeHorizontalOffset = function(px) {
|
||||
var me = setErrorNodeHorizontalOffset,
|
||||
offset = me.theOffset || 0;
|
||||
if ( px === offset ) { return; }
|
||||
var node = me.theNode;
|
||||
if ( node === undefined ) {
|
||||
node = me.theNode = uDom.nodeFromSelector('#whitelist textarea + div');
|
||||
}
|
||||
node.style.right = px + 'px';
|
||||
me.theOffset = px;
|
||||
};
|
||||
|
||||
/******************************************************************************/
|
||||
|
||||
var whitelistChanged = (function() {
|
||||
var changedWhitelist, changed, timer;
|
||||
|
||||
var updateUI = function(good) {
|
||||
uDom.nodeFromId('whitelistApply').disabled = changed || !good;
|
||||
uDom.nodeFromId('whitelistRevert').disabled = changed;
|
||||
uDom.nodeFromId('whitelist').classList.toggle('invalid', !good);
|
||||
};
|
||||
|
||||
var validate = function() {
|
||||
timer = undefined;
|
||||
messaging.send(
|
||||
'dashboard',
|
||||
{ what: 'validateWhitelistString', raw: changedWhitelist },
|
||||
updateUI
|
||||
);
|
||||
|
||||
/******************************************************************************/
|
||||
|
||||
var whitelistChanged = function() {
|
||||
var whitelistElem = uDom.nodeFromId('whitelist');
|
||||
var bad = whitelistElem.querySelector('.cm-error') !== null;
|
||||
var changedWhitelist = cmEditor.getValue().trim();
|
||||
var changed = changedWhitelist !== cachedWhitelist;
|
||||
uDom.nodeFromId('whitelistApply').disabled = !changed || bad;
|
||||
uDom.nodeFromId('whitelistRevert').disabled = !changed;
|
||||
CodeMirror.commands.save = changed && !bad ? applyChanges : noopFunc;
|
||||
};
|
||||
|
||||
return function() {
|
||||
changedWhitelist = getTextareaNode().value.trim();
|
||||
changed = changedWhitelist === cachedWhitelist;
|
||||
if ( timer !== undefined ) { clearTimeout(timer); }
|
||||
timer = vAPI.setTimeout(validate, 251);
|
||||
var textarea = getTextareaNode();
|
||||
setErrorNodeHorizontalOffset(textarea.offsetWidth - textarea.clientWidth);
|
||||
};
|
||||
})();
|
||||
cmEditor.on('changes', whitelistChanged);
|
||||
|
||||
/******************************************************************************/
|
||||
|
||||
var renderWhitelist = function() {
|
||||
var onRead = function(whitelist) {
|
||||
cachedWhitelist = whitelist.trim();
|
||||
getTextareaNode().value = cachedWhitelist + '\n';
|
||||
whitelistChanged();
|
||||
var onRead = function(details) {
|
||||
var first = reBadHostname === undefined;
|
||||
if ( first ) {
|
||||
reBadHostname = new RegExp(details.reBadHostname);
|
||||
reHostnameExtractor = new RegExp(details.reHostnameExtractor);
|
||||
}
|
||||
cachedWhitelist = details.whitelist.trim();
|
||||
cmEditor.setValue(cachedWhitelist + '\n');
|
||||
if ( first ) {
|
||||
cmEditor.clearHistory();
|
||||
}
|
||||
};
|
||||
messaging.send('dashboard', { what: 'getWhitelist' }, onRead);
|
||||
};
|
||||
|
@ -100,17 +115,16 @@ var renderWhitelist = function() {
|
|||
|
||||
var handleImportFilePicker = function() {
|
||||
var fileReaderOnLoadHandler = function() {
|
||||
var textarea = getTextareaNode();
|
||||
textarea.value = [textarea.value.trim(), this.result.trim()].join('\n').trim();
|
||||
whitelistChanged();
|
||||
cmEditor.setValue(
|
||||
[
|
||||
cmEditor.getValue().trim(),
|
||||
this.result.trim()
|
||||
].join('\n').trim()
|
||||
);
|
||||
};
|
||||
var file = this.files[0];
|
||||
if ( file === undefined || file.name === '' ) {
|
||||
return;
|
||||
}
|
||||
if ( file.type.indexOf('text') !== 0 ) {
|
||||
return;
|
||||
}
|
||||
if ( file === undefined || file.name === '' ) { return; }
|
||||
if ( file.type.indexOf('text') !== 0 ) { return; }
|
||||
var fr = new FileReader();
|
||||
fr.onload = fileReaderOnLoadHandler;
|
||||
fr.readAsText(file);
|
||||
|
@ -130,7 +144,7 @@ var startImportFilePicker = function() {
|
|||
/******************************************************************************/
|
||||
|
||||
var exportWhitelistToFile = function() {
|
||||
var val = getTextareaNode().value.trim();
|
||||
var val = cmEditor.getValue().trim();
|
||||
if ( val === '' ) { return; }
|
||||
var filename = vAPI.i18n('whitelistExportFilename')
|
||||
.replace('{{datetime}}', uBlockDashboard.dateNowToSensibleString())
|
||||
|
@ -144,35 +158,35 @@ var exportWhitelistToFile = function() {
|
|||
/******************************************************************************/
|
||||
|
||||
var applyChanges = function() {
|
||||
cachedWhitelist = getTextareaNode().value.trim();
|
||||
var request = {
|
||||
cachedWhitelist = cmEditor.getValue().trim();
|
||||
messaging.send(
|
||||
'dashboard',
|
||||
{
|
||||
what: 'setWhitelist',
|
||||
whitelist: cachedWhitelist
|
||||
};
|
||||
messaging.send('dashboard', request, renderWhitelist);
|
||||
},
|
||||
renderWhitelist
|
||||
);
|
||||
};
|
||||
|
||||
var revertChanges = function() {
|
||||
getTextareaNode().value = cachedWhitelist + '\n';
|
||||
whitelistChanged();
|
||||
var content = cachedWhitelist;
|
||||
if ( content !== '' ) { content += '\n'; }
|
||||
cmEditor.setValue(content);
|
||||
};
|
||||
|
||||
/******************************************************************************/
|
||||
|
||||
var getCloudData = function() {
|
||||
return getTextareaNode().value;
|
||||
return cmEditor.getValue();
|
||||
};
|
||||
|
||||
var setCloudData = function(data, append) {
|
||||
if ( typeof data !== 'string' ) {
|
||||
return;
|
||||
}
|
||||
var textarea = getTextareaNode();
|
||||
if ( typeof data !== 'string' ) { return; }
|
||||
if ( append ) {
|
||||
data = uBlockDashboard.mergeNewLines(textarea.value.trim(), data);
|
||||
data = uBlockDashboard.mergeNewLines(cmEditor.getValue().trim(), data);
|
||||
}
|
||||
textarea.value = data.trim() + '\n';
|
||||
whitelistChanged();
|
||||
cmEditor.setValue(data.trim() + '\n');
|
||||
};
|
||||
|
||||
self.cloud.onPush = getCloudData;
|
||||
|
@ -183,7 +197,6 @@ self.cloud.onPull = setCloudData;
|
|||
uDom('#importWhitelistFromFile').on('click', startImportFilePicker);
|
||||
uDom('#importFilePicker').on('change', handleImportFilePicker);
|
||||
uDom('#exportWhitelistToFile').on('click', exportWhitelistToFile);
|
||||
uDom('#whitelist textarea').on('input', whitelistChanged);
|
||||
uDom('#whitelistApply').on('click', applyChanges);
|
||||
uDom('#whitelistRevert').on('click', revertChanges);
|
||||
|
||||
|
|
|
@ -4,10 +4,15 @@
|
|||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<title>uBlock — Whitelist</title>
|
||||
<link rel="stylesheet" type="text/css" href="css/common.css">
|
||||
<link rel="stylesheet" type="text/css" href="css/dashboard-common.css">
|
||||
<link rel="stylesheet" type="text/css" href="css/cloud-ui.css">
|
||||
<link rel="stylesheet" type="text/css" href="css/whitelist.css">
|
||||
|
||||
<link rel="stylesheet" href="lib/codemirror/lib/codemirror.css">
|
||||
<link rel="stylesheet" href="lib/codemirror/addon/search/matchesonscrollbar.css">
|
||||
|
||||
<link rel="stylesheet" href="css/common.css">
|
||||
<link rel="stylesheet" href="css/dashboard-common.css">
|
||||
<link rel="stylesheet" href="css/cloud-ui.css">
|
||||
<link rel="stylesheet" href="css/whitelist.css">
|
||||
<link rel="stylesheet" href="css/codemirror.css">
|
||||
</head>
|
||||
|
||||
<body>
|
||||
|
@ -18,16 +23,22 @@
|
|||
<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><section id="whitelist">
|
||||
<textarea dir="auto" spellcheck="false"></textarea>
|
||||
<div>E</div>
|
||||
</section>
|
||||
<p><div id="whitelist" class="codeMirrorContainer"></div>
|
||||
<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">
|
||||
|
||||
<script src="lib/codemirror/lib/codemirror.js"></script>
|
||||
<script src="lib/codemirror/addon/display/panel.js"></script>
|
||||
<script src="lib/codemirror/addon/scroll/annotatescrollbar.js"></script>
|
||||
<script src="lib/codemirror/addon/search/matchesonscrollbar.js"></script>
|
||||
<script src="lib/codemirror/addon/search/searchcursor.js"></script>
|
||||
<script src="lib/codemirror/addon/selection/active-line.js"></script>
|
||||
<script src="lib/punycode.js"></script>
|
||||
|
||||
<script src="js/codemirror/search.js"></script>
|
||||
|
||||
<script src="js/vapi.js"></script>
|
||||
<script src="js/vapi-common.js"></script>
|
||||
<script src="js/vapi-client.js"></script>
|
||||
|
|
Loading…
Reference in New Issue