├── .gitignore ├── LICENSE ├── README.md ├── example.png ├── extension-icon.png ├── extension-screenshot.png ├── icon-install_2x.png ├── jsonview.safariextension ├── Info.plist ├── default.css ├── icon.png └── jsonview.js ├── jsonview.safariextz └── update.plist /.gitignore: -------------------------------------------------------------------------------- 1 | *.certSigningRequest 2 | *.cer 3 | .DS_Store 4 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2014 Adrian Rangel 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Advanced JSON Viewer 2 | 3 | Better improved version: 4 | https://apps.apple.com/app/advanced-json-viewer/id6739271287 5 | 6 | # No longer available 7 | The safari extensions framework change a lot. This code is outdated 8 | 9 | jsonview-safari 10 | =============== 11 | 12 | Formats & syntax highlights JSON viewed inside of the web browser! 13 | 14 | [Install Now ![download-icon]][download-link] 15 | 16 | [View on Apple Safari Extensions Gallery](https://extensions.apple.com/details/?id=com.acrogenesis.jsonview-56Q494QF3L) 17 | 18 | This plugin was ported from [jsonview](https://github.com/bhollis/jsonview) and [jsonview-chrome](https://github.com/jamiew/jsonview-chrome) 19 | 20 | __If you like JSONView check out [JSONAce](https://github.com/acrogenesis/JSONAce) it's like JSONView but uses the ACE editor.__ 21 | 22 | ![example] 23 | 24 | Contributing 25 | --- 26 | 27 | 1. Fork it. 28 | 2. Create a branch `git checkout -b my_markup` 29 | 3. Commit your changes `git commit -am "Cool new feature"` 30 | 4. Push to the branch `git push origin my_markup` 31 | 5. Open a [Pull Request][1] 32 | 6. Enjoy a refreshing `Insert Favorite Beverage` and wait 33 | 34 | [1]: https://github.com/acrogenesis/jsonview-safari/pulls 35 | [download-link]: https://github.com/acrogenesis/jsonview-safari/raw/v1.7/jsonview.safariextz 36 | [download-icon]: https://github.com/acrogenesis/jsonview-safari/blob/master/icon-install_2x.png 37 | [example]: https://github.com/acrogenesis/jsonview-safari/blob/master/example.png 38 | -------------------------------------------------------------------------------- /example.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/acrogenesis/jsonview-safari/72421642adc65c987a637caf3df87bb5faef1140/example.png -------------------------------------------------------------------------------- /extension-icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/acrogenesis/jsonview-safari/72421642adc65c987a637caf3df87bb5faef1140/extension-icon.png -------------------------------------------------------------------------------- /extension-screenshot.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/acrogenesis/jsonview-safari/72421642adc65c987a637caf3df87bb5faef1140/extension-screenshot.png -------------------------------------------------------------------------------- /icon-install_2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/acrogenesis/jsonview-safari/72421642adc65c987a637caf3df87bb5faef1140/icon-install_2x.png -------------------------------------------------------------------------------- /jsonview.safariextension/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Author 6 | Adrian Rangel 7 | Builder Version 8 | 10600.7.5 9 | CFBundleDisplayName 10 | JsonView 11 | CFBundleIdentifier 12 | com.acrogenesis.jsonview 13 | CFBundleInfoDictionaryVersion 14 | 6.0 15 | CFBundleShortVersionString 16 | 1.7 17 | CFBundleVersion 18 | 1.7 19 | Chrome 20 | 21 | Global Page 22 | global.html 23 | 24 | Content 25 | 26 | Scripts 27 | 28 | End 29 | 30 | jsonview.js 31 | 32 | 33 | Stylesheets 34 | 35 | jsonview.css 36 | 37 | 38 | Description 39 | An extension that helps you view JSON documents in the browser. 40 | DeveloperIdentifier 41 | 56Q494QF3L 42 | ExtensionInfoDictionaryVersion 43 | 1.0 44 | Permissions 45 | 46 | Website Access 47 | 48 | Include Secure Pages 49 | 50 | Level 51 | All 52 | 53 | 54 | Update Manifest URL 55 | https://raw.githubusercontent.com/acrogenesis/jsonview-safari/master/update.plist 56 | Website 57 | https://github.com/acrogenesis/jsonview-safari 58 | 59 | 60 | -------------------------------------------------------------------------------- /jsonview.safariextension/default.css: -------------------------------------------------------------------------------- 1 | body { 2 | font-family: sans-serif; 3 | margin: 0; 4 | padding: 0; 5 | } 6 | 7 | .prop { 8 | font-weight: bold; 9 | } 10 | 11 | .null { 12 | color: red; 13 | } 14 | 15 | .bool { 16 | color: blue; 17 | } 18 | 19 | .num { 20 | color: blue; 21 | } 22 | 23 | .string { 24 | color: green; 25 | white-space: pre-wrap; 26 | } 27 | 28 | .collapser { 29 | position: absolute; 30 | left: -1em; 31 | top: -.2em; 32 | cursor: pointer; 33 | user-select: none; 34 | -webkit-backface-visibility: hidden; 35 | backface-visibility: hidden; 36 | -webkit-perspective: 1000; 37 | perspective: 1000; 38 | -webkit-transform: rotate(90deg); 39 | transform: rotate(90deg); 40 | -webkit-transition: -webkit-transform .2s; 41 | transition: transform .2s; 42 | } 43 | 44 | .collapser:before { 45 | content: "\25B8"; 46 | } 47 | 48 | .collapsible { 49 | -webkit-backface-visibility: hidden; 50 | backface-visibility: hidden; 51 | -webkit-perspective: 1000; 52 | perspective: 1000; 53 | -webkit-transition: height 1.2s; 54 | transition: height 1.2s; 55 | -webkit-transition: width 1.2s; 56 | transition: width 1.2s; 57 | } 58 | 59 | .collapsible.collapsed { 60 | height: .8em; 61 | width: 1em; 62 | display: inline-block; 63 | overflow: hidden; 64 | margin: 0; 65 | } 66 | .collapsible.collapsed:before { 67 | content: "…"; 68 | width: 1em; 69 | margin-left: .2em; 70 | } 71 | 72 | .collapser.collapsed { 73 | -webkit-backface-visibility: hidden; 74 | backface-visibility: hidden; 75 | -webkit-perspective: 1000; 76 | perspective: 1000; 77 | -webkit-transform: rotate(0deg); 78 | transform: rotate(0deg); 79 | } 80 | 81 | .q { 82 | display: inline-block; 83 | width: 0; 84 | color: transparent; 85 | } 86 | 87 | .quoted .q { 88 | display: inline; 89 | width: auto; 90 | color: inherit; 91 | font-weight: normal; 92 | } 93 | 94 | li { 95 | position: relative; 96 | } 97 | 98 | #error { 99 | border-bottom: 1px solid rgb(212, 209, 209); 100 | background-color: rgb(239, 239, 239); 101 | margin-bottom: 1.5em; 102 | padding: 1em .5em; 103 | } 104 | 105 | .errormessage { 106 | font-family: monospace; 107 | margin-top: .5em; 108 | color: rgb(167, 5, 5); 109 | } 110 | 111 | .errorcolumn { 112 | background-color: rgb(167, 5, 5); 113 | color: white; 114 | } 115 | 116 | .errorline { 117 | background-color: rgb(255, 226, 226); 118 | } 119 | 120 | #json { 121 | font-family: monospace; 122 | font-size: 1.1em; 123 | white-space: pre-wrap; 124 | margin: .5em; 125 | } 126 | 127 | ul { 128 | list-style: none; 129 | margin: 0 0 0 2em; 130 | padding: 0; 131 | } 132 | 133 | h1 { 134 | font-size: 1.2em; 135 | } 136 | 137 | /* Indent JSON when there's a callback. */ 138 | .callback + #json { 139 | padding-left: 1em; 140 | } 141 | 142 | .callback { 143 | font-family: monospace; 144 | color: #A52A2A; 145 | } 146 | 147 | .btn { 148 | z-index: 10; 149 | position: absolute; 150 | right: 10px; 151 | top: 10px; 152 | } 153 | /*! 154 | * Bootstrap v3.3.4 (http://getbootstrap.com) 155 | * Copyright 2011-2015 Twitter, Inc. 156 | * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE) 157 | */ 158 | 159 | /*! 160 | * Generated using the Bootstrap Customizer (http://getbootstrap.com/customize/?id=81c419f0ed2a9be873de) 161 | * Config saved to config.json and https://gist.github.com/81c419f0ed2a9be873de 162 | *//*! normalize.css v3.0.2 | MIT License | git.io/normalize */html{font-family:sans-serif;-ms-text-size-adjust:100%;-webkit-text-size-adjust:100%}body{margin:0}article,aside,details,figcaption,figure,footer,header,hgroup,main,menu,nav,section,summary{display:block}audio,canvas,progress,video{display:inline-block;vertical-align:baseline}audio:not([controls]){display:none;height:0}[hidden],template{display:none}a{background-color:transparent}a:active,a:hover{outline:0}abbr[title]{border-bottom:1px dotted}b,strong{font-weight:bold}dfn{font-style:italic}h1{font-size:2em;margin:0.67em 0}mark{background:#ff0;color:#000}small{font-size:80%}sub,sup{font-size:75%;line-height:0;position:relative;vertical-align:baseline}sup{top:-0.5em}sub{bottom:-0.25em}img{border:0}svg:not(:root){overflow:hidden}figure{margin:1em 40px}hr{-moz-box-sizing:content-box;-webkit-box-sizing:content-box;box-sizing:content-box;height:0}pre{overflow:auto}code,kbd,pre,samp{font-family:monospace, monospace;font-size:1em}button,input,optgroup,select,textarea{color:inherit;font:inherit;margin:0}button{overflow:visible}button,select{text-transform:none}button,html input[type="button"],input[type="reset"],input[type="submit"]{-webkit-appearance:button;cursor:pointer}button[disabled],html input[disabled]{cursor:default}button::-moz-focus-inner,input::-moz-focus-inner{border:0;padding:0}input{line-height:normal}input[type="checkbox"],input[type="radio"]{-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box;padding:0}input[type="number"]::-webkit-inner-spin-button,input[type="number"]::-webkit-outer-spin-button{height:auto}input[type="search"]{-webkit-appearance:textfield;-moz-box-sizing:content-box;-webkit-box-sizing:content-box;box-sizing:content-box}input[type="search"]::-webkit-search-cancel-button,input[type="search"]::-webkit-search-decoration{-webkit-appearance:none}fieldset{border:1px solid #c0c0c0;margin:0 2px;padding:0.35em 0.625em 0.75em}legend{border:0;padding:0}textarea{overflow:auto}optgroup{font-weight:bold}table{border-collapse:collapse;border-spacing:0}td,th{padding:0}*{-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box}*:before,*:after{-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box}html{font-size:10px;-webkit-tap-highlight-color:rgba(0,0,0,0)}body{font-family:"Helvetica Neue",Helvetica,Arial,sans-serif;font-size:14px;line-height:1.42857143;color:#333;background-color:#fff}input,button,select,textarea{font-family:inherit;font-size:inherit;line-height:inherit}a{color:#337ab7;text-decoration:none}a:hover,a:focus{color:#23527c;text-decoration:underline}a:focus{outline:thin dotted;outline:5px auto -webkit-focus-ring-color;outline-offset:-2px}figure{margin:0}img{vertical-align:middle}.img-responsive{display:block;max-width:100%;height:auto}.img-rounded{border-radius:6px}.img-thumbnail{padding:4px;line-height:1.42857143;background-color:#fff;border:1px solid #ddd;border-radius:4px;-webkit-transition:all .2s ease-in-out;-o-transition:all .2s ease-in-out;transition:all .2s ease-in-out;display:inline-block;max-width:100%;height:auto}.img-circle{border-radius:50%}hr{margin-top:20px;margin-bottom:20px;border:0;border-top:1px solid #eee}.sr-only{position:absolute;width:1px;height:1px;margin:-1px;padding:0;overflow:hidden;clip:rect(0, 0, 0, 0);border:0}.sr-only-focusable:active,.sr-only-focusable:focus{position:static;width:auto;height:auto;margin:0;overflow:visible;clip:auto}[role="button"]{cursor:pointer}.btn{display:inline-block;margin-bottom:0;font-weight:normal;text-align:center;vertical-align:middle;-ms-touch-action:manipulation;touch-action:manipulation;cursor:pointer;background-image:none;border:1px solid transparent;white-space:nowrap;padding:6px 12px;font-size:14px;line-height:1.42857143;border-radius:4px;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none}.btn:focus,.btn:active:focus,.btn.active:focus,.btn.focus,.btn:active.focus,.btn.active.focus{outline:thin dotted;outline:5px auto -webkit-focus-ring-color;outline-offset:-2px}.btn:hover,.btn:focus,.btn.focus{color:#333;text-decoration:none}.btn:active,.btn.active{outline:0;background-image:none;-webkit-box-shadow:inset 0 3px 5px rgba(0,0,0,0.125);box-shadow:inset 0 3px 5px rgba(0,0,0,0.125)}.btn.disabled,.btn[disabled],fieldset[disabled] .btn{cursor:not-allowed;pointer-events:none;opacity:.65;filter:alpha(opacity=65);-webkit-box-shadow:none;box-shadow:none}.btn-default{color:#333;background-color:#fff;border-color:#ccc}.btn-default:hover,.btn-default:focus,.btn-default.focus,.btn-default:active,.btn-default.active,.open>.dropdown-toggle.btn-default{color:#333;background-color:#e6e6e6;border-color:#adadad}.btn-default:active,.btn-default.active,.open>.dropdown-toggle.btn-default{background-image:none}.btn-default.disabled,.btn-default[disabled],fieldset[disabled] .btn-default,.btn-default.disabled:hover,.btn-default[disabled]:hover,fieldset[disabled] .btn-default:hover,.btn-default.disabled:focus,.btn-default[disabled]:focus,fieldset[disabled] .btn-default:focus,.btn-default.disabled.focus,.btn-default[disabled].focus,fieldset[disabled] .btn-default.focus,.btn-default.disabled:active,.btn-default[disabled]:active,fieldset[disabled] .btn-default:active,.btn-default.disabled.active,.btn-default[disabled].active,fieldset[disabled] .btn-default.active{background-color:#fff;border-color:#ccc}.btn-default .badge{color:#fff;background-color:#333}.btn-primary{color:#fff;background-color:#337ab7;border-color:#2e6da4}.btn-primary:hover,.btn-primary:focus,.btn-primary.focus,.btn-primary:active,.btn-primary.active,.open>.dropdown-toggle.btn-primary{color:#fff;background-color:#286090;border-color:#204d74}.btn-primary:active,.btn-primary.active,.open>.dropdown-toggle.btn-primary{background-image:none}.btn-primary.disabled,.btn-primary[disabled],fieldset[disabled] .btn-primary,.btn-primary.disabled:hover,.btn-primary[disabled]:hover,fieldset[disabled] .btn-primary:hover,.btn-primary.disabled:focus,.btn-primary[disabled]:focus,fieldset[disabled] .btn-primary:focus,.btn-primary.disabled.focus,.btn-primary[disabled].focus,fieldset[disabled] .btn-primary.focus,.btn-primary.disabled:active,.btn-primary[disabled]:active,fieldset[disabled] .btn-primary:active,.btn-primary.disabled.active,.btn-primary[disabled].active,fieldset[disabled] .btn-primary.active{background-color:#337ab7;border-color:#2e6da4}.btn-primary .badge{color:#337ab7;background-color:#fff}.btn-success{color:#fff;background-color:#5cb85c;border-color:#4cae4c}.btn-success:hover,.btn-success:focus,.btn-success.focus,.btn-success:active,.btn-success.active,.open>.dropdown-toggle.btn-success{color:#fff;background-color:#449d44;border-color:#398439}.btn-success:active,.btn-success.active,.open>.dropdown-toggle.btn-success{background-image:none}.btn-success.disabled,.btn-success[disabled],fieldset[disabled] .btn-success,.btn-success.disabled:hover,.btn-success[disabled]:hover,fieldset[disabled] .btn-success:hover,.btn-success.disabled:focus,.btn-success[disabled]:focus,fieldset[disabled] .btn-success:focus,.btn-success.disabled.focus,.btn-success[disabled].focus,fieldset[disabled] .btn-success.focus,.btn-success.disabled:active,.btn-success[disabled]:active,fieldset[disabled] .btn-success:active,.btn-success.disabled.active,.btn-success[disabled].active,fieldset[disabled] .btn-success.active{background-color:#5cb85c;border-color:#4cae4c}.btn-success .badge{color:#5cb85c;background-color:#fff}.btn-info{color:#fff;background-color:#5bc0de;border-color:#46b8da}.btn-info:hover,.btn-info:focus,.btn-info.focus,.btn-info:active,.btn-info.active,.open>.dropdown-toggle.btn-info{color:#fff;background-color:#31b0d5;border-color:#269abc}.btn-info:active,.btn-info.active,.open>.dropdown-toggle.btn-info{background-image:none}.btn-info.disabled,.btn-info[disabled],fieldset[disabled] .btn-info,.btn-info.disabled:hover,.btn-info[disabled]:hover,fieldset[disabled] .btn-info:hover,.btn-info.disabled:focus,.btn-info[disabled]:focus,fieldset[disabled] .btn-info:focus,.btn-info.disabled.focus,.btn-info[disabled].focus,fieldset[disabled] .btn-info.focus,.btn-info.disabled:active,.btn-info[disabled]:active,fieldset[disabled] .btn-info:active,.btn-info.disabled.active,.btn-info[disabled].active,fieldset[disabled] .btn-info.active{background-color:#5bc0de;border-color:#46b8da}.btn-info .badge{color:#5bc0de;background-color:#fff}.btn-warning{color:#fff;background-color:#f0ad4e;border-color:#eea236}.btn-warning:hover,.btn-warning:focus,.btn-warning.focus,.btn-warning:active,.btn-warning.active,.open>.dropdown-toggle.btn-warning{color:#fff;background-color:#ec971f;border-color:#d58512}.btn-warning:active,.btn-warning.active,.open>.dropdown-toggle.btn-warning{background-image:none}.btn-warning.disabled,.btn-warning[disabled],fieldset[disabled] .btn-warning,.btn-warning.disabled:hover,.btn-warning[disabled]:hover,fieldset[disabled] .btn-warning:hover,.btn-warning.disabled:focus,.btn-warning[disabled]:focus,fieldset[disabled] .btn-warning:focus,.btn-warning.disabled.focus,.btn-warning[disabled].focus,fieldset[disabled] .btn-warning.focus,.btn-warning.disabled:active,.btn-warning[disabled]:active,fieldset[disabled] .btn-warning:active,.btn-warning.disabled.active,.btn-warning[disabled].active,fieldset[disabled] .btn-warning.active{background-color:#f0ad4e;border-color:#eea236}.btn-warning .badge{color:#f0ad4e;background-color:#fff}.btn-danger{color:#fff;background-color:#d9534f;border-color:#d43f3a}.btn-danger:hover,.btn-danger:focus,.btn-danger.focus,.btn-danger:active,.btn-danger.active,.open>.dropdown-toggle.btn-danger{color:#fff;background-color:#c9302c;border-color:#ac2925}.btn-danger:active,.btn-danger.active,.open>.dropdown-toggle.btn-danger{background-image:none}.btn-danger.disabled,.btn-danger[disabled],fieldset[disabled] .btn-danger,.btn-danger.disabled:hover,.btn-danger[disabled]:hover,fieldset[disabled] .btn-danger:hover,.btn-danger.disabled:focus,.btn-danger[disabled]:focus,fieldset[disabled] .btn-danger:focus,.btn-danger.disabled.focus,.btn-danger[disabled].focus,fieldset[disabled] .btn-danger.focus,.btn-danger.disabled:active,.btn-danger[disabled]:active,fieldset[disabled] .btn-danger:active,.btn-danger.disabled.active,.btn-danger[disabled].active,fieldset[disabled] .btn-danger.active{background-color:#d9534f;border-color:#d43f3a}.btn-danger .badge{color:#d9534f;background-color:#fff}.btn-link{color:#337ab7;font-weight:normal;border-radius:0}.btn-link,.btn-link:active,.btn-link.active,.btn-link[disabled],fieldset[disabled] .btn-link{background-color:transparent;-webkit-box-shadow:none;box-shadow:none}.btn-link,.btn-link:hover,.btn-link:focus,.btn-link:active{border-color:transparent}.btn-link:hover,.btn-link:focus{color:#23527c;text-decoration:underline;background-color:transparent}.btn-link[disabled]:hover,fieldset[disabled] .btn-link:hover,.btn-link[disabled]:focus,fieldset[disabled] .btn-link:focus{color:#777;text-decoration:none}.btn-lg{padding:10px 16px;font-size:18px;line-height:1.3333333;border-radius:6px}.btn-sm{padding:5px 10px;font-size:12px;line-height:1.5;border-radius:3px}.btn-xs{padding:1px 5px;font-size:12px;line-height:1.5;border-radius:3px}.btn-block{display:block;width:100%}.btn-block+.btn-block{margin-top:5px}input[type="submit"].btn-block,input[type="reset"].btn-block,input[type="button"].btn-block{width:100%}.clearfix:before,.clearfix:after{content:" ";display:table}.clearfix:after{clear:both}.center-block{display:block;margin-left:auto;margin-right:auto}.pull-right{float:right !important}.pull-left{float:left !important}.hide{display:none !important}.show{display:block !important}.invisible{visibility:hidden}.text-hide{font:0/0 a;color:transparent;text-shadow:none;background-color:transparent;border:0}.hidden{display:none !important}.affix{position:fixed} 163 | -------------------------------------------------------------------------------- /jsonview.safariextension/icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/acrogenesis/jsonview-safari/72421642adc65c987a637caf3df87bb5faef1140/jsonview.safariextension/icon.png -------------------------------------------------------------------------------- /jsonview.safariextension/jsonview.js: -------------------------------------------------------------------------------- 1 | this.data = document.body.innerHTML; 2 | this.uri = document.location.href; 3 | 4 | if(document.getElementsByTagName("pre")[0] && document.body.getElementsByTagName('*').length == 1 && document.getElementsByTagName("pre").length == 1){ 5 | // console.log("JSONView: data is wrapped in
...
, stripping HTML..."); 6 | this.data = document.getElementsByTagName("pre")[0].innerHTML; 7 | } 8 | 9 | function IsJsonString(str) { 10 | try { 11 | JSON.parse(str); 12 | } catch (e) { 13 | return false; 14 | } 15 | return true; 16 | } 17 | 18 | var is_json = IsJsonString(this.data); 19 | 20 | if(is_json){ 21 | if (!(window.location.hash === "#raw-json")){ 22 | /* 23 | * The JSONFormatter helper object. This contains two major functions, jsonToHTML and errorPage, 24 | * each of which returns an HTML document. 25 | */ 26 | function JSONFormatter() { } 27 | 28 | JSONFormatter.prototype = { 29 | /** 30 | * Completely escape a json string 31 | */ 32 | jsString: function(s) { 33 | // Slice off the surrounding quotes 34 | s = JSON.stringify(s).slice(1, -1); 35 | return s; 36 | }, 37 | 38 | /** 39 | * Is this a valid "bare" property name? 40 | */ 41 | isBareProp: function(prop) { 42 | return /^[A-Za-z_$][A-Za-z0-9_\-$]*$/.test(prop); 43 | }, 44 | 45 | /** 46 | * Surround value with a span, including the given className 47 | */ 48 | decorateWithSpan: function(value, className) { 49 | return '' + value + ''; 50 | }, 51 | 52 | // Convert a basic JSON datatype (number, string, boolean, null, object, array) into an HTML fragment. 53 | valueToHTML: function(value, path) { 54 | var valueType = typeof value; 55 | 56 | if (value === null) { 57 | return this.decorateWithSpan('null', 'null'); 58 | } 59 | else if (Array.isArray(value)) { 60 | return this.arrayToHTML(value, path); 61 | } 62 | else if (valueType == 'object') { 63 | return this.objectToHTML(value, path); 64 | } 65 | else if (valueType == 'number') { 66 | return this.decorateWithSpan(value, 'num'); 67 | } 68 | else if (valueType == 'string') { 69 | if (/^(http|https|file):\/\/[^\s]+$/i.test(value)) { 70 | return '"' + this.jsString(value) + '"'; 71 | } else { 72 | return '"' + this.jsString(value) + '"'; 73 | } 74 | } 75 | else if (valueType == 'boolean') { 76 | return this.decorateWithSpan(value, 'bool'); 77 | } 78 | 79 | return ''; 80 | }, 81 | 82 | // Convert an array into an HTML fragment 83 | arrayToHTML: function(json, path) { 84 | if (json.length === 0) { 85 | return '[ ]'; 86 | } 87 | 88 | var output = ''; 89 | for (var i = 0; i < json.length; i++) { 90 | var subPath = path + '[' + i + ']'; 91 | output += '
  • ' + this.valueToHTML(json[i], subPath); 92 | if (i < json.length - 1) { 93 | output += ','; 94 | } 95 | output += '
  • '; 96 | } 97 | return '[]'; 98 | }, 99 | 100 | // Convert a JSON object to an HTML fragment 101 | objectToHTML: function(json, path) { 102 | var numProps = Object.keys(json).length; 103 | if (numProps === 0) { 104 | return '{ }'; 105 | } 106 | 107 | var output = ''; 108 | for (var prop in json) { 109 | var subPath = ''; 110 | var escapedProp = JSON.stringify(prop).slice(1, -1); 111 | var bare = this.isBareProp(prop); 112 | if (bare) { 113 | subPath = path + '.' + escapedProp; 114 | } else { 115 | escapedProp = '"' + escapedProp + '"'; 116 | } 117 | output += '
  • "' + this.jsString(prop) + 119 | '": ' + this.valueToHTML(json[prop], subPath); 120 | if (numProps > 1) { 121 | output += ','; 122 | } 123 | output += '
  • '; 124 | numProps--; 125 | } 126 | 127 | return '{}'; 128 | }, 129 | 130 | // Convert a whole JSON value / JSONP response into a formatted HTML document 131 | jsonToHTML: function(json, callback, uri) { 132 | var output = '
    ' + this.valueToHTML(json, '') + '
    '; 133 | if (callback) { 134 | output = '
    ' + callback + '(
    ' + output + '
    )
    '; 135 | } 136 | return this.toHTML(output, uri); 137 | }, 138 | 139 | // Clean up a JSON parsing error message 140 | massageError: function(error) { 141 | var message = error.message.replace(/^JSON.parse: /, '').replace(/of the JSON data/, ''); 142 | var parts = /line (\d+) column (\d+)/.exec(message); 143 | 144 | return { 145 | message: message, 146 | line: +parts[1], 147 | column: +parts[2] 148 | }; 149 | }, 150 | 151 | highlightError: function(data, lineNum, columnNum) { 152 | if (!lineNum || !columnNum) { 153 | return data; 154 | } 155 | 156 | var lines = data.match(/^.*((\r\n|\n|\r)|$)/gm); 157 | 158 | var output = ''; 159 | for (var i = 0; i < lines.length; i++) { 160 | var line = lines[i]; 161 | 162 | if (i == lineNum - 1) { 163 | output += ''; 164 | output += line.substring(0, columnNum - 1) + '' + line[columnNum - 1] + '' + line.substring(columnNum); 165 | output += ''; 166 | } else { 167 | output += line; 168 | } 169 | } 170 | 171 | return output; 172 | }, 173 | 174 | // Produce an error document for when parsing fails. 175 | errorPage: function(error, data, uri) { 176 | // Escape unicode nulls 177 | data = data.replace("\u0000","\uFFFD"); 178 | 179 | var errorInfo = this.massageError(error); 180 | 181 | var output = '
    ' + _('errorParsing'); 182 | if (errorInfo.message) { 183 | output += '
    ' + errorInfo.message + '
    '; 184 | } 185 | output += '
    ' + this.highlightError(data, errorInfo.line, errorInfo.column) + '
    '; 186 | return this.toHTML(output, uri + ' - Error'); 187 | }, 188 | 189 | // Wrap the HTML fragment in a full document. Used by jsonToHTML and errorPage. 190 | toHTML: function(content, title) { 191 | return '\n' + 192 | '' + title + '' + 193 | '' + 194 | '' + 195 | content + 196 | ''; 197 | } 198 | }; 199 | 200 | // Sanitize & output -- all magic from JSONView Firefox 201 | this.jsonFormatter = new JSONFormatter(); 202 | 203 | // This regex attempts to match a JSONP structure: 204 | // * Any amount of whitespace (including unicode nonbreaking spaces) between the start of the file and the callback name 205 | // * Callback name (any valid JavaScript function name according to ECMA-262 Edition 3 spec) 206 | // * Any amount of whitespace (including unicode nonbreaking spaces) 207 | // * Open parentheses 208 | // * Any amount of whitespace (including unicode nonbreaking spaces) 209 | // * Either { or [, the only two valid characters to start a JSON string. 210 | // * Any character, any number of times 211 | // * Either } or ], the only two valid closing characters of a JSON string. 212 | // * Any amount of whitespace (including unicode nonbreaking spaces) 213 | // * A closing parenthesis, an optional semicolon, and any amount of whitespace (including unicode nonbreaking spaces) until the end of the file. 214 | // This will miss anything that has comments, or more than one callback, or requires modification before use. 215 | var outputDoc = ''; 216 | // text = text.match(jsonp_regex)[1]; 217 | var cleanData = '', 218 | callback = ''; 219 | 220 | var callback_results = IsJsonString(this.data); 221 | if( callback_results && callback_results.length == 3 ){ 222 | // console.log("THIS IS JSONp"); 223 | callback = callback_results[1]; 224 | cleanData = callback_results[2]; 225 | } else { 226 | // console.log("Vanilla JSON"); 227 | cleanData = this.data; 228 | } 229 | // console.log(cleanData); 230 | 231 | // Covert, and catch exceptions on failure 232 | try { 233 | // var jsonObj = this.nativeJSON.decode(cleanData); 234 | var jsonObj = JSON.parse(cleanData); 235 | if ( jsonObj ) { 236 | outputDoc = this.jsonFormatter.jsonToHTML(jsonObj, callback, this.uri); 237 | } else { 238 | throw "There was no object!"; 239 | } 240 | } catch(e) { 241 | // console.log(e); 242 | outputDoc = this.jsonFormatter.errorPage(e, this.data, this.uri); 243 | } 244 | // document.body.innerHTML = outputDoc; 245 | document.documentElement.innerHTML = 'RAW' + outputDoc; 246 | 247 | ////////////////////////////////// 248 | ////////BEGINS DEFAULT.JS///////// 249 | ////////////////////////////////// 250 | 251 | // Click handler for collapsing and expanding objects and arrays 252 | function collapse(evt) { 253 | var collapser = evt.target; 254 | 255 | while (collapser && (!collapser.classList || !collapser.classList.contains('collapser'))) { 256 | collapser = collapser.nextSibling; 257 | } 258 | if (!collapser || !collapser.classList || !collapser.classList.contains('collapser')) { 259 | return; 260 | } 261 | 262 | evt.stopPropagation(); 263 | 264 | collapser.classList.toggle('collapsed'); 265 | 266 | var collapsible = collapser; 267 | while (collapsible && (!collapsible.classList || !collapsible.classList.contains('collapsible'))) { 268 | collapsible = collapsible.nextSibling; 269 | } 270 | collapsible.classList.toggle('collapsed'); 271 | } 272 | 273 | /* 274 | * Collapses the whole json using keyboard 275 | * TODO: Add a navigator support for each of the elements 276 | */ 277 | function collapseAll(evt) { 278 | var inputList; 279 | 280 | // Ignore anything paired with a modifier key. See https://github.com/bhollis/jsonview/issues/69 281 | if (evt.ctrlKey || evt.shiftKey || evt.altKey || evt.metaKey) { 282 | return; 283 | } 284 | 285 | if (evt.keyCode === 37) { // Collapses the json on left arrow key up 286 | inputList = document.querySelectorAll('.collapsible, .collapser'); 287 | for (var i = 0; i < inputList.length; i++) { 288 | if (inputList[i].parentNode.id != 'json') { 289 | inputList[i].classList.add('collapsed'); 290 | } 291 | } 292 | evt.preventDefault(); 293 | } else if (evt.keyCode === 39) { // Expands the json on right arrow key up 294 | inputList = document.querySelectorAll('.collapsed'); 295 | for (var i = 0; i < inputList.length; i++) { 296 | inputList[i].classList.remove('collapsed'); 297 | } 298 | evt.preventDefault(); 299 | } 300 | } 301 | 302 | // collapse with event delegation 303 | document.addEventListener('click', collapse, false); 304 | document.addEventListener('keyup', collapseAll, false); 305 | 306 | ////////////////////////////////// 307 | /////////ENDS DEFAULT.JS////////// 308 | ////////////////////////////////// 309 | 310 | /** 311 | * Converts the markup to DOM nodes 312 | * 313 | * @private 314 | * @param {string|Markup} value The node 315 | * @return {Node} 316 | */ 317 | function toDOM(value) { 318 | var wrapper = createElement('div'); 319 | wrapper.innerHTML = ''+value; 320 | 321 | // trim extraneous whitespace 322 | trimWhitespace(wrapper); 323 | 324 | // eliminate wrapper for single nodes 325 | if (wrapper.childNodes.length === 1) { 326 | return wrapper.firstChild; 327 | } 328 | 329 | // create a document fragment to hold elements 330 | var frag = createElement(''); 331 | while (wrapper.firstChild) { 332 | frag.appendChild(wrapper.firstChild); 333 | } 334 | return frag; 335 | } 336 | var rawButton = document.getElementById('raw'); 337 | rawButton.href = location.protocol+'//'+location.host+location.pathname+'?&jsonace='+Math.floor(Math.random()*1000)+'#raw-json'; 338 | } else { 339 | var outputDoc = 'BEAUTIFY' + this.data; 340 | var impcss = document.createElement('link'); 341 | impcss.rel = "stylesheet"; 342 | impcss.href = safari.extension.baseURI + 'default.css'; 343 | document.head.appendChild(impcss); 344 | document.body.innerHTML = outputDoc; 345 | } 346 | }else { 347 | // console.log("JSONView: this is not json, not formatting."); 348 | } 349 | -------------------------------------------------------------------------------- /jsonview.safariextz: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/acrogenesis/jsonview-safari/72421642adc65c987a637caf3df87bb5faef1140/jsonview.safariextz -------------------------------------------------------------------------------- /update.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Extension Updates 6 | 7 | 8 | CFBundleIdentifier 9 | com.acrogenesis.jsonview 10 | Developer Identifier 11 | 56Q494QF3L 12 | CFBundleVersion 13 | 1.7 14 | CFBundleShortVersionString 15 | 1.7 16 | URL 17 | https://github.com/acrogenesis/jsonview-safari/raw/v1.7/jsonview.safariextz 18 | 19 | 20 | 21 | 22 | --------------------------------------------------------------------------------