Leetcode 助手

禁英文站跳中文站,增加中英站互跳按钮,删除中英站一些广告,复制题解与描述

Você precisará instalar uma extensão como Tampermonkey, Greasemonkey ou Violentmonkey para instalar este script.

Você precisará instalar uma extensão como Tampermonkey ou Violentmonkey para instalar este script.

Você precisará instalar uma extensão como Tampermonkey ou Violentmonkey para instalar este script.

Você precisará instalar uma extensão como Tampermonkey ou Userscripts para instalar este script.

Você precisará instalar uma extensão como o Tampermonkey para instalar este script.

Você precisará instalar um gerenciador de scripts de usuário para instalar este script.

(Eu já tenho um gerenciador de scripts de usuário, me deixe instalá-lo!)

Você precisará instalar uma extensão como o Stylus para instalar este estilo.

Você precisará instalar uma extensão como o Stylus para instalar este estilo.

Você precisará instalar uma extensão como o Stylus para instalar este estilo.

Você precisará instalar um gerenciador de estilos de usuário para instalar este estilo.

Você precisará instalar um gerenciador de estilos de usuário para instalar este estilo.

Você precisará instalar um gerenciador de estilos de usuário para instalar este estilo.

(Eu já possuo um gerenciador de estilos de usuário, me deixar fazer a instalação!)

// ==UserScript==
// @name         Leetcode 助手
// @namespace    http://tampermonkey.net/
// @homepageURL   https://github.com/h-hg/leetcode-optimization
// @supportURL    https://github.com/h-hg/leetcode-optimization/issues
// @version      0.2.0
// @description  禁英文站跳中文站,增加中英站互跳按钮,删除中英站一些广告,复制题解与描述
// @author       Hunter Hwang
// @license      MIT
// @match        https://leetcode.com/*
// @match        https://leetcode.com/problems/*
// @match        https://leetcode.cn/problems/*
// @icon         https://assets.leetcode.com/static_assets/public/icons/favicon-192x192.png
// @run-at       document-idle
// @grant        GM_addStyle
// @grant        GM_webRequest
// ==/UserScript==

