// ==UserScript==
// @name adjustColor2
// @namespace http://chiebukuro.yahoo.co.jp/my/gbjyn273
// @author Syakku
// @description convert colors to eye-friendly - webページの配色を目に優しく変更します (Flash対応)
// @include *
// @version 2.0
// @run-at document-start
// ==/UserScript==
// ================≪ OPTION ≫================
// ----------------< white" & "black" color >----------------
var FILTER_COLOR_W = "rgb(234, 229, 227)";
var FILTER_COLOR_B = "rgb(41, 34, 29)";
var INNER_COLOR_W = "rgb(211, 203, 198)";
var INNER_COLOR_B = "rgb(41, 34, 29)"; // gb is disabled
// OPTION : apply to Flash object
var FLASH = true;
// OPTION : skip <a> tag (link anchor)
// OPTION : <a> タグ、つまりリンク文字列を処理から除外します
var A_SKIP = true;
// ################################################ //
// ################# オブジェクト #################### //
// ################################################ //
// ================ <色縮小オブジェクト ≫================
function RGBConv(w, b, pre_filter){
this.w = w, this.b = b, this.pf = pre_filter;
this.init();
}
// ----------------< 初期化 >----------------
RGBConv.prototype.init = function(){
this.pr = (this.w[0] - this.b[0]) / 255;
this.pg = (this.w[1] - this.b[1]) / 255;
this.pb = (this.w[2] - this.b[2]) / 255;
this.pr2 = this.b[0], this.pg2 = this.b[1], this.pb2 = this.b[2];
// 先に計算
this.ra = this.pf.r * this.pf.a;
this.ga = this.pf.g * this.pf.a;
this.ba = this.pf.b * this.pf.a;
this.ia = 1 - this.pf.a;
// 色変換の結果保持配列
this.lBefore = [];
this.lAfter = [];
this.lCount = 0;
}
// ----------------< 色変換メソッド >----------------
RGBConv.prototype.conv = function(bg){
// 処理する必要のないカラーは省く
// (本スクリプトで処理後のカラーは大抵変な数字になっている)
// (それを利用して簡易的ではあるが二重処理を防いでいる)
for (var i=0; n<this.lCount; i++){
if (bg === this.lAfter[i]) return;
}
// 既存のカラーは省エネ
for (var i=0; i<this.lCount; i++){
if (bg === this.lBefore[i]){
tElm.style.color = this.lAfter[i];
return;
}
}
var ret = this.calc(bg);
this.lBefore[lCount] = bg;
this.lAfter[lCount] = ret;
this.lCount++;
return ret;
}
// ----------------< 計算メソッド >----------------
RGBConv.prototype.calc = function(bg){
var col = rgbSplit(bg);
var colR = col[0] * this.pr + this.pr2*1;
var colG = col[1] * this.pg + this.pg2*1;
var colB = col[2] * this.pb + this.pb2*1;
// フィルターによる効果を考慮
// filter.r + (colR - filter.r) / filter.ia と同義
colR = Math.floor((colR - this.ra) / this.ia);
colG = Math.floor((colG - this.ga) / this.ia);
colB = Math.floor((colB - this.ba) / this.ia);
if (typeof col[3] === "undefined") {
return ("rgb(" + colR + ", " + colG + ", " + colB + ")");
}
return ("rgb(" + colR + ", " + colG + ", " + colB + ", " + col[3] + ")"); // rgba
}
// ================≪ フィルタオブジェクト≫================
function RGBFilter(w, b){
this.wr = w[0], this.wg = w[1], this.wb = w[2];
this.br = b[0];
this.r = 0, this.b = 0, this.g = 0, this.a = 0, this.str = 0;
this.init();
}
// ----------------< 初期化 >----------------
RGBFilter.prototype.init = function(){
with(this){
// trをwr-0と100-wrの総計で割ってwrの取り分を決める
r = (wr-br) / (255 - (wr-br)) * br + br;
a = br / r; // 中心値rが気をつけないと 0 や 255 になって 0 除算になる
if (isNaN(a) === true) a = (wr-255) / (r-255);
g = r - (wr-wg) / a;
b = r - (wr-wb) / a;
str = "rgba(" + Math.floor(r) + ", " + Math.floor(g) + ", " + Math.floor(b) + ", " + a + ")";
}
}
// ----------------< フィルター適用 >----------------
RGBFilter.prototype.applyFilter = function(elm){
var f = document.createElement("ac_filter");
f.id = "adjustColor";
with (f.style){
backgroundColor = this.str;
pointerEvents = "none";
height = "100%";
width = "100%";
display = "block";
position = "fixed";
zIndex = "2147483647";
}
for (var i=0, len=elm.children.length; i<len; i++){
if (elm.children[i].tagName !== "HEAD"){
elm.insertBefore(f, elm.children[i]);
break;
} else if (i === len - 1){
elm.insertBefore(f, null);
break
}
}
}
// ================≪ フラッシュオブジェクト ≫================
function FlashObj(obj, type){
this.obj = obj;
this.type = type;
this.init();
}
// ----------------< 初期化 >----------------
FlashObj.prototype.init = function(){
if (this.type == "OBJECT"){
this.name = "value";
var params = this.obj.querySelector("param[name='wmode']");
if (params){
this.wmode = params;
} else {
// paramが存在しないとき用(動作未確認)
var param = document.createElement("param");
param.setAttribute("name", "wmode");
this.obj.appendChild(param);
this.wmode = param;
}
} else {
this.name = "wmode";
this.wmode = this.obj;
}
}
// ----------------< 透過メソッド >----------------
FlashObj.prototype.trans = function(){
this.wmode.setAttribute(this.name, "transparent");
this.obj.src = this.obj.src;
this.obj.data = this.obj.data;
}
// ################################################ //
// ################### メソッド ###################### //
// ################################################ //
// ================≪ 追加処理 ≫================
function afterTouch(){
mo.observe(document.body, {childList: true, subtree: true});
var elm;
if (A_SKIP) elm = document.querySelector(":not(A)");
else elm = document.getElementsByTagName("*");
for (var i = 0; i < elm.length; i++) {
var tElm = elm[i];
var style = window.getComputedStyle(tElm, null);
var fgColor = style.getPropertyValue("color");
var tColor = conv.conv(fgColor);
if (tColor) tElm.style.color = tColor;
}
}
// ================≪ RGB値の分割 ≫================
function rgbSplit(col){
var ret = col.match(/\d+/g);
for (var i=0; i<ret.length; i++){
ret[i]*=1;
}
return (ret);
}
// ==========≪ フラッシュにもフィルタを適用 ≫==========
function applyFlash(){
for (var i=0, obj, objs=document.body.getElementsByTagName("object"); obj = objs[i]; i++){
obj = new FlashObj(obj, "OBJECT");
obj.trans();
}
for (var i=0, obj, objs=document.body.getElementsByTagName("embed"); obj = objs[i]; i++){
obj = new FlashObj(obj, "EMBED");
obj.trans();
}
}
// ################################################ //
// ################### メイン処理 #################### //
// ################################################ //
// ----------------< フィルタ関連の初期化 >----------------
var fCw = rgbSplit(FILTER_COLOR_W);
var fCb = rgbSplit(FILTER_COLOR_B);
var iCw = rgbSplit(INNER_COLOR_W);
var iCb = rgbSplit(INNER_COLOR_B);
// インナーがフィルターを超えていたら修正
for (var i=0; i<3; i++){
if (iCw[i] > fCw[i]) iCw[i] = fCw[i];
if (iCb[i] < fCb[i]) iCb[i] = fCb[i];
}
var filter = new RGBFilter(fCw, fCb);
var conv = new RGBConv(iCw, iCb, filter);
// ================≪ 起動処理 ≫================
// iframeではフィルターはいらない
if (window === window.parent) {
filter.applyFilter(document.getElementsByTagName("html")[0]);
}
window.addEventListener("DOMContentLoaded", afterTouch, false);
if (FLASH === true) {
window.addEventListener("DOMContentLoaded", applyFlash, false);
}
var mo = new MutationObserver(function(rec){this.adjustColor(rec)});
// MOをオーバーライド、メモリ節約(?)
MutationObserver.prototype.adjustColor = function(rec){
for (i=0; i<rec.length; i++){
for (j=0; j<rec[i].addedNodes.length; j++){
var node = rec[i].addedNodes[j];
if (node.tagName == "OBJECT" || node.tagName == "EMBED"){
var obj = new FlashObj(node, node.tagName);
obj.trans();
}
}
}
}