コンテンツにスキップ

利用者:Waiesu/ContrastChecker.js

お知らせ: 保存した後、ブラウザのキャッシュをクリアしてページを再読み込みする必要があります。

多くの WindowsLinux のブラウザ

  • Ctrl を押しながら F5 を押す。

Mac における Safari

  • Shift を押しながら、更新ボタン をクリックする。

Mac における ChromeFirefox

  • Cmd Shift を押しながら R を押す。

詳細についてはWikipedia:キャッシュを消すをご覧ください。

//<syntaxhighlight lang=javascript>
/*

== コントラストチェッカー ==

[[User:Waiesu/ContrastChecker.js]]
; 開発者: [[User:Waiesu]]

; 導入方法
	: [[Special:MyPage/common.js]]またはスキンごとのjsに
	:	importScript('User:Waiesu/ContrastChecker.js');
	: を追加してください

; 説明
	: (ベクタースキンの場合)ページ左部のツールバーにある「Contrast Checker」をクリックすると、ON/OFFの切り替えができます。
	: ONの状態だとウィンドウ左上に情報パネルが表示されます。
	:
	: 「マウスオーバーモード」は目的の要素へカーソルを乗せるとリアルタイムでコントラスト比の確認ができます。
	: 「インプットモード」は情報パネルのテキストボックス(またはカラーピック)を上書きすることでコントラスト比の確認ができます。

; クラスの説明
	:; 生成
		:: var hoge = new CCcolor(color) -- CCcolor // colorはString。CSSのrgb(), rgba(), hsl(), hsla(), #HEX, CSSカラーネームに対応。
	:; プロパティ
		:: hoge.RGB -- Array // [R, G, B]
		:: hoge.RGBA -- Array // [R, G, B, A]
		:: hoge.HSL -- Array // [H, S, L]
		:: hoge.HSLA -- Array // [H, S, L, A]
		:: hode.sRGB -- Array // [sR, sG, sB]
		:: hoge.luminance -- Number // 輝度
		:: hoge.brightness -- Number // 明度(参考値)
		:: CCcolor.COLOR_NAME -- Object // カラーネーム一覧
	:; メソッド
		:: hoge.toString(type) -- String // CSS形式の文字列。typeは"rgb"と"hex"に対応。
		:: CCcolor.cRatio(color1, color2) -- Number // 2色のコントラスト比。color1, color2はそれぞれCCcolorかCCcolorを生成できる文字列。
		:: CCcolor.bDiff(color1, color2) -- Number // 2色の明度差(参考値)。引数は同上。
		:: CCcolor.cDiff(color1, color2) -- Number // 2色の色差(参考値)。引数は同上。
*/

'use strict';