(function () {
  'use strict';
  // function handleCopy(e) {
  //   e.stopPropagation();
  //   const copytext = window.getSelection();
  //   const clipdata = e.clipboardData || window.clipboardData;
  //   if (clipdata) {
  //     clipdata.setData("Text", copytext);
  //   }
  // }
  /**
   * @link https://stackoverflow.com/questions/22125865/wait-until-flag-true
   */
  function waitFor(condition, callback) {
    if (!condition()) {
      window.setTimeout(waitFor.bind(null, condition, callback), 1000);
    } else {
      callback();
    }
  }
  function isLoadFinish() {
    var tag = isCNSite() ? 'nav' : 'img';
    return document.querySelector(tag) != null;
  }
  function isCNSite() {
    return location.hostname === 'leetcode.cn'
  }
  function getProblemName() {
    var tmp = location.href.match(/problems\/([^\/]+)/);
    return (tmp != null && tmp[1] != 'all') ? tmp[1] : null;
  }
  function copyCnSolution(callback) {
    // example: https://leetcode.cn/problems/number-of-matching-subsequences/solutions/1973995/pi-pei-zi-xu-lie-de-dan-ci-shu-by-leetco-vki7/
    var match = location.href.match(/problems\/[^\/]+\/solutions\/[^\/]+\/([^\/]+)/);
    if (match == null)
      return;
    const data = JSON.stringify({
      "query": `query discussTopic($slug: String) {
      solutionArticle(slug: $slug, orderBy: DEFAULT) {
        ...solutionArticle
        content
        next {
          slug
          title
        }
        prev {
          slug
          title
        }
      }
    }

    fragment solutionArticle on SolutionArticleNode {
      ipRegion
      rewardEnabled
      canEditReward
      uuid
      title
      slug
      sunk
      chargeType
      status
      identifier
      canEdit
      canSee
      reactionType
      reactionsV2 {
        count
        reactionType
        }
      tags {
        name
        nameTranslated
        slug
        tagType
      }
      createdAt
      thumbnail
      author {
        username
        isDiscussAdmin
        isDiscussStaff
        profile {
          userAvatar
          userSlug
          realName
          reputation
        }
      }
      summary
      topic {
        id
        subscribed
        commentCount
        viewCount
        post {
          id
          status
          voteStatus
          isOwnPost
        }
      }
      byLeetcode
      isMyFavorite
      isMostPopular
      favoriteCount
      isEditorsPick
      hitCount
      videosInfo {
        videoId
        coverUrl
        duration
      }
    }`,
      "variables": {
        "slug": match[1],
      }
    });
    const response = fetch(
      'https://leetcode.cn/graphql/',
      {
        method: 'post',
        body: data,
        headers: {
          'Content-Type': 'application/json',
          'Content-Length': data.length,
        },
      }
    );
    response.then(res => res.json()).then(d => {
      navigator.clipboard.writeText(d.data.solutionArticle.content)
    })
  }
  function copyEnsolution() {
    // example: https://leetcode.com/problems/median-of-two-sorted-arrays/discuss/2799909/Python-or-Easy-Solution
    var match = location.href.match(/problems\/[^\/]+\/discuss\/([^\/]+)\/[^\/]+/);
    if (match == null)
      return;
    const data = JSON.stringify({
    "operationName":"DiscussTopic",
    "variables":{
      "topicId": parseInt(match[1]),
    },
    "query":`query DiscussTopic($topicId: Int!) {
      topic(id: $topicId) {
        id
        viewCount
        topLevelCommentCount
        subscribed
        title
        pinned
        tags
        hideFromTrending
        post {
          ...DiscussPost
          __typename
        }
        __typename
      }
    }

    fragment DiscussPost on PostNode {
      id
      voteCount
      voteStatus
      content
      updationDate
      creationDate
      status
      isHidden
      coinRewards {
        ...CoinReward
        __typename
      }
      author {
        isDiscussAdmin
        isDiscussStaff
        username
        nameColor
        activeBadge {
          displayName
          icon
          __typename
        }
        profile {
          userAvatar
          reputation
          __typename
        }
        isActive
        __typename
      }
      authorIsModerator
      isOwnPost
      __typename
    }

    fragment CoinReward on ScoreNode {
      id
      score
      description
      date
      __typename
    }`});
    const response = fetch(
      'https://leetcode.com/graphql/',
      {
        method: 'post',
        body: data,
        headers: {
          'Content-Type': 'application/json',
          'Content-Length': data.length,
        },
      }
    );
    response.then(res => res.json()).then(d => {
      navigator.clipboard.writeText(d.data.topic.post.content.replaceAll('\\n', '\n'))
    })
  }
  function copySolution() {
    if (isCNSite())
      copyCnSolution();
    else
      copyEnsolution();
  }
  function copyCnDescription() {
    // example: https://leetcode.cn/problems/number-of-matching-subsequences/description/
    var match = location.href.match(/problems\/([^\/]+)\//);
    if (match == null)
      return;
    const data = JSON.stringify({
      "query": `query questionTranslations($titleSlug: String!) {
      question(titleSlug: $titleSlug) {
        translatedTitle
        translatedContent
      }
    }`,
      "variables": {
        "titleSlug": "number-of-matching-subsequences"
      }
    });
    const response = fetch(
      'https://leetcode.cn/graphql/',
      {
        method: 'post',
        body: data,
        headers: {
          'Content-Type': 'application/json',
          'Content-Length': data.length,
        },
      }
    );
    response.then(res => res.json()).then(d => {
      navigator.clipboard.writeText(d.data.question.translatedContent)
    })
  }
  function copyEnDescription() {
    var match = location.href.match(/problems\/([^\/]+)\//);
    if (match == null)
      return;
    const data = JSON.stringify({
      "operationName": "questionData",
      "variables": {
        "titleSlug": match[1],
      },
      "query": `query questionData($titleSlug: String!) {
        question(titleSlug: $titleSlug) {
          questionId
          questionFrontendId
          boundTopicId
          title
          titleSlug
          content
          translatedTitle
          translatedContent
          isPaidOnly
          canSeeQuestion
          difficulty
          likes
          dislikes
          isLiked
          similarQuestions
          exampleTestcases
          categoryTitle
          contributors {
            username
            profileUrl
            avatarUrl
            __typename
          }
          topicTags {
            name
            slug
            translatedName
            __typename
          }
          companyTagStats
          codeSnippets {
            lang
            langSlug
            code
            __typename
          }
          stats
          hints
          solution {
            id
            canSeeDetail
            paidOnly
            hasVideoSolution
            paidOnlyVideo
            __typename
          }
          status
          sampleTestCase
          metaData
          judgerAvailable
          judgeType
          mysqlSchemas
          enableRunCode
          enableTestMode
          enableDebugger
          envInfo
          libraryUrl
          adminUrl
          challengeQuestion {
            id
            date
            incompleteChallengeCount
            streakCount
            type
            __typename
          }
          __typename
      }
      }`
    });
    const response = fetch(
      'https://leetcode.com/graphql/',
      {
        method: 'post',
        body: data,
        headers: {
          'Content-Type': 'application/json',
          'Content-Length': data.length,
        },
      }
    );
    response.then(res => res.json()).then(d => {
      navigator.clipboard.writeText(d.data.question.content);
    })
  }
  function copyDescription() {
    if(isCNSite())
      copyCnDescription();
    else
      copyEnDescription();
  }
  function getOtherLangUrl() {
    var matchRes = location.href.match(/problems\/([^\/]+)\/?([a-z]+)?/);
    if (matchRes == null || matchRes[1] == 'all')
      return null;
    var problemName = matchRes[1], tab = matchRes[2];

    if (tab == 'discuss') {
      tab = 'comments';
    } else if (tab == 'comments') {
      tab = 'discuss';
    } else if (tab == 'submissions' || tab == 'solution') {
    } else {
      tab = '';
    }
    return `https://leetcode${isCNSite() ? '.com' : '.cn'}/problems/${problemName}/${tab}`
  }
  function banAutoJump2Cn() {
    GM_webRequest([
      { selector: 'https://assets.leetcode.cn/*', action: 'cancel' },
    ], function (info, message, details) {
      console.log(info, message, details);
    });
  }
  function html2elem(html) {
    let template = document.createElement('template');
    html = html.trim(); // Never return a text node of whitespace as the result
    template.innerHTML = html;
    return template.content.firstChild;
  }
  function createBall() {
    // add css
    GM_addStyle(`
      .leetcode-wrapper {
        position: fixed;
        top: 30%;
        left: 10px;
        z-index: 1;
      }
      .leetcode-wrapper .btn {
        cursor: move;
        width: 48px;
        height: 48px;
        border-radius: 50%;
        border: 2px solid black;
        opacity: 0.1;
      }
      .leetcode-wrapper:hover .btn {
        background:url(https://leetcode.com/favicon-96x96.png) no-repeat;
        background-size:cover;
        opacity: 0.8;
      }
      .leetcode-wrapper .menu {
        display: none;
        position: absolute;
        background-color: #f9f9f9;
        min-width: 100px;
        box-shadow: 0px 8px 16px 0px rgba(0,0,0,0.2);
      }
      .leetcode-wrapper:hover .menu {
        display: block;
      }
      .leetcode-wrapper .menu a,
      .leetcode-wrapper .menu button {
        color: black;
        padding: 12px 16px;
        text-decoration: none;
        display: block;
        border: none;
        text-align: center;
        background-color: transparent;
        cursor: pointer;
        width: 100%;
      }
      .leetcode-wrapper .menu a:hover,
      .leetcode-wrapper .menu button:hover {
        background-color: #f1f1f1
      }
    `);

    // add html
    var wrapper = html2elem(`
      <div class="leetcode-wrapper">
      <button id="leetcode-btn" class="btn"></button>
        <div class="menu">
          <!-- <a href="", target="_blank"></a> -->
        </div>
      </div>
    `);
    var menu = wrapper.querySelector('.menu');
    waitFor(isLoadFinish, () => {
      // switch to other language
      var link = document.createElement('a');
      link.target = '_blank';
      link.appendChild(document.createTextNode(isCNSite() ? 'English' : '中文'));
      link.href = getOtherLangUrl();
      menu.appendChild(link);
      // copy solution
      var b1 = document.createElement('button')
      b1.textContent = isCNSite() ? '复制题解' : 'Copy discuss';
      b1.onclick = copySolution;
      menu.appendChild(b1)
      // copy problem description
      var b2 = document.createElement('button')
      b2.textContent = isCNSite() ? '复制描述' : 'Copy description';
      b2.onclick = copyDescription;
      menu.appendChild(b2)

      document.body.appendChild(wrapper);
      var btn = document.getElementById('leetcode-btn');
      btn.addEventListener('mouseenter', function (e) {
        menu.firstElementChild.href = getOtherLangUrl();
        // TODO
      })
    })
  }
  // prevent auto jump to leetcode.cn
  if (!isCNSite()) {
    banAutoJump2Cn();
  }

  // AD
  if (isCNSite()) {

  } else {
    GM_addStyle(`
      /* 顶部中文横幅 */
      #cn-banner {
        display: none!important;
      }';
      #region_switcher{
        display: none!important;
      }
      /* 顶部 LeetCode is hiring! Apply Now! */
      .feedback-anchor {
        display: none!important;
      };
    `);
  }

  // problems navigator
  let problemName = getProblemName();
  if (problemName != null) {
    createBall();
  }
  // document.addEventListener("copy", handleCopy, true);
})();