Skip to content

Commit

Permalink
make use of CodeMirror for Whitelist pane
Browse files Browse the repository at this point in the history
  • Loading branch information
gorhill committed Mar 12, 2018
1 parent 42a0574 commit 9715d1e
Show file tree
Hide file tree
Showing 5 changed files with 123 additions and 147 deletions.
29 changes: 0 additions & 29 deletions src/css/whitelist.css
Original file line number Diff line number Diff line change
Expand Up @@ -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;
}
10 changes: 5 additions & 5 deletions src/js/messaging.js
Original file line number Diff line number Diff line change
Expand Up @@ -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':
Expand Down Expand Up @@ -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();
Expand Down
27 changes: 4 additions & 23 deletions src/js/ublock.js
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand All @@ -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;
Expand All @@ -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]*$/;

/******************************************************************************/

Expand Down
177 changes: 95 additions & 82 deletions src/js/whitelist.js
Original file line number Diff line number Diff line change
@@ -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
Expand All @@ -19,79 +19,94 @@
Home: https://github.com/gorhill/uBlock
*/

/* global uDom, uBlockDashboard */

/******************************************************************************/

(function() {
/* global CodeMirror, uDom, uBlockDashboard */

'use strict';

/******************************************************************************/

var messaging = vAPI.messaging,
cachedWhitelist = '';
(function() {

/******************************************************************************/

var getTextareaNode = function() {
var me = getTextareaNode,
node = me.theNode;
if ( node === undefined ) {
node = me.theNode = uDom.nodeFromSelector('#whitelist textarea');
}
return node;
};
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 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 reBadHostname,
reHostnameExtractor;

/******************************************************************************/

var whitelistChanged = (function() {
var changedWhitelist, changed, timer;
var messaging = vAPI.messaging,
cachedWhitelist = '',
noopFunc = function(){};

var cmEditor = new CodeMirror(
document.getElementById('whitelist'),
{
autofocus: true,
inputStyle: 'contenteditable',
lineNumbers: true,
lineWrapping: true,
styleActiveLine: true
}
);

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);
};
Expand All @@ -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);
Expand All @@ -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())
Expand All @@ -144,35 +158,35 @@ var exportWhitelistToFile = function() {
/******************************************************************************/

var applyChanges = function() {
cachedWhitelist = getTextareaNode().value.trim();
var request = {
what: 'setWhitelist',
whitelist: cachedWhitelist
};
messaging.send('dashboard', request, renderWhitelist);
cachedWhitelist = cmEditor.getValue().trim();
messaging.send(
'dashboard',
{
what: 'setWhitelist',
whitelist: cachedWhitelist
},
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;
Expand All @@ -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);

Expand Down
Loading

0 comments on commit 9715d1e

Please sign in to comment.