scriptStorage = window.opera.scriptStorage;String.prototype.trim = function () {return this.replace(/^\s\s*/, '').replace(/\s\s*$/, '');};String.prototype.stripSeparations = function () {return this.replace(/\s/g, '').replace(/\0/g, '');};String.prototype.chunk = function(n) {if (typeof n=='undefined') n=2;return this.match(RegExp('.{1,'+n+'}','g'));};function isArray(o) {return Object.prototype.toString.call(o) === '[object Array]'; }function randomID(){const length = 30 + Math.floor(Math.random() * 11);const chars = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ_1234567890";var generated = chars.charAt(Math.floor(Math.random() * 53)); for(var x=0;x<length;x++)generated += chars.charAt(Math.floor(Math.random() * 63));return generated;}function parseUri (str) {var o = parseUri.options,m = o.parser[o.strictMode ? "strict" : "loose"].exec(str),uri = {},i = 14;while (i--) uri[o.key[i]] = m[i] || "";uri[o.q.name] = {};uri[o.key[12]].replace(o.q.parser, function ($0, $1, $2) {if ($1) uri[o.q.name][$1] = $2;});return uri;};parseUri.options = {strictMode: false,key: ["source","protocol","authority","userInfo","user","password","host","port","relative","path","directory","file","query","anchor"],q:{name: "queryKey",parser: /(?:^|&)([^&=]*)=?([^&]*)/g},parser: {strict: /^(?:([^:\/?#]+):)?(?:\/\/((?:(([^:@]*)(?::([^:@]*))?)?@)?([^:\/?#]*)(?::(\d*))?))?((((?:[^?#\/]*\/)*)([^?#]*))(?:\?([^#]*))?(?:#(.*))?)/,loose: /^(?:(?![^:@]+:[^:@\/]*@)([^:\/?#.]+):)?(?:\/\/)?((?:(([^:@]*)(?::([^:@]*))?)?@)?([^:\/?#]*)(?::(\d*))?)(((\/(?:[^?#](?![^?#\/]*\.[^?#\/.]+(?:[?#]|$)))*\/?)?([^?#\/]*))(?:\?([^#]*))?(?:#(.*))?)/}};const reKnownTLDs = /^(asia|biz|cat|coop|edu|info|eu.int|int|gov|jobs|mil|mobi|name|tel|travel|aaa.pro|aca.pro|acct.pro|avocat.pro|bar.pro|cpa.pro|jur.pro|law.pro|med.pro|eng.pro|pro|ar.com|br.com|cn.com|de.com|eu.com|gb.com|hu.com|jpn.com|kr.com|no.com|qc.com|ru.com|sa.com|se.com|uk.com|us.com|uy.com|za.com|com|ab.ca|bc.ca|mb.ca|nb.ca|nf.ca|nl.ca|ns.ca|nt.ca|nu.ca|on.ca|pe.ca|qc.ca|sk.ca|yk.ca|gc.ca|ca|gb.net|se.net|uk.net|za.net|net|ae.org|za.org|org|[^\.\/]+\.uk|act.edu.au|nsw.edu.au|nt.edu.au|qld.edu.au|sa.edu.au|tas.edu.au|vic.edu.au|wa.edu.au|act.gov.au|nt.gov.au|qld.gov.au|sa.gov.au|tas.gov.au|vic.gov.au|wa.gov.au|[^\.\/]+\.au|de|dk|tv|com.ly|net.ly|gov.ly|plc.ly|edu.ly|sch.ly|med.ly|org.ly|id.ly|ly|xn--55qx5d.hk|xn--wcvs22d.hk|xn--lcvr32d.hk|xn--mxtq1m.hk|xn--gmqw5a.hk|xn--ciqpn.hk|xn--gmq050i.hk|xn--zf0avx.hk|xn--io0a7i.hk|xn--mk0axi.hk|xn--od0alg.hk|xn--od0aq3b.hk|xn--tn0ag.hk|xn--uc0atv.hk|xn--uc0ay4a.hk|com.hk|edu.hk|gov.hk|idv.hk|net.hk|org.hk|hk|ac.cn|com.cn|edu.cn|gov.cn|net.cn|org.cn|mil.cn|xn--55qx5d.cn|xn--io0a7i.cn|xn--od0alg.cn|ah.cn|bj.cn|cq.cn|fj.cn|gd.cn|gs.cn|gz.cn|gx.cn|ha.cn|hb.cn|he.cn|hi.cn|hl.cn|hn.cn|jl.cn|js.cn|jx.cn|ln.cn|nm.cn|nx.cn|qh.cn|sc.cn|sd.cn|sh.cn|sn.cn|sx.cn|tj.cn|xj.cn|xz.cn|yn.cn|zj.cn|hk.cn|mo.cn|tw.cn|cn|edu.tw|gov.tw|mil.tw|com.tw|net.tw|org.tw|idv.tw|game.tw|ebiz.tw|club.tw|xn--zf0ao64a.tw|xn--uc0atv.tw|xn--czrw28b.tw|tw|aichi.jp|akita.jp|aomori.jp|chiba.jp|ehime.jp|fukui.jp|fukuoka.jp|fukushima.jp|gifu.jp|gunma.jp|hiroshima.jp|hokkaido.jp|hyogo.jp|ibaraki.jp|ishikawa.jp|iwate.jp|kagawa.jp|kagoshima.jp|kanagawa.jp|kawasaki.jp|kitakyushu.jp|kobe.jp|kochi.jp|kumamoto.jp|kyoto.jp|mie.jp|miyagi.jp|miyazaki.jp|nagano.jp|nagasaki.jp|nagoya.jp|nara.jp|niigata.jp|oita.jp|okayama.jp|okinawa.jp|osaka.jp|saga.jp|saitama.jp|sapporo.jp|sendai.jp|shiga.jp|shimane.jp|shizuoka.jp|tochigi.jp|tokushima.jp|tokyo.jp|tottori.jp|toyama.jp|wakayama.jp|yamagata.jp|yamaguchi.jp|yamanashi.jp|yokohama.jp|ac.jp|ad.jp|co.jp|ed.jp|go.jp|gr.jp|lg.jp|ne.jp|or.jp|jp|co.in|firm.in|net.in|org.in|gen.in|ind.in|nic.in|ac.in|edu.in|res.in|gov.in|mil.in|in)$/i;const reKnownUrlwTLD = /([^\.\/]+\.(asia|biz|cat|coop|edu|info|eu.int|int|gov|jobs|mil|mobi|name|tel|travel|aaa.pro|aca.pro|acct.pro|avocat.pro|bar.pro|cpa.pro|jur.pro|law.pro|med.pro|eng.pro|pro|ar.com|br.com|cn.com|de.com|eu.com|gb.com|hu.com|jpn.com|kr.com|no.com|qc.com|ru.com|sa.com|se.com|uk.com|us.com|uy.com|za.com|com|ab.ca|bc.ca|mb.ca|nb.ca|nf.ca|nl.ca|ns.ca|nt.ca|nu.ca|on.ca|pe.ca|qc.ca|sk.ca|yk.ca|gc.ca|ca|gb.net|se.net|uk.net|za.net|net|ae.org|za.org|org|[^\.\/]+\.uk|act.edu.au|nsw.edu.au|nt.edu.au|qld.edu.au|sa.edu.au|tas.edu.au|vic.edu.au|wa.edu.au|act.gov.au|nt.gov.au|qld.gov.au|sa.gov.au|tas.gov.au|vic.gov.au|wa.gov.au|[^\.\/]+\.au|de|dk|tv|com.ly|net.ly|gov.ly|plc.ly|edu.ly|sch.ly|med.ly|org.ly|id.ly|ly|xn--55qx5d.hk|xn--wcvs22d.hk|xn--lcvr32d.hk|xn--mxtq1m.hk|xn--gmqw5a.hk|xn--ciqpn.hk|xn--gmq050i.hk|xn--zf0avx.hk|xn--io0a7i.hk|xn--mk0axi.hk|xn--od0alg.hk|xn--od0aq3b.hk|xn--tn0ag.hk|xn--uc0atv.hk|xn--uc0ay4a.hk|com.hk|edu.hk|gov.hk|idv.hk|net.hk|org.hk|hk|ac.cn|com.cn|edu.cn|gov.cn|net.cn|org.cn|mil.cn|xn--55qx5d.cn|xn--io0a7i.cn|xn--od0alg.cn|ah.cn|bj.cn|cq.cn|fj.cn|gd.cn|gs.cn|gz.cn|gx.cn|ha.cn|hb.cn|he.cn|hi.cn|hl.cn|hn.cn|jl.cn|js.cn|jx.cn|ln.cn|nm.cn|nx.cn|qh.cn|sc.cn|sd.cn|sh.cn|sn.cn|sx.cn|tj.cn|xj.cn|xz.cn|yn.cn|zj.cn|hk.cn|mo.cn|tw.cn|cn|edu.tw|gov.tw|mil.tw|com.tw|net.tw|org.tw|idv.tw|game.tw|ebiz.tw|club.tw|xn--zf0ao64a.tw|xn--uc0atv.tw|xn--czrw28b.tw|tw|aichi.jp|akita.jp|aomori.jp|chiba.jp|ehime.jp|fukui.jp|fukuoka.jp|fukushima.jp|gifu.jp|gunma.jp|hiroshima.jp|hokkaido.jp|hyogo.jp|ibaraki.jp|ishikawa.jp|iwate.jp|kagawa.jp|kagoshima.jp|kanagawa.jp|kawasaki.jp|kitakyushu.jp|kobe.jp|kochi.jp|kumamoto.jp|kyoto.jp|mie.jp|miyagi.jp|miyazaki.jp|nagano.jp|nagasaki.jp|nagoya.jp|nara.jp|niigata.jp|oita.jp|okayama.jp|okinawa.jp|osaka.jp|saga.jp|saitama.jp|sapporo.jp|sendai.jp|shiga.jp|shimane.jp|shizuoka.jp|tochigi.jp|tokushima.jp|tokyo.jp|tottori.jp|toyama.jp|wakayama.jp|yamagata.jp|yamaguchi.jp|yamanashi.jp|yokohama.jp|ac.jp|ad.jp|co.jp|ed.jp|go.jp|gr.jp|lg.jp|ne.jp|or.jp|jp|co.in|firm.in|net.in|org.in|gen.in|ind.in|nic.in|ac.in|edu.in|res.in|gov.in|mil.in|in))($|\/|:){1}/i;const reIPv6 =/^\s*((([0-9A-Fa-f]{1,4}:){7}([0-9A-Fa-f]{1,4}|:))|(([0-9A-Fa-f]{1,4}:){6}(:[0-9A-Fa-f]{1,4}|((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3})|:))|(([0-9A-Fa-f]{1,4}:){5}(((:[0-9A-Fa-f]{1,4}){1,2})|:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3})|:))|(([0-9A-Fa-f]{1,4}:){4}(((:[0-9A-Fa-f]{1,4}){1,3})|((:[0-9A-Fa-f]{1,4})?:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:))|(([0-9A-Fa-f]{1,4}:){3}(((:[0-9A-Fa-f]{1,4}){1,4})|((:[0-9A-Fa-f]{1,4}){0,2}:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:))|(([0-9A-Fa-f]{1,4}:){2}(((:[0-9A-Fa-f]{1,4}){1,5})|((:[0-9A-Fa-f]{1,4}){0,3}:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:))|(([0-9A-Fa-f]{1,4}:){1}(((:[0-9A-Fa-f]{1,4}){1,6})|((:[0-9A-Fa-f]{1,4}){0,4}:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:))|(:(((:[0-9A-Fa-f]{1,4}){1,7})|((:[0-9A-Fa-f]{1,4}){0,5}:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:)))(%.+)?\s*$/;const endsWithNums = /\.[0-9]+([^\.\/]+)*$/i;const reInvalidCharsIPv4 = /^[\.\/]|[\+\^\?\|\*\{\}\$\s\0\<\>\[\]\/\\%&=;:!#~`,'"]|\.\.|[\/]$/i; const reInvalidCharsIPv6 = /^[\.\/]|[\+\^\?\|\*\{\}\$\s\0\<\>\[\]\/\\%&=;!#~`,'"]|\.\.|[\/]$/i;const reStartWProtocol = /^[^\.\/:]+:\/\//i;const reFileLocalhost = /^file:\/\/\//i;var punycode = new function Punycode() {this.utf16 = {decode:function(input){var output = [], i=0, len=input.length,value,extra;while (i < len) {value = input.charCodeAt(i++);if ((value & 0xF800) === 0xD800) {extra = input.charCodeAt(i++);if ( ((value & 0xFC00) !== 0xD800) || ((extra & 0xFC00) !== 0xDC00) ) {throw new RangeError("UTF-16(decode): Illegal UTF-16 sequence");}value = ((value & 0x3FF) << 10) + (extra & 0x3FF) + 0x10000;}output.push(value);}return output;},encode:function(input){var output = [], i=0, len=input.length,value;while (i < len) {value = input[i++];if ( (value & 0xF800) === 0xD800 ) {throw new RangeError("UTF-16(encode): Illegal UTF-16 value");}if (value > 0xFFFF) {value -= 0x10000;output.push(String.fromCharCode(((value >>>10) & 0x3FF) | 0xD800));value = 0xDC00 | (value & 0x3FF);}output.push(String.fromCharCode(value));}return output.join("");}}var initial_n = 0x80;var initial_bias = 72;var delimiter = "\x2D";var base = 36;var damp = 700;var tmin=1;var tmax=26;var skew=38;var maxint = 0x7FFFFFFF;function decode_digit(cp) {return cp - 48 < 10 ? cp - 22 : cp - 65 < 26 ? cp - 65 : cp - 97 < 26 ? cp - 97 : base;}function encode_digit(d, flag) {return d + 22 + 75 * (d < 26) - ((flag != 0) << 5);}function adapt(delta, numpoints, firsttime ) {
var k;
delta = firsttime ? Math.floor(delta / damp) : (delta >> 1);
delta += Math.floor(delta / numpoints);
for (k = 0; delta > (((base - tmin) * tmax) >> 1); k += base) {
delta = Math.floor(delta / ( base - tmin ));
}
return Math.floor(k + (base - tmin + 1) * delta / (delta + skew));
}
// encode_basic(bcp,flag) forces a basic code point to lowercase if flag is zero,
// uppercase if flag is nonzero, and returns the resulting code point.
// The code point is unchanged if it is caseless.
// The behavior is undefined if bcp is not a basic code point.
function encode_basic(bcp, flag) {
bcp -= (bcp - 97 < 26) << 5;
return bcp + ((!flag && (bcp - 65 < 26)) << 5);
}
// Main decode
this.decode=function(input,preserveCase) {
// Dont use utf16
var output=[];
var case_flags=[];
var input_length = input.length;
var n, out, i, bias, basic, j, ic, oldi, w, k, digit, t, len;
// Initialize the state:
n = initial_n;
i = 0;
bias = initial_bias;
// Handle the basic code points: Let basic be the number of input code
// points before the last delimiter, or 0 if there is none, then
// copy the first basic code points to the output.
basic = input.lastIndexOf(delimiter);
if (basic < 0) basic = 0;
for (j = 0; j < basic; ++j) {
if(preserveCase) case_flags[output.length] = ( input.charCodeAt(j) -65 < 26);
if ( input.charCodeAt(j) >= 0x80) {
throw new RangeError("Illegal input >= 0x80");
}
output.push( input.charCodeAt(j) );
}
// Main decoding loop: Start just after the last delimiter if any
// basic code points were copied; start at the beginning otherwise.
for (ic = basic > 0 ? basic + 1 : 0; ic < input_length; ) {
// ic is the index of the next character to be consumed,
// Decode a generalized variable-length integer into delta,
// which gets added to i. The overflow checking is easier
// if we increase i as we go, then subtract off its starting
// value at the end to obtain delta.
for (oldi = i, w = 1, k = base; ; k += base) {
if (ic >= input_length) {
throw RangeError ("punycode_bad_input(1)");
}
digit = decode_digit(input.charCodeAt(ic++));
if (digit >= base) {
throw RangeError("punycode_bad_input(2)");
}
if (digit > Math.floor((maxint - i) / w)) {
throw RangeError ("punycode_overflow(1)");
}
i += digit * w;
t = k <= bias ? tmin : k >= bias + tmax ? tmax : k - bias;
if (digit < t) { break; }
if (w > Math.floor(maxint / (base - t))) {
throw RangeError("punycode_overflow(2)");
}
w *= (base - t);
}
out = output.length + 1;
bias = adapt(i - oldi, out, oldi === 0);
// i was supposed to wrap around from out to 0,
// incrementing n each time, so we'll fix that now:
if ( Math.floor(i / out) > maxint - n) {
throw RangeError("punycode_overflow(3)");
}
n += Math.floor( i / out ) ;
i %= out;
// Insert n at position i of the output:
// Case of last character determines uppercase flag:
if (preserveCase) { case_flags.splice(i, 0, input.charCodeAt(ic -1) -65 < 26);}
output.splice(i, 0, n);
i++;
}
if (preserveCase) {
for (i = 0, len = output.length; i < len; i++) {
if (case_flags[i]) {
output[i] = (String.fromCharCode(output[i]).toUpperCase()).charCodeAt(0);
}
}
}
return this.utf16.encode(output);
};
//** Main encode function **
this.encode = function (input,preserveCase) {
//** Bias adaptation function **
var n, delta, h, b, bias, j, m, q, k, t, ijv, case_flags;
if (preserveCase) {
// Preserve case, step1 of 2: Get a list of the unaltered string
case_flags = this.utf16.decode(input);
}
// Converts the input in UTF-16 to Unicode
input = this.utf16.decode(input.toLowerCase());
var input_length = input.length; // Cache the length
if (preserveCase) {
// Preserve case, step2 of 2: Modify the list to true/false
for (j=0; j < input_length; j++) {
case_flags[j] = input[j] != case_flags[j];
}
}
var output=[];
// Initialize the state:
n = initial_n;
delta = 0;
bias = initial_bias;
// Handle the basic code points:
for (j = 0; j < input_length; ++j) {
if ( input[j] < 0x80) {
output.push(
String.fromCharCode(
case_flags ? encode_basic(input[j], case_flags[j]) : input[j]
)
);
}
}
h = b = output.length;
// h is the number of code points that have been handled, b is the
// number of basic code points
if (b > 0) output.push(delimiter);
// Main encoding loop:
//
while (h < input_length) {
// All non-basic code points < n have been
// handled already. Find the next larger one:
for (m = maxint, j = 0; j < input_length; ++j) {
ijv = input[j];
if (ijv >= n && ijv < m) m = ijv;
}
// Increase delta enough to advance the decoder's
// <n,i> state to <m,0>, but guard against overflow:
if (m - n > Math.floor((maxint - delta) / (h + 1))) {
throw RangeError("punycode_overflow (1)");
}
delta += (m - n) * (h + 1);
n = m;
for (j = 0; j < input_length; ++j) {
ijv = input[j];
if (ijv < n ) {
if (++delta > maxint) return Error("punycode_overflow(2)");
}
if (ijv == n) {
// Represent delta as a generalized variable-length integer:
for (q = delta, k = base; ; k += base) {
t = k <= bias ? tmin : k >= bias + tmax ? tmax : k - bias;
if (q < t) break;
output.push( String.fromCharCode(encode_digit(t + (q - t) % (base - t), 0)) );
q = Math.floor( (q - t) / (base - t) );
}
output.push( String.fromCharCode(encode_digit(q, preserveCase && case_flags[j] ? 1:0 )));
bias = adapt(delta, h + 1, h == b);
delta = 0;
++h;
}
}
++delta, ++n;
}
return output.join("");
}
this.ToASCII = function ( domain ) {
var domain_array = domain.split(".");
var out = [];
for (var i=0; i < domain_array.length; ++i) {
var s = domain_array[i];
out.push(
s.match(/[^A-Za-z0-9-]/) ?
"xn--" + punycode.encode(s) :
s
);
}
return out.join(".");
}
this.ToUnicode = function ( domain ) {
var domain_array = domain.split(".");
var out = [];
for (var i=0; i < domain_array.length; ++i) {
var s = domain_array[i];
out.push(
s.match(/^xn--/) ?
punycode.decode(s.slice(4)) :
s
);
}
return out.join(".");
}
}();
/*
Example for http://maps.google.com/something.html or maps.google.com, this returns google.com.
If it cannot match google.com as a known valid primary domain, it will return maps.google.com.
http://en.wikipedia.org/wiki/URI_scheme#Generic_syntax
// http://en.wikipedia.org/wiki/IPv4
// http://en.wikipedia.org/wiki/IPv6
// http://en.wikipedia.org/wiki/IPv6_address
// http://en.wikipedia.org/wiki/Localhost
// http://en.wikipedia.org/wiki/File_URI_scheme
// http://en.wikipedia.org/wiki/Hosts_(file)
// Contains support for localhost style names; hex, decimal, and octal forms of IPv4;
Examples of IPv6 in a URL (The IPv6 must be surrounded by square brackets in a valid URL, )
http://en.wikipedia.org/wiki/IPv6_address#Literal_IPv6_addresses_in_Network_Resource_Identifiers
http://[2001:0db8:85a3:08d3:1319:8a2e:0370:7348]/
https://[2001:0db8:85a3:08d3:1319:8a2e:0370:7348]:443/
Note: The expected input for currURL is a full URL with a leading protocol.
*/
const RECOGNIZE_IPV6 = false;
function getPrimaryDomain(currURL)
{
// Sometimes websites create empty elements (empty src) and then change the src which fires another load event
// This ensures that the empty element gets created so that the second event will fire for verification
if (!currURL || !currURL.trim())
{
if (window.location.href)
return getPrimaryDomain(window.location.href);
else
return null;
}
// Opera does not provide punycode urls automatically like Google Chrome does, important distinction since we have to convert it ourselves
try
{
currURL = decodeURI(currURL).toLowerCase().trim();
}
catch(err)
{
try
{
currURL = unescape(currURL).toLowerCase().trim();
}
catch(err2)
{
try
{
currURL = currURL.toLowerCase().trim();
}
catch(err3)
{
return null;
}
}
}
//opera.extension.postMessage({"type": "Log Message", "msg": "PrimaryDomain 1 " + currURL});
if (reFileLocalhost.test(currURL))
currURL = "localhost";
else
{
var removeExtra = currURL.match(/^([^\.\/:]+:\/\/)*([^\/])+(\/|:|$)/i);
if (removeExtra && removeExtra.length > 0)
currURL = removeExtra[0];
else
return null;
}
// We have IPv6 here but I'm going to turn it off until IPv6 is more widespread and
// more people are familiar with it.
if (RECOGNIZE_IPV6)
{
// Try to parse currURL as an IPv6 address first
var splitIPv6 = currURL.match(/^([^\.\/:]+:\/\/)*([^\/:]+:[^\/:]+@)?\[([a-z0-9:\.]+)(\/[0-9]+)?\]/i);
if (splitIPv6 && splitIPv6.length > 3 && reIPv6.test(splitIPv6[3]))
{
if (reInvalidCharsIPv6.test(splitIPv6[3]))
return null;
else
return encodeURI(splitIPv6[3]);
}
}
var parsedUri = parseUri(currURL);
var parsedProtocol = parsedUri["protocol"];
currURL = parsedUri["host"];
if (!currURL || (parsedProtocol && reInvalidCharsIPv4.test(parsedProtocol)))
return null;
var knownForms = currURL.match(reKnownUrlwTLD);
if (knownForms && knownForms.length > 1)
{
if (reInvalidCharsIPv4.test(knownForms[1]))
return null;
else
{
try
{
//opera.extension.postMessage({"type": "Log Message", "msg": "PrimaryDomain 2 " + encodeURI(punycode.ToASCII(knownForms[1]))});
return encodeURI(punycode.ToASCII(knownForms[1]));
}
catch (err)
{
return null;
}
}
}
else
{
// Need to add check for 3 dots in IPv4 addresses and reject if they are not there, such as 127.0.0.1
// To prevent someone from trying to trick a user into whitelisting something like 2.235
// Must also consider the hex and octal forms
var urlRemovedWWW = currURL.match(/^www\.([^\.]+\.[^\/]+)/i);
if (urlRemovedWWW && urlRemovedWWW.length > 1)
{
// Filters out the common www. in a text style url
if (isInvalidDomain(urlRemovedWWW[1]) || endsWithNums.test(urlRemovedWWW[1]))
return null;
else
{
try
{
//opera.extension.postMessage({"type": "Log Message", "msg": "PrimaryDomain 3 " + encodeURI(punycode.ToASCII(urlRemovedWWW[1]))});
return encodeURI(punycode.ToASCII(urlRemovedWWW[1]));
}
catch (err)
{
return null;
}
}
}
else
{
// Some checking to see if the primary domain contains invalid characters or is a known TLD
if (isInvalidDomain(currURL))
return null;
else
{
try
{
//opera.extension.postMessage({"type": "Log Message", "msg": "PrimaryDomain 4 " + encodeURI(punycode.ToASCII(currURL))});
return encodeURI(punycode.ToASCII(currURL));
}
catch (err)
{
return null;
}
}
}
}
}
function isInvalidDomain(currURL)
{
return (currURL < 4 || reInvalidCharsIPv4.test(currURL) || reKnownTLDs.test(currURL));
}
/*
Used to determine if a url matches a urlPattern.
url: URL to be tested. This ***MUST*** have come from the output of getPrimaryDomain(..).
urlPattern: The pattern to be matched. This is highly recommended to have been generated by getPrimaryDomain(..)
but it can also be user supplied from the whitelist page.
*/
const reSeparators = /[\.:]/i;
function patternMatches(url, urlPattern)
{
var coreUrl = url;
if (!coreUrl || !urlPattern)
return false;
coreUrl = coreUrl.toLowerCase();
urlPattern = urlPattern.toLowerCase();
// Ensure that we are not matching a "localhost" type name with something like "example.localhost"
if (reSeparators.test(coreUrl) !== reSeparators.test(urlPattern))
return false;
// Check to see if the url or urlPattern ends with .ddd (digits or hex).
// If so, we ONLY want an exact match since these are IPv4 addresses.
if (endsWithNums.test(coreUrl) || endsWithNums.test(urlPattern))
{
return (coreUrl === urlPattern);
}
var endsMatch = false;
var matchedIndex = coreUrl.indexOf(urlPattern);
if (matchedIndex >= 0 && (matchedIndex + urlPattern.length) === coreUrl.length)
endsMatch = true;
if (!endsMatch)
{
matchedIndex = urlPattern.indexOf(coreUrl);
if (matchedIndex >= 0 && (matchedIndex + coreUrl.length) === urlPattern.length)
endsMatch = true;
}
if (!endsMatch)
return false;
if (coreUrl.length === urlPattern.length)
return true;
// Check to see that we have a valid separator character where they differ
if ((coreUrl.length > urlPattern.length && reSeparators.test(coreUrl.charAt(coreUrl.length - urlPattern.length - 1)))
|| (urlPattern.length > coreUrl.length && reSeparators.test(urlPattern.charAt(urlPattern.length - coreUrl.length - 1))) )
return true;
return false;
}
function islisted(list, url) {
return (findUrlPatternIndex(list, url) >= 0);
}
/*
Searches a sorted IPv4, IPv6, and text url list with a binary-search like algorithm for efficient scaling.
*/
function findUrlPatternIndex(theArray, key)
{
if (!key || !theArray)
return -1;
var splitFindVals = key.split('.');
var bestInsertionIndex = -1;
if (splitFindVals.length > 1)
{
// See if there is an exact match
var foundIndex = urlBSearch(theArray, key, compareWSeparators);
bestInsertionIndex = foundIndex;
if (foundIndex >= 0)
return foundIndex;
// Otherwise, see if the end segments match
foundIndex = urlBSearch(theArray, key, compareWSeparatorsLoose);
if (foundIndex >= 0)
return foundIndex;
/*
// See if there is an exact match
{
var foundIndex = urlBSearch(theArray, key, compareWSeparators);
bestInsertionIndex = foundIndex;
if (foundIndex >= 0)
return foundIndex;
}
// If no exact match, find the best matching one
for (var i = 0; i < splitFindVals.length; i++)
{
var currSearch = splitFindVals.slice(-(splitFindVals.length - i)).join(".");
var foundIndex = urlBSearch(theArray, currSearch, compareWSeparatorsLoose);
if (foundIndex >= 0)
{
if (patternMatches(theArray[foundIndex], key))
return foundIndex;
//else break; // Is this break valid for this algorithm?
}
}
*/
}
else
{
// Exact match for "localhost" type domains with no separators (ie: TLDs)
var foundIndex = urlBSearch(theArray, key, compareNoSeparators);
bestInsertionIndex = foundIndex;
if (foundIndex >= 0)
return foundIndex;
}
// Return value of -1 means that we couldn't even find a best insertion index
// Otherwise, abs(return value + 2) gives the best insertion index to maintain sorted order
return (bestInsertionIndex && bestInsertionIndex < 0) ? bestInsertionIndex - 1 : -1;
}
function urlBSearch(theArray, key, compare) {
var left = 0;
var right = theArray.length - 1;
while (left <= right) {
var mid = left + Math.floor((right - left) / 2);
var cmp = compare(key, theArray[mid]);
if (cmp < 0)
right = mid - 1;
else if (cmp > 0)
left = mid + 1;
else
return mid;
}
return -(left + 1);
}
function compareWSeparators(a, b) {
a = a.split('.').reverse();
b = b.split('.').reverse();
for (var i = 0; i < a.length && i < b.length; i++)
{
if (a[i] < b[i])
return -1;
else if (a[i] > b[i])
return 1;
}
if (a.length == b.length)
return 0;
else if (a.length < b.length)
return -1;
else
return 1;
}
function compareWSeparatorsLoose(a, b) {
var oA = a;
var oB = b;
a = a.split('.').reverse();
b = b.split('.').reverse();
for (var i = 0; i < a.length && i < b.length; i++)
{
if (a[i] < b[i])
return -1;
else if (a[i] > b[i])
return 1;
}
return patternMatches(oA, oB) ? 0 : -1;
}
function compareNoSeparators(a, b) {
a = a.split('.').reverse();
b = b.split('.').reverse();
if (a.length == 1 && b.length == 1)
{
if (a[0] < b[0])
return -1;
else if (a[0] > b[0])
return 1;
return 0;
}
for (var i = 0; i < a.length && i < b.length; i++)
{
if (a[i] < b[i])
return -1;
else if (a[i] > b[i])
return 1;
}
return -1;
}
/*
In place sort of urls. Returns true if successful, false if there was an error.
If false, you must reload the data in theArray since it is passed by reference.
*/
function sortUrlList(theArray)
{
if (!isArray(theArray))
return false;
try
{
for(var h in theArray)
{
theArray[h] = theArray[h].split('.').reverse();
}
theArray.sort();
for(var h in theArray)
{
theArray[h] = theArray[h].reverse().join(".");
}
return true;
}
catch(err)
{
return false;
}
}
function relativeToAbsoluteUrl(url) {
if(!url)
return url;
if (reStartWProtocol.test(url))
return url;
// Leading / means absolute path
if(url[0] == '/')
return document.location.protocol + "//" + document.location.host + url;
// Remove filename and add relative URL to it
var base = document.baseURI.match(/.+\//);
if(!base) return document.baseURI + "/" + url;
return base[0] + url;
}
const EL_TYPE = {
"OTHER": 0,
"SCRIPT": 1,
"OBJECT": 2,
"EMBED": 3,
"IFRAME": 4,
"FRAME": 5,
/*
"AUDIO": 6,
"VIDEO": 7,
"IMG": 8,
"BODY": 9,
"CSS": 10
*/
};
function getElType(el) {
// Note: We cannot block java that uses the deprecated APPLET tags because it doesn't fire beforeload
//console.log("nodeName: " + el.nodeName);
switch (el.nodeName.toUpperCase())
{
case 'SCRIPT': return EL_TYPE.SCRIPT;
case 'OBJECT': return EL_TYPE.OBJECT;
case 'EMBED': return EL_TYPE.EMBED;
case 'IFRAME': return EL_TYPE.IFRAME;
case 'FRAME': return EL_TYPE.FRAME;
/*
case 'AUDIO': return EL_TYPE.AUDIO;
case 'VIDEO': return EL_TYPE.VIDEO;
case 'IMG': return EL_TYPE.IMG;
case 'LINK': return EL_TYPE.CSS;
case 'BODY': return EL_TYPE.BODY;
*/
default: return EL_TYPE.OTHER;
}
}
function getElUrl(el, type) {
//console.log("getElUrl: " + el.nodeName + " " + el.outerHTML);
switch (type)
{
case EL_TYPE.SCRIPT:
{
return el.src;
}
case EL_TYPE.EMBED:
{
// Does Google Chrome even use embeds?
var codeBase = window.location.href;
if (el.codeBase) codeBase = el.codeBase;
if (el.src)
{
if (reStartWProtocol.test(el.src))
return el.src;
else
return codeBase;
}
if (el.data)
{
if (reStartWProtocol.test(el.data))
return el.data;
else
return codeBase;
}
if (el.code)
{
if (reStartWProtocol.test(el.code))
return el.code;
else
return codeBase;
}
return window.location.href;
}
case EL_TYPE.IFRAME:
{
return el.src;
}
case EL_TYPE.FRAME:
{
return el.src;
}
case EL_TYPE.OBJECT:
{
var codeBase = window.location.href;
if (el.codeBase) codeBase = el.codeBase;
// If the data attribute is given, we know the source.
if (el.data)
{
if (reStartWProtocol.test(el.data))
return el.data;
else
return codeBase;
}
var plist = el.getElementsByTagName('param');
var codeSrc = null;
for(var i=0; i < plist.length; i++){
var paramName = plist[i].name.toLowerCase();
//console.log("Looking at param: " + plist[i].name + " " + plist[i].value);
if(paramName === 'movie' || paramName === 'src' || paramName === 'codebase' || paramName === 'data')
return plist[i].value;
else if (paramName === 'code' || paramName === 'url')
codeSrc = plist[i].value;
}
if (codeSrc)
return codeSrc;
else
return window.location.href;
}
/*
case EL_TYPE.AUDIO:
{
return window.location.href;
// We won't get a el.src if AUDIO uses the <source> tag
//return el.src;
}
case EL_TYPE.VIDEO:
{
return window.location.href;
// We won't get a el.src if VIDEO uses the <source> tag
//return el.src;
}
case EL_TYPE.IMG:
{
return el.src;
}
case EL_TYPE.CSS:
{
return el.href;
}
case EL_TYPE.BODY:
{
var bgImage = getComputedStyle(el,'').getPropertyValue('background-image');
if (bgImage && bgImage !== "none") return bgImage.replace(/"/g,"").replace(/url\(|\)$/ig, "");
else return null;
}
*/
default: return (el.src ? el.src : null);
}
}
var fatalError = false;
var config = {
has: function(key) {
try
{
return key in scriptStorage;
}
catch (err)
{
fatalError = true;
return null;
}
},
get: function(key) {
if (this.has(key)) {
try {
return JSON.parse(scriptStorage[key]);
} catch(err) {
return null;
}
}
else
return null;
},
set: function(key, value) {
try {
scriptStorage[key] = JSON.stringify(value);
} catch (err) {
fatalError = true;
}
},
defaults: function(vals) {
for (var key in vals) { // Opera specific
var currVal = this.get(key);
if (typeof currVal === 'undefined' || currVal === null)
this.set(key, vals[key]);
};
}
};
const BMODE_TYPES = {
"WHITELIST": 0,
"BLACKLIST": 1,
"WHITELIST_ALLOW_TOP_LEVEL": 2
}
config.defaults({
whitelist: ["google.com", "google.ca", "google.co.uk", "google.com.au", "googleapis.com", "gstatic.com", "gmodules.com", "youtube.com", "ytimg.com",
"live.com", "microsoft.com", "hotmail.com", "apple.com", "yahooapis.com", "yimg.com"],
blacklist: [],
tempAllowList: [],
globalAllowAll: false,
blocking_mode: BMODE_TYPES.WHITELIST,
reloadTabsOnToggle: true, // Not currently used in NotScripts for Opera, tabs reload by default
multiSelect: false,
lastVersion: 1001001000,
currVersion: 1001001000,
currDisplayVersion: "1.1.0"
});
var whitelist = config.get('whitelist');
var blacklist = config.get('blacklist');
var tempAllowList = config.get('tempAllowList');
var globalAllowAll = config.get('globalAllowAll');
var blocking_mode = config.get('blocking_mode');
if (config.get("currVersion") < 1001001000)
{
config.set("lastVersion", 1001001000);
config.set("currVersion", 1001001000);
config.set("currDisplayVersion", "1.1.0");
}
function firstSort()
{
if (!sortUrlList(whitelist)) // in place sort
{
whitelist = [];
config.set('whitelist', []);
}
else
{
removeDuplicatesInArray(whitelist);
config.set('whitelist', whitelist);
}
if (!sortUrlList(blacklist)) // in place sort
{
blacklist = [];
config.set('blacklist', []);
}
else
{
removeDuplicatesInArray(blacklist);
config.set('blacklist', blacklist);
}
if (!sortUrlList(tempAllowList)) // in place sort
{
tempAllowList = [];
config.set('tempAllowList', []);
}
else
{
removeDuplicatesInArray(tempAllowList);
config.set('tempAllowList', tempAllowList);
}
}
function clearSettings()
{
config.set("whitelist", []);
config.set("blacklist", []);
config.set("tempAllowList", []);
window.location.reload();
}
/*
Called by the drop down menu to toggle temporary permissions on and off.
*/
function toggleOnOff(newState) {
config.set('globalAllowAll', newState);
tempAllowList = [];
config.set('tempAllowList', tempAllowList);
}
function updateLists()
{
switch(blocking_mode)
{
case BMODE_TYPES.BLACKLIST:
{
config.set("blacklist", blacklist);
config.set("tempAllowList", tempAllowList);
break;
}
case BMODE_TYPES.WHITELIST_ALLOW_TOP_LEVEL:
{
config.set("whitelist", whitelist);
config.set("blacklist", blacklist);
config.set("tempAllowList", tempAllowList);
}
default: // BMODE_TYPES.WHITELIST
{
config.set("whitelist", whitelist);
config.set("tempAllowList", tempAllowList);
break;
}
}
}
function permitUrl(urls)
{
switch(blocking_mode)
{
case BMODE_TYPES.BLACKLIST:
{
for (var i = 0; i < urls.length; i++)
{
removeFromList(tempAllowList, "tempAllowList", urls[i], true);
removeFromList(blacklist, "blacklist", urls[i], false);
}
break;
}
case BMODE_TYPES.WHITELIST_ALLOW_TOP_LEVEL:
{
for (var i = 0; i < urls.length; i++)
{
removeFromList(tempAllowList, "tempAllowList", urls[i], true);
removeFromList(blacklist, "blacklist", urls[i], false);
addToList(whitelist, "whitelist", urls[i], false);
}
}
default: // BMODE_TYPES.WHITELIST
{
for (var i = 0; i < urls.length; i++)
{
removeFromList(tempAllowList, "tempAllowList", urls[i], true);
addToList(whitelist, "whitelist", urls[i], false);
}
break;
}
}
updateLists();
}
function revokeUrl(urls)
{
switch(blocking_mode)
{
case BMODE_TYPES.BLACKLIST:
{
for (var i = 0; i < urls.length; i++)
{
removeFromList(tempAllowList, "tempAllowList", urls[i], true);
addToList(blacklist, "blacklist", urls[i], false);
}
break;
}
case BMODE_TYPES.WHITELIST_ALLOW_TOP_LEVEL:
{
for (var i = 0; i < urls.length; i++)
{
removeFromList(whitelist, "whitelist", urls[i], false);
removeFromList(tempAllowList, "tempAllowList", urls[i], true);
addToList(blacklist, "blacklist", urls[i], false);
}
}
default: // BMODE_TYPES.WHITELIST
{
for (var i = 0; i < urls.length; i++)
{
removeFromList(whitelist, "whitelist", urls[i], false);
removeFromList(tempAllowList, "tempAllowList", urls[i], true);
}
break;
}
}
updateLists();
}
/*
Only called when in BMODE_TYPES.WHITELIST_ALLOW_TOP_LEVEL
*/
function sameSiteUrl(urls)
{
for (var i = 0; i < urls.length; i++)
{
removeFromList(whitelist, "whitelist", urls[i], false);
removeFromList(tempAllowList, "tempAllowList", urls[i], true);
removeFromList(blacklist, "blacklist", urls[i], false);
}
updateLists();
}
function tempPermitUrl(urls)
{
switch(blocking_mode)
{
case BMODE_TYPES.BLACKLIST:
{
for (var i = 0; i < urls.length; i++)
{
addToList(blacklist, "blacklist", urls[i], false);
addToList(tempAllowList, "tempAllowList", urls[i], true);
}
break;
}
case BMODE_TYPES.WHITELIST_ALLOW_TOP_LEVEL:
{
for (var i = 0; i < urls.length; i++)
{
removeFromList(whitelist, "whitelist", urls[i], false);
removeFromList(blacklist, "blacklist", urls[i], false);
addToList(tempAllowList, "tempAllowList", urls[i], true);
}
}
default: // BMODE_TYPES.WHITELIST
{
for (var i = 0; i < urls.length; i++)
{
removeFromList(whitelist, "whitelist", urls[i], false);
addToList(tempAllowList, "tempAllowList", urls[i], true);
}
break;
}
}
updateLists();
}
function addToList(list, listName, url, isSession) {
url = url.toLowerCase();
var returnedVal = findUrlPatternIndex(list, url);
if (returnedVal >= -1)
return;
returnedVal = Math.abs(returnedVal + 2);
list.splice(returnedVal, 0, url);
}
function removeFromList(list, listName, url, isSession) {
url = url.toLowerCase();
var removedOneOrMore = false;
while(true)
{
var returnedVal = findUrlPatternIndex(list, url);
if (returnedVal < 0)
break;
list.splice(returnedVal, 1);
removedOneOrMore = true;
for (var i = returnedVal; i < list.length; i++)
{
if (patternMatches(url, list[i]))
{
list.splice(i, 1);
i--;
}
else
{
break;
}
}
for (var i = returnedVal - 1; i >= 0; i--)
{
if (patternMatches(url, list[i]))
{
list.splice(i, 1);
}
else
{
break;
}
}
}
}
/*
In place removal and trimming of the links array.
*/
function removeEmptyInArray(links)
{
if (links)
{
for (var i = 0; i < links.length; i++)
{
if (links[i])
links[i] = links[i].trim();
if (!links[i])
{
links.splice(i, 1);
i--;
}
}
}
}
/*
In place removal of duplicates. The "links" must already be sorted.
*/
function removeDuplicatesInArray(links)
{
if (links)
{
for (var i = 0; i < links.length - 1; i++)
{
if (patternMatches(links[i], links[i+1]))
{
links.splice(i+1, 1); // links[i+1] will always be longer than links[i] because the list is sorted
}
}
}
}
/*
Used by Options.html whitelist tab to save. Assume that the user enters malformed data.
-Remove empty links/trims them
-Remove duplicates
*/
function saveWhitelist(newWhitelist)
{
removeEmptyInArray(newWhitelist); // in place removal
if (!sortUrlList(newWhitelist)) // in place sort
{
return false;
}
else
{
removeDuplicatesInArray(newWhitelist); // in place removal
whitelist = newWhitelist; // This line required by Options.html to update correctly
config.set('whitelist', whitelist);
return true;
}
}
function saveBlacklist(newBlacklist)
{
removeEmptyInArray(newBlacklist); // in place removal
if (!sortUrlList(newBlacklist)) // in place sort
{
return false;
}
else
{
removeDuplicatesInArray(newBlacklist); // in place removal
blacklist = newBlacklist; // This line required by Options.html to update correctly
config.set('blacklist', blacklist);
return true;
}
}
function saveTempAllowList(newTempAllowList)
{
removeEmptyInArray(newTempAllowList); // in place removal
if (!sortUrlList(newTempAllowList)) // in place sort
{
return false;
}
else
{
removeDuplicatesInArray(newTempAllowList); // in place removal
tempAllowList = newTempAllowList; // This line required by Options.html to update correctly
config.set('tempAllowList', tempAllowList);
return true;
}
}
var pageSourcesAllowed = new Array();
var pageSourcesTempAllowed = new Array();
var pageSourcesForbidden = new Array();
var pageSourcesUntrusted = new Array();
var topDomain = getPrimaryDomain(window.location.href);
function isUntrusted(url)
{
return (blocking_mode == BMODE_TYPES.WHITELIST_ALLOW_TOP_LEVEL ? islisted(blacklist, url) : false);
}
function isWhitelisted(url) {
switch(blocking_mode)
{
case BMODE_TYPES.BLACKLIST:
{
return !islisted(blacklist, url);
}
case BMODE_TYPES.WHITELIST_ALLOW_TOP_LEVEL:
{
if (!islisted(blacklist, url))
{
return (patternMatches(url, topDomain)) || islisted(whitelist, url);
}
else
return false;
}
default: // BMODE_TYPES.WHITELIST
return islisted(whitelist, url);
}
}
function isTempAllowListed(url) {
return islisted(tempAllowList, url);
}
function isGloballyAllowed()
{
return globalAllowAll;
}
function preventAndAddToList(event, mainURL, list)
{
event.preventDefault();
if (list.indexOf(mainURL) < 0)
list.push(mainURL);
}
// Blocks both inline and source scripts: http://www.opera.com/docs/userjs/specs/#evlistener
// Test inline blocking at http://www.w3schools.com/JS/tryit_view.asp?filename=tryjs_alert
// http://www.w3schools.com/JS/tryit.asp?filename=tryjs_alert
window.opera.addEventListener("BeforeScript", function(event){
//opera.extension.postMessage({"type": "Log Message", "msg": "BeforeScript oUrl '" + event.element.src + "' \n " + event.element.text});
var currUrl = relativeToAbsoluteUrl(event.element.src);
var mainURL = getPrimaryDomain(currUrl);
//opera.extension.postMessage({"type": "Log Message", "msg": "BeforeScript " + mainURL});
if (isUntrusted(mainURL))
{
if (pageSourcesUntrusted.indexOf(mainURL) < 0)
pageSourcesUntrusted.push(mainURL);
preventAndAddToList(event, mainURL, pageSourcesUntrusted);
}
else if (isGloballyAllowed() || isWhitelisted(mainURL))
{
if (pageSourcesAllowed.indexOf(mainURL) < 0)
pageSourcesAllowed.push(mainURL);
}
else if (isTempAllowListed(mainURL))
{
if (pageSourcesTempAllowed.indexOf(mainURL) < 0)
pageSourcesTempAllowed.push(mainURL);
}
else
{
preventAndAddToList(event, mainURL, pageSourcesForbidden);
}
}, true);
// Blocks javascript url's
window.opera.addEventListener("BeforeJavascriptURL",function(event){
var currUrl = relativeToAbsoluteUrl(window.location.href);
var mainURL = getPrimaryDomain(currUrl);
if (isUntrusted(mainURL))
{
if (pageSourcesUntrusted.indexOf(mainURL) < 0)
pageSourcesUntrusted.push(mainURL);
preventAndAddToList(event, mainURL, pageSourcesUntrusted);
}
else if (isGloballyAllowed() || isWhitelisted(mainURL))
{
if (pageSourcesAllowed.indexOf(mainURL) < 0)
pageSourcesAllowed.push(mainURL);
}
else if (isTempAllowListed(mainURL))
{
if (pageSourcesTempAllowed.indexOf(mainURL) < 0)
pageSourcesTempAllowed.push(mainURL);
}
else
{
preventAndAddToList(event, mainURL, pageSourcesForbidden);
}
}, true);
//})(window.opera, window.opera.scriptStorage);
function updateSettings()
{
var needToReload = false;
whitelist = config.get('whitelist');
blacklist = config.get('blacklist');
tempAllowList = config.get('tempAllowList');
globalAllowAll = config.get('globalAllowAll');
blocking_mode = config.get('blocking_mode');
if (globalAllowAll && pageSourcesForbidden.length > 0)
{
needToReload = true;
}
if (!needToReload && !isGloballyAllowed())
{
for (var i = 0; i < pageSourcesForbidden.length; i++)
{
if (isTempAllowListed(pageSourcesForbidden[i]) || isWhitelisted(pageSourcesForbidden[i]) || isUntrusted(pageSourcesForbidden[i]))
{
needToReload = true;
break;
}
}
for (var i in pageSourcesUntrusted)
{
if (!isUntrusted(pageSourcesUntrusted[i]))
{
needToReload = true;
break;
}
}
var moveFromAllowed = new Array();
if (!needToReload)
{
for (var i = 0; i < pageSourcesAllowed.length; i++)
{
if (isTempAllowListed(pageSourcesAllowed[i]))
{
moveFromAllowed.push(pageSourcesAllowed[i]);
pageSourcesAllowed.splice(i, 1);
i--;
}
else if (!isWhitelisted(pageSourcesAllowed[i]))
{
needToReload = true;
break;
}
}
}
var moveFromTempAllowed = new Array();
if (!needToReload)
{
for (var i = 0; i < pageSourcesTempAllowed.length; i++)
{
if (isWhitelisted(pageSourcesTempAllowed[i]))
{
moveFromTempAllowed.push(pageSourcesTempAllowed[i]);
pageSourcesTempAllowed.splice(i, 1);
i--;
}
else if (!isTempAllowListed(pageSourcesTempAllowed[i]))
{
needToReload = true;
break;
}
}
}
if (!needToReload)
{
for (var i = 0; i < moveFromAllowed.length; i++)
{
pageSourcesTempAllowed.push(moveFromAllowed[i]);
}
for (var i = 0; i < moveFromTempAllowed.length; i++)
{
pageSourcesAllowed.push(moveFromTempAllowed[i]);
}
}
}
//opera.extension.postMessage({"type": "Log Message", "msg": "Need to reload: " + window.location + " " + needToReload.toString() + fatalError.toString()});
if (needToReload && !fatalError)
{
window.location.reload(true);
}
}
opera.extension.onmessage = function (msg)
{
//opera.extension.postMessage({"type": "Log Message", "msg": "onmessage for " + msg.data.type + " in " + window.location + " " + fatalError});
switch (msg.data.type)
{
case "get sources":
/*
opera.extension.postMessage({"type": "Log Message", "msg": "determine whether to send sources \n"
+ window.location.href + " \n"
+ unescape(window.location.href) + " \n"
+ decodeURI(window.location.href) + " \n"
+ decodeURIComponent(window.location.href) + " \n\n"
+ msg.data.forUrl + " \n"
+ unescape(msg.data.forUrl) + " \n"
+ decodeURI(msg.data.forUrl) + " \n"
+ decodeURIComponent(msg.data.forUrl)});
opera.extension.postMessage({"type": "Log Message", "msg": "Before sending sources from " + window.location + " " + pageSourcesForbidden.length + " " + pageSourcesAllowed.length}); */
var unescWinLoc = unescape(window.location.href);
var unescForUrl = unescape(msg.data.forUrl);
var decodeURIWinLoc;
try
{
decodeURIWinLoc = decodeURI(window.location.href);
}
catch (err)
{
decodeURIWinLoc = unescape(window.location.href);
}
var urlsMatch = ((window.top != window.self) || (window.location.href == msg.data.forUrl) || (unescWinLoc.indexOf(unescForUrl) == 0)
|| (unescForUrl.indexOf(unescWinLoc) == 0) || (decodeURIWinLoc.indexOf(unescForUrl) == 0) || (unescForUrl.indexOf(decodeURIWinLoc) == 0));
if (true || urlsMatch)
{
//opera.extension.postMessage({"type": "Log Message", "msg": "Sending sources from " + window.location + " " + pageSourcesForbidden.length + " " + pageSourcesAllowed.length});
opera.extension.postMessage({"type": "source data response",
"whitelist": whitelist,
"blacklist": blacklist,
"tempAllowList": tempAllowList,
"globalAllowAll": isGloballyAllowed(),
"pageSourcesAllowed": pageSourcesAllowed, "pageSourcesTempAllowed": pageSourcesTempAllowed,
"pageSourcesForbidden": pageSourcesForbidden, "pageSourcesUntrusted": pageSourcesUntrusted,
"fatalError": fatalError, "blocking_mode": blocking_mode, "url": window.location.href, "topDomain": topDomain,
"topDomainIsWhitelisted": (blocking_mode == BMODE_TYPES.WHITELIST_ALLOW_TOP_LEVEL ? islisted(whitelist, topDomain) : false),
"multiSelect": config.get("multiSelect")});
}
break;
case "updateAllTabs": // Tell all tabs to check lists and reload if needed
updateSettings();
break;
case "Get Hello From Tab":
opera.extension.postMessage({"type": "Log Message", "msg": "Hello World"});
break;
case "toggleOnOff":
toggleOnOff(msg.data.newState);
break;
case "tempPermitUrl":
tempPermitUrl(msg.data.urls);
break;
case "revokeUrl":
revokeUrl(msg.data.urls);
break;
case "permitUrl":
permitUrl(msg.data.urls);
break;
case "sameSiteUrl":
sameSiteUrl(msg.data.urls);
break;
case "saveWhitelist":
saveWhitelist(msg.data.newList);
break;
case "saveBlacklist":
saveBlacklist(msg.data.newList);
break;
case "saveTempAllowList":
saveTempAllowList(msg.data.newList);
break;
case "firstSort":
firstSort();
break;
case "changeBlockingMode":
config.set('blocking_mode', msg.data.newBlockingMode);
blocking_mode = config.get('blocking_mode');
updateSettings();
break;
case "set multiSelect":
config.set('multiSelect', msg.data.newState);
break;
}
};