# JSON-DataView
Firefox add-on that displays JSON data in a collapsible tree structure with syntax highlights.
## Screenshot

## Usage
* the `expand` (+) and `collapse` (-) buttons are sensitive to mouse click
* without any modifier key:
the action is applied to the selected tree node
* while pressing any of the following modifier keys: `Ctrl` or `Shift`
the action is applied to the selected tree node,
as well as recursively to all nodes that descend from the selected tree node
## Summary
* [jsonTreeViewer](https://github.com/summerstyle/jsonTreeViewer) served as a solid starting point for creating a DOM structure from JSON data
>* the original web application is very well coded
>* its core logic has been separated and abstracted into a library
>* enhancements have been gradually added over time
* [highlight.js](https://github.com/isagalaev/highlight.js) is used to provide syntax highlighting to the DOM structure
>* specifically, only its css files are used
* [js-beautify](https://github.com/beautify-web/js-beautify) is used to add whitespace for readability when syntax highlighting is turned off
>* the JSON text is piped through this library and displayed within a `
` tag
* [bignumber.js](https://github.com/MikeMcl/bignumber.js) and [json-bigint](https://github.com/sidorares/json-bigint) are used to provide an alternate JSON parser
>* this parser provides a way to work around the potential pitfall that:
> * JSON data can specify numeric values that are arbitrarily large
> * the primitive JavaScript `number` data type has constraints on the possible range of values that it can hold
> ( language limitations are described in detail @ [issue #4](https://github.com/warren-bank/moz-json-data-view/issues/4) )
>
>* it allows the add-on the ability to retain this fidelity
## Detection methodology
* This add-on will modify the display of all server responses (or local files) that satisfy all of the following criteria:
* none of the following short-circuit conditions are true:
* the location protocol is 'view-source:'
* the location hash contains: `No-JSON-DataView`
> notes:
> * not case sensitive
> * can be combined with other hash tokens by using one of the separators: `/,`
* either:
* the HTTP header 'content-type' is one of:
* 'application/json'
* 'text/json'
* 'text/x-json'
* or both must be true:
* the HTTP header 'content-type' is one of:
* 'application/javascript'
* 'application/x-javascript'
* 'text/javascript'
* 'text/plain'
* and one of the following additional conditions are met:
* the location pathname ends with '.json'
* the location querystring contains 'callback=',
and the response is structured as a JSONP callback function
* the location hash contains: `JSON-DataView`
## Comments
* It's become pretty standard practice for jsonp responses to contain javascript comments.
The comments serve as a form of protection against an Adobe Flash Player exploit that uses jsonp to bypass the same-origin security policy. This [attack](https://github.com/mikispag/rosettaflash) is known as [Rosetta Flash](http://miki.it/blog/2014/7/8/abusing-jsonp-with-rosetta-flash/).
* When processing the response to determine whether it contains a valid jsonp callback function,
the following javascript statements will be ignored:
* leading and trailing comments (in both `//` and `/* */` formats)
* leading validation of the callback function, using any of the patterns:
* `cb && cb(json)`
* `typeof cb === 'function' && cb(json)`
After the format of the response is validated, the parameter string is extracted from the callback function and treated as a string of JSON data.
* In the detection methodology, the inspection of the location hash for special `control tokens`
provides a user the added ability to explicitly override the normal detection logic.
This can be useful in a number of different circumstances. For instance:
* A web server response is known to contain JSON data;
however, the 'content-type' headers are too generic to pass normal detection.
This would normally be the result of a misconfigured web server,
or poorly written backend script.
> the solution would be to manually append the `control token` that explicitly signals
to the add-on that it should take action: `#JSON-DataView`
* Another scenario (that I [recently ran into](https://github.com/warren-bank/moz-harviewer)) is when two different add-ons
are both triggered to take action on the same page.
JSON is a very general-purpose way to structure/serialize/transmit data.
There are many `domain specific` data formats that are defined by a JSON schema.
One such example is the [HTTP Archive format](http://www.softwareishard.com/blog/har-12-spec/).
If there's an add-on that specifically targets one such format,
then whether or not there is the potential for conflict between the two add-ons
running at the same time depends on the particular data format.
* If it has been assigned its own 'content-type' (and if servers tend to use it),
then there won't be any conflict.
* However, if this data format is sent with a generic JSON-ish 'content-type',
then both add-ons will most likely be trying to detect the same conditions.
This is where having the option to manually add `control tokens` is a very good thing.
Concrete example:
* both add-ons are installed:
* [JSON-DataView](https://github.com/warren-bank/moz-json-data-view)
* [HTTP Archive Viewer](https://github.com/warren-bank/moz-harviewer)
* a HAR file is requested from a server
* the 'content-type' of the response is: `application/json`
* to view the HAR data in a rich visualization tool
(with charts and graphs, etc) using the `HTTP Archive Viewer` add-on,
the following `control tokens` could be used:
* http://httparchive.webpagetest.org/export.php?test=140801_0_8JH&run=1&cached=0&pretty=1#HTTP-Archive-Viewer/No-JSON-DataView
* conversely, to take a deep dive into the raw data using the `JSON-DataView` add-on,
the following `control tokens` could be used:
* http://httparchive.webpagetest.org/export.php?test=140801_0_8JH&run=1&cached=0&pretty=1#No-HTTP-Archive-Viewer/JSON-DataView
* note that:
* order of the `control tokens` doesn't matter
* they are both case insensitive
> the pretty capitalization is just for the README
* when the HTTP response includes a `content-disposition` header:
>* the browser will prompt to save the JSON data, rather than allowing it to be displayed in-browser by the add-on
>* the most general-purpose work around is to employ an additional add-on that is better suited to intercept and conditionally modify HTTP headers.
recommendations:
* [Open In Browser](https://addons.mozilla.org/en-us/firefox/addon/open-in-browser/)
adds additional options to the browser's _"save as"_ dialog window, which allows the user to choose how to proceed with each individual response
* [moz-rewrite](https://addons.mozilla.org/en-US/firefox/addon/moz-rewrite-js/)
provides a rules engine for conditionally modifying HTTP headers, which allows the user to add a [properly configured rule](https://github.com/warren-bank/moz-rewrite/blob/js/data/recipe-book/response/disable%20CSP.js) that will automatically rewrite the particular response headers when JSON data is received
## User Preferences:
* General Settings
* Syntax Highlights
>* Enable
> > default: `true`
>
> * true:
> builds an HTML DOM structure that supports presenting the data within a collapsible tree
> * false:
> filters the JSON data through `js-beautify`, and outputs into a `` DOM element
>
>* Expand All Nodes
> > default: `false`
>
> * true:
> during page load, initialize all collapsible tree nodes to an expanded state
> * false:
> during page load, initialize only the root tree node to an expanded state
>
>* Theme
> > default: `'solarized_dark'`
>
> options consist of those provided by [highlight.js](https://github.com/isagalaev/highlight.js/tree/master/src/styles)
* Optional Features
>* Use non-native JSON parser to support numbers that are too large to represent using primitive JavaScript data types
> > default: `true`
>
>* Display JSON syntax error when encountered by the JSON parser
> > default: `true`
* Display: Data Values
* Strings
>* `[false]` replace (`\n`) newline with HTML: `
` tag
>* `[false]` replace (`\t`) tab with 4 spaces
>* `[true]` replace urls with HTML: `` tag
>* `[true]` escape (`\` → `\\`) back-slash
>* `[false]` escape (`/` → `\/`) forward-slash
>* `[true]` escape (`"` → `\"`) double quote
>* `[true]` escape (not visible → `\r`) carriage return
>* `[true]` escape (white-space → `\n`) line feed
>* `[true]` escape (white-space → `\t`) tab
>* `[true]` escape (not visible → `\f`) form feed
>* `[true]` escape (not visible → `\b`) backspace
>* `[false]` escape (unicode representation → `\uNNNN`) non-ascii characters
* Display: Styles
* CSS
>* font-family
> > default: `''`
>
> the (internal) stylesheet assigns a default value.
> this preference is optional;
> if assigned a value, it will override the stylesheet.
>
>* font-size
> > default: `13`
>
> units: `px`
>
>* line-height
> > default: `2`
>
> units: `em`
>
>* padding around the ``
> > default: `1`
>
> units: `em`
>
>* width of indentation for expanded children
> > default: `1`
>
> units: `em`
>
> > **NOTE:**
> > `1.5em` is ADDED to the value specified through this setting.
> > This is the width required to ensure the expand/collapse button can be properly displayed.
## Examples
> URLs to render in-browser after the add-on has been installed, which illustrate its functionality
* http://graph.facebook.com/coca-cola?callback=hello_world
> * Facebook's graph API
> * JSONP request/response
> * 'content-type' of response === 'application/json'
>> _note: this should be 'application/javascript' or 'text/javascript'_
> * format of response content:
> ```javascript
/**/ hello_world({});
```
* http://www.google.com/calendar/feeds/developer-calendar@google.com/public/full?alt=json&callback=hello_world
> * Google's calendar of developer events
> * JSONP request/response
> * 'content-type' of response === 'text/javascript'
> * format of response content:
> ```javascript
// API callback
hello_world({});
```
* http://feeds.delicious.com/v2/json/popular?callback=hello_world
> * delicious.com data feed; top 10 most popular.. somethings
> * JSONP request/response
> * 'content-type' of response === 'text/javascript'
> * format of response content:
> ```javascript
hello_world([])
```
* https://api.twitter.com/1.1/statuses/user_timeline.json
> * response contains JSON data
> * 'content-type' of response === 'application/json'
* http://gdata.youtube.com/feeds/api/standardfeeds/most_popular?alt=json&v=2
> * response contains JSON data
> * 'content-type' of response === 'application/json'
* http://headers.jsontest.com/?mime=1
> * response contains JSON data
> * 'content-type' of response === 'application/json'
* http://headers.jsontest.com/?mime=2
> * 'content-type' of response === 'application/javascript'
> * __IS NOT__ acted upon
* the criteria for the detection methodology are not met
* any of the following methods could be used to satisfy these criteria:
* wrap the response in a JSONP callback (requires cooperation server-side):
http://headers.jsontest.com/?mime=2&callback=hello_world
* add a `control token` to the hash:
http://headers.jsontest.com/?mime=2#JSON-DataView
* http://headers.jsontest.com/?mime=3
> * 'content-type' of response === 'text/javascript'
> * __IS NOT__ acted upon
* same work-around methods could be used (as in the earlier example)
* http://headers.jsontest.com/?mime=4
> * 'content-type' of response === 'text/html'
> * __IS NOT__ acted upon
* Anecdotally, it appears that this 'content-type' is a special case.
* Under normal circumstances, an add-on is required to register itself to "convert streams" from the incoming 'content-type' values upon which it wishes to act. These "streams" are then (conditionally) "converted" into a 'text/html' request.
* It's unclear to me whether the internal logic used by the plugin architecture is that add-ons are invoked only on 'text/html' streams; and that this "stream conversion" API is a methodology by which to allow other streams to enter the workflow.
* All that I can say for certain is that the detection methodology doesn't specifically register this add-on to listen for 'text/html' streams, and yet it is invoked during their page-load processing.
* This being the case, one of the two aforementioned work-around methods would work:
* the presence of a `control token` [in the hash](http://headers.jsontest.com/?mime=4#JSON-DataView) will be seen and obeyed
* otherwise, the 'content-type' will fail to match a valid value and the add-on will short-circuit (exit immediately) before the querystring would normally be inspected for [a jsonp signature](http://headers.jsontest.com/?mime=4&callback=hello_world)
* http://headers.jsontest.com/?mime=5
> * 'content-type' of response === 'text/plain'
> * __IS NOT__ acted upon
* same work-around methods could be used (as in the earlier example)
## License
> [GPLv3](http://www.gnu.org/licenses/gpl-3.0.txt)
> Copyright (c) 2014, Warren Bank