var CCcolor = function (color) {
	if (!(this instanceof CCcolor)) throw new TypeError('Call constructor with "new": CCcolor');
	if (typeof color != 'string') throw new TypeError('The argument should be string.');
	
	function convertPercentage(number, limit) {
		for (var i = 0; i < 4; i++) {
			var matches = (number[i] || '100%').match(/([0-9.]+)(%)?/);
			if (!matches) {
				throw new TypeError('The number in argument "' + number[i] + '" is incorrect format.');
			} else if (matches[2]) {
				number[i] = limit[i] * matches[1] / 100;
			} else {
				number[i] = Number(matches[1]);
			}
		}
		return number;
	}
	
	var type, rgba, hsla;
	if (color.match('hsl')) {
		hsla = convertPercentage(color.match(/[0-9.%]+/g), [360, 1, 1, 1]);
		
		var max, min;
		if (hsla[2] < 0.5) {
			max = 255 * (hsla[2] + hsla[1] * hsla[2]);
			min = 255 * (hsla[2] - hsla[1] * hsla[2]);
		} else {
			max = 255 * (hsla[2] + hsla[1] * (1 - hsla[2]));
			min = 255 * (hsla[2] - hsla[1] * (1 - hsla[2]));
		}
		if (hsla[0] < 60) {
			rgba = [max, hsla[0] / 60 * (max - min) + min, min];
		} else if (hsla[0] < 120) {
			rgba = [(120 - hsla[0]) / 60 * (max - min) + min, max, min];
		} else if (hsla[0] < 180) {
			rgba = [min, max, (hsla[0] - 120) / 60 * (max - min) + min];
		} else if (hsla[0] < 240) {
			rgba = [min, (240 - hsla[0]) / 60 * (max - min) + min, max];
		} else if (hsla[0] < 300) {
			rgba = [(hsla[0] - 240) / 60 * (max - min) + min, min, max];
		} else {
			rgba = [max, min, (360 - hsla[0]) / 60 * (max - min) + min];
		}
		rgba[3] = hsla[3];
	} else if (color.match('rgb')) {
		rgba = convertPercentage(color.match(/[0-9.%]+/g), [255, 255, 255, 1]);
	} else if (color.charAt(0) == '#') {
		if (color.length < 6) {
			rgba = color.match(/[0-9A-F]/gi);
			for (var i = 0; i < 4; i++) rgba[i] = Number('0x' + (rgba[i] || 'F') + (rgba[i] || 'F'));
		} else {
			rgba = color.match(/[0-9A-F]{2}/gi);
			for (var j = 0; j < 4; j++) rgba[j] = Number('0x' + (rgba[j] || 'FF'));
		}
	} else if (color == 'transparent') {
		rgba = [0, 0, 0, 0];
	} else if (color in CCcolor.COLOR_NAME) {
		rgba = CCcolor.COLOR_NAME[color];
		rgba[3] = 1;
	} else {
		throw new TypeError('The argument "' + color + '" is incorrect format.');
	}

	// Common
	var srgb = [], // [R, G, B]
		luminance = 0,
		brightness = 0;
	var COEFFICIENT = {
		L: [0.2126, 0.7152, 0.0722],
		B: [76.245, 149.685, 29.07],
	};
	
	for (var k = 0; k < 3; k++) {
		// sRGB
		srgb[k] = rgba[k] / 255;
		// 輝度
		luminance += COEFFICIENT.L[k] * (srgb[k] <= 0.03928 ? srgb[k] / 12.92 : Math.pow(((srgb[k] + 0.055) / 1.055), 2.4));
		// 明度(参考値)
		brightness += COEFFICIENT.B[k] * srgb[k];
	}
	
	// HSL
	if (!hsla) {
		var _max = Math.max.apply(null, srgb), _min = Math.min.apply(null, srgb);
		hsla = [0, 0, (_max + _min) / 2];
		if (_max != _min) {
			for (var l = 0; l < 3; l++) {
	    		if (_max == srgb[l]) {
	    			hsla[0] = 60 * ((srgb[l + 1] || srgb[l - 2] || 0) - (srgb[l - 1] || srgb[l + 2] || 0)) / (_max - _min) + 120 * l;
	    		}
			}
			if (hsla[2] < 0.5) {
				hsla[1] = (hsla[2] - _min) / hsla[2];
			} else {
				hsla[1] = (_max - hsla[2]) / (1 - hsla[2]);
			}
		}
		hsla[3] = rgba[3];
	}
	
	this.alpha = rgba[3];
	this.RGB = rgba.slice(0, 3);
	this.RGBA = rgba;
	this.sRGB = srgb;
	this.HSL = hsla.slice(0, 3);
	this.HSLA = hsla;
	this.luminance = luminance;
	this.brightness = brightness;

	return this;
};

