Inverter

Inverts webpages with a hotkey

目前为 2017-06-11 提交的版本。查看 最新版本

// ==UserScript==
// @name         Inverter
// @icon         http://i.imgur.com/wBrRGXc.png
// @namespace    skoshy.com
// @version      0.2.25
// @description  Inverts webpages with a hotkey
// @author       Stefan Koshy
// @run-at       document-start
// @match        *://*/*
// @grant        GM_getValue
// @grant        GM_setValue
// @grant        GM_deleteValue
// ==/UserScript==

var currentSite = '';
var scriptId = 'inverter';

var timers = {};
timers.lastToggle = 0; // default the last toggle to nothing

var options = {};
options.toggleDelayMs = 200; // number of milliseconds delay there needs to be to switch from toggle mode to save inversion mode

var css = {};
css.defaults = {};
css.overrides = {};

css.common = {};
css.common.css = `
html {
  filter: invert(1);
  min-height: 100%;
  background-color: black;
}

img, figure, video, picture {
  filter: invert(1);
}

figure img { /* no need to invert imgs in figures */
  filter: invert(0);
}

*[style*="url(/"],
*[style*="blob:"],
*[style*="url('https:"],
*[style*="url('http:"],
*[style*="url('://"],
*[style*="url('blob:"],
*[style*='url("https:'],
*[style*='url("https:'],
*[style*='url("http:'],
*[style*='url("://'],
*[style*='url("blob:']
{ filter: invert(1); }

body, body > div {
  background-color: white;
}

iframe[src*="youtube.com"], iframe[src*="vimeo.com"] {
  filter: invert(1);
}

twitterwidget::shadow .MediaCard-media {
  filter: invert(1);
}

twitterwidget::shadow .Avatar {
  filter: invert(1);
}

.gbii /* This is for multiple Google Domains to reinvert the profile icon in the top right */
{filter: invert(1);}
`;
css.messenger = {};
css.messenger.includeCommon = false;
css.messenger.javascriptOnce = function() {
    var chatColor = '';
    
    setInterval(function() {
        var chatColorEl = document.querySelector('._fl2 [data-testid="info_panel_button"] svg polygon, ._fl2 [data-testid="info_panel_button"] svg path'); /* Two elements, depends on if the info button is pressed or not */
        if (!chatColorEl || chatColorEl.style.fill == chatColor) return;
        
        chatColor = chatColorEl.style.fill;
        
        var newCss = `
          ._43by
          { background: `+chatColor.replace('rgb', 'rgba').replace(')', ', .3)')+` !important; }
        `;
        
        addGlobalStyle(newCss, scriptId+'-css', isInverterEnabled(), scriptId+'-messengerSpecialCss');
    }, 500);
};
css.messenger.css = `
body
,._5i_d * /* Text from YouTube/video share */
{ color: white; }

/* Bug Fixes */
._kmc /* message box, min height fix */
{ min-height: 26px !important; }

._4sp8 {
background: black !important;
}

._4rv3 /* Message box container */
,._5743 * /* Top name */
,._1ht6 /* Names in the sidebar */
,._1ht3 ._1htf /* Unread message in sidebar */
,._1tqi ._4qba /* App Name */
,._5swm * /* Text from link shares */
,._hh7 a /* Link from received message */
,._55r /* video chat notices */
,._1enh ._364g /* from searching, contact names */
,._225b /* from searching, header */
,._lll * /* Intro message text for first time chatting someone */
,._3szq /* Info Box text */
,._58ak input /* Composing new message text box */
,._1jt6 ._3oh- /* Info box contact name */
,._4rph ._4rpj /* Info box group chat, add people */
,._4_j5 ._364g /* Info box group chat, additional people */
,._2jnv /* Info box group chat, chat name */
{ background: transparent !important; color: white !important; }

._2y8_ * /* Popup boxes text */
{ color: black; }

._29_7 ._hh7 /* receiving message boxes */
{ background: rgba(255,255,255,.12) !important; color: white; }

._43by /* sent message boxes */
{ background: rgba(0, 132, 255,.45) !important; transition: background 0.3s ease; }

._497p /* Timestamps and joining video chat messages in the chat */
,._1ht7 /* timestamps in sidebar */
,._ih3 /* Names in group chat */
,._5iwm ._58al::placeholder /* placeholder text for search */
,._1lj0 /* Info Box Headers */
,._3eus, ._5uh /* Info Box Person Description */
,._2y8z /* Composing new message "To" field" */
,._58ak input::placeholder /* Composing new message text box, placeholder */
,._5i_d .__6m /* Link share URL */
,._3x6v /* Info box, side text next to notifications if notifications are muted */
{ color: rgba(255,255,255,.6) !important; }

._29_7 ._52mr /* Borders of message bubble stuff, like video chat notifications, links, etc, received */
,._nd_ ._52mr /* Same as above, but sent */
{ border-color: rgba(255,255,255,.2); }

.sp_1g5zN81j-1P /* tiny icons, like when a video chat ends */
,._5iwm ._58ak::before /* search icon */
,.sp_6SI1R2TSgtb /* more tiny icons */
,._2t5t /* more icons, like the forward icon */
{ filter: invert(1); }

a._4ce_ /* games icon */
,._4rv6 /* stickers icon */
{ opacity: .7 !important; filter: invert(1) !important; }

._5iwm ._58al /* search box */
{ background: rgba(255,255,255,.3) !important; color: white !important; }

::-webkit-scrollbar-track
{
  -webkit-box-shadow: inset 0 0 6px rgba(0,0,0,0.3) !important;
  border-radius: 10px !important;
  background-color: #333 !important;
}

::-webkit-scrollbar
{
  width: 12px !important;
  background-color: transparent !important;
}

::-webkit-scrollbar-thumb
{
  border-radius: 10px !important;
  -webkit-box-shadow: inset 0 0 6px rgba(0,0,0,.3) !important;
  background-color: #555 !important;
}
`;
css.gmail = {};
css.gmail.css = `
iframe[src*="hangouts.google.com"]
{ filter: invert(1); }
`;
css.inbox = {};
css.inbox.css = `
img[src*="ssl.gstatic.com"], img[src*="www.gstatic.com"]
{ filter: invert(0); }
nav#GH > .b4 /* Header bar, kepp the same color */
{ filter: invert(1); }
iframe[src*="hangouts.google.com"]
{ filter: invert(1); }
.cf /* Shipping notification headers */
{ filter: invert(1); }
.xW /* Shipping icon */
{ filter: invert(1) !important; }
`;
css.hangouts = {};
css.hangouts.enableInIframe = true;
css.hangouts.css = `
.Ik:not(.uB) /* Chat Headers */
{ filter: invert(1); }
`;
css.youtube = {};
css.youtube.css = `
.player-api video
{filter: inherit;}

#theater-background
{background: white !important;}

#player-playlist,
.player-api,
#c4-header-bg-container /* Banner on YouTube channel page */
{filter: invert(1);}

#player-playlist img,
#c4-header-bg-container img /* Banner on YouTube channel page */
{filter: invert(0);}

`;
css.facebook = {};
css.facebook.css = `
i /* Emoji and other small icons */
{filter: invert(1);}
`;
css.twitter = {};
css.twitter.css = `
.PermalinkOverlay-with-background /* overlay when clicking on a tweet */
{background: rgba(255,255,255,.55) !important;}

iframe
{background-color: white; filter: invert(1);}

.is-generic-video {
  filter: invert(1);
}
`;
css.soundcloud = {};
css.soundcloud.css = `
span[style]
`;
css.pocketcasts = {};
css.pocketcasts.css = `
#header {filter: invert(1);}
#audio_player {filter: invert(1); background-color: black;}

#header img {filter: invert(0);}
#audio_player img {filter: invert(0);}

#audio_player #audio_player_wrapper .player_top,
#audio_player #audio_player_wrapper .player_bottom
{background-color: transparent;}
`;
css.none = {};
css.none.css = ``;


