235 lines
		
	
	
		
			7.5 KiB
		
	
	
	
		
			HTML
		
	
	
	
	
	
			
		
		
	
	
			235 lines
		
	
	
		
			7.5 KiB
		
	
	
	
		
			HTML
		
	
	
	
	
	
| <!DOCTYPE html>
 | |
| <html>
 | |
| 
 | |
|   <head>
 | |
|     <meta name="viewport" content="width=device-width, initial-scale=1.0">
 | |
|     <meta charset="utf-8" />
 | |
| 
 | |
|     <link rel="stylesheet" href="../assets/css/tools/r11form.css">
 | |
|     <link rel="stylesheet" href="../assets/css/highlight.css">
 | |
|     <script src="//cdnjs.cloudflare.com/ajax/libs/highlight.js/11.7.0/highlight.min.js"></script>
 | |
|     <script src="../assets/scripts/tools/scripts.js"></script>
 | |
|     <script src="../assets/scripts/tools/highlight.js"></script>
 | |
|     <script>hljs.highlightAll();</script>
 | |
|   </head>
 | |
| 
 | |
|   <body onload="init()">
 | |
|     <div class="container">
 | |
|       <div id="tool" class="tool rwd-expandable">
 | |
|         <div class="tool-context">
 | |
|           <div class="headline">
 | |
|             <h1>Online JSON Formatter</h1>
 | |
|           </div>
 | |
| 
 | |
|           <div class="display-space-between">
 | |
|             <div>
 | |
|               <b><span id="processInfo"></span></b><br>
 | |
|               <b>Insert your JSON:</b>
 | |
|             </div>
 | |
| 
 | |
|             <div>
 | |
|               <button class="action-button active" id="clearXMLButton" style="padding: 3px 10px;"
 | |
|                       onclick="clearJsonData()">Clear</button>
 | |
|               <button class="action-button active" id="defaultXMLButton" style="padding: 3px 10px;"
 | |
|                       onclick="insertDefaultJson()">Insert default XML</button>
 | |
|             </div>
 | |
|           </div>
 | |
|           <pre>
 | |
|               <code class="hightlight-json json-block bordered-field" id="jsonBlock" contenteditable="True">{"enter": "your", "json": "here"}</code>
 | |
|           </pre>
 | |
| 
 | |
|           <button style="margin-top: 20px"
 | |
|                   class="max-width block-label action-button active"
 | |
|                   onclick="formatAndValidateJson('processInfo')"
 | |
|           >Prettify JSON</button>
 | |
| 
 | |
|           <button class="max-width block-label action-button active"
 | |
|                   onclick="minimizeJson('processInfo')"
 | |
|           >Minimize JSON</button>
 | |
|         </div>
 | |
|       </div>
 | |
| 
 | |
|       <div class="tooltip-window rwd-hideable">
 | |
|         <h2>What is this?</h2>
 | |
|         <p>This tool has 2 main functions:
 | |
|         <ul>
 | |
|           <li><strong>Prettify JSON</strong> to make it human-readable (add indentation etc.)</li>
 | |
|           <li><strong>Minimize JSON</strong> to make it more compact (exactly opposite to above)</li>
 | |
|         </ul>
 | |
|         </p>
 | |
|       </div>
 | |
| 
 | |
|     </div>
 | |
| 
 | |
|     <script>
 | |