/* プロパティ */
CCcolor.COLOR_NAME = {
	aliceblue: [240, 248, 255],
	antiquewhite: [250, 235, 215],
	aqua: [0, 255, 255],
	aquamarine: [127, 255, 212],
	azure: [240, 255, 255],
	beige: [245, 245, 220],
	bisque: [255, 228, 196],
	black: [0, 0, 0],
	blanchedalmond: [255, 235, 205],
	blue: [0, 0, 255],
	blueviolet: [138, 43, 226],
	brown: [165, 42, 42],
	burlywood: [222, 184, 135],
	cadetblue: [95, 158, 160],
	chartreuse: [127, 255, 0],
	chocolate: [210, 105, 30],
	coral: [255, 127, 80],
	cornflowerblue: [100, 149, 237],
	cornsilk: [255, 248, 220],
	crimson: [220, 20, 60],
	cyan: [0, 255, 255],
	darkblue: [0, 0, 139],
	darkcyan: [0, 139, 139],
	darkgoldenrod: [184, 134, 11],
	darkgray: [169, 169, 169],
	darkgreen: [0, 100, 0],
	darkgrey: [169, 169, 169],
	darkkhaki: [189, 183, 107],
	darkmagenta: [139, 0, 139],
	darkolivegreen: [85, 107, 47],
	darkorange: [255, 140, 0],
	darkorchid: [153, 50, 204],
	darkred: [139, 0, 0],
	darksalmon: [233, 150, 122],
	darkseagreen: [143, 188, 143],
	darkslateblue: [72, 61, 139],
	darkslategray: [47, 79, 79],
	darkslategrey: [47, 79, 79],
	darkturquoise: [0, 206, 209],
	darkviolet: [148, 0, 211],
	deeppink: [255, 20, 147],
	deepskyblue: [0, 191, 255],
	dimgray: [105, 105, 105],
	dimgrey: [105, 105, 105],
	dodgerblue: [30, 144, 255],
	firebrick: [178, 34, 34],
	floralwhite: [255, 250, 240],
	forestgreen: [34, 139, 34],
	fuchsia: [255, 0, 255],
	gainsboro: [220, 220, 220],
	ghostwhite: [248, 248, 255],
	gold: [255, 215, 0],
	goldenrod: [218, 165, 32],
	gray: [128, 128, 128],
	green: [0, 128, 0],
	greenyellow: [173, 255, 47],
	grey: [128, 128, 128],
	honeydew: [240, 255, 240],
	hotpink: [255, 105, 180],
	indianred: [205, 92, 92],
	indigo: [75, 0, 130],
	ivory: [255, 255, 240],
	khaki: [240, 230, 140],
	lavender: [230, 230, 250],
	lavenderblush: [255, 240, 245],
	lawngreen: [124, 252, 0],
	lemonchiffon: [255, 250, 205],
	lightblue: [173, 216, 230],
	lightcoral: [240, 128, 128],
	lightcyan: [224, 255, 255],
	lightgoldenrodyellow: [250, 250, 210],
	lightgray: [211, 211, 211],
	lightgreen: [144, 238, 144],
	lightgrey: [211, 211, 211],
	lightpink: [255, 182, 193],
	lightsalmon: [255, 160, 122],
	lightseagreen: [32, 178, 170],
	lightskyblue: [135, 206, 250],
	lightslategray: [119, 136, 153],
	lightslategrey: [119, 136, 153],
	lightsteelblue: [176, 196, 222],
	lightyellow: [255, 255, 224],
	lime: [0, 255, 0],
	limegreen: [50, 205, 50],
	linen: [250, 240, 230],
	magenta: [255, 0, 255],
	maroon: [128, 0, 0],
	mediumaquamarine: [102, 205, 170],
	mediumblue: [0, 0, 205],
	mediumorchid: [186, 85, 211],
	mediumpurple: [147, 112, 219],
	mediumseagreen: [60, 179, 113],
	mediumslateblue: [123, 104, 238],
	mediumspringgreen: [0, 250, 154],
	mediumturquoise: [72, 209, 204],
	mediumvioletred: [199, 21, 133],
	midnightblue: [25, 25, 112],
	mintcream: [245, 255, 250],
	mistyrose: [255, 228, 225],
	moccasin: [255, 228, 181],
	navajowhite: [255, 222, 173],
	navy: [0, 0, 128],
	oldlace: [253, 245, 230],
	olive: [128, 128, 0],
	olivedrab: [107, 142, 35],
	orange: [255, 165, 0],
	orangered: [255, 69, 0],
	orchid: [218, 112, 214],
	palegoldenrod: [238, 232, 170],
	palegreen: [152, 251, 152],
	paleturquoise: [175, 238, 238],
	palevioletred: [219, 112, 147],
	papayawhip: [255, 239, 213],
	peachpuff: [255, 218, 185],
	peru: [205, 133, 63],
	pink: [255, 192, 203],
	plum: [221, 160, 221],
	powderblue: [176, 224, 230],
	purple: [128, 0, 128],
	rebeccapurple: [102, 51, 153],
	red: [255, 0, 0],
	rosybrown: [188, 143, 143],
	royalblue: [65, 105, 225],
	saddlebrown: [139, 69, 19],
	salmon: [250, 128, 114],
	sandybrown: [244, 164, 96],
	seagreen: [46, 139, 87],
	seashell: [255, 245, 238],
	sienna: [160, 82, 45],
	silver: [192, 192, 192],
	skyblue: [135, 206, 235],
	slateblue: [106, 90, 205],
	slategray: [112, 128, 144],
	slategrey: [112, 128, 144],
	snow: [255, 250, 250],
	springgreen: [0, 255, 127],
	steelblue: [70, 130, 180],
	tan: [210, 180, 140],
	teal: [0, 128, 128],
	thistle: [216, 191, 216],
	tomato: [255, 99, 71],
	turquoise: [64, 224, 208],
	violet: [238, 130, 238],
	wheat: [245, 222, 179],
	white: [255, 255, 255],
	whitesmoke: [245, 245, 245],
	yellow: [255, 255, 0],
	yellowgreen: [154, 205, 50]
};

