Syntax highlight now should work on all tools apart from Mock Services. Co-authored-by: Adam Bem <adam.bem@zoho.eu> Reviewed-on: #156 Reviewed-by: Mikolaj Widla <widlam@noreply.example.com>
		
			
				
	
	
		
			236 lines
		
	
	
		
			7.5 KiB
		
	
	
	
		
			HTML
		
	
	
	
	
	
			
		
		
	
	
			236 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 src="../assets/scripts/tools/json.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>
 |