fill the input and select of form as last time inputed automatically

This script supports SPA like vue。

질문, 리뷰하거나, 이 스크립트를 신고하세요.
// ==UserScript==
// @name            fill the input and select of form as last time inputed automatically
// @name:zh-CN 使用上次输入的值自动填写表格
// @namespace  http://tampermonkey.net/
// @description  This script supports SPA like vue。
// @description:zh-CN  对所有网站生效,支持SPA(比如vue)动态插入的input和select。比如可以自动填写用户名和密码,自动点击同意协议。浏览器需要安装Tampermonkey或者Greasemonkey扩展,安卓手机浏览器推荐Yandex或者Kiwi浏览器。
// @include         http://*
// @include         https://*
// @exclude         https://www.baidu.com/*
// @exclude         https://www.google.com*
// @grant           GM_setValue
// @grant           GM_getValue
// @author          yechenyin
// @license         MIT
// @version         1.0.3
// ==/UserScript==
//闭包里的方法的用法和jquery同命名方法一致
(function () {
  var jQuery = function (selector) {
    return new jQuery.fn.init(selector)
  }
  jQuery.fn = jQuery.prototype = {
    length: 0,
    selector: '',
    init: function (elementOrSelector) {
      var nodes = []
      if (typeof (elementOrSelector) == 'string') {
        this.selector = elementOrSelector
        nodes = document.querySelectorAll(this.selector)
      } else if (elementOrSelector instanceof NodeList) {
        nodes = elementOrSelector
      } else if (elementOrSelector instanceof Element) {
        nodes = [elementOrSelector]
      }
      this.length = nodes.length
      for (var i = 0; i < nodes.length; i++) {
        this[i] = nodes[i]
      }
    },
    each: function (callback) {
      for (var i = 0; i < this.length; i++) {
        callback.call(this[i])
      }
    },
    on: function (event, callback) {
      this.each(function () {
        this.addEventListener(event, callback)
      })
    },
    text: function (string) {
      var i = 0
      if (string !== undefined) {
        for (i = 0; i < this.length; i++) {
          this[i].innerText = string
        }
      } else {
        return this[0].innerText
      }
    },
    val: function (value) {
      if (value === undefined) {
        var ret
        if (this[0].type == 'checkbox')
          ret = this[0].checked
        else if (this[0].type == 'radio' || this[0].type == 'text' || this[0].type == 'password' || this[0].tagName == 'select')
          ret = this[0].value
        return ret
      } else {
        for (var i = 0; i < this.length; i++) {
          if (this[i].type == 'checkbox' && Boolean(value))
            this[i].click()
          else if (this[i].type == 'radio')
            this[i].checked = this[i].value == value
          else if (this[i].type == 'text')
            this[i].value = value
          else if (this[i].type == 'password')
            this[i].value = value
          else if (this[i].tagName == 'select')
            this[i].value = value
          this[i].dispatchEvent(new Event('input', { bubbles: true }))
        }
      }
    },
    attr: function (attribute, value) {
      if (value === undefined) {
        return this[0].getAttribute(attribute)
      } else {
        this.each(function () {
          this.setAttribute(attribute, value)
        })
      }
    },
    click: function () {
      this[0].click()
    },
    find: function (selector) {
      var j = 0
      var result = []
      for (var i = 0; i < this.length; i++) {
        if (this[i].querySelectorAll(selector).length) {
        }
      }
    },
    append: function (html) {
      for (var i = 0; i < this.length; i++) {
        this[i].innerHTML += html
      }
    },
  }

  jQuery.fn.init.prototype = jQuery.fn
  if (!window.jQuery) window.jQuery = window.$ = jQuery
})()

//每当符合选择器规则的元素插入到页面中时,唤起callback方法。如果trigger_once为true,只唤起一次
jQuery.fn.inserted = function (callback, trigger_once = false) {
  var selector = this.selector;
  if ($(selector).length > 0) {
    //log($(selector).length + ' ' + selector + " is loaded at begin");
    callback.call($(selector));
  }
  var finished = false
  var recall = function (mutationsList, observer) {
    for (var i = 0; i < mutationsList.length; i++) {
      //log(mutationsList[i].target)
      if (mutationsList[i].addedNodes) {
        for (var j = 0; j < mutationsList[i].addedNodes.length; j++) {
          var element = mutationsList[i].addedNodes[j]
          if (!(trigger_once && finished) && element instanceof Element && element.querySelectorAll(selector).length) {
            var container = ''
            if (element.id)
              container = '#' + element.id
            else if (element.className)
              container = '.' + element.className
            else
              container = element.outerHtml
            //log(container + ' which contains ' + selector + ' is loaded')
            if (trigger_once) {
              observer.disconnect()
              finished = true
            }
            callback.call($(element.querySelectorAll(selector)))
          }
        }
      }
    }
  };
  var MutationObserver = window.MutationObserver || window.WebKitMutationObserver || window.MozMutationObserver
  if (MutationObserver) {
    var observer = new MutationObserver(recall)
    observer.observe(document.body, {
      childList: true,
      subtree: true
    })
  }
}

