学习通/MOOC等 隐藏答案 Hide Answer

添加一个切换答案按钮,点击可显示/隐藏答案

// ==UserScript==
// @name         学习通/MOOC等 隐藏答案 Hide Answer
// @namespace    https://github.com/lcandy2/user.js/tree/main/generics/hide-answer
// @version      2.3.2
// @author       甜檸Cirtron (lcandy2)
// @description  添加一个切换答案按钮,点击可显示/隐藏答案
// @license      AGPL-3.0-or-later
// @copyright    lcandy2 All Rights Reserved
// @homepage     https://greasyfork.org/scripts/469779
// @homepageURL  https://greasyfork.org/scripts/469779
// @match        *://mooc1.chaoxing.com/mooc*
// @match        *://mooc1.chaoxing.com/exam*
// @require      https://registry.npmmirror.com/vue/3.4.27/files/dist/vue.global.prod.js
// @require      data:application/javascript,%3Bwindow.Vue%3DVue%3B
// ==/UserScript==

(function (vue) {
  'use strict';

  const chaoxingMooc = "mooc1.chaoxing.com/mooc";
  const chaoxingExam = "mooc1.chaoxing.com/exam";
  const UrlDetection = () => {
    const url = window.location.href;
    if (url.includes(chaoxingMooc) || url.includes(chaoxingExam)) {
      if (url.includes("work/view") || url.includes("test/reVersionPaperMarkContentNew")) {
        return "chaoxing-mooc";
      }
    }
  };
  var oe = "M8.27,3L3,8.27V15.73L8.27,21H15.73C17.5,19.24 21,15.73 21,15.73V8.27L15.73,3M9.1,5H14.9L19,9.1V14.9L14.9,19H9.1L5,14.9V9.1M11,15H13V17H11V15M11,7H13V13H11V7", ae = "M12,2L1,21H23M12,6L19.53,19H4.47M11,10V14H13V10M11,16V18H13V16", re = "M12 2C6.5 2 2 6.5 2 12S6.5 22 12 22 22 17.5 22 12 17.5 2 12 2M10 17L5 12L6.41 10.59L10 14.17L17.59 6.58L19 8L10 17Z", ne = "M19,6.41L17.59,5L12,10.59L6.41,5L5,6.41L10.59,12L5,17.59L6.41,19L12,13.41L17.59,19L19,17.59L13.41,12L19,6.41Z", ie = "M11,9H13V7H11M12,20C7.59,20 4,16.41 4,12C4,7.59 7.59,4 12,4C16.41,4 20,7.59 20,12C20,16.41 16.41,20 12,20M12,2A10,10 0 0,0 2,12A10,10 0 0,0 12,22A10,10 0 0,0 22,12A10,10 0 0,0 12,2M11,17H13V11H11V17Z";
  const le = {
    transform: "rotate(var(--r, 0deg)) scale(var(--sx, 1), var(--sy, 1))"
  }, E = {
    fill: "currentColor"
  }, z = {
    mdi: {
      size: 24,
      viewbox: "0 0 24 24"
    },
    "simple-icons": {
      size: 24,
      viewbox: "0 0 24 24"
    },
    default: {
      size: 0,
      viewbox: "0 0 0 0"
    }
  }, N = {
    name: "icon",
    props: {
      type: {
        type: String,
        default: "mdi"
      },
      faIcon: {
        type: Object,
        default: null
      },
      path: {
        type: [String, Object, Array]
      },
      size: {
        type: [Number, String],
        default: 24
      },
      viewbox: String,
      flip: {
        type: String,
        validator: (t) => ["horizontal", "vertical", "both"].includes(t)
      },
      rotate: {
        type: [Number, String],
        default: 0
      }
    },
    setup(t) {
      if (!t.path && !t.faIcon)
        return console.warn("vue3-icon requires either a 'path' or an 'fa-icon' property"), () => vue.h("div");
      const s = vue.computed(() => {
        var d;
        return ((d = t.faIcon) == null ? void 0 : d.prefix) || t.type;
      }), o = vue.computed(() => parseInt(t.rotate, 10)), e = vue.computed(() => z[s.value] || z.default), r = vue.computed(() => parseInt(t.size, 10) || e.value.size), i = vue.computed(() => t.faIcon ? `0 0 ${t.faIcon.icon[0]} ${t.faIcon.icon[1]}` : false), l = vue.computed(() => i.value || t.viewbox || e.value.viewbox), f = vue.computed(() => ({
        ...le,
        "--sx": ["both", "horizontal"].includes(t.flip) ? "-1" : "1",
        "--sy": ["both", "vertical"].includes(t.flip) ? "-1" : "1",
        "--r": isNaN(o.value) ? o.value : o.value + "deg"
      })), m = vue.computed(() => {
        var d;
        return t.faIcon ? (d = t.faIcon) == null ? void 0 : d.icon[4] : t.type === "simple-icons" && typeof t.path == "object" ? t.path.path : t.path;
      }), p = vue.computed(() => s.value === "fad" ? (console.warn("vue3-icon does not currently support Duotone FontAwesome icons"), vue.h("path")) : Array.isArray(t.path) ? vue.h(
        "g",
        { style: { ...E } },
        t.path.map((d) => typeof d == "string" ? vue.h("path", { d }) : vue.h("path", { ...d }))
      ) : vue.h("path", { d: m.value, style: { ...E } }));
      return () => vue.h(
        "svg",
        {
          style: f.value,
          class: ["vue3-icon"],
          width: r.value,
          height: r.value,
          viewBox: l.value
        },
        [p.value]
      );
    }
  }, ue = { class: "vue3-snackbar-message-wrapper" }, ce = {
    key: 0,
    class: "vue3-snackbar-message-icon"
  }, de = { class: "vue3-snackbar-message-content" }, fe = {
    key: 0,
    class: "vue3-snackbar-message-badge"
  }, me = { class: "vue3-snackbar-message-title" }, pe = {
    key: 0,
    class: "vue3-snackbar-message-additional"
  }, ge = /* @__PURE__ */ vue.createElementVNode("div", { class: "spacer" }, null, -1), ve = { class: "vue3-snackbar-message-close" }, be = {
    __name: "Vue3SnackbarMessage",
    props: {
      borderClass: {
        type: String,
        default: ""
      },
      message: {
        type: Object,
        default: () => ({})
      },
      messageClass: {
        type: String,
        default: ""
      },
      dense: {
        type: Boolean,
        default: false
      }
    },
    emits: ["dismiss"],
    setup(t, { emit: s }) {
      const o = s, e = t;
      let r = null, i = null, l = vue.ref(false);
      const f = () => {
        const a = !e.message.duration && !e.message.dismissible ? 4e3 : e.message.duration;
        r = setTimeout(p, a);
      };
      vue.onMounted(() => {
        f();
      }), vue.watch(
        () => e.message.count,
        (a) => {
          if (a === 1)
            return false;
          clearTimeout(r), clearTimeout(i), i = setTimeout(() => {
            l.value = false;
          }, 1e3), l.value = true, f();
        }
      );
      const m = () => {
        r && clearTimeout(r), p();
      }, p = () => {
        o("dismiss", e.message);
      }, d = {
        success: {
          path: re
        },
        info: {
          path: ie
        },
        warning: {
          path: ae
        },
        error: {
          path: oe
        }
      }, n = vue.computed(() => {
        const a = d[e.message.type];
        return a ? (a.type = "mdi", a) : e.message.icon && typeof e.message.icon == "object" ? e.message.icon : {
          path: "",
          type: "default"
        };
      });
      return (a, u) => (vue.openBlock(), vue.createElementBlock("article", {
        class: vue.normalizeClass(["vue3-snackbar-message", [
          e.message.type || "custom",
          e.messageClass,
          e.borderClass,
          {
            "has-background": e.message.background,
            "has-border": e.borderClass,
            "is-dense": e.dense,
            "shake-baby-shake": vue.unref(l)
          }
        ]]),
        style: vue.normalizeStyle({
          "--message-background": e.message.background,
          "--message-text-color": e.message.textColor,
          "--message-icon-color": e.message.iconColor
        })
      }, [
        vue.renderSlot(a.$slots, "message-inner", {
          message: e.message
        }, () => [
          vue.createElementVNode("div", ue, [
            n.value ? (vue.openBlock(), vue.createElementBlock("div", ce, [
              vue.renderSlot(a.$slots, "message-icon", {
                message: e.message,
                icon: n.value
              }, () => [
                vue.createVNode(vue.unref(N), vue.mergeProps(n.value, { role: "img" }), null, 16)
              ])
            ])) : vue.createCommentVNode("", true),
            vue.createElementVNode("div", de, [
              vue.renderSlot(a.$slots, "message-badge", {
                message: e.message,
                count: e.message.count
              }, () => [
                e.message.count > 1 ? (vue.openBlock(), vue.createElementBlock("div", fe, vue.toDisplayString(e.message.count), 1)) : vue.createCommentVNode("", true)
              ]),
              vue.renderSlot(a.$slots, "message-content", {
                message: e.message,
                title: e.message.title,
                text: e.message.text
              }, () => [
                vue.createElementVNode("div", me, vue.toDisplayString(e.message.title || e.message.text), 1),
                e.message.title && e.message.text ? (vue.openBlock(), vue.createElementBlock("div", pe, vue.toDisplayString(e.message.text), 1)) : vue.createCommentVNode("", true)
              ])
            ]),
            ge,
            vue.createElementVNode("div", ve, [
              vue.renderSlot(a.$slots, "message-close-icon", {
                message: e.message,
                isDimissible: e.message.dismissible,
                isDismissible: e.message.dismissible,
                dismiss: m
              }, () => [
                e.message.dismissible !== false ? (vue.openBlock(), vue.createElementBlock("button", {
                  key: 0,
                  onClick: m
                }, [
                  vue.createVNode(vue.unref(N), {
                    type: "mdi",
                    path: vue.unref(ne)
                  }, null, 8, ["path"])
                ])) : vue.createCommentVNode("", true)
              ])
            ])
          ])
        ])
      ], 6));
    }
  }, ye = typeof window < "u" ? HTMLElement : Object, he = {
    /* ******************************************
     * LOCATION PROPS
     ****************************************** */
    top: {
      type: Boolean,
      default: false
    },
    bottom: {
      type: Boolean,
      default: false
    },
    left: {
      type: Boolean,
      default: false
    },
    right: {
      type: Boolean,
      default: false
    },
    /* ******************************************
     * COLOUR PROPS
     ****************************************** */
    success: {
      type: String,
      default: "#4caf50"
    },
    error: {
      type: String,
      default: "#ff5252"
    },
    warning: {
      type: String,
      default: "#fb8c00"
    },
    info: {
      type: String,
      default: "#2196f3"
    },
    messageTextColor: {
      type: String,
      default: "#fff"
    },
    messageIconColor: {
      type: String,
      default: "currentColor"
    },
    /* ******************************************
     * OTHER PROPS
     ****************************************** */
    attach: {
      type: [String, ye],
      default: "body"
    },
    border: {
      type: String,
      default: "",
      validator: (t) => ["top", "bottom", "left", "right", ""].includes(t)
    },
    backgroundOpacity: {
      type: [String, Number],
      default: 0.12,
      validator: (t) => !isNaN(parseFloat(t)) && isFinite(t)
    },
    backgroundColor: {
      type: String,
      default: "currentColor"
    },
    baseBackgroundColor: {
      type: String,
      default: "#fff"
    },
    duration: {
      type: [Number, String],
      default: null
    },
    messageClass: {
      type: String
    },
    zIndex: {
      type: Number,
      default: 1e4
    },
    dense: {
      type: Boolean,
      default: false
    },
    reverse: {
      type: Boolean,
      default: false
    },
    groups: {
      type: Boolean,
      default: false
    },
    shadow: {
      type: Boolean,
      default: false
    }
  };
  function Se(t) {
    return t && t.__esModule && Object.prototype.hasOwnProperty.call(t, "default") ? t.default : t;
  }
  var V = { exports: {} };
  function B() {
  }
  B.prototype = {
    on: function(t, s, o) {
      var e = this.e || (this.e = {});
      return (e[t] || (e[t] = [])).push({
        fn: s,
        ctx: o
      }), this;
    },
    once: function(t, s, o) {
      var e = this;
      function r() {
        e.off(t, r), s.apply(o, arguments);
      }
      return r._ = s, this.on(t, r, o);
    },
    emit: function(t) {
      var s = [].slice.call(arguments, 1), o = ((this.e || (this.e = {}))[t] || []).slice(), e = 0, r = o.length;
      for (e; e < r; e++)
        o[e].fn.apply(o[e].ctx, s);
      return this;
    },
    off: function(t, s) {
      var o = this.e || (this.e = {}), e = o[t], r = [];
      if (e && s)
        for (var i = 0, l = e.length; i < l; i++)
          e[i].fn !== s && e[i].fn._ !== s && r.push(e[i]);
      return r.length ? o[t] = r : delete o[t], this;
    }
  };
  V.exports = B;
  V.exports.TinyEmitter = B;
  var ke = V.exports, Ce = ke, we = new Ce();
  const _ = /* @__PURE__ */ Se(we), C = {
    $on: (...t) => _.on(...t),
    $once: (...t) => _.once(...t),
    $off: (...t) => _.off(...t),
    $emit: (...t) => _.emit(...t)
  }, b = vue.ref([]), W = Symbol();
  function Ee() {
    const t = vue.inject(W);
    if (!t)
      throw new Error("No Snackbar provided!");
    return t;
  }
  const ze = {
    install: (t, s = {}) => {
      const { disableGlobals: o = false } = s, e = {
        add: (r) => {
          C.$emit("add", r);
        },
        clear: () => {
          C.$emit("clear");
        }
      };
      o !== true && (t.config.globalProperties.$snackbar = e, typeof window < "u" && (window.$snackbar = e)), t.provide(W, e);
    }
  };
  function _e(t) {
    return vue.getCurrentScope() ? (vue.onScopeDispose(t), true) : false;
  }
  function P(t) {
    return typeof t == "function" ? t() : vue.unref(t);
  }
  const Z = typeof window < "u" && typeof document < "u";
  typeof WorkerGlobalScope < "u" && globalThis instanceof WorkerGlobalScope;
  const Me = (t) => t != null;
  function Le(t) {
    return vue.getCurrentInstance();
  }
  function $e(t, s = true, o) {
    Le() ? vue.onMounted(t, o) : s ? t() : vue.nextTick(t);
  }
  function xe(t) {
    var s;
    const o = P(t);
    return (s = o == null ? void 0 : o.$el) != null ? s : o;
  }
  const Ve = Z ? window : void 0, Be = Z ? window.document : void 0;
  function Oe() {
    const t = vue.ref(false), s = vue.getCurrentInstance();
    return s && vue.onMounted(() => {
      t.value = true;
    }, s), t;
  }
  function Te(t) {
    const s = Oe();
    return vue.computed(() => (s.value, !!t()));
  }
  function Ae(t, s, o = {}) {
    const { window: e = Ve, ...r } = o;
    let i;
    const l = Te(() => e && "MutationObserver" in e), f = () => {
      i && (i.disconnect(), i = void 0);
    }, m = vue.computed(() => {
      const a = P(t), u = (Array.isArray(a) ? a : [a]).map(xe).filter(Me);
      return new Set(u);
    }), p = vue.watch(
      () => m.value,
      (a) => {
        f(), l.value && a.size && (i = new MutationObserver(s), a.forEach((u) => i.observe(u, r)));
      },
      { immediate: true, flush: "post" }
    ), d = () => i == null ? void 0 : i.takeRecords(), n = () => {
      f(), p();
    };
    return _e(n), {
      isSupported: l,
      stop: n,
      takeRecords: d
    };
  }
  function He(t = {}) {
    const {
      document: s = Be,
      selector: o = "html",
      observe: e = false,
      initialValue: r = "ltr"
    } = t;
    function i() {
      var f, m;
      return (m = (f = s == null ? void 0 : s.querySelector(o)) == null ? void 0 : f.getAttribute("dir")) != null ? m : r;
    }
    const l = vue.ref(i());
    return $e(() => l.value = i()), e && s && Ae(
      s.querySelector(o),
      () => l.value = i(),
      { attributes: true }
    ), vue.computed({
      get() {
        return l.value;
      },
      set(f) {
        var m, p;
        l.value = f, s && (l.value ? (m = s.querySelector(o)) == null || m.setAttribute("dir", l.value) : (p = s.querySelector(o)) == null || p.removeAttribute("dir"));
      }
    });
  }
  const Ne = {
    __name: "Vue3Snackbar",
    props: { ...he },
    emits: ["added", "dismissed", "removed", "cleared"],
    setup(t, { emit: s }) {
      const o = He(), e = t, r = s, i = vue.computed(() => ({
        "is-top": e.top,
        "is-bottom": e.top === false && e.bottom,
        "is-left": e.left,
        "is-right": e.left === false && e.right,
        "is-middle": e.top === false && e.bottom === false,
        "is-centre": e.left === false && e.right === false,
        "has-shadow": e.shadow,
        "is-rtl": o.value === "rtl"
      })), l = vue.computed(() => ({
        "--success-colour": e.success,
        "--error-colour": e.error,
        "--warning-colour": e.warning,
        "--info-colour": e.info,
        "--snackbar-zindex": e.zIndex,
        "--background-opacity": e.backgroundOpacity,
        "--background-color": e.backgroundColor,
        "--base-background-color": e.baseBackgroundColor,
        "--message-text-color": e.messageTextColor,
        "--message-icon-color": e.messageIconColor
      })), f = vue.computed(() => e.border ? `border-${e.border}` : ""), m = (n) => Math.abs(n.split("").reduce((a, u) => (a << 5) - a + u.charCodeAt(0) | 0, 0));
      let p = 1;
      C.$on("add", (n) => {
        r("added", n), n.group || (n.group = m(`${n.type}${n.title}${n.text}`).toString(16)), e.duration && !n.duration && n.duration !== 0 && (n.duration = +e.duration);
        const a = n.group && b.value.find((u) => u.group === n.group);
        if (e.groups === false || !a) {
          const u = {
            ...n,
            id: p,
            count: 1
          };
          e.reverse ? b.value.unshift(u) : b.value.push(u), p++;
        } else
          a.count++;
      }), C.$on("clear", () => {
        r("cleared"), b.value = [];
      }), vue.onUnmounted(() => {
        C.$off("add"), C.$off("clear");
      });
      const d = (n, a = false) => {
        r(a ? "dismissed" : "removed", n), b.value = b.value.filter((u) => u.id !== n.id);
      };
      return (n, a) => (vue.openBlock(), vue.createBlock(vue.Teleport, {
        to: e.attach
      }, [
        vue.createElementVNode("section", {
          id: "vue3-snackbar--container",
          class: vue.normalizeClass([[i.value], "vue3-snackbar"]),
          style: vue.normalizeStyle(l.value)
        }, [
          vue.createVNode(vue.TransitionGroup, {
            name: "vue3-snackbar-message",
            tag: "div"
          }, {
            default: vue.withCtx(() => [
              (vue.openBlock(true), vue.createElementBlock(vue.Fragment, null, vue.renderList(vue.unref(b), (u) => (vue.openBlock(), vue.createBlock(be, {
                key: u.id,
                message: u,
                "message-class": e.messageClass,
                dense: e.dense,
                "border-class": f.value,
                onDismiss: a[0] || (a[0] = (O) => d(O, true))
              }, vue.createSlots({ _: 2 }, [
                vue.renderList(n.$slots, (O, T) => ({
                  name: T,
                  fn: vue.withCtx((R) => [
                    vue.renderSlot(n.$slots, T, vue.mergeProps({ ref_for: true }, R))
                  ])
                }))
              ]), 1032, ["message", "message-class", "dense", "border-class"]))), 128))
            ]),
            _: 3
          })
        ], 6)
      ], 8, ["to"]));
    }
  };
  const _sfc_main = /* @__PURE__ */ vue.defineComponent({
    __name: "chaoxing-mooc",
    setup(__props) {
      const snackbar = Ee();
      const elements = Array.from(
        document.getElementsByClassName("mark_answer")
      );
      const isHide = vue.ref(false);
      const toggleHide = () => {
        const value = isHide.value;
        snackbar.clear();
        snackbar.add({
          type: value ? "info" : "success",
          title: value ? "答案已显示" : "答案已隐藏",
          text: '你还可以按下 "H" 键来切换答案的显示状态。'
        });
        elements.forEach((element) => {
          element.style.visibility = isHide.value ? "visible" : "hidden";
        });
        isHide.value = !isHide.value;
      };
      const keydownHandler = (event) => {
        if (event.key === "h" || event.key === "H") {
          toggleHide();
        }
      };
      vue.onMounted(() => {
        window.addEventListener("keydown", keydownHandler);
      });
      vue.onUnmounted(() => {
        window.removeEventListener("keydown", keydownHandler);
      });
      return (_ctx, _cache) => {
        return vue.openBlock(), vue.createElementBlock(vue.Fragment, null, [
          vue.createElementVNode("a", {
            href: "javascript:;",
            onClick: toggleHide,
            class: "fl",
            tabindex: "0",
            role: "button"
          }, vue.toDisplayString(isHide.value ? "显示答案 (H)" : "隐藏答案 (H)"), 1),
          vue.createVNode(vue.unref(Ne), {
            top: "",
            duration: 0,
            border: "left",
            shadow: "",
            dense: "",
            reverse: ""
          })
        ], 64);
      };
    }
  });
  const appendChaoxingMoocButton = () => {
    const app = vue.createApp(_sfc_main);
    app.use(ze);
    const targetElement = document.querySelector(".subNav");
    if (targetElement) {
      const css = document.createElement("link");
      css.rel = "stylesheet";
      css.href = "https://unpkg.zhimg.com/vue3-snackbar@2.2.2/dist/style.css";
      document.head.appendChild(css);
      const toggleButton = document.createElement("div");
      toggleButton.className = "sub-button fr";
      toggleButton.id = "submitFocus";
      toggleButton.setAttribute("tabIndex", "-1");
      targetElement.appendChild(toggleButton);
      app.mount(toggleButton);
    }
  };
  const urlDetection = UrlDetection();
  if (urlDetection === "chaoxing-mooc") {
    appendChaoxingMoocButton();
  }

})(Vue);