科举小抄

科举小抄 - 阿里云大学“科考”辅助工具

目前為 2020-09-22 提交的版本,檢視 最新版本

您需要先安裝使用者腳本管理器擴展,如 TampermonkeyGreasemonkeyViolentmonkey 之後才能安裝該腳本。

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

您需要先安裝使用者腳本管理器擴充功能,如 TampermonkeyViolentmonkey 後才能安裝該腳本。

您需要先安裝使用者腳本管理器擴充功能,如 TampermonkeyUserscripts 後才能安裝該腳本。

你需要先安裝一款使用者腳本管理器擴展,比如 Tampermonkey,才能安裝此腳本

您需要先安裝使用者腳本管理器擴充功能後才能安裝該腳本。

(我已經安裝了使用者腳本管理器,讓我安裝!)

你需要先安裝一款使用者樣式管理器擴展,比如 Stylus,才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展,比如 Stylus,才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展,比如 Stylus,才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展後才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展後才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展後才能安裝此樣式

(我已經安裝了使用者樣式管理器,讓我安裝!)

// ==UserScript==
// @name         科举小抄
// @namespace    https://github.com/fuckKeju/fuckKeju
// @version      0.0.4
// @description  科举小抄 - 阿里云大学“科考”辅助工具
// @author       fuckKeju
// @match        *.developer.aliyun.com/*
// @run-at       document-start
// @grant        unsafeWindow
// ==/UserScript==

/* 题库数据 */
var customQuestionsDatabase = []
var useCustomQuestionsDatabase = false

var hasInit = false

