优化 pac.js 的代码

pull/292/head
王良 2024-04-10 21:30:31 +08:00
parent 34211d062c
commit de6769c330
1 changed files with 609 additions and 624 deletions

View File

@ -3,17 +3,16 @@ const path = require('path')
const log = require('../../../../utils/util.log') const log = require('../../../../utils/util.log')
function createPacClient (pacFilePath) { function createPacClient (pacFilePath) {
var __PROXY__ = 'PROXY 127.0.0.1:1080;' const __PROXY__ = 'PROXY 127.0.0.1:1080;'
var __USERRULES__ = []
function readFile (location) { function readFile (location) {
try { try {
log.info('pac root dir', path.resolve('./')) log.info('pac root dir:', path.resolve('./'))
const filePath = path.resolve(location) const filePath = path.resolve(location)
log.info('read pac path:', filePath) log.info('read pac path:', filePath)
return fs.readFileSync(location).toString() return fs.readFileSync(location).toString()
} catch (e) { } catch (e) {
log.error('读取pac失败') log.error('读取pac失败:', e)
return '' return ''
} }
} }
@ -34,7 +33,7 @@ function createPacClient (pacFilePath) {
} }
return rules return rules
} }
var __RULES__ = getRules(pacFilePath) const __RULES__ = getRules(pacFilePath)
/* eslint-disable */ /* eslint-disable */
// Was generated by gfwlist2pac in precise mode // Was generated by gfwlist2pac in precise mode
@ -43,21 +42,14 @@ function createPacClient (pacFilePath) {
// 2019-10-06: More 'javascript' way to interaction with main program // 2019-10-06: More 'javascript' way to interaction with main program
// 2019-02-08: Updated to support shadowsocks-windows user rules. // 2019-02-08: Updated to support shadowsocks-windows user rules.
var proxy = __PROXY__; const proxy = __PROXY__
var userrules = []; const rules = []
var rules = [];
// convert to abp grammar // convert to abp grammar
for (var i = 0; i < __RULES__.length; i++) { for (let i = 0; i < __RULES__.length; i++) {
var s = __RULES__[i]; let s = __RULES__[i]
if (s.substring(0, 2) == "||") s += "^"; if (s.substring(0, 2) === "||") s += "^"
rules.push(s); rules.push(s)
}
for (var i = 0; i < __USERRULES__.length; i++) {
var s = __USERRULES__[i];
if (s.substring(0, 2) == "||") s += "^";
userrules.push(s);
} }
/* /*
@ -77,85 +69,84 @@ function createPacClient (pacFilePath) {
* along with Adblock Plus. If not, see <http://www.gnu.org/licenses/>. * along with Adblock Plus. If not, see <http://www.gnu.org/licenses/>.
*/ */
function createDict() { function createDict () {
var result = {}; const result = {}
result.__proto__ = null; result.__proto__ = null
return result; return result
} }
function getOwnPropertyDescriptor(obj, key) { function getOwnPropertyDescriptor (obj, key) {
if (obj.hasOwnProperty(key)) { if (obj.hasOwnProperty(key)) {
return obj[key]; return obj[key]
} }
return null; return null
} }
function extend(subclass, superclass, definition) { function extend (subClass, superClass, definition) {
if (Object.__proto__) { if (Object.__proto__) {
definition.__proto__ = superclass.prototype; definition.__proto__ = superClass.prototype
subclass.prototype = definition; subClass.prototype = definition
} else { } else {
var tmpclass = function () { const tmpClass = function () {}
}, ret; tmpClass.prototype = superClass.prototype
tmpclass.prototype = superclass.prototype; subClass.prototype = new tmpClass()
subclass.prototype = new tmpclass(); subClass.prototype.constructor = superClass
subclass.prototype.constructor = superclass; for (const key in definition) {
for (var i in definition) { if (definition.hasOwnProperty(key)) {
if (definition.hasOwnProperty(i)) { subClass.prototype[key] = definition[key]
subclass.prototype[i] = definition[i];
} }
} }
} }
} }
function Filter(text) { function Filter (text) {
this.text = text; this.text = text
this.subscriptions = []; this.subscriptions = []
} }
Filter.prototype = { Filter.prototype = {
text: null, text: null,
subscriptions: null, subscriptions: null,
toString: function () { toString: function () {
return this.text; return this.text
} }
}; }
Filter.knownFilters = createDict(); Filter.knownFilters = createDict()
Filter.elemhideRegExp = /^([^\/\*\|\@"!]*?)#(\@)?(?:([\w\-]+|\*)((?:\([\w\-]+(?:[$^*]?=[^\(\)"]*)?\))*)|#([^{}]+))$/; Filter.elemhideRegExp = /^([^\/\*\|\@"!]*?)#(\@)?(?:([\w\-]+|\*)((?:\([\w\-]+(?:[$^*]?=[^\(\)"]*)?\))*)|#([^{}]+))$/
Filter.regexpRegExp = /^(@@)?\/.*\/(?:\$~?[\w\-]+(?:=[^,\s]+)?(?:,~?[\w\-]+(?:=[^,\s]+)?)*)?$/; Filter.regexpRegExp = /^(@@)?\/.*\/(?:\$~?[\w\-]+(?:=[^,\s]+)?(?:,~?[\w\-]+(?:=[^,\s]+)?)*)?$/
Filter.optionsRegExp = /\$(~?[\w\-]+(?:=[^,\s]+)?(?:,~?[\w\-]+(?:=[^,\s]+)?)*)$/; Filter.optionsRegExp = /\$(~?[\w\-]+(?:=[^,\s]+)?(?:,~?[\w\-]+(?:=[^,\s]+)?)*)$/
Filter.fromText = function (text) { Filter.fromText = function (text) {
if (text in Filter.knownFilters) { if (text in Filter.knownFilters) {
return Filter.knownFilters[text]; return Filter.knownFilters[text]
} }
var ret; let ret
if (text.charAt(0) == "!") { if (text.charAt(0) === "!") {
ret = new CommentFilter(text); ret = new CommentFilter(text)
} else { } else {
ret = RegExpFilter.fromText(text); ret = RegExpFilter.fromText(text)
}
Filter.knownFilters[ret.text] = ret
return ret
} }
Filter.knownFilters[ret.text] = ret;
return ret;
};
function InvalidFilter(text, reason) { function InvalidFilter (text, reason) {
Filter.call(this, text); Filter.call(this, text)
this.reason = reason; this.reason = reason
} }
extend(InvalidFilter, Filter, { extend(InvalidFilter, Filter, {
reason: null reason: null
}); })
function CommentFilter(text) { function CommentFilter (text) {
Filter.call(this, text); Filter.call(this, text)
} }
extend(CommentFilter, Filter, {}); extend(CommentFilter, Filter, {})
function ActiveFilter(text, domains) { function ActiveFilter (text, domains) {
Filter.call(this, text); Filter.call(this, text)
this.domainSource = domains; this.domainSource = domains
} }
extend(ActiveFilter, Filter, { extend(ActiveFilter, Filter, {
@ -164,116 +155,115 @@ function createPacClient (pacFilePath) {
ignoreTrailingDot: true, ignoreTrailingDot: true,
domainSourceIsUpperCase: false, domainSourceIsUpperCase: false,
getDomains: function () { getDomains: function () {
var prop = getOwnPropertyDescriptor(this, "domains"); const prop = getOwnPropertyDescriptor(this, "domains")
if (prop) { if (prop) {
return prop; return prop
} }
var domains = null; let domains = null
if (this.domainSource) { if (this.domainSource) {
var source = this.domainSource; let source = this.domainSource
if (!this.domainSourceIsUpperCase) { if (!this.domainSourceIsUpperCase) {
source = source.toUpperCase(); source = source.toUpperCase()
} }
var list = source.split(this.domainSeparator); const list = source.split(this.domainSeparator)
if (list.length == 1 && (list[0]).charAt(0) != "~") { if (list.length === 1 && (list[0]).charAt(0) !== "~") {
domains = createDict(); domains = createDict()
domains[""] = false; domains[""] = false
if (this.ignoreTrailingDot) { if (this.ignoreTrailingDot) {
list[0] = list[0].replace(/\.+$/, ""); list[0] = list[0].replace(/\.+$/, "")
} }
domains[list[0]] = true; domains[list[0]] = true
} else { } else {
var hasIncludes = false; let hasIncludes = false
for (var i = 0; i < list.length; i++) { for (let i = 0; i < list.length; i++) {
var domain = list[i]; let domain = list[i]
if (this.ignoreTrailingDot) { if (this.ignoreTrailingDot) {
domain = domain.replace(/\.+$/, ""); domain = domain.replace(/\.+$/, "")
} }
if (domain == "") { if (domain === "") {
continue; continue
} }
var include; let include
if (domain.charAt(0) == "~") { if (domain.charAt(0) === "~") {
include = false; include = false
domain = domain.substr(1); domain = domain.substr(1)
} else { } else {
include = true; include = true
hasIncludes = true; hasIncludes = true
} }
if (!domains) { if (!domains) {
domains = createDict(); domains = createDict()
} }
domains[domain] = include; domains[domain] = include
} }
domains[""] = !hasIncludes; domains[""] = !hasIncludes
} }
this.domainSource = null; this.domainSource = null
} }
return this.domains; return this.domains
}, },
sitekeys: null, siteKeys: null,
isActiveOnDomain: function (docDomain, sitekey) { isActiveOnDomain: function (docDomain, siteKey) {
if (this.getSitekeys() && (!sitekey || this.getSitekeys().indexOf(sitekey.toUpperCase()) < 0)) { if (this.getSiteKeys() && (!siteKey || this.getSiteKeys().indexOf(siteKey.toUpperCase()) < 0)) {
return false; return false
} }
if (!this.getDomains()) { if (!this.getDomains()) {
return true; return true
} }
if (!docDomain) { if (!docDomain) {
return this.getDomains()[""]; return this.getDomains()[""]
} }
if (this.ignoreTrailingDot) { if (this.ignoreTrailingDot) {
docDomain = docDomain.replace(/\.+$/, ""); docDomain = docDomain.replace(/\.+$/, "")
} }
docDomain = docDomain.toUpperCase(); docDomain = docDomain.toUpperCase()
while (true) { while (true) {
if (docDomain in this.getDomains()) { if (docDomain in this.getDomains()) {
return this.domains[docDomain]; return this.domains[docDomain]
} }
var nextDot = docDomain.indexOf("."); const nextDot = docDomain.indexOf(".")
if (nextDot < 0) { if (nextDot < 0) {
break; break
} }
docDomain = docDomain.substr(nextDot + 1); docDomain = docDomain.substr(nextDot + 1)
} }
return this.domains[""]; return this.domains[""]
}, }/*,
isActiveOnlyOnDomain: function (docDomain) { isActiveOnlyOnDomain: function (docDomain) {
if (!docDomain || !this.getDomains() || this.getDomains()[""]) { if (!docDomain || !this.getDomains() || this.getDomains()[""]) {
return false; return false
} }
if (this.ignoreTrailingDot) { if (this.ignoreTrailingDot) {
docDomain = docDomain.replace(/\.+$/, ""); docDomain = docDomain.replace(/\.+$/, "")
} }
docDomain = docDomain.toUpperCase(); docDomain = docDomain.toUpperCase()
for (var domain in this.getDomains()) { for (const domain in this.getDomains()) {
if (this.domains[domain] && domain != docDomain && (domain.length <= docDomain.length || domain.indexOf("." + docDomain) != domain.length - docDomain.length - 1)) { if (this.domains[domain] && domain != docDomain && (domain.length <= docDomain.length || domain.indexOf("." + docDomain) != domain.length - docDomain.length - 1)) {
return false; return false
} }
} }
return true; return true
} }*/
}); })
function RegExpFilter(text, regexpSource, contentType, matchCase, domains, thirdParty, sitekeys) { function RegExpFilter (text, regexpSource, contentType, matchCase, domains, thirdParty, siteKeys) {
ActiveFilter.call(this, text, domains, sitekeys); ActiveFilter.call(this, text, domains, siteKeys)
if (contentType != null) { if (contentType != null) {
this.contentType = contentType; this.contentType = contentType
} }
if (matchCase) { if (matchCase) {
this.matchCase = matchCase; this.matchCase = matchCase
} }
if (thirdParty != null) { if (thirdParty != null) {
this.thirdParty = thirdParty; this.thirdParty = thirdParty
} }
if (sitekeys != null) { if (siteKeys != null) {
this.sitekeySource = sitekeys; this.siteKeySource = siteKeys
} }
if (regexpSource.length >= 2 && regexpSource.charAt(0) == "/" && regexpSource.charAt(regexpSource.length - 1) == "/") { if (regexpSource.length >= 2 && regexpSource.charAt(0) === "/" && regexpSource.charAt(regexpSource.length - 1) === "/") {
var regexp = new RegExp(regexpSource.substr(1, regexpSource.length - 2), this.matchCase ? "" : "i"); this.regexp = new RegExp(regexpSource.substr(1, regexpSource.length - 2), this.matchCase ? "" : "i")
this.regexp = regexp;
} else { } else {
this.regexpSource = regexpSource; this.regexpSource = regexpSource
} }
} }
@ -283,114 +273,111 @@ function createPacClient (pacFilePath) {
domainSeparator: "|", domainSeparator: "|",
regexpSource: null, regexpSource: null,
getRegexp: function () { getRegexp: function () {
var prop = getOwnPropertyDescriptor(this, "regexp"); const prop = getOwnPropertyDescriptor(this, "regexp")
if (prop) { if (prop) {
return prop; return prop
} }
var source = this.regexpSource.replace(/\*+/g, "*").replace(/\^\|$/, "^").replace(/\W/g, "\\$&").replace(/\\\*/g, ".*").replace(/\\\^/g, "(?:[\\x00-\\x24\\x26-\\x2C\\x2F\\x3A-\\x40\\x5B-\\x5E\\x60\\x7B-\\x7F]|$)").replace(/^\\\|\\\|/, "^[\\w\\-]+:\\/+(?!\\/)(?:[^\\/]+\\.)?").replace(/^\\\|/, "^").replace(/\\\|$/, "$").replace(/^(\.\*)/, "").replace(/(\.\*)$/, ""); const source = this.regexpSource.replace(/\*+/g, "*").replace(/\^\|$/, "^").replace(/\W/g, "\\$&").replace(/\\\*/g, ".*").replace(/\\\^/g, "(?:[\\x00-\\x24\\x26-\\x2C\\x2F\\x3A-\\x40\\x5B-\\x5E\\x60\\x7B-\\x7F]|$)").replace(/^\\\|\\\|/, "^[\\w\\-]+:\\/+(?!\\/)(?:[^\\/]+\\.)?").replace(/^\\\|/, "^").replace(/\\\|$/, "$").replace(/^(\.\*)/, "").replace(/(\.\*)$/, "")
var regexp = new RegExp(source, this.matchCase ? "" : "i"); const regexp = new RegExp(source, this.matchCase ? "" : "i")
this.regexp = regexp; this.regexp = regexp
return regexp; return regexp
}, },
contentType: 2147483647, contentType: 2147483647,
matchCase: false, matchCase: false,
thirdParty: null, thirdParty: null,
sitekeySource: null, siteKeySource: null,
getSitekeys: function () { getSiteKeys: function () {
var prop = getOwnPropertyDescriptor(this, "sitekeys"); const prop = getOwnPropertyDescriptor(this, "siteKeys")
if (prop) { if (prop) {
return prop; return prop
} }
var sitekeys = null; let siteKeys = null
if (this.sitekeySource) { if (this.siteKeySource) {
sitekeys = this.sitekeySource.split("|"); siteKeys = this.siteKeySource.split("|")
this.sitekeySource = null; this.siteKeySource = null
} }
this.sitekeys = sitekeys; this.siteKeys = siteKeys
return this.sitekeys; return this.siteKeys
}, },
matches: function (location, contentType, docDomain, thirdParty, sitekey) { matches: function (location, contentType, docDomain, thirdParty, siteKey) {
if (this.getRegexp().test(location) && this.isActiveOnDomain(docDomain, sitekey)) { return !!(this.getRegexp().test(location) && this.isActiveOnDomain(docDomain, siteKey))
return true;
} }
return false; })
} RegExpFilter.prototype["0"] = "#this"
});
RegExpFilter.prototype["0"] = "#this";
RegExpFilter.fromText = function (text) { RegExpFilter.fromText = function (text) {
var blocking = true; let blocking = true
var origText = text; const origText = text
if (text.indexOf("@@") == 0) { if (text.indexOf("@@") === 0) {
blocking = false; blocking = false
text = text.substr(2); text = text.substr(2)
} }
var contentType = null; let contentType = null
var matchCase = null; let matchCase = null
var domains = null; let domains = null
var sitekeys = null; let siteKeys = null
var thirdParty = null; let thirdParty = null
var collapse = null; let collapse = null
var options; let options
var match = text.indexOf("$") >= 0 ? Filter.optionsRegExp.exec(text) : null; const match = text.indexOf("$") >= 0 ? Filter.optionsRegExp.exec(text) : null
if (match) { if (match) {
options = match[1].toUpperCase().split(","); options = match[1].toUpperCase().split(",")
text = match.input.substr(0, match.index); text = match.input.substr(0, match.index)
for (var _loopIndex6 = 0; _loopIndex6 < options.length; ++_loopIndex6) { for (let _loopIndex6 = 0; _loopIndex6 < options.length; ++_loopIndex6) {
var option = options[_loopIndex6]; let option = options[_loopIndex6]
var value = null; let value = null
var separatorIndex = option.indexOf("="); const separatorIndex = option.indexOf("=")
if (separatorIndex >= 0) { if (separatorIndex >= 0) {
value = option.substr(separatorIndex + 1); value = option.substr(separatorIndex + 1)
option = option.substr(0, separatorIndex); option = option.substr(0, separatorIndex)
} }
option = option.replace(/-/, "_"); option = option.replace(/-/, "_")
if (option in RegExpFilter.typeMap) { if (option in RegExpFilter.typeMap) {
if (contentType == null) { if (contentType == null) {
contentType = 0; contentType = 0
} }
contentType |= RegExpFilter.typeMap[option]; contentType |= RegExpFilter.typeMap[option]
} else if (option.charAt(0) == "~" && option.substr(1) in RegExpFilter.typeMap) { } else if (option.charAt(0) === "~" && option.substr(1) in RegExpFilter.typeMap) {
if (contentType == null) { if (contentType == null) {
contentType = RegExpFilter.prototype.contentType; contentType = RegExpFilter.prototype.contentType
} }
contentType &= ~RegExpFilter.typeMap[option.substr(1)]; contentType &= ~RegExpFilter.typeMap[option.substr(1)]
} else if (option == "MATCH_CASE") { } else if (option === "MATCH_CASE") {
matchCase = true; matchCase = true
} else if (option == "~MATCH_CASE") { } else if (option === "~MATCH_CASE") {
matchCase = false; matchCase = false
} else if (option == "DOMAIN" && typeof value != "undefined") { } else if (option === "DOMAIN" && typeof value != "undefined") {
domains = value; domains = value
} else if (option == "THIRD_PARTY") { } else if (option === "THIRD_PARTY") {
thirdParty = true; thirdParty = true
} else if (option == "~THIRD_PARTY") { } else if (option === "~THIRD_PARTY") {
thirdParty = false; thirdParty = false
} else if (option == "COLLAPSE") { } else if (option === "COLLAPSE") {
collapse = true; collapse = true
} else if (option == "~COLLAPSE") { } else if (option === "~COLLAPSE") {
collapse = false; collapse = false
} else if (option == "SITEKEY" && typeof value != "undefined") { } else if (option === "SITEKEY" && typeof value != "undefined") {
sitekeys = value; siteKeys = value
} else { } else {
return new InvalidFilter(origText, "Unknown option " + option.toLowerCase()); return new InvalidFilter(origText, "Unknown option " + option.toLowerCase())
} }
} }
} }
if (!blocking && (contentType == null || contentType & RegExpFilter.typeMap.DOCUMENT) && (!options || options.indexOf("DOCUMENT") < 0) && !/^\|?[\w\-]+:/.test(text)) { if (!blocking && (contentType == null || contentType & RegExpFilter.typeMap.DOCUMENT) && (!options || options.indexOf("DOCUMENT") < 0) && !/^\|?[\w\-]+:/.test(text)) {
if (contentType == null) { if (contentType == null) {
contentType = RegExpFilter.prototype.contentType; contentType = RegExpFilter.prototype.contentType
} }
contentType &= ~RegExpFilter.typeMap.DOCUMENT; contentType &= ~RegExpFilter.typeMap.DOCUMENT
} }
try { try {
if (blocking) { if (blocking) {
return new BlockingFilter(origText, text, contentType, matchCase, domains, thirdParty, sitekeys, collapse); return new BlockingFilter(origText, text, contentType, matchCase, domains, thirdParty, siteKeys, collapse)
} else { } else {
return new WhitelistFilter(origText, text, contentType, matchCase, domains, thirdParty, sitekeys); return new WhitelistFilter(origText, text, contentType, matchCase, domains, thirdParty, siteKeys)
} }
} catch (e) { } catch (e) {
return new InvalidFilter(origText, e); return new InvalidFilter(origText, e)
}
} }
};
RegExpFilter.typeMap = { RegExpFilter.typeMap = {
OTHER: 1, OTHER: 1,
SCRIPT: 2, SCRIPT: 2,
@ -409,281 +396,279 @@ function createPacClient (pacFilePath) {
BACKGROUND: 4, BACKGROUND: 4,
POPUP: 268435456, POPUP: 268435456,
ELEMHIDE: 1073741824 ELEMHIDE: 1073741824
}; }
RegExpFilter.prototype.contentType &= ~(RegExpFilter.typeMap.ELEMHIDE | RegExpFilter.typeMap.POPUP); RegExpFilter.prototype.contentType &= ~(RegExpFilter.typeMap.ELEMHIDE | RegExpFilter.typeMap.POPUP)
function BlockingFilter(text, regexpSource, contentType, matchCase, domains, thirdParty, sitekeys, collapse) { function BlockingFilter (text, regexpSource, contentType, matchCase, domains, thirdParty, siteKeys, collapse) {
RegExpFilter.call(this, text, regexpSource, contentType, matchCase, domains, thirdParty, sitekeys); RegExpFilter.call(this, text, regexpSource, contentType, matchCase, domains, thirdParty, siteKeys)
this.collapse = collapse; this.collapse = collapse
} }
extend(BlockingFilter, RegExpFilter, { extend(BlockingFilter, RegExpFilter, {
collapse: null collapse: null
}); })
function WhitelistFilter(text, regexpSource, contentType, matchCase, domains, thirdParty, sitekeys) { function WhitelistFilter (text, regexpSource, contentType, matchCase, domains, thirdParty, siteKeys) {
RegExpFilter.call(this, text, regexpSource, contentType, matchCase, domains, thirdParty, sitekeys); RegExpFilter.call(this, text, regexpSource, contentType, matchCase, domains, thirdParty, siteKeys)
} }
extend(WhitelistFilter, RegExpFilter, {}); extend(WhitelistFilter, RegExpFilter, {})
function Matcher() { function Matcher () {
this.clear(); this.clear()
} }
Matcher.prototype = { Matcher.prototype = {
filterByKeyword: null, filterByKeyword: null,
keywordByFilter: null, keywordByFilter: null,
clear: function () { clear: function () {
this.filterByKeyword = createDict(); this.filterByKeyword = createDict()
this.keywordByFilter = createDict(); this.keywordByFilter = createDict()
}, },
add: function (filter) { add: function (filter) {
if (filter.text in this.keywordByFilter) { if (filter.text in this.keywordByFilter) {
return; return
} }
var keyword = this.findKeyword(filter); const keyword = this.findKeyword(filter)
var oldEntry = this.filterByKeyword[keyword]; const oldEntry = this.filterByKeyword[keyword]
if (typeof oldEntry == "undefined") { if (typeof oldEntry == "undefined") {
this.filterByKeyword[keyword] = filter; this.filterByKeyword[keyword] = filter
} else if (oldEntry.length == 1) { } else if (oldEntry.length === 1) {
this.filterByKeyword[keyword] = [oldEntry, filter]; this.filterByKeyword[keyword] = [oldEntry, filter]
} else { } else {
oldEntry.push(filter); oldEntry.push(filter)
} }
this.keywordByFilter[filter.text] = keyword; this.keywordByFilter[filter.text] = keyword
}, },
remove: function (filter) { remove: function (filter) {
if (!(filter.text in this.keywordByFilter)) { if (!(filter.text in this.keywordByFilter)) {
return; return
} }
var keyword = this.keywordByFilter[filter.text]; const keyword = this.keywordByFilter[filter.text]
var list = this.filterByKeyword[keyword]; const list = this.filterByKeyword[keyword]
if (list.length <= 1) { if (list.length <= 1) {
delete this.filterByKeyword[keyword]; delete this.filterByKeyword[keyword]
} else { } else {
var index = list.indexOf(filter); const index = list.indexOf(filter)
if (index >= 0) { if (index >= 0) {
list.splice(index, 1); list.splice(index, 1)
if (list.length == 1) { if (list.length === 1) {
this.filterByKeyword[keyword] = list[0]; this.filterByKeyword[keyword] = list[0]
} }
} }
} }
delete this.keywordByFilter[filter.text]; delete this.keywordByFilter[filter.text]
}, },
findKeyword: function (filter) { findKeyword: function (filter) {
var result = ""; let result = ""
var text = filter.text; let text = filter.text
if (Filter.regexpRegExp.test(text)) { if (Filter.regexpRegExp.test(text)) {
return result; return result
} }
var match = Filter.optionsRegExp.exec(text); const match = Filter.optionsRegExp.exec(text)
if (match) { if (match) {
text = match.input.substr(0, match.index); text = match.input.substr(0, match.index)
} }
if (text.substr(0, 2) == "@@") { if (text.substr(0, 2) === "@@") {
text = text.substr(2); text = text.substr(2)
} }
var candidates = text.toLowerCase().match(/[^a-z0-9%*][a-z0-9%]{3,}(?=[^a-z0-9%*])/g); const candidates = text.toLowerCase().match(/[^a-z0-9%*][a-z0-9%]{3,}(?=[^a-z0-9%*])/g)
if (!candidates) { if (!candidates) {
return result; return result
} }
var hash = this.filterByKeyword; const hash = this.filterByKeyword
var resultCount = 16777215; let resultCount = 16777215
var resultLength = 0; let resultLength = 0
for (var i = 0, l = candidates.length; i < l; i++) { for (let i = 0, l = candidates.length; i < l; i++) {
var candidate = candidates[i].substr(1); const candidate = candidates[i].substr(1)
var count = candidate in hash ? hash[candidate].length : 0; const count = candidate in hash ? hash[candidate].length : 0
if (count < resultCount || count == resultCount && candidate.length > resultLength) { if (count < resultCount || count === resultCount && candidate.length > resultLength) {
result = candidate; result = candidate
resultCount = count; resultCount = count
resultLength = candidate.length; resultLength = candidate.length
} }
} }
return result; return result
}, },
hasFilter: function (filter) { hasFilter: function (filter) {
return filter.text in this.keywordByFilter; return filter.text in this.keywordByFilter
}, },
getKeywordForFilter: function (filter) { getKeywordForFilter: function (filter) {
if (filter.text in this.keywordByFilter) { if (filter.text in this.keywordByFilter) {
return this.keywordByFilter[filter.text]; return this.keywordByFilter[filter.text]
} else { } else {
return null; return null
} }
}, },
_checkEntryMatch: function (keyword, location, contentType, docDomain, thirdParty, sitekey) { _checkEntryMatch: function (keyword, location, contentType, docDomain, thirdParty, siteKey) {
var list = this.filterByKeyword[keyword]; const list = this.filterByKeyword[keyword]
for (var i = 0; i < list.length; i++) { for (let i = 0; i < list.length; i++) {
var filter = list[i]; let filter = list[i]
if (filter == "#this") { if (filter === "#this") {
filter = list; filter = list
} }
if (filter.matches(location, contentType, docDomain, thirdParty, sitekey)) { if (filter.matches(location, contentType, docDomain, thirdParty, siteKey)) {
return filter; return filter
} }
} }
return null; return null
}, }/*,
matchesAny: function (location, contentType, docDomain, thirdParty, sitekey) { matchesAny: function (location, contentType, docDomain, thirdParty, siteKey) {
var candidates = location.toLowerCase().match(/[a-z0-9%]{3,}/g); let candidates = location.toLowerCase().match(/[a-z0-9%]{3,}/g)
if (candidates === null) { if (candidates === null) {
candidates = []; candidates = []
} }
candidates.push(""); candidates.push("")
for (var i = 0, l = candidates.length; i < l; i++) { for (let i = 0, l = candidates.length; i < l; i++) {
var substr = candidates[i]; const substr = candidates[i]
if (substr in this.filterByKeyword) { if (substr in this.filterByKeyword) {
var result = this._checkEntryMatch(substr, location, contentType, docDomain, thirdParty, sitekey); const result = this._checkEntryMatch(substr, location, contentType, docDomain, thirdParty, siteKey)
if (result) { if (result) {
return result; return result
} }
} }
} }
return null; return null
} }*/
};
function CombinedMatcher() {
this.blacklist = new Matcher();
this.whitelist = new Matcher();
this.resultCache = createDict();
} }
CombinedMatcher.maxCacheEntries = 1000; function CombinedMatcher () {
this.blacklist = new Matcher()
this.whitelist = new Matcher()
this.resultCache = createDict()
}
CombinedMatcher.maxCacheEntries = 1000
CombinedMatcher.prototype = { CombinedMatcher.prototype = {
blacklist: null, blacklist: null,
whitelist: null, whitelist: null,
resultCache: null, resultCache: null,
cacheEntries: 0, cacheEntries: 0,
clear: function () { clear: function () {
this.blacklist.clear(); this.blacklist.clear()
this.whitelist.clear(); this.whitelist.clear()
this.resultCache = createDict(); this.resultCache = createDict()
this.cacheEntries = 0; this.cacheEntries = 0
}, },
add: function (filter) { add: function (filter) {
if (filter instanceof WhitelistFilter) { if (filter instanceof WhitelistFilter) {
this.whitelist.add(filter); this.whitelist.add(filter)
} else { } else {
this.blacklist.add(filter); this.blacklist.add(filter)
} }
if (this.cacheEntries > 0) { if (this.cacheEntries > 0) {
this.resultCache = createDict(); this.resultCache = createDict()
this.cacheEntries = 0; this.cacheEntries = 0
} }
}, },
remove: function (filter) { remove: function (filter) {
if (filter instanceof WhitelistFilter) { if (filter instanceof WhitelistFilter) {
this.whitelist.remove(filter); this.whitelist.remove(filter)
} else { } else {
this.blacklist.remove(filter); this.blacklist.remove(filter)
} }
if (this.cacheEntries > 0) { if (this.cacheEntries > 0) {
this.resultCache = createDict(); this.resultCache = createDict()
this.cacheEntries = 0; this.cacheEntries = 0
} }
}, },
findKeyword: function (filter) { findKeyword: function (filter) {
if (filter instanceof WhitelistFilter) { if (filter instanceof WhitelistFilter) {
return this.whitelist.findKeyword(filter); return this.whitelist.findKeyword(filter)
} else { } else {
return this.blacklist.findKeyword(filter); return this.blacklist.findKeyword(filter)
} }
}, },
hasFilter: function (filter) { hasFilter: function (filter) {
if (filter instanceof WhitelistFilter) { if (filter instanceof WhitelistFilter) {
return this.whitelist.hasFilter(filter); return this.whitelist.hasFilter(filter)
} else { } else {
return this.blacklist.hasFilter(filter); return this.blacklist.hasFilter(filter)
} }
}, },
getKeywordForFilter: function (filter) { getKeywordForFilter: function (filter) {
if (filter instanceof WhitelistFilter) { if (filter instanceof WhitelistFilter) {
return this.whitelist.getKeywordForFilter(filter); return this.whitelist.getKeywordForFilter(filter)
} else { } else {
return this.blacklist.getKeywordForFilter(filter); return this.blacklist.getKeywordForFilter(filter)
} }
}, },
isSlowFilter: function (filter) { /*isSlowFilter: function (filter) {
var matcher = filter instanceof WhitelistFilter ? this.whitelist : this.blacklist; const matcher = filter instanceof WhitelistFilter ? this.whitelist : this.blacklist
if (matcher.hasFilter(filter)) { if (matcher.hasFilter(filter)) {
return !matcher.getKeywordForFilter(filter); return !matcher.getKeywordForFilter(filter)
} else { } else {
return !matcher.findKeyword(filter); return !matcher.findKeyword(filter)
} }
}, },*/
matchesAnyInternal: function (location, contentType, docDomain, thirdParty, sitekey) { matchesAnyInternal: function (location, contentType, docDomain, thirdParty, siteKey) {
var candidates = location.toLowerCase().match(/[a-z0-9%]{3,}/g); let candidates = location.toLowerCase().match(/[a-z0-9%]{3,}/g)
if (candidates === null) { if (candidates === null) {
candidates = []; candidates = []
} }
candidates.push(""); candidates.push("")
var blacklistHit = null; let blacklistHit = null
for (var i = 0, l = candidates.length; i < l; i++) { for (let i = 0, l = candidates.length; i < l; i++) {
var substr = candidates[i]; const substr = candidates[i]
if (substr in this.whitelist.filterByKeyword) { if (substr in this.whitelist.filterByKeyword) {
var result = this.whitelist._checkEntryMatch(substr, location, contentType, docDomain, thirdParty, sitekey); const result = this.whitelist._checkEntryMatch(substr, location, contentType, docDomain, thirdParty, siteKey)
if (result) { if (result) {
return result; return result
} }
} }
if (substr in this.blacklist.filterByKeyword && blacklistHit === null) { if (substr in this.blacklist.filterByKeyword && blacklistHit === null) {
blacklistHit = this.blacklist._checkEntryMatch(substr, location, contentType, docDomain, thirdParty, sitekey); blacklistHit = this.blacklist._checkEntryMatch(substr, location, contentType, docDomain, thirdParty, siteKey)
} }
} }
return blacklistHit; return blacklistHit
}, },
matchesAny: function (location, docDomain) { matchesAny: function (location, docDomain) {
var key = location + " " + docDomain + " "; const key = location + " " + docDomain + " "
if (key in this.resultCache) { if (key in this.resultCache) {
return this.resultCache[key]; return this.resultCache[key]
} }
var result = this.matchesAnyInternal(location, 0, docDomain, null, null); const result = this.matchesAnyInternal(location, 0, docDomain, null, null)
if (this.cacheEntries >= CombinedMatcher.maxCacheEntries) { if (this.cacheEntries >= CombinedMatcher.maxCacheEntries) {
this.resultCache = createDict(); this.resultCache = createDict()
this.cacheEntries = 0; this.cacheEntries = 0
} }
this.resultCache[key] = result; this.resultCache[key] = result
this.cacheEntries++; this.cacheEntries++
return result; return result
} }
};
var userrulesMatcher = new CombinedMatcher();
var defaultMatcher = new CombinedMatcher();
var direct = 'DIRECT;';
for (var i = 0; i < userrules.length; i++) {
userrulesMatcher.add(Filter.fromText(userrules[i]));
} }
for (var i = 0; i < rules.length; i++) { const userrulesMatcher = new CombinedMatcher()
defaultMatcher.add(Filter.fromText(rules[i])); const defaultMatcher = new CombinedMatcher()
const direct = 'DIRECT;'
for (let i = 0; i < rules.length; i++) {
defaultMatcher.add(Filter.fromText(rules[i]))
} }
function FindProxyForURL(url, host) { function FindProxyForURL (url, host) {
if (userrulesMatcher.matchesAny(url, host) instanceof BlockingFilter) { let matchedResult = userrulesMatcher.matchesAny(url, host)
return proxy; if (matchedResult instanceof BlockingFilter) {
} return proxy
if (userrulesMatcher.matchesAny(url, host) instanceof WhitelistFilter) { } else if (matchedResult instanceof WhitelistFilter) {
return direct; return direct
} }
// Hack for Geosite, it provides a whitelist... // Hack for Geosite, it provides a whitelist...
if (defaultMatcher.matchesAny(url, host) instanceof WhitelistFilter) { matchedResult = defaultMatcher.matchesAny(url, host)
return direct; if (matchedResult instanceof BlockingFilter) {
return proxy
} else if (matchedResult instanceof WhitelistFilter) {
return direct
} }
if (defaultMatcher.matchesAny(url, host) instanceof BlockingFilter) {
return proxy; return direct
}
return direct;
} }
return { return {
FindProxyForURL, FindProxyForURL
} }
} }