PageSourceSearch

https://tools.m-bsys.com/js/json-beautifier.js?20260403

js m-bsys.com collected 2026-10-03 23:17:56 UTC 8,676 bytes, 286 lines download raw bytes

1$(function() {
2
3	if (UA.browserName !== "Chrome" && UA.browserName !== "Firefox") {
4		$("#errPoint").hide();
5	}
6
7	// codemirror
8	var codemirrorOption = {
9		lineNumbers: true,
10		matchBrackets: true,
11		indentWithTabs: true,
12		indentUnit: 4,
13		foldGutter: true,
14		gutters: ["CodeMirror-linenumbers", "CodeMirror-foldgutter"],
15	};
16	editor1 = CodeMirror.fromTextArea($("#input")[0], codemirrorOption);
17	editor2 = CodeMirror.fromTextArea($("#result")[0], codemirrorOption);
18
19	editor1.on("change", function() {
20		$("#message").removeClass("info warn error").val("");
21		$("#errPoint").html("");
22	});
23
24	$("#btn").on("click", function () {
25		var source = editor1.getValue();
26		var obj = parse(source);
27		var indent = indentStrs[$("#indentStrs").val()];
28		if (indent === null) {
29			var output = stringify(obj);
30		} else {
31			var output = JSON.stringify(obj, null, indent);
32		}
33		if (output) {
34			editor2.setValue(output);
35
36			var sourceByte = TOOLS.string.countUtf8byte(source);
37			var outputByte = TOOLS.string.countUtf8byte(output);
38			var msgStr = "(UTF-8でのバイト数:" + sourceByte + " → " + outputByte + ")";
39			$("#message").val($("#message").val() + msgStr);
40
41		} else {
42			editor2.setValue("");
43		}
44	});
45
46	// SELECTボックスの中身の作成と選択時の挙動の設定。
47	$("#samples").append($("<option>"));
48	for (var key in samples) {
49		$("#samples").append($("<optgroup>").attr("label", key));
50		var group = samples[key]; 
51
52		for (var groupkey in group) {
53			$("#samples optgroup:last").append($("<option>").text(groupkey).val(group[groupkey]));
54		}
55	}
56	$("#samples").change(function() {
57		editor1.setValue($("#samples").val());
58		editor2.setValue("");
59		$("#message").removeClass("info warn error").val("");
60		$("#errPoint").html("");
61	});
62
63	for (var key in indentStrs) {
64		$("#indentStrs").append($("<option>").text(key).val(key));
65	}
66	$("#indentStrs").change(function() {
67		$("#btn").click();
68	});
69
70	for (var key in tabSize) {
71		$("#tabSize").append($("<option>").text(key).val(tabSize[key]));
72	}
73	$("#tabSize").change(function() {
74		editor2.options.tabSize = $("#tabSize").val();
75		editor2.refresh();
76	});
77	$("#tabSize").val(4);
78
79	$("#クリア").on("click", function() {
80		editor1.setValue("");
81		editor2.setValue("");
82		$("#message").removeClass("info warn error").val("");
83		$("#errPoint").html("");
84	});
85
86
87	$(".CodeMirror:eq(1)").on("dblclick", function() {
88		editor2.execCommand("selectAll");
89	});
90
91	// サイズ調整機能
92	$(".CodeMirror:eq(0)").resizable({
93		resize: function() {
94			editor1.setSize("100%", $(this).height());
95		},
96		handles: "s",
97		minHeight: 50,
98	});
99	$(".CodeMirror:eq(1)").resizable({
100		resize: function() {
101			editor2.setSize("100%", $(this).height());
102		},
103		handles: "s",
104		minHeight: 50,
105	});
106	$(".ui-resizable-handle.ui-resizable-s").prop("title", "この部分をドラッグすると、テキストエリアのサイズを変更できます。");
107	$("#autoFit").change(function() {
108		if ($("#autoFit").prop("checked")) {
109			editor2.setSize("100%", "auto");
110		} else {
111			editor2.setSize("100%", $(".CodeMirror:eq(1)").height() + 4);
112		}
113	});
114
115});
116
117var editor1;
118var editor2;
119
120var tabSize = {
121	"2文字": 2,
122	"4文字": 4,
123	"6文字": 6,
124	"8文字": 8,
125};
126
127var indentStrs = {
128	"TAB": "\t",
129	"半角空白 2個": 2,
130	"半角空白 4個": 4,
131	"半角空白 6個": 6,
132	"半角空白 8個": 8,
133	"全角空白 2個": "  ",
134	"アンダーバー 4個": "____",
135	"改行・インデントなし": null,
136	"改行・インデント・空白なし": 0,
137};
138
139function parse(str) {
140	if (!/^[ 	\n]*[{\[]/.test(str) || !/[\]}][ 	\n]*/.test(str)) {
141		var msg = "「{」で始まり「}」で終わる、もしくは「[」で始まり「]」で終わる、\n" +
142			"JSON形式もしくはJavaScriptオブジェクトを入力してください。";
143		$("#message").val(msg);
144		$("#message").addClass("error").removeClass("warn info");
145		return;
146	}
147
148	var obj;
149	try {
150		obj = JSON.parse(str);
151
152		var msgStr = "正しいJSON形式でした。\n";
153		$("#message").val(msgStr);
154		$("#message").addClass("info").removeClass("warn error");
155		$("#errPoint").html("");
156
157	} catch(e) {
158		$("#message").addClass("error").removeClass("warn info");
159		var msgStr = "正しいJSON形式ではありません。(";
160		var msg = e.message;
161		msgStr += msg + ")\n";
162
163		$("#message").val(msgStr);
164		dispErrPoint(msg, str);
165
166		if (/[()]/.test(str)) {
167			msgStr += "小カッコが含まれる文字列は、セキュリティの観点で処理を中断します。";
168			$("#message").val(msgStr);
169			return;
170		}
171
172		try {
173			var f = new Function("return " + str);
174		} catch(e) {
175			console.log(e);
176			return;
177		}
178
179		obj = f();
180		msgStr += "ただし、JavaScriptオブジェクトとしてパースできました。\n";
181		$("#message").addClass("warn").removeClass("info error");
182	}
183
184	$("#message").val(msgStr);
185	return obj;
186}
187
188function dispErrPoint(msg, str) {
189
190	if (UA.browserName == "Chrome") {
191		// Chromeの場合
192		var m = msg.match(/in JSON at position (\d+)/m);
193		var p = m[1] - 0;
194	} else if (UA.browserName == "Firefox") {
195		// Firefoxの場合
196		var m = msg.match(/at line (\d+) column (\d+)/m);
197		var l = m[1] - 0;
198		var p = m[2] - 1;
199		var lines = str.split(/\n/g);
200		str = lines[l - 1];
201	} else {
202		return;
203	}
204
205	var t1 = (p - 1 > 0) ? str.substr(0, p - 1) : "";
206	var t2 = (p > 0) ? mark2(str.substr(p - 1, 1)) : "";
207	var t3 = mark1(str.substr(p, 1));
208	var t4 = (str.length > p + 1) ? mark2(str.substr(p + 1, 1)) : "";
209	var t5 = (str.length > p + 2) ? str.substr(p + 2) : "";
210	$("#errPoint").html(t1 + t2 + t3 + t4 + t5);
211
212	function mark1(str) {
213		return "<span class='p1'>" + str + "</span>";
214	}
215	function mark2(str) {
216		return "<span class='p2'>" + str + "</span>";
217	}
218}
219// JSON - JavaScript | MDN
220// https://developer.mozilla.org/ja/docs/Web/JavaScript/Reference/Global_Objects/JSON#Polyfill
221function stringify(value) {
222	var toString = Object.prototype.toString;
223	var isArray = Array.isArray || function(a) {
224		return toString.call(a) === '[object Array]';
225	};
226	var escMap = {
227		'"': '\\"',
228		'\\': '\\\\',
229		'\b': '\\b',
230		'\f': '\\f',
231		'\n': '\\n',
232		'\r': '\\r',
233		'\t': '\\t'
234	};
235	var escFunc = function(m) {
236		return escMap[m] || '\\u' + (m.charCodeAt(0) + 0x10000).toString(16).substr(1);
237	};
238	var escRE = /[\\"\u0000-\u001F\u2028\u2029]/g; // "
239
240	if (value == null) {
241		return 'null';
242	} else if (typeof value === 'number') {
243		return isFinite(value) ? value.toString() : 'null';
244	} else if (typeof value === 'boolean') {
245		return value.toString();
246	} else if (typeof value === 'object') {
247		if (typeof value.toJSON === 'function') {
248			return stringify(value.toJSON());
249		} else if (isArray(value)) {
250			var res = '[';
251			for (var i = 0; i < value.length; i++)
252				res += (i ? ', ' : '') + stringify(value[i]);
253			return res + ']';
254		} else if (toString.call(value) === '[object Object]') {
255			var tmp = [];
256			for (var k in value) {
257				if (value.hasOwnProperty(k))
258					tmp.push(stringify(k) + ': ' + stringify(value[k]));
259			}
260			return '{' + tmp.join(', ') + '}';
261		}
262	}
263	return '"' + value.toString().replace(escRE, escFunc) + '"';
264};
265
266
267var samples = {
268	"JSONオブジェクト": {
269		"JSON": '{"foundation": "Mozilla", "model": "box", "week": 45, "transport": "car", "month": 7}',
270		"大きめJSON": '{"quotes":[{"high":"111.18","open":"109.75","bid":"111.09","currencyPairCode":"USDJPY","ask":"111.10","low":"109.59"},{"high":"121.64","open":"119.27","bid":"121.48","currencyPairCode":"EURJPY","ask":"121.50","low":"119.05"},{"high":"142.60","open":"140.43","bid":"142.55","currencyPairCode":"GBPJPY","ask":"142.58","low":"140.05"}]}',
271		"空のオブジェクト": "{\n}",
272	},
273	"JavaScriptオブジェクト": {
274		"JavaScriptオブジェクト": '{foundation: "Mozilla", model: "box", week: 45, transport: "car", month: 7}',
275		"一部シングルクォート": '{\'foundation\': "Mozilla", "model": "box", "week": 45, "transport": "car", "month": 7}',
276		"カンマで終わっている": '{"foundation": "Mozilla", "model": "box", "week": 45, "transport": "car", "month": 7,}',
277
278		"エンコード文字列を含む": '{"":"","東京":"AKB","名古屋":"SKE","大阪":"NMB","博多":"HKT","\\u65b0\\u6f5f":"\\x4e\\x47\\x54"}',
279	},
280	"エラーパターン": {
281		"alert()": 'alert("123")',
282		"{alert()}": '{alert("123")}',
283		"Math": "Math.round",
284		select3: "select * from ash_table where c1 is not null and c1 != 'ab''cd';",
285	},
286};

Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.