mirror of
https://github.com/gorhill/uBlock.git
synced 2024-11-02 00:42:45 +01:00
this fixes #257
This commit is contained in:
parent
b920b49d7d
commit
8d839d0230
@ -25,6 +25,7 @@
|
||||
<script src="js/tab.js"></script>
|
||||
<script src="js/traffic.js"></script>
|
||||
<script src="js/messaging-handlers.js"></script>
|
||||
<script src="js/contextmenu.js"></script>
|
||||
<script src="js/start.js"></script>
|
||||
</body>
|
||||
</html>
|
||||
|
@ -54,6 +54,7 @@ return {
|
||||
userSettings: {
|
||||
autoUpdate: true,
|
||||
collapseBlocked: true,
|
||||
contextMenuEnabled: false,
|
||||
externalLists: defaultExternalLists,
|
||||
logRequests: false,
|
||||
parseAllABPHideFilters: true,
|
||||
@ -114,6 +115,7 @@ return {
|
||||
noopFunc: function(){},
|
||||
|
||||
apiErrorCount: 0,
|
||||
elementPickerTarget: '',
|
||||
|
||||
// so that I don't have to care for last comma
|
||||
dummy: 0
|
||||
|
104
js/contextmenu.js
Normal file
104
js/contextmenu.js
Normal file
@ -0,0 +1,104 @@
|
||||
/*******************************************************************************
|
||||
|
||||
µBlock - a Chromium browser extension to block requests.
|
||||
Copyright (C) 2014 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
|
||||
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/uBlock
|
||||
*/
|
||||
|
||||
/* global */
|
||||
|
||||
/******************************************************************************/
|
||||
|
||||
// New namespace
|
||||
|
||||
µBlock.contextMenu = (function() {
|
||||
|
||||
/******************************************************************************/
|
||||
|
||||
var µb = µBlock;
|
||||
var enabled = false;
|
||||
|
||||
/******************************************************************************/
|
||||
|
||||
var onContextMenuClicked = function(details, tab) {
|
||||
if ( details.menuItemId !== 'blockElement' ) {
|
||||
return;
|
||||
}
|
||||
if ( tab === undefined ) {
|
||||
return;
|
||||
}
|
||||
if ( /^https?:\/\//.test(tab.url) === false ) {
|
||||
return;
|
||||
}
|
||||
var tagName = '';
|
||||
var src = '';
|
||||
if ( typeof details.frameUrl === 'string' ) {
|
||||
tagName = 'iframe';
|
||||
src = details.frameUrl;
|
||||
} else if ( typeof details.srcUrl === 'string' ) {
|
||||
if ( details.mediaType === 'image' ) {
|
||||
tagName = 'img';
|
||||
} else if ( details.mediaType === 'video' ) {
|
||||
tagName = 'video';
|
||||
} else if ( details.mediaType === 'audio' ) {
|
||||
tagName = 'audio';
|
||||
}
|
||||
src = details.srcUrl;
|
||||
} else if ( typeof details.linkUrl === 'string' ) {
|
||||
tagName = 'a';
|
||||
src = details.linkUrl;
|
||||
}
|
||||
|
||||
if ( src === '' ) {
|
||||
return;
|
||||
}
|
||||
|
||||
µb.elementPickerExec(tab.id, tagName + '\t' + src);
|
||||
};
|
||||
|
||||
/******************************************************************************/
|
||||
|
||||
var toggleMenu = function(on) {
|
||||
// This needs to be local scope: we can't reuse it for more than one
|
||||
// menu creation call.
|
||||
var menuCreateDetails = {
|
||||
id: 'blockElement',
|
||||
title: chrome.i18n.getMessage('pickerContextMenuEntry'),
|
||||
contexts: ['frame', 'link', 'image', 'video'],
|
||||
documentUrlPatterns: ['https://*/*', 'http://*/*']
|
||||
};
|
||||
|
||||
if ( on === true && enabled === false ) {
|
||||
chrome.contextMenus.create(menuCreateDetails);
|
||||
chrome.contextMenus.onClicked.addListener(onContextMenuClicked);
|
||||
enabled = true;
|
||||
} else if ( on !== true && enabled === true ) {
|
||||
chrome.contextMenus.onClicked.removeListener(onContextMenuClicked);
|
||||
chrome.contextMenus.remove(menuCreateDetails.id);
|
||||
enabled = false;
|
||||
}
|
||||
};
|
||||
|
||||
/******************************************************************************/
|
||||
|
||||
return {
|
||||
toggle: toggleMenu
|
||||
};
|
||||
|
||||
/******************************************************************************/
|
||||
|
||||
})();
|
@ -229,6 +229,10 @@ var svgOcean = null;
|
||||
var svgIslands = null;
|
||||
var divDialog = null;
|
||||
var taCandidate = null;
|
||||
var urlNormalizer = null;
|
||||
|
||||
var netFilterCandidates = [];
|
||||
var cosmeticFilterCandidates = [];
|
||||
|
||||
var targetElements = [];
|
||||
var svgWidth = 0;
|
||||
@ -329,7 +333,7 @@ var removeElements = function(elems) {
|
||||
|
||||
// Extract the best possible net filter, i.e. as specific as possible.
|
||||
|
||||
var netFilterFromElement = function(elem) {
|
||||
var netFilterFromElement = function(elem, out) {
|
||||
if ( elem === null ) {
|
||||
return;
|
||||
}
|
||||
@ -344,14 +348,31 @@ var netFilterFromElement = function(elem) {
|
||||
if ( typeof src !== 'string' || src.length === 0 ) {
|
||||
return;
|
||||
}
|
||||
return src.replace(/^https?:\/\//, '||').replace(/\?.*$/, '');
|
||||
// Remove fragment
|
||||
var pos = src.indexOf('#');
|
||||
if ( pos !== -1 ) {
|
||||
src = src.slice(0, pos);
|
||||
}
|
||||
// Feed the attribute to a link element, then retrieve back: this
|
||||
// should normalize it.
|
||||
urlNormalizer.href = src;
|
||||
src = urlNormalizer.href;
|
||||
// Anchor absolute filter to hostname
|
||||
src = src.replace(/^https?:\/\//, '||');
|
||||
out.push(src);
|
||||
// Suggest a less narrow filter if possible
|
||||
pos = src.indexOf('?');
|
||||
if ( pos !== -1 ) {
|
||||
src = src.slice(0, pos);
|
||||
out.push(src);
|
||||
}
|
||||
};
|
||||
|
||||
/******************************************************************************/
|
||||
|
||||
// Extract the best possible cosmetic filter, i.e. as specific as possible.
|
||||
|
||||
var cosmeticFilterFromElement = function(elem) {
|
||||
var cosmeticFilterFromElement = function(elem, out) {
|
||||
if ( elem === null ) {
|
||||
return;
|
||||
}
|
||||
@ -417,42 +438,73 @@ var cosmeticFilterFromElement = function(elem) {
|
||||
}
|
||||
}
|
||||
|
||||
return '##' + prefix + suffix.join('');
|
||||
out.push('##' + prefix + suffix.join(''));
|
||||
};
|
||||
|
||||
/******************************************************************************/
|
||||
|
||||
var selectorFromCandidate = function() {
|
||||
var selector = '';
|
||||
var v = taCandidate.value;
|
||||
if ( v.slice(0, 2) === '##' ) {
|
||||
selector = v.replace('##', '');
|
||||
} else {
|
||||
if ( v.slice(0, 2) === '||' ) {
|
||||
v = v.replace('||', '');
|
||||
}
|
||||
selector = '[src*="' + v + '"]';
|
||||
var filtersFromElement = function(elem) {
|
||||
netFilterCandidates.length = 0;
|
||||
cosmeticFilterCandidates.length = 0;
|
||||
while ( elem && elem !== document.body ) {
|
||||
netFilterFromElement(elem, netFilterCandidates);
|
||||
cosmeticFilterFromElement(elem, cosmeticFilterCandidates);
|
||||
elem = elem.parentNode;
|
||||
}
|
||||
try {
|
||||
if ( document.querySelector(selector) === null ) {
|
||||
return '';
|
||||
}
|
||||
}
|
||||
catch (e) {
|
||||
return '';
|
||||
}
|
||||
return selector;
|
||||
};
|
||||
|
||||
/******************************************************************************/
|
||||
|
||||
var elementsFromFilter = function(filter) {
|
||||
var out = [];
|
||||
|
||||
// Cosmetic filters: these are straight CSS selectors
|
||||
// TODO: This is still not working well for a[href], because there are
|
||||
// many ways to compose a valid href to the same effective URL.
|
||||
// One idea is to normalize all a[href] on the page, but for now I will
|
||||
// wait and see, as I prefer to refrain from tampering with the page
|
||||
// content if I can avoid it.
|
||||
if ( filter.slice(0, 2) === '##' ) {
|
||||
try {
|
||||
out = document.querySelectorAll(filter.replace('##', ''));
|
||||
}
|
||||
catch (e) {
|
||||
}
|
||||
return out;
|
||||
}
|
||||
|
||||
// Net filters: we need to lookup manually -- translating into a
|
||||
// foolproof CSS selector is just not possible
|
||||
if ( filter.slice(0, 2) === '||' ) {
|
||||
filter = filter.replace('||', '');
|
||||
}
|
||||
var elems = document.querySelectorAll('[src]');
|
||||
var i = elems.length;
|
||||
var elem;
|
||||
while ( i-- ) {
|
||||
elem = elems[i];
|
||||
if ( typeof elem.src !== 'string' ) {
|
||||
continue;
|
||||
}
|
||||
if ( elem.src.indexOf(filter) !== -1 ) {
|
||||
out.push(elem);
|
||||
}
|
||||
}
|
||||
return out;
|
||||
};
|
||||
|
||||
// https://www.youtube.com/watch?v=YI2XuIOW3gM
|
||||
|
||||
/******************************************************************************/
|
||||
|
||||
var userFilterFromCandidate = function() {
|
||||
if ( selectorFromCandidate() === '' ) {
|
||||
var v = taCandidate.value;
|
||||
|
||||
var elems = elementsFromFilter(v);
|
||||
if ( elems.length === 0 ) {
|
||||
return false;
|
||||
}
|
||||
|
||||
var v = taCandidate.value;
|
||||
|
||||
// Cosmetic filter?
|
||||
if ( v.slice(0, 2) === '##' ) {
|
||||
return window.location.hostname + v;
|
||||
@ -468,52 +520,62 @@ var userFilterFromCandidate = function() {
|
||||
|
||||
/******************************************************************************/
|
||||
|
||||
var onCandidateChanged = function(ev) {
|
||||
var selector = selectorFromCandidate();
|
||||
divDialog.querySelector('#create').disabled = selector === '';
|
||||
if ( selector === '' ) {
|
||||
highlightElements([]);
|
||||
return;
|
||||
}
|
||||
highlightElements(document.querySelectorAll(selector));
|
||||
var onCandidateChanged = function() {
|
||||
var elems = elementsFromFilter(taCandidate.value);
|
||||
divDialog.querySelector('#create').disabled = elems.length === 0;
|
||||
highlightElements(elems);
|
||||
};
|
||||
|
||||
/******************************************************************************/
|
||||
|
||||
var candidateFromClickEvent = function(ev) {
|
||||
var target = ev.target;
|
||||
if ( !target ) {
|
||||
var candidateFromFilterChoice = function(filterChoice) {
|
||||
var slot = filterChoice.slot;
|
||||
var filters = filterChoice.filters;
|
||||
var filter = filters[slot];
|
||||
|
||||
if ( filter === undefined ) {
|
||||
return '';
|
||||
}
|
||||
|
||||
// Bare
|
||||
if ( ev.ctrlKey || ev.metaKey ) {
|
||||
return target.textContent;
|
||||
}
|
||||
|
||||
// For net filters there no such thing as a path
|
||||
if ( target.textContent.charAt(0) !== '#' ) {
|
||||
return target.textContent;
|
||||
if ( filterChoice.type === 'net' || filterChoice.modifier ) {
|
||||
return filter;
|
||||
}
|
||||
|
||||
// Return path: the target element, then all siblings prepended
|
||||
var selector = [];
|
||||
while ( target ) {
|
||||
if ( target.nodeType !== 1 || target.tagName.toLowerCase() !== 'li' ) {
|
||||
continue;
|
||||
}
|
||||
selector.unshift(target.textContent.replace(/^##/, ''));
|
||||
var filter;
|
||||
for ( ; slot < filters.length; slot++ ) {
|
||||
filter = filters[slot];
|
||||
selector.unshift(filter.replace(/^##/, ''));
|
||||
// Stop at any element with an id: these are unique in a web page
|
||||
if ( target.textContent.slice(0, 3) === '###' ) {
|
||||
if ( filter.slice(0, 3) === '###' ) {
|
||||
break;
|
||||
}
|
||||
target = target.nextSibling;
|
||||
}
|
||||
return '##' + selector.join(' > ');
|
||||
};
|
||||
|
||||
/******************************************************************************/
|
||||
|
||||
var filterChoiceFromEvent = function(ev) {
|
||||
var li = ev.target;
|
||||
var isNetFilter = li.textContent.slice(0, 2) !== '##';
|
||||
var r = {
|
||||
type: isNetFilter ? 'net' : 'cosmetic',
|
||||
filters: isNetFilter ? netFilterCandidates : cosmeticFilterCandidates,
|
||||
slot: 0,
|
||||
modifier: ev.ctrlKey || ev.metaKey
|
||||
};
|
||||
while ( li.previousSibling !== null ) {
|
||||
li = li.previousSibling;
|
||||
r.slot += 1;
|
||||
}
|
||||
return r;
|
||||
};
|
||||
|
||||
/******************************************************************************/
|
||||
|
||||
var onDialogClicked = function(ev) {
|
||||
if ( ev.target === null ) {
|
||||
/* do nothing */
|
||||
@ -523,7 +585,7 @@ var onDialogClicked = function(ev) {
|
||||
var filter = userFilterFromCandidate();
|
||||
if ( filter ) {
|
||||
messaging.tell({ what: 'createUserFilter', filters: filter });
|
||||
removeElements(document.querySelectorAll(selectorFromCandidate()));
|
||||
removeElements(elementsFromFilter(taCandidate.value));
|
||||
stopPicker();
|
||||
}
|
||||
}
|
||||
@ -537,7 +599,7 @@ var onDialogClicked = function(ev) {
|
||||
}
|
||||
|
||||
else if ( ev.target.tagName.toLowerCase() === 'li' && pickerRootDistance(ev.target) === 5 ) {
|
||||
taCandidate.value = candidateFromClickEvent(ev);
|
||||
taCandidate.value = candidateFromFilterChoice(filterChoiceFromEvent(ev));
|
||||
onCandidateChanged();
|
||||
}
|
||||
|
||||
@ -558,28 +620,51 @@ var removeAllChildren = function(parent) {
|
||||
// TODO: for convenience I could provide a small set of net filters instead
|
||||
// of just a single one. Truncating the right-most part of the path etc.
|
||||
|
||||
var showDialog = function(filters) {
|
||||
var divNet = divDialog.querySelector('ul > li:nth-of-type(1) > ul');
|
||||
var divCosmetic = divDialog.querySelector('ul > li:nth-of-type(2) > ul');
|
||||
removeAllChildren(divCosmetic);
|
||||
removeAllChildren(divNet);
|
||||
var filter, li;
|
||||
for ( var i = 0; i < filters.length; i++ ) {
|
||||
filter = filters[i];
|
||||
li = document.createElement('li');
|
||||
li.textContent = filter;
|
||||
if ( filter.indexOf('##') === 0 ) {
|
||||
divCosmetic.appendChild(li);
|
||||
} else {
|
||||
divNet.appendChild(li);
|
||||
}
|
||||
}
|
||||
divDialog.querySelector('ul > li:nth-of-type(1)').style.display = divNet.firstChild ? '' : 'none';
|
||||
divDialog.querySelector('ul > li:nth-of-type(2)').style.display = divCosmetic.firstChild ? '' : 'none';
|
||||
divDialog.querySelector('ul').style.display = divNet.firstChild || divCosmetic.firstChild ? '' : 'none';
|
||||
taCandidate.value = '';
|
||||
divDialog.querySelector('#create').disabled = true;
|
||||
var showDialog = function(options) {
|
||||
pausePicker();
|
||||
|
||||
options = options || {};
|
||||
|
||||
// Create lists of candidate filters
|
||||
var populate = function(src, des) {
|
||||
var root = divDialog.querySelector(des);
|
||||
var ul = root.querySelector('ul');
|
||||
removeAllChildren(ul);
|
||||
var li;
|
||||
for ( var i = 0; i < src.length; i++ ) {
|
||||
li = document.createElement('li');
|
||||
li.textContent = src[i];
|
||||
ul.appendChild(li);
|
||||
}
|
||||
root.style.display = src.length !== 0 ? '' : 'none';
|
||||
};
|
||||
|
||||
populate(netFilterCandidates, 'ul > li:nth-of-type(1)');
|
||||
populate(cosmeticFilterCandidates, 'ul > li:nth-of-type(2)');
|
||||
|
||||
divDialog.querySelector('ul').style.display = netFilterCandidates.length || cosmeticFilterCandidates.length ? '' : 'none';
|
||||
divDialog.querySelector('#create').disabled = true;
|
||||
|
||||
// Auto-select a candidate filter
|
||||
var filterChoice = {
|
||||
type: '',
|
||||
filters: [],
|
||||
slot: 0,
|
||||
modifier: options.modifier || false
|
||||
};
|
||||
if ( netFilterCandidates.length ) {
|
||||
filterChoice.type = 'net';
|
||||
filterChoice.filters = netFilterCandidates;
|
||||
} else if ( cosmeticFilterCandidates.length ) {
|
||||
filterChoice.type = 'cosmetic';
|
||||
filterChoice.filters = cosmeticFilterCandidates;
|
||||
}
|
||||
|
||||
taCandidate.value = '';
|
||||
if ( filterChoice.type !== '' ) {
|
||||
taCandidate.value = candidateFromFilterChoice(filterChoice);
|
||||
onCandidateChanged();
|
||||
}
|
||||
};
|
||||
|
||||
/******************************************************************************/
|
||||
@ -604,18 +689,8 @@ var onSvgClicked = function() {
|
||||
if ( pickerPaused() ) {
|
||||
return;
|
||||
}
|
||||
|
||||
var filter;
|
||||
var filters = [];
|
||||
for ( var elem = targetElements[0]; elem && elem !== document.body; elem = elem.parentNode ) {
|
||||
if ( filter = cosmeticFilterFromElement(elem) ) {
|
||||
filters.push(filter);
|
||||
}
|
||||
if ( filter = netFilterFromElement(elem) ) {
|
||||
filters.push(filter);
|
||||
}
|
||||
}
|
||||
showDialog(filters);
|
||||
filtersFromElement(targetElements[0]);
|
||||
showDialog();
|
||||
};
|
||||
|
||||
/******************************************************************************/
|
||||
@ -646,6 +721,9 @@ var onScrolled = function(ev) {
|
||||
|
||||
/******************************************************************************/
|
||||
|
||||
// Let's have the element picker code flushed from memory when no longer
|
||||
// in use: to ensure this, release all local references.
|
||||
|
||||
var stopPicker = function() {
|
||||
if ( pickerRoot !== null ) {
|
||||
document.removeEventListener('keydown', onKeyPressed);
|
||||
@ -655,7 +733,11 @@ var stopPicker = function() {
|
||||
svgRoot.removeEventListener('mousemove', onSvgHovered);
|
||||
svgRoot.removeEventListener('click', onSvgClicked);
|
||||
document.body.removeChild(pickerRoot);
|
||||
pickerRoot = divDialog = svgRoot = svgOcean = svgIslands = taCandidate = null;
|
||||
pickerRoot =
|
||||
divDialog =
|
||||
svgRoot = svgOcean = svgIslands =
|
||||
taCandidate =
|
||||
urlNormalizer = null;
|
||||
messaging.stop();
|
||||
}
|
||||
targetElements = [];
|
||||
@ -685,6 +767,7 @@ var startPicker = function() {
|
||||
'color: #000;',
|
||||
'font: 12px sans-serif;',
|
||||
'margin: 0;',
|
||||
'max-width: none;',
|
||||
'outline: 0;',
|
||||
'overflow: visible;',
|
||||
'padding: 0;',
|
||||
@ -710,6 +793,9 @@ var startPicker = function() {
|
||||
'color: #999;',
|
||||
'background-color: #ccc;',
|
||||
'}',
|
||||
'.µBlock button#create:not(:disabled) {',
|
||||
'background-color: #ffdca8;',
|
||||
'}',
|
||||
'.µBlock > svg {',
|
||||
'position: absolute;',
|
||||
'top: 0;',
|
||||
@ -782,6 +868,17 @@ var startPicker = function() {
|
||||
'text-align: left;',
|
||||
'overflow: hidden;',
|
||||
'}',
|
||||
'.µBlock > div > ul > li:not(:first-child) {',
|
||||
'margin-top: 0.5em;',
|
||||
'}',
|
||||
'.µBlock > div > ul > li > span:nth-of-type(1) {',
|
||||
'font-weight: bold;',
|
||||
'}',
|
||||
'.µBlock > div > ul > li > span:nth-of-type(2) {',
|
||||
'margin: 0 0 0 1em;',
|
||||
'font-size: smaller;',
|
||||
'color: gray;',
|
||||
'}',
|
||||
'.µBlock > div > ul > li > ul {',
|
||||
'margin: 0 0 0 1em;',
|
||||
'list-style-type: none;',
|
||||
@ -832,8 +929,8 @@ var startPicker = function() {
|
||||
'</div>',
|
||||
'</div>',
|
||||
'<ul>',
|
||||
'<li>.<ul></ul>',
|
||||
'<li>.<ul></ul>',
|
||||
'<li><span>.</span><ul></ul>',
|
||||
'<li><span>.</span><span>.</span><ul></ul>',
|
||||
'</ul>',
|
||||
''
|
||||
].join('');
|
||||
@ -845,6 +942,7 @@ var startPicker = function() {
|
||||
divDialog.addEventListener('click', onDialogClicked);
|
||||
taCandidate = divDialog.querySelector('textarea');
|
||||
taCandidate.addEventListener('input', onCandidateChanged);
|
||||
urlNormalizer = document.createElement('a');
|
||||
window.addEventListener('scroll', onScrolled);
|
||||
document.addEventListener('keydown', onKeyPressed);
|
||||
};
|
||||
@ -855,12 +953,59 @@ startPicker();
|
||||
|
||||
/******************************************************************************/
|
||||
|
||||
messaging.ask({ what: 'i18n' }, function(details) {
|
||||
divDialog.querySelector('#create').firstChild.nodeValue = details.create;
|
||||
divDialog.querySelector('#pick').firstChild.nodeValue = details.pick;
|
||||
divDialog.querySelector('#quit').firstChild.nodeValue = details.quit;
|
||||
divDialog.querySelector('ul > li:nth-of-type(1)').firstChild.nodeValue = details.netFilters;
|
||||
divDialog.querySelector('ul > li:nth-of-type(2)').firstChild.nodeValue = details.cosmeticFilters;
|
||||
messaging.ask({ what: 'elementPickerArguments' }, function(details) {
|
||||
var i18nMap = {
|
||||
'#create': 'create',
|
||||
'#pick': 'pick',
|
||||
'#quit': 'quit',
|
||||
'ul > li:nth-of-type(1) > span:nth-of-type(1)': 'netFilters',
|
||||
'ul > li:nth-of-type(2) > span:nth-of-type(1)': 'cosmeticFilters',
|
||||
'ul > li:nth-of-type(2) > span:nth-of-type(2)': 'cosmeticFiltersHint'
|
||||
|
||||
};
|
||||
for ( var k in i18nMap ) {
|
||||
if ( i18nMap.hasOwnProperty(k) === false ) {
|
||||
continue;
|
||||
}
|
||||
divDialog.querySelector(k).firstChild.nodeValue = details.i18n[i18nMap[k]];
|
||||
}
|
||||
|
||||
// Auto-select a specific target, if any, and if possible
|
||||
var targetElement = details.targetElement || '';
|
||||
var pos = targetElement.indexOf('\t');
|
||||
if ( pos === -1 ) {
|
||||
return;
|
||||
}
|
||||
var srcAttrMap = {
|
||||
'a': 'href',
|
||||
'img': 'src',
|
||||
'iframe': 'src',
|
||||
'video': 'src',
|
||||
'audio': 'src'
|
||||
};
|
||||
var tagName = targetElement.slice(0, pos);
|
||||
var url = targetElement.slice(pos + 1);
|
||||
var attr = srcAttrMap[tagName];
|
||||
if ( attr === undefined ) {
|
||||
return;
|
||||
}
|
||||
var elems = document.querySelectorAll(tagName + '[' + attr + ']');
|
||||
var i = elems.length;
|
||||
var elem, src;
|
||||
while ( i-- ) {
|
||||
elem = elems[i];
|
||||
src = elem.getAttribute(attr);
|
||||
if ( src === null || src === '' ) {
|
||||
continue;
|
||||
}
|
||||
urlNormalizer.href = src;
|
||||
src = urlNormalizer.href;
|
||||
if ( src === url ) {
|
||||
filtersFromElement(elem);
|
||||
showDialog({ modifier: true });
|
||||
break;
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
/******************************************************************************/
|
||||
|
@ -24,14 +24,17 @@
|
||||
/******************************************************************************/
|
||||
/******************************************************************************/
|
||||
|
||||
// popup.js
|
||||
|
||||
(function() {
|
||||
|
||||
// popup.js
|
||||
/******************************************************************************/
|
||||
|
||||
var µb = µBlock;
|
||||
|
||||
/******************************************************************************/
|
||||
|
||||
var getStats = function(request) {
|
||||
var µb = µBlock;
|
||||
var r = {
|
||||
globalBlockedRequestCount: µb.localSettings.blockedRequestCount,
|
||||
globalAllowedRequestCount: µb.localSettings.allowedRequestCount,
|
||||
@ -72,26 +75,28 @@ var onMessage = function(request, sender, callback) {
|
||||
break;
|
||||
|
||||
case 'toggleNetFiltering':
|
||||
µBlock.toggleNetFilteringSwitch(
|
||||
µb.toggleNetFilteringSwitch(
|
||||
request.url,
|
||||
request.scope,
|
||||
request.state
|
||||
);
|
||||
µBlock.updateBadgeAsync(request.tabId);
|
||||
µb.updateBadgeAsync(request.tabId);
|
||||
break;
|
||||
|
||||
case 'gotoPick':
|
||||
chrome.tabs.executeScript(request.tabId, { file: 'js/element-picker.js' });
|
||||
µb.elementPickerExec(request.tabId);
|
||||
break;
|
||||
|
||||
default:
|
||||
return µBlock.messaging.defaultHandler(request, sender, callback);
|
||||
return µb.messaging.defaultHandler(request, sender, callback);
|
||||
}
|
||||
|
||||
callback(response);
|
||||
};
|
||||
|
||||
µBlock.messaging.listen('popup.js', onMessage);
|
||||
µb.messaging.listen('popup.js', onMessage);
|
||||
|
||||
/******************************************************************************/
|
||||
|
||||
})();
|
||||
|
||||
@ -102,8 +107,12 @@ var onMessage = function(request, sender, callback) {
|
||||
|
||||
(function() {
|
||||
|
||||
/******************************************************************************/
|
||||
|
||||
var µb = µBlock;
|
||||
|
||||
/******************************************************************************/
|
||||
|
||||
var onMessage = function(request, sender, callback) {
|
||||
// Async
|
||||
switch ( request.what ) {
|
||||
@ -135,6 +144,8 @@ var onMessage = function(request, sender, callback) {
|
||||
|
||||
µb.messaging.listen('contentscript-start.js', onMessage);
|
||||
|
||||
/******************************************************************************/
|
||||
|
||||
})();
|
||||
|
||||
/******************************************************************************/
|
||||
@ -144,6 +155,8 @@ var onMessage = function(request, sender, callback) {
|
||||
|
||||
(function() {
|
||||
|
||||
/******************************************************************************/
|
||||
|
||||
var µb = µBlock;
|
||||
|
||||
/******************************************************************************/
|
||||
@ -256,6 +269,8 @@ var onMessage = function(details, sender, callback) {
|
||||
|
||||
µb.messaging.listen('contentscript-end.js', onMessage);
|
||||
|
||||
/******************************************************************************/
|
||||
|
||||
})();
|
||||
|
||||
/******************************************************************************/
|
||||
@ -265,6 +280,12 @@ var onMessage = function(details, sender, callback) {
|
||||
|
||||
(function() {
|
||||
|
||||
/******************************************************************************/
|
||||
|
||||
var µb = µBlock;
|
||||
|
||||
/******************************************************************************/
|
||||
|
||||
var onMessage = function(request, sender, callback) {
|
||||
// Async
|
||||
switch ( request.what ) {
|
||||
@ -276,28 +297,35 @@ var onMessage = function(request, sender, callback) {
|
||||
var response;
|
||||
|
||||
switch ( request.what ) {
|
||||
case 'i18n':
|
||||
case 'elementPickerArguments':
|
||||
response = {
|
||||
create: chrome.i18n.getMessage('pickerCreate'),
|
||||
pick: chrome.i18n.getMessage('pickerPick'),
|
||||
quit: chrome.i18n.getMessage('pickerQuit'),
|
||||
netFilters: chrome.i18n.getMessage('pickerNetFilters'),
|
||||
cosmeticFilters: chrome.i18n.getMessage('pickerCosmeticFilters')
|
||||
i18n: {
|
||||
create: chrome.i18n.getMessage('pickerCreate'),
|
||||
pick: chrome.i18n.getMessage('pickerPick'),
|
||||
quit: chrome.i18n.getMessage('pickerQuit'),
|
||||
netFilters: chrome.i18n.getMessage('pickerNetFilters'),
|
||||
cosmeticFilters: chrome.i18n.getMessage('pickerCosmeticFilters'),
|
||||
cosmeticFiltersHint: chrome.i18n.getMessage('pickerCosmeticFiltersHint')
|
||||
},
|
||||
targetElement: µb.elementPickerTarget
|
||||
};
|
||||
µb.elementPickerTarget = '';
|
||||
break;
|
||||
|
||||
case 'createUserFilter':
|
||||
µBlock.appendUserFilters(request.filters);
|
||||
µb.appendUserFilters(request.filters);
|
||||
break;
|
||||
|
||||
default:
|
||||
return µBlock.messaging.defaultHandler(request, sender, callback);
|
||||
return µb.messaging.defaultHandler(request, sender, callback);
|
||||
}
|
||||
|
||||
callback(response);
|
||||
};
|
||||
|
||||
µBlock.messaging.listen('element-picker.js', onMessage);
|
||||
µb.messaging.listen('element-picker.js', onMessage);
|
||||
|
||||
/******************************************************************************/
|
||||
|
||||
})();
|
||||
|
||||
@ -308,8 +336,13 @@ var onMessage = function(request, sender, callback) {
|
||||
|
||||
(function() {
|
||||
|
||||
/******************************************************************************/
|
||||
|
||||
var µb = µBlock;
|
||||
|
||||
/******************************************************************************/
|
||||
|
||||
var getLists = function(callback) {
|
||||
var µb = µBlock;
|
||||
var r = {
|
||||
available: null,
|
||||
current: µb.remoteBlacklists,
|
||||
@ -334,8 +367,6 @@ var getLists = function(callback) {
|
||||
/******************************************************************************/
|
||||
|
||||
var onMessage = function(request, sender, callback) {
|
||||
var µb = µBlock;
|
||||
|
||||
// Async
|
||||
switch ( request.what ) {
|
||||
case 'getLists':
|
||||
@ -369,7 +400,9 @@ var onMessage = function(request, sender, callback) {
|
||||
callback(response);
|
||||
};
|
||||
|
||||
µBlock.messaging.listen('3p-filters.js', onMessage);
|
||||
µb.messaging.listen('3p-filters.js', onMessage);
|
||||
|
||||
/******************************************************************************/
|
||||
|
||||
})();
|
||||
|
||||
@ -380,9 +413,13 @@ var onMessage = function(request, sender, callback) {
|
||||
|
||||
(function() {
|
||||
|
||||
var onMessage = function(request, sender, callback) {
|
||||
var µb = µBlock;
|
||||
/******************************************************************************/
|
||||
|
||||
var µb = µBlock;
|
||||
|
||||
/******************************************************************************/
|
||||
|
||||
var onMessage = function(request, sender, callback) {
|
||||
// Async
|
||||
switch ( request.what ) {
|
||||
case 'readUserFilters':
|
||||
@ -406,7 +443,9 @@ var onMessage = function(request, sender, callback) {
|
||||
callback(response);
|
||||
};
|
||||
|
||||
µBlock.messaging.listen('1p-filters.js', onMessage);
|
||||
µb.messaging.listen('1p-filters.js', onMessage);
|
||||
|
||||
/******************************************************************************/
|
||||
|
||||
})();
|
||||
|
||||
@ -417,9 +456,13 @@ var onMessage = function(request, sender, callback) {
|
||||
|
||||
(function() {
|
||||
|
||||
var onMessage = function(request, sender, callback) {
|
||||
var µb = µBlock;
|
||||
/******************************************************************************/
|
||||
|
||||
var µb = µBlock;
|
||||
|
||||
/******************************************************************************/
|
||||
|
||||
var onMessage = function(request, sender, callback) {
|
||||
// Async
|
||||
switch ( request.what ) {
|
||||
default:
|
||||
@ -446,7 +489,9 @@ var onMessage = function(request, sender, callback) {
|
||||
callback(response);
|
||||
};
|
||||
|
||||
µBlock.messaging.listen('whitelist.js', onMessage);
|
||||
µb.messaging.listen('whitelist.js', onMessage);
|
||||
|
||||
/******************************************************************************/
|
||||
|
||||
})();
|
||||
|
||||
@ -457,6 +502,12 @@ var onMessage = function(request, sender, callback) {
|
||||
|
||||
(function() {
|
||||
|
||||
/******************************************************************************/
|
||||
|
||||
var µb = µBlock;
|
||||
|
||||
/******************************************************************************/
|
||||
|
||||
var getPageDetails = function(µb, tabId) {
|
||||
var r = {
|
||||
blockedRequests: [],
|
||||
@ -510,8 +561,6 @@ var getPageDetails = function(µb, tabId) {
|
||||
/******************************************************************************/
|
||||
|
||||
var onMessage = function(request, sender, callback) {
|
||||
var µb = µBlock;
|
||||
|
||||
// Async
|
||||
switch ( request.what ) {
|
||||
default:
|
||||
@ -537,7 +586,9 @@ var onMessage = function(request, sender, callback) {
|
||||
callback(response);
|
||||
};
|
||||
|
||||
µBlock.messaging.listen('stats.js', onMessage);
|
||||
µb.messaging.listen('stats.js', onMessage);
|
||||
|
||||
/******************************************************************************/
|
||||
|
||||
})();
|
||||
|
||||
@ -548,9 +599,13 @@ var onMessage = function(request, sender, callback) {
|
||||
|
||||
(function() {
|
||||
|
||||
var onMessage = function(request, sender, callback) {
|
||||
var µb = µBlock;
|
||||
/******************************************************************************/
|
||||
|
||||
var µb = µBlock;
|
||||
|
||||
/******************************************************************************/
|
||||
|
||||
var onMessage = function(request, sender, callback) {
|
||||
// Async
|
||||
switch ( request.what ) {
|
||||
|
||||
@ -570,7 +625,9 @@ var onMessage = function(request, sender, callback) {
|
||||
callback(response);
|
||||
};
|
||||
|
||||
µBlock.messaging.listen('about.js', onMessage);
|
||||
µb.messaging.listen('about.js', onMessage);
|
||||
|
||||
/******************************************************************************/
|
||||
|
||||
})();
|
||||
|
||||
|
@ -55,6 +55,12 @@ var onUserSettingsReceived = function(details) {
|
||||
.on('change', function(){
|
||||
changeUserSettings('showIconBadge', this.checked);
|
||||
});
|
||||
|
||||
uDom('#context-menu-enabled')
|
||||
.prop('checked', details.contextMenuEnabled === true)
|
||||
.on('change', function(){
|
||||
changeUserSettings('contextMenuEnabled', this.checked);
|
||||
});
|
||||
};
|
||||
|
||||
messaging.ask({ what: 'userSettings' }, onUserSettingsReceived);
|
||||
|
@ -74,7 +74,7 @@
|
||||
var settingsLoaded = function(store) {
|
||||
µBlock.userSettings = store;
|
||||
if ( typeof callback === 'function' ) {
|
||||
callback();
|
||||
callback(store);
|
||||
}
|
||||
};
|
||||
|
||||
@ -666,7 +666,8 @@
|
||||
};
|
||||
|
||||
// User settings are in memory
|
||||
var onUserSettingsReady = function() {
|
||||
var onUserSettingsReady = function(settings) {
|
||||
µb.contextMenu.toggle(settings.contextMenuEnabled);
|
||||
µb.fromSelfie(onSelfieReady);
|
||||
};
|
||||
|
||||
|
22
js/ublock.js
22
js/ublock.js
@ -184,10 +184,8 @@
|
||||
// Return all settings if none specified.
|
||||
|
||||
µBlock.changeUserSettings = function(name, value) {
|
||||
var µb = µBlock;
|
||||
|
||||
if ( name === undefined ) {
|
||||
return µb.userSettings;
|
||||
return this.userSettings;
|
||||
}
|
||||
|
||||
if ( typeof name !== 'string' || name === '' ) {
|
||||
@ -195,12 +193,12 @@
|
||||
}
|
||||
|
||||
// Do not allow an unknown user setting to be created
|
||||
if ( µb.userSettings[name] === undefined ) {
|
||||
if ( this.userSettings[name] === undefined ) {
|
||||
return;
|
||||
}
|
||||
|
||||
if ( value === undefined ) {
|
||||
return µb.userSettings[name];
|
||||
return this.userSettings[name];
|
||||
}
|
||||
|
||||
// Pre-change
|
||||
@ -210,15 +208,18 @@
|
||||
}
|
||||
|
||||
// Change
|
||||
µb.userSettings[name] = value;
|
||||
this.userSettings[name] = value;
|
||||
|
||||
// Post-change
|
||||
switch ( name ) {
|
||||
case 'contextMenuEnabled':
|
||||
this.contextMenu.toggle(value === true);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
µb.saveUserSettings();
|
||||
this.saveUserSettings();
|
||||
};
|
||||
|
||||
/******************************************************************************/
|
||||
@ -242,3 +243,10 @@
|
||||
};
|
||||
|
||||
/******************************************************************************/
|
||||
|
||||
µBlock.elementPickerExec = function(tabId, targetElement) {
|
||||
this.elementPickerTarget = targetElement || '';
|
||||
chrome.tabs.executeScript(tabId, { file: 'js/element-picker.js' });
|
||||
};
|
||||
|
||||
/******************************************************************************/
|
||||
|
@ -2,7 +2,7 @@
|
||||
"manifest_version": 2,
|
||||
"name": "__MSG_extName__",
|
||||
"short_name": "µBlock",
|
||||
"version": "0.6.4.0",
|
||||
"version": "0.6.5.0",
|
||||
"description": "__MSG_extShortDesc__",
|
||||
"icons": {
|
||||
"16": "img/icon_16.png",
|
||||
@ -38,6 +38,7 @@
|
||||
"minimum_chrome_version": "22.0",
|
||||
"options_page": "dashboard.html",
|
||||
"permissions": [
|
||||
"contextMenus",
|
||||
"downloads",
|
||||
"storage",
|
||||
"tabs",
|
||||
|
@ -17,6 +17,7 @@ ul {
|
||||
<ul>
|
||||
<li><input id="collapse-blocked" type="checkbox"> <label data-i18n="settingsCollapseBlockedPrompt"></label>
|
||||
<li><input id="icon-badge" type="checkbox"> <label data-i18n="settingsIconBadgePrompt"></label>
|
||||
<li><input id="context-menu-enabled" type="checkbox"><label data-i18n="settingsContextMenuPrompt"></label>
|
||||
</ul>
|
||||
|
||||
<script src="js/udom.js"></script>
|
||||
|
Loading…
Reference in New Issue
Block a user