function addGlobalStyle(css, className, enabled, id) {
    var head, style;
    head = document.getElementsByTagName('head')[0];
    if (!head) { return; }
    
    // check to see if this element already exists, if it does override it
    var oldEl = document.getElementById(id);
    
    style = document.createElement('style');
    style.type = 'text/css';
    style.innerHTML = css;
    style.id = id;
    style.className = className;
    head.appendChild(style);
    style.disabled = !enabled;
    
    // delete old element if it exists
    if (oldEl) {
        oldEl.parentNode.removeChild(oldEl);
    }
}

function parseCSS(parsed) {
    for (attribute in css.defaults) {
        exceptionToReplace = new RegExp('{{'+attribute+'}}', 'g');
        parsed = parsed.replace(exceptionToReplace, css['defaults'][attribute]);
    }

    return parsed;
}

document.addEventListener("keydown", function(e) {
  if (e.altKey === true && e.shiftKey === false && e.ctrlKey === true && e.metaKey === false && e.code == 'KeyI') {
  var timestamp = new Date();
  timestamp = timestamp.getTime();
  
  if (timers.lastToggle > timestamp-options.toggleDelayMs) {
    if (isInverterEnabled()) {
    GM_setValue('enabled_'+document.domain, true);
    alert('Saved inversion for '+document.domain);
    } else {
    GM_deleteValue('enabled_'+document.domain);
    alert('Deleted inversion setting for '+document.domain);
    }
  } else {
    // toggle style
    var cssEls = document.getElementsByClassName(scriptId+'-css');

    if (isInverterEnabled()) {
      for (let i = 0; i < cssEls.length; i++) {
          cssEls[i].disabled = true;
      }
    } else {
      for (let i = 0; i < cssEls.length; i++) {
          cssEls[i].disabled = false;
      }
    }
  }
  
  timers.lastToggle = timestamp;
  }
});