function fuckKeju () {
  if (hasInit) { return false }
  console.log('科举小抄 init suc')
  hasInit = true

  const subjectTitle = document.querySelector('.yq-main-examination .top-info h2.title-content').innerText
  const isExamMode = document.querySelector('.yq-main-examination .time-info')

  let questionsDatabase = []
  try {
    questionsDatabase = JSON.parse(localStorage.getItem(subjectTitle) || '[]')
  } catch (e) {
    questionsDatabase = []
  }

  /* 使用预置数据,而非定义的数据 */
  if (useCustomQuestionsDatabase) {
    questionsDatabase = customQuestionsDatabase
  }

  var result = []
  var items = document.querySelectorAll('.question-panel .question-item')
  if (items) {
    items.forEach(item => {
      const type = item.querySelector('.q-title .q-tag').innerText.trim()
      const title = item.querySelector('.q-title .q-t-text').innerText.trim().replace(/\s+/gi, ' ').replace(/\?{2,}/gi, ' ')
      const answerList = []
      const answerListEl = item.querySelectorAll('.q-option .answer-text')
      console.log(answerListEl)
      answerListEl.forEach(answerEl => {
        let answer = answerEl.innerText.trim()
        const checkedEl = answerEl.parentNode.querySelector('input')
        if (checkedEl && checkedEl.checked) {
          answer += '  --checked'
        }
        answerList.push(answer)
      })

      const data = {
        title,
        type,
        answerList
      }

      const pointEl = item.querySelector('.e-point .p-detail')
      if (pointEl) {
        data.point = '相关知识点:' + pointEl.innerText.trim()
      } else {
        data.point = '未匹配到任何相关知识点'
      }

      const rightAnswerEl = item.querySelector('.right-answer')
      if (rightAnswerEl) {
        data.rightAnswer = rightAnswerEl.innerText.trim() || '答案正确'
      } else {
        if (isExamMode) {
          data.rightAnswer = '答案未知'
        } else {
          data.rightAnswer = '答案正确'
        }
      }

      result.push(data)

      /* 判断当前题目是否已经存在 */
      let hasInDatabase = false

      /* 提取相关问题 */
      const titleTxt = title.replace(/^\d+\./, '')
      let relatedQuestions = []

      function relatedQuestionsHandler (item) {
        const itemTitle = item.title.replace(/\s+/gi, ' ').replace(/\?{2,}/gi, ' ')
        if (itemTitle.includes(titleTxt)) {
          const answerA = answerList[0].replace(/^[ABCDEFG]\.\s/, '').replace(/\s+--checked/, '')
          for (let i = 0; i < item.answerList.length; i++) {
            const answerTxt = item.answerList[i]
            if (answerTxt.includes(answerA)) {
              relatedQuestions.push(item)
              break
            }
          }
        }

        if (itemTitle === title) {
          hasInDatabase = true
        }
      }

      questionsDatabase.forEach(list => {
        if (Array.isArray(list)) {
          list.forEach(item => {
            relatedQuestionsHandler(item)
          })
        } else {
          relatedQuestionsHandler(list)
        }
      })

      /* 查找是否存在答对的历史记录,优先显示答对的数据 */
      if (relatedQuestions.length > 1) {
        const rightAnswerArr = []
        const wrongAnswerArr = []
        relatedQuestions.forEach(data => {
          if (data.rightAnswer === '答案正确') {
            rightAnswerArr.push(data)
          } else {
            wrongAnswerArr.push(data)
          }
        })
        relatedQuestions = rightAnswerArr.concat(wrongAnswerArr)
      }

      /* 收集题目数据 */
      if (!isExamMode && !hasInDatabase) {
        questionsDatabase.push(data)
      }

      const dd = document.createElement('dd')
      dd.setAttribute('class', 'relatedQuestions')
      dd.style.marginTop = '30px'
      dd.style.display = 'none'
      dd.style.border = '1px solid #ccc'
      dd.style.borderRadius = '5px'
      // dd.style.padding = '10px'
      // dd.style.backgroundColor = '#f9f9f9'

      if (item.querySelector('.relatedQuestions')) {
        item.removeChild(item.querySelector('.relatedQuestions'))
      }

      if (relatedQuestions.length) {
        const codeEl = document.createElement('pre')
        codeEl.style.border = 'none'
        codeEl.innerHTML = JSON.stringify(relatedQuestions, null, 2)
        dd.appendChild(codeEl)
        item.appendChild(dd)
      } else {
        dd.innerText = '暂无相关题目信息,先考几遍,然后查看考试结果再试试吧'
        item.appendChild(dd)
      }

      item.ondblclick = function (event) {
        const relatedQuestions = item.querySelector('.relatedQuestions')
        if (relatedQuestions) {
          if (relatedQuestions.style.display === 'none') {
            relatedQuestions.style.display = 'block'
            relatedQuestions.style.opacity = 0.4
            relatedQuestions.style.overflow = 'auto'
            relatedQuestions.style.maxHeight = '200px'
          } else {
            relatedQuestions.style.display = 'none'
          }
        }
      }
    })
  }

  localStorage.setItem(subjectTitle, JSON.stringify(questionsDatabase))

  /* 切换题目时候,隐藏小抄 */
  const switchDoms = document.querySelectorAll('.question-num span.item')
  const switchDoms02 = document.querySelectorAll('.e-opt-panel a')

  function hideRelatedQuestions () {
    const relatedQuestionsEls = document.querySelectorAll('.relatedQuestions')
    relatedQuestionsEls.forEach(item => {
      item.style.display = 'none'
    })
  }

  switchDoms.forEach(el => {
    el.onmouseenter = hideRelatedQuestions
  })
  switchDoms02.forEach(el => {
    el.onclick = hideRelatedQuestions
  })

  console.log(JSON.stringify(result, null, 2))
}

function ready (selector, fn, shadowRoot) {
  const listeners = []
  const win = window
  const doc = shadowRoot || win.document
  const MutationObserver = win.MutationObserver || win.WebKitMutationObserver
  let observer

  function $ready (selector, fn) {
    // 储存选择器和回调函数
    listeners.push({
      selector: selector,
      fn: fn
    })
    if (!observer) {
      // 监听document变化
      observer = new MutationObserver(check)
      observer.observe(shadowRoot || doc.documentElement, {
        childList: true,
        subtree: true
      })
    }
    // 检查该节点是否已经在DOM中
    check()
  }

  function check () {
    for (let i = 0; i < listeners.length; i++) {
      var listener = listeners[i]
      var elements = doc.querySelectorAll(listener.selector)
      for (let j = 0; j < elements.length; j++) {
        var element = elements[j]
        if (!element._isMutationReady_) {
          element._isMutationReady_ = true
          listener.fn.call(element, element)
        }
      }
    }
  }

  $ready(selector, fn)
}

ready('.question-panel .question-item', () => {
  /**
   * 此处必须延迟执行,题目渲染和选中渲染是异步操作
   * 需要延时等待选中的渲染成功才执行初始化逻辑
   */
  console.log('检查到进入了试题页面,即将为你初始化小炒逻辑')
  setTimeout(function () {
    fuckKeju()
  }, 1000 * 3)
})