Reddit - Remember RES tag action

Saves previously set tag and colour to use again.

当前为 2018-07-13 提交的版本,查看 最新版本

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴Greasemonkey 油猴子Violentmonkey 暴力猴,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴Violentmonkey 暴力猴,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴Userscripts ,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展后才能安装此脚本。

(我已经安装了用户脚本管理器,让我安装!)

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

(我已经安装了用户样式管理器,让我安装!)

// ==UserScript==
// @name           Reddit - Remember RES tag action
// @description    Saves previously set tag and colour to use again.
// @author         James Skinner <[email protected]> (http://github.com/spiralx)
// @namespace      http://spiralx.org/
// @version        0.8.0
// @icon           data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAAJcEhZcwAAFiAAABYgAWToQQYAAAAYdEVYdFNvZnR3YXJlAHBhaW50Lm5ldCA0LjAuNWWFMmUAAAF3SURBVDhP1ZI/SAJhGMZN3WpKOAgsKGiIoKWg0K2LXIJWj6DAhpAgKMiW7irBqziHbhYE12sLGtWGKHBx0kHcQrBJXNIz9e35vvsQoj841g9+fHy8z/PyHZzrXzANNbgKF2ASHsOhOYGTUIb3cBSq4hyKa3Ey2GsMeMRvvzALd+AVvIPbcAb+yAq8hBehUOgxnU5TtVol27ap3W5TpVKhVCpFsiw/IHMusstwgOHxeDZM07T7/T5Rr0vUfacBvR7uHWIzwzBayK+zDm8KktFo9FXEifaXiHbnxAXEZKKtqcHSSCTygs6nBYuWZfEhR1eIzjbFBdzsYcma8xKQyWQInXmn6jCeSCQ6fDoEqqqyzxhzqgK/368Xi0UeKJfLpGka5fN5yuVyrEClUonPCoUCSZLE/oMvjGBwqut6q16vU6PRoGw2yxc0m02q1WoUj8fffD5fjGWdyvdMeL3ew2AweKsoynM4HH4KBAKW2+0+wExyIn8Hl+sDt5ENCrpr91QAAAAASUVORK5CYII=
// @icon64         data:image/svg+xml;charset=utf-8,%3Csvg%20xmlns=%22http://www.w3.org/2000/svg%22%20viewBox=%220%200%2029%2025%22%3E%3Cg%20transform=%22translate(1%201)%22%20stroke-width=%221.1%22%20fill-rule=%22evenodd%22%20fill=%22none%22%3E%3Ccircle%20stroke=%22#000%22%20cx=%222.7%22%20r=%222.7%22%20cy=%2210.7%22%20fill=%22#fff%22/%3E%3Ccircle%20stroke=%22#000%22%20cx=%2224.7%22%20r=%222.7%22%20cy=%2210.7%22%20fill=%22#fff%22/%3E%3Cpath%20stroke-linejoin=%22round%22%20stroke=%22#000%22%20stroke-linecap=%22round%22%20d=%22M21.23%201.35L15.83.08l-2%207.28%22/%3E%3Ccircle%20stroke=%22#000%22%20cx=%2223.13%22%20r=%222.13%22%20cy=%222.13%22%20fill=%22#fff%22/%3E%3Cellipse%20cy=%2214.99%22%20rx=%2212.24%22%20ry=%227.99%22%20stroke=%22#000%22%20cx=%2213.24%22%20fill=%22#fff%22/%3E%3Cg%20transform=%22translate(8%2012)%22%3E%3Ccircle%20stroke=%22#FF4500%22%20cx=%221%22%20r=%221.43%22%20cy=%221.43%22%20fill=%22#FF4500%22/%3E%3Ccircle%20stroke=%22#FF4500%22%20cx=%2210%22%20r=%221.43%22%20cy=%221.43%22%20fill=%22#FF4500%22/%3E%3Cpath%20stroke=%22#000%22%20d=%22M1.5%206.23C2.58%207.3%204.3%207.5%205.73%207.5m4.24-1.27C8.9%207.3%207.17%207.5%205.77%207.5%22/%3E%3C/g%3E%3C/g%3E%3C/svg%3E
// @match          *://*.reddit.com/r/*/comments/*
// @match          *://*.reddit.com/user/*
// @grant          unsafeWindow
// @run-at         document-end
// @require        https://unpkg.com/jquery@3/dist/jquery.min.js
// @require        https://greasyfork.org/scripts/370255-console-message/code/consolemessage.js?version=612436
// ==/UserScript==

