科举小抄

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

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

// ==UserScript==
// @name         科举小抄
// @namespace    https://github.com/fuckKeju/fuckKeju
// @version      0.0.3
// @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')
      answerListEl.forEach(answerEl => {
        let answer = answerEl.innerText.trim()
        const checkedEl = answerEl.parentElement.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 {
        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', () => {
  fuckKeju()
})