add ability to filter out rules in "My rules" pane

This commit is contained in:
Raymond Hill 2018-03-21 07:24:52 -04:00
parent 4f2d071137
commit 6871d9aed4
No known key found for this signature in database
GPG key ID: 25E1490B761470C2
5 changed files with 140 additions and 57 deletions

View file

@ -34,7 +34,7 @@ body {
font-weight: normal;
margin: 0.5em 0;
}
#diff .ruleFilter {
#ruleFilter {
text-align: center;
}
body[dir="ltr"] #revertButton:after {

View file

@ -5,7 +5,6 @@
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>uBlock — Dynamic filtering rules</title>
<link rel="stylesheet" type="text/css" href="lib/codemirror/lib/codemirror.css">
<link rel="stylesheet" type="text/css" href="lib/codemirror/addon/dialog/dialog.css">
<link rel="stylesheet" type="text/css" href="lib/codemirror/addon/merge/merge.css">
<link rel="stylesheet" type="text/css" href="css/common.css">
@ -33,7 +32,7 @@
<button type="button" class="custom" id="importButton" data-i18n="rulesImport"></button>
<button type="button" class="custom" id="editSaveButton" data-i18n="rulesEditSave"></button>
</div>
<!-- TO BE IMPLEMENTED: <div class="ruleFilter"><span class="fa">&#xf0b0;</span>&emsp;<input type="text" size="32"></div> -->
<div id="ruleFilter"><span class="fa">&#xf0b0;</span>&ensp;<input type="text" size="32"></div>
</div>
<div class="codeMirrorContainer codeMirrorMergeContainer"></div>
</div>

View file

@ -53,7 +53,7 @@
var searchWidgetHtml =
'<div class="cm-search-widget">' +
'<span class="fa">&#xf002;</span>&emsp;' +
'<span class="fa">&#xf002;</span>&ensp;' +
'<span>' +
'<input type="text" size="32">' +
'<span class="cm-search-widget-count">' +

View file

