Xikui Wang has uploaded a new change for review. https://asterix-gerrit.ics.uci.edu/1574
Change subject: Pretty JSON output ...................................................................... Pretty JSON output 1. Added Pretty JSON output query option. 2. Tweaked front end JS to rerender the JSON result. (Take the HTML element and rerender it with json-viewer plugin). 3. Added jquery.json-viewer library (MIT License) to front end, with several customization: a) Several compatibility bug fix (semi-colon, comment); b) expand logo change. Change-Id: Ieec8489c0a055b01e754bba5f9827a7c1f175567 --- M asterixdb/asterix-app/src/main/java/org/apache/asterix/app/result/ResultPrinter.java M asterixdb/asterix-app/src/main/resources/webui/querytemplate.html A asterixdb/asterix-app/src/main/resources/webui/static/css/jquery.json-viewer.css A asterixdb/asterix-app/src/main/resources/webui/static/js/jquery.json-viewer.js 4 files changed, 209 insertions(+), 2 deletions(-) git pull ssh://asterix-gerrit.ics.uci.edu:29418/asterixdb refs/changes/74/1574/1 diff --git a/asterixdb/asterix-app/src/main/java/org/apache/asterix/app/result/ResultPrinter.java b/asterixdb/asterix-app/src/main/java/org/apache/asterix/app/result/ResultPrinter.java index f401576..088d153 100644 --- a/asterixdb/asterix-app/src/main/java/org/apache/asterix/app/result/ResultPrinter.java +++ b/asterixdb/asterix-app/src/main/java/org/apache/asterix/app/result/ResultPrinter.java @@ -87,7 +87,7 @@ // output by displayCSVHeader(), so skip it here if (conf.is(SessionConfig.FORMAT_HTML)) { conf.out().println("<h4>Results:</h4>"); - conf.out().println("<pre>"); + conf.out().println("<pre class=\"result-content\">"); } try { diff --git a/asterixdb/asterix-app/src/main/resources/webui/querytemplate.html b/asterixdb/asterix-app/src/main/resources/webui/querytemplate.html index 878b1cf..a83c636 100644 --- a/asterixdb/asterix-app/src/main/resources/webui/querytemplate.html +++ b/asterixdb/asterix-app/src/main/resources/webui/querytemplate.html @@ -35,8 +35,10 @@ <link href="/webui/static/css/style.css" rel="stylesheet" type="text/css" /> +<script src="/webui/static/js/jquery.json-viewer.js"></script> +<link href="/webui/static/css/jquery.json-viewer.css" type="text/css" rel="stylesheet" /> -<script type="text/javascript"> + <script type="text/javascript"> $(document).ready(function() { var optionButtonSize = $('#checkboxes-on').width(); @@ -155,6 +157,28 @@ } } + /* Handling Pretty JSON */ + var resultFormat = $('#output-format option:checked').val(); + var prettyJson = $('[name="pretty-json"]').is(':checked'); + if ( prettyJson && (resultFormat == 'LOSSLESS_JSON' || resultFormat == 'CLEAN_JSON')) { + $('.result-content').each( + function(idx) { + var results = $(this).text().split('\n'); + $(this).css('padding-left', '20px'); + $(this).text(''); + for (var iter1 = 0; iter1 < results.length - 1; iter1++) { + if (results[iter1].length < 1) { + continue; + } + var resultJSON = $.parseJSON(results[iter1]); + $(this).append($('<div/>').attr("id", "json-record"+idx+"-"+iter1)); + $('#json-record'+idx+"-"+iter1).jsonViewer(resultJSON); + } + } + ); + } + + var contentString = data.toString(); if (contentString.indexOf(durPattern) != -1) { $('<div/>') @@ -235,6 +259,7 @@ <option value="LOSSLESS_JSON">JSON (lossless)</option> </select> </label> + <label class="optlabel"><input type="checkbox" name="pretty-json" value="true" /> Pretty JSON</label> <label class="optlabel"><input type="checkbox" name="wrapper-array" value="true" /> Wrap results in outer array</label> <label class="checkbox optlabel"><input type="checkbox" name="print-expr-tree" value="true" /> Print parsed expressions</label> <label class="checkbox optlabel"><input type="checkbox" name="print-rewritten-expr-tree" value="true" /> Print rewritten expressions</label> diff --git a/asterixdb/asterix-app/src/main/resources/webui/static/css/jquery.json-viewer.css b/asterixdb/asterix-app/src/main/resources/webui/static/css/jquery.json-viewer.css new file mode 100644 index 0000000..d6143f9 --- /dev/null +++ b/asterixdb/asterix-app/src/main/resources/webui/static/css/jquery.json-viewer.css @@ -0,0 +1,45 @@ +/* Syntax highlighting for JSON objects */ +ul.json-dict, ol.json-array { + list-style-type: none; + margin: 0 0 0 1px; + border-left: 1px dotted #ccc; + padding-left: 2em; +} +.json-string { + color: #0B7500; +} +.json-literal { + color: #1A01CC; + font-weight: bold; +} + +/* Toggle button */ +a.json-toggle { + position: relative; + color: inherit; + text-decoration: none; +} +a.json-toggle:focus { + outline: none; +} +a.json-toggle:before { + color: #aaa; + content: "\25BC"; /* down arrow */ + position: absolute; + display: inline-block; + width: 1em; + left: -1em; +} +a.json-toggle.collapsed:before { + content: "\25B6"; /* left arrow */ +} + +/* Collapsable placeholder links */ +a.json-placeholder { + color: #aaa; + padding: 0 1em; + text-decoration: none; +} +a.json-placeholder:hover { + text-decoration: underline; +} diff --git a/asterixdb/asterix-app/src/main/resources/webui/static/js/jquery.json-viewer.js b/asterixdb/asterix-app/src/main/resources/webui/static/js/jquery.json-viewer.js new file mode 100644 index 0000000..6ef9bdd --- /dev/null +++ b/asterixdb/asterix-app/src/main/resources/webui/static/js/jquery.json-viewer.js @@ -0,0 +1,137 @@ +/** + * jQuery json-viewer + * @author: Alexandre Bodelot <alexandre.bode...@gmail.com> + */ +(function($){ + + /** + * Check if arg is either an array with at least 1 element, or a dict with at least 1 key + * @return boolean + */ + function isCollapsable(arg) { + return arg instanceof Object && Object.keys(arg).length > 0; + } + + /** + * Check if a string represents a valid url + * @return boolean + */ + function isUrl(string) { + var regexp = /^(ftp|http|https):\/\/(\w+:{0,1}\w*@)?(\S+)(:[0-9]+)?(\/|\/([\w#!:.?+=&%@!\-\/]))?/; + return regexp.test(string); + } + + /** + * Transform a json object into html representation + * @return string + */ + function json2html(json, options) { + var html = ''; + if (typeof json === 'string') { + json = json.replace(/&/g, '&').replace(/</g, '<').replace(/>/g, '>'); + if (isUrl(json)) + html += '<a href="' + json + '" class="json-string">' + json + '</a>'; + else + html += '<span class="json-string">"' + json + '"</span>'; + } + else if (typeof json === 'number') { + html += '<span class="json-literal">' + json + '</span>'; + } + else if (typeof json === 'boolean') { + html += '<span class="json-literal">' + json + '</span>'; + } + else if (json === null) { + html += '<span class="json-literal">null</span>'; + } + else if (json instanceof Array) { + if (json.length > 0) { + html += '[<ol class="json-array">'; + for (var i = 0; i < json.length; ++i) { + html += '<li>'; + if (isCollapsable(json[i])) { + html += '<a href class="json-toggle"></a>'; + } + html += json2html(json[i], options); + if (i < json.length - 1) { + html += ','; + } + html += '</li>'; + } + html += '</ol>]'; + } + else { + html += '[]'; + } + } + else if (typeof json === 'object') { + var key_count = Object.keys(json).length; + if (key_count > 0) { + html += '{<ul class="json-dict">'; + for (var key in json) { + if (json.hasOwnProperty(key)) { + html += '<li>'; + var keyRepr = options.withQuotes ? + '<span class="json-string">"' + key + '"</span>' : key; + if (isCollapsable(json[key])) { + html += '<a href class="json-toggle">' + keyRepr + '</a>'; + } + else { + html += keyRepr; + } + html += ': ' + json2html(json[key], options); + if (--key_count > 0) + html += ','; + html += '</li>'; + } + } + html += '</ul>}'; + } + else { + html += '{}'; + } + } + return html; + } + + /** + * jQuery plugin method + * @param json: a javascript object + * @param options: an optional options hash + */ + $.fn.jsonViewer = function(json, options) { + options = options || {}; + + return this.each(function() { + + var html = json2html(json, options); + if (isCollapsable(json)) + html = '<a href class="json-toggle"></a>' + html; + + $(this).html(html); + + $(this).off('click'); + $(this).on('click', 'a.json-toggle', function() { + var target = $(this).toggleClass('collapsed').siblings('ul.json-dict, ol.json-array'); + target.toggle(); + if (target.is(':visible')) { + target.siblings('.json-placeholder').remove(); + } + else { + var count = target.children('li').length; + var placeholder = count + (count > 1 ? ' items' : ' item'); + target.after('<a href class="json-placeholder">' + placeholder + '</a>'); + } + return false; + }); + + $(this).on('click', 'a.json-placeholder', function() { + $(this).siblings('a.json-toggle').click(); + return false; + }); + + if (options.collapsed == true) { + $(this).find('a.json-toggle').click(); + } + }); + }; +})(jQuery); -- To view, visit https://asterix-gerrit.ics.uci.edu/1574 To unsubscribe, visit https://asterix-gerrit.ics.uci.edu/settings Gerrit-MessageType: newchange Gerrit-Change-Id: Ieec8489c0a055b01e754bba5f9827a7c1f175567 Gerrit-PatchSet: 1 Gerrit-Project: asterixdb Gerrit-Branch: master Gerrit-Owner: Xikui Wang <xkk...@gmail.com>