function isInverterEnabled() {
  var cssEl = document.getElementById(scriptId+'-css');
  return cssEl.disabled === false;
}

function getSetCurrentSite() {
    var url = document.documentURI;
  
  currentSite = 'none'

    if (url.indexOf('messenger.com') != -1) currentSite = 'messenger';
    if (url.indexOf('youtube.com') != -1) currentSite = 'youtube';
    if (url.indexOf('twitter.com') != -1) currentSite = 'twitter';
    if (url.indexOf('inbox.google.com') != -1) currentSite = 'inbox';
  if (url.indexOf('hangouts.google.com') != -1) currentSite = 'hangouts';
  if (url.indexOf('mail.google.com') != -1) currentSite = 'gmail';
  if (url.indexOf('facebook.com') != -1) currentSite = 'facebook';
  if (url.indexOf('play.pocketcasts.com') != -1) currentSite = 'pocketcasts';

  console.log('Detected site for '+scriptId+' script: '+currentSite);
  
    return currentSite;
}

function init() {
    getSetCurrentSite();

    var styleEnabled = GM_getValue( 'enabled_'+document.domain , false );
  
    console.log('Inversion Enabled for site ('+currentSite+'): '+styleEnabled);
    if (inIframe() && isFalsy(css[currentSite].enableInIframe)) { styleEnabled = false; }
    
    var cssToInclude = '';
    
    if (css[currentSite].includeCommon === false) {}
    else { cssToInclude += css.common.css; }
    
    if (css[currentSite].javascriptOnce) { css[currentSite].javascriptOnce(); }
    
    cssToInclude += css[currentSite].css;

    addGlobalStyle(parseCSS(
        cssToInclude
    ), scriptId+'-css', styleEnabled, scriptId+'-css');
}

init();

/*
* Utility functions
*/

function isTruthy(item) {
  return !isFalsy(item);
}

function isFalsy(item) {
  if (
    !item
    || (typeof item == "object" && Object.keys(item).length == 0) // for empty objects, like {}, []
  )
    return true;
  else
    return false;
}

function addEvent(obj, evt, fn) {
    if (obj.addEventListener) {
        obj.addEventListener(evt, fn, false);
    }
    else if (obj.attachEvent) {
        obj.attachEvent("on" + evt, fn);
    }
}

function inIframe () {
    try {
        return window.self !== window.top;
    } catch (e) {
        return true;
    }
}