// @require        https://greasyfork.org/scripts/10443-datacache-simple-storage-wrapper/code/DataCache%20-%20Simple%20storage%20wrapper.js?version=56961

/* jshint asi: true, esnext: true, laxbreak: true */
/* global jQuery, MutationSummary */

/**
==== 0.8.0 (2018.07.13) ====
* Changed console.message require to use GreasyFork

==== 0.7.0 (2018.02.13) ====
* Changed the rendering of the tag preview to match how RES does it
* Moved output to all use console.message

==== 0.6.0 (2018.02.11) ====
* Use unpkg.com for jQuery
* Add console.message for logging
* Use localstorage to save tags
* Use new Tag class to store tag info
* Updated ID for text field in tag popup

==== 0.5.1 (2018.02.11) ====
* Update icons to match other Reddit script

==== 0.5.0 (26.08.2017) ====
* Change to simple use of GM_getValue and GM_setValue for storage

==== 0.4.0 (21.08.2017) ====
* Update all other tags correctly when changing a user's tag
* Handle removing all other tags when clearing a user's tag

==== 0.3.0 (13.07.2017) ====

* Checks tag link to see if tag set, always overwrites if not
* Updates other tags for same user on current page

==== 0.2.1 (27.06.2017) ====
* Changed timeout of field set function to 250ms

==== 0.2.0 (31.05.2017) ====
* Updated jQuery to v3.2.1
* Added timeout before overriding tag/colour fields
* Update preview when setting tag/colour

*/

