Forked from simonzack/google_search_filter_plus.user.js
Created
May 19, 2016 22:54
-
-
Save botmtl/b87806daec94c6c99a1082b6a4d4a3b2 to your computer and use it in GitHub Desktop.
google search filter plus
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// ==UserScript== | |
// @name Google Search Filter Plus | |
// @description Filters google search results | |
// @namespace smk | |
// @license MPL 1.1; http://www.mozilla.org/MPL/MPL-1.1.html | |
// @include http://www.google.tld/ | |
// @include http://www.google.tld/?* | |
// @include http://www.google.tld/#*&q=* | |
// @include http://www.google.tld/#q=* | |
// @include http://www.google.tld/cse?* | |
// @include http://www.google.tld/custom?* | |
// @include http://www.google.tld/search?* | |
// @include https://encrypted.google.com/ | |
// @include https://encrypted.google.com/#*&q=* | |
// @include https://encrypted.google.com/search?* | |
// @include https://www.google.tld/ | |
// @include https://www.google.tld/?* | |
// @include https://www.google.tld/#*&q=* | |
// @include https://www.google.tld/#q=* | |
// @include https://www.google.tld/cse?* | |
// @include https://www.google.tld/custom?* | |
// @include https://www.google.tld/search?* | |
// @grant GM_addStyle | |
// @grant GM_getResourceText | |
// @grant GM_getResourceURL | |
// @grant GM_getValue | |
// @grant GM_registerMenuCommand | |
// @grant GM_setValue | |
// @require https://cdn.jsdelivr.net/jquery/2.1.3/jquery.min.js | |
// @require https://cdn.jsdelivr.net/jquery.ui/1.11.3/jquery-ui.min.js | |
// @require https://cdn.jsdelivr.net/jquery.event.drag/2.2/jquery.event.drag.min.js | |
// @require https://rawgit.com/mleibman/SlickGrid/2.1.0/slick.core.js | |
// @require https://rawgit.com/mleibman/SlickGrid/2.1.0/slick.editors.js | |
// @require https://rawgit.com/mleibman/SlickGrid/2.1.0/slick.grid.js | |
// @resource jquery-ui-css https://cdn.jsdelivr.net/jquery.ui/1.11.3/themes/smoothness/jquery-ui.min.css | |
// @resource jquery-ui-css/images/ui-bg_glass_55_fbf9ee_1x400.png https://cdn.jsdelivr.net/jquery.ui/1.11.3/themes/smoothness/images/ui-bg_glass_55_fbf9ee_1x400.png | |
// @resource jquery-ui-css/images/ui-bg_glass_65_ffffff_1x400.png https://cdn.jsdelivr.net/jquery.ui/1.11.3/themes/smoothness/images/ui-bg_glass_65_ffffff_1x400.png | |
// @resource jquery-ui-css/images/ui-bg_glass_75_dadada_1x400.png https://cdn.jsdelivr.net/jquery.ui/1.11.3/themes/smoothness/images/ui-bg_glass_75_dadada_1x400.png | |
// @resource jquery-ui-css/images/ui-bg_glass_75_e6e6e6_1x400.png https://cdn.jsdelivr.net/jquery.ui/1.11.3/themes/smoothness/images/ui-bg_glass_75_e6e6e6_1x400.png | |
// @resource jquery-ui-css/images/ui-bg_glass_95_fef1ec_1x400.png https://cdn.jsdelivr.net/jquery.ui/1.11.3/themes/smoothness/images/ui-bg_glass_95_fef1ec_1x400.png | |
// @resource jquery-ui-css/images/ui-bg_highlight-soft_75_cccccc_1x100.png https://cdn.jsdelivr.net/jquery.ui/1.11.3/themes/smoothness/images/ui-bg_highlight-soft_75_cccccc_1x100.png | |
// @resource jquery-ui-css/images/ui-icons_222222_256x240.png https://cdn.jsdelivr.net/jquery.ui/1.11.3/themes/smoothness/images/ui-icons_222222_256x240.png | |
// @resource jquery-ui-css/images/ui-icons_2e83ff_256x240.png https://cdn.jsdelivr.net/jquery.ui/1.11.3/themes/smoothness/images/ui-icons_2e83ff_256x240.png | |
// @resource jquery-ui-css/images/ui-icons_454545_256x240.png https://cdn.jsdelivr.net/jquery.ui/1.11.3/themes/smoothness/images/ui-icons_454545_256x240.png | |
// @resource jquery-ui-css/images/ui-icons_888888_256x240.png https://cdn.jsdelivr.net/jquery.ui/1.11.3/themes/smoothness/images/ui-icons_888888_256x240.png | |
// @resource jquery-ui-css/images/ui-icons_cd0a0a_256x240.png https://cdn.jsdelivr.net/jquery.ui/1.11.3/themes/smoothness/images/ui-icons_cd0a0a_256x240.png | |
// @resource slickgrid-css https://rawgit.com/mleibman/SlickGrid/2.1.0/slick.grid.css | |
// @resource slickgrid-css/images/sort-asc.gif https://rawgit.com/mleibman/SlickGrid/2.1.0/images/sort-asc.gif | |
// @resource slickgrid-css/images/sort-desc.gif https://rawgit.com/mleibman/SlickGrid/2.1.0/images/sort-desc.gif | |
// ==/UserScript== | |
/******/ (function(modules) { // webpackBootstrap | |
/******/ // The module cache | |
/******/ var installedModules = {}; | |
/******/ // The require function | |
/******/ function __webpack_require__(moduleId) { | |
/******/ // Check if module is in cache | |
/******/ if(installedModules[moduleId]) | |
/******/ return installedModules[moduleId].exports; | |
/******/ // Create a new module (and put it into the cache) | |
/******/ var module = installedModules[moduleId] = { | |
/******/ exports: {}, | |
/******/ id: moduleId, | |
/******/ loaded: false | |
/******/ }; | |
/******/ // Execute the module function | |
/******/ modules[moduleId].call(module.exports, module, module.exports, __webpack_require__); | |
/******/ // Flag the module as loaded | |
/******/ module.loaded = true; | |
/******/ // Return the exports of the module | |
/******/ return module.exports; | |
/******/ } | |
/******/ // expose the modules object (__webpack_modules__) | |
/******/ __webpack_require__.m = modules; | |
/******/ // expose the module cache | |
/******/ __webpack_require__.c = installedModules; | |
/******/ // __webpack_public_path__ | |
/******/ __webpack_require__.p = ""; | |
/******/ // Load entry module and return exports | |
/******/ return __webpack_require__(0); | |
/******/ }) | |
/************************************************************************/ | |
/******/ ([ | |
/* 0 */ | |
/***/ function(module, exports, __webpack_require__) { | |
"use strict"; | |
var LogTime = __webpack_require__(2).LogTime; | |
function main() { | |
LogTime.start(); | |
__webpack_require__(3); | |
let config = __webpack_require__(4); | |
let SearchGui = __webpack_require__(5).SearchGui; | |
let searchGui; | |
if (SearchGui.isSearchPage()) { | |
searchGui = new SearchGui(); | |
searchGui.filterResults(SearchGui.getResults()); | |
} | |
for (let pluginName of config.plugins) __webpack_require__(1)("./" + pluginName.toLowerCase())(searchGui); | |
LogTime.snap("Total init time"); | |
} | |
main(); | |
/***/ }, | |
/* 1 */ | |
/***/ function(module, exports, __webpack_require__) { | |
var map = { | |
"./customsearch": 6, | |
"./customsearch.js": 6, | |
"./instant": 7, | |
"./instant.js": 7 | |
}; | |
function webpackContext(req) { | |
return __webpack_require__(webpackContextResolve(req)); | |
}; | |
function webpackContextResolve(req) { | |
return map[req] || (function() { throw new Error("Cannot find module '" + req + "'.") }()); | |
}; | |
webpackContext.keys = function webpackContextKeys() { | |
return Object.keys(map); | |
}; | |
webpackContext.resolve = webpackContextResolve; | |
module.exports = webpackContext; | |
webpackContext.id = 1; | |
/***/ }, | |
/* 2 */ | |
/***/ function(module, exports, __webpack_require__) { | |
"use strict"; | |
let Logger = exports.Logger = { | |
msg: (function (_msg) { | |
var _msgWrapper = function msg(_x) { | |
return _msg.apply(this, arguments); | |
}; | |
_msgWrapper.toString = function () { | |
return _msg.toString(); | |
}; | |
return _msgWrapper; | |
})(function (msg) { | |
console.log(msg); | |
}), | |
error: function error(msg) { | |
console.log("Error: " + msg); | |
} | |
}; | |
let LogTime = exports.LogTime = { | |
curTime: null, | |
start: function start() { | |
this.curTime = new Date().getTime(); | |
}, | |
snap: function snap(msg) { | |
console.log("" + msg + ": " + (new Date().getTime() - this.curTime) + "ms"); | |
} | |
}; | |
Object.defineProperty(exports, "__esModule", { | |
value: true | |
}); | |
/***/ }, | |
/* 3 */ | |
/***/ function(module, exports, __webpack_require__) { | |
"use strict"; | |
var _interopRequire = function (obj) { return obj && obj.__esModule ? obj["default"] : obj; }; | |
var _prototypeProperties = function (child, staticProps, instanceProps) { if (staticProps) Object.defineProperties(child, staticProps); if (instanceProps) Object.defineProperties(child.prototype, instanceProps); }; | |
var _classCallCheck = function (instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }; | |
/* global $, GM_addStyle, GM_registerMenuCommand, Slick*/ | |
var config = _interopRequire(__webpack_require__(4)); | |
var prefStyle = _interopRequire(__webpack_require__(12)); | |
var prefHTML = _interopRequire(__webpack_require__(11)); | |
var CombinedMultiMatcher = __webpack_require__(8).CombinedMultiMatcher; | |
var _gfpUtils = __webpack_require__(9); | |
var addStyleResolve = _gfpUtils.addStyleResolve; | |
var pad = _gfpUtils.pad; | |
let Pref = (function () { | |
function Pref() { | |
_classCallCheck(this, Pref); | |
let dialog = null; | |
let resourcesAdded = false; | |
GM_registerMenuCommand("Google Search Filter +", () => { | |
if (!resourcesAdded) this.addResources(); | |
if (dialog) return; | |
dialog = new PrefDialog().dialog.on("dialogclose", () => dialog = null); | |
}, null); | |
} | |
_prototypeProperties(Pref, null, { | |
addResources: { | |
value: function addResources() { | |
addStyleResolve("jquery-ui-css"); | |
addStyleResolve("slickgrid-css"); | |
GM_addStyle(prefStyle.toString()); | |
}, | |
writable: true, | |
configurable: true | |
} | |
}); | |
return Pref; | |
})(); | |
let PrefDialog = (function () { | |
function PrefDialog() { | |
_classCallCheck(this, PrefDialog); | |
this.dialog = $(prefHTML).dialog(Object.assign({ title: "Google Search Filter +" }, this.dialogConfig)); | |
this.grid = this.dialog.find(".grid"); | |
this.bindImport(); | |
this.bindExport(); | |
this.addGrid(); | |
} | |
_prototypeProperties(PrefDialog, null, { | |
dialogConfig: { | |
get: function () { | |
return { | |
width: $(window).width() * 0.5, | |
height: $(window).height() * 0.5, | |
close: function close() { | |
$(this).remove(); | |
} | |
}; | |
}, | |
configurable: true | |
}, | |
bindImport: { | |
value: function bindImport() { | |
this.dialog.find(".import").click(e => { | |
$("<textarea></textarea>").dialog(Object.assign({ | |
title: "Import", | |
buttons: [{ | |
text: "OK", | |
click: function click() { | |
config.filtersObject = JSON.parse($(this).val()); | |
config.flushFilters(); | |
config.constructor.call(config); | |
$(this).dialog("close"); | |
} | |
}, { text: "Cancel", click: function click() { | |
$(this).dialog("close"); | |
} }], | |
create: function create() { | |
setTimeout(() => this.select(), 0); | |
} | |
}, this.dialogConfig)); | |
return false; | |
}); | |
}, | |
writable: true, | |
configurable: true | |
}, | |
bindExport: { | |
value: function bindExport() { | |
this.dialog.find(".export").click(e => { | |
$("<textarea></textarea>").attr("readonly", "readonly").val(JSON.stringify(config.filtersObject, null, 2)).dialog(Object.assign({ | |
title: "Export", | |
buttons: [{ text: "Close", click: function click() { | |
$(this).dialog("close"); | |
} }], | |
create: function create() { | |
setTimeout(() => { | |
this.focus();this.setSelectionRange(0, this.value.length, "backward"); | |
}, 0); | |
} | |
}, this.dialogConfig)); | |
return false; | |
}); | |
}, | |
writable: true, | |
configurable: true | |
}, | |
addGrid: { | |
value: function addGrid() { | |
let data = []; | |
for (let filter of config.filters) { | |
data.push({ | |
text: filter.text, | |
slow: CombinedMultiMatcher.isSlowFilter(filter), | |
enabled: !filter.disabled, | |
hitCount: filter.hitCount, | |
lastHit: filter.lastHit }); | |
} | |
let slickGrid = new Slick.Grid(this.grid, data, [{ | |
id: "text", field: "text", name: "Filter rule", width: 300, sortable: true, editor: Slick.Editors.Text | |
}, { | |
id: "slow", field: "slow", name: "!", width: 1, sortable: true | |
}, { | |
id: "enabled", field: "enabled", name: "Enabled", width: 40, sortable: true, | |
formatter: Slick.Formatters.Checkmark, editor: Slick.Editors.Checkbox | |
}, { | |
id: "hitCount", field: "hitCount", name: "Hits", width: 1, sortable: true, editor: Slick.Editors.Text | |
}, { | |
id: "lastHit", field: "lastHit", name: "Last hit", width: 110, sortable: true, | |
formatter: (row, cell, value, columnDef, dataContext) => { | |
let date = new Date(value); | |
return dataContext.hitCount > 0 ? "" + date.getFullYear() + "-" + pad(date.getMonth() + 1, 2) + "-" + pad(date.getDate(), 2) + " " + ("" + pad(date.getHours() + 1, 2) + ":" + pad(date.getMinutes(), 2) + ":" + pad(date.getSeconds(), 2) + ":") + ("" + pad(date.getMilliseconds(), 3)) : ""; | |
}, editor: Slick.Editors.Text | |
}], { | |
enableCellNavigation: true, | |
enableColumnReorder: false, | |
forceFitColumns: true }); | |
slickGrid.onSort.subscribe((e, args) => { | |
let field = args.sortCol.field; | |
let res = args.sortAsc ? 1 : -1; | |
data.sort((x, y) => x[field] > y[field] ? res : x[field] < y[field] ? -res : 0); | |
slickGrid.invalidateAllRows(); | |
slickGrid.render(); | |
}); | |
let height = this.grid.height(); | |
this.dialog.on("dialogresize", (e, ui) => { | |
this.grid.css("height", "" + (height + (ui.size.height - ui.originalSize.height)) + "px"); | |
slickGrid.resizeCanvas(); | |
}); | |
}, | |
writable: true, | |
configurable: true | |
} | |
}); | |
return PrefDialog; | |
})(); | |
module.exports = new Pref(); | |
/***/ }, | |
/* 4 */ | |
/***/ function(module, exports, __webpack_require__) { | |
"use strict"; | |
var _prototypeProperties = function (child, staticProps, instanceProps) { if (staticProps) Object.defineProperties(child, staticProps); if (instanceProps) Object.defineProperties(child.prototype, instanceProps); }; | |
var _classCallCheck = function (instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }; | |
/* globals GM_getValue, GM_setValue */ | |
var Filter = __webpack_require__(10).Filter; | |
let Filters = (function () { | |
function Filters(filters) { | |
_classCallCheck(this, Filters); | |
this._filters = filters; | |
this._callbacks = []; | |
} | |
_prototypeProperties(Filters, null, (function () { | |
var _prototypeProperties2 = { | |
get: { | |
value: function get(i) { | |
return this._filters[i]; | |
}, | |
writable: true, | |
configurable: true | |
}, | |
trigger: { | |
value: function trigger(type, value) { | |
for (let cb of this._callbacks) cb(type, value); | |
}, | |
writable: true, | |
configurable: true | |
}, | |
push: { | |
value: function push(filter) { | |
this._filters.push(filter); | |
this.trigger("push", filter); | |
}, | |
writable: true, | |
configurable: true | |
}, | |
remove: { | |
value: function remove(filter) { | |
this._filters.pop(this._filters.indexOf(filter)); | |
this.trigger("remove", filter); | |
}, | |
writable: true, | |
configurable: true | |
}, | |
update: { | |
value: function update(filter) { | |
this.trigger("update", filter); | |
}, | |
writable: true, | |
configurable: true | |
}, | |
observe: { | |
value: function observe(cb) { | |
this._callbacks.push(cb); | |
}, | |
writable: true, | |
configurable: true | |
}, | |
unobserve: { | |
value: function unobserve(cb) { | |
this._callbacks.pop(this._callbacks.indexOf(cb)); | |
}, | |
writable: true, | |
configurable: true | |
} | |
}; | |
_prototypeProperties2[Symbol.iterator] = { | |
value: function () { | |
return this._filters[Symbol.iterator](); | |
}, | |
writable: true, | |
configurable: true | |
}; | |
return _prototypeProperties2; | |
})()); | |
return Filters; | |
})(); | |
let Config = (function () { | |
function Config() { | |
_classCallCheck(this, Config); | |
this.plugins = ["customSearch", "instant"]; | |
this.allowHidden = GM_getValue("allowHidden", true); | |
this.filtersObject = JSON.parse(GM_getValue("filters", "{}")); | |
let filters = []; | |
for (let key in this.filtersObject) filters.push(Filter.fromObject(key, this.filtersObject[key])); | |
this.filters = new Filters(filters); | |
this.filters.observe((type, value) => { | |
switch (type) { | |
case "push": | |
this.filtersObject[value.text] = value.toObject();break; | |
case "remove": | |
delete this.filtersObject[value.text];break; | |
case "update": | |
this.filtersObject[value.text] = value.toObject();break; | |
} | |
}); | |
} | |
_prototypeProperties(Config, null, { | |
flushAllowHidden: { | |
value: function flushAllowHidden() { | |
GM_setValue("allowHidden", this.allowHidden); | |
}, | |
writable: true, | |
configurable: true | |
}, | |
flushFilters: { | |
value: function flushFilters() { | |
GM_setValue("filters", JSON.stringify(this.filtersObject)); | |
}, | |
writable: true, | |
configurable: true | |
} | |
}); | |
return Config; | |
})(); | |
module.exports = new Config(); | |
/***/ }, | |
/* 5 */ | |
/***/ function(module, exports, __webpack_require__) { | |
"use strict"; | |
var _defaults = function (obj, defaults) { var keys = Object.getOwnPropertyNames(defaults); for (var i = 0; i < keys.length; i++) { var key = keys[i]; var value = Object.getOwnPropertyDescriptor(defaults, key); if (value && value.configurable && obj[key] === undefined) { Object.defineProperty(obj, key, value); } } return obj; }; | |
var _interopRequire = function (obj) { return obj && obj.__esModule ? obj["default"] : obj; }; | |
var _inherits = function (subClass, superClass) { if (typeof superClass !== "function" && superClass !== null) { throw new TypeError("Super expression must either be null or a function, not " + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) _defaults(subClass, superClass); }; | |
var _prototypeProperties = function (child, staticProps, instanceProps) { if (staticProps) Object.defineProperties(child, staticProps); if (instanceProps) Object.defineProperties(child.prototype, instanceProps); }; | |
var _classCallCheck = function (instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }; | |
/* globals GM_addStyle */ | |
var config = _interopRequire(__webpack_require__(4)); | |
var guiStyle = _interopRequire(__webpack_require__(13)); | |
var _gfpFilter = __webpack_require__(10); | |
var BlockingFilter = _gfpFilter.BlockingFilter; | |
var MultiRegExpFilter = _gfpFilter.MultiRegExpFilter; | |
var CombinedMultiMatcher = __webpack_require__(8).CombinedMultiMatcher; | |
var cache = __webpack_require__(9).cache; | |
let NodeData = exports.NodeData = (function () { | |
function NodeData(node) { | |
_classCallCheck(this, NodeData); | |
this.node = node; | |
} | |
_prototypeProperties(NodeData, null, { | |
act: { | |
value: function act(action, filter) { | |
if (this.action == action) { | |
this.redo(filter); | |
return true; | |
} else if (this.action !== null) { | |
this.undo(); | |
delete this.redo; | |
delete this.undo; | |
} | |
this.action = action; | |
}, | |
writable: true, | |
configurable: true | |
}, | |
getChildren: { | |
value: function* getChildren() {}, | |
writable: true, | |
configurable: true | |
}, | |
redo: { | |
value: function redo() {}, | |
writable: true, | |
configurable: true | |
}, | |
undo: { | |
value: function undo() {}, | |
writable: true, | |
configurable: true | |
} | |
}); | |
return NodeData; | |
})(); | |
NodeData.attrs = ["url", "title", "summary"]; | |
NodeData.prototype.linkArea = null; | |
NodeData.prototype.url = null; | |
NodeData.prototype.title = null; | |
NodeData.prototype.summary = null; | |
NodeData.prototype.action = null; | |
let ResultsData = exports.ResultsData = (function (NodeData) { | |
function ResultsData() { | |
_classCallCheck(this, ResultsData); | |
if (NodeData != null) { | |
NodeData.apply(this, arguments); | |
} | |
} | |
_inherits(ResultsData, NodeData); | |
_prototypeProperties(ResultsData, null, { | |
getChildren: { | |
value: function* getChildren() { | |
for (let child of this.node.querySelectorAll("li.g")) { | |
if (child.id == "imagebox_bigimages") { | |
yield new ImageContainerData(child); | |
} else if (child.id == "lclbox") { | |
yield new MapContainerData(child); | |
} else if (child.classList.contains("mnr-c")) { | |
yield new KnowledgeData(child); | |
} else if (child.classList.contains("card-section")) { | |
yield new NewsData(child); | |
} else if (!child.classList.contains("obcontainer")) { | |
yield new TextData(child); | |
} | |
} | |
}, | |
writable: true, | |
configurable: true | |
} | |
}); | |
return ResultsData; | |
})(NodeData); | |
let CommonData = (function (NodeData) { | |
function CommonData() { | |
_classCallCheck(this, CommonData); | |
if (NodeData != null) { | |
NodeData.apply(this, arguments); | |
} | |
} | |
_inherits(CommonData, NodeData); | |
_prototypeProperties(CommonData, null, { | |
linkArea: { | |
get: function () { | |
return cache(this, "linkArea", this.node.querySelector("cite").parentNode); | |
}, | |
configurable: true | |
}, | |
url: { | |
get: function () { | |
return cache(this, "url", this.node.querySelector("h3.r>a").href); | |
}, | |
configurable: true | |
}, | |
title: { | |
get: function () { | |
return cache(this, "title", this.node.querySelector("h2.r, h3.r").textContent); | |
}, | |
configurable: true | |
} | |
}); | |
return CommonData; | |
})(NodeData); | |
let KnowledgeData = (function (CommonData) { | |
function KnowledgeData() { | |
_classCallCheck(this, KnowledgeData); | |
if (CommonData != null) { | |
CommonData.apply(this, arguments); | |
} | |
} | |
_inherits(KnowledgeData, CommonData); | |
return KnowledgeData; | |
})(CommonData); | |
let TextData = (function (CommonData) { | |
function TextData() { | |
_classCallCheck(this, TextData); | |
if (CommonData != null) { | |
CommonData.apply(this, arguments); | |
} | |
} | |
_inherits(TextData, CommonData); | |
_prototypeProperties(TextData, null, { | |
summary: { | |
get: function () { | |
return cache(this, "summary", this.node.querySelector("div.s").textContent); | |
}, | |
configurable: true | |
} | |
}); | |
return TextData; | |
})(CommonData); | |
let NewsData = (function (NodeData) { | |
function NewsData() { | |
_classCallCheck(this, NewsData); | |
if (NodeData != null) { | |
NodeData.apply(this, arguments); | |
} | |
} | |
_inherits(NewsData, NodeData); | |
_prototypeProperties(NewsData, null, { | |
linkArea: { | |
get: function () { | |
return cache(this, "linkArea", this.node.querySelector("cite").parentNode); | |
}, | |
configurable: true | |
}, | |
url: { | |
get: function () { | |
return cache(this, "url", this.node.querySelector("a").href); | |
}, | |
configurable: true | |
}, | |
title: { | |
get: function () { | |
return cache(this, "title", this.node.querySelector("a").textContent); | |
}, | |
configurable: true | |
}, | |
summary: { | |
get: function () { | |
let node = this.node.querySelector("span.s"); | |
return cache(this, "summary", node ? node.textContent : null); | |
}, | |
configurable: true | |
} | |
}); | |
return NewsData; | |
})(NodeData); | |
let MapContainerData = (function (NodeData) { | |
function MapContainerData() { | |
_classCallCheck(this, MapContainerData); | |
if (NodeData != null) { | |
NodeData.apply(this, arguments); | |
} | |
} | |
_inherits(MapContainerData, NodeData); | |
_prototypeProperties(MapContainerData, null, { | |
getChildren: { | |
value: function* getChildren() { | |
for (let child of this.node.querySelectorAll("div.g")) yield new MapData(child); | |
}, | |
writable: true, | |
configurable: true | |
} | |
}); | |
return MapContainerData; | |
})(NodeData); | |
let MapData = (function (CommonData) { | |
function MapData() { | |
_classCallCheck(this, MapData); | |
if (CommonData != null) { | |
CommonData.apply(this, arguments); | |
} | |
} | |
_inherits(MapData, CommonData); | |
return MapData; | |
})(CommonData); | |
let ImageContainerData = (function (NodeData) { | |
function ImageContainerData() { | |
_classCallCheck(this, ImageContainerData); | |
if (NodeData != null) { | |
NodeData.apply(this, arguments); | |
} | |
} | |
_inherits(ImageContainerData, NodeData); | |
_prototypeProperties(ImageContainerData, null, { | |
getChildren: { | |
value: function* getChildren() { | |
for (let child of this.node.querySelectorAll(".bia")) yield new ImageData(child); | |
}, | |
writable: true, | |
configurable: true | |
} | |
}); | |
return ImageContainerData; | |
})(NodeData); | |
let ImageData = (function (NodeData) { | |
function ImageData() { | |
_classCallCheck(this, ImageData); | |
if (NodeData != null) { | |
NodeData.apply(this, arguments); | |
} | |
} | |
_inherits(ImageData, NodeData); | |
_prototypeProperties(ImageData, null, { | |
url: { | |
get: function () { | |
return cache(this, "url", this.node.href); | |
}, | |
configurable: true | |
} | |
}); | |
return ImageData; | |
})(NodeData); | |
let SearchGui = exports.SearchGui = (function () { | |
function SearchGui() { | |
_classCallCheck(this, SearchGui); | |
this.matcher = new CombinedMultiMatcher(3); | |
for (let filter of config.filters) this.matcher.add(filter); | |
config.filters.observe((type, value) => { | |
switch (type) { | |
case "push": | |
this.matcher.add(value);break; | |
case "remove": | |
this.matcher.remove(value);break; | |
} | |
}); | |
this.nodeData = { children: [] }; | |
this.createNodes(); | |
GM_addStyle(guiStyle.toString()); | |
} | |
_prototypeProperties(SearchGui, { | |
isSearchPage: { | |
value: function isSearchPage() { | |
return window.location.href.indexOf("/search?") > -1; | |
}, | |
writable: true, | |
configurable: true | |
}, | |
getResults: { | |
value: function getResults() { | |
return document.getElementById("ires"); | |
}, | |
writable: true, | |
configurable: true | |
} | |
}, { | |
createNodes: { | |
value: function createNodes() { | |
/** | |
Create once and clone to improve performance. | |
*/ | |
// dash | |
this.dash = document.createElement("span"); | |
this.dash.textContent = "-"; | |
this.dash.classList.add("dash"); | |
// add filter link | |
this.addLink = document.createElement("a"); | |
this.addLink.textContent = "Filter"; | |
this.addLink.setAttribute("href", "#"); | |
this.addLink.classList.add("filter-add"); | |
// hidden result title | |
this.showTitle = document.createElement("span"); | |
this.showTitle.classList.add("show-title"); | |
// hidden result 'show' link | |
this.showLink = document.createElement("a"); | |
this.showLink.textContent = "show"; | |
this.showLink.setAttribute("href", "#"); | |
this.showLink.classList.add("show-link"); | |
}, | |
writable: true, | |
configurable: true | |
}, | |
toggleResult: { | |
value: function toggleResult(nodeData, showTitle, showLink) { | |
let initial = arguments[3] === undefined ? false : arguments[3]; | |
for (let child of nodeData.node.children) if (child != showTitle && child != showLink) child.classList.toggle("hide"); | |
if (initial) { | |
return; | |
}if (showTitle) showTitle.classList.toggle("hide"); | |
showLink.classList.toggle("hide"); | |
showLink.textContent = showLink.classList.contains("hide") ? "hide" : "show"; | |
}, | |
writable: true, | |
configurable: true | |
}, | |
hideResult: { | |
value: function hideResult(nodeData) { | |
let filter = arguments[1] === undefined ? null : arguments[1]; | |
if (nodeData.act(this.hideResult, filter)) { | |
return; | |
}if (config.allowHidden && filter.collapse) { | |
nodeData.node.classList.add("hide"); | |
nodeData.undo = () => nodeData.node.classList.remove("hide"); | |
return; | |
} | |
let showTitle = null; | |
if (nodeData.title !== null) { | |
showTitle = this.showTitle.cloneNode(false); | |
showTitle.textContent = nodeData.title; | |
nodeData.node.appendChild(showTitle); | |
} | |
let showLink = this.showLink.cloneNode(true); | |
if (filter) showLink.title = filter.text; | |
nodeData.node.appendChild(showLink); | |
this.toggleResult(nodeData, showTitle, showLink, true); | |
showLink.onclick = () => { | |
this.toggleResult(nodeData, showTitle, showLink); | |
return false; | |
}; | |
nodeData.redo = filter => { | |
if (filter) showLink.title = filter.text; | |
}; | |
nodeData.undo = () => { | |
if (!showLink.classList.contains("hide")) this.toggleResult(nodeData, showTitle, showLink); | |
if (showTitle) nodeData.node.removeChild(showTitle); | |
nodeData.node.removeChild(showLink); | |
}; | |
}, | |
writable: true, | |
configurable: true | |
}, | |
addFilterLink: { | |
value: function addFilterLink(nodeData) { | |
let filter = arguments[1] === undefined ? null : arguments[1]; | |
if (nodeData.act(this.addFilterLink, filter)) { | |
return; | |
}if (!nodeData.linkArea) { | |
return; | |
}let dash = this.dash.cloneNode(true); | |
nodeData.linkArea.appendChild(dash); | |
let addLink = this.addLink.cloneNode(true); | |
if (filter) addLink.title = filter.text; | |
nodeData.linkArea.appendChild(addLink); | |
addLink.onclick = () => { | |
this.addFromResult(nodeData); | |
return false; | |
}; | |
nodeData.redo = filter => { | |
if (filter) addLink.title = filter.text; | |
}; | |
nodeData.undo = () => { | |
nodeData.linkArea.removeChild(dash); | |
nodeData.linkArea.removeChild(addLink); | |
}; | |
}, | |
writable: true, | |
configurable: true | |
}, | |
addFromResult: { | |
value: function addFromResult(nodeData) { | |
let domainUrl = "||" + nodeData.url.replace(/^[\w\-]+:\/+(?:www\.)?/, ""); | |
let text = prompt("Filter: ", domainUrl); | |
if (text === null) { | |
return; | |
}config.filters.push(MultiRegExpFilter.fromText(text)); | |
this.filterResults(); | |
}, | |
writable: true, | |
configurable: true | |
}, | |
_filterResults: { | |
value: function _filterResults(nodeData) { | |
let filter = this.matcher.matchesAny(nodeData, NodeData.attrs); | |
if (filter) { | |
filter.hitCount++; | |
filter.lastHit = new Date().getTime(); | |
config.filters.update(filter); | |
if (filter instanceof BlockingFilter) { | |
this.hideResult(nodeData, filter); | |
return true; | |
} else { | |
this.addFilterLink(nodeData, filter); | |
return false; | |
} | |
} | |
if (nodeData.children === undefined) nodeData.children = Array.from(nodeData.getChildren()); | |
let filtered = !!nodeData.children.length; | |
for (let childData of nodeData.children) { | |
if (!this._filterResults(childData)) filtered = false; | |
} | |
if (filtered) this.hideResult(nodeData);else if (NodeData.attrs.some(attr => nodeData[attr] !== null)) this.addFilterLink(nodeData); | |
return filtered; | |
}, | |
writable: true, | |
configurable: true | |
}, | |
filterResults: { | |
value: function filterResults() { | |
let node = arguments[0] === undefined ? null : arguments[0]; | |
let matched = false; | |
let listener = (type, value) => { | |
if (type == "update") matched = true; | |
}; | |
config.filters.observe(listener); | |
if (node) { | |
let nodeData = new ResultsData(node); | |
this.nodeData.children.push(nodeData); | |
this._filterResults(nodeData); | |
} else { | |
this._filterResults(this.nodeData); | |
} | |
if (matched) config.flushFilters(); | |
config.filters.unobserve(listener); | |
}, | |
writable: true, | |
configurable: true | |
} | |
}); | |
return SearchGui; | |
})(); | |
Object.defineProperty(exports, "__esModule", { | |
value: true | |
}); | |
/***/ }, | |
/* 6 */ | |
/***/ function(module, exports, __webpack_require__) { | |
"use strict"; | |
module.exports = function (searchGui) {}; | |
// //check if url is a google custom search url | |
// let location = window.location.href; | |
// if(location.indexOf('/cse?') == -1 && location.indexOf('/custom?') == -1) | |
// return; | |
// //set searchGui first so searchGui.filterResults() produces no errors | |
// searchGui.getResultType=function() null; | |
// //wait when ajax loads search results | |
// let cse=document.getElementById('cse'); | |
// let resultsCallback=function(e){ | |
// searchGui.getResults=function() document.querySelector('.gsc-results'); | |
// searchGui.r={ | |
// res: { | |
// getResults: function(node) node.querySelectorAll('.gsc-table-result'), | |
// getLinkArea: function() null, | |
// getUrl: function() null, | |
// getTitle: function() null, | |
// getSummary: function() null, | |
// }, | |
// text: { | |
// getResults: function() null, | |
// getLinkArea: function(node) node.querySelector('.gsc-url-bottom'), | |
// getUrl: function(node) node.querySelector('a.gs-title').href, | |
// getTitle: function(node) node.querySelector('a.gs-title').textContent, | |
// getSummary: function(node) node.querySelector('.gs-snippet').textContent, | |
// }, | |
// } | |
// searchGui.getResultType=function(node,filterClass){ | |
// if(node.classList.contains('gsc-results')){ | |
// return searchGui.r.res; | |
// }else if(node.classList.contains('gsc-table-result')){ | |
// return searchGui.r.text; | |
// } | |
// } | |
// //google custom search layout | |
// prefLink.createLink=function(){ | |
// let linkParent=searchGui.getResults(); | |
// let link=document.createElement('a'); | |
// link.setAttribute('style','float:right; margin-top:10px; color:#0000CC; font-size:14px; text-decoration:none;'); | |
// link.setAttribute('href','javascript:void(0);'); | |
// link.appendChild(document.createTextNode('Config Filters')); | |
// linkParent.insertBefore(document.createElement('br'),linkParent.firstElementChild); | |
// linkParent.insertBefore(link,linkParent.firstElementChild); | |
// return link; | |
// } | |
// prefLink.init(); | |
// GM_addStyle('.gs-visibleUrl-long{display: inline !important}'); | |
// //there are no previous results, filter directly | |
// searchGui.filterResults(); | |
// }; | |
// //hook draw | |
// CustomSearchControl=unsafeWindow.google.search.CustomSearchControl; | |
// let o_draw=CustomSearchControl.prototype.draw; | |
// CustomSearchControl.prototype.draw=function(){ | |
// //privileged code | |
// o_draw.apply(this,arguments); | |
// let cse=document.querySelector('#cse'); | |
// let o_resultsTable=null; | |
// //observer callback after loading the first set of results | |
// let loadNextCallback=function(mutations){ | |
// /** | |
// returns: | |
// whether new results have loaded | |
// */ | |
// let resultsTable=cse.querySelector('.gsc-table-result'); | |
// if(resultsTable==null || o_resultsTable==resultsTable) | |
// return false; | |
// o_resultsTable=resultsTable; | |
// window.dispatchEvent(new CustomEvent('results')); | |
// return true; | |
// } | |
// let loadNextObserver=new MutationObserver(loadNextCallback); | |
// //observer callback when loading the first set of results | |
// let loadFirstCallback=function(mutations){ | |
// //wait until the first results have loaded before disconnecting | |
// if(!loadNextCallback()) | |
// return false; | |
// loadFirstObserver.disconnect(); | |
// let results=cse.querySelector('.gsc-results'); | |
// if(results==null) | |
// return; | |
// loadNextObserver.observe(results,{attributes: true}); | |
// } | |
// let loadFirstObserver=new MutationObserver(loadFirstCallback); | |
// //wait until the first set of results have loaded | |
// loadFirstObserver.observe(cse,{subtree: true, childList: true}); | |
// } | |
// //resultsCallback needs privileged functions | |
// window.addEventListener('results',resultsCallback,false); | |
/***/ }, | |
/* 7 */ | |
/***/ function(module, exports, __webpack_require__) { | |
"use strict"; | |
module.exports = function (searchGui) {}; | |
// //initialize searchGui if we haven't already (e.g. on the home page) | |
// if(!searchGui.initialized) | |
// searchGui.init(); | |
// //results parent node | |
// let resultsNode; | |
// //pref link | |
// let _prefLink; | |
// let resultsObserver=new MutationObserver(function(mutations){ | |
// mutations.forEach(function(mutation) { | |
// //filter nodes whenever they are added, instead of doing batch filters | |
// for(let i=0;i<mutation.addedNodes.length;i++){ | |
// addedNode=mutation.addedNodes[i]; | |
// if(addedNode.id=='ires'){ | |
// //update prefLink (google instant inserts a new one every time) | |
// if(!_prefLink || !isInDom(_prefLink)) | |
// _prefLink=prefLink.createLinkSettings(); | |
// //we have a new query, google only adds this node with all results added (to test this properly, disable all other userscripts) | |
// searchGui.filterResults(); | |
// prefMeta.isUpdated=false; | |
// //other extensions might use this | |
// window.dispatchEvent(new CustomEvent('instantResults')); | |
// } | |
// }; | |
// }); | |
// }); | |
// let mainNode=document.getElementById('main'); | |
// if(!mainNode) | |
// return false; | |
// resultsObserver.observe(mainNode,{subtree: true, childList: true}); | |
/***/ }, | |
/* 8 */ | |
/***/ function(module, exports, __webpack_require__) { | |
"use strict"; | |
var _defaults = function (obj, defaults) { var keys = Object.getOwnPropertyNames(defaults); for (var i = 0; i < keys.length; i++) { var key = keys[i]; var value = Object.getOwnPropertyDescriptor(defaults, key); if (value && value.configurable && obj[key] === undefined) { Object.defineProperty(obj, key, value); } } return obj; }; | |
var _slicedToArray = function (arr, i) { if (Array.isArray(arr)) { return arr; } else if (Symbol.iterator in Object(arr)) { var _arr = []; for (var _iterator = arr[Symbol.iterator](), _step; !(_step = _iterator.next()).done;) { _arr.push(_step.value); if (i && _arr.length === i) break; } return _arr; } else { throw new TypeError("Invalid attempt to destructure non-iterable instance"); } }; | |
var _prototypeProperties = function (child, staticProps, instanceProps) { if (staticProps) Object.defineProperties(child, staticProps); if (instanceProps) Object.defineProperties(child.prototype, instanceProps); }; | |
var _inherits = function (subClass, superClass) { if (typeof superClass !== "function" && superClass !== null) { throw new TypeError("Super expression must either be null or a function, not " + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) _defaults(subClass, superClass); }; | |
var _classCallCheck = function (instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }; | |
var WhitelistFilter = __webpack_require__(10).WhitelistFilter; | |
var Matcher = __webpack_require__(14).Matcher; | |
let SubMatcher = exports.SubMatcher = (function (Matcher) { | |
function SubMatcher() { | |
_classCallCheck(this, SubMatcher); | |
if (Matcher != null) { | |
Matcher.apply(this, arguments); | |
} | |
} | |
_inherits(SubMatcher, Matcher); | |
_prototypeProperties(SubMatcher, { | |
_findCandidates: { | |
value: function _findCandidates(filter) { | |
if (filter.regexpSource === null) { | |
return null; | |
}return filter.regexpSource.toLowerCase().match(/[^a-z0-9%*][a-z0-9%]{3,}(?=[^a-z0-9%*])/g); | |
}, | |
writable: true, | |
configurable: true | |
}, | |
isSlowFilter: { | |
value: function isSlowFilter(filter) { | |
return !this._findCandidates(filter); | |
}, | |
writable: true, | |
configurable: true | |
} | |
}, { | |
findKeyword: { | |
value: function findKeyword(filter) { | |
let res = ""; | |
let candidates = this.constructor._findCandidates(filter); | |
if (candidates === null) { | |
return res; | |
}let resCount = 16777215; | |
let resLength = 0; | |
for (let candidate of candidates) { | |
candidate = candidate.substr(1); | |
let count = this.filterByKeyword.has(candidate) ? this.filterByKeyword.get(candidate).length : 0; | |
if (count < resCount || count == resCount && candidate.length > resLength) { | |
res = candidate; | |
resCount = count; | |
resLength = candidate.length; | |
} | |
} | |
return res; | |
}, | |
writable: true, | |
configurable: true | |
}, | |
add: { | |
value: function add(filter) { | |
// Duplicates are rare and not checked for efficiency, otherwise we need to store sub filter text maps, and multiple | |
// parents per sub filter. | |
let keyword = this.findKeyword(filter); | |
let prevEntry = this.filterByKeyword.get(keyword); | |
if (prevEntry === undefined) { | |
this.filterByKeyword.set(keyword, filter); | |
} else if (prevEntry.length == 1) { | |
this.filterByKeyword.set(keyword, [prevEntry, filter]); | |
} else { | |
prevEntry.push(filter); | |
} | |
}, | |
writable: true, | |
configurable: true | |
}, | |
remove: { | |
value: function remove(filter) { | |
// only used by pref, doesn't need to be efficient | |
let candidates = this.constructor._findCandidates(filter); | |
if (candidates === null) candidates = [""]; | |
for (let candidate of candidates) { | |
candidate = candidate.substr(1); | |
let prevEntry = this.filterByKeyword.get(candidate); | |
if (prevEntry === undefined) {} else if (prevEntry.length == 1) { | |
if (prevEntry == filter) { | |
this.filterByKeyword["delete"](candidate); | |
break; | |
} | |
} else { | |
let i = prevEntry.indexOf(filter); | |
if (i > -1) { | |
if (prevEntry.length == 2) this.filterByKeyword.set(candidate, prevEntry[1 - i]);else prevEntry.pop(i); | |
break; | |
} | |
} | |
} | |
}, | |
writable: true, | |
configurable: true | |
}, | |
clear: { | |
value: function clear() { | |
this.filterByKeyword = new Map(); | |
}, | |
writable: true, | |
configurable: true | |
}, | |
hasFilter: { | |
value: function hasFilter() { | |
throw "not implemented"; | |
}, | |
writable: true, | |
configurable: true | |
}, | |
getKeywordForFilter: { | |
value: function getKeywordForFilter() { | |
throw "not implemented"; | |
}, | |
writable: true, | |
configurable: true | |
}, | |
_iterMatches: { | |
value: function* _iterMatches(filters, data, parents) { | |
for (let filter of filters) { | |
let parent = filter.parent; | |
if ((filter.dataIndex === 0 || parents.has(parent)) && filter.matches(data)) yield filter; | |
} | |
}, | |
writable: true, | |
configurable: true | |
}, | |
iterMatches: { | |
value: function* iterMatches(data, parents) { | |
/** | |
Args: | |
parents: All parent filters matched so far, excluding the ones whose subFilters have so far been null. | |
*/ | |
if (data === null) { | |
return; | |
}let candidates = data.toLowerCase().match(/[a-z0-9%]{3,}/g); | |
if (candidates === null) candidates = []; | |
candidates.push(""); | |
for (let keyword of candidates) { | |
let filters = this.filterByKeyword.get(keyword); | |
if (filters) { | |
for (let res of this._iterMatches(filters, data, parents)) yield res; | |
} | |
} | |
}, | |
writable: true, | |
configurable: true | |
} | |
}); | |
return SubMatcher; | |
})(Matcher); | |
let MultiMatcher = exports.MultiMatcher = (function () { | |
function MultiMatcher(n) { | |
_classCallCheck(this, MultiMatcher); | |
this.n = n; | |
this.matchers = []; | |
for (let i = 0; i < n; i++) this.matchers.push(new SubMatcher()); | |
} | |
_prototypeProperties(MultiMatcher, { | |
isSlowFilter: { | |
value: function isSlowFilter(filter) { | |
for (let subFilter of filter.filters) { | |
if (SubMatcher.isSlowFilter(subFilter)) { | |
return true; | |
} | |
} | |
return false; | |
}, | |
writable: true, | |
configurable: true | |
} | |
}, { | |
add: { | |
value: function add(filter) { | |
for (let subFilter of filter.filters) this.matchers[subFilter.index].add(subFilter); | |
}, | |
writable: true, | |
configurable: true | |
}, | |
remove: { | |
value: function remove(filter) { | |
for (let subFilter of filter.filters) this.matchers[subFilter.index].remove(subFilter); | |
}, | |
writable: true, | |
configurable: true | |
}, | |
clear: { | |
value: function clear() { | |
for (let matcher of this.matchers) matcher.clear(); | |
}, | |
writable: true, | |
configurable: true | |
}, | |
matchesAny: { | |
value: function matchesAny(data, attrs) { | |
// {filter: nextNullNum} | |
let prevFilters = new Map(); | |
let curFilters = new Map(); | |
for (let i = 0; i < this.n; i++) { | |
for (let subFilter of this.matchers[i].iterMatches(data[attrs[i]], prevFilters)) { | |
if (subFilter.dataIndex == subFilter.parent.filters.length - 1) { | |
return subFilter.parent; | |
}curFilters.set(subFilter.parent, subFilter.parent.filters[subFilter.dataIndex + 1].index - i); | |
} | |
if (i != this.n - 1) { | |
// include null subFilters whose parents have so far matched | |
for (let _ref of prevFilters.entries()) { | |
var _ref2 = _slicedToArray(_ref, 2); | |
let filter = _ref2[0]; | |
let nextNullNum = _ref2[1]; | |
if (nextNullNum > 0) curFilters.set(filter, nextNullNum - 1); | |
} | |
var _ref3 = [curFilters, new Map()]; | |
var _ref32 = _slicedToArray(_ref3, 2); | |
prevFilters = _ref32[0]; | |
curFilters = _ref32[1]; | |
} | |
} | |
return null; | |
}, | |
writable: true, | |
configurable: true | |
} | |
}); | |
return MultiMatcher; | |
})(); | |
let CombinedMultiMatcher = exports.CombinedMultiMatcher = (function () { | |
function CombinedMultiMatcher(n) { | |
_classCallCheck(this, CombinedMultiMatcher); | |
// caching is not required since we have one instance per tab, and past filter results are easily accessible | |
this.blacklist = new MultiMatcher(n); | |
this.whitelist = new MultiMatcher(n); | |
} | |
_prototypeProperties(CombinedMultiMatcher, { | |
isSlowFilter: { | |
value: function isSlowFilter(filter) { | |
return MultiMatcher.isSlowFilter(filter); | |
}, | |
writable: true, | |
configurable: true | |
} | |
}, { | |
add: { | |
value: function add(filter) { | |
if (filter instanceof WhitelistFilter) { | |
this.whitelist.add(filter); | |
} else { | |
this.blacklist.add(filter); | |
} | |
}, | |
writable: true, | |
configurable: true | |
}, | |
remove: { | |
value: function remove(filter) { | |
if (filter instanceof WhitelistFilter) { | |
this.whitelist.remove(filter); | |
} else { | |
this.blacklist.remove(filter); | |
} | |
}, | |
writable: true, | |
configurable: true | |
}, | |
clear: { | |
value: function clear() { | |
this.blacklist.clear(); | |
this.whitelist.clear(); | |
}, | |
writable: true, | |
configurable: true | |
}, | |
matchesAny: { | |
value: function matchesAny(data, attrs) { | |
return this.whitelist.matchesAny(data, attrs) || this.blacklist.matchesAny(data, attrs); | |
}, | |
writable: true, | |
configurable: true | |
} | |
}); | |
return CombinedMultiMatcher; | |
})(); | |
Object.defineProperty(exports, "__esModule", { | |
value: true | |
}); | |
/***/ }, | |
/* 9 */ | |
/***/ function(module, exports, __webpack_require__) { | |
"use strict"; | |
/* global GM_addStyle, GM_getResourceText, GM_getResourceURL */ | |
exports.cache = cache; | |
exports.pad = pad; | |
exports.addStyleResolve = addStyleResolve; | |
function cache(obj, prop, value) { | |
Object.defineProperty(obj, prop, { value: value }); | |
return value; | |
} | |
function pad(num, size) { | |
return Array(Math.max(size - num.toString().length + 1, 0)).join(0) + num; | |
} | |
function addStyleResolve(name) { | |
GM_addStyle(GM_getResourceText(name).replace(/url\("?([^":]+)"?\)/g, (match, url) => "url(\"" + GM_getResourceURL("" + name + "/" + url) + "\")")); | |
} | |
Object.defineProperty(exports, "__esModule", { | |
value: true | |
}); | |
/***/ }, | |
/* 10 */ | |
/***/ function(module, exports, __webpack_require__) { | |
"use strict"; | |
var _defaults = function (obj, defaults) { var keys = Object.getOwnPropertyNames(defaults); for (var i = 0; i < keys.length; i++) { var key = keys[i]; var value = Object.getOwnPropertyDescriptor(defaults, key); if (value && value.configurable && obj[key] === undefined) { Object.defineProperty(obj, key, value); } } return obj; }; | |
var _prototypeProperties = function (child, staticProps, instanceProps) { if (staticProps) Object.defineProperties(child, staticProps); if (instanceProps) Object.defineProperties(child.prototype, instanceProps); }; | |
var _inherits = function (subClass, superClass) { if (typeof superClass !== "function" && superClass !== null) { throw new TypeError("Super expression must either be null or a function, not " + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) _defaults(subClass, superClass); }; | |
var _classCallCheck = function (instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }; | |
var _gfpLibFilterClasses = __webpack_require__(15); | |
var ActiveFilter = _gfpLibFilterClasses.ActiveFilter; | |
var Filter_ = _gfpLibFilterClasses.Filter; | |
var InvalidFilter = _gfpLibFilterClasses.InvalidFilter; | |
var RegExpFilter_ = _gfpLibFilterClasses.RegExpFilter; | |
let RegExpFilter = exports.RegExpFilter = (function (RegExpFilter_) { | |
function RegExpFilter(regexpSource, matchCase, collapse) { | |
_classCallCheck(this, RegExpFilter); | |
if (matchCase) this.matchCase = matchCase; | |
if (collapse) this.collapse = collapse; | |
// convert regex filters immediately to catch syntax errors, normal filters on-demand | |
if (regexpSource.length >= 2 && regexpSource.startsWith("/") && regexpSource.endsWith("/")) { | |
let regexp = new RegExp(regexpSource.substr(1, regexpSource.length - 2), this.matchCase ? "" : "i"); | |
Object.defineProperty(this, "regexp", { value: regexp }); | |
} else { | |
this.regexpSource = regexpSource; | |
} | |
} | |
_inherits(RegExpFilter, RegExpFilter_); | |
_prototypeProperties(RegExpFilter, { | |
fromParts: { | |
value: function fromParts(text) { | |
let optionsStr = arguments[1] === undefined ? "" : arguments[1]; | |
// text is not stored to save memory | |
let matchCase = false; | |
let collapse = false; | |
let options = optionsStr.toUpperCase().split(","); | |
for (let option of options) { | |
switch (option) { | |
case "MATCH_CASE": | |
matchCase = true;break; | |
case "COLLAPSE": | |
collapse = true;break; | |
case "": | |
break; | |
default: | |
return new InvalidFilter("Unknown option: " + option); | |
} | |
} | |
return new RegExpFilter(text, matchCase, collapse); | |
}, | |
writable: true, | |
configurable: true | |
} | |
}, (function () { | |
var _prototypeProperties2 = { | |
matches: { | |
value: function matches(data) { | |
return this.regexp.test(data); | |
}, | |
writable: true, | |
configurable: true | |
} | |
}; | |
_prototypeProperties2[Symbol.iterator] = { | |
value: function* () { | |
yield this; | |
}, | |
writable: true, | |
configurable: true | |
}; | |
return _prototypeProperties2; | |
})()); | |
return RegExpFilter; | |
})(RegExpFilter_); | |
RegExpFilter.prototype.matchCase = false; | |
RegExpFilter.prototype.collapse = false; | |
RegExpFilter.prototype.index = 0; | |
RegExpFilter.prototype.dataIndex = 0; | |
let MultiRegExpFilter = exports.MultiRegExpFilter = (function (ActiveFilter) { | |
function MultiRegExpFilter(text, filters) { | |
_classCallCheck(this, MultiRegExpFilter); | |
this.text = text; | |
this.filters = filters; | |
for (let filter of filters) filter.parent = this; | |
} | |
_inherits(MultiRegExpFilter, ActiveFilter); | |
_prototypeProperties(MultiRegExpFilter, { | |
fromText: { | |
value: function fromText(text) { | |
let origText = text; | |
let filters = []; | |
let blocking = true; | |
if (text.indexOf("@@") === 0) { | |
blocking = false; | |
text = text.substr(2); | |
} | |
let parts = text.split(/\$([\w,]*?)(?:\$|$)/); | |
for (let i = 0; i < parts.length; i += 2) { | |
let part = parts[i]; | |
let options = parts[i + 1]; | |
if (!part) continue; | |
let filter = RegExpFilter.fromParts(part, options); | |
if (filter instanceof InvalidFilter) { | |
return new InvalidFilter(text); | |
}if (i > 0) filter.index = i / 2; | |
if (filters.length > 0) filter.dataIndex = filters.length; | |
filters.push(filter); | |
} | |
if (filters.length == 1) filters = filters[0]; | |
return blocking ? new BlockingFilter(origText, filters) : new WhitelistFilter(origText, filters); | |
}, | |
writable: true, | |
configurable: true | |
} | |
}, { | |
collapse: { | |
get: function () { | |
for (let filter of this.filters) if (filter.collapse) return true; | |
return false; | |
}, | |
configurable: true | |
} | |
}); | |
return MultiRegExpFilter; | |
})(ActiveFilter); | |
let Filter = exports.Filter = Filter_; | |
Filter.fromObject = function (text, obj) { | |
let res = Filter.fromText(text); | |
if (res instanceof ActiveFilter) { | |
if ("disabled" in obj) res._disabled = obj.disabled === true; | |
if ("hitCount" in obj) res._hitCount = parseInt(obj.hitCount) || 0; | |
if ("lastHit" in obj) res._lastHit = parseInt(obj.lastHit) || 0; | |
} | |
return res; | |
}; | |
Filter.prototype.toObject = function () { | |
let res = {}; | |
if (this instanceof ActiveFilter) { | |
if (this._disabled) res.disabled = true; | |
if (this._hitCount) res.hitCount = this._hitCount; | |
if (this._lastHit) res.lastHit = this._lastHit; | |
} | |
return res; | |
}; | |
Filter.fromText = MultiRegExpFilter.fromText; | |
let BlockingFilter = exports.BlockingFilter = (function (MultiRegExpFilter) { | |
function BlockingFilter() { | |
_classCallCheck(this, BlockingFilter); | |
if (MultiRegExpFilter != null) { | |
MultiRegExpFilter.apply(this, arguments); | |
} | |
} | |
_inherits(BlockingFilter, MultiRegExpFilter); | |
return BlockingFilter; | |
})(MultiRegExpFilter); | |
let WhitelistFilter = exports.WhitelistFilter = (function (MultiRegExpFilter) { | |
function WhitelistFilter() { | |
_classCallCheck(this, WhitelistFilter); | |
if (MultiRegExpFilter != null) { | |
MultiRegExpFilter.apply(this, arguments); | |
} | |
} | |
_inherits(WhitelistFilter, MultiRegExpFilter); | |
return WhitelistFilter; | |
})(MultiRegExpFilter); | |
Object.defineProperty(exports, "__esModule", { | |
value: true | |
}); | |
/***/ }, | |
/* 11 */ | |
/***/ function(module, exports, __webpack_require__) { | |
module.exports = "<div class=gfp><div class=row><a href=# class=add title=\"Add filter\"><img class=button-icon src=" + __webpack_require__(19) + "></a> <a href=# class=import title=\"Import filters\"><img class=button-icon src=" + __webpack_require__(20) + "></a> <a href=# class=export title=\"Export filters\"><img class=button-icon src=" + __webpack_require__(21) + "></a></div><div class=row><div class=grid></div></div></div>"; | |
/***/ }, | |
/* 12 */ | |
/***/ function(module, exports, __webpack_require__) { | |
exports = module.exports = __webpack_require__(16)(); | |
exports.push([module.id, ".ui-front{z-index:1000}.ui-dialog textarea{box-sizing:border-box;width:100%!important;resize:none}.slick-sort-indicator{float:none}.gfp{display:flex!important;flex-direction:column}.gfp a{outline:0}.gfp .button-icon{height:16px;width:16px}.gfp .row:last-child{flex-grow:1}.gfp .grid{height:100%}.gfp .slick-cell:first-child{white-space:pre}.gfp .slick-header-column:not(:first-child),.gfp .slick-cell:not(:first-child){text-align:right}", ""]); | |
/***/ }, | |
/* 13 */ | |
/***/ function(module, exports, __webpack_require__) { | |
exports = module.exports = __webpack_require__(16)(); | |
exports.push([module.id, ".hide{display:none}.dash{padding:0 3px}.filter-add{color:#12C!important;font-size:90%;text-decoration:none}.filter-add:hover{text-decoration:underline}.show-title{padding-right:5px;color:#999;font-size:90%}.show-link{color:#999!important;font-size:90%;text-decoration:none}.show-link.hide{display:inline;color:#12C!important}", ""]); | |
/***/ }, | |
/* 14 */ | |
/***/ function(module, exports, __webpack_require__) { | |
"use strict"; | |
/* | |
* This file is part of Adblock Plus <https://adblockplus.org/>, | |
* Copyright (C) 2006-2015 Eyeo GmbH | |
* | |
* Adblock Plus is free software: you can redistribute it and/or modify | |
* it under the terms of the GNU General Public License version 3 as | |
* published by the Free Software Foundation. | |
* | |
* Adblock Plus 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 Adblock Plus. If not, see <http://www.gnu.org/licenses/>. | |
*/ | |
/** | |
* @fileOverview Matcher class implementing matching addresses against a list of filters. | |
*/ | |
var _require = __webpack_require__(15); | |
let Filter = _require.Filter; | |
let RegExpFilter = _require.RegExpFilter; | |
let WhitelistFilter = _require.WhitelistFilter; | |
/** | |
* Blacklist/whitelist filter matching | |
* @constructor | |
*/ | |
function Matcher() { | |
this.clear(); | |
} | |
exports.Matcher = Matcher; | |
Matcher.prototype = { | |
/** | |
* Lookup table for filters by their associated keyword | |
* @type Object | |
*/ | |
filterByKeyword: null, | |
/** | |
* Lookup table for keywords by the filter text | |
* @type Object | |
*/ | |
keywordByFilter: null, | |
/** | |
* Removes all known filters | |
*/ | |
clear: function clear() { | |
this.filterByKeyword = Object.create(null); | |
this.keywordByFilter = Object.create(null); | |
}, | |
/** | |
* Adds a filter to the matcher | |
* @param {RegExpFilter} filter | |
*/ | |
add: function add(filter) { | |
if (filter.text in this.keywordByFilter) { | |
return; | |
} // Look for a suitable keyword | |
let keyword = this.findKeyword(filter); | |
let oldEntry = this.filterByKeyword[keyword]; | |
if (typeof oldEntry == "undefined") this.filterByKeyword[keyword] = filter;else if (oldEntry.length == 1) this.filterByKeyword[keyword] = [oldEntry, filter];else oldEntry.push(filter); | |
this.keywordByFilter[filter.text] = keyword; | |
}, | |
/** | |
* Removes a filter from the matcher | |
* @param {RegExpFilter} filter | |
*/ | |
remove: function remove(filter) { | |
if (!(filter.text in this.keywordByFilter)) { | |
return; | |
}let keyword = this.keywordByFilter[filter.text]; | |
let list = this.filterByKeyword[keyword]; | |
if (list.length <= 1) delete this.filterByKeyword[keyword];else { | |
let index = list.indexOf(filter); | |
if (index >= 0) { | |
list.splice(index, 1); | |
if (list.length == 1) this.filterByKeyword[keyword] = list[0]; | |
} | |
} | |
delete this.keywordByFilter[filter.text]; | |
}, | |
/** | |
* Chooses a keyword to be associated with the filter | |
* @param {String} text text representation of the filter | |
* @return {String} keyword (might be empty string) | |
*/ | |
findKeyword: function findKeyword(filter) { | |
let result = ""; | |
let text = filter.text; | |
if (Filter.regexpRegExp.test(text)) { | |
return result; | |
} // Remove options | |
let match = Filter.optionsRegExp.exec(text); | |
if (match) text = match.input.substr(0, match.index); | |
// Remove whitelist marker | |
if (text.substr(0, 2) == "@@") text = text.substr(2); | |
let candidates = text.toLowerCase().match(/[^a-z0-9%*][a-z0-9%]{3,}(?=[^a-z0-9%*])/g); | |
if (!candidates) { | |
return result; | |
}let hash = this.filterByKeyword; | |
let resultCount = 16777215; | |
let resultLength = 0; | |
for (let i = 0, l = candidates.length; i < l; i++) { | |
let candidate = candidates[i].substr(1); | |
let count = candidate in hash ? hash[candidate].length : 0; | |
if (count < resultCount || count == resultCount && candidate.length > resultLength) { | |
result = candidate; | |
resultCount = count; | |
resultLength = candidate.length; | |
} | |
} | |
return result; | |
}, | |
/** | |
* Checks whether a particular filter is being matched against. | |
*/ | |
hasFilter: function hasFilter( /**RegExpFilter*/filter) /**Boolean*/ | |
{ | |
return filter.text in this.keywordByFilter; | |
}, | |
/** | |
* Returns the keyword used for a filter, null for unknown filters. | |
*/ | |
getKeywordForFilter: function getKeywordForFilter( /**RegExpFilter*/filter) /**String*/ | |
{ | |
if (filter.text in this.keywordByFilter) { | |
return this.keywordByFilter[filter.text]; | |
} else { | |
return null; | |
} | |
}, | |
/** | |
* Checks whether the entries for a particular keyword match a URL | |
*/ | |
_checkEntryMatch: function _checkEntryMatch(keyword, location, contentType, docDomain, thirdParty, sitekey) { | |
let list = this.filterByKeyword[keyword]; | |
for (let i = 0; i < list.length; i++) { | |
let filter = list[i]; | |
if (filter.matches(location, contentType, docDomain, thirdParty, sitekey)) { | |
return filter; | |
} | |
} | |
return null; | |
}, | |
/** | |
* Tests whether the URL matches any of the known filters | |
* @param {String} location URL to be tested | |
* @param {String} contentType content type identifier of the URL | |
* @param {String} docDomain domain name of the document that loads the URL | |
* @param {Boolean} thirdParty should be true if the URL is a third-party request | |
* @param {String} sitekey public key provided by the document | |
* @return {RegExpFilter} matching filter or null | |
*/ | |
matchesAny: function matchesAny(location, contentType, docDomain, thirdParty, sitekey) { | |
let candidates = location.toLowerCase().match(/[a-z0-9%]{3,}/g); | |
if (candidates === null) candidates = []; | |
candidates.push(""); | |
for (let i = 0, l = candidates.length; i < l; i++) { | |
let substr = candidates[i]; | |
if (substr in this.filterByKeyword) { | |
let result = this._checkEntryMatch(substr, location, contentType, docDomain, thirdParty, sitekey); | |
if (result) { | |
return result; | |
} | |
} | |
} | |
return null; | |
} | |
}; | |
/** | |
* Combines a matcher for blocking and exception rules, automatically sorts | |
* rules into two Matcher instances. | |
* @constructor | |
*/ | |
function CombinedMatcher() { | |
this.blacklist = new Matcher(); | |
this.whitelist = new Matcher(); | |
this.resultCache = Object.create(null); | |
} | |
exports.CombinedMatcher = CombinedMatcher; | |
/** | |
* Maximal number of matching cache entries to be kept | |
* @type Number | |
*/ | |
CombinedMatcher.maxCacheEntries = 1000; | |
CombinedMatcher.prototype = { | |
/** | |
* Matcher for blocking rules. | |
* @type Matcher | |
*/ | |
blacklist: null, | |
/** | |
* Matcher for exception rules. | |
* @type Matcher | |
*/ | |
whitelist: null, | |
/** | |
* Lookup table of previous matchesAny results | |
* @type Object | |
*/ | |
resultCache: null, | |
/** | |
* Number of entries in resultCache | |
* @type Number | |
*/ | |
cacheEntries: 0, | |
/** | |
* @see Matcher#clear | |
*/ | |
clear: function clear() { | |
this.blacklist.clear(); | |
this.whitelist.clear(); | |
this.resultCache = Object.create(null); | |
this.cacheEntries = 0; | |
}, | |
/** | |
* @see Matcher#add | |
*/ | |
add: function add(filter) { | |
if (filter instanceof WhitelistFilter) this.whitelist.add(filter);else this.blacklist.add(filter); | |
if (this.cacheEntries > 0) { | |
this.resultCache = Object.create(null); | |
this.cacheEntries = 0; | |
} | |
}, | |
/** | |
* @see Matcher#remove | |
*/ | |
remove: function remove(filter) { | |
if (filter instanceof WhitelistFilter) this.whitelist.remove(filter);else this.blacklist.remove(filter); | |
if (this.cacheEntries > 0) { | |
this.resultCache = Object.create(null); | |
this.cacheEntries = 0; | |
} | |
}, | |
/** | |
* @see Matcher#findKeyword | |
*/ | |
findKeyword: function findKeyword(filter) { | |
if (filter instanceof WhitelistFilter) { | |
return this.whitelist.findKeyword(filter); | |
} else { | |
return this.blacklist.findKeyword(filter); | |
} | |
}, | |
/** | |
* @see Matcher#hasFilter | |
*/ | |
hasFilter: function hasFilter(filter) { | |
if (filter instanceof WhitelistFilter) { | |
return this.whitelist.hasFilter(filter); | |
} else { | |
return this.blacklist.hasFilter(filter); | |
} | |
}, | |
/** | |
* @see Matcher#getKeywordForFilter | |
*/ | |
getKeywordForFilter: function getKeywordForFilter(filter) { | |
if (filter instanceof WhitelistFilter) { | |
return this.whitelist.getKeywordForFilter(filter); | |
} else { | |
return this.blacklist.getKeywordForFilter(filter); | |
} | |
}, | |
/** | |
* Checks whether a particular filter is slow | |
*/ | |
isSlowFilter: function isSlowFilter( /**RegExpFilter*/filter) /**Boolean*/ | |
{ | |
let matcher = filter instanceof WhitelistFilter ? this.whitelist : this.blacklist; | |
if (matcher.hasFilter(filter)) { | |
return !matcher.getKeywordForFilter(filter); | |
} else { | |
return !matcher.findKeyword(filter); | |
} | |
}, | |
/** | |
* Optimized filter matching testing both whitelist and blacklist matchers | |
* simultaneously. For parameters see Matcher.matchesAny(). | |
* @see Matcher#matchesAny | |
*/ | |
matchesAnyInternal: function matchesAnyInternal(location, contentType, docDomain, thirdParty, sitekey) { | |
let candidates = location.toLowerCase().match(/[a-z0-9%]{3,}/g); | |
if (candidates === null) candidates = []; | |
candidates.push(""); | |
let blacklistHit = null; | |
for (let i = 0, l = candidates.length; i < l; i++) { | |
let substr = candidates[i]; | |
if (substr in this.whitelist.filterByKeyword) { | |
let result = this.whitelist._checkEntryMatch(substr, location, contentType, docDomain, thirdParty, sitekey); | |
if (result) { | |
return result; | |
} | |
} | |
if (substr in this.blacklist.filterByKeyword && blacklistHit === null) blacklistHit = this.blacklist._checkEntryMatch(substr, location, contentType, docDomain, thirdParty, sitekey); | |
} | |
return blacklistHit; | |
}, | |
/** | |
* @see Matcher#matchesAny | |
*/ | |
matchesAny: function matchesAny(location, contentType, docDomain, thirdParty, sitekey) { | |
let key = location + " " + contentType + " " + docDomain + " " + thirdParty + " " + sitekey; | |
if (key in this.resultCache) { | |
return this.resultCache[key]; | |
}let result = this.matchesAnyInternal(location, contentType, docDomain, thirdParty, sitekey); | |
if (this.cacheEntries >= CombinedMatcher.maxCacheEntries) { | |
this.resultCache = Object.create(null); | |
this.cacheEntries = 0; | |
} | |
this.resultCache[key] = result; | |
this.cacheEntries++; | |
return result; | |
} | |
}; | |
/** | |
* Shared CombinedMatcher instance that should usually be used. | |
* @type CombinedMatcher | |
*/ | |
let defaultMatcher = exports.defaultMatcher = new CombinedMatcher(); | |
/***/ }, | |
/* 15 */ | |
/***/ function(module, exports, __webpack_require__) { | |
"use strict"; | |
var _slicedToArray = function (arr, i) { if (Array.isArray(arr)) { return arr; } else if (Symbol.iterator in Object(arr)) { var _arr = []; for (var _iterator = arr[Symbol.iterator](), _step; !(_step = _iterator.next()).done;) { _arr.push(_step.value); if (i && _arr.length === i) break; } return _arr; } else { throw new TypeError("Invalid attempt to destructure non-iterable instance"); } }; | |
/* | |
* This file is part of Adblock Plus <https://adblockplus.org/>, | |
* Copyright (C) 2006-2015 Eyeo GmbH | |
* | |
* Adblock Plus is free software: you can redistribute it and/or modify | |
* it under the terms of the GNU General Public License version 3 as | |
* published by the Free Software Foundation. | |
* | |
* Adblock Plus 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 Adblock Plus. If not, see <http://www.gnu.org/licenses/>. | |
*/ | |
/** | |
* @fileOverview Definition of Filter class and its subclasses. | |
*/ | |
var _require = __webpack_require__(17); | |
let FilterNotifier = _require.FilterNotifier; | |
/** | |
* Abstract base class for filters | |
* | |
* @param {String} text string representation of the filter | |
* @constructor | |
*/ | |
function Filter(text) { | |
this.text = text; | |
this.subscriptions = []; | |
} | |
exports.Filter = Filter; | |
Filter.prototype = { | |
/** | |
* String representation of the filter | |
* @type String | |
*/ | |
text: null, | |
/** | |
* Filter subscriptions the filter belongs to | |
* @type Array of Subscription | |
*/ | |
subscriptions: null, | |
/** | |
* Serializes the filter to an array of strings for writing out on the disk. | |
* @param {Array of String} buffer buffer to push the serialization results into | |
*/ | |
serialize: function serialize(buffer) { | |
buffer.push("[Filter]"); | |
buffer.push("text=" + this.text); | |
}, | |
toString: function toString() { | |
return this.text; | |
} | |
}; | |
/** | |
* Cache for known filters, maps string representation to filter objects. | |
* @type Object | |
*/ | |
Filter.knownFilters = Object.create(null); | |
/** | |
* Regular expression that element hiding filters should match | |
* @type RegExp | |
*/ | |
Filter.elemhideRegExp = /^([^\/\*\|\@"!]*?)#(\@)?(?:([\w\-]+|\*)((?:\([\w\-]+(?:[$^*]?=[^\(\)"]*)?\))*)|#([^{}]+))$/; | |
/** | |
* Regular expression that RegExp filters specified as RegExps should match | |
* @type RegExp | |
*/ | |
Filter.regexpRegExp = /^(@@)?\/.*\/(?:\$~?[\w\-]+(?:=[^,\s]+)?(?:,~?[\w\-]+(?:=[^,\s]+)?)*)?$/; | |
/** | |
* Regular expression that options on a RegExp filter should match | |
* @type RegExp | |
*/ | |
Filter.optionsRegExp = /\$(~?[\w\-]+(?:=[^,\s]+)?(?:,~?[\w\-]+(?:=[^,\s]+)?)*)$/; | |
/** | |
* Creates a filter of correct type from its text representation - does the basic parsing and | |
* calls the right constructor then. | |
* | |
* @param {String} text as in Filter() | |
* @return {Filter} | |
*/ | |
Filter.fromText = function (text) { | |
if (text in Filter.knownFilters) return Filter.knownFilters[text]; | |
let ret; | |
let match = text.indexOf("#") >= 0 ? Filter.elemhideRegExp.exec(text) : null; | |
if (match) ret = ElemHideBase.fromText(text, match[1], match[2], match[3], match[4], match[5]);else if (text[0] == "!") ret = new CommentFilter(text);else ret = RegExpFilter.fromText(text); | |
Filter.knownFilters[ret.text] = ret; | |
return ret; | |
}; | |
/** | |
* Deserializes a filter | |
* | |
* @param {Object} obj map of serialized properties and their values | |
* @return {Filter} filter or null if the filter couldn't be created | |
*/ | |
Filter.fromObject = function (obj) { | |
let ret = Filter.fromText(obj.text); | |
if (ret instanceof ActiveFilter) { | |
if ("disabled" in obj) ret._disabled = obj.disabled == "true"; | |
if ("hitCount" in obj) ret._hitCount = parseInt(obj.hitCount) || 0; | |
if ("lastHit" in obj) ret._lastHit = parseInt(obj.lastHit) || 0; | |
} | |
return ret; | |
}; | |
/** | |
* Removes unnecessary whitespaces from filter text, will only return null if | |
* the input parameter is null. | |
*/ | |
Filter.normalize = function ( /**String*/text) /**String*/ | |
{ | |
if (!text) return text; | |
// Remove line breaks and such | |
text = text.replace(/[^\S ]/g, ""); | |
if (/^\s*!/.test(text)) { | |
// Don't remove spaces inside comments | |
return text.trim(); | |
} else if (Filter.elemhideRegExp.test(text)) { | |
// Special treatment for element hiding filters, right side is allowed to contain spaces | |
var _$$exec = /^(.*?)(#\@?#?)(.*)$/.exec(text); | |
var _$$exec2 = _slicedToArray(_$$exec, 4); | |
let domain = _$$exec2[1]; | |
let separator = _$$exec2[2]; | |
let selector = _$$exec2[3]; | |
return domain.replace(/\s/g, "") + separator + selector.trim(); | |
} else return text.replace(/\s/g, ""); | |
}; | |
/** | |
* Class for invalid filters | |
* @param {String} text see Filter() | |
* @param {String} reason Reason why this filter is invalid | |
* @constructor | |
* @augments Filter | |
*/ | |
function InvalidFilter(text, reason) { | |
Filter.call(this, text); | |
this.reason = reason; | |
} | |
exports.InvalidFilter = InvalidFilter; | |
InvalidFilter.prototype = { | |
__proto__: Filter.prototype, | |
/** | |
* Reason why this filter is invalid | |
* @type String | |
*/ | |
reason: null, | |
/** | |
* See Filter.serialize() | |
*/ | |
serialize: function serialize(buffer) {} | |
}; | |
/** | |
* Class for comments | |
* @param {String} text see Filter() | |
* @constructor | |
* @augments Filter | |
*/ | |
function CommentFilter(text) { | |
Filter.call(this, text); | |
} | |
exports.CommentFilter = CommentFilter; | |
CommentFilter.prototype = { | |
__proto__: Filter.prototype, | |
/** | |
* See Filter.serialize() | |
*/ | |
serialize: function serialize(buffer) {} | |
}; | |
/** | |
* Abstract base class for filters that can get hits | |
* @param {String} text see Filter() | |
* @param {String} [domains] Domains that the filter is restricted to separated by domainSeparator e.g. "foo.com|bar.com|~baz.com" | |
* @constructor | |
* @augments Filter | |
*/ | |
function ActiveFilter(text, domains) { | |
Filter.call(this, text); | |
this.domainSource = domains; | |
} | |
exports.ActiveFilter = ActiveFilter; | |
ActiveFilter.prototype = Object.defineProperties({ | |
__proto__: Filter.prototype, | |
_disabled: false, | |
_hitCount: 0, | |
_lastHit: 0, | |
/** | |
* String that the domains property should be generated from | |
* @type String | |
*/ | |
domainSource: null, | |
/** | |
* Separator character used in domainSource property, must be overridden by subclasses | |
* @type String | |
*/ | |
domainSeparator: null, | |
/** | |
* Determines whether the trailing dot in domain names isn't important and | |
* should be ignored, must be overridden by subclasses. | |
* @type Boolean | |
*/ | |
ignoreTrailingDot: true, | |
/** | |
* Determines whether domainSource is already upper-case, | |
* can be overridden by subclasses. | |
* @type Boolean | |
*/ | |
domainSourceIsUpperCase: false, | |
/** | |
* Array containing public keys of websites that this filter should apply to | |
* @type Array of String | |
*/ | |
sitekeys: null, | |
/** | |
* Checks whether this filter is active on a domain. | |
* @param {String} docDomain domain name of the document that loads the URL | |
* @param {String} [sitekey] public key provided by the document | |
* @return {Boolean} true in case of the filter being active | |
*/ | |
isActiveOnDomain: function isActiveOnDomain(docDomain, sitekey) { | |
// Sitekeys are case-sensitive so we shouldn't convert them to upper-case to avoid false | |
// positives here. Instead we need to change the way filter options are parsed. | |
if (this.sitekeys && (!sitekey || this.sitekeys.indexOf(sitekey.toUpperCase()) < 0)) { | |
return false; | |
} // If no domains are set the rule matches everywhere | |
if (!this.domains) { | |
return true; | |
} // If the document has no host name, match only if the filter isn't restricted to specific domains | |
if (!docDomain) { | |
return this.domains[""]; | |
}if (this.ignoreTrailingDot) docDomain = docDomain.replace(/\.+$/, ""); | |
docDomain = docDomain.toUpperCase(); | |
while (true) { | |
if (docDomain in this.domains) { | |
return this.domains[docDomain]; | |
}let nextDot = docDomain.indexOf("."); | |
if (nextDot < 0) break; | |
docDomain = docDomain.substr(nextDot + 1); | |
} | |
return this.domains[""]; | |
}, | |
/** | |
* Checks whether this filter is active only on a domain and its subdomains. | |
*/ | |
isActiveOnlyOnDomain: function isActiveOnlyOnDomain( /**String*/docDomain) /**Boolean*/ | |
{ | |
if (!docDomain || !this.domains || this.domains[""]) { | |
return false; | |
}if (this.ignoreTrailingDot) docDomain = docDomain.replace(/\.+$/, ""); | |
docDomain = docDomain.toUpperCase(); | |
for (let domain in this.domains) if (this.domains[domain] && domain != docDomain && (domain.length <= docDomain.length || domain.indexOf("." + docDomain) != domain.length - docDomain.length - 1)) { | |
return false; | |
}return true; | |
}, | |
/** | |
* See Filter.serialize() | |
*/ | |
serialize: function serialize(buffer) { | |
if (this._disabled || this._hitCount || this._lastHit) { | |
Filter.prototype.serialize.call(this, buffer); | |
if (this._disabled) buffer.push("disabled=true"); | |
if (this._hitCount) buffer.push("hitCount=" + this._hitCount); | |
if (this._lastHit) buffer.push("lastHit=" + this._lastHit); | |
} | |
} | |
}, { | |
disabled: { | |
get: function () { | |
return this._disabled; | |
}, | |
set: function (value) { | |
if (value != this._disabled) { | |
let oldValue = this._disabled; | |
this._disabled = value; | |
FilterNotifier.triggerListeners("filter.disabled", this, value, oldValue); | |
} | |
return this._disabled; | |
}, | |
enumerable: true, | |
configurable: true | |
}, | |
hitCount: { | |
get: function () { | |
return this._hitCount; | |
}, | |
set: function (value) { | |
if (value != this._hitCount) { | |
let oldValue = this._hitCount; | |
this._hitCount = value; | |
FilterNotifier.triggerListeners("filter.hitCount", this, value, oldValue); | |
} | |
return this._hitCount; | |
}, | |
enumerable: true, | |
configurable: true | |
}, | |
lastHit: { | |
get: function () { | |
return this._lastHit; | |
}, | |
set: function (value) { | |
if (value != this._lastHit) { | |
let oldValue = this._lastHit; | |
this._lastHit = value; | |
FilterNotifier.triggerListeners("filter.lastHit", this, value, oldValue); | |
} | |
return this._lastHit; | |
}, | |
enumerable: true, | |
configurable: true | |
}, | |
domains: { | |
get: function () { | |
// Despite this property being cached, the getter is called | |
// several times on Safari, due to WebKit bug 132872 | |
let prop = Object.getOwnPropertyDescriptor(this, "domains"); | |
if (prop) return prop.value; | |
let domains = null; | |
if (this.domainSource) { | |
let source = this.domainSource; | |
if (!this.domainSourceIsUpperCase) { | |
// RegExpFilter already have uppercase domains | |
source = source.toUpperCase(); | |
} | |
let list = source.split(this.domainSeparator); | |
if (list.length == 1 && list[0][0] != "~") { | |
// Fast track for the common one-domain scenario | |
domains = { __proto__: null, "": false }; | |
if (this.ignoreTrailingDot) list[0] = list[0].replace(/\.+$/, ""); | |
domains[list[0]] = true; | |
} else { | |
let hasIncludes = false; | |
for (let i = 0; i < list.length; i++) { | |
let domain = list[i]; | |
if (this.ignoreTrailingDot) domain = domain.replace(/\.+$/, ""); | |
if (domain == "") continue; | |
let include; | |
if (domain[0] == "~") { | |
include = false; | |
domain = domain.substr(1); | |
} else { | |
include = true; | |
hasIncludes = true; | |
} | |
if (!domains) domains = Object.create(null); | |
domains[domain] = include; | |
} | |
domains[""] = !hasIncludes; | |
} | |
this.domainSource = null; | |
} | |
Object.defineProperty(this, "domains", { value: domains, enumerable: true }); | |
return this.domains; | |
}, | |
enumerable: true, | |
configurable: true | |
} | |
}); | |
/** | |
* Abstract base class for RegExp-based filters | |
* @param {String} text see Filter() | |
* @param {String} regexpSource filter part that the regular expression should be build from | |
* @param {Number} [contentType] Content types the filter applies to, combination of values from RegExpFilter.typeMap | |
* @param {Boolean} [matchCase] Defines whether the filter should distinguish between lower and upper case letters | |
* @param {String} [domains] Domains that the filter is restricted to, e.g. "foo.com|bar.com|~baz.com" | |
* @param {Boolean} [thirdParty] Defines whether the filter should apply to third-party or first-party content only | |
* @param {String} [sitekeys] Public keys of websites that this filter should apply to | |
* @constructor | |
* @augments ActiveFilter | |
*/ | |
function RegExpFilter(text, regexpSource, contentType, matchCase, domains, thirdParty, sitekeys) { | |
ActiveFilter.call(this, text, domains, sitekeys); | |
if (contentType != null) this.contentType = contentType; | |
if (matchCase) this.matchCase = matchCase; | |
if (thirdParty != null) this.thirdParty = thirdParty; | |
if (sitekeys != null) this.sitekeySource = sitekeys; | |
if (regexpSource.length >= 2 && regexpSource[0] == "/" && regexpSource[regexpSource.length - 1] == "/") { | |
// The filter is a regular expression - convert it immediately to catch syntax errors | |
let regexp = new RegExp(regexpSource.substr(1, regexpSource.length - 2), this.matchCase ? "" : "i"); | |
Object.defineProperty(this, "regexp", { value: regexp }); | |
} else { | |
// No need to convert this filter to regular expression yet, do it on demand | |
this.regexpSource = regexpSource; | |
} | |
} | |
exports.RegExpFilter = RegExpFilter; | |
RegExpFilter.prototype = Object.defineProperties({ | |
__proto__: ActiveFilter.prototype, | |
/** | |
* @see ActiveFilter.domainSourceIsUpperCase | |
*/ | |
domainSourceIsUpperCase: true, | |
/** | |
* Number of filters contained, will always be 1 (required to optimize Matcher). | |
* @type Integer | |
*/ | |
length: 1, | |
/** | |
* @see ActiveFilter.domainSeparator | |
*/ | |
domainSeparator: "|", | |
/** | |
* Expression from which a regular expression should be generated - for delayed creation of the regexp property | |
* @type String | |
*/ | |
regexpSource: null, | |
/** | |
* Content types the filter applies to, combination of values from RegExpFilter.typeMap | |
* @type Number | |
*/ | |
contentType: 2147483647, | |
/** | |
* Defines whether the filter should distinguish between lower and upper case letters | |
* @type Boolean | |
*/ | |
matchCase: false, | |
/** | |
* Defines whether the filter should apply to third-party or first-party content only. Can be null (apply to all content). | |
* @type Boolean | |
*/ | |
thirdParty: null, | |
/** | |
* String that the sitekey property should be generated from | |
* @type String | |
*/ | |
sitekeySource: null, | |
/** | |
* Tests whether the URL matches this filter | |
* @param {String} location URL to be tested | |
* @param {String} contentType content type identifier of the URL | |
* @param {String} docDomain domain name of the document that loads the URL | |
* @param {Boolean} thirdParty should be true if the URL is a third-party request | |
* @param {String} sitekey public key provided by the document | |
* @return {Boolean} true in case of a match | |
*/ | |
matches: function matches(location, contentType, docDomain, thirdParty, sitekey) { | |
if (this.regexp.test(location) && (RegExpFilter.typeMap[contentType] & this.contentType) != 0 && (this.thirdParty == null || this.thirdParty == thirdParty) && this.isActiveOnDomain(docDomain, sitekey)) { | |
return true; | |
} | |
return false; | |
} | |
}, { | |
regexp: { | |
get: function () { | |
// Despite this property being cached, the getter is called | |
// several times on Safari, due to WebKit bug 132872 | |
let prop = Object.getOwnPropertyDescriptor(this, "regexp"); | |
if (prop) return prop.value; | |
// Remove multiple wildcards | |
let source = this.regexpSource.replace(/\*+/g, "*") // remove multiple wildcards | |
.replace(/\^\|$/, "^") // remove anchors following separator placeholder | |
.replace(/\W/g, "\\$&") // escape special symbols | |
.replace(/\\\*/g, ".*") // replace wildcards by .* | |
// process separator placeholders (all ANSI characters but alphanumeric characters and _%.-) | |
.replace(/\\\^/g, "(?:[\\x00-\\x24\\x26-\\x2C\\x2F\\x3A-\\x40\\x5B-\\x5E\\x60\\x7B-\\x7F]|$)").replace(/^\\\|\\\|/, "^[\\w\\-]+:\\/+(?!\\/)(?:[^\\/]+\\.)?") // process extended anchor at expression start | |
.replace(/^\\\|/, "^") // process anchor at expression start | |
.replace(/\\\|$/, "$") // process anchor at expression end | |
.replace(/^(\.\*)/, "") // remove leading wildcards | |
.replace(/(\.\*)$/, ""); // remove trailing wildcards | |
let regexp = new RegExp(source, this.matchCase ? "" : "i"); | |
Object.defineProperty(this, "regexp", { value: regexp }); | |
return regexp; | |
}, | |
enumerable: true, | |
configurable: true | |
}, | |
sitekeys: { | |
get: function () { | |
// Despite this property being cached, the getter is called | |
// several times on Safari, due to WebKit bug 132872 | |
let prop = Object.getOwnPropertyDescriptor(this, "sitekeys"); | |
if (prop) return prop.value; | |
let sitekeys = null; | |
if (this.sitekeySource) { | |
sitekeys = this.sitekeySource.split("|"); | |
this.sitekeySource = null; | |
} | |
Object.defineProperty(this, "sitekeys", { value: sitekeys, enumerable: true }); | |
return this.sitekeys; | |
}, | |
enumerable: true, | |
configurable: true | |
} | |
}); | |
// Required to optimize Matcher, see also RegExpFilter.prototype.length | |
Object.defineProperty(RegExpFilter.prototype, "0", { | |
get: function get() { | |
return this; | |
} | |
}); | |
/** | |
* Creates a RegExp filter from its text representation | |
* @param {String} text same as in Filter() | |
*/ | |
RegExpFilter.fromText = function (text) { | |
let blocking = true; | |
let origText = text; | |
if (text.indexOf("@@") == 0) { | |
blocking = false; | |
text = text.substr(2); | |
} | |
let contentType = null; | |
let matchCase = null; | |
let domains = null; | |
let sitekeys = null; | |
let thirdParty = null; | |
let collapse = null; | |
let options; | |
let match = text.indexOf("$") >= 0 ? Filter.optionsRegExp.exec(text) : null; | |
if (match) { | |
options = match[1].toUpperCase().split(","); | |
text = match.input.substr(0, match.index); | |
for (let option of options) { | |
let value = null; | |
let separatorIndex = option.indexOf("="); | |
if (separatorIndex >= 0) { | |
value = option.substr(separatorIndex + 1); | |
option = option.substr(0, separatorIndex); | |
} | |
option = option.replace(/-/, "_"); | |
if (option in RegExpFilter.typeMap) { | |
if (contentType == null) contentType = 0; | |
contentType |= RegExpFilter.typeMap[option]; | |
} else if (option[0] == "~" && option.substr(1) in RegExpFilter.typeMap) { | |
if (contentType == null) contentType = RegExpFilter.prototype.contentType; | |
contentType &= ~RegExpFilter.typeMap[option.substr(1)]; | |
} else if (option == "MATCH_CASE") matchCase = true;else if (option == "~MATCH_CASE") matchCase = false;else if (option == "DOMAIN" && typeof value != "undefined") domains = value;else if (option == "THIRD_PARTY") thirdParty = true;else if (option == "~THIRD_PARTY") thirdParty = false;else if (option == "COLLAPSE") collapse = true;else if (option == "~COLLAPSE") collapse = false;else if (option == "SITEKEY" && typeof value != "undefined") sitekeys = value;else return new InvalidFilter(origText, "Unknown option " + option.toLowerCase()); | |
} | |
} | |
if (!blocking && (contentType == null || contentType & RegExpFilter.typeMap.DOCUMENT) && (!options || options.indexOf("DOCUMENT") < 0) && !/^\|?[\w\-]+:/.test(text)) { | |
// Exception filters shouldn't apply to pages by default unless they start with a protocol name | |
if (contentType == null) contentType = RegExpFilter.prototype.contentType; | |
contentType &= ~RegExpFilter.typeMap.DOCUMENT; | |
} | |
try { | |
if (blocking) return new BlockingFilter(origText, text, contentType, matchCase, domains, thirdParty, sitekeys, collapse);else return new WhitelistFilter(origText, text, contentType, matchCase, domains, thirdParty, sitekeys); | |
} catch (e) { | |
return new InvalidFilter(origText, e); | |
} | |
}; | |
/** | |
* Maps type strings like "SCRIPT" or "OBJECT" to bit masks | |
*/ | |
RegExpFilter.typeMap = { | |
OTHER: 1, | |
SCRIPT: 2, | |
IMAGE: 4, | |
STYLESHEET: 8, | |
OBJECT: 16, | |
SUBDOCUMENT: 32, | |
DOCUMENT: 64, | |
XBL: 1, | |
PING: 1, | |
XMLHTTPREQUEST: 2048, | |
OBJECT_SUBREQUEST: 4096, | |
DTD: 1, | |
MEDIA: 16384, | |
FONT: 32768, | |
BACKGROUND: 4, // Backwards compat, same as IMAGE | |
POPUP: 268435456, | |
ELEMHIDE: 1073741824 | |
}; | |
// ELEMHIDE, POPUP option shouldn't be there by default | |
RegExpFilter.prototype.contentType &= ~(RegExpFilter.typeMap.ELEMHIDE | RegExpFilter.typeMap.POPUP); | |
/** | |
* Class for blocking filters | |
* @param {String} text see Filter() | |
* @param {String} regexpSource see RegExpFilter() | |
* @param {Number} contentType see RegExpFilter() | |
* @param {Boolean} matchCase see RegExpFilter() | |
* @param {String} domains see RegExpFilter() | |
* @param {Boolean} thirdParty see RegExpFilter() | |
* @param {String} sitekeys see RegExpFilter() | |
* @param {Boolean} collapse defines whether the filter should collapse blocked content, can be null | |
* @constructor | |
* @augments RegExpFilter | |
*/ | |
function BlockingFilter(text, regexpSource, contentType, matchCase, domains, thirdParty, sitekeys, collapse) { | |
RegExpFilter.call(this, text, regexpSource, contentType, matchCase, domains, thirdParty, sitekeys); | |
this.collapse = collapse; | |
} | |
exports.BlockingFilter = BlockingFilter; | |
BlockingFilter.prototype = { | |
__proto__: RegExpFilter.prototype, | |
/** | |
* Defines whether the filter should collapse blocked content. Can be null (use the global preference). | |
* @type Boolean | |
*/ | |
collapse: null | |
}; | |
/** | |
* Class for whitelist filters | |
* @param {String} text see Filter() | |
* @param {String} regexpSource see RegExpFilter() | |
* @param {Number} contentType see RegExpFilter() | |
* @param {Boolean} matchCase see RegExpFilter() | |
* @param {String} domains see RegExpFilter() | |
* @param {Boolean} thirdParty see RegExpFilter() | |
* @param {String} sitekeys see RegExpFilter() | |
* @constructor | |
* @augments RegExpFilter | |
*/ | |
function WhitelistFilter(text, regexpSource, contentType, matchCase, domains, thirdParty, sitekeys) { | |
RegExpFilter.call(this, text, regexpSource, contentType, matchCase, domains, thirdParty, sitekeys); | |
} | |
exports.WhitelistFilter = WhitelistFilter; | |
WhitelistFilter.prototype = { | |
__proto__: RegExpFilter.prototype | |
}; | |
/** | |
* Base class for element hiding filters | |
* @param {String} text see Filter() | |
* @param {String} [domains] Host names or domains the filter should be restricted to | |
* @param {String} selector CSS selector for the HTML elements that should be hidden | |
* @constructor | |
* @augments ActiveFilter | |
*/ | |
function ElemHideBase(text, domains, selector) { | |
ActiveFilter.call(this, text, domains || null); | |
if (domains) this.selectorDomain = domains.replace(/,~[^,]+/g, "").replace(/^~[^,]+,?/, "").toLowerCase(); | |
this.selector = selector; | |
} | |
exports.ElemHideBase = ElemHideBase; | |
ElemHideBase.prototype = { | |
__proto__: ActiveFilter.prototype, | |
/** | |
* @see ActiveFilter.domainSeparator | |
*/ | |
domainSeparator: ",", | |
/** | |
* @see ActiveFilter.ignoreTrailingDot | |
*/ | |
ignoreTrailingDot: false, | |
/** | |
* Host name or domain the filter should be restricted to (can be null for no restriction) | |
* @type String | |
*/ | |
selectorDomain: null, | |
/** | |
* CSS selector for the HTML elements that should be hidden | |
* @type String | |
*/ | |
selector: null | |
}; | |
/** | |
* Creates an element hiding filter from a pre-parsed text representation | |
* | |
* @param {String} text same as in Filter() | |
* @param {String} domain domain part of the text representation (can be empty) | |
* @param {String} tagName tag name part (can be empty) | |
* @param {String} attrRules attribute matching rules (can be empty) | |
* @param {String} selector raw CSS selector (can be empty) | |
* @return {ElemHideFilter|ElemHideException|InvalidFilter} | |
*/ | |
ElemHideBase.fromText = function (text, domain, isException, tagName, attrRules, selector) { | |
if (!selector) { | |
if (tagName == "*") tagName = ""; | |
let id = null; | |
let additional = ""; | |
if (attrRules) { | |
attrRules = attrRules.match(/\([\w\-]+(?:[$^*]?=[^\(\)"]*)?\)/g); | |
for (let rule of attrRules) { | |
rule = rule.substr(1, rule.length - 2); | |
let separatorPos = rule.indexOf("="); | |
if (separatorPos > 0) { | |
rule = rule.replace(/=/, "=\"") + "\""; | |
additional += "[" + rule + "]"; | |
} else { | |
if (id) { | |
var _require2 = __webpack_require__(18); | |
let Utils = _require2.Utils; | |
return new InvalidFilter(text, Utils.getString("filter_elemhide_duplicate_id")); | |
} else id = rule; | |
} | |
} | |
} | |
if (id) selector = tagName + "." + id + additional + "," + tagName + "#" + id + additional;else if (tagName || additional) selector = tagName + additional;else { | |
var _require3 = __webpack_require__(18); | |
let Utils = _require3.Utils; | |
return new InvalidFilter(text, Utils.getString("filter_elemhide_nocriteria")); | |
} | |
} | |
if (isException) return new ElemHideException(text, domain, selector);else return new ElemHideFilter(text, domain, selector); | |
}; | |
/** | |
* Class for element hiding filters | |
* @param {String} text see Filter() | |
* @param {String} domains see ElemHideBase() | |
* @param {String} selector see ElemHideBase() | |
* @constructor | |
* @augments ElemHideBase | |
*/ | |
function ElemHideFilter(text, domains, selector) { | |
ElemHideBase.call(this, text, domains, selector); | |
} | |
exports.ElemHideFilter = ElemHideFilter; | |
ElemHideFilter.prototype = { | |
__proto__: ElemHideBase.prototype | |
}; | |
/** | |
* Class for element hiding exceptions | |
* @param {String} text see Filter() | |
* @param {String} domains see ElemHideBase() | |
* @param {String} selector see ElemHideBase() | |
* @constructor | |
* @augments ElemHideBase | |
*/ | |
function ElemHideException(text, domains, selector) { | |
ElemHideBase.call(this, text, domains, selector); | |
} | |
exports.ElemHideException = ElemHideException; | |
ElemHideException.prototype = { | |
__proto__: ElemHideBase.prototype | |
}; | |
/** | |
* Defines whether the filter is disabled | |
* @type Boolean | |
*/ | |
/** | |
* Number of hits on the filter since the last reset | |
* @type Number | |
*/ | |
/** | |
* Last time the filter had a hit (in milliseconds since the beginning of the epoch) | |
* @type Number | |
*/ | |
/** | |
* Map containing domains that this filter should match on/not match on or null if the filter should match on all domains | |
* @type Object | |
*/ | |
/** | |
* Regular expression to be used when testing against this filter | |
* @type RegExp | |
*/ | |
/** | |
* Array containing public keys of websites that this filter should apply to | |
* @type Array of String | |
*/ | |
/***/ }, | |
/* 16 */ | |
/***/ function(module, exports, __webpack_require__) { | |
module.exports = function() { | |
var list = []; | |
list.toString = function toString() { | |
var result = []; | |
for(var i = 0; i < this.length; i++) { | |
var item = this[i]; | |
if(item[2]) { | |
result.push("@media " + item[2] + "{" + item[1] + "}"); | |
} else { | |
result.push(item[1]); | |
} | |
} | |
return result.join(""); | |
}; | |
return list; | |
} | |
/***/ }, | |
/* 17 */ | |
/***/ function(module, exports, __webpack_require__) { | |
"use strict"; | |
/* | |
* This file is part of Adblock Plus <https://adblockplus.org/>, | |
* Copyright (C) 2006-2015 Eyeo GmbH | |
* | |
* Adblock Plus is free software: you can redistribute it and/or modify | |
* it under the terms of the GNU General Public License version 3 as | |
* published by the Free Software Foundation. | |
* | |
* Adblock Plus 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 Adblock Plus. If not, see <http://www.gnu.org/licenses/>. | |
*/ | |
/** | |
* @fileOverview This component manages listeners and calls them to distributes | |
* messages about filter changes. | |
*/ | |
/** | |
* List of registered listeners | |
* @type Array of function(action, item, newValue, oldValue) | |
*/ | |
let listeners = []; | |
/** | |
* This class allows registering and triggering listeners for filter events. | |
* @class | |
*/ | |
let FilterNotifier = exports.FilterNotifier = { | |
/** | |
* Adds a listener | |
*/ | |
addListener: function addListener( /**function(action, item, newValue, oldValue)*/listener) { | |
if (listeners.indexOf(listener) >= 0) { | |
return; | |
}listeners.push(listener); | |
}, | |
/** | |
* Removes a listener that was previosly added via addListener | |
*/ | |
removeListener: function removeListener( /**function(action, item, newValue, oldValue)*/listener) { | |
let index = listeners.indexOf(listener); | |
if (index >= 0) listeners.splice(index, 1); | |
}, | |
/** | |
* Notifies listeners about an event | |
* @param {String} action event code ("load", "save", "elemhideupdate", | |
* "subscription.added", "subscription.removed", | |
* "subscription.disabled", "subscription.title", | |
* "subscription.lastDownload", "subscription.downloadStatus", | |
* "subscription.homepage", "subscription.updated", | |
* "filter.added", "filter.removed", "filter.moved", | |
* "filter.disabled", "filter.hitCount", "filter.lastHit") | |
* @param {Subscription|Filter} item item that the change applies to | |
*/ | |
triggerListeners: function triggerListeners(action, item, param1, param2, param3) { | |
let list = listeners.slice(); | |
for (let listener of list) listener(action, item, param1, param2, param3); | |
} | |
}; | |
/***/ }, | |
/* 18 */ | |
/***/ function(module, exports, __webpack_require__) { | |
"use strict"; | |
/***/ }, | |
/* 19 */ | |
/***/ function(module, exports, __webpack_require__) { | |
module.exports = "data:image/png;base64,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" | |
/***/ }, | |
/* 20 */ | |
/***/ function(module, exports, __webpack_require__) { | |
module.exports = "data:image/png;base64,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" | |
/***/ }, | |
/* 21 */ | |
/***/ function(module, exports, __webpack_require__) { | |
module.exports = "data:image/png;base64,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" | |
/***/ } | |
/******/ ]); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment