禁止访问网页

禁止访问网页列表,做一个自律的人,成为更好的自己。

// ==UserScript==
// @name                禁止访问网页
// @name:en             Be A Good Guy | Block Webites
// @name:zh-cn          禁止访问网页
// @description         禁止访问网页列表,做一个自律的人,成为更好的自己。
// @description:en      Block websites, be a disciplined person, and become a better self.
// @description:zh-cn   禁止访问网页列表,做一个自律的人,成为更好的自己。
// @namespace           https://github.com/BasilGuo/
// @license             MIT
// @version             0.1.3
// @author              Basil Guo
// @match               http*://*/*
// @grant               GM_addStyle
// @grant               GM_deleteValue
// @grant               GM_getValue
// @grant               GM_listValues
// @grant               GM_setValue
// @grant               GM_registerMenuCommand
// @icon                https://images2.imgbox.com/01/e3/5as6gVyM_o.png
// @homepageURL         https://gist.github.com/BasilGuo/64c0730f2cdee29d2abf057577b86b39
// @run-at              document-start
// ==/UserScript==

(function () {
    'use strict';

    // Your code here...
    var g_one_rule_window = false;
    const G_LANGUAGE_INDEX = ('en' === navigator.language || 'en' === navigator.userLanguage) ? 0 : 1;

    // https://stackoverflow.com/questions/55935641/how-to-get-language-of-user-navigator-languages-not-working
    // console.log(navigator.language, navigator.userLanguage);
    // console.log(navigator.languages ? navigator.languages[0] : (navigator.language || navigator.userLanguage));

    const G_RULE_PROMPT = {
        'title': {
            'addRule': ["Add A Rule For Current Site", '为本站添加规则'],
            'listRule': ["List Rules of All Sites", '列出所有网站规则'],
            'clearRule': ["Clear All Rules of All Sites", "清空所有网站规则"],
            'customURL': ["Custom the Redirect Webite", "自定义重定向网站"]
        },
        'common': {
            'btnCancelText': [`Cancel`, `取消`],
            'btnOKText': [`OK`, `确认`],
            'btnDeleteText': [`Delete`, `删除`],
            'noRuleText': [`There is no access rules now.`, `当前没有受限访问网站`],
            'alertText': [`There is a rule window opened. Please close it first~`, `已经有一个规则窗口了,请先关闭吧~`],
            'redirectURLText': [`Input the URL you would finally accessed when browsing blocked websites`, `输入访问被禁止网站时,最终重定向到的网址`]
        },
        'add': {
            'titleText': [`Add access time limit rule for<br/>${location.host}`, `给${location.host}<br/>添加访问限制时间规则`],
            'siteURLText': [`Set the site path. Default is the current hostname so you could let it alone. Not support for wildcards now.`, `设置本网站屏蔽规则,暂不支持通配符,默认为当前网站域名,非必填项`],
            'limitDayLongText': [`Limit for all day`, `全天限制`],
            'limitStartTimeText': [`Limit start time: `, `限制开始时间`],
            'limitEndTimeText': [`Limit  end  time: `, `限制结束时间`],
            'failedTimeText': [`Add rule failed for limit start time should be less than end time`, `添加规则失败:限制开始时间应该先于限制结束时间`],
            'failedHostText': [`Add rule failed because URL of current site is wrong, please correct it.<br/>Take the following www.example.com/path/subpath as an example. No protocols is needed.`, `添加规则失败:网站URL规则设置错误,参考例子不带协议`],
            'successText': [`Add rule success, effect after refress site.`, `规则添加成功,刷新页面以生效规则`]
        },
        'list': {
            'titleText': [`Limit access rules for all websites`, `当前所有网站限制访问规则`],
            'ruleHostText': [`Limit Website`, `限制网站`],
            'ruleTimeStartText': [`Limit Start Time`, `限制开始时间`],
            'ruleTimeEndText': [`Limit End Time`, `限制结束时间`],
            'deleteActionText': [`Actions`, `操作`],
            'deleteRuleConfirmText1': [`Are you sure to delete?`, `真的要删除限制规则吗?`],
            'deleteRuleConfirmText2': [`Buddy, being the best self?`, `你不打算成为更好的自己了吗?`],
            'deleteRuleConfirmText3': [`OK, the last time to confirm.`, `好吧,最后一次确认了。`],
            'deleteRuleCancelText': [`OK, I have known you are the best.`, `我就知道你是最棒的!`],
        }
    };

    function cancelRuleWindow() {
        let rule_window = document.getElementById('bs-rule');
        rule_window.parentNode.removeChild(rule_window);
        g_one_rule_window = false;
    }

    function addRuleLimitTimeDaylongOnChange() {
        let limit_time_daylong_cbx = document.getElementById('rule-time-day-long');
        let limit_time_start_input = document.getElementById('rule-time-start');
        let limit_time_end_input = document.getElementById('rule-time-end');

        if (limit_time_daylong_cbx.checked) {
            limit_time_start_input.disabled = "disabled";
            limit_time_end_input.disabled = "disabled";
        }
        else {
            limit_time_start_input.disabled = "";
            limit_time_end_input.disabled = "";
        }
    }

    function addRuleOKButtonClick() {
        let time_arr, hour, minute;
        let limit_start_secs, limit_end_secs;
        let limit_time_daylong_cbx = document.getElementById('rule-time-day-long');
        let limit_time_start = document.getElementById('rule-time-start').value;
        let limit_time_end = document.getElementById('rule-time-end').value;
        let rule_url_input = document.getElementById('rule-url');

        if (limit_time_daylong_cbx.checked) {
            limit_time_start = '00:00';
            limit_time_end = '23:59';
        }

        time_arr = limit_time_start.split(':');
        hour = time_arr[0];
        minute = time_arr[1];
        limit_start_secs = hour * 3600 + minute * 60;
        time_arr = limit_time_end.split(':');
        hour = time_arr[0];
        minute = time_arr[1];
        limit_end_secs = hour * 3600 + minute * 60;

        if (limit_start_secs > limit_end_secs) {
            alert(G_RULE_PROMPT.add.failedTimeText[G_LANGUAGE_INDEX]);
            return;
        } else {
            let rule_url = rule_url_input.value;
            let all_hosts = GM_getValue('BlockWebsitesRules');
            let all_hosts_set;

            if (rule_url === '') {
                rule_url = document.location.host;
            } else if (rule_url.indexOf("://") !== -1) {
                rule_url = rule_url.split("://")[1];
            }
            rule_url = rule_url.replace(/\/$/g, ''); // remove the last '/'

            if (all_hosts === undefined || all_hosts === '') {
                all_hosts_set = new Set();
            } else {
                all_hosts_set = new Set(JSON.parse(all_hosts)['BlockWebsitesRules'].split(' '));
            }

            if (document.location.href.indexOf(rule_url) === -1 || rule_url.indexOf(document.location.host) === -1) {
                alert(G_RULE_PROMPT.add.failedHostText[G_LANGUAGE_INDEX]);
                return;
            }

            console.log("all_hosts: ", all_hosts);
            all_hosts_set.add(rule_url);
            all_hosts = { 'BlockWebsitesRules': Array.from(all_hosts_set).join(' ') };
            console.log('BlockWebsitesRules: ', all_hosts);
            GM_setValue(rule_url, JSON.stringify({ 'start': limit_start_secs, 'end': limit_end_secs }));
            GM_setValue("BlockWebsitesRules", JSON.stringify(all_hosts));
            cancelRuleWindow();
            alert(G_RULE_PROMPT.add.successText[G_LANGUAGE_INDEX]);
            location.reload();
        }
    }

    function addRuleActions() {
        let limit_time_daylong_cbx = document.getElementById('rule-time-day-long');
        let ok_add_btn = document.getElementById("ok-add-btn");
        let cancel_add_btn = document.getElementById("cancel-add-btn");

        limit_time_daylong_cbx.onchange = addRuleLimitTimeDaylongOnChange;
        ok_add_btn.onclick = addRuleOKButtonClick;
        cancel_add_btn.onclick = cancelRuleWindow;
        g_one_rule_window = true;
    }

    function addRuleWindow() {
        let add_rule_div = `<div class="bs-rule-content" id="bs-rule">
            <div class="bs-rule-header" id="bs-rule-header">
              <!-- <span class="close">×</span> -->
              <h2>${G_RULE_PROMPT.add.titleText[G_LANGUAGE_INDEX]}</h2>
            </div>
            <div class="bs-rule-body" id="bs-rule-body">
              <form>
                <label for="rule-time-day-long">${G_RULE_PROMPT.add.limitDayLongText[G_LANGUAGE_INDEX]}</label>
                <input type="checkbox" id="rule-time-day-long" name="ruleTimeDayLong" />
                <hr/>
                <label for="rule-time-start">${G_RULE_PROMPT.add.limitStartTimeText[G_LANGUAGE_INDEX]}</label>
                <input id="rule-time-start" type="time" name="limitTimeStart", value="00:00" pattern="[0-9]{2}:[0-9]{2}" required />
                <br/>
                <label for="rule-time-end">${G_RULE_PROMPT.add.limitEndTimeText[G_LANGUAGE_INDEX]}</label>
                <input id="rule-time-end" type="time" name="limitTimeEnd", value="18:00" pattern="[0-9]{2}:[0-9]{2}" required />
                <hr/>
                <label for="rule-url">${G_RULE_PROMPT.add.siteURLText[G_LANGUAGE_INDEX]}</label>
                <br/>
                <input type="text" id="rule-url" name="ruleURL" placeholder="www.example.com/path/subpath" style="width:68%" />
              </form>
            </div>
            <div class="bs-rule-footer" id="bs-rule-footer">
              <button id="cancel-add-btn" class="bs-btn bs-cancel-btn">${G_RULE_PROMPT.common.btnCancelText[G_LANGUAGE_INDEX]}</button>
              <button id="ok-add-btn" class="bs-btn bs-ok-btn">${G_RULE_PROMPT.common.btnOKText[G_LANGUAGE_INDEX]}</button>
            </div>
        </div>`
        let body = document.getElementsByTagName("body")[0];
        let rule_div = document.createElement("div");
        rule_div.setAttribute("id", "bs-rule");
        rule_div.setAttribute("class", "bs-rule");
        rule_div.innerHTML = add_rule_div;
        body.prepend(rule_div);
        g_one_rule_window = true;
    }

    function addRule(event) {
        if (g_one_rule_window) {
            alert(G_RULE_PROMPT.common.alertText[G_LANGUAGE_INDEX]);
            return;
        }

        addRuleWindow();
        addRuleActions();
    }

    function listRuleWindow() {
        let list_rule_div = `<div class="bs-rule-content" id="bs-rule">
            <div class="bs-rule-header" id="bs-rule-header">
              <!-- <span class="close">×</span> -->
              <h2>${G_RULE_PROMPT.list.titleText[G_LANGUAGE_INDEX]}</h2>
            </div>
            <div class="bs-rule-body" id="bs-rule-body">
                <table id="bs-rule-list-tbl">
                <thead>
                  <tr>
                    <td>${G_RULE_PROMPT.list.ruleHostText[G_LANGUAGE_INDEX]}</td>
                    <td>${G_RULE_PROMPT.list.ruleTimeStartText[G_LANGUAGE_INDEX]}</td>
                    <td>${G_RULE_PROMPT.list.ruleTimeEndText[G_LANGUAGE_INDEX]}</td>
                    <td>${G_RULE_PROMPT.list.deleteActionText[G_LANGUAGE_INDEX]}</td>
                  </tr>
                </thead>
                <tbody id="bs-rule-list-tbl-body">
                </tbody>
                </table>
            </div>
            <div class="bs-rule-footer" id="bs-rule-footer">
              <button id="cancel-add-btn" class="bs-btn bs-cancel-btn">${G_RULE_PROMPT.common.btnCancelText[G_LANGUAGE_INDEX]}</button>
            </div>
        </div>`
        let body = document.getElementsByTagName("body")[0];
        let rule_div = document.createElement("div");
        rule_div.setAttribute("id", "bs-rule");
        rule_div.setAttribute("class", "bs-rule");
        rule_div.innerHTML = list_rule_div;
        body.prepend(rule_div);
        g_one_rule_window = true;
    }

    function listRuleActions() {
        let cancel_btn = document.getElementById("cancel-add-btn");
        let bs_rule_window = document.getElementById('bs-rule');

        cancel_btn.onclick = cancelRuleWindow;
        window.onclick = function (event) {
            if (event.target === bs_rule_window) {
                cancelRuleWindow();
            }
        }
    }

    function deleteRuleConfirm(msg) {
        if (msg && msg.length != 0) {
            msg = "\n" + msg;
        } else {
            msg = '';
        }
        let rs = confirm(G_RULE_PROMPT.list.deleteRuleConfirmText1[G_LANGUAGE_INDEX] + msg);
        if (!rs) {
            alert(G_RULE_PROMPT.list.deleteRuleCancelText[G_LANGUAGE_INDEX]);
        } else {
            rs = confirm(G_RULE_PROMPT.list.deleteRuleConfirmText2[G_LANGUAGE_INDEX] + msg);
            if (!rs) {
                alert(G_RULE_PROMPT.list.deleteRuleCancelText[G_LANGUAGE_INDEX]);
            } else {
                rs = confirm(G_RULE_PROMPT.list.deleteRuleConfirmText3[G_LANGUAGE_INDEX] + msg);
                if (!rs) {
                    alert(G_RULE_PROMPT.list.deleteRuleCancelText[G_LANGUAGE_INDEX]);
                }
            }
        }
        return rs;
    }

    function deleteRule() {
        let mrs = JSON.parse(GM_getValue(this.name));
        let msg = this.name + " : ";
        msg = msg + parseInt(mrs['start'] / 3600).toFixed(0).padStart(2, '0') + ":" + parseInt(mrs['start'] % 3600 / 60).toFixed(0).padStart(2, '0');
        msg += '-';
        msg = msg + parseInt(mrs['end'] / 3600).toFixed(0).padStart(2, '0') + ":" + parseInt(mrs['end'] % 3600 / 60).toFixed(0).padStart(2, '0');
        let rs = deleteRuleConfirm(msg);

        if (!rs) return;

        let all_hosts = GM_getValue('BlockWebsitesRules');
        let all_hosts_set;

        if (all_hosts === undefined || all_hosts === '') {
            all_hosts_set = new Set();
        } else {
            all_hosts_set = new Set(JSON.parse(all_hosts)['BlockWebsitesRules'].split(' '));
        }

        console.log("all_hosts: ", all_hosts);
        all_hosts_set.delete(this.name);
        all_hosts = { 'BlockWebsitesRules': Array.from(all_hosts_set).join(' ') };
        console.log('BlockWebsitesRules: ', all_hosts);
        GM_deleteValue(this.name);
        GM_setValue("BlockWebsitesRules", JSON.stringify(all_hosts));
        cancelRuleWindow();
        listRuleFillTable();
    }

    function listRuleFillTable() {
        let blockedSites = JSON.parse(GM_getValue('BlockWebsitesRules'));
        blockedSites = blockedSites['BlockWebsitesRules'].split(' ');
        let rule_tbl_body = document.getElementById("bs-rule-list-tbl-body");
        let is_no_rule = true;

        for (let blocked_site of blockedSites) {
            let site = GM_getValue(blocked_site);
            if (site !== undefined) {
                is_no_rule = false;
                let mrs = JSON.parse(site);
                let tr = document.createElement("tr");
                tr.innerHTML = `<td>${blocked_site}</td>
                <td>${parseInt(mrs['start'] / 3600).toFixed(0).padStart(2, '0')}:${parseInt(mrs['start'] % 3600 / 60).toFixed(0).padStart(2, '0')}</td>
                <td>${parseInt(mrs['end'] / 3600).toFixed(0).padStart(2, '0')}:${parseInt(mrs['end'] % 3600 / 60).toFixed(0).padStart(2, '0')}</td>
                <td><button name="${blocked_site}" class="bs-btn bs-delete-btn">${G_RULE_PROMPT.common.btnDeleteText[G_LANGUAGE_INDEX]}</button></td>`
                rule_tbl_body.append(tr);
            }
        }

        if (is_no_rule) {
            let bs_rule_body = document.getElementById("bs-rule-body");
            let no_rule_txt = document.createElement('h2');
            no_rule_txt.innerHTML = G_RULE_PROMPT.common.noRuleText[G_LANGUAGE_INDEX];
            bs_rule_body.append(no_rule_txt);
        } else {
            let del_btns = document.getElementsByClassName("bs-delete-btn");
            for (let i = 0; i < del_btns.length; ++i) {
                del_btns[i].onclick = deleteRule;
            }
        }
    }

    function listRule(event) {
        if (g_one_rule_window) {
            alert(G_RULE_PROMPT.common.alertText[G_LANGUAGE_INDEX]);
            return;
        }

        listRuleWindow();
        listRuleActions();
        listRuleFillTable();
    }

    function clearRule() {
        let rs = deleteRuleConfirm();
        if (!rs) { return; }

        let all_hosts = GM_getValue('BlockWebsitesRules');
        let all_hosts_set;

        if (all_hosts === undefined || all_hosts === '') {
            all_hosts_set = new Set();
        } else {
            all_hosts_set = new Set(JSON.parse(all_hosts)['BlockWebsitesRules'].split(' '));
        }

        console.log("all_hosts_set", all_hosts_set);

        for (let site of all_hosts_set) {
            console.log(site);
            GM_deleteValue(site);
        }

        all_hosts = { 'BlockWebsitesRules': '' };
        console.log('BlockWebsitesRules: ', all_hosts);
        GM_setValue("BlockWebsitesRules", JSON.stringify(all_hosts));
    }

    function customURL() {
        let url = prompt(G_RULE_PROMPT.common.redirectURLText[G_LANGUAGE_INDEX], "https://www.example.org/");
        GM_setValue("BlockWebsiteRedirectURL", url);
    }

    function setup() {
        let css = `#bs-rule-body>input,.bs-rule{width:100%}.bs-rule{display:block;position:fixed;max-width:unset;z-index:99999;padding-top:100px;left:0;top:0;height:100%;overflow:auto;background-color:rgba(0,0,0,.4)}.bs-rule-content{position:relative;background-color:#fefefe;margin:auto;padding:0;border:1px solid #888;width:35%;box-shadow:0 4px 8px 0 rgba(0,0,0,.2),0 6px 20px 0 rgba(0,0,0,.19);-webkit-animation-name:bs-rule-animatetop;-webkit-animation-duration:.4s;animation-name:bs-rule-animatetop;animation-duration:.4s}.bs-rule-body,.bs-rule-footer,.bs-rule-header{padding:2px 16px}.bs-rule-footer,.bs-rule-header{background-color:#5587a2;color:#fff}@-webkit-keyframes bs-rule-animatetop{from{top:-300px;opacity:0}to{top:0;opacity:1}}@keyframes bs-rule-animatetop{from{top:-300px;opacity:0}to{top:0;opacity:1}}.bs-close{color:#fff;float:right;font-size:28px;font-weight:700}.bs-close:focus,.bs-close:hover{color:#000;text-decoration:none;cursor:pointer}.bs-rule-body{min-height:20%;max-height:100%;min-width:20%;max-width:100%;overflow-x:auto;overflow-y:auto}.bs-rule-footer{text-align:right}.bs-btn{color:#000;border:none;border-radius:5px;font-size:1.2em}.bs-btn:focus,.bs-btn:hover{cursor:pointer;box-shadow:1px 1px 1px rgba(0,0,0,.5);-moz-box-shadow:1px 1px .5em rgba(0,0,0,.5);-webkit-box-shadow:1px 1px .5em rgba(0,0,0,.5);background-color:#5587a2}.bs-ok-btn{background-color:#888}.bs-cancel-btn{background-color:#afafaf}.bs-delete-btn{border-color:none;font-size:1em;background-color:#e5624a}#bs-rule-body>table{border-collapse:collapse}#bs-rule-body>table>thead{background-color:#333;color:#fff;font-size:.875rem;text-transform:title;letter-spacing:2%}#bs-rule-body>table>td,#bs-rule-body>table>tr{border:1px solid #000;color:#000;text-align:left}`;
        GM_addStyle(css);

        let url = GM_getValue("BlockWebsiteRedirectURL");
        if (url === undefined || url === '') {
            GM_setValue("BlockWebsiteRedirectURL", 'chrome-extension://invalid');
        }

    }

    function register_menu() {
        GM_registerMenuCommand(G_RULE_PROMPT.title.addRule[G_LANGUAGE_INDEX], addRule, "a");
        GM_registerMenuCommand(G_RULE_PROMPT.title.listRule[G_LANGUAGE_INDEX], listRule, "l");
        GM_registerMenuCommand(G_RULE_PROMPT.title.clearRule[G_LANGUAGE_INDEX], clearRule, "c");
        GM_registerMenuCommand(G_RULE_PROMPT.title.customURL[G_LANGUAGE_INDEX], customURL, "s");
    }

    function locationReplace(url) {
        console.log(url);
        // location.href = url;
        // location.assign(url);
        location.replace(url);
    }

    function check_to_block() {
        let datetime = new Date();
        let hours = datetime.getHours();
        let minutes = datetime.getMinutes();
        let seconds = hours * 3600 + minutes * 60;
        let paths = document.location.href.split("://")[1].split("/");
        let path = document.location.host;
        let i = 1;

        do {
            let rs = GM_getValue(path);

            if (rs !== undefined) {
                let trs = JSON.parse(rs); // time rs
                if (trs['start'] <= seconds && seconds <= trs['end']) {
                    let url = GM_getValue("BlockWebsiteRedirectURL");
                    locationReplace(url);
                }
            }
            if (i < paths.length) {
                path = path + "/" + paths[i];
            }
        } while (i++ < paths.length)
    }

    function deleteKeysWithConfirm() {
        let keys = GM_listValues();
        console.log(keys);
        for (let i = 0; i < keys.length; ++i) {
            let v = keys[i];
            let rs = confirm("Delete?" + v + ": " + GM_getValue(v));
            if (rs) {
                GM_deleteValue(v);
            }
        }
        console.log(GM_listValues());
    }

    function main() {
        // deleteKeysWithConfirm();
        setup();
        register_menu();
        check_to_block();
    }

    main();
})();