/* 静的メソッド */
CCcolor.cRatio = function (c1, c2) {
	if (!(c1 instanceof CCcolor)) c1 = new CCcolor(c1);
	if (!(c2 instanceof CCcolor)) c2 = new CCcolor(c2);
	var maxL = Math.max(c1.luminance, c2.luminance);
	var minL = Math.min(c1.luminance, c2.luminance);
	return (maxL + 0.05) / (minL + 0.05);
};
CCcolor.bDiff = function (c1, c2) {
	if (!(c1 instanceof CCcolor)) c1 = new CCcolor(c1);
	if (!(c2 instanceof CCcolor)) c2 = new CCcolor(c2);
	return Math.abs(c1.brightness - c2.brightness);
};
CCcolor.cDiff = function (c1, c2) {
	if (!(c1 instanceof CCcolor)) c1 = new CCcolor(c1);
	if (!(c2 instanceof CCcolor)) c2 = new CCcolor(c2);
	var cDiff = 0;
	for (var i = 0; i < 3; i++) cDiff += Math.abs(c1.RGBA[i] - c2.RGBA[i]);
	return cDiff;
};

/* 動的メソッド */
CCcolor.prototype = {
	constructor: CCcolor,
	toString: function (type) {
		if (!type) type = 'hex';
		switch (type) {
			case 'rgb': return 'rgb(' + this.RGBA.join() + ')';
			case 'hex':
				var hex = '#';
				for (var i = 0; i < 3; i++) hex += ('0' + Number(this.RGBA[i]).toString(16)).slice(-2);
				return hex;
		}
	}
};
(function (mw) {
	// Polyfill
	if (!Element.prototype.closest) {
		Element.prototype.closest = Element.prototype.matches ||
			Element.prototype.mozMatchesSelector ||
			Element.prototype.msMatchesSelector ||
			Element.prototype.oMatchesSelector ||
			Element.prototype.webkitMatchesSelector;
	}

	var display = localStorage.jawpContrastChecker || 'none';
	var userskin = mw.config.get('skin');

	// Create Table
	var table = document.createElement('table');
	table.id = 'cc-table';
	table.className = 'wikitable';
	table.style.display = display;
	table.style.position = 'fixed';
	table.style.top = 0;
	table.style.tableLayout = 'fixed';
	table.style.zIndex = 101;
	table.style.wordBreak = 'keep-all';
	switch (userskin) {
		case 'cologneblue':
			table.style.fontSize = '0.75em';
			table.style.width = '140px';
			break;
		case 'vector':
			table.style.margin = '1em';
			table.style.fontSize = '0.75em';
			table.style.width = '12.7em';
			break;
		case 'modern':
			table.style.margin = '0.5em';
			table.style.fontSize = '1.3em';
			table.style.width = '13em';
			break;
		case 'monobook':
			table.style.fontSize = '1.16em';
			table.style.width = '12.7em';
			break;
	}

	var htmlCaption = '<caption hidden>コントラストチェッカー';
	var htmlThead = '<thead><tr><th>サンプル<td><span style="font-size:150%">TEXT</span>';
	var htmlTbody = '<tbody>';
	var thtext = [
		'背景色',
		'文字色',
		'<span style="display:inline-block;transform-origin:left center;transform:scaleX(0.77);">コントラスト比</span>',
		'WCAG 2.0<wbr>適合レベル',
		'明度差',
		'色差'
	];
	var tdtext = [
		'<input class=cc-changeable-bgcolor type=text><br><input class=cc-changeable-bgcolor type=color><dl><dt>H<dd id=cc-bgcolor-h><dt>S<dd id=cc-bgcolor-s><dt>L<dd id=cc-bgcolor-l></dl>',
		'<input class=cc-changeable-color type=text><br><input class=cc-changeable-color type=color><dl><dt>H<dd id=cc-color-h><dt>S<dd id=cc-color-s><dt>L<dd id=cc-color-l></dl>',
		'<span><input type=text readonly style="color:#222;width:3em;">: 1</span>',
		'<input type=text readonly style="font-size:1.25em;">',
		'<input type=text readonly>',
		'<input type=text readonly>',
	];
	for (var i = 0, len = thtext.length; i < len; i++) {
		htmlTbody += '<tr><th>' + thtext[i] + '<td>' + tdtext[i];
	}
	var htmlTfoot = '<tfoot><tr><td colspan=2 style="padding:0;"><form name=ContrastChecker class=center><label><input type=radio name=onoff checked>マウスオーバーモード</label><br><label><input type=radio name=onoff>インプットモード</label></form>';
	var buttonText = ['リンクを訪問済みにする', '閉じる'];
	var buttonId = ['cc-togglelink', 'cc-close'];
	for (var b = 0; b < 2; b++) {
		htmlTfoot += '<button id=' + buttonId[b] + ' class=mw-ui-button type=button style="display:block;margin:auto;padding:.25em;">' + buttonText[b] + '</button>';
	}

	table.insertAdjacentHTML('beforeend', htmlCaption + htmlThead + htmlTbody + htmlTfoot);
	document.body.appendChild(table);

	// For Outputs
	var tds = table.getElementsByTagName('td');
	var inputs = table.getElementsByTagName('input');
	var bH = document.getElementById('cc-bgcolor-h');
	var bS = document.getElementById('cc-bgcolor-s');
	var bL = document.getElementById('cc-bgcolor-l');
	var cH = document.getElementById('cc-color-h');
	var cS = document.getElementById('cc-color-s');
	var cL = document.getElementById('cc-color-l');
	
	// For Inputs
	var changeableBgcolor = document.getElementsByClassName('cc-changeable-bgcolor');
	changeableBgcolor[0].onchange = changedBgcolor;
	changeableBgcolor[1].onchange = changedBgcolor;
	var changeableColor = document.getElementsByClassName('cc-changeable-color');
	changeableColor[0].onchange = changedColor;
	changeableColor[1].onchange = changedColor;

	// Add Button To Toolbar
	var li = document.createElement('li');
	li.id = 't-contrastchecker';
	var a = document.createElement('a');
	a.textContent = 'Contrast Checker';
	a.href = '#';
	a.role = 'button';
	li.appendChild(a);

	var toolbar = document.getElementById('p-tb') || document.getElementById('p-cactions');
	toolbar.getElementsByTagName('ul')[0].appendChild(li);

	a.addEventListener('click', function(e) {
		e.preventDefault();
		if (table.style.display == 'none') {
			table.style.display = 'table';
			localStorage.jawpContrastChecker = 'table';
			document.body.addEventListener('mouseover', main, true);
		} else {
			close();
		}
	}, false);

	// Style
	var style = '<style>#cc-table tbody input{width:90%;} #cc-table input[readonly]{border:0;background-color:inherit;color:#fff;} #cc-table dt,#cc-table dd{display:inline-block;margin:0;width:40%;}';
	switch (userskin) {
		case 'cologneblue':
			style += 'a.cc-visited,a.extiw.cc-visited,a.external.cc-visited{color:#8d0749} a.new.cc-visited{color:#c20}';
			break;
		case 'vector':
			style += 'a.cc-visited{color:#0b0080} a.extiw.cc-visited{color:#636} a.external.cc-visited{color:#636} a.new.cc-visited{color:#a55858}';
			break;
		case 'modern':
			style += 'a.cc-visited{color:#5a3696} a.extiw.cc-visited,a.external.cc-visited{color:#36b} a.new.cc-visited{color:#a55858}';
			break;
		case 'monobook':
			style += 'a.cc-visited{color:#5a3696} a.extiw.cc-visited,a.external.cc-visited{color:#636} a.new.cc-visited{color:#a55858}';
			break;
	}
	style += '</style>';
	document.head.insertAdjacentHTML('beforeend', style);

	document.getElementById('cc-togglelink').addEventListener('click', function(e) {
		var target = e.target;
		var textTo;
		var allLink = document.getElementsByTagName('a');
		if (target.textContent === 'リンクを訪問済みにする') {
			for (var i = 0, ilen = allLink.length; i < ilen; i++) {
				allLink[i].classList.add('cc-visited');
			}
			textTo = '未訪問';
		} else {
			for (var j = 0, jlen = allLink.length; j < jlen; j++) {
				allLink[j].classList.remove('cc-visited');
			}
			textTo = '訪問済み';
		}
		target.textContent = 'リンクを' + textTo + 'にする';
	}, false);
	document.getElementById('cc-close').addEventListener('click', close, false);

	if (display == 'table') document.body.addEventListener('mouseover', main, true);

	function close() {
		table.style.display = 'none';
		localStorage.jawpContrastChecker = 'none';
		document.body.removeEventListener('mouseover', main, true);
	}

	function main(e) {
		var target = e.target;
		if (target.closest('table') == table) return;
		if (document.forms.ContrastChecker.onoff[1].checked) return;
		var style = document.defaultView.getComputedStyle(target, null);
		var font = {
			size: parseInt(style.getPropertyValue('font-size')),
			weight: style.getPropertyValue('font-weight')
		};

		if (isNaN(font.weight)) {
			if (font.weight == 'bolder') {
				font.weight = 'bold';
			} else if (font.weight == 'lighter') {
				font.weight = 'normal';
			}
		} else {
			if (font.weight > 500) {
				font.weight = 'bold';
			} else {
				font.weight = 'normal';
			}
		}

		var bgcolor = new CCcolor(style.getPropertyValue('background-color'));
		var color = new CCcolor(style.getPropertyValue('color'));

		while (!bgcolor.RGBA[3]) {
			if (target == document.documentElement) {
				bgcolor = new CCcolor('white');
			} else {
				target = target.parentNode;
				style = document.defaultView.getComputedStyle(target, null);
				bgcolor = new CCcolor(style.getPropertyValue('background-color'));
			}
		}
		var ratio = CCcolor.cRatio(bgcolor, color);

		var lang = e.target.lang || document.documentElement.lang || 'ja';
		var size = { bold: 14, normal: 18 };
		if (/ja|ko|zh/.test(lang)) size = { bold: 18, normal: 22 };
		var level = [
			[7, '#060', 'AAA'],
			[4.5, '#660', 'AA']
		];
		if (font.size >= size[font.weight]) {
			level.splice(1, 0, [4.5, '#060', 'AAA–']);
			level.splice(4, 0, [3, '#660', 'AA–']);
		}
		var rank;
		for (var i = 0, len = level.length; i < len; i++) {
			if (ratio >= level[i][0]) {
				rank = [level[i][2], level[i][1]];
				break;
			}
		}
		if (!rank) rank = ['A', '#600'];

		output({
			bgcolor: bgcolor,
			color: color,
			ratio: ratio,
			rank: rank[0],
			rankbg: rank[1],
			bdiff: CCcolor.bDiff(bgcolor, color),
			cdiff: CCcolor.cDiff(bgcolor, color)
		});
	}

	function changedBgcolor(e) {
		var value = e.target.value;
		var bgcolor = new CCcolor(value);
		changeableBgcolor[0].value = value;
		changeableBgcolor[1].value = value;
		var color = new CCcolor(changeableColor[0].value);
		var ratio = CCcolor.cRatio(bgcolor, color);
		var rank;
		if (ratio < 4.5) {
			rank = ['A', '#600'];
		} else if (ratio < 7) {
			rank = ['AA', '#660'];
		} else {
			rank = ['AAA', '#060'];
		}
		output({
			bgcolor: bgcolor,
			color: color,
			ratio: ratio,
			rank: rank[0],
			rankbg: rank[1],
			bdiff: CCcolor.bDiff(bgcolor, color),
			cdiff: CCcolor.cDiff(bgcolor, color)
		});
	}
	function changedColor(e) {
		var value = e.target.value;
		var bgcolor = new CCcolor(changeableBgcolor[0].value);
		var color = new CCcolor(value);
		changeableColor[0].value = value;
		changeableColor[1].value = value;
		var ratio = CCcolor.cRatio(bgcolor, color);
		var rank;
		if (ratio < 4.5) {
			rank = ['A', '#600'];
		} else if (ratio < 7) {
			rank = ['AA', '#660'];
		} else {
			rank = ['AAA', '#060'];
		}
		output({
			bgcolor: bgcolor,
			color: color,
			ratio: ratio,
			rank: rank[0],
			rankbg: rank[1],
			bdiff: CCcolor.bDiff(bgcolor, color),
			cdiff: CCcolor.cDiff(bgcolor, color)
		});
	}

	function output(result) {
		var strBgcolor = result.bgcolor.toString();
		var strColor = result.color.toString();
		tds[0].style.backgroundColor = strBgcolor;
		tds[0].style.color = strColor;
		inputs[0].value = strBgcolor;
		inputs[1].value = strBgcolor;
		bH.textContent = Math.round(result.bgcolor.HSL[0]);
		bS.textContent = Math.round(result.bgcolor.HSL[1] * 100) + '%';
		bL.textContent = Math.round(result.bgcolor.HSL[2] * 100) + '%';
		inputs[2].value = strColor;
		inputs[3].value = strColor;
		cH.textContent = Math.round(result.color.HSL[0]);
		cS.textContent = Math.round(result.color.HSL[1] * 100) + '%';
		cL.textContent = Math.round(result.color.HSL[2] * 100) + '%';
		inputs[4].value = Math.round(result.ratio * 100) / 100;
		tds[4].style.backgroundColor = result.rankbg;
		inputs[5].value = result.rank;
		tds[5].style.backgroundColor = result.bdiff < 125 ? '#660' : '#060';
		inputs[6].value = Math.round(result.bdiff * 100) / 100;
		tds[6].style.backgroundColor = result.cdiff < 500 ? '#660' : '#060';
		inputs[7].value = result.cdiff;
	}
})(mediaWiki);

//</syntaxhighlight>