|       const mergeHTMLPlugin = (function () {
 | |
|         'use strict';
 | |
| 
 | |
|         var originalStream;
 | |
| 
 | |
|         /**
 | |
|          * @param {string} value
 | |
|          * @returns {string}
 | |
|          */
 | |
|         function escapeHTML(value) {
 | |
|           return value
 | |
|           .replace(/&/g, '&')
 | |
|           .replace(/</g, '<')
 | |
|           .replace(/>/g, '>')
 | |
|           .replace(/"/g, '"')
 | |
|           .replace(/'/g, ''');
 | |
|         }
 | |
| 
 | |
|         /* plugin itself */
 | |
| 
 | |
|         /** @type {HLJSPlugin} */
 | |
|         const mergeHTMLPlugin = {
 | |
|           // preserve the original HTML token stream
 | |
|           "before:highlightElement": ({el}) => {
 | |
|             originalStream = nodeStream(el);
 | |
|           },
 | |
|           // merge it afterwards with the highlighted token stream
 | |
|           "after:highlightElement": ({el, result, text}) => {
 | |
|             if (!originalStream.length) {
 | |
|               return;
 | |
|             }
 | |
| 
 | |
|             const resultNode = document.createElement('div');
 | |
|             resultNode.innerHTML = result.value;
 | |
|             result.value = mergeStreams(originalStream, nodeStream(resultNode), text);
 | |
|             el.innerHTML = result.value;
 | |
|           }
 | |
|         };
 | |
| 
 | |
|         /**
 | |
|          * @param {Node} node
 | |
|          */
 | |
|         function tag(node) {
 | |
|           return node.nodeName.toLowerCase();
 | |
|         }
 | |
| 
 | |
|         /**
 | |
|          * @param {Node} node
 | |
|          */
 | |
|         function nodeStream(node) {
 | |
|           /** @type Event[] */
 | |
|           const result = [];
 | |
|           (function _nodeStream(node, offset) {
 | |
|             for (let child = node.firstChild; child; child = child.nextSibling) {
 | |
|               if (child.nodeType === 3) {
 | |
|                 offset += child.nodeValue.length;
 | |
|               } else if (child.nodeType === 1) {
 | |
|                 result.push({
 | |
|                   event: 'start',
 | |
|                   offset: offset,
 | |
|                   node: child
 | |
|                 });
 | |
|                 offset = _nodeStream(child, offset);
 | |
| 
 | |
|                 if (!tag(child).match(/br|hr|img|input/)) {
 | |
|                   result.push({
 | |
|                     event: 'stop',
 | |
|                     offset: offset,
 | |
|                     node: child
 | |
|                   });
 | |
|                 }
 | |
|               }
 | |
|             }
 | |
|             return offset;
 | |
|           })(node, 0);
 | |
|           return result;
 | |
|         }
 | |
| 
 | |
|         /**
 | |
|          * @param {any} original - the original stream
 | |
|          * @param {any} highlighted - stream of the highlighted source
 | |
|          * @param {string} value - the original source itself
 | |
|          */
 | |
|         function mergeStreams(original, highlighted, value) {
 | |
|           let processed = 0;
 | |
|           let result = '';
 | |
|           const nodeStack = [];
 | |
| 
 | |
|           function selectStream() {
 | |
|             if (!original.length || !highlighted.length) {
 | |
|               return original.length ? original : highlighted;
 | |
|             }
 | |
|             if (original[0].offset !== highlighted[0].offset) {
 | |
|               return (original[0].offset < highlighted[0].offset) ? original : highlighted;
 | |
|             }
 | |
| 
 | |
|             return highlighted[0].event === 'start' ? original : highlighted;
 | |
|           }
 | |
| 
 | |
|           /**
 | |
|            * @param {Node} node
 | |
|            */
 | |
|           function open(node) {
 | |
|             /** @param {Attr} attr */
 | |
|             function attributeString(attr) {
 | |
|               return ' ' + attr.nodeName + '="' + escapeHTML(attr.value) + '"';
 | |
|             }
 | |
| 
 | |
|             // @ts-ignore
 | |
|             result += '<' + tag(node) + [].map.call(node.attributes, attributeString).join('')
 | |
|                 + '>';
 | |
|           }
 | |
| 
 | |
|           /**
 | |
|            * @param {Node} node
 | |
|            */
 | |
|           function close(node) {
 | |
|             result += '</' + tag(node) + '>';
 | |
|           }
 | |
| 
 | |
|           /**
 | |
|            * @param {Event} event
 | |
|            */
 | |
|           function render(event) {
 | |
|             (event.event === 'start' ? open : close)(event.node);
 | |
|           }
 | |
| 
 | |
|           while (original.length || highlighted.length) {
 | |
|             let stream = selectStream();
 | |
|             result += escapeHTML(value.substring(processed, stream[0].offset));
 | |
|             processed = stream[0].offset;
 | |
|             if (stream === original) {
 | |
|               /*
 | |
|               On any opening or closing tag of the original markup we first close
 | |
|               the entire highlighted node stack, then render the original tag along
 | |
|               with all the following original tags at the same offset and then
 | |
|               reopen all the tags on the highlighted stack.
 | |
|               */
 | |
|               nodeStack.reverse().forEach(close);
 | |
|               do {
 | |
|                 render(stream.splice(0, 1)[0]);
 | |
|                 stream = selectStream();
 | |
|               } while (stream === original && stream.length && stream[0].offset === processed);
 | |
|               nodeStack.reverse().forEach(open);
 | |
|             } else {
 | |
|               if (stream[0].event === 'start') {
 | |
|                 nodeStack.push(stream[0].node);
 | |
|               } else {
 | |
|                 nodeStack.pop();
 | |
|               }
 | |
|               render(stream.splice(0, 1)[0]);
 | |
|             }
 | |
|           }
 | |
|           return result + escapeHTML(value.substr(processed));
 | |
|         }
 | |
| 
 | |
|         return mergeHTMLPlugin;
 | |
| 
 | |
|       }());
 | |
| 
 | |
|       hljs.addPlugin(mergeHTMLPlugin);
 | |
| 
 | |
|       function init() {
 | |
|         // Make sure that only plain text is pasted
 | |
|         configurePastingInElement("jsonBlock");
 | |
|         
 | |
|       }
 | |
|     </script>
 | |
|   </body>
 | |
| </html>
 |