学习通/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);