2014-12-31 23:26:17 +01:00
|
|
|
/*******************************************************************************
|
|
|
|
|
2016-03-06 16:51:06 +01:00
|
|
|
uBlock Origin - a browser extension to block requests.
|
2018-03-11 15:59:39 +01:00
|
|
|
Copyright (C) 2014-2018 Raymond Hill
|
2014-12-31 23:26:17 +01:00
|
|
|
|
|
|
|
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
|
|
|
|
the Free Software Foundation, either version 3 of the License, or
|
|
|
|
(at your option) any later version.
|
|
|
|
|
|
|
|
This program is distributed in the hope that it will be useful,
|
|
|
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
|
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
|
|
GNU General Public License for more details.
|
|
|
|
|
|
|
|
You should have received a copy of the GNU General Public License
|
|
|
|
along with this program. If not, see {http://www.gnu.org/licenses/}.
|
|
|
|
|
|
|
|
Home: https://github.com/gorhill/uMatrix
|
|
|
|
*/
|
|
|
|
|
2018-03-11 15:59:39 +01:00
|
|
|
/* global diff_match_patch, CodeMirror, uDom, uBlockDashboard */
|
|
|
|
|
|
|
|
'use strict';
|
2014-12-31 23:26:17 +01:00
|
|
|
|
|
|
|
/******************************************************************************/
|
|
|
|
|
|
|
|
(function() {
|
|
|
|
|
|
|
|
/******************************************************************************/
|
|
|
|
|
2016-03-06 16:51:06 +01:00
|
|
|
var messaging = vAPI.messaging;
|
2014-12-31 23:26:17 +01:00
|
|
|
|
2018-03-11 15:59:39 +01:00
|
|
|
var mergeView = new CodeMirror.MergeView(
|
|
|
|
document.querySelector('.codeMirrorMergeContainer'),
|
|
|
|
{
|
|
|
|
allowEditingOriginals: true,
|
2018-03-16 12:49:56 +01:00
|
|
|
connect: 'align',
|
2018-03-11 15:59:39 +01:00
|
|
|
lineNumbers: true,
|
|
|
|
lineWrapping: false,
|
|
|
|
origLeft: '',
|
|
|
|
revertButtons: true,
|
|
|
|
value: ''
|
2015-04-09 17:19:31 +02:00
|
|
|
}
|
2018-03-11 15:59:39 +01:00
|
|
|
);
|
|
|
|
mergeView.editor().setOption('styleActiveLine', true);
|
|
|
|
mergeView.editor().setOption('lineNumbers', false);
|
|
|
|
mergeView.leftOriginal().setOption('readOnly', 'nocursor');
|
2015-04-09 17:19:31 +02:00
|
|
|
|
2018-03-11 15:59:39 +01:00
|
|
|
var cleanToken = 0;
|
|
|
|
var cleanEditText = '';
|
2015-02-11 17:34:51 +01:00
|
|
|
|
2018-03-11 15:59:39 +01:00
|
|
|
var differ;
|
|
|
|
|
|
|
|
/******************************************************************************/
|
|
|
|
|
|
|
|
// 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 ) {
|
|
|
|
doc.setValue(rules.length !== 0 ? rules.join('\n') : '');
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
if ( differ === undefined ) { differ = new diff_match_patch(); }
|
|
|
|
var beforeText = doc.getValue();
|
|
|
|
var afterText = rules.join('\n');
|
|
|
|
var diffs = differ.diff_main(beforeText, afterText);
|
|
|
|
doc.startOperation();
|
|
|
|
var i = diffs.length,
|
|
|
|
iedit = beforeText.length;
|
2015-02-11 17:34:51 +01:00
|
|
|
while ( i-- ) {
|
2018-03-11 15:59:39 +01:00
|
|
|
var diff = diffs[i];
|
|
|
|
if ( diff[0] === 0 ) {
|
|
|
|
iedit -= diff[1].length;
|
2015-02-11 17:49:08 +01:00
|
|
|
continue;
|
|
|
|
}
|
2018-03-11 15:59:39 +01:00
|
|
|
var end = doc.posFromIndex(iedit);
|
|
|
|
if ( diff[0] === 1 ) {
|
|
|
|
doc.replaceRange(diff[1], end, end);
|
2015-02-11 17:49:08 +01:00
|
|
|
continue;
|
|
|
|
}
|
2018-03-11 15:59:39 +01:00
|
|
|
/* diff[0] === -1 */
|
|
|
|
iedit -= diff[1].length;
|
|
|
|
var beg = doc.posFromIndex(iedit);
|
|
|
|
doc.replaceRange('', beg, end);
|
2015-02-11 17:34:51 +01:00
|
|
|
}
|
2018-03-11 15:59:39 +01:00
|
|
|
doc.endOperation();
|
|
|
|
};
|
|
|
|
|
|
|
|
/******************************************************************************/
|
|
|
|
|
|
|
|
var renderRules = (function() {
|
|
|
|
var firstVisit = true;
|
|
|
|
|
|
|
|
return function(details) {
|
|
|
|
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();
|
|
|
|
if ( firstVisit ) {
|
|
|
|
mergeView.editor().clearHistory();
|
|
|
|
firstVisit = false;
|
|
|
|
mergeView.editor().execCommand('goNextDiff');
|
2015-02-11 17:34:51 +01:00
|
|
|
}
|
2018-03-11 15:59:39 +01:00
|
|
|
cleanToken = mergeView.editor().changeGeneration();
|
|
|
|
onChange(true);
|
|
|
|
};
|
|
|
|
})();
|
2014-12-31 23:26:17 +01:00
|
|
|
|
2018-03-11 15:59:39 +01:00
|
|
|
/******************************************************************************/
|
|
|
|
|
|
|
|
var applyDiff = function(permanent, toAdd, toRemove, callback) {
|
|
|
|
messaging.send(
|
|
|
|
'dashboard',
|
|
|
|
{
|
|
|
|
what: 'modifyRuleset',
|
|
|
|
permanent: permanent,
|
|
|
|
toAdd: toAdd,
|
|
|
|
toRemove: toRemove
|
|
|
|
},
|
|
|
|
callback
|
|
|
|
);
|
|
|
|
};
|
|
|
|
|
|
|
|
/******************************************************************************/
|
|
|
|
|
|
|
|
// CodeMirror quirk: sometimes fromStart.ch and/or toStart.ch is undefined.
|
|
|
|
// When this happens, use 0.
|
|
|
|
|
|
|
|
mergeView.options.revertChunk = function(
|
|
|
|
mv,
|
|
|
|
from, fromStart, fromEnd,
|
|
|
|
to, toStart, toEnd
|
|
|
|
) {
|
2018-03-16 23:33:50 +01:00
|
|
|
// https://github.com/gorhill/uBlock/issues/3611
|
|
|
|
if ( document.body.getAttribute('dir') === 'rtl' ) {
|
|
|
|
var tmp;
|
|
|
|
tmp = from; from = to; to = tmp;
|
|
|
|
tmp = fromStart; fromStart = toStart; toStart = tmp;
|
|
|
|
tmp = fromEnd; fromEnd = toEnd; toEnd = tmp;
|
|
|
|
}
|
2018-03-11 15:59:39 +01:00
|
|
|
if ( typeof fromStart.ch !== 'number' ) { fromStart.ch = 0; }
|
|
|
|
if ( fromEnd.ch !== 0 ) { fromEnd.line += 1; }
|
|
|
|
var toAdd = from.getRange(
|
|
|
|
{ line: fromStart.line, ch: 0 },
|
|
|
|
{ line: fromEnd.line, ch: 0 }
|
|
|
|
);
|
|
|
|
if ( typeof toStart.ch !== 'number' ) { toStart.ch = 0; }
|
|
|
|
if ( toEnd.ch !== 0 ) { toEnd.line += 1; }
|
|
|
|
var toRemove = to.getRange(
|
|
|
|
{ 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();
|
2014-12-31 23:26:17 +01:00
|
|
|
};
|
|
|
|
|
|
|
|
/******************************************************************************/
|
|
|
|
|
|
|
|
function handleImportFilePicker() {
|
|
|
|
var fileReaderOnLoadHandler = function() {
|
|
|
|
if ( typeof this.result !== 'string' || this.result === '' ) {
|
|
|
|
return;
|
|
|
|
}
|
2015-04-07 03:26:05 +02:00
|
|
|
// https://github.com/chrisaljoudi/uBlock/issues/757
|
2015-02-10 02:12:37 +01:00
|
|
|
// Support RequestPolicy rule syntax
|
|
|
|
var result = this.result;
|
|
|
|
var matches = /\[origins-to-destinations\]([^\[]+)/.exec(result);
|
|
|
|
if ( matches && matches.length === 2 ) {
|
|
|
|
result = matches[1].trim()
|
|
|
|
.replace(/\|/g, ' ')
|
|
|
|
.replace(/\n/g, ' * noop\n');
|
|
|
|
}
|
2018-03-11 15:59:39 +01:00
|
|
|
applyDiff(false, result, '', renderRules);
|
2014-12-31 23:26:17 +01:00
|
|
|
};
|
|
|
|
var file = this.files[0];
|
2018-03-11 15:59:39 +01:00
|
|
|
if ( file === undefined || file.name === '' ) { return; }
|
|
|
|
if ( file.type.indexOf('text') !== 0 ) { return; }
|
2014-12-31 23:26:17 +01:00
|
|
|
var fr = new FileReader();
|
|
|
|
fr.onload = fileReaderOnLoadHandler;
|
|
|
|
fr.readAsText(file);
|
|
|
|
}
|
|
|
|
|
|
|
|
/******************************************************************************/
|
|
|
|
|
|
|
|
var startImportFilePicker = function() {
|
|
|
|
var input = document.getElementById('importFilePicker');
|
|
|
|
// Reset to empty string, this will ensure an change event is properly
|
|
|
|
// triggered if the user pick a file, even if it is the same as the last
|
|
|
|
// one picked.
|
|
|
|
input.value = '';
|
|
|
|
input.click();
|
|
|
|
};
|
|
|
|
|
|
|
|
/******************************************************************************/
|
|
|
|
|
|
|
|
function exportUserRulesToFile() {
|
2015-01-06 18:14:37 +01:00
|
|
|
var filename = vAPI.i18n('rulesDefaultFileName')
|
2016-10-13 19:25:57 +02:00
|
|
|
.replace('{{datetime}}', uBlockDashboard.dateNowToSensibleString())
|
2015-01-06 18:14:37 +01:00
|
|
|
.replace(/ +/g, '_');
|
|
|
|
vAPI.download({
|
2018-03-11 15:59:39 +01:00
|
|
|
url: 'data:text/plain,' + encodeURIComponent(
|
|
|
|
mergeView.leftOriginal().getValue().trim() + '\n'
|
|
|
|
),
|
|
|
|
filename: filename,
|
|
|
|
saveAs: true
|
2014-12-31 23:26:17 +01:00
|
|
|
});
|
|
|
|
}
|
|
|
|
|
|
|
|
/******************************************************************************/
|
|
|
|
|
2018-03-11 15:59:39 +01:00
|
|
|
/*
|
|
|
|
var onFilter = (function() {
|
|
|
|
var timer;
|
2015-02-11 17:34:51 +01:00
|
|
|
|
2018-03-11 15:59:39 +01:00
|
|
|
var process = function() {
|
|
|
|
timer = undefined;
|
|
|
|
};
|
2015-02-11 17:34:51 +01:00
|
|
|
|
2018-03-11 15:59:39 +01:00
|
|
|
return function() {
|
|
|
|
if ( timer !== undefined ) { clearTimeout(timer); }
|
|
|
|
timer = vAPI.setTimeout(process, 577);
|
2015-02-11 17:34:51 +01:00
|
|
|
};
|
2018-03-11 15:59:39 +01:00
|
|
|
})();
|
|
|
|
*/
|
2015-02-11 17:34:51 +01:00
|
|
|
|
|
|
|
/******************************************************************************/
|
|
|
|
|
2018-03-11 15:59:39 +01:00
|
|
|
var onChange = (function() {
|
|
|
|
var timer;
|
|
|
|
|
|
|
|
var process = function(now) {
|
|
|
|
timer = undefined;
|
|
|
|
var isClean = mergeView.editor().isClean(cleanToken);
|
|
|
|
var diff = document.getElementById('diff');
|
|
|
|
if (
|
|
|
|
now &&
|
|
|
|
isClean === false &&
|
|
|
|
mergeView.editor().getValue().trim() === cleanEditText
|
|
|
|
) {
|
|
|
|
cleanToken = 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;
|
2015-02-11 17:34:51 +01:00
|
|
|
};
|
2018-03-11 15:59:39 +01:00
|
|
|
|
|
|
|
return function(now) {
|
|
|
|
if ( timer !== undefined ) { clearTimeout(timer); }
|
|
|
|
timer = now ? process(now) : vAPI.setTimeout(process, 57);
|
|
|
|
};
|
|
|
|
})();
|
2015-02-11 17:34:51 +01:00
|
|
|
|
|
|
|
/******************************************************************************/
|
|
|
|
|
2018-03-11 15:59:39 +01:00
|
|
|
var revertAllHandler = function() {
|
|
|
|
var toAdd = [], toRemove = [];
|
|
|
|
var left = mergeView.leftOriginal(),
|
|
|
|
edit = mergeView.editor();
|
|
|
|
for ( var chunk of mergeView.leftChunks() ) {
|
|
|
|
var addedLines = left.getRange(
|
|
|
|
{ line: chunk.origFrom, ch: 0 },
|
|
|
|
{ line: chunk.origTo, ch: 0 }
|
|
|
|
);
|
|
|
|
var removedLines = edit.getRange(
|
|
|
|
{ line: chunk.editFrom, ch: 0 },
|
|
|
|
{ line: chunk.editTo, ch: 0 }
|
|
|
|
);
|
|
|
|
toAdd.push(addedLines.trim());
|
|
|
|
toRemove.push(removedLines.trim());
|
2015-03-12 06:52:27 +01:00
|
|
|
}
|
2018-03-11 15:59:39 +01:00
|
|
|
applyDiff(false, toAdd.join('\n'), toRemove.join('\n'), renderRules);
|
2015-02-11 17:34:51 +01:00
|
|
|
};
|
|
|
|
|
|
|
|
/******************************************************************************/
|
|
|
|
|
2018-03-11 15:59:39 +01:00
|
|
|
var commitAllHandler = function() {
|
|
|
|
var toAdd = [], toRemove = [];
|
|
|
|
var left = mergeView.leftOriginal(),
|
|
|
|
edit = mergeView.editor();
|
|
|
|
for ( var chunk of mergeView.leftChunks() ) {
|
|
|
|
var addedLines = edit.getRange(
|
|
|
|
{ line: chunk.editFrom, ch: 0 },
|
|
|
|
{ line: chunk.editTo, ch: 0 }
|
|
|
|
);
|
|
|
|
var removedLines = left.getRange(
|
|
|
|
{ line: chunk.origFrom, ch: 0 },
|
|
|
|
{ line: chunk.origTo, ch: 0 }
|
|
|
|
);
|
|
|
|
toAdd.push(addedLines.trim());
|
|
|
|
toRemove.push(removedLines.trim());
|
|
|
|
}
|
|
|
|
applyDiff(true, toAdd.join('\n'), toRemove.join('\n'), renderRules);
|
2015-02-11 17:34:51 +01:00
|
|
|
};
|
|
|
|
|
|
|
|
/******************************************************************************/
|
|
|
|
|
2018-03-11 15:59:39 +01:00
|
|
|
var editSaveHandler = function() {
|
|
|
|
var editor = mergeView.editor();
|
|
|
|
var editText = editor.getValue().trim();
|
|
|
|
if ( editText === cleanEditText ) {
|
|
|
|
onChange(true);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
if ( differ === undefined ) { differ = new diff_match_patch(); }
|
|
|
|
var toAdd = [], toRemove = [];
|
|
|
|
var diffs = differ.diff_main(cleanEditText, editText);
|
|
|
|
for ( var diff of diffs ) {
|
|
|
|
if ( diff[0] === 1 ) {
|
|
|
|
toAdd.push(diff[1]);
|
|
|
|
} else if ( diff[0] === -1 ) {
|
|
|
|
toRemove.push(diff[1]);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
applyDiff(false, toAdd.join(''), toRemove.join(''), renderRules);
|
2015-02-11 17:34:51 +01:00
|
|
|
};
|
|
|
|
|
|
|
|
/******************************************************************************/
|
|
|
|
|
2015-08-11 21:29:14 +02:00
|
|
|
var getCloudData = function() {
|
2018-03-11 15:59:39 +01:00
|
|
|
return mergeView.leftOriginal().getValue().trim();
|
2015-08-11 21:29:14 +02:00
|
|
|
};
|
|
|
|
|
2015-08-12 18:17:39 +02:00
|
|
|
var setCloudData = function(data, append) {
|
2018-03-11 15:59:39 +01:00
|
|
|
if ( typeof data !== 'string' ) { return; }
|
|
|
|
applyDiff(
|
|
|
|
false,
|
|
|
|
data,
|
|
|
|
append ? '' : mergeView.editor().getValue().trim(),
|
|
|
|
renderRules
|
|
|
|
);
|
2015-08-11 21:29:14 +02:00
|
|
|
};
|
|
|
|
|
|
|
|
self.cloud.onPush = getCloudData;
|
|
|
|
self.cloud.onPull = setCloudData;
|
|
|
|
|
|
|
|
/******************************************************************************/
|
|
|
|
|
2018-03-11 15:59:39 +01:00
|
|
|
messaging.send('dashboard', { what: 'getRules' }, renderRules);
|
|
|
|
|
2015-08-11 21:29:14 +02:00
|
|
|
// Handle user interaction
|
|
|
|
uDom('#importButton').on('click', startImportFilePicker);
|
|
|
|
uDom('#importFilePicker').on('change', handleImportFilePicker);
|
|
|
|
uDom('#exportButton').on('click', exportUserRulesToFile);
|
2018-03-11 15:59:39 +01:00
|
|
|
uDom('#revertButton').on('click', revertAllHandler);
|
|
|
|
uDom('#commitButton').on('click', commitAllHandler);
|
|
|
|
uDom('#editSaveButton').on('click', editSaveHandler);
|
2015-08-11 21:29:14 +02:00
|
|
|
|
2018-03-11 15:59:39 +01:00
|
|
|
// https://groups.google.com/forum/#!topic/codemirror/UQkTrt078Vs
|
|
|
|
mergeView.editor().on('updateDiff', function() { onChange(); });
|
2014-12-31 23:26:17 +01:00
|
|
|
|
|
|
|
/******************************************************************************/
|
|
|
|
|
|
|
|
})();
|
|
|
|
|