adjustColor2

convert colors to eye-friendly - webページの配色を目に優しく変更します (Flash対応)

  1. // ==UserScript==
  2. // @name adjustColor2
  3. // @namespace http://chiebukuro.yahoo.co.jp/my/gbjyn273
  4. // @author Syakku
  5. // @description convert colors to eye-friendly - webページの配色を目に優しく変更します (Flash対応)
  6. // @include *
  7. // @version 2.0
  8. // @run-at document-start
  9. // ==/UserScript==
  10.  
  11. // ================≪ OPTION ≫================
  12.  
  13. // ----------------< white" & "black" color >----------------
  14. var FILTER_COLOR_W = "rgb(234, 229, 227)";
  15. var FILTER_COLOR_B = "rgb(41, 34, 29)";
  16. var INNER_COLOR_W = "rgb(211, 203, 198)";
  17. var INNER_COLOR_B = "rgb(41, 34, 29)"; // gb is disabled
  18.  
  19. // OPTION : apply to Flash object
  20. var FLASH = true;
  21.  
  22. // OPTION : skip <a> tag (link anchor)
  23. // OPTION : <a> タグ、つまりリンク文字列を処理から除外します
  24. var A_SKIP = true;
  25.  
  26.  
  27.  
  28. // ################################################ //
  29. // ################# オブジェクト #################### //
  30. // ################################################ //
  31. // ================ <色縮小オブジェクト ≫================
  32. function RGBConv(w, b, pre_filter){
  33. this.w = w, this.b = b, this.pf = pre_filter;
  34. this.init();
  35. }
  36.  
  37. // ----------------< 初期化 >----------------
  38. RGBConv.prototype.init = function(){
  39.  
  40. this.pr = (this.w[0] - this.b[0]) / 255;
  41. this.pg = (this.w[1] - this.b[1]) / 255;
  42. this.pb = (this.w[2] - this.b[2]) / 255;
  43. this.pr2 = this.b[0], this.pg2 = this.b[1], this.pb2 = this.b[2];
  44. // 先に計算
  45. this.ra = this.pf.r * this.pf.a;
  46. this.ga = this.pf.g * this.pf.a;
  47. this.ba = this.pf.b * this.pf.a;
  48. this.ia = 1 - this.pf.a;
  49. // 色変換の結果保持配列
  50. this.lBefore = [];
  51. this.lAfter = [];
  52. this.lCount = 0;
  53. }
  54.  
  55. // ----------------< 色変換メソッド >----------------
  56. RGBConv.prototype.conv = function(bg){
  57. // 処理する必要のないカラーは省く
  58. // (本スクリプトで処理後のカラーは大抵変な数字になっている)
  59. // (それを利用して簡易的ではあるが二重処理を防いでいる)
  60. for (var i=0; n<this.lCount; i++){
  61. if (bg === this.lAfter[i]) return;
  62. }
  63. // 既存のカラーは省エネ
  64. for (var i=0; i<this.lCount; i++){
  65. if (bg === this.lBefore[i]){
  66. tElm.style.color = this.lAfter[i];
  67. return;
  68. }
  69. }
  70. var ret = this.calc(bg);
  71. this.lBefore[lCount] = bg;
  72. this.lAfter[lCount] = ret;
  73. this.lCount++;
  74. return ret;
  75. }
  76.  
  77. // ----------------< 計算メソッド >----------------
  78. RGBConv.prototype.calc = function(bg){
  79.  
  80. var col = rgbSplit(bg);
  81. var colR = col[0] * this.pr + this.pr2*1;
  82. var colG = col[1] * this.pg + this.pg2*1;
  83. var colB = col[2] * this.pb + this.pb2*1;
  84. // フィルターによる効果を考慮
  85. // filter.r + (colR - filter.r) / filter.ia と同義
  86. colR = Math.floor((colR - this.ra) / this.ia);
  87. colG = Math.floor((colG - this.ga) / this.ia);
  88. colB = Math.floor((colB - this.ba) / this.ia);
  89.  
  90. if (typeof col[3] === "undefined") {
  91. return ("rgb(" + colR + ", " + colG + ", " + colB + ")");
  92. }
  93. return ("rgb(" + colR + ", " + colG + ", " + colB + ", " + col[3] + ")"); // rgba
  94. }
  95.  
  96. // ================≪ フィルタオブジェクト≫================
  97. function RGBFilter(w, b){
  98. this.wr = w[0], this.wg = w[1], this.wb = w[2];
  99. this.br = b[0];
  100. this.r = 0, this.b = 0, this.g = 0, this.a = 0, this.str = 0;
  101. this.init();
  102. }
  103.  
  104. // ----------------< 初期化 >----------------
  105. RGBFilter.prototype.init = function(){
  106. with(this){
  107. // trをwr-0と100-wrの総計で割ってwrの取り分を決める
  108. r = (wr-br) / (255 - (wr-br)) * br + br;
  109. a = br / r; // 中心値rが気をつけないと 0 や 255 になって 0 除算になる
  110. if (isNaN(a) === true) a = (wr-255) / (r-255);
  111.  
  112. g = r - (wr-wg) / a;
  113. b = r - (wr-wb) / a;
  114. str = "rgba(" + Math.floor(r) + ", " + Math.floor(g) + ", " + Math.floor(b) + ", " + a + ")";
  115. }
  116. }
  117.  
  118. // ----------------< フィルター適用 >----------------
  119. RGBFilter.prototype.applyFilter = function(elm){
  120. var f = document.createElement("ac_filter");
  121. f.id = "adjustColor";
  122. with (f.style){
  123. backgroundColor = this.str;
  124. pointerEvents = "none";
  125. height = "100%";
  126. width = "100%";
  127. display = "block";
  128. position = "fixed";
  129. zIndex = "2147483647";
  130. }
  131.  
  132. for (var i=0, len=elm.children.length; i<len; i++){
  133. if (elm.children[i].tagName !== "HEAD"){
  134. elm.insertBefore(f, elm.children[i]);
  135. break;
  136. } else if (i === len - 1){
  137. elm.insertBefore(f, null);
  138. break
  139. }
  140. }
  141. }
  142.  
  143.  
  144. // ================≪ フラッシュオブジェクト ≫================
  145. function FlashObj(obj, type){
  146. this.obj = obj;
  147. this.type = type;
  148. this.init();
  149. }
  150.  
  151. // ----------------< 初期化 >----------------
  152. FlashObj.prototype.init = function(){
  153. if (this.type == "OBJECT"){
  154. this.name = "value";
  155. var params = this.obj.querySelector("param[name='wmode']");
  156. if (params){
  157. this.wmode = params;
  158. } else {
  159. // paramが存在しないとき用(動作未確認)
  160. var param = document.createElement("param");
  161. param.setAttribute("name", "wmode");
  162. this.obj.appendChild(param);
  163. this.wmode = param;
  164. }
  165. } else {
  166. this.name = "wmode";
  167. this.wmode = this.obj;
  168. }
  169. }
  170.  
  171. // ----------------< 透過メソッド >----------------
  172. FlashObj.prototype.trans = function(){
  173.  
  174. this.wmode.setAttribute(this.name, "transparent");
  175. this.obj.src = this.obj.src;
  176. this.obj.data = this.obj.data;
  177. }
  178.  
  179. // ################################################ //
  180. // ################### メソッド ###################### //
  181. // ################################################ //
  182.  
  183. // ================≪ 追加処理 ≫================
  184. function afterTouch(){
  185.  
  186. mo.observe(document.body, {childList: true, subtree: true});
  187. var elm;
  188. if (A_SKIP) elm = document.querySelector(":not(A)");
  189. else elm = document.getElementsByTagName("*");
  190. for (var i = 0; i < elm.length; i++) {
  191.  
  192. var tElm = elm[i];
  193. var style = window.getComputedStyle(tElm, null);
  194. var fgColor = style.getPropertyValue("color");
  195. var tColor = conv.conv(fgColor);
  196. if (tColor) tElm.style.color = tColor;
  197. }
  198. }
  199.  
  200.  
  201. // ================≪ RGB値の分割 ≫================
  202. function rgbSplit(col){
  203. var ret = col.match(/\d+/g);
  204. for (var i=0; i<ret.length; i++){
  205. ret[i]*=1;
  206. }
  207. return (ret);
  208. }
  209.  
  210.  
  211. // ==========≪ フラッシュにもフィルタを適用 ≫==========
  212. function applyFlash(){
  213.  
  214. for (var i=0, obj, objs=document.body.getElementsByTagName("object"); obj = objs[i]; i++){
  215. obj = new FlashObj(obj, "OBJECT");
  216. obj.trans();
  217. }
  218. for (var i=0, obj, objs=document.body.getElementsByTagName("embed"); obj = objs[i]; i++){
  219. obj = new FlashObj(obj, "EMBED");
  220. obj.trans();
  221. }
  222. }
  223.  
  224.  
  225.  
  226. // ################################################ //
  227. // ################### メイン処理 #################### //
  228. // ################################################ //
  229.  
  230. // ----------------< フィルタ関連の初期化 >----------------
  231. var fCw = rgbSplit(FILTER_COLOR_W);
  232. var fCb = rgbSplit(FILTER_COLOR_B);
  233. var iCw = rgbSplit(INNER_COLOR_W);
  234. var iCb = rgbSplit(INNER_COLOR_B);
  235.  
  236. // インナーがフィルターを超えていたら修正
  237. for (var i=0; i<3; i++){
  238. if (iCw[i] > fCw[i]) iCw[i] = fCw[i];
  239. if (iCb[i] < fCb[i]) iCb[i] = fCb[i];
  240. }
  241.  
  242. var filter = new RGBFilter(fCw, fCb);
  243. var conv = new RGBConv(iCw, iCb, filter);
  244.  
  245.  
  246. // ================≪ 起動処理 ≫================
  247. // iframeではフィルターはいらない
  248. if (window === window.parent) {
  249. filter.applyFilter(document.getElementsByTagName("html")[0]);
  250. }
  251.  
  252. window.addEventListener("DOMContentLoaded", afterTouch, false);
  253.  
  254. if (FLASH === true) {
  255. window.addEventListener("DOMContentLoaded", applyFlash, false);
  256. }
  257.  
  258. var mo = new MutationObserver(function(rec){this.adjustColor(rec)});
  259.  
  260. // MOをオーバーライド、メモリ節約(?)
  261. MutationObserver.prototype.adjustColor = function(rec){
  262.  
  263. for (i=0; i<rec.length; i++){
  264. for (j=0; j<rec[i].addedNodes.length; j++){
  265. var node = rec[i].addedNodes[j];
  266. if (node.tagName == "OBJECT" || node.tagName == "EMBED"){
  267. var obj = new FlashObj(node, node.tagName);
  268. obj.trans();
  269. }
  270. }
  271. }
  272. }