Browse Source

json format增加jsonlint功能

zxlie 7 years ago
parent
commit
aad6b48cc7

+ 2 - 2
apps/en-decode/endecode-lib.js

@@ -235,8 +235,8 @@ module.exports = (() => {
      * @param str
      */
     let md5 = (str) => {
-        let tools = Tarp.require('./md5');
-        return tools.hex_md5(str);
+        let md5 = Tarp.require('./md5');
+        return md5(str);
     };
 
     return {

+ 250 - 230
apps/en-decode/md5.js

@@ -1,260 +1,280 @@
 /*
+ * JavaScript MD5
+ * https://github.com/blueimp/JavaScript-MD5
+ *
+ * Copyright 2011, Sebastian Tschan
+ * https://blueimp.net
+ *
+ * Licensed under the MIT license:
+ * https://opensource.org/licenses/MIT
+ *
+ * Based on
  * A JavaScript implementation of the RSA Data Security, Inc. MD5 Message
  * Digest Algorithm, as defined in RFC 1321.
- * Version 2.1 Copyright (C) Paul Johnston 1999 - 2002.
+ * Version 2.2 Copyright (C) Paul Johnston 1999 - 2009
  * Other contributors: Greg Holt, Andrew Kepert, Ydnar, Lostinet
  * Distributed under the BSD License
  * See http://pajhome.org.uk/crypt/md5 for more info.
  */
 
-/*
- * Configurable variables. You may need to tweak these to be compatible with
- * the server-side, but the defaults work in most cases.
- */
-var hexcase = 0;  /* hex output format. 0 - lowercase; 1 - uppercase        */
-var b64pad  = ""; /* base-64 pad character. "=" for strict RFC compliance   */
-var chrsz   = 8;  /* bits per input character. 8 - ASCII; 16 - Unicode      */
+/* global define */
 
-/*
- * These are the functions you'll usually want to call
- * They take string arguments and return either hex or base-64 encoded strings
- */
-function hex_md5(s){ return binl2hex(core_md5(str2binl(s), s.length * chrsz));}
-function b64_md5(s){ return binl2b64(core_md5(str2binl(s), s.length * chrsz));}
-function str_md5(s){ return binl2str(core_md5(str2binl(s), s.length * chrsz));}
-function hex_hmac_md5(key, data) { return binl2hex(core_hmac_md5(key, data)); }
-function b64_hmac_md5(key, data) { return binl2b64(core_hmac_md5(key, data)); }
-function str_hmac_md5(key, data) { return binl2str(core_hmac_md5(key, data)); }
+;(function ($) {
+    'use strict'
 
-/*
- * Perform a simple self-test to see if the VM is working
- */
-function md5_vm_test()
-{
-  return hex_md5("abc") == "900150983cd24fb0d6963f7d28e17f72";
-}
-
-/*
- * Calculate the MD5 of an array of little-endian words, and a bit length
- */
-function core_md5(x, len)
-{
-  /* append padding */
-  x[len >> 5] |= 0x80 << ((len) % 32);
-  x[(((len + 64) >>> 9) << 4) + 14] = len;
+    /*
+    * Add integers, wrapping at 2^32. This uses 16-bit operations internally
+    * to work around bugs in some JS interpreters.
+    */
+    function safeAdd (x, y) {
+        var lsw = (x & 0xffff) + (y & 0xffff)
+        var msw = (x >> 16) + (y >> 16) + (lsw >> 16)
+        return (msw << 16) | (lsw & 0xffff)
+    }
 
-  var a =  1732584193;
-  var b = -271733879;
-  var c = -1732584194;
-  var d =  271733878;
+    /*
+    * Bitwise rotate a 32-bit number to the left.
+    */
+    function bitRotateLeft (num, cnt) {
+        return (num << cnt) | (num >>> (32 - cnt))
+    }
 
-  for(var i = 0; i < x.length; i += 16)
-  {
-    var olda = a;
-    var oldb = b;
-    var oldc = c;
-    var oldd = d;
+    /*
+    * These functions implement the four basic operations the algorithm uses.
+    */
+    function md5cmn (q, a, b, x, s, t) {
+        return safeAdd(bitRotateLeft(safeAdd(safeAdd(a, q), safeAdd(x, t)), s), b)
+    }
+    function md5ff (a, b, c, d, x, s, t) {
+        return md5cmn((b & c) | (~b & d), a, b, x, s, t)
+    }
+    function md5gg (a, b, c, d, x, s, t) {
+        return md5cmn((b & d) | (c & ~d), a, b, x, s, t)
+    }
+    function md5hh (a, b, c, d, x, s, t) {
+        return md5cmn(b ^ c ^ d, a, b, x, s, t)
+    }
+    function md5ii (a, b, c, d, x, s, t) {
+        return md5cmn(c ^ (b | ~d), a, b, x, s, t)
+    }
 
-    a = md5_ff(a, b, c, d, x[i+ 0], 7 , -680876936);
-    d = md5_ff(d, a, b, c, x[i+ 1], 12, -389564586);
-    c = md5_ff(c, d, a, b, x[i+ 2], 17,  606105819);
-    b = md5_ff(b, c, d, a, x[i+ 3], 22, -1044525330);
-    a = md5_ff(a, b, c, d, x[i+ 4], 7 , -176418897);
-    d = md5_ff(d, a, b, c, x[i+ 5], 12,  1200080426);
-    c = md5_ff(c, d, a, b, x[i+ 6], 17, -1473231341);
-    b = md5_ff(b, c, d, a, x[i+ 7], 22, -45705983);
-    a = md5_ff(a, b, c, d, x[i+ 8], 7 ,  1770035416);
-    d = md5_ff(d, a, b, c, x[i+ 9], 12, -1958414417);
-    c = md5_ff(c, d, a, b, x[i+10], 17, -42063);
-    b = md5_ff(b, c, d, a, x[i+11], 22, -1990404162);
-    a = md5_ff(a, b, c, d, x[i+12], 7 ,  1804603682);
-    d = md5_ff(d, a, b, c, x[i+13], 12, -40341101);
-    c = md5_ff(c, d, a, b, x[i+14], 17, -1502002290);
-    b = md5_ff(b, c, d, a, x[i+15], 22,  1236535329);
+    /*
+    * Calculate the MD5 of an array of little-endian words, and a bit length.
+    */
+    function binlMD5 (x, len) {
+        /* append padding */
+        x[len >> 5] |= 0x80 << (len % 32)
+        x[((len + 64) >>> 9 << 4) + 14] = len
 
-    a = md5_gg(a, b, c, d, x[i+ 1], 5 , -165796510);
-    d = md5_gg(d, a, b, c, x[i+ 6], 9 , -1069501632);
-    c = md5_gg(c, d, a, b, x[i+11], 14,  643717713);
-    b = md5_gg(b, c, d, a, x[i+ 0], 20, -373897302);
-    a = md5_gg(a, b, c, d, x[i+ 5], 5 , -701558691);
-    d = md5_gg(d, a, b, c, x[i+10], 9 ,  38016083);
-    c = md5_gg(c, d, a, b, x[i+15], 14, -660478335);
-    b = md5_gg(b, c, d, a, x[i+ 4], 20, -405537848);
-    a = md5_gg(a, b, c, d, x[i+ 9], 5 ,  568446438);
-    d = md5_gg(d, a, b, c, x[i+14], 9 , -1019803690);
-    c = md5_gg(c, d, a, b, x[i+ 3], 14, -187363961);
-    b = md5_gg(b, c, d, a, x[i+ 8], 20,  1163531501);
-    a = md5_gg(a, b, c, d, x[i+13], 5 , -1444681467);
-    d = md5_gg(d, a, b, c, x[i+ 2], 9 , -51403784);
-    c = md5_gg(c, d, a, b, x[i+ 7], 14,  1735328473);
-    b = md5_gg(b, c, d, a, x[i+12], 20, -1926607734);
+        var i
+        var olda
+        var oldb
+        var oldc
+        var oldd
+        var a = 1732584193
+        var b = -271733879
+        var c = -1732584194
+        var d = 271733878
 
-    a = md5_hh(a, b, c, d, x[i+ 5], 4 , -378558);
-    d = md5_hh(d, a, b, c, x[i+ 8], 11, -2022574463);
-    c = md5_hh(c, d, a, b, x[i+11], 16,  1839030562);
-    b = md5_hh(b, c, d, a, x[i+14], 23, -35309556);
-    a = md5_hh(a, b, c, d, x[i+ 1], 4 , -1530992060);
-    d = md5_hh(d, a, b, c, x[i+ 4], 11,  1272893353);
-    c = md5_hh(c, d, a, b, x[i+ 7], 16, -155497632);
-    b = md5_hh(b, c, d, a, x[i+10], 23, -1094730640);
-    a = md5_hh(a, b, c, d, x[i+13], 4 ,  681279174);
-    d = md5_hh(d, a, b, c, x[i+ 0], 11, -358537222);
-    c = md5_hh(c, d, a, b, x[i+ 3], 16, -722521979);
-    b = md5_hh(b, c, d, a, x[i+ 6], 23,  76029189);
-    a = md5_hh(a, b, c, d, x[i+ 9], 4 , -640364487);
-    d = md5_hh(d, a, b, c, x[i+12], 11, -421815835);
-    c = md5_hh(c, d, a, b, x[i+15], 16,  530742520);
-    b = md5_hh(b, c, d, a, x[i+ 2], 23, -995338651);
+        for (i = 0; i < x.length; i += 16) {
+            olda = a
+            oldb = b
+            oldc = c
+            oldd = d
 
-    a = md5_ii(a, b, c, d, x[i+ 0], 6 , -198630844);
-    d = md5_ii(d, a, b, c, x[i+ 7], 10,  1126891415);
-    c = md5_ii(c, d, a, b, x[i+14], 15, -1416354905);
-    b = md5_ii(b, c, d, a, x[i+ 5], 21, -57434055);
-    a = md5_ii(a, b, c, d, x[i+12], 6 ,  1700485571);
-    d = md5_ii(d, a, b, c, x[i+ 3], 10, -1894986606);
-    c = md5_ii(c, d, a, b, x[i+10], 15, -1051523);
-    b = md5_ii(b, c, d, a, x[i+ 1], 21, -2054922799);
-    a = md5_ii(a, b, c, d, x[i+ 8], 6 ,  1873313359);
-    d = md5_ii(d, a, b, c, x[i+15], 10, -30611744);
-    c = md5_ii(c, d, a, b, x[i+ 6], 15, -1560198380);
-    b = md5_ii(b, c, d, a, x[i+13], 21,  1309151649);
-    a = md5_ii(a, b, c, d, x[i+ 4], 6 , -145523070);
-    d = md5_ii(d, a, b, c, x[i+11], 10, -1120210379);
-    c = md5_ii(c, d, a, b, x[i+ 2], 15,  718787259);
-    b = md5_ii(b, c, d, a, x[i+ 9], 21, -343485551);
+            a = md5ff(a, b, c, d, x[i], 7, -680876936)
+            d = md5ff(d, a, b, c, x[i + 1], 12, -389564586)
+            c = md5ff(c, d, a, b, x[i + 2], 17, 606105819)
+            b = md5ff(b, c, d, a, x[i + 3], 22, -1044525330)
+            a = md5ff(a, b, c, d, x[i + 4], 7, -176418897)
+            d = md5ff(d, a, b, c, x[i + 5], 12, 1200080426)
+            c = md5ff(c, d, a, b, x[i + 6], 17, -1473231341)
+            b = md5ff(b, c, d, a, x[i + 7], 22, -45705983)
+            a = md5ff(a, b, c, d, x[i + 8], 7, 1770035416)
+            d = md5ff(d, a, b, c, x[i + 9], 12, -1958414417)
+            c = md5ff(c, d, a, b, x[i + 10], 17, -42063)
+            b = md5ff(b, c, d, a, x[i + 11], 22, -1990404162)
+            a = md5ff(a, b, c, d, x[i + 12], 7, 1804603682)
+            d = md5ff(d, a, b, c, x[i + 13], 12, -40341101)
+            c = md5ff(c, d, a, b, x[i + 14], 17, -1502002290)
+            b = md5ff(b, c, d, a, x[i + 15], 22, 1236535329)
 
-    a = safe_add(a, olda);
-    b = safe_add(b, oldb);
-    c = safe_add(c, oldc);
-    d = safe_add(d, oldd);
-  }
-  return Array(a, b, c, d);
+            a = md5gg(a, b, c, d, x[i + 1], 5, -165796510)
+            d = md5gg(d, a, b, c, x[i + 6], 9, -1069501632)
+            c = md5gg(c, d, a, b, x[i + 11], 14, 643717713)
+            b = md5gg(b, c, d, a, x[i], 20, -373897302)
+            a = md5gg(a, b, c, d, x[i + 5], 5, -701558691)
+            d = md5gg(d, a, b, c, x[i + 10], 9, 38016083)
+            c = md5gg(c, d, a, b, x[i + 15], 14, -660478335)
+            b = md5gg(b, c, d, a, x[i + 4], 20, -405537848)
+            a = md5gg(a, b, c, d, x[i + 9], 5, 568446438)
+            d = md5gg(d, a, b, c, x[i + 14], 9, -1019803690)
+            c = md5gg(c, d, a, b, x[i + 3], 14, -187363961)
+            b = md5gg(b, c, d, a, x[i + 8], 20, 1163531501)
+            a = md5gg(a, b, c, d, x[i + 13], 5, -1444681467)
+            d = md5gg(d, a, b, c, x[i + 2], 9, -51403784)
+            c = md5gg(c, d, a, b, x[i + 7], 14, 1735328473)
+            b = md5gg(b, c, d, a, x[i + 12], 20, -1926607734)
 
-}
+            a = md5hh(a, b, c, d, x[i + 5], 4, -378558)
+            d = md5hh(d, a, b, c, x[i + 8], 11, -2022574463)
+            c = md5hh(c, d, a, b, x[i + 11], 16, 1839030562)
+            b = md5hh(b, c, d, a, x[i + 14], 23, -35309556)
+            a = md5hh(a, b, c, d, x[i + 1], 4, -1530992060)
+            d = md5hh(d, a, b, c, x[i + 4], 11, 1272893353)
+            c = md5hh(c, d, a, b, x[i + 7], 16, -155497632)
+            b = md5hh(b, c, d, a, x[i + 10], 23, -1094730640)
+            a = md5hh(a, b, c, d, x[i + 13], 4, 681279174)
+            d = md5hh(d, a, b, c, x[i], 11, -358537222)
+            c = md5hh(c, d, a, b, x[i + 3], 16, -722521979)
+            b = md5hh(b, c, d, a, x[i + 6], 23, 76029189)
+            a = md5hh(a, b, c, d, x[i + 9], 4, -640364487)
+            d = md5hh(d, a, b, c, x[i + 12], 11, -421815835)
+            c = md5hh(c, d, a, b, x[i + 15], 16, 530742520)
+            b = md5hh(b, c, d, a, x[i + 2], 23, -995338651)
 
-/*
- * These functions implement the four basic operations the algorithm uses.
- */
-function md5_cmn(q, a, b, x, s, t)
-{
-  return safe_add(bit_rol(safe_add(safe_add(a, q), safe_add(x, t)), s),b);
-}
-function md5_ff(a, b, c, d, x, s, t)
-{
-  return md5_cmn((b & c) | ((~b) & d), a, b, x, s, t);
-}
-function md5_gg(a, b, c, d, x, s, t)
-{
-  return md5_cmn((b & d) | (c & (~d)), a, b, x, s, t);
-}
-function md5_hh(a, b, c, d, x, s, t)
-{
-  return md5_cmn(b ^ c ^ d, a, b, x, s, t);
-}
-function md5_ii(a, b, c, d, x, s, t)
-{
-  return md5_cmn(c ^ (b | (~d)), a, b, x, s, t);
-}
+            a = md5ii(a, b, c, d, x[i], 6, -198630844)
+            d = md5ii(d, a, b, c, x[i + 7], 10, 1126891415)
+            c = md5ii(c, d, a, b, x[i + 14], 15, -1416354905)
+            b = md5ii(b, c, d, a, x[i + 5], 21, -57434055)
+            a = md5ii(a, b, c, d, x[i + 12], 6, 1700485571)
+            d = md5ii(d, a, b, c, x[i + 3], 10, -1894986606)
+            c = md5ii(c, d, a, b, x[i + 10], 15, -1051523)
+            b = md5ii(b, c, d, a, x[i + 1], 21, -2054922799)
+            a = md5ii(a, b, c, d, x[i + 8], 6, 1873313359)
+            d = md5ii(d, a, b, c, x[i + 15], 10, -30611744)
+            c = md5ii(c, d, a, b, x[i + 6], 15, -1560198380)
+            b = md5ii(b, c, d, a, x[i + 13], 21, 1309151649)
+            a = md5ii(a, b, c, d, x[i + 4], 6, -145523070)
+            d = md5ii(d, a, b, c, x[i + 11], 10, -1120210379)
+            c = md5ii(c, d, a, b, x[i + 2], 15, 718787259)
+            b = md5ii(b, c, d, a, x[i + 9], 21, -343485551)
 
-/*
- * Calculate the HMAC-MD5, of a key and some data
- */
-function core_hmac_md5(key, data)
-{
-  var bkey = str2binl(key);
-  if(bkey.length > 16) bkey = core_md5(bkey, key.length * chrsz);
+            a = safeAdd(a, olda)
+            b = safeAdd(b, oldb)
+            c = safeAdd(c, oldc)
+            d = safeAdd(d, oldd)
+        }
+        return [a, b, c, d]
+    }
 
-  var ipad = Array(16), opad = Array(16);
-  for(var i = 0; i < 16; i++)
-  {
-    ipad[i] = bkey[i] ^ 0x36363636;
-    opad[i] = bkey[i] ^ 0x5C5C5C5C;
-  }
+    /*
+    * Convert an array of little-endian words to a string
+    */
+    function binl2rstr (input) {
+        var i
+        var output = ''
+        var length32 = input.length * 32
+        for (i = 0; i < length32; i += 8) {
+            output += String.fromCharCode((input[i >> 5] >>> (i % 32)) & 0xff)
+        }
+        return output
+    }
 
-  var hash = core_md5(ipad.concat(str2binl(data)), 512 + data.length * chrsz);
-  return core_md5(opad.concat(hash), 512 + 128);
-}
+    /*
+    * Convert a raw string to an array of little-endian words
+    * Characters >255 have their high-byte silently ignored.
+    */
+    function rstr2binl (input) {
+        var i
+        var output = []
+        output[(input.length >> 2) - 1] = undefined
+        for (i = 0; i < output.length; i += 1) {
+            output[i] = 0
+        }
+        var length8 = input.length * 8
+        for (i = 0; i < length8; i += 8) {
+            output[i >> 5] |= (input.charCodeAt(i / 8) & 0xff) << (i % 32)
+        }
+        return output
+    }
 
-/*
- * Add integers, wrapping at 2^32. This uses 16-bit operations internally
- * to work around bugs in some JS interpreters.
- */
-function safe_add(x, y)
-{
-  var lsw = (x & 0xFFFF) + (y & 0xFFFF);
-  var msw = (x >> 16) + (y >> 16) + (lsw >> 16);
-  return (msw << 16) | (lsw & 0xFFFF);
-}
+    /*
+    * Calculate the MD5 of a raw string
+    */
+    function rstrMD5 (s) {
+        return binl2rstr(binlMD5(rstr2binl(s), s.length * 8))
+    }
 
-/*
- * Bitwise rotate a 32-bit number to the left.
- */
-function bit_rol(num, cnt)
-{
-  return (num << cnt) | (num >>> (32 - cnt));
-}
+    /*
+    * Calculate the HMAC-MD5, of a key and some data (raw strings)
+    */
+    function rstrHMACMD5 (key, data) {
+        var i
+        var bkey = rstr2binl(key)
+        var ipad = []
+        var opad = []
+        var hash
+        ipad[15] = opad[15] = undefined
+        if (bkey.length > 16) {
+            bkey = binlMD5(bkey, key.length * 8)
+        }
+        for (i = 0; i < 16; i += 1) {
+            ipad[i] = bkey[i] ^ 0x36363636
+            opad[i] = bkey[i] ^ 0x5c5c5c5c
+        }
+        hash = binlMD5(ipad.concat(rstr2binl(data)), 512 + data.length * 8)
+        return binl2rstr(binlMD5(opad.concat(hash), 512 + 128))
+    }
 
-/*
- * Convert a string to an array of little-endian words
- * If chrsz is ASCII, characters >255 have their hi-byte silently ignored.
- */
-function str2binl(str)
-{
-  var bin = Array();
-  var mask = (1 << chrsz) - 1;
-  for(var i = 0; i < str.length * chrsz; i += chrsz)
-    bin[i>>5] |= (str.charCodeAt(i / chrsz) & mask) << (i%32);
-  return bin;
-}
+    /*
+    * Convert a raw string to a hex string
+    */
+    function rstr2hex (input) {
+        var hexTab = '0123456789abcdef'
+        var output = ''
+        var x
+        var i
+        for (i = 0; i < input.length; i += 1) {
+            x = input.charCodeAt(i)
+            output += hexTab.charAt((x >>> 4) & 0x0f) + hexTab.charAt(x & 0x0f)
+        }
+        return output
+    }
 
-/*
- * Convert an array of little-endian words to a string
- */
-function binl2str(bin)
-{
-  var str = "";
-  var mask = (1 << chrsz) - 1;
-  for(var i = 0; i < bin.length * 32; i += chrsz)
-    str += String.fromCharCode((bin[i>>5] >>> (i % 32)) & mask);
-  return str;
-}
+    /*
+    * Encode a string as utf-8
+    */
+    function str2rstrUTF8 (input) {
+        return unescape(encodeURIComponent(input))
+    }
 
-/*
- * Convert an array of little-endian words to a hex string.
- */
-function binl2hex(binarray)
-{
-  var hex_tab = hexcase ? "0123456789ABCDEF" : "0123456789abcdef";
-  var str = "";
-  for(var i = 0; i < binarray.length * 4; i++)
-  {
-    str += hex_tab.charAt((binarray[i>>2] >> ((i%4)*8+4)) & 0xF) +
-           hex_tab.charAt((binarray[i>>2] >> ((i%4)*8  )) & 0xF);
-  }
-  return str;
-}
+    /*
+    * Take string arguments and return either raw or hex encoded strings
+    */
+    function rawMD5 (s) {
+        return rstrMD5(str2rstrUTF8(s))
+    }
+    function hexMD5 (s) {
+        return rstr2hex(rawMD5(s))
+    }
+    function rawHMACMD5 (k, d) {
+        return rstrHMACMD5(str2rstrUTF8(k), str2rstrUTF8(d))
+    }
+    function hexHMACMD5 (k, d) {
+        return rstr2hex(rawHMACMD5(k, d))
+    }
 
-/*
- * Convert an array of little-endian words to a base-64 string
- */
-function binl2b64(binarray)
-{
-  var tab = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
-  var str = "";
-  for(var i = 0; i < binarray.length * 4; i += 3)
-  {
-    var triplet = (((binarray[i   >> 2] >> 8 * ( i   %4)) & 0xFF) << 16)
-                | (((binarray[i+1 >> 2] >> 8 * ((i+1)%4)) & 0xFF) << 8 )
-                |  ((binarray[i+2 >> 2] >> 8 * ((i+2)%4)) & 0xFF);
-    for(var j = 0; j < 4; j++)
-    {
-      if(i * 8 + j * 6 > binarray.length * 32) str += b64pad;
-      else str += tab.charAt((triplet >> 6*(3-j)) & 0x3F);
+    function md5 (string, key, raw) {
+        if (!key) {
+            if (!raw) {
+                return hexMD5(string)
+            }
+            return rawMD5(string)
+        }
+        if (!raw) {
+            return hexHMACMD5(key, string)
+        }
+        return rawHMACMD5(key, string)
     }
-  }
-  return str;
-}
 
-module.exports = {
-  hex_md5:hex_md5
-};
+    if (typeof define === 'function' && define.amd) {
+        define(function () {
+            return md5
+        })
+    } else if (typeof module === 'object' && module.exports) {
+        module.exports = md5
+    } else {
+        $.md5 = md5
+    }
+})(this)

+ 27 - 17
apps/json-format/format-lib.js

@@ -362,6 +362,26 @@ var JsonFormatEntrance = (function () {
         alert('Json片段复制成功,随处粘贴可用!')
     };
 
+
+    /**
+     * 从el中获取json文本
+     * @param el
+     * @returns {string}
+     */
+    var getJsonText = function(el){
+
+        var txt = el.text().replace(/":\s/gm, '":').replace(/,$/, '').trim();
+        if (!(/^{/.test(txt) && /\}$/.test(txt)) && !(/^\[/.test(txt) && /\]$/.test(txt))) {
+            txt = '{' + txt + '}';
+        }
+        try {
+            txt = JSON.stringify(JSON.parse(txt), null, 4);
+        } catch (err) {
+        }
+
+        return txt;
+    };
+
     /**
      * 给某个节点增加操作项
      * @param el
@@ -371,15 +391,8 @@ var JsonFormatEntrance = (function () {
 
         // 下载json片段
         var fnDownload = function (ec) {
-            var txt = el.text().replace(/":\s/gm, '":').replace(/,$/, '').trim();
-            if (!(/^{/.test(txt) && /\}$/.test(txt)) && !(/^\[/.test(txt) && /\]$/.test(txt))) {
-                txt = '{' + txt + '}';
-            }
-            try {
-                txt = JSON.stringify(JSON.parse(txt), null, 4);
-            } catch (err) {
-            }
 
+            var txt = getJsonText(el);
             // 下载片段
             var dt = (new Date()).format('yyyyMMddHHmmss');
             var blob = new Blob([txt], {type: 'application/octet-stream'});
@@ -389,15 +402,7 @@ var JsonFormatEntrance = (function () {
 
         // 复制json片段
         var fnCopy = function (ec) {
-            var txt = el.text().replace(/":\s/gm, '":').replace(/,$/, '').trim();
-            if (!(/^{/.test(txt) && /\}$/.test(txt)) && !(/^\[/.test(txt) && /\]$/.test(txt))) {
-                txt = '{' + txt + '}';
-            }
-            try {
-                txt = JSON.stringify(JSON.parse(txt), null, 4);
-            } catch (err) {
-            }
-            _copyToClipboard(txt);
+            _copyToClipboard(getJsonText(el));
         };
 
         // 删除json片段
@@ -446,6 +451,11 @@ var JsonFormatEntrance = (function () {
             } else {
                 $(e.target).parent().trigger('click');
             }
+
+            // 触发钩子
+            if(typeof window._OnJsonItemClickByFH === 'function'){
+                window._OnJsonItemClickByFH(getJsonText(el));
+            }
         }).bind('mouseover', function (e) {
             $(this).addClass('x-hover');
             return false;

+ 70 - 1
apps/json-format/index.css

@@ -1,3 +1,4 @@
+@import url("../static/vendor/codemirror/codemirror.css");
 @import url("../static/css/bootstrap.min.css");
 @import url("./without-ui.css");
 
@@ -38,7 +39,7 @@ body {
     display: none;
     padding: 10px;
 }
-#jsonSource {
+#jsonSource,.CodeMirror {
     height: calc(100% - 50px);
     font-size: 10px;
 }
@@ -67,4 +68,72 @@ body {
 }
 .x-xdemo:hover {
     text-decoration: underline;
+}
+
+
+#errorTips {
+    position: fixed;
+    left: 20px;
+    bottom: 4px;
+    z-index: 23;
+    width: 500px;
+    border-radius: 4px;
+    box-shadow: 6px 5px 7px rgba(229, 163, 163, 0.4);
+    background-color: #ffecf1;
+    border: 1px solid #dbb2b2;
+    overflow: auto;
+    max-height: 98%;
+    min-height: 100px;
+    display: none;
+}
+#errorBtn {
+    padding:5px 0 0 5px;
+    font-size:16px;
+    cursor:pointer;
+    color:#e96969;
+    left: 490px;
+    background: #ffecf1;
+    position: fixed;
+    z-index: 100;
+}
+#errorBtn:hover {
+    opacity:0.6;
+}
+#errorCode .errorEm {
+    background-color:#ff921b;
+}
+#errorTarget {
+    color:#000;
+    background-color:#fff;
+}
+#errorCode {
+    padding:0 10px 10px;
+    word-break: break-all;
+    margin-top: 30px;
+}
+#errorCode ol {
+    padding:0 0 0 3em;
+}
+#errorCode .x-ph {
+    color: #f00;
+    font-weight: bold;
+}
+#tipsBox {
+    color:#b4465c;
+    padding: 6px 0 4px 2em;
+    position: fixed;
+    background-color: #ffecf1;
+    width: 500px;
+}
+#errorCode ol li {
+    padding:4px 7px 0;
+    list-style-type: decimal;
+    color:#b4465c;
+    background-color:#fdf7f7;
+}
+#errorCode ol li div {
+    color:#000;
+}
+input[type=checkbox] {
+    margin-left: 20px;
 }

+ 19 - 4
apps/json-format/index.html

@@ -22,8 +22,18 @@
 
             <div class="panel-body mod-json">
                 <div class="row panel-txt">
-                    <textarea class="form-control mod-textarea" id="jsonSource" placeholder="在这里粘贴您需要进行格式化的JSON代码" ref="jsonBox" v-model="jsonSource" @input="format"></textarea>
-                    <div><button id="btnFormat" class="btn btn-primary ui-mt-10" @click="format">格式化</button><span id="errorMsg" v-html="errorMsg"></span></div>
+                    <textarea class="form-control mod-textarea" id="jsonSource" placeholder="在这里粘贴您需要进行格式化的JSON代码" ref="jsonBox"></textarea>
+                    <div class="ui-mt-10">
+                        <button id="btnFormat" class="btn btn-primary" @click="format">格式化</button>
+                        <button id="btnCompress" class="btn btn-success" @click="compress">压缩</button>
+                        <input type="checkbox" v-model="jsonLintSwitch" id="jsonLint" @click="lintOn"><label for="jsonLint">开启JSONLint</label>
+                        <input type="checkbox" v-model="overrideJson" id="jsonOvrd"><label for="jsonOvrd">点选JSON项时默认进行编辑</label>
+                    </div>
+                    <div id="errorTips" v-bind:style="{display:showTips?'block':'none'}">
+                        <div id="errorBtn" @click="closeTips"><span id="closeError">☓</span></div>
+                        <div id="tipsBox" v-html="errorPos"></div>
+                        <div id="errorCode" v-html="errorJsonCode"></div>
+                    </div>
                 </div>
 
                 <div class="row rst-item" id="modJsonResult">
@@ -33,15 +43,20 @@
                         </svg>
                         加载中...
                     </div>
-                    <div id="jfCallbackName_start" class="callback-name">{{jfCallbackName_start}}</div>
+                    <div id="jfCallbackName_start" class="callback-name" v-html="jfCallbackName_start"></div>
                     <div id="jfContent" v-html="resultContent"></div>
                     <pre id="jfContent_pre"></pre>
-                    <div id="jfCallbackName_end" class="callback-name">{{jfCallbackName_end}}</div>
+                    <div id="jfCallbackName_end" class="callback-name" v-html="jfCallbackName_end"></div>
                 </div>
             </div>
 
         </div>
         <script src="../static/vendor/jquery/jquery-3.3.1.min.js"></script>
+        <script src="../static/vendor/codemirror/codemirror.js"></script>
+        <script src="../static/vendor/codemirror/javascript.js"></script>
+        <script src="../static/vendor/codemirror/active-line.js"></script>
+        <script src="../static/vendor/codemirror/matchbrackets.js"></script>
+        <script src="../static/vendor/codemirror/placeholder.js"></script>
         <script src="index.js"></script>
     </body>
 </html>

+ 100 - 20
apps/json-format/index.js

@@ -1,42 +1,70 @@
 /**
  * FeHelper Json Format Tools
  */
+let editor = {};
+
 new Vue({
     el: '#pageContainer',
     data: {
         defaultResultTpl: '<div class="x-placeholder"><img src="./json-demo.jpg" alt="json-placeholder"></div>',
         resultContent: '',
-        jsonSource: '',
+        jsonFormattedSource: '',
         errorMsg: '',
+        errorJsonCode: '',
+        errorPos: '',
         jfCallbackName_start: '',
-        jfCallbackName_end: ''
+        jfCallbackName_end: '',
+        showTips: false,
+        jsonLintSwitch: true,
+        fireChange: true,
+        overrideJson: false
     },
     mounted: function () {
         this.resultContent = this.defaultResultTpl;
 
+        editor = CodeMirror.fromTextArea(this.$refs.jsonBox, {
+            mode: "text/javascript",
+            lineNumbers: true,
+            matchBrackets: true,
+            styleActiveLine: true,
+            lineWrapping: true
+        });
+
+        //输入框聚焦
+        editor.focus();
+
+        // 格式化以后的JSON,点击以后可以重置原内容
+        window._OnJsonItemClickByFH = (jsonTxt) => {
+            if (this.overrideJson) {
+                this.disableEditorChange(jsonTxt);
+            }
+        };
+        editor.on('change', (editor, changes) => {
+            this.fireChange && this.format();
+        });
+
         // 在tab创建或者更新时候,监听事件,看看是否有参数传递过来
         chrome.runtime.onMessage.addListener((request, sender, callback) => {
             let MSG_TYPE = Tarp.require('../static/js/msg_type');
             if (request.type === MSG_TYPE.TAB_CREATED_OR_UPDATED && request.event === MSG_TYPE.JSON_FORMAT) {
                 if (request.content) {
-                    this.jsonSource = request.content || this.defaultResultTpl;
+                    editor.setValue(request.content || this.defaultResultTpl);
                     this.format();
                 }
             }
         });
 
-        //输入框聚焦
-        this.$refs.jsonBox.focus();
 
     },
     methods: {
         format: function () {
+            this.showTips = false;
             this.errorMsg = '';
             this.resultContent = this.defaultResultTpl;
 
-            let source = this.jsonSource.replace(/\n/gm, ' ');
+            let source = editor.getValue().replace(/\n/gm, ' ');
             if (!source) {
-                return;
+                return true;
             }
 
             // JSONP形式下的callback name
@@ -66,31 +94,83 @@ new Vue({
                 }
             } catch (ex) {
                 this.errorMsg = ex.message;
-                return;
             }
 
             // 是json格式,可以进行JSON自动格式化
-            if (jsonObj != null && typeof jsonObj === "object") {
+            if (jsonObj != null && typeof jsonObj === "object" && !this.errorMsg.length) {
                 try {
                     // 要尽量保证格式化的东西一定是一个json,所以需要把内容进行JSON.stringify处理
                     source = JSON.stringify(jsonObj);
                 } catch (ex) {
                     // 通过JSON反解不出来的,一定有问题
-                    return;
+                    this.errorMsg = ex.message;
                 }
 
-                // 格式化
-                Tarp.require('./format-lib').format(source);
+                if (!this.errorMsg.length) {
+                    // 格式化
+                    Tarp.require('./format-lib').format(source);
+                    this.jsonFormattedSource = source;
 
-                // 如果是JSONP格式的,需要把方法名也显示出来
-                if (funcName != null) {
-                    this.jfCallbackName_start = funcName + '(';
-                    this.jfCallbackName_end = ')';
-                } else {
-                    this.jfCallbackName_start = '';
-                    this.jfCallbackName_end = '';
+                    // 如果是JSONP格式的,需要把方法名也显示出来
+                    if (funcName != null) {
+                        this.jfCallbackName_start = funcName + '(';
+                        this.jfCallbackName_end = ')';
+                    } else {
+                        this.jfCallbackName_start = '';
+                        this.jfCallbackName_end = '';
+                    }
                 }
             }
+
+            if (this.errorMsg.length) {
+                return this.lintOn();
+            }
+            return true;
+        },
+
+        compress: function () {
+            if (this.format()) {
+                let jsonTxt = this.jfCallbackName_start + this.jsonFormattedSource + this.jfCallbackName_end;
+                this.disableEditorChange(jsonTxt);
+            }
+        },
+
+        lintOn: function () {
+            if (!editor.getValue().trim()) {
+                return true;
+            }
+            this.$nextTick(() => {
+                if (!this.jsonLintSwitch) {
+                    return;
+                }
+                let lintResult = Tarp.require('./jsonlint')(editor.getValue());
+                if (!isNaN(lintResult.line)) {
+                    this.errorPos = '错误位置:' + (lintResult.line + 1) + '行,' + (lintResult.col + 1) + '列;缺少字符或字符不正确';
+                    this.errorJsonCode = lintResult.dom;
+                    this.showTips = true;
+                    this.$nextTick(() => {
+                        let el = document.querySelector('#errorCode .errorEm');
+                        el && el.scrollIntoView();
+                        let scrollEl = document.querySelector('#errorTips');
+                        scrollEl.scrollBy(0, el.offsetTop - scrollEl.scrollTop - 50);
+                    });
+                }
+            });
+            return false;
+        },
+
+        closeTips: function () {
+            this.showTips = false;
+        },
+
+        disableEditorChange: function (jsonTxt) {
+            this.fireChange = false;
+            this.$nextTick(() => {
+                editor.setValue(jsonTxt);
+                this.$nextTick(() => {
+                    this.fireChange = true;
+                })
+            })
         },
 
         setDemo: function () {
@@ -177,7 +257,7 @@ new Vue({
                     }]
                 }
             };
-            this.jsonSource = JSON.stringify(demo);
+            editor.setValue(JSON.stringify(demo));
             this.$nextTick(this.format)
         }
     }

+ 658 - 0
apps/json-format/jsonlint.js

@@ -0,0 +1,658 @@
+/* Jison generated parser */
+var jsonlint = (function () {
+    var parser = {
+        trace: function trace() {
+        },
+        yy: {},
+        symbols_: {
+            "error": 2,
+            "JSONString": 3,
+            "STRING": 4,
+            "JSONNumber": 5,
+            "NUMBER": 6,
+            "JSONNullLiteral": 7,
+            "NULL": 8,
+            "JSONBooleanLiteral": 9,
+            "TRUE": 10,
+            "FALSE": 11,
+            "JSONText": 12,
+            "JSONValue": 13,
+            "EOF": 14,
+            "JSONObject": 15,
+            "JSONArray": 16,
+            "{": 17,
+            "}": 18,
+            "JSONMemberList": 19,
+            "JSONMember": 20,
+            ":": 21,
+            ",": 22,
+            "[": 23,
+            "]": 24,
+            "JSONElementList": 25,
+            "$accept": 0,
+            "$end": 1
+        },
+        terminals_: {
+            2: "error",
+            4: "STRING",
+            6: "NUMBER",
+            8: "NULL",
+            10: "TRUE",
+            11: "FALSE",
+            14: "EOF",
+            17: "{",
+            18: "}",
+            21: ":",
+            22: ",",
+            23: "[",
+            24: "]"
+        },
+        productions_: [0, [3, 1], [5, 1], [7, 1], [9, 1], [9, 1], [12, 2], [13, 1], [13, 1], [13, 1], [13, 1], [13, 1], [13, 1], [15, 2], [15, 3], [20, 3], [19, 1], [19, 3], [16, 2], [16, 3], [25, 1], [25, 3]],
+        performAction: function anonymous(yytext, yyleng, yylineno, yy, yystate, $$, _$) {
+
+            var $0 = $$.length - 1;
+            switch (yystate) {
+                case 1: // replace escaped characters with actual character
+                    this.$ = yytext.replace(/\\(\\|")/g, "$" + "1")
+                        .replace(/\\n/g, '\n')
+                        .replace(/\\r/g, '\r')
+                        .replace(/\\t/g, '\t')
+                        .replace(/\\v/g, '\v')
+                        .replace(/\\f/g, '\f')
+                        .replace(/\\b/g, '\b');
+
+                    break;
+                case 2:
+                    this.$ = Number(yytext);
+                    break;
+                case 3:
+                    this.$ = null;
+                    break;
+                case 4:
+                    this.$ = true;
+                    break;
+                case 5:
+                    this.$ = false;
+                    break;
+                case 6:
+                    return this.$ = $$[$0 - 1];
+                    break;
+                case 13:
+                    this.$ = {};
+                    break;
+                case 14:
+                    this.$ = $$[$0 - 1];
+                    break;
+                case 15:
+                    this.$ = [$$[$0 - 2], $$[$0]];
+                    break;
+                case 16:
+                    this.$ = {};
+                    this.$[$$[$0][0]] = $$[$0][1];
+                    break;
+                case 17:
+                    this.$ = $$[$0 - 2];
+                    $$[$0 - 2][$$[$0][0]] = $$[$0][1];
+                    break;
+                case 18:
+                    this.$ = [];
+                    break;
+                case 19:
+                    this.$ = $$[$0 - 1];
+                    break;
+                case 20:
+                    this.$ = [$$[$0]];
+                    break;
+                case 21:
+                    this.$ = $$[$0 - 2];
+                    $$[$0 - 2].push($$[$0]);
+                    break;
+            }
+        },
+        table: [{
+            3: 5,
+            4: [1, 12],
+            5: 6,
+            6: [1, 13],
+            7: 3,
+            8: [1, 9],
+            9: 4,
+            10: [1, 10],
+            11: [1, 11],
+            12: 1,
+            13: 2,
+            15: 7,
+            16: 8,
+            17: [1, 14],
+            23: [1, 15]
+        }, {1: [3]}, {14: [1, 16]}, {14: [2, 7], 18: [2, 7], 22: [2, 7], 24: [2, 7]}, {
+            14: [2, 8],
+            18: [2, 8],
+            22: [2, 8],
+            24: [2, 8]
+        }, {14: [2, 9], 18: [2, 9], 22: [2, 9], 24: [2, 9]}, {
+            14: [2, 10],
+            18: [2, 10],
+            22: [2, 10],
+            24: [2, 10]
+        }, {14: [2, 11], 18: [2, 11], 22: [2, 11], 24: [2, 11]}, {
+            14: [2, 12],
+            18: [2, 12],
+            22: [2, 12],
+            24: [2, 12]
+        }, {14: [2, 3], 18: [2, 3], 22: [2, 3], 24: [2, 3]}, {
+            14: [2, 4],
+            18: [2, 4],
+            22: [2, 4],
+            24: [2, 4]
+        }, {14: [2, 5], 18: [2, 5], 22: [2, 5], 24: [2, 5]}, {
+            14: [2, 1],
+            18: [2, 1],
+            21: [2, 1],
+            22: [2, 1],
+            24: [2, 1]
+        }, {14: [2, 2], 18: [2, 2], 22: [2, 2], 24: [2, 2]}, {3: 20, 4: [1, 12], 18: [1, 17], 19: 18, 20: 19}, {
+            3: 5,
+            4: [1, 12],
+            5: 6,
+            6: [1, 13],
+            7: 3,
+            8: [1, 9],
+            9: 4,
+            10: [1, 10],
+            11: [1, 11],
+            13: 23,
+            15: 7,
+            16: 8,
+            17: [1, 14],
+            23: [1, 15],
+            24: [1, 21],
+            25: 22
+        }, {1: [2, 6]}, {14: [2, 13], 18: [2, 13], 22: [2, 13], 24: [2, 13]}, {18: [1, 24], 22: [1, 25]}, {
+            18: [2, 16],
+            22: [2, 16]
+        }, {21: [1, 26]}, {14: [2, 18], 18: [2, 18], 22: [2, 18], 24: [2, 18]}, {
+            22: [1, 28],
+            24: [1, 27]
+        }, {22: [2, 20], 24: [2, 20]}, {14: [2, 14], 18: [2, 14], 22: [2, 14], 24: [2, 14]}, {
+            3: 20,
+            4: [1, 12],
+            20: 29
+        }, {
+            3: 5,
+            4: [1, 12],
+            5: 6,
+            6: [1, 13],
+            7: 3,
+            8: [1, 9],
+            9: 4,
+            10: [1, 10],
+            11: [1, 11],
+            13: 30,
+            15: 7,
+            16: 8,
+            17: [1, 14],
+            23: [1, 15]
+        }, {14: [2, 19], 18: [2, 19], 22: [2, 19], 24: [2, 19]}, {
+            3: 5,
+            4: [1, 12],
+            5: 6,
+            6: [1, 13],
+            7: 3,
+            8: [1, 9],
+            9: 4,
+            10: [1, 10],
+            11: [1, 11],
+            13: 31,
+            15: 7,
+            16: 8,
+            17: [1, 14],
+            23: [1, 15]
+        }, {18: [2, 17], 22: [2, 17]}, {18: [2, 15], 22: [2, 15]}, {22: [2, 21], 24: [2, 21]}],
+        defaultActions: {16: [2, 6]},
+        parseError: function parseError(str, hash) {
+            throw new Error(str);
+        },
+        parse: function parse(input) {
+            var self = this,
+                stack = [0],
+                vstack = [null], // semantic value stack
+                lstack = [], // location stack
+                table = this.table,
+                yytext = '',
+                yylineno = 0,
+                yyleng = 0,
+                recovering = 0,
+                TERROR = 2,
+                EOF = 1;
+
+            //this.reductionCount = this.shiftCount = 0;
+
+            this.lexer.setInput(input);
+            this.lexer.yy = this.yy;
+            this.yy.lexer = this.lexer;
+            if (typeof this.lexer.yylloc == 'undefined')
+                this.lexer.yylloc = {};
+            var yyloc = this.lexer.yylloc;
+            lstack.push(yyloc);
+
+            if (typeof this.yy.parseError === 'function')
+                this.parseError = this.yy.parseError;
+
+            function popStack(n) {
+                stack.length = stack.length - 2 * n;
+                vstack.length = vstack.length - n;
+                lstack.length = lstack.length - n;
+            }
+
+            function lex() {
+                var token;
+                token = self.lexer.lex() || 1; // $end = 1
+                // if token isn't its numeric value, convert
+                if (typeof token !== 'number') {
+                    token = self.symbols_[token] || token;
+                }
+                return token;
+            }
+
+            var symbol, preErrorSymbol, state, action, a, r, yyval = {}, p, len, newState, expected;
+            while (true) {
+                // retreive state number from top of stack
+                state = stack[stack.length - 1];
+
+                // use default actions if available
+                if (this.defaultActions[state]) {
+                    action = this.defaultActions[state];
+                } else {
+                    if (symbol == null)
+                        symbol = lex();
+                    // read action for current state and first input
+                    action = table[state] && table[state][symbol];
+                }
+
+                // handle parse error
+                _handle_error:
+                    if (typeof action === 'undefined' || !action.length || !action[0]) {
+
+                        if (!recovering) {
+                            // Report error
+                            expected = [];
+                            for (p in table[state]) if (this.terminals_[p] && p > 2) {
+                                expected.push("'" + this.terminals_[p] + "'");
+                            }
+                            var errStr = '';
+                            if (this.lexer.showPosition) {
+                                errStr = 'Parse error on line ' + (yylineno + 1) + ":\n" + this.lexer.showPosition() + "\nExpecting " + expected.join(', ') + ", got '" + this.terminals_[symbol] + "'";
+                            } else {
+                                errStr = 'Parse error on line ' + (yylineno + 1) + ": Unexpected " +
+                                    (symbol == 1 /*EOF*/ ? "end of input" :
+                                        ("'" + (this.terminals_[symbol] || symbol) + "'"));
+                            }
+                            this.parseError(errStr,
+                                {
+                                    text: this.lexer.match,
+                                    token: this.terminals_[symbol] || symbol,
+                                    lineText: this.lexer._sLine,
+                                    line: this.lexer.yylineno,
+                                    pos: this.lexer._pre,
+                                    loc: yyloc,
+                                    expected: expected
+                                });
+                        }
+
+                        // just recovered from another error
+                        if (recovering == 3) {
+                            if (symbol == EOF) {
+                                throw new Error(errStr || 'Parsing halted.');
+                            }
+
+                            // discard current lookahead and grab another
+                            yyleng = this.lexer.yyleng;
+                            yytext = this.lexer.yytext;
+                            yylineno = this.lexer.yylineno;
+                            yyloc = this.lexer.yylloc;
+                            symbol = lex();
+                        }
+
+                        // try to recover from error
+                        while (1) {
+                            // check for error recovery rule in this state
+                            if ((TERROR.toString()) in table[state]) {
+                                break;
+                            }
+                            if (state == 0) {
+                                throw new Error(errStr || 'Parsing halted.');
+                            }
+                            popStack(1);
+                            state = stack[stack.length - 1];
+                        }
+
+                        preErrorSymbol = symbol; // save the lookahead token
+                        symbol = TERROR;         // insert generic error symbol as new lookahead
+                        state = stack[stack.length - 1];
+                        action = table[state] && table[state][TERROR];
+                        recovering = 3; // allow 3 real symbols to be shifted before reporting a new error
+                    }
+
+                // this shouldn't happen, unless resolve defaults are off
+                if (action[0] instanceof Array && action.length > 1) {
+                    throw new Error('Parse Error: multiple actions possible at state: ' + state + ', token: ' + symbol);
+                }
+
+                switch (action[0]) {
+
+                    case 1: // shift
+                        //this.shiftCount++;
+
+                        stack.push(symbol);
+                        vstack.push(this.lexer.yytext);
+                        lstack.push(this.lexer.yylloc);
+                        stack.push(action[1]); // push state
+                        symbol = null;
+                        if (!preErrorSymbol) { // normal execution/no error
+                            yyleng = this.lexer.yyleng;
+                            yytext = this.lexer.yytext;
+                            yylineno = this.lexer.yylineno;
+                            yyloc = this.lexer.yylloc;
+                            if (recovering > 0)
+                                recovering--;
+                        } else { // error just occurred, resume old lookahead f/ before error
+                            symbol = preErrorSymbol;
+                            preErrorSymbol = null;
+                        }
+                        break;
+
+                    case 2: // reduce
+                        //this.reductionCount++;
+
+                        len = this.productions_[action[1]][1];
+
+                        // perform semantic action
+                        yyval.$ = vstack[vstack.length - len]; // default to $$ = $1
+                        // default location, uses first token for firsts, last for lasts
+                        yyval._$ = {
+                            first_line: lstack[lstack.length - (len || 1)].first_line,
+                            last_line: lstack[lstack.length - 1].last_line,
+                            first_column: lstack[lstack.length - (len || 1)].first_column,
+                            last_column: lstack[lstack.length - 1].last_column
+                        };
+                        r = this.performAction.call(yyval, yytext, yyleng, yylineno, this.yy, action[1], vstack, lstack);
+
+                        if (typeof r !== 'undefined') {
+                            return r;
+                        }
+
+                        // pop off stack
+                        if (len) {
+                            stack = stack.slice(0, -1 * len * 2);
+                            vstack = vstack.slice(0, -1 * len);
+                            lstack = lstack.slice(0, -1 * len);
+                        }
+
+                        stack.push(this.productions_[action[1]][0]);    // push nonterminal (reduce)
+                        vstack.push(yyval.$);
+                        lstack.push(yyval._$);
+                        // goto new state = table[STATE][NONTERMINAL]
+                        newState = table[stack[stack.length - 2]][stack[stack.length - 1]];
+                        stack.push(newState);
+                        break;
+
+                    case 3: // accept
+                        return true;
+                }
+
+            }
+
+            return true;
+        }
+    };
+    /* Jison generated lexer */
+    var lexer = (function () {
+        var lexer = ({
+            EOF: 1,
+            parseError: function parseError(str, hash) {
+                if (this.yy.parseError) {
+                    this.yy.parseError(str, hash);
+                } else {
+                    throw new Error(str);
+                }
+            },
+            setInput: function (input) {
+                this._input = input;
+                this._more = this._less = this.done = false;
+                this.yylineno = this.yyleng = 0;
+                this.yytext = this.matched = this.match = '';
+                this.conditionStack = ['INITIAL'];
+                this.yylloc = {first_line: 1, first_column: 0, last_line: 1, last_column: 0};
+                return this;
+            },
+            input: function () {
+                var ch = this._input[0];
+                this.yytext += ch;
+                this.yyleng++;
+                this.match += ch;
+                this.matched += ch;
+                var lines = ch.match(/\n/);
+                if (lines) this.yylineno++;
+                this._input = this._input.slice(1);
+                return ch;
+            },
+            unput: function (ch) {
+                this._input = ch + this._input;
+                return this;
+            },
+            more: function () {
+                this._more = true;
+                return this;
+            },
+            less: function (n) {
+                this._input = this.match.slice(n) + this._input;
+            },
+            pastInput: function () {
+                var past = this.matched.substr(0, this.matched.length - this.match.length);
+
+                this._pre = past.slice(past.lastIndexOf('\n')).length - 1;
+                return (past.length > 20 ? '...' : '') + past.substr(-20).replace(/\n/g, "");
+            },
+            upcomingInput: function () {
+                var next = this.match;
+                if (next.length < 20) {
+                    next += this._input.substr(0, 20 - next.length);
+                }
+                var s = (next.substr(0, 20) + (next.length > 20 ? '...' : '')).replace(/\n/g, "");
+                return s;
+            },
+            showPosition: function () {
+                var pre = this.pastInput();
+                var c = new Array(pre.length + 1).join("-");
+                var sW = this.upcomingInput();
+                var s = pre + sW;
+                this._sLine = s;
+                return s + "\n" + c + "^";
+            },
+            next: function () {
+                if (this.done) {
+                    return this.EOF;
+                }
+                if (!this._input) this.done = true;
+
+                var token,
+                    match,
+                    tempMatch,
+                    index,
+                    col,
+                    lines;
+                if (!this._more) {
+                    this.yytext = '';
+                    this.match = '';
+                }
+                var rules = this._currentRules();
+                for (var i = 0; i < rules.length; i++) {
+                    tempMatch = this._input.match(this.rules[rules[i]]);
+                    if (tempMatch && (!match || tempMatch[0].length > match[0].length)) {
+                        match = tempMatch;
+                        index = i;
+                        if (!this.options.flex) break;
+                    }
+                }
+                if (match) {
+                    lines = match[0].match(/\n.*/g);
+                    if (lines) this.yylineno += lines.length;
+                    this.yylloc = {
+                        first_line: this.yylloc.last_line,
+                        last_line: this.yylineno + 1,
+                        first_column: this.yylloc.last_column,
+                        last_column: lines ? lines[lines.length - 1].length - 1 : this.yylloc.last_column + match[0].length
+                    }
+                    this.yytext += match[0];
+                    this.match += match[0];
+                    this.yyleng = this.yytext.length;
+                    this._more = false;
+                    this._input = this._input.slice(match[0].length);
+                    this.matched += match[0];
+                    token = this.performAction.call(this, this.yy, this, rules[index], this.conditionStack[this.conditionStack.length - 1]);
+                    if (this.done && this._input) this.done = false;
+                    if (token) return token;
+                    else return;
+                }
+                if (this._input === "") {
+                    return this.EOF;
+                } else {
+                    this.parseError('Lexical error on line ' + (this.yylineno + 1) + '. Unrecognized text.\n' + this.showPosition(),
+                        {
+                            text: "",
+                            token: null,
+                            lineText: this.lexer._sLine,
+                            line: this.yylineno,
+                            pos: this.lexer._pre
+                        });
+                }
+            },
+            lex: function lex() {
+                var r = this.next();
+                if (typeof r !== 'undefined') {
+                    return r;
+                } else {
+                    return this.lex();
+                }
+            },
+            begin: function begin(condition) {
+                this.conditionStack.push(condition);
+            },
+            popState: function popState() {
+                return this.conditionStack.pop();
+            },
+            _currentRules: function _currentRules() {
+                return this.conditions[this.conditionStack[this.conditionStack.length - 1]].rules;
+            },
+            topState: function () {
+                return this.conditionStack[this.conditionStack.length - 2];
+            },
+            pushState: function begin(condition) {
+                this.begin(condition);
+            }
+        });
+        lexer.options = {};
+        lexer.performAction = function anonymous(yy, yy_, $avoiding_name_collisions, YY_START) {
+
+            var YYSTATE = YY_START
+            switch ($avoiding_name_collisions) {
+                case 0:/* skip whitespace */
+                    break;
+                case 1:
+                    return 6
+                    break;
+                case 2:
+                    yy_.yytext = yy_.yytext.substr(1, yy_.yyleng - 2);
+                    return 4
+                    break;
+                case 3:
+                    return 17
+                    break;
+                case 4:
+                    return 18
+                    break;
+                case 5:
+                    return 23
+                    break;
+                case 6:
+                    return 24
+                    break;
+                case 7:
+                    return 22
+                    break;
+                case 8:
+                    return 21
+                    break;
+                case 9:
+                    return 10
+                    break;
+                case 10:
+                    return 11
+                    break;
+                case 11:
+                    return 8
+                    break;
+                case 12:
+                    return 14
+                    break;
+                case 13:
+                    return 'INVALID'
+                    break;
+            }
+        };
+        lexer.rules = [/^(?:\s+)/, /^(?:(-?([0-9]|[1-9][0-9]+))(\.[0-9]+)?([eE][-+]?[0-9]+)?\b)/, /^(?:"(?:\\[\\"bfnrt/]|\\u[a-fA-F0-9]{4}|[^\\\0-\x09\x0a-\x1f"])*")/, /^(?:\{)/, /^(?:\})/, /^(?:\[)/, /^(?:\])/, /^(?:,)/, /^(?::)/, /^(?:true\b)/, /^(?:false\b)/, /^(?:null\b)/, /^(?:$)/, /^(?:.)/];
+        lexer.conditions = {"INITIAL": {"rules": [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13], "inclusive": true}};
+
+        return lexer;
+    })();
+    parser.lexer = lexer;
+    return parser;
+})();
+
+
+/**
+ * json lint entry
+ * @param sJson
+ * @returns {{}}
+ */
+module.exports = function (sJson) {
+    let result = {};
+
+    let insertErrorFlag = function (s) {
+        s = s.replace(/^(\s*){([^\s])/, ($0, $1, $2) => ($1 + '{ ' + $2));
+        s = s.replace(/([\s,]+)([^,:\{\}\[\]\s'"]+)(\s*:)/gm, ($0, $1, $2, $3) => ($1 + '"' + $2 + '"' + $3));
+        s = s.replace(/\r\n/g, '\n').replace(/\r/g, '\n');
+        let aLine = s.split('\n');
+        jsonlint.yy.parseError = function (sError, oError) {
+            let theLineNum = oError.line === oError.loc.first_line ? oError.line - 1 : oError.line;
+            let sLine = aLine[theLineNum];
+            result.line = theLineNum;
+            result.col = oError.loc.first_column;
+            aLine[theLineNum] = sLine.slice(0, oError.loc.first_column) +
+                '@◆$#errorEm#$◆@' + sLine.slice(oError.loc.first_column, oError.loc.last_column) + '@◆$#/errorEm#$◆@' +
+                sLine.slice(oError.loc.last_column);
+        };
+
+        try {
+            jsonlint.parse(s);
+        } catch (e) {
+            result["hasError"] = true;
+        }
+        return aLine.join('\n');
+    };
+
+    let escape = function (s) {
+        s = s || '';
+        return s.replace(/\&/g, '&amp;').replace(/\</g, '&lt;').replace(/\>/g, '&gt;').replace(/\"/g, '&quot;').replace(/ /g, '&nbsp;');
+    };
+
+    sJson = insertErrorFlag(sJson);
+    sJson = escape(sJson);
+    let placeholder = '<span class="x-ph"><<<<</span>';
+    sJson = sJson.replace('@◆$#errorEm#$◆@', '<span class="errorEm">').replace('@◆$#/errorEm#$◆@', '</span>' + placeholder);
+    sJson = '<ol><li><div>' + sJson.split('\n').join('</div></li><li><div>') + '</div></li></ol>';
+    result["dom"] = '<div class="line-code">' + sJson + '</div>';
+
+    return result;
+};

+ 1 - 1
apps/manifest.json

@@ -1,6 +1,6 @@
 {
   "name": "WEB前端助手(FeHelper)",
-  "version": "2018.05.1412",
+  "version": "2018.05.1712",
   "manifest_version": 2,
   "default_locale": "zh_CN",
   "description": "FE助手:包括JSON格式化、二维码生成与解码、信息编解码、代码压缩、美化、页面取色、Markdown与HTML互转、网页转为图片、正则表达式、时间转换工具、编码规范检测、页面性能检测、Ajax接口调试",