🎉 欢迎访问GreasyFork.Org 镜像站!本镜像站由公众号【爱吃馍】搭建,用于分享脚本。联系邮箱📮

Greasy fork 爱吃馍镜像

记录页面滚动

记录页面滚动容器和位置,下次页面加载完成时恢复,脚本菜单可以控制网站禁用与启用

Tendrás que instalar una extensión para tu navegador como Tampermonkey, Greasemonkey o Violentmonkey si quieres utilizar este script.

You will need to install an extension such as Tampermonkey to install this script.

Necesitarás instalar una extensión como Tampermonkey o Violentmonkey para instalar este script.

Necesitarás instalar una extensión como Tampermonkey o Userscripts para instalar este script.

Necesitará instalar una extensión como Tampermonkey para instalar este script.

Necesitarás instalar una extensión para administrar scripts de usuario si quieres instalar este script.

(Ya tengo un administrador de scripts de usuario, déjame instalarlo)

🚀 安装遇到问题?关注公众号获取帮助

公众号二维码

扫码关注【爱吃馍】

回复【脚本】获取最新教程和防失联地址

Necesitará instalar una extensión como Stylus para instalar este estilo.

Necesitará instalar una extensión como Stylus para instalar este estilo.

Necesitará instalar una extensión como Stylus para instalar este estilo.

Necesitará instalar una extensión del gestor de estilos de usuario para instalar este estilo.

Necesitará instalar una extensión del gestor de estilos de usuario para instalar este estilo.

Necesitará instalar una extensión del gestor de estilos de usuario para instalar este estilo.

(Ya tengo un administrador de estilos de usuario, déjame instalarlo)

🚀 安装遇到问题?关注公众号获取帮助

公众号二维码

扫码关注【爱吃馍】

回复【脚本】获取最新教程和防失联地址

