1
0
mirror of https://github.com/gorhill/uBlock.git synced 2024-09-04 10:09:38 +02:00
This commit is contained in:
gorhill 2014-09-28 12:05:46 -04:00
parent b920b49d7d
commit 8d839d0230
10 changed files with 463 additions and 137 deletions

View File

@ -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>

View File

@ -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
View 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
};
/******************************************************************************/
})();

View File

@ -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;
}
}
});
/******************************************************************************/

View File

@ -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);
/******************************************************************************/
})();

View File

@ -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);

View File

@ -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);
};

View File

@ -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' });
};
/******************************************************************************/

View File

@ -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",

View File

@ -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>