@ -36,6 +36,7 @@ var mergeView = new CodeMirror.MergeView(
{
allowEditingOriginals: true,
connect: 'align',
inputStyle: 'contenteditable',
lineNumbers: true,
lineWrapping: false,
origLeft: '',
@ -47,21 +48,63 @@ mergeView.editor().setOption('styleActiveLine', true);
mergeView.editor().setOption('lineNumbers', false);
mergeView.leftOriginal().setOption('readOnly', 'nocursor');
var cleanToken = 0;
var unfilteredRules = {
orig: { doc: mergeView.leftOriginal(), rules: [] },
edit: { doc: mergeView.editor(), rules: [] }
};
var cleanEditToken = 0;
var cleanEditText = '';
var differ;
/******************************************************************************/
// Borrowed from...
// https://github.com/codemirror/CodeMirror/blob/3e1bb5fff682f8f6cbfaef0e56c61d62403d4798/addon/search/search.js#L22
// ... and modified as needed.
var updateOverlay = (function() {
var reFilter;
var mode = {
token: function(stream) {
if ( reFilter !== undefined ) {
reFilter.lastIndex = stream.pos;
var match = reFilter.exec(stream.string);
if ( match !== null ) {
if ( match.index === stream.pos ) {
stream.pos += match[0].length || 1;
return 'searching';
}
stream.pos = match.index;
return;
}
}
stream.skipToEnd();
}
};
return function(filter) {
reFilter = typeof filter === 'string' && filter !== '' ?
new RegExp(filter.replace(/[.*+?^${}()|[\]\\]/g, '\\$&'), 'gi') :
undefined;
return mode;
};
})();
/******************************************************************************/
// Incrementally update text in a CodeMirror editor for best user experience:
// - Scroll position preserved
// - Minimum amount of text updated
var rulesToDoc = function(doc, rules) {
if ( doc.getValue() === '' || rules.length === 0 ) {
var rulesToDoc = function(clearHistory) {
for ( var key in unfilteredRules ) {
if ( unfilteredRules.hasOwnProperty(key) === false ) { continue; }
var doc = unfilteredRules[key].doc;
var rules = filterRules(key);
if ( doc.lineCount() === 1 && doc.getValue() === '' || rules.length === 0 ) {
doc.setValue(rules.length !== 0 ? rules.join('\n') : '');
return;
continue;
}
if ( differ === undefined ) { differ = new diff_match_patch(); }
var beforeText = doc.getValue();
@ -87,6 +130,29 @@ var rulesToDoc = function(doc, rules) {
doc.replaceRange('', beg, end);
}
doc.endOperation();
}
cleanEditText = mergeView.editor().getValue().trim();
cleanEditToken = mergeView.editor().changeGeneration();
if ( clearHistory ) {
mergeView.editor().clearHistory();
}
};
/******************************************************************************/
var filterRules = function(key) {
var rules = unfilteredRules[key].rules;
var filter = uDom('#ruleFilter input').val();
if ( filter !== '' ) {
rules = rules.slice();
var i = rules.length;
while ( i-- ) {
if ( rules[i].indexOf(filter) === -1 ) {
rules.splice(i, 1);
}
}
}
return rules;
};
/******************************************************************************/
@ -98,18 +164,16 @@ var renderRules = (function() {
details.hnSwitches.sort();
details.permanentRules.sort();
details.sessionRules.sort();
var orig = details.hnSwitches.concat(details.permanentRules),
edit = details.hnSwitches.concat(details.sessionRules);
rulesToDoc(mergeView.leftOriginal(), orig);
rulesToDoc(mergeView.editor(), edit);
cleanEditText = mergeView.editor().getValue().trim();
unfilteredRules.orig.rules =
details.hnSwitches.concat(details.permanentRules);
unfilteredRules.edit.rules =
details.hnSwitches.concat(details.sessionRules);
rulesToDoc(firstVisit);
if ( firstVisit ) {
mergeView.editor().clearHistory();
firstVisit = false;
mergeView.editor().execCommand('goNextDiff');
}
cleanToken = mergeView.editor().changeGeneration();
onChange(true);
onTextChanged(true);
};
})();
@ -157,19 +221,14 @@ mergeView.options.revertChunk = function(
{ line: toStart.line, ch: 0 },
{ line: toEnd.line, ch: 0 }
);
applyDiff(from === mv.editor(), toAdd, toRemove);
to.replaceRange(toAdd, toStart, toEnd);
cleanToken = mergeView.editor().changeGeneration();
cleanEditText = mergeView.editor().getValue().trim();
applyDiff(from === mv.editor(), toAdd, toRemove, renderRules);
};
/******************************************************************************/
function handleImportFilePicker() {
var fileReaderOnLoadHandler = function() {
if ( typeof this.result !== 'string' || this.result === '' ) {
return;
}
if ( typeof this.result !== 'string' || this.result === '' ) { return; }
// https://github.com/chrisaljoudi/uBlock/issues/757
// Support RequestPolicy rule syntax
var result = this.result;
@ -217,41 +276,64 @@ function exportUserRulesToFile() {
/******************************************************************************/
/*
var onFilter = (function() {
var timer;
var onFilterChanged = (function() {
var timer,
overlay = null,
last = '';
var process = function() {
timer = undefined;
if ( mergeView.editor().isClean(cleanEditToken) === false ) { return; }
if ( overlay !== null ) {
mergeView.leftOriginal().removeOverlay(overlay);
mergeView.editor().removeOverlay(overlay);
overlay = null;
}
var filter = uDom('#ruleFilter input').val();
if ( filter === last ) { return; }
last = filter;
if ( filter !== '' ) {
overlay = updateOverlay(filter);
mergeView.leftOriginal().addOverlay(overlay);
mergeView.editor().addOverlay(overlay);
}
rulesToDoc(true);
};
return function() {
if ( timer !== undefined ) { clearTimeout(timer); }
timer = vAPI.setTimeout(process, 577);
timer = vAPI.setTimeout(process, 773);
};
})();
*/
/******************************************************************************/
var onChange = (function() {
var onTextChanged = (function() {
var timer;
var process = function(now) {
timer = undefined;
var isClean = mergeView.editor().isClean(cleanToken);
var isClean = mergeView.editor().isClean(cleanEditToken);
var diff = document.getElementById('diff');
if (
now &&
isClean === false &&
mergeView.editor().getValue().trim() === cleanEditText
) {
cleanToken = mergeView.editor().changeGeneration();
cleanEditToken = mergeView.editor().changeGeneration();
isClean = true;
}
diff.classList.toggle('editing', isClean === false);
diff.classList.toggle('dirty', mergeView.leftChunks().length !== 0);
CodeMirror.commands.save = isClean ? undefined : editSaveHandler;
var input = document.querySelector('#ruleFilter input');
if ( isClean ) {
input.removeAttribute('disabled');
CodeMirror.commands.save = undefined;
} else {
input.setAttribute('disabled', '');
CodeMirror.commands.save = editSaveHandler;
}
};
return function(now) {
@ -308,7 +390,7 @@ var editSaveHandler = function() {
var editor = mergeView.editor();
var editText = editor.getValue().trim();
if ( editText === cleanEditText ) {
onChange(true);
onTextChanged(true);
return;
}
if ( differ === undefined ) { differ = new diff_match_patch(); }
@ -354,9 +436,10 @@ uDom('#exportButton').on('click', exportUserRulesToFile);
uDom('#revertButton').on('click', revertAllHandler);
uDom('#commitButton').on('click', commitAllHandler);
uDom('#editSaveButton').on('click', editSaveHandler);
uDom('#ruleFilter input').on('input', onFilterChanged);
// https://groups.google.com/forum/#!topic/codemirror/UQkTrt078Vs
mergeView.editor().on('updateDiff', function() { onChange(); });
mergeView.editor().on('updateDiff', function() { onTextChanged(); });
/******************************************************************************/

View file

@ -664,6 +664,7 @@
function getChunks(diff) {
var chunks = [];
if (!diff.length) return chunks;
var startEdit = 0, startOrig = 0;
var edit = Pos(0, 0), orig = Pos(0, 0);
for (var i = 0; i < diff.length; ++i) {