diff --git a/platform/chromium/vapi-client.js b/platform/chromium/vapi-client.js index 4b0f56256..d1d096605 100644 --- a/platform/chromium/vapi-client.js +++ b/platform/chromium/vapi-client.js @@ -25,14 +25,13 @@ /******************************************************************************/ -(function() { +(function(self) { 'use strict'; /******************************************************************************/ var vAPI = self.vAPI = self.vAPI || {}; - var chrome = self.chrome; // https://github.com/gorhill/uBlock/issues/456 @@ -40,8 +39,10 @@ var chrome = self.chrome; if ( vAPI.vapiClientInjected ) { return; } -vAPI.vapiClientInjected = true; +vAPI.vapiClientInjected = true; +vAPI.sessionId = String.fromCharCode(Date.now() % 25 + 97) + + Math.random().toString(36).slice(2); vAPI.chrome = true; /******************************************************************************/ @@ -85,21 +86,14 @@ var messagingConnector = function(response) { /******************************************************************************/ -var uniqueId = function() { - return Math.random().toString(36).slice(2); -}; - -/******************************************************************************/ - vAPI.messaging = { port: null, channels: {}, listeners: {}, requestId: 1, - connectorId: uniqueId(), setup: function() { - this.port = chrome.runtime.connect({name: this.connectorId}); + this.port = chrome.runtime.connect({name: vAPI.sessionId}); this.port.onMessage.addListener(messagingConnector); }, @@ -150,6 +144,6 @@ vAPI.messaging = { /******************************************************************************/ -})(); +})(this); /******************************************************************************/ diff --git a/platform/firefox/vapi-client.js b/platform/firefox/vapi-client.js index c9caa85d4..2d35b2e12 100644 --- a/platform/firefox/vapi-client.js +++ b/platform/firefox/vapi-client.js @@ -31,8 +31,10 @@ /******************************************************************************/ -self.vAPI = self.vAPI || {}; +var vAPI = self.vAPI = self.vAPI || {}; vAPI.firefox = true; +vAPI.sessionId = String.fromCharCode(Date.now() % 25 + 97) + + Math.random().toString(36).slice(2); /******************************************************************************/ diff --git a/platform/safari/vapi-client.js b/platform/safari/vapi-client.js index 2e6285f5f..3eb57e954 100644 --- a/platform/safari/vapi-client.js +++ b/platform/safari/vapi-client.js @@ -21,7 +21,7 @@ /******************************************************************************/ // For non background pages -(function() { +(function(self) { 'use strict'; var vAPI = self.vAPI = self.vAPI || {}; if(vAPI.vapiClientInjected) { @@ -29,7 +29,8 @@ } vAPI.vapiClientInjected = true; vAPI.safari = true; - + vAPI.sessionId = String.fromCharCode(Date.now() % 25 + 97) + + Math.random().toString(36).slice(2); /******************************************************************************/ var messagingConnector = function(response) { if(!response) { @@ -63,26 +64,21 @@ } }; /******************************************************************************/ - var uniqueId = function() { - return Math.random().toString(36).slice(2); - }; - /******************************************************************************/ // Relevant? // https://developer.apple.com/library/safari/documentation/Tools/Conceptual/SafariExtensionGuide/MessagesandProxies/MessagesandProxies.html#//apple_ref/doc/uid/TP40009977-CH14-SW12 vAPI.messaging = { channels: {}, listeners: {}, requestId: 1, - connectorId: uniqueId(), setup: function() { if(typeof safari === "undefined") { return; } this.connector = function(msg) { // messages from the background script are sent to every frame, - // so we need to check the connectorId to accept only + // so we need to check the vAPI.sessionId to accept only // what is meant for the current context - if(msg.name === vAPI.messaging.connectorId || msg.name === 'broadcast') { + if(msg.name === vAPI.sessionId || msg.name === 'broadcast') { messagingConnector(msg.message); } }; @@ -131,7 +127,7 @@ return; } safari.extension.globalPage.contentWindow.vAPI.messaging.onMessage({ - name: vAPI.messaging.connectorId, + name: vAPI.sessionId, message: message, target: { page: { @@ -142,7 +138,7 @@ } }); } else { - safari.self.tab.dispatchMessage(vAPI.messaging.connectorId, message); + safari.self.tab.dispatchMessage(vAPI.sessionId, message); } }, close: function() { @@ -215,8 +211,7 @@ var firstMutation = function() { document.removeEventListener("DOMContentLoaded", firstMutation, true); firstMutation = false; - var randEventName = uniqueId(); - document.addEventListener(randEventName, function(e) { + document.addEventListener(vAPI.sessionId, function(e) { if(shouldBlockDetailedRequest(e.detail)) { e.detail.url = false; } @@ -225,7 +220,7 @@ var tmpScript = "\ (function() {\ var block = function(u, t) {\ -var e = new CustomEvent('" + randEventName + "', {\ +var e = new CustomEvent('" + vAPI.sessionId + "', {\ detail: {\ url: u,\ type: t\ @@ -298,5 +293,5 @@ return r;\ safari.self.tab.setContextMenuEventUserInfo(e, details); }; self.addEventListener("contextmenu", onContextMenu, true); -})(); +})(this); /******************************************************************************/ diff --git a/src/epicker.html b/src/epicker.html new file mode 100644 index 000000000..7b0d081ca --- /dev/null +++ b/src/epicker.html @@ -0,0 +1,120 @@ + + + + + + +
+ +
+ + + +
+
+ + diff --git a/src/js/element-picker.js b/src/js/element-picker.js index 7915aea4f..e521dd4c9 100644 --- a/src/js/element-picker.js +++ b/src/js/element-picker.js @@ -125,12 +125,7 @@ if ( window.top !== window ) { return; } -// https://github.com/gorhill/uBlock/issues/314#issuecomment-58878112 -// Using an id makes uBlock's CSS rules more specific, thus prevents -// surrounding external rules from winning over own rules. -var µBlockId = CSS.escape('µBlock'); - -var pickerRoot = document.getElementById(µBlockId); +var pickerRoot = document.getElementById(vAPI.sessionId); if ( pickerRoot ) { return; @@ -138,22 +133,22 @@ if ( pickerRoot ) { var localMessager = vAPI.messaging.channel('element-picker.js'); -var svgns = 'http://www.w3.org/2000/svg'; - var svgRoot = null; var svgOcean = null; var svgIslands = null; -var divDialog = null; +var frameDialog = null; +var dialogBody = null; var taCandidate = null; var urlNormalizer = null; +var lastDetails = null; + var netFilterCandidates = []; var cosmeticFilterCandidates = []; var targetElements = []; var svgWidth = 0; var svgHeight = 0; -var elementFromPointCSSProperty = 'pointerEvents'; var onSvgHoveredTimer = null; /******************************************************************************/ @@ -189,20 +184,6 @@ var unpausePicker = function() { /******************************************************************************/ -var pickerRootDistance = function(elem) { - var distance = 0; - while ( elem ) { - if ( elem === pickerRoot ) { - return distance; - } - elem = elem.parentNode; - distance += 1; - } - return -1; -}; - -/******************************************************************************/ - var highlightElements = function(elems, force) { // To make mouse move handler more efficient if ( !force && elems.length === targetElements.length ) { @@ -479,7 +460,7 @@ var userFilterFromCandidate = function() { var onCandidateChanged = function() { var elems = elementsFromFilter(taCandidate.value); - divDialog.querySelector('#create').disabled = elems.length === 0; + dialogBody.querySelector('#create').disabled = elems.length === 0; highlightElements(elems); }; @@ -554,7 +535,7 @@ var onDialogClicked = function(ev) { stopPicker(); } - else if ( ev.target.tagName.toLowerCase() === 'li' && pickerRootDistance(ev.target) === 5 ) { + else if ( ev.target.parentNode.classList.contains('changeFilter') ) { taCandidate.value = candidateFromFilterChoice(filterChoiceFromEvent(ev)); onCandidateChanged(); } @@ -583,7 +564,7 @@ var showDialog = function(options) { // Create lists of candidate filters var populate = function(src, des) { - var root = divDialog.querySelector(des); + var root = dialogBody.querySelector(des); var ul = root.querySelector('ul'); removeAllChildren(ul); var li; @@ -598,8 +579,8 @@ var showDialog = function(options) { populate(netFilterCandidates, '#netFilters'); populate(cosmeticFilterCandidates, '#cosmeticFilters'); - divDialog.querySelector('ul').style.display = netFilterCandidates.length || cosmeticFilterCandidates.length ? '' : 'none'; - divDialog.querySelector('#create').disabled = true; + dialogBody.querySelector('ul').style.display = netFilterCandidates.length || cosmeticFilterCandidates.length ? '' : 'none'; + dialogBody.querySelector('#create').disabled = true; // Auto-select a candidate filter var filterChoice = { @@ -621,17 +602,19 @@ var showDialog = function(options) { taCandidate.value = candidateFromFilterChoice(filterChoice); onCandidateChanged(); } + + frameDialog.style.height = dialogBody.offsetHeight + 'px'; }; /******************************************************************************/ var elementFromPoint = function(x, y) { - svgRoot.style[elementFromPointCSSProperty] = 'none'; + svgRoot.style.pointerEvents = 'none'; var elem = document.elementFromPoint(x, y); if ( elem === document.body || elem === document.documentElement ) { elem = null; } - svgRoot.style[elementFromPointCSSProperty] = ''; + svgRoot.style.pointerEvents = ''; return elem; }; @@ -699,12 +682,13 @@ var stopPicker = function() { window.removeEventListener('keydown', onKeyPressed, true); window.removeEventListener('scroll', onScrolled, true); taCandidate.removeEventListener('input', onCandidateChanged); - divDialog.removeEventListener('click', onDialogClicked); + dialogBody.removeEventListener('click', onDialogClicked); svgRoot.removeEventListener('mousemove', onSvgHovered); svgRoot.removeEventListener('click', onSvgClicked); pickerRoot.parentNode.removeChild(pickerRoot); pickerRoot = - divDialog = + frameDialog = + dialogBody = svgRoot = svgOcean = svgIslands = taCandidate = urlNormalizer = null; @@ -715,267 +699,29 @@ var stopPicker = function() { /******************************************************************************/ -var startPicker = function(details) { - pickerRoot = document.createElement('div'); - pickerRoot.id = µBlockId; - pickerRoot.setAttribute('lang', navigator.language); +var onFrameReady = function() { + var elem; + var details = lastDetails; + this.onload = lastDetails = null; - var pickerStyle = document.createElement('style'); - pickerStyle.setAttribute('scoped', ''); - pickerStyle.textContent = [ - '#µBlock, #µBlock * {', - 'background: transparent;', - 'background-image: none;', - 'border: 0;', - 'border-radius: 0;', - 'box-shadow: none;', - 'color: #000;', - 'display: inline;', - 'float: none;', - 'font: 12px sans-serif;', - 'height: auto;', - 'letter-spacing: normal;', - 'margin: 0;', - 'max-width: none;', - 'min-height: 0;', - 'min-width: 0;', - 'outline: 0;', - 'overflow: visible;', - 'padding: 0;', - 'text-transform: none;', - 'vertical-align: baseline;', - 'width: auto;', - 'z-index: auto;', - '}', - '#µBlock {', - 'position: absolute;', - 'top: 0;', - 'left: 0;', - '}', - '#µBlock style, #µBlock script {', - 'display: none;', - '}', - '#µBlock ul, #µBlock li, #µBlock div {', - 'display: block;', - '}', - '#µBlock *::selection {', - 'background-color: Highlight;', - 'color: HighlightText;', - '}', - '#µBlock button {', - 'border: 1px solid #aaa !important;', - 'padding: 6px 8px 4px 8px;', - 'box-sizing: border-box;', - 'box-shadow: none;', - 'border-radius: 3px;', - 'display: inline;', - 'line-height: 1;', - 'color: #444;', - 'background-color: #ccc;', - 'cursor: pointer;', - '}', - '#µBlock button:hover {', - 'background: none;', - 'background-color: #eee;', - 'background-image: none;', - '}', - '#µBlock button:disabled {', - 'color: #999;', - 'background-color: #ccc;', - '}', - '#µBlock button#create:not(:disabled) {', - 'background-color: #ffdca8;', - '}', - '#µBlock > svg {', - 'position: absolute;', - 'top: 0;', - 'left: 0;', - 'pointer-events: auto;', - 'cursor: crosshair;', - 'z-index: 4999999999;', - '}', - '#µBlock.paused > svg {', - 'cursor: wait;', - '}', - '#µBlock > svg > path:first-child {', - 'fill: rgba(0,0,0,0.75);', - 'fill-rule: evenodd;', - '}', - '#µBlock > svg > path + path {', - 'stroke: #F00;', - 'stroke-width: 0.5px;', - 'fill: rgba(255,0,0,0.25);', - '}', - '#µBlock > div {', - 'background-color: rgba(255,255,255,0.9);', - 'bottom: 4px;', - 'display: none;', - 'font: 12px sans-serif;', - 'padding: 4px;', - 'position: fixed;', - 'right: 4px;', - 'width: 30em;', - 'z-index: 5999999999;', - '}', - '#µBlock.paused > div {', - 'opacity: 0.2;', - 'display: block;', - '}', - '#µBlock.paused > div:hover {', - 'opacity: 1;', - '}', - '#µBlock > div > div {', - 'box-sizing: border-box;', - 'display: inline-block;', - 'height: 8em;', - 'padding: 0;', - 'position: relative;', - 'width: 100%;', - '}', - '#µBlock > div > div > textarea {', - 'background-color: white;', - 'border: 1px solid #ccc;', - 'box-sizing: border-box;', - 'font: 11px monospace;', - 'height: 100% !important;', - 'max-height: 100% !important;', - 'min-height: 100% !important;', - 'overflow: hidden !important;', - 'padding: 2px;', - 'resize: none;', - 'width: 100% !important;', - '}', - '#µBlock > div > div > div {', - 'bottom: 2px;', - 'direction: ltr;', - 'opacity: 0.2;', - 'position: absolute;', - 'right: 2px;', - '}', - '#µBlock > div > div > div:hover {', - 'opacity: 1;', - '}', - '#µBlock > div > div > div > button {', - 'margin-left: 3px !important;', - '}', - '#µBlock > div > ul {', - 'margin: 0;', - 'list-style-type: none;', - 'text-align: left;', - 'overflow: hidden;', - '}', - '#µBlock > div > ul > li {', - 'padding-top: 3px;', - '}', - '#µBlock > div > ul > li > span:nth-of-type(1) {', - 'font-weight: bold;', - '}', - '#µBlock > div > ul > li > span:nth-of-type(2) {', - 'font-size: smaller;', - 'color: gray;', - '}', - '#µBlock > div > ul > li > ul {', - 'background-color: #eee;', - 'list-style-type: none;', - 'margin: 0 0 0 1em;', - 'overflow: hidden;', - 'text-align: left;', - '}', - '#µBlock > div > ul > li > ul > li {', - 'font: 11px monospace;', - 'white-space: nowrap;', - 'cursor: pointer;', - 'direction: ltr;', - '}', - '#µBlock > div > ul > li > ul > li:hover {', - 'background-color: rgba(255,255,255,1.0);', - '}', - '' - ].join('\n'); - pickerRoot.appendChild(pickerStyle); - - svgRoot = document.createElementNS(svgns, 'svg'); - svgRoot.appendChild(document.createElementNS(svgns, 'path')); - svgRoot.appendChild(document.createElementNS(svgns, 'path')); - svgWidth = document.documentElement.scrollWidth; - svgHeight = Math.max( - document.documentElement.scrollHeight, - window.scrollY + window.innerHeight + var parsedDom = (new DOMParser()).parseFromString( + details.frameContent, + 'text/html' + ); + var frameDoc = this.contentDocument; + frameDoc.documentElement.replaceChild( + frameDoc.adoptNode(parsedDom.head), + frameDoc.head + ); + frameDoc.documentElement.replaceChild( + frameDoc.adoptNode(parsedDom.body), + frameDoc.body ); - svgRoot.setAttribute('x', 0); - svgRoot.setAttribute('y', 0); - svgRoot.style.width = svgWidth + 'px'; - svgRoot.style.height = svgHeight + 'px'; - svgRoot.setAttribute('viewBox', '0 0 ' + svgWidth + ' ' + svgHeight); - svgOcean = svgRoot.firstChild; - svgIslands = svgRoot.lastChild; - pickerRoot.appendChild(svgRoot); - // TODO: do not rely on element ids, they could collide with whatever - // is used in the page. Just use built-in hierarchy of elements as - // selectors. - - divDialog = document.createElement('div'); - divDialog.innerHTML = [ - '
', - '', - '
', - '', - '', - '', - '
', - '
', - '' - ].join(''); - pickerRoot.appendChild(divDialog); - - // https://github.com/gorhill/uBlock/issues/344#issuecomment-60775958 - // Insert in `html` tag, not `body` tag. - document.documentElement.appendChild(pickerRoot); - svgRoot.addEventListener('click', onSvgClicked); - svgRoot.addEventListener('mousemove', onSvgHovered); - divDialog.addEventListener('click', onDialogClicked); - taCandidate = divDialog.querySelector('textarea'); + dialogBody = frameDoc.body; + dialogBody.addEventListener('click', onDialogClicked); + taCandidate = dialogBody.querySelector('textarea'); taCandidate.addEventListener('input', onCandidateChanged); - urlNormalizer = document.createElement('a'); - window.addEventListener('scroll', onScrolled, true); - window.addEventListener('keydown', onKeyPressed, true); - - highlightElements([], true); - - var i18nMap = { - '#µBlock > div': '@@bidi_dir', - '#create': 'create', - '#pick': 'pick', - '#quit': 'quit', - 'ul > li#netFilters > span:nth-of-type(1)': 'netFilters', - 'ul > li#cosmeticFilters > span:nth-of-type(1)': 'cosmeticFilters', - 'ul > li#cosmeticFilters > span:nth-of-type(2)': 'cosmeticFiltersHint' - }; - - if ( details.i18n['@@bidi_dir'] ) { - divDialog.style.direction = details.i18n['@@bidi_dir']; - delete i18nMap['#µBlock > div']; - } - - for ( var k in i18nMap ) { - if ( i18nMap.hasOwnProperty(k) === false ) { - continue; - } - divDialog.querySelector(k).firstChild.nodeValue = details.i18n[i18nMap[k]]; - } - - // First we test if pointer-events are hadnled in Node.elementFromPoint(). - // If the browser ignores pointer-events in Node.elementFromPoint(), - // then use the display property instead (e.g., for older Safari). - var elem = elementFromPoint(0, 0); - - if ( elem === svgRoot ) { - elementFromPointCSSProperty = 'display'; - } // Auto-select a specific target, if any, and if possible @@ -1028,6 +774,124 @@ var startPicker = function(details) { /******************************************************************************/ +var startPicker = function(details) { + pickerRoot = document.createElement('div'); + var pid = vAPI.sessionId; + pickerRoot.id = pid; + pickerRoot.setAttribute('lang', navigator.language); + + var pickerStyle = document.createElement('style'); + pickerStyle.setAttribute('scoped', ''); + pid = '#' + pid; + pickerStyle.textContent = [ + pid + ' {', + 'position: absolute;', + 'top: 0;', + 'left: 0;', + '}', + pid + ', ' + pid + ' > iframe, ' + pid + ' > svg {', + 'background: transparent;', + 'border: 0;', + 'border-radius: 0;', + 'box-shadow: none;', + 'display: inline;', + 'float: none;', + 'height: auto;', + 'margin: 0;', + 'max-width: none;', + 'min-height: 0;', + 'min-width: 0;', + 'outline: 0;', + 'overflow: visible;', + 'padding: 0;', + 'width: auto;', + 'z-index: auto;', + '}', + pid + ' > svg {', + 'position: absolute;', + 'top: 0;', + 'left: 0;', + 'pointer-events: auto;', + 'cursor: crosshair;', + 'z-index: 4999999999;', + '}', + pid + '.paused > svg {', + 'cursor: wait;', + '}', + pid + ' > svg > path:first-child {', + 'fill: rgba(0,0,0,0.75);', + 'fill-rule: evenodd;', + '}', + pid + ' > svg > path + path {', + 'stroke: #F00;', + 'stroke-width: 0.5px;', + 'fill: rgba(255,0,0,0.25);', + '}', + pid + ' > iframe {', + 'background-color: rgba(255,255,255,0.9);', + 'bottom: 4px;', + 'display: none;', + 'padding: 4px;', + 'position: fixed;', + 'right: 4px;', + 'width: 30em;', + 'z-index: 5999999999;', + '}', + pid + '.paused > iframe {', + 'opacity: 0.2;', + 'display: block;', + '}', + pid + '.paused > iframe:hover {', + 'opacity: 1;', + '}', + '' + ].join('\n'); + pickerRoot.appendChild(pickerStyle); + + var svgns = 'http://www.w3.org/2000/svg'; + svgRoot = document.createElementNS(svgns, 'svg'); + svgRoot.appendChild(document.createElementNS(svgns, 'path')); + svgRoot.appendChild(document.createElementNS(svgns, 'path')); + svgWidth = document.documentElement.scrollWidth; + svgHeight = Math.max( + document.documentElement.scrollHeight, + window.scrollY + window.innerHeight + ); + svgRoot.setAttribute('x', 0); + svgRoot.setAttribute('y', 0); + svgRoot.style.width = svgWidth + 'px'; + svgRoot.style.height = svgHeight + 'px'; + svgRoot.setAttribute('viewBox', '0 0 ' + svgWidth + ' ' + svgHeight); + svgOcean = svgRoot.firstChild; + svgIslands = svgRoot.lastChild; + pickerRoot.appendChild(svgRoot); + + // https://github.com/gorhill/uBlock/issues/344#issuecomment-60775958 + // Insert in `html` tag, not `body` tag. + document.documentElement.appendChild(pickerRoot); + svgRoot.addEventListener('click', onSvgClicked); + svgRoot.addEventListener('mousemove', onSvgHovered); + urlNormalizer = document.createElement('a'); + window.addEventListener('scroll', onScrolled, true); + window.addEventListener('keydown', onKeyPressed, true); + + highlightElements([], true); + + lastDetails = { + frameContent: details.frameContent, + clientX: details.clientX, + clientY: details.clientY, + target: details.target + }; + + frameDialog = document.createElement('iframe'); + frameDialog.setAttribute('seamless', ''); + frameDialog.onload = onFrameReady; + pickerRoot.appendChild(frameDialog); +}; + +/******************************************************************************/ + localMessager.send({ what: 'elementPickerArguments' }, startPicker); // So the shortcuts will be usable in Firefox diff --git a/src/js/messaging.js b/src/js/messaging.js index 22894d903..6d7176ce7 100644 --- a/src/js/messaging.js +++ b/src/js/messaging.js @@ -527,6 +527,40 @@ var µb = µBlock; var onMessage = function(request, sender, callback) { // Async switch ( request.what ) { + case 'elementPickerArguments': + var xhr = new XMLHttpRequest(); + xhr.open('GET', 'epicker.html', true); + xhr.overrideMimeType('text/html;charset=utf-8'); + xhr.responseType = 'text'; + xhr.onload = function() { + this.onload = null; + var i18n = { + bidi_dir: document.body.getAttribute('dir'), + create: vAPI.i18n('pickerCreate'), + pick: vAPI.i18n('pickerPick'), + quit: vAPI.i18n('pickerQuit'), + netFilters: vAPI.i18n('pickerNetFilters'), + cosmeticFilters: vAPI.i18n('pickerCosmeticFilters'), + cosmeticFiltersHint: vAPI.i18n('pickerCosmeticFiltersHint') + }; + var reStrings = /\{\{(\w+)\}\}/g; + var replacer = function(a0, string) { + return i18n[string]; + }; + + callback({ + frameContent: this.responseText.replace(reStrings, replacer), + target: µb.contextMenuTarget, + clientX: µb.contextMenuClientX, + clientY: µb.contextMenuClientY + }); + + µb.contextMenuTarget = ''; + µb.contextMenuClientX = -1; + µb.contextMenuClientY = -1; + }; + xhr.send(); + return; default: break; } @@ -535,26 +569,6 @@ var onMessage = function(request, sender, callback) { var response; switch ( request.what ) { - case 'elementPickerArguments': - response = { - i18n: { - '@@bidi_dir': document.body.getAttribute('dir'), - create: vAPI.i18n('pickerCreate'), - pick: vAPI.i18n('pickerPick'), - quit: vAPI.i18n('pickerQuit'), - netFilters: vAPI.i18n('pickerNetFilters'), - cosmeticFilters: vAPI.i18n('pickerCosmeticFilters'), - cosmeticFiltersHint: vAPI.i18n('pickerCosmeticFiltersHint') - }, - target: µb.contextMenuTarget, - clientX: µb.contextMenuClientX, - clientY: µb.contextMenuClientY - }; - µb.contextMenuTarget = ''; - µb.contextMenuClientX = -1; - µb.contextMenuClientY = -1; - break; - case 'createUserFilter': µb.appendUserFilters(request.filters); break;