From 238f00a5d4864f0df00f35a2f7ed38fb59de93bb Mon Sep 17 00:00:00 2001 From: discotroll65 Date: Thu, 17 Nov 2016 00:10:53 -0800 Subject: [PATCH 1/2] Deal with plain text JSON response having header of text/html --- extension/js/content.js | 97 +++++++++++++++++++++++++++-------------- 1 file changed, 64 insertions(+), 33 deletions(-) diff --git a/extension/js/content.js b/extension/js/content.js index b930379..229e961 100644 --- a/extension/js/content.js +++ b/extension/js/content.js @@ -36,7 +36,6 @@ (function() { "use strict" ; - var jfContent, pre, jfStyleEl, @@ -48,15 +47,15 @@ exitedNotJsonTime, displayedFormattedJsonTime ; - + // Open the port "jf" now, ready for when we need it // console.time('established port') ; port = chrome.extension.connect({name: 'jf'}) ; - + // Add listener to receive response from BG when ready port.onMessage.addListener( function (msg) { // console.log('Port msg received', msg[0], (""+msg[1]).substring(0,30)) ; - + switch (msg[0]) { case 'NOT JSON' : pre.hidden = false ; @@ -64,7 +63,7 @@ document.body.removeChild(jfContent) ; exitedNotJsonTime = +(new Date()) ; break ; - + case 'FORMATTING' : isJsonTime = +(new Date()) ; @@ -72,7 +71,7 @@ // Clear the slowAnalysisTimeout (if the BG worker had taken longer than 1s to respond with an answer to whether or not this is JSON, then it would have fired, unhiding the PRE... But now that we know it's JSON, we can clear this timeout, ensuring the PRE stays hidden.) clearTimeout(slowAnalysisTimeout) ; - + // Insert CSS jfStyleEl = document.createElement('style') ; jfStyleEl.id = 'jfStyleEl' ; @@ -83,7 +82,7 @@ 'beforeend', 'body{-webkit-user-select:text;overflow-y:scroll !important;margin:0;position:relative}#optionBar{-webkit-user-select:none;display:block;position:absolute;top:9px;right:17px}#buttonFormatted,#buttonPlain{-webkit-border-radius:2px;-webkit-box-shadow:0px 1px 3px rgba(0,0,0,0.1);-webkit-user-select:none;background:-webkit-linear-gradient(#fafafa, #f4f4f4 40%, #e5e5e5);border:1px solid #aaa;color:#444;font-size:12px;margin-bottom:0px;min-width:4em;padding:3px 0;position:relative;z-index:10;display:inline-block;width:80px;text-shadow:1px 1px rgba(255,255,255,0.3)}#buttonFormatted{margin-left:0;border-top-left-radius:0;border-bottom-left-radius:0}#buttonPlain{margin-right:0;border-top-right-radius:0;border-bottom-right-radius:0;border-right:none}#buttonFormatted:hover,#buttonPlain:hover{-webkit-box-shadow:0px 1px 3px rgba(0,0,0,0.2);background:#ebebeb -webkit-linear-gradient(#fefefe, #f8f8f8 40%, #e9e9e9);border-color:#999;color:#222}#buttonFormatted:active,#buttonPlain:active{-webkit-box-shadow:inset 0px 1px 3px rgba(0,0,0,0.2);background:#ebebeb -webkit-linear-gradient(#f4f4f4, #efefef 40%, #dcdcdc);color:#333}#buttonFormatted.selected,#buttonPlain.selected{-webkit-box-shadow:inset 0px 1px 5px rgba(0,0,0,0.2);background:#ebebeb -webkit-linear-gradient(#e4e4e4, #dfdfdf 40%, #dcdcdc);color:#333}#buttonFormatted:focus,#buttonPlain:focus{outline:0}#jsonpOpener,#jsonpCloser{padding:4px 0 0 8px;color:#000;margin-bottom:-6px}#jsonpCloser{margin-top:0}#formattedJson{padding-left:28px;padding-top:6px}pre{padding:36px 5px 5px 5px}.kvov{display:block;padding-left:20px;margin-left:-20px;position:relative}.collapsed{white-space:nowrap}.collapsed>.blockInner{display:none}.collapsed>.ell:after{content:"…";font-weight:bold}.collapsed>.ell{margin:0 4px;color:#888}.collapsed .kvov{display:inline}.e{width:20px;height:18px;display:block;position:absolute;left:-2px;top:1px;z-index:5;background-image:url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAgAAAAICAYAAADED76LAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAD1JREFUeNpiYGBgOADE%2F3Hgw0DM4IRHgSsDFOzFInmMAQnY49ONzZRjDFiADT7dMLALiE8y4AGW6LoBAgwAuIkf%2F%2FB7O9sAAAAASUVORK5CYII%3D");background-repeat:no-repeat;background-position:center center;display:block;opacity:0.15}.collapsed>.e{-webkit-transform:rotate(-90deg);width:18px;height:20px;left:0px;top:0px}.e:hover{opacity:0.35}.e:active{opacity:0.5}.collapsed .kvov .e{display:none}.blockInner{display:block;padding-left:24px;border-left:1px dotted #bbb;margin-left:2px}#formattedJson,#jsonpOpener,#jsonpCloser{color:#333;font:13px/18px monospace}#formattedJson{color:#444}.b{font-weight:bold}.s{color:#0B7500;word-wrap:break-word}a:link,a:visited{text-decoration:none;color:inherit}a:hover,a:active{text-decoration:underline;color:#050}.bl,.nl,.n{font-weight:bold;color:#1A01CC}.k{color:#000}#formattingMsg{font:13px "Lucida Grande","Segoe UI","Tahoma";padding:10px 0 0 8px;margin:0;color:#333}#formattingMsg>svg{margin:0 7px;position:relative;top:1px}[hidden]{display:none !important}span{white-space:pre-wrap}@-webkit-keyframes spin{from{-webkit-transform:rotate(0deg)}to{-webkit-transform:rotate(360deg)}}#spinner{-webkit-animation:spin 2s 0 infinite}*{-webkit-font-smoothing:antialiased}' ) ; - + // Add custom font name if set - FROM FUTURE // if (typeof settings.fontName === 'string') { // jfStyleEl.insertAdjacentHTML( @@ -104,13 +103,13 @@ setTimeout(function(){ formattingMsg.hidden = false ; }, 250) ; - - + + // Create option bar var optionBar = document.createElement('div') ; optionBar.id = 'optionBar' ; - - + + // Show options link, if needed - FROM FUTURE // if (settings.enableOptionsLink) { // var optionsLink = document.createElement('a') ; @@ -120,7 +119,7 @@ // optionsLink.target = '_BLANK' ; // optionBar.appendChild(optionsLink) ; // } - + // Create toggleFormat button var buttonPlain = document.createElement('button'), buttonFormatted = document.createElement('button') ; @@ -129,7 +128,7 @@ buttonFormatted.id = 'buttonFormatted' ; buttonFormatted.innerText = 'Parsed' ; buttonFormatted.classList.add('selected') ; - + var plainOn = false ; buttonPlain.addEventListener( 'click', @@ -146,7 +145,7 @@ }, false ) ; - + buttonFormatted.addEventListener( 'click', function () { @@ -162,7 +161,7 @@ }, false ) ; - + // Put it in optionBar optionBar.appendChild(buttonPlain) ; optionBar.appendChild(buttonFormatted) ; @@ -173,16 +172,16 @@ generalClick, false // No need to propogate down ) ; - + // Put option bar in DOM document.body.insertBefore(optionBar, pre) ; break ; - + case 'FORMATTED' : // Insert HTML content jfContent.innerHTML = msg[1] ; - + displayedFormattedJsonTime = Date.now() ; // Log times @@ -201,22 +200,44 @@ }, 100) ; break ; - + default : throw new Error('Message not understood: ' + msg[0]) ; } }); - + // console.timeEnd('established port') ; + function determineCorrectPre(bodyChildren){ + var manualPre, plainTextPageWithTextHtmlHeader; + + // This could be a 'plain text' page with a text/html response header -- + // in that case, it's just a body with text inside of it + plainTextPageWithTextHtmlHeader = ( + bodyChildren.length === 1 && bodyChildren[0].nodeName === "#text" + ); + + if(plainTextPageWithTextHtmlHeader){ + // Manually make own pre, clear out body + manualPre = document.createElement('pre'); + manualPre.style = "word-wrap: break-word; white-space: pre-wrap;"; + manualPre.innerText = bodyChildren[0].textContent; + document.body.innerHTML = ""; + document.body.appendChild(manualPre); + return manualPre; + } + + // Otherwise probably not an json response -- continue on as normal + return bodyChildren[0]; + } function ready () { - + domReadyTime = Date.now() ; - + // First, check if it's a PRE and exit if not var bodyChildren = document.body.childNodes ; - pre = bodyChildren[0] ; + pre = determineCorrectPre(bodyChildren); var jsonLength = (pre && pre.innerText || "").length ; if ( bodyChildren.length !== 1 || @@ -226,22 +247,32 @@ // console.log('Not even text (or longer than 3MB); exiting') ; // console.log(bodyChildren.length,pre.tagName, pre.innerText.length) ; + // Give some Feedback in the console as to why it may not be working + // if first node is text, JSON could contain an unsanitized html + // tag + if(bodyChildren.length > 1 && pre.nodeName === '#text'){ + console.log('JSON Formatter: Body has ' + bodyChildren.length + + ' nodes -- is json Valid? Try inspecting document.body.childNodes' + + ' in the console.' + ); + } + // Disconnect the port (without even having used it) port.disconnect() ; - + // EXIT POINT: NON-PLAIN-TEXT PAGE (or longer than 3MB) } else { // This is a 'plain text' page (just a body with one PRE child). // It might be JSON/JSONP, or just some other kind of plain text (eg CSS). - + // Hide the PRE immediately (until we know what to do, to prevent FOUC) pre.hidden = true ; //console.log('It is text; hidden pre at ') ; slowAnalysisTimeout = setTimeout(function(){ pre.hidden = false ; }, 1000) ; - + // Send the contents of the PRE to the BG script // Add jfContent DIV, ready to display stuff jfContent = document.createElement('div') ; @@ -254,7 +285,7 @@ text: pre.innerText, length: jsonLength }); - + // Now, this script will just wait to receive anything back via another port message. The returned message will be something like "NOT JSON" or "IS JSON" } @@ -267,7 +298,7 @@ } }); } - + document.addEventListener("DOMContentLoaded", ready, false); var lastKvovIdGiven = 0 ; @@ -328,7 +359,7 @@ if (ev.which === 1) { var elem = ev.target ; - + if (elem.className === 'e') { // It's a click on an expander. @@ -340,7 +371,7 @@ scrollTop = document.body.scrollTop, parentSiblings ; - + // Expand or collapse if (parent.classList.contains('collapsed')) { // EXPAND @@ -374,10 +405,10 @@ } // console.log('Scrolltop HAS changed. document.body.scrollTop is now '+document.body.scrollTop+'; was '+scrollTop) ; - + // The body has got a bit shorter. // We need to increase the body height by a bit (by increasing the bottom margin on the jfContent div). The amount to increase it is whatever is the difference between our previous scrollTop and our new one. - + // Work out how much more our target scrollTop is than this. var difference = scrollTop - document.body.scrollTop + 8 ; // it always loses 8px; don't know why @@ -387,7 +418,7 @@ // Now change the scrollTop back to what it was document.body.scrollTop = scrollTop ; - + return ; } } From 9a16be0163a96f831522a178e5121711036a90dc Mon Sep 17 00:00:00 2001 From: discotroll65 Date: Thu, 17 Nov 2016 00:11:54 -0800 Subject: [PATCH 2/2] Bump version to 0.7.0 to include commit 238f00 --- extension/manifest.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/extension/manifest.json b/extension/manifest.json index da90eb7..b268fc2 100644 --- a/extension/manifest.json +++ b/extension/manifest.json @@ -1,6 +1,6 @@ { "name": "JSON Formatter", - "version": "0.6.0", + "version": "0.7.0", "manifest_version": 2, "description": "Makes JSON easy to read. Open source.", "homepage_url": "https://github.com/callumlocke/json-formatter",