//当符合选择器规则的元素插入到页面中时,调用一次callback方法,之后不会再唤起
jQuery.fn.loaded = function (callback) {
  if (callback)
    this.inserted(callback, true);
}

//每当元素类名变化时,唤起callback方法
jQuery.fn.onClassChanged = function (callback) {
  var recall = function (mutationsList, observer) {
    for (var i = 0; i < mutationsList.length; i++) {
      //log(mutationsList[i].target)
      if (mutationsList[i].type === 'attributes' && mutationsList[i].attributeName === 'class') {
        //callback.call($(mutationsList[i].target))
        callback.call(mutationsList[i].target)
      }
    }
  };
  var MutationObserver = window.MutationObserver || window.WebKitMutationObserver || window.MozMutationObserver
  if (MutationObserver) {
    var observer = new MutationObserver(recall)
    var list = document.querySelectorAll(this.selector)
    for (var i = 0; i < list.length; i++) {
        observer.observe(list[i], { attributes: true })
    }
  }
}

//在元素加载完成后经过delay(单位毫秒)延迟后点击元素
jQuery.fn.clickAfterLoaded = function (delay = 0) {
  this.loaded(function () {
    setTimeout(function () {
      this[0].click()
    }.bind(this), delay)
  })
}
//在元素加载完成后经过delay(单位毫秒)延迟后设置输入值
jQuery.fn.setAfterLoaded = function (value, delay = 0) {
  this.loaded(function () {
    setTimeout(function () {
      this.val(value)
    }.bind(this), delay)
  })
}

//替代得到http成功回应后的回调
$.replaceResponseCallback = function (callback, continueOriginalCallback = false) {
  var open = XMLHttpRequest.prototype.open
  XMLHttpRequest.prototype.open = function () {
    this.addEventListener(
      'readystatechange',
      function () {
        if (
          this.readyState == 4 &&
          this.response
        ) {
          callback.call(this)
        }
      },
      false
    )
    if (continueOriginalCallback)
      open.apply(this, arguments)
    XMLHttpRequest.prototype.open = open
  }
}
//url改变后唤起callback,支持监测SPA的#后面的字符串变化
$.onUrlChange = function (callback) {
  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'))
  })
  window.addEventListener('urlchange', function () {
    callback()
  })
}

jQuery.fn.saveChangedValue = function () {
  var that = this
  that.on('change', function () {
    //log(that)
    var href = 'inputed_' + location.href.replace(/\?.*/, '')
    var inputs = ''
    if (typeof GM_getValue === 'undefined')
      inputs = localStorage[href]
    else if (GM_getValue(href)) {
      inputs = GM_getValue(href)
    }
    if (inputs)
      inputs = JSON.parse(inputs)
    else
      inputs = {}
    //log(this.constructor.name)
    //log(Object.keys(this))
    var name = ''
    for (var i = 0; i < that.length; i++) {
        if (this == that[i])
            name = i
    }
    inputs[name] = $(this).val()
    log(name, inputs[name])
    if (typeof GM_setValue === 'undefined')
      localStorage[href] = JSON.stringify(inputs)
    else
      GM_setValue(href, JSON.stringify(inputs))
    //log(GM_getValue(href))
  })
}
jQuery.fn.recoverSavedValue = function () {
  this.inserted(function () {
    var that = this
    var href = 'inputed_' + location.href.replace(/\?.*/, '')
    var inputs = ''
    if (typeof GM_getValue === 'undefined')
      inputs = localStorage[href]
    else if (GM_getValue(href)) {
      inputs = GM_getValue(href)
    }
    if (inputs)
      inputs = JSON.parse(inputs)
    else
      inputs = {}
    log(inputs)
    if (Object.keys(inputs).length) {
      this.each(function () {
        var name = ''
        for (var i = 0; i < that.length; i++) {
            if (this == that[i])
                name = i
        }
        log(name)
        if (inputs.hasOwnProperty(name)) {
          $(this).val(inputs[name])
        }
      })
    }
  })
}

function log(info) {
    var printInConsole = false;
    if (printInConsole) {
        console.log(info)
    }
}

window.onload = function () {
  $('input, select').recoverSavedValue()
  $('input, select').inserted(function () {
    $('input, select').saveChangedValue()
    $('.el-select-dropdown__item').onClassChanged(function () {
        if (this.classList.contains('selected')) {
            log(this.innerText)
        }
    })
  })
}