Javascript

Discussion in 'Болталка' started by fury_makers, 23 May 2015.

  1. fury_makers

    fury_makers Member

    Joined:
    7 Mar 2013
    Messages:
    128
    Likes Received:
    20
    Reputations:
    0
    Code:
    // ==/UserScript==
    //(function(opera, scriptStorage){
    scriptStorage = window.opera.scriptStorage;
    
    String.prototype.trim = function () {
        // http://blog.stevenlevithan.com/archives/faster-trim-javascript
        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]';
    }
    
    /*
    Returns a random string suitable for use as an id in html/javascript code.
    Length is hardcoded to be between 30 and 40 characters
    */
    function randomID()
    {
        const length = 30 + Math.floor(Math.random() * 11);    // minimum 30, max 40
        const chars = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ_1234567890";    // total 63 characters
        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;
    }
    
    // parseUri 1.2.2
    // (c) Steven Levithan <stevenlevithan.com>
    // MIT License
    // http://stevenlevithan.com/demo/parseuri/js/
    // parseUri does not handle IPv6 addresses
    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*))?)(((\/(?:[^?#](?![^?#\/]*\.[^?#\/.]+(?:[?#]|$)))*\/?)?([^?#\/]*))(?:\?([^#]*))?(?:#(.*))?)/
        }
    };
    
    // These are some of the common and known top level domains from Mozilla's http://publicsuffix.org/
    // They are used to remove the subdomains from url's with known top level domains
    // A more complete list will be generated in the future
    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;
    
    // http://intermapper.ning.com/profiles/blogs/a-regular-expression-for-ipv6
    // http://www.intermapper.com/ipv6validator
    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;
    
    // http://stackoverflow.com/questions/183485/can-anyone-recommend-a-good-free-javascript-for-punycode-to-unicode-conversion
    //Javascript Punycode converter derived from example in RFC3492.
    //This implementation is created by some@domain.name and released into public domain
    var punycode = new function Punycode() {
        // This object converts to and from puny-code used in IDN
        //
        // punycode.ToASCII ( domain )
        //
        // Returns a puny coded representation of "domain".
        // It only converts the part of the domain name that
        // has non ASCII characters. I.e. it dosent matter if
        // you call it with a domain that already is in ASCII.
        //
        // punycode.ToUnicode (domain)
        //
        // Converts a puny-coded domain name to unicode.
        // It only converts the puny-coded parts of the domain name.
        // I.e. it dosent matter if you call it on a string
        // that already has been converted to unicode.
        //
        //
        this.utf16 = {
            // The utf16-class is necessary to convert from javascripts internal character representation to unicode and back.
            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("");
            }
        }
    
        //Default parameters
        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;
    
        // decode_digit(cp) returns the numeric value of a basic code
        // point (for use in representing integers) in the range 0 to
        // base-1, or base if cp is does not represent a value.
    
        function decode_digit(cp) {
            return cp - 48 < 10 ? cp - 22 : cp - 65 < 26 ? cp - 65 : cp - 97 < 26 ? cp - 97 : base;
        }
    
        // encode_digit(d,flag) returns the basic code point whose value
        // (when used for representing integers) is d, which needs to be in
        // the range 0 to base-1. The lowercase form is used unless flag is
        // nonzero, in which case the uppercase form is used. The behavior
        // is undefined if flag is nonzero and digit d has no uppercase form.
    
        function encode_digit(d, flag) {
            return d + 22 + 75 * (d < 26) - ((flag != 0) << 5);
            //  0..25 map to ASCII a..z or A..Z
            // 26..35 map to ASCII 0..9
        }
        //** Bias adaptation function **
        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);
        }   
    
    
    
    
    Подскажите для чего предназначен даный скрипт, и как он работает? Спасибо.