// ==UserScript==
// @name         记录页面滚动
// @version      4
// @description  记录页面滚动容器和位置,下次页面加载完成时恢复,脚本菜单可以控制网站禁用与启用
// @author       Lemon399
// @match        *://*/*
// @grant GM_getValue
// @grant GM_setValue
// @grant GM_registerMenuCommand
// @run-at       document-end
// @namespace https://greasyfork.org/users/452911
// ==/UserScript==
(function(){
const id = decodeURIComponent('3753');

function runOnce(fn, key) {
  const uniqId = 'BEXT_UNIQ_ID_' + id + (key ? key : '');
  if (window[uniqId]) {
    return;
  }
  window[uniqId] = true;
  fn && fn();
}

function runNeed(
  condition,
  fn,
  option = {
    count: 20,
    delay: 200,
    failFn: () => null,
  },
  ...args
) {
  if (typeof condition != 'function' || typeof fn != 'function') return;
  if (
    !option ||
    typeof option.count != 'number' ||
    typeof option.delay != 'number' ||
    typeof option.failFn != 'function'
  ) {
    option = {
      count: 20,
      delay: 200,
      failFn: () => null,
    };
  }
  let sleep = () => {
      return new Promise((resolve) => setTimeout(resolve, option.delay));
    },
    ok = false;
  new Promise(async (resolve, reject) => {
    for (let c = 0; !ok && c < option.count; c++) {
      await sleep();
      ok = condition.call(this, c + 1);
    }
    if (ok) {
      resolve();
    } else {
      reject();
    }
  }).then(fn.bind(this, ...args), option.failFn);
}

function runAt(start, fn, ...args) {
  if (typeof fn !== 'function') return;
  switch (start) {
    case 'document-end':
      if (
        document.readyState === 'interactive' ||
        document.readyState === 'complete'
      ) {
        fn.call(this, ...args);
      } else {
        document.addEventListener('DOMContentLoaded', fn.bind(this, ...args));
      }
      break;
    case 'document-idle':
      if (document.readyState === 'complete') {
        fn.call(this, ...args);
      } else {
        window.addEventListener('load', fn.bind(this, ...args));
      }
      break;
    default:
      if (document.readyState === 'complete') {
        setTimeout(fn, start, ...args);
      } else {
        window.addEventListener('load', () => {
          setTimeout(fn, start, ...args);
        });
      }
  }
}

function runMatch(opt = {}) {
  const { white = [], black = [], full = true } = opt;
  let addr = full ? location.href : location.hostname,
    matcher = (url) => {
      if (url.startsWith('//') && url.endsWith('//')) {
        try {
          let expr = new RegExp(url.slice(2).slice(0, -2), 'gu');
          return expr.test(addr);
        } catch (e) {
          console.error(e);
          return addr.indexOf(url) >= 0;
        }
      }
      return addr.indexOf(url) >= 0;
    },
    ok = true,
    pick = addr;
  return new Promise((resolve, reject) => {
    black.forEach((r) => {
      if (matcher(r)) {
        ok = false;
        pick = r;
      }
    });
    if (white.length > 0) {
      ok = false;
      white.forEach((r) => {
        if (matcher(r)) {
          ok = true;
          pick = r;
        }
      });
    }
    if (ok) {
      resolve(pick);
    } else reject(pick);
  });
}

function addElement({
  tag,
  attrs = {},
  to = document.body || document.documentElement,
}) {
  const el = document.createElement(tag);
  Object.assign(el, attrs);
  to.appendChild(el);
  return el;
}

function addStyle(css) {
  return addElement({
    tag: 'style',
    attrs: {
      textContent: css,
    },
    to: document.head,
  });
}

var config = {"toast":0.1,"out":1};

const blackKey = "recordScrollKey";
const savedBlack = JSON.parse(GM_getValue(blackKey, "[]"));
config.black = savedBlack;

if (savedBlack.indexOf(location.hostname) < 0) {
  GM_registerMenuCommand("在此域名禁用", () => {
    savedBlack.push(location.hostname);
    GM_setValue(blackKey, JSON.stringify(savedBlack));
    location.reload();
  })
} else {
  GM_registerMenuCommand("在此域名启用", () => {
    GM_setValue(blackKey, JSON.stringify(savedBlack.filter((domain) => domain !== location.hostname)));
    location.reload();
  })
}
function toast(text, time = 3, callback, transition = 0.2) {
  let isObj = (o) =>
      typeof o == 'object' &&
      typeof o.toString == 'function' &&
      o.toString() === '[object Object]',
    timeout,
    toastTransCount = 0;
  if (typeof text != 'string') text = String(text);
  if (typeof time != 'number' || time <= 0) time = 3;
  if (typeof transition != 'number' || transition < 0) transition = 0.2;
  if (callback && !isObj(callback)) callback = undefined;
  if (callback) {
    if (callback.text && typeof callback.text != 'string')
      callback.text = String(callback.text);
    if (
      callback.color &&
      (typeof callback.color != 'string' || callback.color === '')
    )
      delete callback.color;
    if (callback.onclick && typeof callback.onclick != 'function')
      callback.onclick = () => null;
    if (callback.onclose && typeof callback.onclose != 'function')
      delete callback.onclose;
  }

  let toastStyle = addStyle(`
  #bextToast {
    all: initial;
    display: flex;
    position: fixed;
    left: 0;
    right: 0;
    bottom: 10vh;
    width: max-content;
    max-width: 80vw;
    max-height: 80vh;
    margin: 0 auto;
    border-radius: 20px;
    padding: .5em 1em;
    font-size: 16px;
    background-color: rgba(0,0,0,0.5);
    color: white;
    z-index: 1000002;
    opacity: 0%;
    transition: opacity ${transition}s;
  }
  #bextToast > * {
    display: -webkit-box;
    height: max-content;
    margin: auto .25em;
    width: max-content;
    max-width: calc(40vw - .5em);
    max-height: 80vh;
    overflow: hidden;
    -webkit-line-clamp: 22;
    -webkit-box-orient: vertical;
    text-overflow: ellipsis;
    overflow-wrap: anywhere;
  }
  #bextToastBtn {
    color: ${callback && callback.color ? callback.color : 'turquoise'}
  }
  #bextToast.bextToastShow {
    opacity: 1;
  }
    `),
    toastDiv = addElement({
      tag: 'div',
      attrs: {
        id: 'bextToast',
      },
    }),
    toastShow = () => {
      toastDiv.classList.toggle('bextToastShow');
      toastTransCount++;
      if (toastTransCount >= 2) {
        setTimeout(function () {
          toastDiv.remove();
          toastStyle.remove();
          if (callback && callback.onclose) callback.onclose.call(this);
        }, transition * 1000 + 1);
      }
    };
  addElement({
    tag: 'div',
    attrs: {
      id: 'bextToastText',
      innerText: text,
    },
    to: toastDiv,
  });
  if (callback && callback.text) {
    addElement({
      tag: 'div',
      attrs: {
        id: 'bextToastBtn',
        innerText: callback.text,
        onclick:
          callback && callback.onclick
            ? () => {
                callback.onclick.call(this);
                clearTimeout(timeout);
                toastShow();
              }
            : null,
      },
      to: toastDiv,
    });
  }
  setTimeout(toastShow, 1);
  timeout = setTimeout(toastShow, (time + transition * 2) * 1000);
}


var now = Date.now || function() {
  return new Date().getTime();
};






function throttle(func, wait, options) {
  var timeout, context, args, result;
  var previous = 0;
  if (!options) options = {};

  var later = function() {
    previous = options.leading === false ? 0 : now();
    timeout = null;
    result = func.apply(context, args);
    if (!timeout) context = args = null;
  };

  var throttled = function() {
    var _now = now();
    if (!previous && options.leading === false) previous = _now;
    var remaining = wait - (_now - previous);
    context = this;
    args = arguments;
    if (remaining <= 0 || remaining > wait) {
      if (timeout) {
        clearTimeout(timeout);
        timeout = null;
      }
      previous = _now;
      result = func.apply(context, args);
      if (!timeout) context = args = null;
    } else if (!timeout && options.trailing !== false) {
      timeout = setTimeout(later, remaining);
    }
    return result;
  };

  throttled.cancel = function() {
    clearTimeout(timeout);
    previous = 0;
    timeout = context = args = null;
  };

  return throttled;
}

runOnce(() => {
    if (!config.hasOwnProperty('black')) config.black = [];
    if (!config.hasOwnProperty('white')) config.white = [];
    runMatch({
        black: config.black,
        white: config.white,
        full: true
    }).then(() => {
        (() => {
            function isDocument(d) {
                return d && d.nodeType === 9;
            }
            function getDocument(node) {
                if (isDocument(node)) {
                    return node;
                } else if (isDocument(node.ownerDocument)) {
                    return node.ownerDocument;

                } else if (isDocument(node.document)) {
                    return node.document;

                } else if (node.parentNode) {
                    return getDocument(node.parentNode);
                } else if (node.commonAncestorContainer) {
                    return getDocument(node.commonAncestorContainer);
                } else if (node.startContainer) {
                    return getDocument(node.startContainer);
                } else if (node.anchorNode) {
                    return getDocument(node.anchorNode);
                }
            }
            class DOMException {
                constructor(message, name) {
                    this.message = message;
                    this.name = name;
                    this.stack = (new Error()).stack;
                }
            }
            DOMException.prototype = new Error();
            DOMException.prototype.toString = function () {
                return `${this.name}: ${this.message}`
            };
            const FIRST_ORDERED_NODE_TYPE = 9;
            const HTML_NAMESPACE = 'http://www.w3.org/1999/xhtml';
            window.sXPath = {};
            window.sXPath.fromNode = (node, root = null) => {
                if (node === undefined) {
                    throw new Error('missing required parameter "node"')
                }
                root = root || getDocument(node);
                let path = '/';
                while (node !== root) {
                    if (!node) {
                        let message = 'The supplied node is not contained by the root node.';
                        let name = 'InvalidNodeTypeError';
                        throw new DOMException(message, name)
                    }
                    path = `/${nodeName(node)}[${nodePosition(node)}]${path}`;
                    node = node.parentNode;
                }
                return path.replace(/\/$/, '')
            };
            window.sXPath.toNode = (path, root, resolver = null) => {
                if (path === undefined) {
                    throw new Error('missing required parameter "path"')
                }
                if (root === undefined) {
                    throw new Error('missing required parameter "root"')
                }
                let document = getDocument(root);
                if (root !== document) path = path.replace(/^\//, './');
                let documentElement = document.documentElement;
                if (resolver === null && documentElement.lookupNamespaceURI) {
                    let defaultNS = documentElement.lookupNamespaceURI(null) || HTML_NAMESPACE;
                    resolver = (prefix) => {
                        let ns = { '_default_': defaultNS };
                        return ns[prefix] || documentElement.lookupNamespaceURI(prefix)
                    };
                }
                return resolve(path, root, resolver)
            };
            function nodeName(node) {
                switch (node.nodeName) {
                    case '#text': return 'text()'
                    case '#comment': return 'comment()'
                    case '#cdata-section': return 'cdata-section()'
                    default: return node.nodeName.toLowerCase()
                }
            }
            function nodePosition(node) {
                let name = node.nodeName;
                let position = 1;
                while ((node = node.previousSibling)) {
                    if (node.nodeName === name) position += 1;
                }
                return position
            }
            function resolve(path, root, resolver) {
                try {
                    let nspath = path.replace(/\/(?!\.)([^\/:\(]+)(?=\/|$)/g, '/_default_:$1');
                    return platformResolve(nspath, root, resolver)
                } catch (err) {
                    return fallbackResolve(path, root)
                }
            }
            function fallbackResolve(path, root) {
                let steps = path.split("/");
                let node = root;
                while (node) {
                    let step = steps.shift();
                    if (step === undefined) break
                    if (step === '.') continue
                    let [name, position] = step.split(/[\[\]]/);
                    name = name.replace('_default_:', '');
                    position = position ? parseInt(position) : 1;
                    node = findChild(node, name, position);
                }
                return node
            }
            function platformResolve(path, root, resolver) {
                let document = getDocument(root);
                let r = document.evaluate(path, root, resolver, FIRST_ORDERED_NODE_TYPE, null);
                return r.singleNodeValue
            }
            function findChild(node, name, position) {
                for (node = node.firstChild; node; node = node.nextSibling) {
                    if (nodeName(node) === name && --position === 0) break
                }
                return node
            }

            let urlChangeFn = null;
            history.pushState = (f => function pushState() {
                var ret = f.apply(this, arguments);
                window.dispatchEvent(new Event('pushstate'));
                window.dispatchEvent(new Event('urlchange'));
                return ret;
            })(history.pushState);
            history.replaceState = (f => function replaceState() {
                var ret = f.apply(this, arguments);
                window.dispatchEvent(new Event('replacestate'));
                window.dispatchEvent(new Event('urlchange'));
                return ret;
            })(history.replaceState);
            window.addEventListener('popstate', () => {
                window.dispatchEvent(new Event('urlchange'));
            });
            Object.defineProperty(window, 'onurlchange', {
                get() { return urlChangeFn; },
                set(fn) {
                    if (typeof fn === 'function') {
                        urlChangeFn = fn;
                        window.addEventListener('urlchange', urlChangeFn);
                    } else {
                        window.removeEventListener('urlchange', urlChangeFn);
                        urlChangeFn = null;
                    }
                },
            });
        })();
        runAt('document-end', () => {
            const stor = window.localStorage,
                boxkey = 'lemonScrollBox';
            let boxobj = null, box = null, boxel = null;
            function getScrollBox(e) {
                boxel = e.target;
                let pageid = location.href;
                if (boxel.scrollTop === undefined) boxel = document.documentElement;
                try {
                    box = window.sXPath.fromNode(boxel, document.documentElement);
                } catch (e) {
                    box = '.';
                }
                if (!boxobj) boxobj = {};
                boxobj[pageid] =
                {
                    box: box,
                    pos: boxel.scrollTop,
                    class: boxel.className,
                    id: boxel.id
                };
                stor.setItem(
                    boxkey,
                    JSON.stringify(boxobj)
                );
            }
            function startNewRecord() {
                toast('开始记录滚动', config.toast);
                document.addEventListener('scroll', throttle(getScrollBox, 300), true);
            }
            function scanPage() {
                boxobj = JSON.parse(stor.getItem(boxkey));
                let pageid = location.href;
                if (boxobj[pageid]) {
                    runNeed(
                        () => {
                            boxel = (boxobj[pageid].box === '') ?
                                document.documentElement : window.sXPath.toNode(
                                    boxobj[pageid].box,
                                    document.documentElement
                                );
                            if (boxel &&
                                boxel.id === boxobj[pageid].id &&
                                boxel.className === boxobj[pageid].class &&
                                boxel.scrollHeight > window.innerHeight) {
                                return true;
                            } else return false;
                        },
                        () => {
                            setTimeout(() => {
                                boxel.scrollTop = boxobj[pageid].pos;
                            }, config.out);
                        }
                    );
                    document.addEventListener('scroll', throttle(getScrollBox, 300), true);
                } else startNewRecord();
            }
            if (stor.hasOwnProperty(boxkey)) {
                window.onurlchange = scanPage;
                window.onhashchange = scanPage;
                scanPage();
            } else {
                startNewRecord();
            }
        });
    });
});

})();