diff --git a/src/_locales/en/messages.json b/src/_locales/en/messages.json index cbe6ec913..23909cb5f 100644 --- a/src/_locales/en/messages.json +++ b/src/_locales/en/messages.json @@ -855,6 +855,10 @@ "message": "Type a shortcut", "description": "Placeholder string for input field used to capture a keyboard shortcut" }, + "genericMergeViewScrollLock":{ + "message":"Toggle locked scrolling", + "description":"Tooltip for the button used to lock scrolling between the views in the 'My rules' pane" + }, "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/js/dyna-rules.js b/src/js/dyna-rules.js index 8ffe536b6..5e36ce912 100644 --- a/src/js/dyna-rules.js +++ b/src/js/dyna-rules.js @@ -29,9 +29,9 @@ /******************************************************************************/ -var messaging = vAPI.messaging; +const messaging = vAPI.messaging; -var mergeView = new CodeMirror.MergeView( +const mergeView = new CodeMirror.MergeView( document.querySelector('.codeMirrorMergeContainer'), { allowEditingOriginals: true, @@ -50,15 +50,52 @@ mergeView.leftOriginal().setOption('readOnly', 'nocursor'); uBlockDashboard.patchCodeMirrorEditor(mergeView.editor()); -var unfilteredRules = { +const unfilteredRules = { orig: { doc: mergeView.leftOriginal(), rules: [] }, edit: { doc: mergeView.editor(), rules: [] } }; -var cleanEditToken = 0; -var cleanEditText = ''; +let cleanEditToken = 0; +let cleanEditText = ''; -var differ; +let differ; + +/******************************************************************************/ + +// The following code is to take care of properly internationalizing +// the tooltips of the arrows used by the CodeMirror merge view. These +// are hard-coded by CodeMirror ("Push to left", "Push to right"). An +// observer is necessary because there is no hook for uBO to overwrite +// reliably the default title attribute assigned by CodeMirror. + +(function() { + const i18nCommitStr = vAPI.i18n('rulesCommit'); + const i18nRevertStr = vAPI.i18n('rulesRevert'); + const commitArrowSelector = '.CodeMirror-merge-copybuttons-left .CodeMirror-merge-copy-reverse:not([title="' + i18nCommitStr + '"])'; + const revertArrowSelector = '.CodeMirror-merge-copybuttons-left .CodeMirror-merge-copy:not([title="' + i18nRevertStr + '"])'; + + uDom.nodeFromSelector('.CodeMirror-merge-scrolllock') + .setAttribute('title', vAPI.i18n('genericMergeViewScrollLock')); + + const translate = function() { + let elems = document.querySelectorAll(commitArrowSelector); + for ( const elem of elems ) { + elem.setAttribute('title', i18nCommitStr); + } + elems = document.querySelectorAll(revertArrowSelector); + for ( const elem of elems ) { + elem.setAttribute('title', i18nRevertStr); + } + }; + + const mergeGapObserver = new MutationObserver(translate); + + mergeGapObserver.observe( + uDom.nodeFromSelector('.CodeMirror-merge-copybuttons-left'), + { attributes: true, attributeFilter: [ 'title' ], subtree: true } + ); + +})(); /******************************************************************************/ @@ -66,7 +103,7 @@ var differ; // https://github.com/codemirror/CodeMirror/blob/3e1bb5fff682f8f6cbfaef0e56c61d62403d4798/addon/search/search.js#L22 // ... and modified as needed. -var updateOverlay = (function() { +const updateOverlay = (function() { let reFilter; let mode = { token: function(stream) { @@ -99,7 +136,7 @@ var updateOverlay = (function() { // - Scroll position preserved // - Minimum amount of text updated -var rulesToDoc = function(clearHistory) { +const rulesToDoc = function(clearHistory) { for ( let key in unfilteredRules ) { if ( unfilteredRules.hasOwnProperty(key) === false ) { continue; } let doc = unfilteredRules[key].doc; @@ -145,7 +182,7 @@ var rulesToDoc = function(clearHistory) { /******************************************************************************/ -var filterRules = function(key) { +const filterRules = function(key) { let rules = unfilteredRules[key].rules; let filter = uDom.nodeFromSelector('#ruleFilter input').value; if ( filter !== '' ) { @@ -162,7 +199,7 @@ var filterRules = function(key) { /******************************************************************************/ -var renderRules = (function() { +const renderRules = (function() { let firstVisit = true; let reIsSwitchRule = /^[a-z-]+: /; @@ -191,7 +228,7 @@ var renderRules = (function() { /******************************************************************************/ -var applyDiff = function(permanent, toAdd, toRemove) { +const applyDiff = function(permanent, toAdd, toRemove) { messaging.send( 'dashboard', { @@ -261,7 +298,7 @@ function handleImportFilePicker() { /******************************************************************************/ -var startImportFilePicker = function() { +const startImportFilePicker = function() { let 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 @@ -287,7 +324,7 @@ function exportUserRulesToFile() { /******************************************************************************/ -var onFilterChanged = (function() { +const onFilterChanged = (function() { let timer, overlay = null, last = ''; @@ -319,13 +356,13 @@ var onFilterChanged = (function() { /******************************************************************************/ -var onTextChanged = (function() { +const onTextChanged = (function() { let timer; let process = function(now) { timer = undefined; + const diff = document.getElementById('diff'); let isClean = mergeView.editor().isClean(cleanEditToken); - let diff = document.getElementById('diff'); if ( now && isClean === false && @@ -340,7 +377,7 @@ var onTextChanged = (function() { 'disabled', isClean ); - let input = document.querySelector('#ruleFilter input'); + const input = document.querySelector('#ruleFilter input'); if ( isClean ) { input.removeAttribute('disabled'); CodeMirror.commands.save = undefined; @@ -358,7 +395,7 @@ var onTextChanged = (function() { /******************************************************************************/ -var revertAllHandler = function() { +const revertAllHandler = function() { let toAdd = [], toRemove = []; let left = mergeView.leftOriginal(), edit = mergeView.editor(); @@ -379,7 +416,7 @@ var revertAllHandler = function() { /******************************************************************************/ -var commitAllHandler = function() { +const commitAllHandler = function() { let toAdd = [], toRemove = []; let left = mergeView.leftOriginal(), edit = mergeView.editor(); @@ -400,7 +437,7 @@ var commitAllHandler = function() { /******************************************************************************/ -var editSaveHandler = function() { +const editSaveHandler = function() { let editor = mergeView.editor(); let editText = editor.getValue().trim(); if ( editText === cleanEditText ) {