; ($ => {
  'use strict'
  
  const NORMAL = 'font-weight: normal; text-decoration: none; color: black'
  const ERROR = 'font-weight: bold; color: #f4f'
  const LINK = 'color: #05f; text-decoration: underline'
  const BOLD = 'font-weight: bold'
  const BLUE = [ 'color: #05f', 'color: #000' ]
  const GREEN = [ 'color: #c1007f', 'color: #000' ]

  const qval = (v, n) => `${n}=%c${JSON.stringify(v)}%c`

  const _BOLD = { fontWeight: 'bold' }
  const _GREEN = { color: '#c1007f' }
  const _BLUE = { color: '#05f' }

  // --------------------------------------------------------------------

  const bgToTextColorMap = {
	none: 'inherit',
	aqua: 'black',
	black: 'white',
	blue: 'white',
	cornflowerblue: 'white',
	fuchsia: 'white',
	gray: 'white',
	green: 'white',
	lime: 'black',
	maroon: 'white',
	navy: 'white',
	olive: 'white',
	orange: 'white',
	orangered: 'white',
	pink: 'black',
	purple: 'white',
	red: 'white',
	silver: 'black',
	teal: 'white',
	white: 'black',
	yellow: 'black'
  }


  // --------------------------------------------------------------------------

  $.fn.outer = function() {
    const elem = this[0] || {}
    return elem.nodeType === Document.ELEMENT_NODE
      ? elem.outerHTML
      : null
  }

  console.message.prototype.dump = function(obj) {
    if (obj && typeof obj.dump === 'function') {
      obj.dump.call(obj, this)
    } else if (typeof obj === 'function') {
      obj(this)
    }
    return this
  }

  // --------------------------------------------------------------------------

  class Tag {
    constructor(text = '', colour = 'none') {
      this.text = text
      this.colour = colour
    }

    get css() {
      const color = bgToTextColorMap[ this.colour ] // + ` !important`

      const backgroundColor = this.colour === 'none'
        ? 'transparent'
        : this.colour

      return { color, backgroundColor }
    }

    buildPreviewTag() {
      const $a = $(`<a>`, { href: '#', title: this.text })
        .addClass(`userTagLink hasTag truncateTag`)
        .css(this.css)
        .text(this.text)

      return $(`<span>`)
        .addClass('RESUserTag')
        .append($a)
    }

    apply($elem) {
      return $elem.text(this.text).css(this.css)
    }

    equals(other) {
      return this.text === other.text && this.colour === other.colour
    }

    json() {
      return JSON.stringify(this)
    }

    dump(msg) {
      /* jshint ignore:start */
      msg.text(this.text, {
        ...this.css,
        fontSize: '0.9em',
        padding: '0 4px',
        border: 'solid 1px rgb(199, 199, 199)',
        borderRadius: 3,
      })
      /* jshint ignore:end */
    }
  }

  Tag.parse = str => {
    const { text, colour } = JSON.parse(str)
    return new Tag(text, colour)
  }

  Tag.default = () => new Tag($('.pagename a').text(), 'olive')

  // --------------------------------------------------------------------------

  const tags = JSON.parse(localStorage.getItem('resrem') || '[]').map(v => new Tag(v.text, v.colour))

  function getLastTag() {
    return tags.length > 0 ? tags[0] : Tag.default()
  }

  function saveTag(tag) {
    const idx = tags.findIndex(t => t.equals(tag))

    if (idx !== -1) {
      tags.splice(idx, 1)
    }

    tags.unshift(tag)

    localStorage.setItem('resrem', JSON.stringify(tags))

    return tag
  }

  const msg = console.message().text('onScriptInit:', _BOLD)
  tags.forEach(tag => { msg.text(' ').dump(tag) })
  msg.print()

  // --------------------------------------------------------------------------

  function onTagModalOpened() {
    const $tagField = $('#userTaggerText')
    const $colourField = $('#userTaggerColor')
    const $previewField = $('#userTaggerPreview')

    const previousTag = getLastTag()

    if (previousTag) {
      $tagField.val(previousTag.text)
      $colourField.val(previousTag.colour)

      const $previewTag = previousTag.buildPreviewTag()
      console.info($previewTag.outer())

      $previewField
        .empty()
        .append($previewTag)
    }
  }  


  // --------------------------------------------------------------------------

  function onSaveTag() {
    const text = $('#userTaggerText').val()
    const colour = $('#userTaggerColor').val()
    const user = $('#userTaggerName').val()

    if (text) {
      const newTag = saveTag(new Tag(text, colour))

      const msg = console.message()
        .text('onSaveTag:', _BOLD)
        .text(' ')
        .dump(newTag)
        .text('* ')
      tags.slice(1).forEach(tag => { msg.text(' ').dump(tag) })
      msg.print()

      $(`a.userTagLink[username=${user}]`)
        .text(newTag.text)
        .css(newTag.css)
        .addClass('hasTag')
        .removeClass('RESUserTagImage')
    } else {
      $(`a.userTagLink[username=${user}]`)
        .text('')
        .removeAttr('style')
        .removeClass('hasTag')
        .addClass('RESUserTagImage')
    }
  }
  

  // --------------------------------------------------------------------------

  $('body')
    .on('click.resrem', 'a.userTagLink:not(.hasTag)', () => {
      setTimeout(onTagModalOpened, 250)
    })
    .on('click.resrem', '#userTaggerSave', onSaveTag)

  /*
    <div class="RESHover RESHoverInfoCard RESDialogSmall" style="top: 387.918px; left: 215.767px; width: 350px; display: block; opacity: 1;">
      <h3 class="RESHoverTitle" data-hover-element="0">
        <div>
          <span class="res-icon"></span>&nbsp;<span>Plan-Six</span>
        </div>
      </h3>

      <div class="RESCloseButton">x</div>

      <div class="RESHoverBody RESDialogContents" data-hover-element="1">
        <form id="userTaggerToolTip">
          <div class="fieldPair">
            <label class="fieldPair-label" for="userTaggerText">Text</label>

            <input class="fieldPair-text" type="text" id="userTaggerText">
          </div>

          <div class="fieldPair">
            <label class="fieldPair-label" for="userTaggerColor">Color</label>

            <select id="userTaggerColor">
              <option style="color: inherit; background-color: none" value="none">none</option>
              <option style="color: black; background-color: aqua" value="aqua">aqua</option>
              <option style="color: white; background-color: black" value="black">black</option>
              <option style="color: white; background-color: blue" value="blue">blue</option>
              <option style="color: white; background-color: cornflowerblue" value="cornflowerblue">cornflowerblue</option>
              <option style="color: white; background-color: fuchsia" value="fuchsia">fuchsia</option>
              <option style="color: white; background-color: gray" value="gray">gray</option>
              <option style="color: white; background-color: green" value="green">green</option>
              <option style="color: black; background-color: lime" value="lime">lime</option>
              <option style="color: white; background-color: maroon" value="maroon">maroon</option>
              <option style="color: white; background-color: navy" value="navy">navy</option>
              <option style="color: white; background-color: olive" value="olive">olive</option>
              <option style="color: white; background-color: orange" value="orange">orange</option>
              <option style="color: white; background-color: orangered" value="orangered">orangered</option>
              <option style="color: black; background-color: pink" value="pink">pink</option>
              <option style="color: white; background-color: purple" value="purple">purple</option>
              <option style="color: white; background-color: red" value="red">red</option>
              <option style="color: black; background-color: silver" value="silver">silver</option>
              <option style="color: white; background-color: teal" value="teal">teal</option>
              <option style="color: black; background-color: white" value="white">white</option>
              <option style="color: black; background-color: yellow" value="yellow">yellow</option>
            </select>
          </div>

          <div class="fieldPair">
            <label class="fieldPair-label" for="userTaggerPreview">Preview</label>

            <span id="userTaggerPreview" style="color: white; background-color: olive;">
              <span class="RESUserTag">
                <a class="userTagLink hasTag truncateTag" style="background-color: olive; color: white !important;" title="Sexist" href="javascript:void 0">Sexist</a>
              </span>
            </span>
          </div>
          <a class="userTagLink hasTag truncateTag" style="background-color: olive; color: white !important;" title="Feminist" href="javascript:void 0">Feminist</a>

          <div class="fieldPair res-usertag-ignore">
            <label class="fieldPair-label" for="userTaggerIgnore">Ignore</label>

            <div id="userTaggerIgnoreContainer" class="toggleButton ">
              <span class="toggleThumb"></span>
              <div class="toggleLabel res-icon" data-enabled-text="" data-disabled-text=""></div>
              <input id="userTaggerIgnore" name="userTaggerIgnore" type="checkbox">
            </div>

            <a class="gearIcon" href="#res:settings/userTagger/hardIgnore" title="RES Settings > User Tagger > hardIgnore"> configure </a>
          </div>

          <div class="fieldPair">
            <label class="fieldPair-label" for="userTaggerLink">
              <span class="userTaggerOpenLink">
                <a title="open link" href="javascript:void 0">Source URL</a>
              </span>
            </label>

            <input class="fieldPair-text" type="text" id="userTaggerLink" value="https://www.reddit.com/r/giantbomb/comments/7x251d/all_systems_goku_02/du5fpwr/">
          </div>

          <div class="fieldPair">
            <label class="fieldPair-label" for="userTaggerVotesUp" title="Upvotes you have given this redditor">Upvotes</label>

            <input type="number" style="width: 50px;" id="userTaggerVotesUp" value="0">
          </div>

          <div class="fieldPair">
            <label class="fieldPair-label" for="userTaggerVotesDown" title="Downvotes you have given this redditor">Downvotes</label>

            <input type="number" style="width: 50px;" id="userTaggerVotesDown" value="0">
          </div>

          <div class="res-usertagger-footer">
            <a href="/r/dashboard#userTaggerContents" target="_blank" rel="noopener noreferer">View tagged users</a>

            <input type="submit" id="userTaggerSave" value="✓ save tag">
          </div>
        </form>
      </div>
    </div>

  */

})(jQuery)

jQuery.noConflict(true)