Permalink
Browse files

DOS to UNIX

  • Loading branch information...
1 parent 5b69e10 commit 91dadbb95825f45dd37793be5e6977372691aa43 @bhollis committed Jul 11, 2011
View
84 CHANGELOG.txt
@@ -1,43 +1,43 @@
-JSONView ???
-* Fixed display of newlines and multiple spaces - formatting is now better preserved.
-* Fixed display of ECMAScript 5th Edition style JSON (with non-object/array root values) thanks to a patch from Josh Kline.
-* URLs are matched even with uppercase scheme (such as "httpS://")
-
-JSONView 0.4
-* Preference dialog with an option to send "application/json" in the HTTP Accept header.
-* Fixed a bug where values that were 0 or false would not show up.
-* Fixed a bug where documents would sometimes show up as invalid when Firebug was enabled
-* Bumped compatibility for 3.7a1pre
-
-JSONView 0.3
-* Detects and displays JSONP, including display of the callback.
-* Doesn't show empty arrays and objects as collapsible, and displays them on one line.
-* Fixed an error reading large files.
-
-JSONView 0.2
-* .json files can be opened in JSONView from the local machine.
-* Added Bulgarian, German, Hungarian, and (partial) Turkish translations. Thanks to
- Стоян Димитров, Archaeopteryx, Team erweiterungen.de, MIKES KASZMÁN István and Ersen Yolda from BabelZilla.org.
-* Compatible with FF 3.5.*
-
-JSONView 0.1
-
-* String values which contain only a URL are displayed as a clickable link. Props to stig.murberg for the patch.
-* Added Catalan, Spanish, Japanese, Portugese (Brazil), Russian, and Simplified Chinese translations. Thanks to
- Fani Kozolchyk, and Xavi Ivars - Softcatalà, hoolooday, drry, Marcelo Ghelman (ghelman.net), Пирятинский Виталий,
- and urko from BabelZilla.org.
-
-JSONView 0.1b3:
-
-* Compatible with FF 3.1 Beta 3
-* Fixed a bug where documents >8KB wouldn't be parsed.
-* Added Czech, French, and Dutch translations. Thanks to funTomas, Goofy, and markh at babelzilla.org.
-* Removed dependency on jQuery - extension is smaller, faster, no more warnings in strict mode.
-
-JSONView 0.1b2:
-
-* Compatible with FF 3.1 Beta 2
-
-JSONView 0.1b1:
-
+JSONView ???
+* Fixed display of newlines and multiple spaces - formatting is now better preserved.
+* Fixed display of ECMAScript 5th Edition style JSON (with non-object/array root values) thanks to a patch from Josh Kline.
+* URLs are matched even with uppercase scheme (such as "httpS://")
+
+JSONView 0.4
+* Preference dialog with an option to send "application/json" in the HTTP Accept header.
+* Fixed a bug where values that were 0 or false would not show up.
+* Fixed a bug where documents would sometimes show up as invalid when Firebug was enabled
+* Bumped compatibility for 3.7a1pre
+
+JSONView 0.3
+* Detects and displays JSONP, including display of the callback.
+* Doesn't show empty arrays and objects as collapsible, and displays them on one line.
+* Fixed an error reading large files.
+
+JSONView 0.2
+* .json files can be opened in JSONView from the local machine.
+* Added Bulgarian, German, Hungarian, and (partial) Turkish translations. Thanks to
+ Стоян Димитров, Archaeopteryx, Team erweiterungen.de, MIKES KASZMÁN István and Ersen Yolda from BabelZilla.org.
+* Compatible with FF 3.5.*
+
+JSONView 0.1
+
+* String values which contain only a URL are displayed as a clickable link. Props to stig.murberg for the patch.
+* Added Catalan, Spanish, Japanese, Portugese (Brazil), Russian, and Simplified Chinese translations. Thanks to
+ Fani Kozolchyk, and Xavi Ivars - Softcatalà, hoolooday, drry, Marcelo Ghelman (ghelman.net), Пирятинский Виталий,
+ and urko from BabelZilla.org.
+
+JSONView 0.1b3:
+
+* Compatible with FF 3.1 Beta 3
+* Fixed a bug where documents >8KB wouldn't be parsed.
+* Added Czech, French, and Dutch translations. Thanks to funTomas, Goofy, and markh at babelzilla.org.
+* Removed dependency on jQuery - extension is smaller, faster, no more warnings in strict mode.
+
+JSONView 0.1b2:
+
+* Compatible with FF 3.1 Beta 2
+
+JSONView 0.1b1:
+
* First beta release
View
58 build.xml
@@ -1,29 +1,29 @@
-<?xml version="1.0"?>
-<project name="jsonview" default="createxpi">
- <property name="build.version" value="0.6"/>
- <property name="xpi.name" value="${ant.project.name}-${build.version}.xpi"/>
- <property name="xpi-babelzilla.name" value="${ant.project.name}-${build.version}-babelzilla.xpi"/>
-
- <target name="createxpi">
- <delete file="${xpi.name}"/>
- <zip destfile="${xpi.name}">
- <zipfileset dir="src">
- <exclude name="**.svn" />
- <!-- these locales are only for BabelZilla, and should not be included in release versions -->
- <exclude name="**/amo-descriptions.properties" />
- <exclude name="**/description.properties" />
- </zipfileset>
- </zip>
- <echo message="Remember to bump the version number in build.xml and install.rdf!" />
- </target>
-
- <!-- Create an XPI for BabelZilla that contains extra strings for translation. Do not release this! -->
- <target name="babelzilla">
- <delete file="${xpi-babelzilla.name}"/>
- <zip destfile="${xpi-babelzilla.name}">
- <zipfileset dir="src">
- <exclude name="**.svn" />
- </zipfileset>
- </zip>
- </target>
-</project>
+<?xml version="1.0"?>
+<project name="jsonview" default="createxpi">
+ <property name="build.version" value="0.6"/>
+ <property name="xpi.name" value="${ant.project.name}-${build.version}.xpi"/>
+ <property name="xpi-babelzilla.name" value="${ant.project.name}-${build.version}-babelzilla.xpi"/>
+
+ <target name="createxpi">
+ <delete file="${xpi.name}"/>
+ <zip destfile="${xpi.name}">
+ <zipfileset dir="src">
+ <exclude name="**.svn" />
+ <!-- these locales are only for BabelZilla, and should not be included in release versions -->
+ <exclude name="**/amo-descriptions.properties" />
+ <exclude name="**/description.properties" />
+ </zipfileset>
+ </zip>
+ <echo message="Remember to bump the version number in build.xml and install.rdf!" />
+ </target>
+
+ <!-- Create an XPI for BabelZilla that contains extra strings for translation. Do not release this! -->
+ <target name="babelzilla">
+ <delete file="${xpi-babelzilla.name}"/>
+ <zip destfile="${xpi-babelzilla.name}">
+ <zipfileset dir="src">
+ <exclude name="**.svn" />
+ </zipfileset>
+ </zip>
+ </target>
+</project>
View
40 license.txt
@@ -1,21 +1,21 @@
-MIT License
-
-Copyright (c) 2009 Benjamin Hollis
-
-Permission is hereby granted, free of charge, to any person obtaining a copy
-of this software and associated documentation files (the "Software"), to deal
-in the Software without restriction, including without limitation the rights
-to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
-copies of the Software, and to permit persons to whom the Software is
-furnished to do so, subject to the following conditions:
-
-The above copyright notice and this permission notice shall be included in
-all copies or substantial portions of the Software.
-
-THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
-AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+MIT License
+
+Copyright (c) 2009 Benjamin Hollis
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in
+all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
View
38 src/chrome.manifest
@@ -1,20 +1,20 @@
-content jsonview content/ contentaccessible=yes
-overlay chrome://browser/content/browser.xul chrome://jsonview/content/browser-overlay.xul
-skin jsonview default skin/default/
-locale jsonview en-US locale/en-US/
-locale jsonview cs-CZ locale/cs-CZ/
-locale jsonview fr-FR locale/fr-FR/
-locale jsonview nl-NL locale/nl-NL/
-locale jsonview es-ES locale/es-ES/
-locale jsonview ca-AD locale/ca-AD/
-locale jsonview ja-JP locale/ja-JP/
-locale jsonview pt-BR locale/pt-BR/
-locale jsonview ru-RU locale/ru-RU/
-locale jsonview zh-CN locale/zh-CN/
-locale jsonview bg-BG locale/bg-BG/
-locale jsonview de-DE locale/de-DE/
-locale jsonview hu-HU locale/hu-HU/
-locale jsonview tr-TR locale/tr-TR/
-component {64890660-53c4-11dd-ae16-0800200c9a66} components/jsonview.js
-contract @mozilla.org/streamconv;1?from=application/json&to=*/* {64890660-53c4-11dd-ae16-0800200c9a66}
+content jsonview content/ contentaccessible=yes
+overlay chrome://browser/content/browser.xul chrome://jsonview/content/browser-overlay.xul
+skin jsonview default skin/default/
+locale jsonview en-US locale/en-US/
+locale jsonview cs-CZ locale/cs-CZ/
+locale jsonview fr-FR locale/fr-FR/
+locale jsonview nl-NL locale/nl-NL/
+locale jsonview es-ES locale/es-ES/
+locale jsonview ca-AD locale/ca-AD/
+locale jsonview ja-JP locale/ja-JP/
+locale jsonview pt-BR locale/pt-BR/
+locale jsonview ru-RU locale/ru-RU/
+locale jsonview zh-CN locale/zh-CN/
+locale jsonview bg-BG locale/bg-BG/
+locale jsonview de-DE locale/de-DE/
+locale jsonview hu-HU locale/hu-HU/
+locale jsonview tr-TR locale/tr-TR/
+component {64890660-53c4-11dd-ae16-0800200c9a66} components/jsonview.js
+contract @mozilla.org/streamconv;1?from=application/json&to=*/* {64890660-53c4-11dd-ae16-0800200c9a66}
category ext-to-type-mapping json application/json
View
674 src/components/jsonview.js
@@ -1,338 +1,338 @@
-/**
- * @author Benjamin Hollis
- *
- * This component provides a stream converter that can translate from JSON to HTML.
- * It is compatible with Firefox 3 and up, since it uses many components that are new
- * to Firefox 3.
- */
-
-// Save some tedious typing
-const Ci = Components.interfaces;
-const Cc = Components.classes;
-
-// Import XPCOMUtils to help set up our JSONView XPCOM component (new to FF3)
-Components.utils.import("resource://gre/modules/XPCOMUtils.jsm");
-
-/*
- * The JSONFormatter helper object. This contains two major functions, jsonToHTML and errorPage,
- * each of which returns an HTML document.
- */
-function JSONFormatter() {
- var src = 'chrome://jsonview/locale/jsonview.properties';
- var localeService = Cc["@mozilla.org/intl/nslocaleservice;1"].getService(Ci.nsILocaleService);
-
- var appLocale = localeService.getApplicationLocale();
- var stringBundleService = Cc["@mozilla.org/intl/stringbundle;1"].getService(Ci.nsIStringBundleService);
- this.stringbundle = stringBundleService.createBundle(src, appLocale);
-}
-JSONFormatter.prototype = {
- htmlEncode: function (t) {
- return t != null ? t.toString().replace(/&/g,"&amp;").replace(/"/g,"&quot;").replace(/</g,"&lt;").replace(/>/g,"&gt;") : '';
- },
-
- decorateWithSpan: function (value, className) {
- return '<span class="' + className + '">' + this.htmlEncode(value) + '</span>';
- },
-
- // Convert a basic JSON datatype (number, string, boolean, null, object, array) into an HTML fragment.
- valueToHTML: function(value) {
- var valueType = typeof value;
-
- var output = "";
- if (value == null) {
- output += this.decorateWithSpan('null', 'null');
- }
- else if (value && value.constructor == Array) {
- output += this.arrayToHTML(value);
- }
- else if (valueType == 'object') {
- output += this.objectToHTML(value);
- }
- else if (valueType == 'number') {
- output += this.decorateWithSpan(value, 'num');
- }
- else if (valueType == 'string') {
- if (/^(http|https):\/\/[^\s]+$/i.test(value)) {
- output += '<a href="' + value + '">' + this.htmlEncode(value) + '</a>';
- } else {
- output += this.decorateWithSpan('"' + value + '"', 'string');
- }
- }
- else if (valueType == 'boolean') {
- output += this.decorateWithSpan(value, 'bool');
- }
-
- return output;
- },
-
- // Convert an array into an HTML fragment
- arrayToHTML: function(json) {
- var output = '[<ul class="array collapsible">';
- var hasContents = false;
- for ( var prop in json ) {
- hasContents = true;
- output += '<li>';
- output += this.valueToHTML(json[prop]);
- output += '</li>';
- }
- output += '</ul>]';
-
- if ( ! hasContents ) {
- output = "[ ]";
- }
-
- return output;
- },
-
- // Convert a JSON object to an HTML fragment
- objectToHTML: function(json) {
- var output = '{<ul class="obj collapsible">';
- var hasContents = false;
- for ( var prop in json ) {
- hasContents = true;
- output += '<li>';
- output += '<span class="prop">' + this.htmlEncode(prop) + '</span>: '
- output += this.valueToHTML(json[prop]);
- output += '</li>';
- }
- output += '</ul>}';
-
- if ( ! hasContents ) {
- output = "{ }";
- }
-
- return output;
- },
-
- // Convert a whole JSON object into a formatted HTML document.
- jsonToHTML: function(json, callback, uri) {
- var output = '';
- if( callback ){
- output += '<div class="callback">' + callback + ' (</div>';
- output += '<div id="json">';
- }else{
- output += '<div id="json">';
- }
- output += this.valueToHTML(json);
- output += '</div>';
- if( callback ){
- output += '<div class="callback">)</div>';
- }
- return this.toHTML(output, uri);
- },
-
- // Produce an error document for when parsing fails.
- errorPage: function(error, data, uri) {
- var output = '<div id="error">' + this.stringbundle.GetStringFromName('errorParsing') + '</div>';
- output += '<h1>' + this.stringbundle.GetStringFromName('docContents') + ':</h1>';
- output += '<div id="json">' + this.htmlEncode(data) + '</div>';
- return this.toHTML(output, uri + ' - Error');
- },
-
- // Wrap the HTML fragment in a full document. Used by jsonToHTML and errorPage.
- toHTML: function(content, title) {
- return '<doctype html>' +
- '<html><head><title>' + title + '</title>' +
- '<link rel="stylesheet" type="text/css" href="chrome://jsonview/content/default.css">' +
- '<script type="text/javascript" src="chrome://jsonview/content/default.js"></script>' +
- '</head><body>' +
- content +
- '</body></html>';
- }
-};
-
-// This component is an implementation of nsIStreamConverter that converts application/json to html
-const JSONVIEW_CONVERSION =
- "?from=application/json&to=*/*";
-const JSONVIEW_CONTRACT_ID =
- "@mozilla.org/streamconv;1" + JSONVIEW_CONVERSION;
-const JSONVIEW_COMPONENT_ID =
- Components.ID("{64890660-53c4-11dd-ae16-0800200c9a66}");
-
-// JSONView class constructor. Not much to see here.
-function JSONView() {
- // Native JSON implementation, new to FF3
- this.nativeJSON = Cc["@mozilla.org/dom/json;1"].createInstance(Ci.nsIJSON);
- this.jsonFormatter = new JSONFormatter();
-};
-
-// This defines an object that implements our converter, and is set up to be XPCOM-ified by XPCOMUtils
-JSONView.prototype = {
-
- // properties required for XPCOM registration:
- classDescription: "JSONView XPCOM Component",
- classID: JSONVIEW_COMPONENT_ID,
- contractID: JSONVIEW_CONTRACT_ID,
-
- _xpcom_categories: [{
- category: "@mozilla.org/streamconv;1",
- entry: JSONVIEW_CONVERSION,
- value: "JSON to HTML stream converter"
- }],
-
- QueryInterface: XPCOMUtils.generateQI([
- Ci.nsISupports,
- Ci.nsIStreamConverter,
- Ci.nsIStreamListener,
- Ci.nsIRequestObserver
- ]),
-
- /*
- * This component works as such:
- * 1. asyncConvertData captures the listener
- * 2. onStartRequest fires, initializes stuff, modifies the listener to match our output type
- * 3. onDataAvailable transcodes the data into a UTF-8 string
- * 4. onStopRequest gets the collected data and converts it, spits it to the listener
- * 5. convert does nothing, it's just the synchronous version of asyncConvertData
- */
-
- // nsIStreamConverter::convert
- convert: function (aFromStream, aFromType, aToType, aCtxt) {
- return aFromStream;
- },
-
- // nsIStreamConverter::asyncConvertData
- asyncConvertData: function (aFromType, aToType, aListener, aCtxt) {
- // Store the listener passed to us
- this.listener = aListener;
- },
-
- // nsIStreamListener::onDataAvailable
- onDataAvailable: function (aRequest, aContext, aInputStream, aOffset, aCount) {
- // From https://developer.mozilla.org/en/Reading_textual_data
- var is = Cc["@mozilla.org/intl/converter-input-stream;1"].createInstance(Ci.nsIConverterInputStream);
- is.init(aInputStream, this.charset, -1, Ci.nsIConverterInputStream.DEFAULT_REPLACEMENT_CHARACTER);
-
- var str = {};
-
- // This used to read in a loop until readString returned 0, but it caused it to crash Firefox on OSX/Win32 (but not Win64)
- // It seems just reading once with -1 (default buffer size) gets the file done.
- // However, *not* reading in a loop seems to cause problems with Firebug
- // So I read in a loop, but do whatever I can to avoid infinite-looping.
- var totalBytesRead = 0;
- var bytesRead = 1; // Seed it with something positive
-
- while (totalBytesRead < aCount && bytesRead > 0) {
- bytesRead = is.readString(-1, str);
- totalBytesRead += bytesRead;
- this.data += str.value;
- }
-
- },
-
- // nsIRequestObserver::onStartRequest
- onStartRequest: function (aRequest, aContext) {
- this.data = '';
- this.uri = aRequest.QueryInterface(Ci.nsIChannel).URI.spec;
-
- // Sets the charset if it is available. (For documents loaded from the
- // filesystem, this is not set.)
- this.charset = aRequest.QueryInterface(Ci.nsIChannel).contentCharset || 'UTF-8';
-
- this.channel = aRequest;
- this.channel.contentType = "text/html";
- // All our data will be coerced to UTF-8
- this.channel.contentCharset = "UTF-8";
-
- this.listener.onStartRequest (this.channel, aContext);
- },
-
- // nsIRequestObserver::onStopRequest
- onStopRequest: function (aRequest, aContext, aStatusCode) {
- /*
- * This should go something like this:
- * 1. Make sure we have a unicode string.
- * 2. Convert it to a Javascript object.
- * 2.1 Removes the callback
- * 3. Convert that to HTML? Or XUL?
- * 4. Spit it back out at the listener
- */
-
- var converter = Components
- .classes["@mozilla.org/intl/scriptableunicodeconverter"]
- .createInstance(Ci.nsIScriptableUnicodeConverter);
- converter.charset = this.charset;
- var outputDoc = "";
-
- var cleanData = '',
- callback = '';
-
- // This regex attempts to match a JSONP structure:
- // * Any amount of whitespace (including unicode nonbreaking spaces) between the start of the file and the callback name
- // * Callback name (any valid JavaScript function name according to ECMA-262 Edition 3 spec)
- // * Any amount of whitespace (including unicode nonbreaking spaces)
- // * Open parentheses
- // * Any amount of whitespace (including unicode nonbreaking spaces)
- // * Either { or [, the only two valid characters to start a JSON string.
- // * Any character, any number of times
- // * Either } or ], the only two valid closing characters of a JSON string.
- // * Any amount of whitespace (including unicode nonbreaking spaces)
- // * A closing parenthesis, an optional semicolon, and any amount of whitespace (including unicode nonbreaking spaces) until the end of the file.
- // This will miss anything that has comments, or more than one callback, or requires modification before use.
- var callback_results = /^[\s\u200B\uFEFF]*([\w$\[\]\.]+)[\s\u200B\uFEFF]*\([\s\u200B\uFEFF]*([\[{][\s\S]*[\]}])[\s\u200B\uFEFF]*\);?[\s\u200B\uFEFF]*$/.exec(this.data);
- if( callback_results && callback_results.length == 3 ){
- callback = callback_results[1];
- cleanData = callback_results[2];
- }else{
- cleanData = this.data;
- }
-
- try {
- var jsonObj = this.nativeJSON.decode(cleanData);
- outputDoc = this.jsonFormatter.jsonToHTML(jsonObj, callback, this.uri);
- }
- catch(e) {
- outputDoc = this.jsonFormatter.errorPage(e, this.data, this.uri);
- }
-
- // I don't really understand this part, but basically it's a way to get our UTF-8 stuff
- // spit back out as a byte stream
- // See http://www.mail-archive.com/mozilla-xpcom@mozilla.org/msg04194.html
- var storage = Cc["@mozilla.org/storagestream;1"]
- .createInstance(Ci.nsIStorageStream);
-
- // I have no idea what to pick for the first parameter (segments)
- storage.init(4, 0xffffffff, null);
- var out = storage.getOutputStream(0);
-
- var binout = Cc["@mozilla.org/binaryoutputstream;1"]
- .createInstance(Ci.nsIBinaryOutputStream);
- binout.setOutputStream(out);
- binout.writeUtf8Z(outputDoc);
- binout.close();
-
- // I can't explain it, but we need to trim 4 bytes off the front or else it includes random crap
- var trunc = 4;
- var instream = storage.newInputStream(trunc);
-
- // Pass the data to the main content listener
- this.listener.onDataAvailable(this.channel, aContext, instream, 0, storage.length - trunc);
-
- this.listener.onStopRequest(this.channel, aContext, aStatusCode);
- }
-};
-
-// We only have one component to register
-var components = [JSONView];
-
-// The actual hook into XPCOM
-if (XPCOMUtils.generateNSGetFactory) {
- // Gecko 2 (FF4) uses a different component registration strategy and registers categories in chrome.manifest
- var NSGetFactory = XPCOMUtils.generateNSGetFactory(components);
-} else {
- // Older Firefox requires manually setting up category entries
- var NSGetModule = function NSGetModule(compMgr, fileSpec){
- function postRegister(){
- var catMgr = XPCOMUtils.categoryManager;
- catMgr.addCategoryEntry('ext-to-type-mapping', 'json', 'application/json', true, true);
- }
-
-
- function preUnregister(){
- var catMgr = XPCOMUtils.categoryManager;
- catMgr.addCategoryEntry('ext-to-type-mapping', 'json', true);
- }
-
- return XPCOMUtils.generateModule(components, postRegister, preUnregister);
- }
+/**
+ * @author Benjamin Hollis
+ *
+ * This component provides a stream converter that can translate from JSON to HTML.
+ * It is compatible with Firefox 3 and up, since it uses many components that are new
+ * to Firefox 3.
+ */
+
+// Save some tedious typing
+const Ci = Components.interfaces;
+const Cc = Components.classes;
+
+// Import XPCOMUtils to help set up our JSONView XPCOM component (new to FF3)
+Components.utils.import("resource://gre/modules/XPCOMUtils.jsm");
+
+/*
+ * The JSONFormatter helper object. This contains two major functions, jsonToHTML and errorPage,
+ * each of which returns an HTML document.
+ */
+function JSONFormatter() {
+ var src = 'chrome://jsonview/locale/jsonview.properties';
+ var localeService = Cc["@mozilla.org/intl/nslocaleservice;1"].getService(Ci.nsILocaleService);
+
+ var appLocale = localeService.getApplicationLocale();
+ var stringBundleService = Cc["@mozilla.org/intl/stringbundle;1"].getService(Ci.nsIStringBundleService);
+ this.stringbundle = stringBundleService.createBundle(src, appLocale);
+}
+JSONFormatter.prototype = {
+ htmlEncode: function (t) {
+ return t != null ? t.toString().replace(/&/g,"&amp;").replace(/"/g,"&quot;").replace(/</g,"&lt;").replace(/>/g,"&gt;") : '';
+ },
+
+ decorateWithSpan: function (value, className) {
+ return '<span class="' + className + '">' + this.htmlEncode(value) + '</span>';
+ },
+
+ // Convert a basic JSON datatype (number, string, boolean, null, object, array) into an HTML fragment.
+ valueToHTML: function(value) {
+ var valueType = typeof value;
+
+ var output = "";
+ if (value == null) {
+ output += this.decorateWithSpan('null', 'null');
+ }
+ else if (value && value.constructor == Array) {
+ output += this.arrayToHTML(value);
+ }
+ else if (valueType == 'object') {
+ output += this.objectToHTML(value);
+ }
+ else if (valueType == 'number') {
+ output += this.decorateWithSpan(value, 'num');
+ }
+ else if (valueType == 'string') {
+ if (/^(http|https):\/\/[^\s]+$/i.test(value)) {
+ output += '<a href="' + value + '">' + this.htmlEncode(value) + '</a>';
+ } else {
+ output += this.decorateWithSpan('"' + value + '"', 'string');
+ }
+ }
+ else if (valueType == 'boolean') {
+ output += this.decorateWithSpan(value, 'bool');
+ }
+
+ return output;
+ },
+
+ // Convert an array into an HTML fragment
+ arrayToHTML: function(json) {
+ var output = '[<ul class="array collapsible">';
+ var hasContents = false;
+ for ( var prop in json ) {
+ hasContents = true;
+ output += '<li>';
+ output += this.valueToHTML(json[prop]);
+ output += '</li>';
+ }
+ output += '</ul>]';
+
+ if ( ! hasContents ) {
+ output = "[ ]";
+ }
+
+ return output;
+ },
+
+ // Convert a JSON object to an HTML fragment
+ objectToHTML: function(json) {
+ var output = '{<ul class="obj collapsible">';
+ var hasContents = false;
+ for ( var prop in json ) {
+ hasContents = true;
+ output += '<li>';
+ output += '<span class="prop">' + this.htmlEncode(prop) + '</span>: '
+ output += this.valueToHTML(json[prop]);
+ output += '</li>';
+ }
+ output += '</ul>}';
+
+ if ( ! hasContents ) {
+ output = "{ }";
+ }
+
+ return output;
+ },
+
+ // Convert a whole JSON object into a formatted HTML document.
+ jsonToHTML: function(json, callback, uri) {
+ var output = '';
+ if( callback ){
+ output += '<div class="callback">' + callback + ' (</div>';
+ output += '<div id="json">';
+ }else{
+ output += '<div id="json">';
+ }
+ output += this.valueToHTML(json);
+ output += '</div>';
+ if( callback ){
+ output += '<div class="callback">)</div>';
+ }
+ return this.toHTML(output, uri);
+ },
+
+ // Produce an error document for when parsing fails.
+ errorPage: function(error, data, uri) {
+ var output = '<div id="error">' + this.stringbundle.GetStringFromName('errorParsing') + '</div>';
+ output += '<h1>' + this.stringbundle.GetStringFromName('docContents') + ':</h1>';
+ output += '<div id="json">' + this.htmlEncode(data) + '</div>';
+ return this.toHTML(output, uri + ' - Error');
+ },
+
+ // Wrap the HTML fragment in a full document. Used by jsonToHTML and errorPage.
+ toHTML: function(content, title) {
+ return '<doctype html>' +
+ '<html><head><title>' + title + '</title>' +
+ '<link rel="stylesheet" type="text/css" href="chrome://jsonview/content/default.css">' +
+ '<script type="text/javascript" src="chrome://jsonview/content/default.js"></script>' +
+ '</head><body>' +
+ content +
+ '</body></html>';
+ }
+};
+
+// This component is an implementation of nsIStreamConverter that converts application/json to html
+const JSONVIEW_CONVERSION =
+ "?from=application/json&to=*/*";
+const JSONVIEW_CONTRACT_ID =
+ "@mozilla.org/streamconv;1" + JSONVIEW_CONVERSION;
+const JSONVIEW_COMPONENT_ID =
+ Components.ID("{64890660-53c4-11dd-ae16-0800200c9a66}");
+
+// JSONView class constructor. Not much to see here.
+function JSONView() {
+ // Native JSON implementation, new to FF3
+ this.nativeJSON = Cc["@mozilla.org/dom/json;1"].createInstance(Ci.nsIJSON);
+ this.jsonFormatter = new JSONFormatter();
+};
+
+// This defines an object that implements our converter, and is set up to be XPCOM-ified by XPCOMUtils
+JSONView.prototype = {
+
+ // properties required for XPCOM registration:
+ classDescription: "JSONView XPCOM Component",
+ classID: JSONVIEW_COMPONENT_ID,
+ contractID: JSONVIEW_CONTRACT_ID,
+
+ _xpcom_categories: [{
+ category: "@mozilla.org/streamconv;1",
+ entry: JSONVIEW_CONVERSION,
+ value: "JSON to HTML stream converter"
+ }],
+
+ QueryInterface: XPCOMUtils.generateQI([
+ Ci.nsISupports,
+ Ci.nsIStreamConverter,
+ Ci.nsIStreamListener,
+ Ci.nsIRequestObserver
+ ]),
+
+ /*
+ * This component works as such:
+ * 1. asyncConvertData captures the listener
+ * 2. onStartRequest fires, initializes stuff, modifies the listener to match our output type
+ * 3. onDataAvailable transcodes the data into a UTF-8 string
+ * 4. onStopRequest gets the collected data and converts it, spits it to the listener
+ * 5. convert does nothing, it's just the synchronous version of asyncConvertData
+ */
+
+ // nsIStreamConverter::convert
+ convert: function (aFromStream, aFromType, aToType, aCtxt) {
+ return aFromStream;
+ },
+
+ // nsIStreamConverter::asyncConvertData
+ asyncConvertData: function (aFromType, aToType, aListener, aCtxt) {
+ // Store the listener passed to us
+ this.listener = aListener;
+ },
+
+ // nsIStreamListener::onDataAvailable
+ onDataAvailable: function (aRequest, aContext, aInputStream, aOffset, aCount) {
+ // From https://developer.mozilla.org/en/Reading_textual_data
+ var is = Cc["@mozilla.org/intl/converter-input-stream;1"].createInstance(Ci.nsIConverterInputStream);
+ is.init(aInputStream, this.charset, -1, Ci.nsIConverterInputStream.DEFAULT_REPLACEMENT_CHARACTER);
+
+ var str = {};
+
+ // This used to read in a loop until readString returned 0, but it caused it to crash Firefox on OSX/Win32 (but not Win64)
+ // It seems just reading once with -1 (default buffer size) gets the file done.
+ // However, *not* reading in a loop seems to cause problems with Firebug
+ // So I read in a loop, but do whatever I can to avoid infinite-looping.
+ var totalBytesRead = 0;
+ var bytesRead = 1; // Seed it with something positive
+
+ while (totalBytesRead < aCount && bytesRead > 0) {
+ bytesRead = is.readString(-1, str);
+ totalBytesRead += bytesRead;
+ this.data += str.value;
+ }
+
+ },
+
+ // nsIRequestObserver::onStartRequest
+ onStartRequest: function (aRequest, aContext) {
+ this.data = '';
+ this.uri = aRequest.QueryInterface(Ci.nsIChannel).URI.spec;
+
+ // Sets the charset if it is available. (For documents loaded from the
+ // filesystem, this is not set.)
+ this.charset = aRequest.QueryInterface(Ci.nsIChannel).contentCharset || 'UTF-8';
+
+ this.channel = aRequest;
+ this.channel.contentType = "text/html";
+ // All our data will be coerced to UTF-8
+ this.channel.contentCharset = "UTF-8";
+
+ this.listener.onStartRequest (this.channel, aContext);
+ },
+
+ // nsIRequestObserver::onStopRequest
+ onStopRequest: function (aRequest, aContext, aStatusCode) {
+ /*
+ * This should go something like this:
+ * 1. Make sure we have a unicode string.
+ * 2. Convert it to a Javascript object.
+ * 2.1 Removes the callback
+ * 3. Convert that to HTML? Or XUL?
+ * 4. Spit it back out at the listener
+ */
+
+ var converter = Components
+ .classes["@mozilla.org/intl/scriptableunicodeconverter"]
+ .createInstance(Ci.nsIScriptableUnicodeConverter);
+ converter.charset = this.charset;
+ var outputDoc = "";
+
+ var cleanData = '',
+ callback = '';
+
+ // This regex attempts to match a JSONP structure:
+ // * Any amount of whitespace (including unicode nonbreaking spaces) between the start of the file and the callback name
+ // * Callback name (any valid JavaScript function name according to ECMA-262 Edition 3 spec)
+ // * Any amount of whitespace (including unicode nonbreaking spaces)
+ // * Open parentheses
+ // * Any amount of whitespace (including unicode nonbreaking spaces)
+ // * Either { or [, the only two valid characters to start a JSON string.
+ // * Any character, any number of times
+ // * Either } or ], the only two valid closing characters of a JSON string.
+ // * Any amount of whitespace (including unicode nonbreaking spaces)
+ // * A closing parenthesis, an optional semicolon, and any amount of whitespace (including unicode nonbreaking spaces) until the end of the file.
+ // This will miss anything that has comments, or more than one callback, or requires modification before use.
+ var callback_results = /^[\s\u200B\uFEFF]*([\w$\[\]\.]+)[\s\u200B\uFEFF]*\([\s\u200B\uFEFF]*([\[{][\s\S]*[\]}])[\s\u200B\uFEFF]*\);?[\s\u200B\uFEFF]*$/.exec(this.data);
+ if( callback_results && callback_results.length == 3 ){
+ callback = callback_results[1];
+ cleanData = callback_results[2];
+ }else{
+ cleanData = this.data;
+ }
+
+ try {
+ var jsonObj = this.nativeJSON.decode(cleanData);
+ outputDoc = this.jsonFormatter.jsonToHTML(jsonObj, callback, this.uri);
+ }
+ catch(e) {
+ outputDoc = this.jsonFormatter.errorPage(e, this.data, this.uri);
+ }
+
+ // I don't really understand this part, but basically it's a way to get our UTF-8 stuff
+ // spit back out as a byte stream
+ // See http://www.mail-archive.com/mozilla-xpcom@mozilla.org/msg04194.html
+ var storage = Cc["@mozilla.org/storagestream;1"]
+ .createInstance(Ci.nsIStorageStream);
+
+ // I have no idea what to pick for the first parameter (segments)
+ storage.init(4, 0xffffffff, null);
+ var out = storage.getOutputStream(0);
+
+ var binout = Cc["@mozilla.org/binaryoutputstream;1"]
+ .createInstance(Ci.nsIBinaryOutputStream);
+ binout.setOutputStream(out);
+ binout.writeUtf8Z(outputDoc);
+ binout.close();
+
+ // I can't explain it, but we need to trim 4 bytes off the front or else it includes random crap
+ var trunc = 4;
+ var instream = storage.newInputStream(trunc);
+
+ // Pass the data to the main content listener
+ this.listener.onDataAvailable(this.channel, aContext, instream, 0, storage.length - trunc);
+
+ this.listener.onStopRequest(this.channel, aContext, aStatusCode);
+ }
+};
+
+// We only have one component to register
+var components = [JSONView];
+
+// The actual hook into XPCOM
+if (XPCOMUtils.generateNSGetFactory) {
+ // Gecko 2 (FF4) uses a different component registration strategy and registers categories in chrome.manifest
+ var NSGetFactory = XPCOMUtils.generateNSGetFactory(components);
+} else {
+ // Older Firefox requires manually setting up category entries
+ var NSGetModule = function NSGetModule(compMgr, fileSpec){
+ function postRegister(){
+ var catMgr = XPCOMUtils.categoryManager;
+ catMgr.addCategoryEntry('ext-to-type-mapping', 'json', 'application/json', true, true);
+ }
+
+
+ function preUnregister(){
+ var catMgr = XPCOMUtils.categoryManager;
+ catMgr.addCategoryEntry('ext-to-type-mapping', 'json', true);
+ }
+
+ return XPCOMUtils.generateModule(components, postRegister, preUnregister);
+ }
}
View
158 src/content/default.css
@@ -1,80 +1,80 @@
-body {
- font-family: sans-serif;
-}
-
-.prop {
- font-weight: bold;
-}
-
-.null {
- color: red;
-}
-
-.bool {
- color: blue;
-}
-
-.num {
- color: blue;
-}
-
-.string {
- color: green;
- white-space: pre-wrap;
-}
-
-.collapser {
- position: absolute;
- left: -1em;
- cursor: pointer;
-}
-
-li {
- position: relative;
-}
-
-li:after {
- content: ',';
-}
-
-li:last-child:after {
- content: '';
-}
-
-#error {
- -moz-border-radius: 8px;
- border: 1px solid #970000;
- background-color: #F7E8E8;
- margin: .5em;
- padding: .5em;
-}
-
-.errormessage {
- font-family: monospace;
-}
-
-#json {
- font-family: monospace;
- font-size: 1.1em;
- white-space: pre-wrap;
-}
-
-ul {
- list-style: none;
- margin: 0 0 0 2em;
- padding: 0;
-}
-
-h1 {
- font-size: 1.2em;
-}
-
-/* Indent JSON when there's a callback. */
-.callback + #json {
- padding-left: 1em;
-}
-
-.callback {
- font-family: monospace;
- color: #A52A2A;
+body {
+ font-family: sans-serif;
+}
+
+.prop {
+ font-weight: bold;
+}
+
+.null {
+ color: red;
+}
+
+.bool {
+ color: blue;
+}
+
+.num {
+ color: blue;
+}
+
+.string {
+ color: green;
+ white-space: pre-wrap;
+}
+
+.collapser {
+ position: absolute;
+ left: -1em;
+ cursor: pointer;
+}
+
+li {
+ position: relative;
+}
+
+li:after {
+ content: ',';
+}
+
+li:last-child:after {
+ content: '';
+}
+
+#error {
+ -moz-border-radius: 8px;
+ border: 1px solid #970000;
+ background-color: #F7E8E8;
+ margin: .5em;
+ padding: .5em;
+}
+
+.errormessage {
+ font-family: monospace;
+}
+
+#json {
+ font-family: monospace;
+ font-size: 1.1em;
+ white-space: pre-wrap;
+}
+
+ul {
+ list-style: none;
+ margin: 0 0 0 2em;
+ padding: 0;
+}
+
+h1 {
+ font-size: 1.2em;
+}
+
+/* Indent JSON when there's a callback. */
+.callback + #json {
+ padding-left: 1em;
+}
+
+.callback {
+ font-family: monospace;
+ color: #A52A2A;
}
View
96 src/content/default.js
@@ -1,48 +1,48 @@
-// This used to use jQuery, but was rewritten in plan DOM for speed and to get rid of the jQuery dependency.
-document.addEventListener('DOMContentLoaded', function() {
- // Click handler for collapsing and expanding objects and arrays
- function collapse(evt) {
- var collapser = evt.target;
-
- var target = collapser.parentNode.getElementsByClassName('collapsible');
-
- if ( ! target.length ) {
- return;
- }
-
- target = target[0];
-
- if ( target.style.display == 'none' ) {
- var ellipsis = target.parentNode.getElementsByClassName('ellipsis')[0];
- target.parentNode.removeChild(ellipsis);
- target.style.display = '';
- } else {
- target.style.display = 'none';
-
- var ellipsis = document.createElement('span');
- ellipsis.className = 'ellipsis';
- ellipsis.innerHTML = ' &hellip; ';
- target.parentNode.insertBefore(ellipsis, target);
- }
-
- collapser.innerHTML = ( collapser.innerHTML == '-' ) ? '+' : '-';
- }
-
- function addCollapser(item) {
- // This mainly filters out the root object (which shouldn't be collapsible)
- if ( item.nodeName != 'LI' ) {
- return;
- }
-
- var collapser = document.createElement('div');
- collapser.className = 'collapser';
- collapser.innerHTML = '-';
- collapser.addEventListener('click', collapse, false);
- item.insertBefore(collapser, item.firstChild);
- }
-
- var items = document.getElementsByClassName('collapsible');
- for( var i = 0; i < items.length; i++) {
- addCollapser(items[i].parentNode);
- }
-}, false);
+// This used to use jQuery, but was rewritten in plan DOM for speed and to get rid of the jQuery dependency.
+document.addEventListener('DOMContentLoaded', function() {
+ // Click handler for collapsing and expanding objects and arrays
+ function collapse(evt) {
+ var collapser = evt.target;
+
+ var target = collapser.parentNode.getElementsByClassName('collapsible');
+
+ if ( ! target.length ) {
+ return;
+ }
+
+ target = target[0];
+
+ if ( target.style.display == 'none' ) {
+ var ellipsis = target.parentNode.getElementsByClassName('ellipsis')[0];
+ target.parentNode.removeChild(ellipsis);
+ target.style.display = '';
+ } else {
+ target.style.display = 'none';
+
+ var ellipsis = document.createElement('span');
+ ellipsis.className = 'ellipsis';
+ ellipsis.innerHTML = ' &hellip; ';
+ target.parentNode.insertBefore(ellipsis, target);
+ }
+
+ collapser.innerHTML = ( collapser.innerHTML == '-' ) ? '+' : '-';
+ }
+
+ function addCollapser(item) {
+ // This mainly filters out the root object (which shouldn't be collapsible)
+ if ( item.nodeName != 'LI' ) {
+ return;
+ }
+
+ var collapser = document.createElement('div');
+ collapser.className = 'collapser';
+ collapser.innerHTML = '-';
+ collapser.addEventListener('click', collapse, false);
+ item.insertBefore(collapser, item.firstChild);
+ }
+
+ var items = document.getElementsByClassName('collapsible');
+ for( var i = 0; i < items.length; i++) {
+ addCollapser(items[i].parentNode);
+ }
+}, false);
View
10 src/content/prefs/prefs.xul
@@ -7,14 +7,14 @@
title="&jsonViewOptions;"
onload="setupUIFromPrefs();">
- <script type="application/x-javascript" src="chrome://jsonview/content/prefs/httpAccept.js"/>
+ <script type="application/x-javascript" src="chrome://jsonview/content/prefs/httpAccept.js"/>
- <prefpane id="jsonviewoptions">
+ <prefpane id="jsonviewoptions">
<preferences>
<preference id="httpAcceptDefault" name="network.http.accept.default" type="string" />
</preferences>
-
+
<checkbox id="httpAcceptCheckbox" accesskey="&httpAcceptAccesskey;" label="&httpAcceptLabel;" oncommand="saveHttpAcceptPref();" />
- </prefpane>
-</prefwindow>
+ </prefpane>
+</prefwindow>
View
2 src/locale/de-DE/amo-descriptions.properties
@@ -1,2 +1,2 @@
-detailed_description=Normalerweise bietet Firefox beim Aufrufen eines JSON-Dokuments (Content-Type "application/json") das Abspeichern der Datei an. Die JSONView-Erweiterung stellt JSON-Dokumente analog wie XML-Dokumente dar. Das Dokument verfügt über Formatierungen sowie Hervorhebungen und Felder und Objekte können erweitert und zusammengefasst werden. Enthält das JSON-Dokument Fehler, so zeigt JSONView nur den Quelltext an.\nUm JSONView in Aktion zu sehen, rufen Sie nach der Installation http://brh.numbera.com/software/jsonview/example.json auf.
+detailed_description=Normalerweise bietet Firefox beim Aufrufen eines JSON-Dokuments (Content-Type "application/json") das Abspeichern der Datei an. Die JSONView-Erweiterung stellt JSON-Dokumente analog wie XML-Dokumente dar. Das Dokument verfügt über Formatierungen sowie Hervorhebungen und Felder und Objekte können erweitert und zusammengefasst werden. Enthält das JSON-Dokument Fehler, so zeigt JSONView nur den Quelltext an.\nUm JSONView in Aktion zu sehen, rufen Sie nach der Installation http://brh.numbera.com/software/jsonview/example.json auf.
developer_comments=Bekannte Probleme:\n* "Speichern unter..." speichert das Dokument als HTML anstatt des ursprünglichen JSON.\n* "Seiteninformationen" gibt Inhalt als HTML anstatt JSON an.\n* Kopieren und Einfügen des formatierten JSON kopiert nicht das Originaldokument.
View
2 src/locale/de-DE/jsonview.properties
@@ -1,2 +1,2 @@
-errorParsing=Beim Auswerten des JSON-Dokuments trat ein Fehler auf. Eventuell ist das Dokument nicht nach den Spezifikationen aufgebaut.
+errorParsing=Beim Auswerten des JSON-Dokuments trat ein Fehler auf. Eventuell ist das Dokument nicht nach den Spezifikationen aufgebaut.
docContents=Dokumentinhalt
View
2 src/locale/en-US/amo-descriptions.properties
@@ -1,2 +1,2 @@
-detailed_description=Normally when encountering a JSON document (content type "application/json"), Firefox simply prompts you to download the file. With the JSONView extension, JSON documents are shown in the browser similar to how XML documents are shown. The document is formatted, highlighted, and arrays and objects can be collapsed. Even if the JSON document contains errors, JSONView will still show the raw text.\nOnce you've got JSONView installed, check out http://brh.numbera.com/software/jsonview/example.json to see the extension in action!
+detailed_description=Normally when encountering a JSON document (content type "application/json"), Firefox simply prompts you to download the file. With the JSONView extension, JSON documents are shown in the browser similar to how XML documents are shown. The document is formatted, highlighted, and arrays and objects can be collapsed. Even if the JSON document contains errors, JSONView will still show the raw text.\nOnce you've got JSONView installed, check out http://brh.numbera.com/software/jsonview/example.json to see the extension in action!
developer_comments=Known bugs:\n* "Save as..." saves HTML instead of the original JSON document.\n* "Page Info" panel reports content as HTML instead of JSON.\n* Copying and pasting from the formatted JSON does not copy the original document.
View
6 src/locale/en-US/jsonview.properties
@@ -1,4 +1,4 @@
-# The "there was an error parsing your JSON" message
-errorParsing=There was an error parsing the JSON document. The document may not be well-formed.
-# The text that gets displayed on the error page before the dump of the response
+# The "there was an error parsing your JSON" message
+errorParsing=There was an error parsing the JSON document. The document may not be well-formed.
+# The text that gets displayed on the error page before the dump of the response
docContents=Document contents
View
6 src/locale/en-US/prefs.dtd
@@ -1,3 +1,3 @@
-<!ENTITY jsonViewOptions "JSONView Options">
-<!ENTITY httpAcceptLabel "Include &quot;application/json&quot; in the HTTP Accept header for requests">
-<!ENTITY httpAcceptAccesskey "I">
+<!ENTITY jsonViewOptions "JSONView Options">
+<!ENTITY httpAcceptLabel "Include &quot;application/json&quot; in the HTTP Accept header for requests">
+<!ENTITY httpAcceptAccesskey "I">
View
4 test/minimal-whitespace.json
@@ -1,2 +1,2 @@
-
- {}
+
+ {}
View
8 test/whitespace.json
@@ -1,5 +1,5 @@
-{
- "tabs": "check\tout\tmy\t\ttabs",
- "spaces": "lots of consecutive spaces",
- "newlines": "this\nhas\n\nnewlines\r\nincluding\r\nwin-style"
+{
+ "tabs": "check\tout\tmy\t\ttabs",
+ "spaces": "lots of consecutive spaces",
+ "newlines": "this\nhas\n\nnewlines\r\nincluding\r\nwin-style"
}

0 comments on commit 91dadbb

Please sign in to comment.