NOTICE: By continued use of this site you understand and agree to the binding Terms of Service and Privacy Policy.
// ==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(); } } } }