|
@@ -0,0 +1,250 @@
|
|
|
+(function(jQuery) {
|
|
|
+ var $, Collapser, JSONFormatter, JSONView;
|
|
|
+ JSONFormatter = (function() {
|
|
|
+ function JSONFormatter(options) {
|
|
|
+ if (options == null) {
|
|
|
+ options = {};
|
|
|
+ }
|
|
|
+ this.options = options;
|
|
|
+ }
|
|
|
+
|
|
|
+ JSONFormatter.prototype.htmlEncode = function(html) {
|
|
|
+ if (html !== null) {
|
|
|
+ return html.toString().replace(/&/g, "&").replace(/"/g, """).replace(/</g, "<").replace(/>/g, ">");
|
|
|
+ } else {
|
|
|
+ return '';
|
|
|
+ }
|
|
|
+ };
|
|
|
+
|
|
|
+ JSONFormatter.prototype.jsString = function(s) {
|
|
|
+ s = JSON.stringify(s).slice(1, -1);
|
|
|
+ return this.htmlEncode(s);
|
|
|
+ };
|
|
|
+
|
|
|
+ JSONFormatter.prototype.decorateWithSpan = function(value, className) {
|
|
|
+ return "<span class=\"" + className + "\">" + (this.htmlEncode(value)) + "</span>";
|
|
|
+ };
|
|
|
+
|
|
|
+ JSONFormatter.prototype.valueToHTML = function(value, level) {
|
|
|
+ var valueType;
|
|
|
+ if (level == null) {
|
|
|
+ level = 0;
|
|
|
+ }
|
|
|
+ valueType = Object.prototype.toString.call(value).match(/\s(.+)]/)[1].toLowerCase();
|
|
|
+ return this["" + valueType + "ToHTML"].call(this, value, level);
|
|
|
+ };
|
|
|
+
|
|
|
+ JSONFormatter.prototype.nullToHTML = function(value) {
|
|
|
+ return this.decorateWithSpan('null', 'null');
|
|
|
+ };
|
|
|
+
|
|
|
+ JSONFormatter.prototype.numberToHTML = function(value) {
|
|
|
+ return this.decorateWithSpan(value, 'num');
|
|
|
+ };
|
|
|
+
|
|
|
+ JSONFormatter.prototype.stringToHTML = function(value) {
|
|
|
+ var multilineClass, newLinePattern;
|
|
|
+ if (/^(http|https|file):\/\/[^\s]+$/i.test(value)) {
|
|
|
+ return "<a href=\"" + (this.htmlEncode(value)) + "\"><span class=\"q\">\"</span>" + (this.jsString(value)) + "<span class=\"q\">\"</span></a>";
|
|
|
+ } else {
|
|
|
+ multilineClass = '';
|
|
|
+ value = this.jsString(value);
|
|
|
+ if (this.options.nl2br) {
|
|
|
+ newLinePattern = /([^>\\r\\n]?)(\\r\\n|\\n\\r|\\r|\\n)/g;
|
|
|
+ if (newLinePattern.test(value)) {
|
|
|
+ multilineClass = ' multiline';
|
|
|
+ value = (value + '').replace(newLinePattern, '$1' + '<br />');
|
|
|
+ }
|
|
|
+ }
|
|
|
+ return "<span class=\"string" + multilineClass + "\">\"" + value + "\"</span>";
|
|
|
+ }
|
|
|
+ };
|
|
|
+
|
|
|
+ JSONFormatter.prototype.booleanToHTML = function(value) {
|
|
|
+ return this.decorateWithSpan(value, 'bool');
|
|
|
+ };
|
|
|
+
|
|
|
+ JSONFormatter.prototype.arrayToHTML = function(array, level) {
|
|
|
+ var collapsible, hasContents, index, numProps, output, value, _i, _len;
|
|
|
+ if (level == null) {
|
|
|
+ level = 0;
|
|
|
+ }
|
|
|
+ hasContents = false;
|
|
|
+ output = '';
|
|
|
+ numProps = array.length;
|
|
|
+ for (index = _i = 0, _len = array.length; _i < _len; index = ++_i) {
|
|
|
+ value = array[index];
|
|
|
+ hasContents = true;
|
|
|
+ output += '<li>' + this.valueToHTML(value, level + 1);
|
|
|
+ if (numProps > 1) {
|
|
|
+ output += ',';
|
|
|
+ }
|
|
|
+ output += '</li>';
|
|
|
+ numProps--;
|
|
|
+ }
|
|
|
+ if (hasContents) {
|
|
|
+ collapsible = level === 0 ? '' : ' collapsible';
|
|
|
+ return "[<ul class=\"array level" + level + collapsible + "\">" + output + "</ul>]";
|
|
|
+ } else {
|
|
|
+ return '[ ]';
|
|
|
+ }
|
|
|
+ };
|
|
|
+
|
|
|
+ JSONFormatter.prototype.objectToHTML = function(object, level) {
|
|
|
+ var collapsible, hasContents, numProps, output, prop, value;
|
|
|
+ if (level == null) {
|
|
|
+ level = 0;
|
|
|
+ }
|
|
|
+ hasContents = false;
|
|
|
+ output = '';
|
|
|
+ numProps = 0;
|
|
|
+ for (prop in object) {
|
|
|
+ numProps++;
|
|
|
+ }
|
|
|
+ for (prop in object) {
|
|
|
+ value = object[prop];
|
|
|
+ hasContents = true;
|
|
|
+ output += "<li><span class=\"prop\"><span class=\"q\">\"</span>" + (this.jsString(prop)) + "<span class=\"q\">\"</span></span>: " + (this.valueToHTML(value, level + 1));
|
|
|
+ if (numProps > 1) {
|
|
|
+ output += ',';
|
|
|
+ }
|
|
|
+ output += '</li>';
|
|
|
+ numProps--;
|
|
|
+ }
|
|
|
+ if (hasContents) {
|
|
|
+ collapsible = level === 0 ? '' : ' collapsible';
|
|
|
+ return "{<ul class=\"obj level" + level + collapsible + "\">" + output + "</ul>}";
|
|
|
+ } else {
|
|
|
+ return '{ }';
|
|
|
+ }
|
|
|
+ };
|
|
|
+
|
|
|
+ JSONFormatter.prototype.jsonToHTML = function(json) {
|
|
|
+ return "<div class=\"jsonview\">" + (this.valueToHTML(json)) + "</div>";
|
|
|
+ };
|
|
|
+
|
|
|
+ return JSONFormatter;
|
|
|
+
|
|
|
+ })();
|
|
|
+ (typeof module !== "undefined" && module !== null) && (module.exports = JSONFormatter);
|
|
|
+ Collapser = {
|
|
|
+ bindEvent: function(item, collapsed) {
|
|
|
+ var collapser;
|
|
|
+ collapser = document.createElement('div');
|
|
|
+ collapser.className = 'collapser';
|
|
|
+ collapser.innerHTML = collapsed ? '+' : '-';
|
|
|
+ collapser.addEventListener('click', (function(_this) {
|
|
|
+ return function(event) {
|
|
|
+ return _this.toggle(event.target);
|
|
|
+ };
|
|
|
+ })(this));
|
|
|
+ item.insertBefore(collapser, item.firstChild);
|
|
|
+ if (collapsed) {
|
|
|
+ return this.collapse(collapser);
|
|
|
+ }
|
|
|
+ },
|
|
|
+ expand: function(collapser) {
|
|
|
+ var ellipsis, target;
|
|
|
+ target = this.collapseTarget(collapser);
|
|
|
+ ellipsis = target.parentNode.getElementsByClassName('ellipsis')[0];
|
|
|
+ target.parentNode.removeChild(ellipsis);
|
|
|
+ target.style.display = '';
|
|
|
+ return collapser.innerHTML = '-';
|
|
|
+ },
|
|
|
+ collapse: function(collapser) {
|
|
|
+ var ellipsis, target;
|
|
|
+ target = this.collapseTarget(collapser);
|
|
|
+ target.style.display = 'none';
|
|
|
+ ellipsis = document.createElement('span');
|
|
|
+ ellipsis.className = 'ellipsis';
|
|
|
+ ellipsis.innerHTML = ' … ';
|
|
|
+ target.parentNode.insertBefore(ellipsis, target);
|
|
|
+ return collapser.innerHTML = '+';
|
|
|
+ },
|
|
|
+ toggle: function(collapser) {
|
|
|
+ var target;
|
|
|
+ target = this.collapseTarget(collapser);
|
|
|
+ if (target.style.display === 'none') {
|
|
|
+ return this.expand(collapser);
|
|
|
+ } else {
|
|
|
+ return this.collapse(collapser);
|
|
|
+ }
|
|
|
+ },
|
|
|
+ collapseTarget: function(collapser) {
|
|
|
+ var target, targets;
|
|
|
+ targets = collapser.parentNode.getElementsByClassName('collapsible');
|
|
|
+ if (!targets.length) {
|
|
|
+ return;
|
|
|
+ }
|
|
|
+ return target = targets[0];
|
|
|
+ }
|
|
|
+ };
|
|
|
+ $ = jQuery;
|
|
|
+ JSONView = {
|
|
|
+ collapse: function(el) {
|
|
|
+ if (el.innerHTML === '-') {
|
|
|
+ return Collapser.collapse(el);
|
|
|
+ }
|
|
|
+ },
|
|
|
+ expand: function(el) {
|
|
|
+ if (el.innerHTML === '+') {
|
|
|
+ return Collapser.expand(el);
|
|
|
+ }
|
|
|
+ },
|
|
|
+ toggle: function(el) {
|
|
|
+ return Collapser.toggle(el);
|
|
|
+ }
|
|
|
+ };
|
|
|
+ return $.fn.JSONView = function() {
|
|
|
+ var args, defaultOptions, formatter, json, method, options, outputDoc;
|
|
|
+ args = arguments;
|
|
|
+ if (JSONView[args[0]] != null) {
|
|
|
+ method = args[0];
|
|
|
+ return this.each(function() {
|
|
|
+ var $this, level;
|
|
|
+ $this = $(this);
|
|
|
+ if (args[1] != null) {
|
|
|
+ level = args[1];
|
|
|
+ return $this.find(".jsonview .collapsible.level" + level).siblings('.collapser').each(function() {
|
|
|
+ return JSONView[method](this);
|
|
|
+ });
|
|
|
+ } else {
|
|
|
+ return $this.find('.jsonview > ul > li > .collapsible').siblings('.collapser').each(function() {
|
|
|
+ return JSONView[method](this);
|
|
|
+ });
|
|
|
+ }
|
|
|
+ });
|
|
|
+ } else {
|
|
|
+ json = args[0];
|
|
|
+ options = args[1] || {};
|
|
|
+ defaultOptions = {
|
|
|
+ collapsed: false,
|
|
|
+ nl2br: false
|
|
|
+ };
|
|
|
+ options = $.extend(defaultOptions, options);
|
|
|
+ formatter = new JSONFormatter({
|
|
|
+ nl2br: options.nl2br
|
|
|
+ });
|
|
|
+ if (Object.prototype.toString.call(json) === '[object String]') {
|
|
|
+ json = JSON.parse(json);
|
|
|
+ }
|
|
|
+ outputDoc = formatter.jsonToHTML(json);
|
|
|
+ return this.each(function() {
|
|
|
+ var $this, item, items, _i, _len, _results;
|
|
|
+ $this = $(this);
|
|
|
+ $this.html(outputDoc);
|
|
|
+ items = $this[0].getElementsByClassName('collapsible');
|
|
|
+ _results = [];
|
|
|
+ for (_i = 0, _len = items.length; _i < _len; _i++) {
|
|
|
+ item = items[_i];
|
|
|
+ if (item.parentNode.nodeName === 'LI') {
|
|
|
+ _results.push(Collapser.bindEvent(item.parentNode, options.collapsed));
|
|
|
+ } else {
|
|
|
+ _results.push(void 0);
|
|
|
+ }
|
|
|
+ }
|
|
|
+ return _results;
|
|
|
+ });
|
|
|
+ }
|
|
|
+ };
|
|
|
+})(jQuery);
|