1
0
zxlie 7 жил өмнө
parent
commit
fddb877387

+ 3 - 2
README.md

@@ -24,8 +24,9 @@ https://chrome.google.com/webstore/detail/pkgccpejnmalmdinmhkkfafefagiiiad?hl=zh
 ![安装教程](/apps/static/screenshot/how-to-install.gif)
 
 ## 三、扩展功能:
-- Json自动美化(页面自动检测并格式化)
-- Json手动美化(粘贴文本、手动格式化)
+- JSON自动美化(页面自动检测并格式化)
+- JSON手动美化(粘贴文本、手动格式化)
+- JSON比对工具(支持左右两个JSON片段进行键值对比较)
 - 字符串编解码(Unicode/UTF8/Base64/MD5)
 - 代码美化工具(HTML/CSS/JS/XML/SQL)
 - 代码压缩工具(HTML/CSS/JS)

+ 29 - 10
apps/background/index.js

@@ -556,18 +556,37 @@ var BgPageInstance = (function () {
         chrome.tabs.query({active: true, currentWindow: true}, function (tabs) {
 
             let tab = tabs[0];
-            let url = new URL(tab.url);
-            let ext = url.pathname.substring(url.pathname.lastIndexOf(".") + 1).toLowerCase();
-            let fileType = ({'js': 'Javascript', 'css': 'CSS'})[ext];
-            if (!fileType) {
-                return false;
-            }
 
-            Settings.getOptsFromBgPage(opts => {
-                opts.JS_CSS_PAGE_BEAUTIFY && chrome.tabs.sendMessage(tab.id, {
-                    type: MSG_TYPE.JS_CSS_PAGE_BEAUTIFY
-                });
+            chrome.tabs.executeScript(tab.id, {
+                code: '(' + (() => {
+
+                    let ext = location.pathname.substring(location.pathname.lastIndexOf(".") + 1).toLowerCase();
+                    let fileType = ({'js': 'javascript', 'css': 'css'})[ext];
+                    let contentType = document.contentType.toLowerCase();
+
+                    if (!fileType) {
+                        if (/\/javascript$/.test(contentType)) {
+                            fileType = 'javascript';
+                        } else if (/\/css$/.test(contentType)) {
+                            fileType = 'css';
+                        }
+                    } else if (contentType === 'text/html') {
+                        fileType = undefined;
+                    }
+
+                    return fileType;
+                }).toString() + ')()'
+            }, function (fileType) {
+                if (fileType[0] === 'javascript' || fileType[0] === 'css') {
+                    Settings.getOptsFromBgPage(opts => {
+                        opts.JS_CSS_PAGE_BEAUTIFY && chrome.tabs.sendMessage(tab.id, {
+                            type: MSG_TYPE.JS_CSS_PAGE_BEAUTIFY,
+                            content:fileType[0]
+                        });
+                    });
+                }
             });
+
         });
     };
 

+ 3 - 0
apps/code-beautify/automatic.css

@@ -130,3 +130,6 @@ body.processing > :not(#fehelper_tips) {
     pointer-events: none;
     cursor: wait;
 }
+pre>code[class*="language"] {
+    overflow: initial;
+}

+ 1 - 6
apps/code-beautify/automatic.js

@@ -51,13 +51,8 @@ module.exports = (() => {
      * 检测
      * @returns {boolean}
      */
-    let detect = () => {
+    let detect = (fileType) => {
 
-        let ext = location.pathname.substring(location.pathname.lastIndexOf(".") + 1).toLowerCase();
-        let fileType = ({'js': 'javascript', 'css': 'css'})[ext];
-        if (!fileType || document.contentType.toLowerCase() === 'text/html') {
-            return false;
-        }
         let source = document.body.textContent;
 
         let cssUrl = chrome.extension.getURL('code-beautify/automatic.css');

+ 1 - 1
apps/content-script/index.js

@@ -43,7 +43,7 @@ chrome.runtime.onMessage.addListener(function (request, sender, callback) {
 
         // js、css页面自动检测,提示格式化
         case MSG_TYPE.JS_CSS_PAGE_BEAUTIFY:
-            Tarp.require('../code-beautify/automatic', true).then(beautifier => beautifier.detect());
+            Tarp.require('../code-beautify/automatic', true).then(beautifier => beautifier.detect(request.content));
             break;
 
         // 二维码解码

+ 47 - 0
apps/json-diff/index.css

@@ -0,0 +1,47 @@
+@import url("../static/vendor/codemirror/codemirror.css");
+@import url("../static/css/bootstrap.min.css");
+
+body {
+    background-color: #fff;
+}
+
+.wp-json {
+    width:auto;
+}
+.wp-json .mod-json {
+    position: absolute;
+    top: 60px;
+    bottom: 0;
+    right:20px;
+    left:20px;
+}
+.wp-json .mod-json .panel-txt {
+    position: absolute;
+    width: 500px;
+    top: 15px;
+    bottom: 0;
+}
+.wp-json .panel-body {
+    padding:15px 0;
+}
+
+.box-wrapper-left {
+    height: 100%;
+    padding:0 5px 0 0;
+}
+.box-wrapper-right {
+    height: 100%;
+    padding:0 0 0 5px;
+}
+#jsonSourceLeft, #jsonSourceRight, .CodeMirror {
+    height: calc(100%);
+    font-size: 10px;
+}
+.x-error {
+    float: right;
+    color: #0c0;
+    font-size: 14px;
+}
+.x-error.x-hlt {
+    color:#f00;
+}

+ 44 - 0
apps/json-diff/index.html

@@ -0,0 +1,44 @@
+<!DOCTYPE HTML>
+<html lang="zh-CN">
+    <head>
+        <title>JSON比对工具</title>
+        <meta charset="UTF-8">
+        <link rel="stylesheet" href="index.css" />
+        <script type="text/javascript" src="../static/vendor/vue/vue.js"></script>
+    </head>
+    <body>
+        <div class="wrapper wp-json" id="pageContainer">
+            <div class="panel panel-default" style="margin-bottom: 0px;">
+                <div class="panel-heading">
+                    <h3 class="panel-title">
+                        <span class="x-error" v-bind:class="{'x-hlt' : errorHighlight}" v-html="errorMessage"></span>
+                        <a href="http://www.baidufe.com/fehelper/feedback.html" target="_blank" class="x-a-high">
+                            <img src="../static/img/fe-16.png" alt="fehelper"/> FeHelper</a>:JSON比对工具
+                    </h3>
+                </div>
+            </div>
+
+            <div class="panel-body mod-json">
+                <div class="col-md-6 box-wrapper-left">
+                    <textarea class="form-control mod-textarea" id="jsonSourceLeft" ref="srcLeft" placeholder="在这里粘贴JSON代码"></textarea>
+                </div>
+                <div class="col-md-6 box-wrapper-right">
+                    <textarea class="form-control mod-textarea" id="jsonSourceRight" ref="srcRight" placeholder="在这里粘贴JSON代码"></textarea>
+                </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="../static/vendor/codemirror/formatting.js"></script>
+        <script src="../static/vendor/json-diff/backbone-events.min.js"></script>
+        <script src="../static/vendor/json-diff/json-patch-duplex.min.js"></script>
+        <script src="../static/vendor/json-diff/json-source-map.js"></script>
+        <script src="../static/vendor/json-diff/json-diff.js"></script>
+        <script src="./index.js"></script>
+    </body>
+</html>

+ 33 - 0
apps/json-diff/index.js

@@ -0,0 +1,33 @@
+new Vue({
+    el: '#pageContainer',
+    data: {
+        errorMessage: '',
+        errorHighlight: false
+    },
+    mounted: function () {
+        // 错误处理器
+        let errorHandler = (which, ok) => {
+            if (ok) {
+                this.errorMessage = '两侧JSON比对完成!';
+                this.errorHighlight = false;
+            } else {
+                this.errorMessage = {'left': '左', 'right': '右', 'left-right': '两'}[which] + '侧JSON不合法!';
+                this.errorHighlight = true;
+            }
+        };
+
+        // diff处理器
+        let diffHandler = (diffs) => {
+            if (!this.errorHighlight) {
+                if (diffs.length) {
+                    this.errorMessage += '共有 ' + diffs.length + ' 处不一致!';
+                } else {
+                    this.errorMessage += '左右两侧JSON内容一致!';
+                }
+            }
+        };
+
+        // 代码比对
+        JsonDiff.init(this.$refs.srcLeft, this.$refs.srcRight, errorHandler, diffHandler);
+    }
+});

+ 1 - 1
apps/json-format/index.html

@@ -1,7 +1,7 @@
 <!DOCTYPE HTML>
 <html lang="zh-CN">
     <head>
-        <title>Json格式化查看工具</title>
+        <title>JSON格式化查看工具</title>
         <meta charset="UTF-8">
         <link rel="stylesheet" href="index.css" />
         <script type="text/javascript" src="../static/vendor/vue/vue.js"></script>

+ 2 - 2
apps/manifest.json

@@ -1,9 +1,9 @@
 {
   "name": "WEB前端助手(FeHelper)",
-  "version": "2018.07.0910",
+  "version": "2018.07.1010",
   "manifest_version": 2,
   "default_locale": "zh_CN",
-  "description": "FE助手:包括JSON格式化、二维码生成与解码、信息编解码、代码压缩、美化、页面取色、Markdown与HTML互转、网页滚动截屏、正则表达式、时间转换工具、编码规范检测、页面性能检测、Ajax接口调试、密码生成器",
+  "description": "FE助手:包括JSON格式化、二维码生成与解码、信息编解码、代码压缩、美化、页面取色、Markdown与HTML互转、网页滚动截屏、正则表达式、时间转换工具、编码规范检测、页面性能检测、Ajax接口调试、密码生成器、JSON比对工具",
   "icons": {
     "16": "static/img/fe-16.png",
     "48": "static/img/fe-48.png",

Файлын зөрүү хэтэрхий том тул дарагдсан байна
+ 0 - 0
apps/options/index.html


+ 1 - 0
apps/options/settings.js

@@ -14,6 +14,7 @@ module.exports = (() => {
         'CODE_BEAUTIFY',
         'CODE_COMPRESS',
         'JSON_FORMAT',
+        'JSON_COMPARE',
         'QR_CODE',
         'COLOR_PICKER',
         'REGEXP_TOOL',

+ 14 - 14
apps/popup/index.css

@@ -82,27 +82,23 @@ ul.fe-function-list li:hover > b {
 }
 
 ul.fe-function-list li.-x-fcp b {
-    background-position: 0px -145px;
-}
-
-ul.fe-function-list li.-x-grid b {
-    background-position: -113px -113px;
+    background-position: 0 -143px;
 }
 
 ul.fe-function-list li.-x-regexp b {
-    background-position: -144px 0;
+    background-position: -144px 1px;
 }
 
 ul.fe-function-list li.-x-endecode b {
-    background-position: -113px -81px;
+    background-position: -113px -80px;
 }
 
 ul.fe-function-list li.-x-loadtime b {
-    background-position: -81px -113px;
+    background-position: -81px -111px;
 }
 
 ul.fe-function-list li.-x-jsonformat b {
-    background-position: -224px -113px;
+    background-position: -224px -111px;
 }
 
 ul.fe-function-list li.-x-qrcode b {
@@ -110,7 +106,7 @@ ul.fe-function-list li.-x-qrcode b {
 }
 
 ul.fe-function-list li.-x-codebeautify b {
-    background-position: -64px -145px;
+    background-position: -64px -144px;
 }
 
 ul.fe-function-list li.-x-tracker b {
@@ -118,15 +114,15 @@ ul.fe-function-list li.-x-tracker b {
 }
 
 ul.fe-function-list li.-x-timestamp b {
-    background-position: 0px -161px;
+    background-position: 0 -160px;
 }
 
 ul.fe-function-list li.-x-codecompress b {
-    background-position: -65px -81px;
+    background-position: -65px -80px;
 }
 
 ul.fe-function-list li.-x-base64 b {
-    background-position: -33px -209px;
+    background-position: -33px -208px;
 }
 
 ul.fe-function-list li.-x-colorpicker b {
@@ -134,7 +130,7 @@ ul.fe-function-list li.-x-colorpicker b {
 }
 
 ul.fe-function-list li.-x-ajax-debugger b {
-    background-position: -81px -129px;
+    background-position: -81px -127px;
 }
 
 ul.fe-function-list li.-x-markdown b {
@@ -148,6 +144,10 @@ ul.fe-function-list li.-x-pagecapture b {
 ul.fe-function-list li.-x-password b {
     background-position: -144px -95px;
 }
+ul.fe-function-list li.-x-jsondiff b {
+    background-position: -113px -111px;
+}
+
 
 ul.fe-function-list li i {
     color: #aaa;

Файлын зөрүү хэтэрхий том тул дарагдсан байна
+ 0 - 0
apps/popup/index.html


+ 3 - 0
apps/static/js/msg_type.js

@@ -73,6 +73,9 @@ const MSG_TYPE = {
     RANDOM_PASSWORD:'password',
     // 二维码解码
     QR_DECODE: 'qr-decode',
+
+    // JSON比对
+    JSON_COMPARE:'json-diff',
     // JSON页面自动格式化
     JSON_PAGE_FORMAT: 'JSON_PAGE_FORMAT',
     JSON_PAGE_FORMAT_REQUEST: 'JSON_PAGE_FORMAT_REQUEST',

+ 108 - 0
apps/static/vendor/codemirror/formatting.js

@@ -0,0 +1,108 @@
+(function() {
+
+    CodeMirror.extendMode("css", {
+        commentStart: "/*",
+        commentEnd: "*/",
+        newlineAfterToken: function(type, content) {
+            return /^[;{}]$/.test(content);
+        }
+    });
+
+    CodeMirror.extendMode("javascript", {
+        commentStart: "/*",
+        commentEnd: "*/",
+        // FIXME semicolons inside of for
+        newlineAfterToken: function(type, content, textAfter, state) {
+            if (this.jsonMode) {
+                return /^[\[,{]$/.test(content) || /^}/.test(textAfter);
+            } else {
+                if (content == ";" && state.lexical && state.lexical.type == ")") return false;
+                return /^[;{}]$/.test(content) && !/^;/.test(textAfter);
+            }
+        }
+    });
+
+    CodeMirror.extendMode("xml", {
+        commentStart: "<!--",
+        commentEnd: "-->",
+        newlineAfterToken: function(type, content, textAfter) {
+            return type == "tag" && />$/.test(content) || /^</.test(textAfter);
+        }
+    });
+
+    // Comment/uncomment the specified range
+    CodeMirror.defineExtension("commentRange", function (isComment, from, to) {
+        var cm = this, curMode = CodeMirror.innerMode(cm.getMode(), cm.getTokenAt(from).state).mode;
+        cm.operation(function() {
+            if (isComment) { // Comment range
+                cm.replaceRange(curMode.commentEnd, to);
+                cm.replaceRange(curMode.commentStart, from);
+                if (from.line == to.line && from.ch == to.ch) // An empty comment inserted - put cursor inside
+                    cm.setCursor(from.line, from.ch + curMode.commentStart.length);
+            } else { // Uncomment range
+                var selText = cm.getRange(from, to);
+                var startIndex = selText.indexOf(curMode.commentStart);
+                var endIndex = selText.lastIndexOf(curMode.commentEnd);
+                if (startIndex > -1 && endIndex > -1 && endIndex > startIndex) {
+                    // Take string till comment start
+                    selText = selText.substr(0, startIndex)
+                        // From comment start till comment end
+                        + selText.substring(startIndex + curMode.commentStart.length, endIndex)
+                        // From comment end till string end
+                        + selText.substr(endIndex + curMode.commentEnd.length);
+                }
+                cm.replaceRange(selText, from, to);
+            }
+        });
+    });
+
+    // Applies automatic mode-aware indentation to the specified range
+    CodeMirror.defineExtension("autoIndentRange", function (from, to) {
+        var cmInstance = this;
+        this.operation(function () {
+            for (var i = from.line; i <= to.line; i++) {
+                cmInstance.indentLine(i, "smart");
+            }
+        });
+    });
+
+    // Applies automatic formatting to the specified range
+    CodeMirror.defineExtension("autoFormatRange", function (from, to) {
+        var cm = this;
+        var outer = cm.getMode(), text = cm.getRange(from, to).split("\n");
+        var state = CodeMirror.copyState(outer, cm.getTokenAt(from).state);
+        var tabSize = cm.getOption("tabSize");
+
+        var out = "", lines = 0, atSol = from.ch == 0;
+        function newline() {
+            out += "\n";
+            atSol = true;
+            ++lines;
+        }
+
+        for (var i = 0; i < text.length; ++i) {
+            var stream = new CodeMirror.StringStream(text[i], tabSize);
+            while (!stream.eol()) {
+                var inner = CodeMirror.innerMode(outer, state);
+                var style = outer.token(stream, state), cur = stream.current();
+                stream.start = stream.pos;
+                if (!atSol || /\S/.test(cur)) {
+                    out += cur;
+                    atSol = false;
+                }
+                if (!atSol && inner.mode.newlineAfterToken &&
+                    inner.mode.newlineAfterToken(style, cur, stream.string.slice(stream.pos) || text[i+1] || "", inner.state))
+                    newline();
+            }
+            if (!stream.pos && outer.blankLine) outer.blankLine(state);
+            if (!atSol) newline();
+        }
+
+        cm.operation(function () {
+            cm.replaceRange(out, from, to);
+            for (var cur = from.line + 1, end = from.line + lines; cur <= end; ++cur)
+                cm.indentLine(cur, "smart");
+            cm.setSelection(from, cm.getCursor(false));
+        });
+    });
+})();

Файлын зөрүү хэтэрхий том тул дарагдсан байна
+ 1 - 0
apps/static/vendor/json-diff/backbone-events.min.js


+ 198 - 0
apps/static/vendor/json-diff/json-diff.js

@@ -0,0 +1,198 @@
+// http://json-diff.com/
+
+let JsonDiff = (function () {
+
+    function JsonInputView(el, initialText) {
+        this.el = el;
+        let codemirror = this.codemirror = CodeMirror.fromTextArea(this.el, {
+            lineNumbers: true,
+            mode: {
+                name: "javascript",
+                json: true
+            },
+            matchBrackets: true,
+            theme: 'tomorrow-night'
+        });
+        if (initialText) {
+            codemirror.setValue(initialText);
+        }
+        let self = this;
+
+        codemirror.on('inputRead', function (cm, e) {
+            if (e.origin === 'paste') {
+                autoFormat();
+            }
+            triggerChange();
+        });
+        codemirror.on('keyup', triggerChange);
+        codemirror.on('change', triggerChange);
+        codemirror.on('clear', function () {
+            console.log(arguments);
+        });
+
+        let oldValue = '';
+
+        function triggerChange() {
+            let text = codemirror.getValue();
+            if (text !== oldValue) {
+                self.trigger('change');
+            }
+            oldValue = text;
+        }
+
+        function autoFormat() {
+            let totalLines = codemirror.lineCount();
+            codemirror.autoFormatRange({
+                line: 0,
+                ch: 0
+            }, {
+                line: totalLines
+            });
+            codemirror.setSelection({
+                line: 0,
+                ch: 0
+            });
+        }
+    }
+
+    JsonInputView.prototype.getText = function () {
+        return this.codemirror.getValue();
+    };
+
+    JsonInputView.prototype.setText = function (text) {
+        return this.codemirror.setValue(text);
+    };
+
+    JsonInputView.prototype.highlightRemoval = function (diff) {
+        this._highlight(diff, '#DD4444');
+    };
+
+    JsonInputView.prototype.highlightAddition = function (diff) {
+        this._highlight(diff, '#4ba2ff');
+    };
+
+    JsonInputView.prototype.highlightChange = function (diff) {
+        this._highlight(diff, '#E5E833');
+    };
+
+    JsonInputView.prototype._highlight = function (diff, color) {
+        let pos = getStartAndEndPosOfDiff(this.getText(), diff);
+        this.codemirror.markText(pos.start, pos.end, {
+            css: 'background-color: ' + color
+        });
+    };
+
+    JsonInputView.prototype.clearMarkers = function () {
+        this.codemirror.getAllMarks().forEach(function (marker) {
+            marker.clear();
+        });
+    };
+
+    function getStartAndEndPosOfDiff(textValue, diff) {
+        let result = parse(textValue);
+        let pointers = result.pointers;
+        let path = diff.path;
+        let start = {
+            line: pointers[path].key ? pointers[path].key.line : pointers[path].value.line,
+            ch: pointers[path].key ? pointers[path].key.column : pointers[path].value.column
+        };
+        let end = {
+            line: pointers[path].valueEnd.line,
+            ch: pointers[path].valueEnd.column
+        };
+
+        return {
+            start: start,
+            end: end
+        }
+    }
+
+    function onInputChange() {
+        compareJson();
+    }
+
+    let leftInputView = null;
+    let rightInputView = null;
+    let errHandler = null;
+    let diffHandler = null;
+
+    function compareJson() {
+        leftInputView.clearMarkers();
+        rightInputView.clearMarkers();
+        let leftText = leftInputView.getText(),
+            rightText = rightInputView.getText();
+        let leftJson, rightJson;
+        try {
+            if (leftText) {
+                leftJson = JSON.parse(leftText);
+            }
+            errHandler && errHandler('left', true);
+        } catch (e) {
+            console.log('left ==>', e);
+        }
+        try {
+            if (rightText) {
+                rightJson = JSON.parse(rightText);
+            }
+            errHandler && errHandler('right', true);
+        } catch (e) {
+            console.log('right ==>', e);
+        }
+
+        if (!leftJson || !rightJson) {
+            if (!leftJson && !rightJson) {
+                errHandler && errHandler('left-right', false);
+            } else if (!leftJson) {
+                errHandler && errHandler('left', false);
+
+            } else {
+                errHandler && errHandler('right', false);
+            }
+
+            return;
+        }
+        let diffs = jsonpatch.compare(leftJson, rightJson);
+        diffHandler && diffHandler(diffs);
+
+        diffs.forEach(function (diff) {
+            try {
+                if (diff.op === 'remove') {
+                    leftInputView.highlightRemoval(diff);
+                } else if (diff.op === 'add') {
+                    rightInputView.highlightAddition(diff);
+                } else if (diff.op === 'replace') {
+                    rightInputView.highlightChange(diff);
+                    leftInputView.highlightChange(diff);
+                }
+            } catch (e) {
+                console.warn('error while trying to highlight diff', e);
+            }
+        });
+    }
+
+
+    function init(left, right, errorHandler, dfHandler) {
+
+        errHandler = errorHandler;
+        diffHandler = dfHandler;
+
+        BackboneEvents.mixin(JsonInputView.prototype);
+
+        leftInputView = new JsonInputView(left, '');
+        rightInputView = new JsonInputView(right, '');
+        leftInputView.on('change', onInputChange);
+        rightInputView.on('change', onInputChange);
+        leftInputView.codemirror.on('scroll', function () {
+            let scrollInfo = leftInputView.codemirror.getScrollInfo();
+            rightInputView.codemirror.scrollTo(scrollInfo.left, scrollInfo.top);
+        });
+        rightInputView.codemirror.on('scroll', function () {
+            let scrollInfo = rightInputView.codemirror.getScrollInfo();
+            leftInputView.codemirror.scrollTo(scrollInfo.left, scrollInfo.top);
+        });
+    }
+
+    return {
+        init: init
+    }
+})();

Файлын зөрүү хэтэрхий том тул дарагдсан байна
+ 6 - 0
apps/static/vendor/json-diff/json-patch-duplex.min.js


+ 443 - 0
apps/static/vendor/json-diff/json-source-map.js

@@ -0,0 +1,443 @@
+'use strict';
+
+var escapedChars = {
+    'b': '\b',
+    'f': '\f',
+    'n': '\n',
+    'r': '\r',
+    't': '\t',
+    '"': '"',
+    '/': '/',
+    '\\': '\\'
+};
+
+var A_CODE = 'a'.charCodeAt();
+
+
+function parse(source) {
+    var pointers = {};
+    var line = 0;
+    var column = 0;
+    var pos = 0;
+    return {
+        data: _parse('', true),
+        pointers: pointers
+    };
+
+    function _parse(ptr, topLevel) {
+        whitespace();
+        var data;
+        map(ptr, 'value');
+        var char = getChar();
+        switch (char) {
+            case 't':
+                read('rue');
+                data = true;
+                break;
+            case 'f':
+                read('alse');
+                data = false;
+                break;
+            case 'n':
+                read('ull');
+                data = null;
+                break;
+            case '"':
+                data = parseString();
+                break;
+            case '[':
+                data = parseArray(ptr);
+                break;
+            case '{':
+                data = parseObject(ptr);
+                break;
+            default:
+                backChar();
+                if ('-0123456789'.indexOf(char) >= 0)
+                    data = parseNumber();
+                else
+                    unexpectedToken();
+        }
+        map(ptr, 'valueEnd');
+        whitespace();
+        if (topLevel && pos < source.length) unexpectedToken();
+        return data;
+    }
+
+    function whitespace() {
+        loop: while (pos < source.length) {
+            switch (source[pos]) {
+                case ' ':
+                    column++;
+                    break;
+                case '\t':
+                    column += 4;
+                    break;
+                case '\r':
+                    column = 0;
+                    break;
+                case '\n':
+                    column = 0;
+                    line++;
+                    break;
+                default:
+                    break loop;
+            }
+            pos++;
+        }
+    }
+
+    function parseString() {
+        var str = '';
+        var char;
+        while (true) {
+            char = getChar();
+            if (char == '"') {
+                break;
+            } else if (char == '\\') {
+                char = getChar();
+                if (char in escapedChars)
+                    str += escapedChars[char];
+                else if (char == 'u')
+                    str += getCharCode();
+                else
+                    wasUnexpectedToken();
+            } else {
+                str += char;
+            }
+        }
+        return str;
+    }
+
+    function parseNumber() {
+        var numStr = '';
+        if (source[pos] == '-') numStr += getChar();
+
+        numStr += source[pos] == '0' ? getChar() : getDigits();
+
+        if (source[pos] == '.')
+            numStr += getChar() + getDigits();
+
+        if (source[pos] == 'e' || source[pos] == 'E') {
+            numStr += getChar();
+            if (source[pos] == '+' || source[pos] == '-') numStr += getChar();
+            numStr += getDigits();
+        }
+
+        return +numStr;
+    }
+
+    function parseArray(ptr) {
+        whitespace();
+        var arr = [];
+        var i = 0;
+        if (getChar() == ']') return arr;
+        backChar();
+
+        while (true) {
+            var itemPtr = ptr + '/' + i;
+            arr.push(_parse(itemPtr));
+            whitespace();
+            var char = getChar();
+            if (char == ']') break;
+            if (char != ',') wasUnexpectedToken();
+            whitespace();
+            i++;
+        }
+        return arr;
+    }
+
+    function parseObject(ptr) {
+        whitespace();
+        var obj = {};
+        if (getChar() == '}') return obj;
+        backChar();
+
+        while (true) {
+            var loc = getLoc();
+            if (getChar() != '"') wasUnexpectedToken();
+            var key = parseString();
+            var propPtr = ptr + '/' + escapeJsonPointer(key);
+            mapLoc(propPtr, 'key', loc);
+            map(propPtr, 'keyEnd');
+            whitespace();
+            if (getChar() != ':') wasUnexpectedToken();
+            whitespace();
+            obj[key] = _parse(propPtr);
+            whitespace();
+            var char = getChar();
+            if (char == '}') break;
+            if (char != ',') wasUnexpectedToken();
+            whitespace();
+        }
+        return obj;
+    }
+
+    function read(str) {
+        for (var i = 0; i < str.length; i++)
+            if (getChar() !== str[i]) wasUnexpectedToken();
+    }
+
+    function getChar() {
+        checkUnexpectedEnd();
+        var char = source[pos];
+        pos++;
+        column++; // new line?
+        return char;
+    }
+
+    function backChar() {
+        pos--;
+        column--;
+    }
+
+    function getCharCode() {
+        var count = 4;
+        var code = 0;
+        while (count--) {
+            code <<= 4;
+            var char = getChar().toLowerCase();
+            if (char >= 'a' && char <= 'f')
+                code += char.charCodeAt() - A_CODE + 10;
+            else if (char >= '0' && char <= '9')
+                code += +char;
+            else
+                wasUnexpectedToken();
+        }
+        return String.fromCharCode(code);
+    }
+
+    function getDigits() {
+        var digits = '';
+        while (source[pos] >= '0' && source[pos] <= '9')
+            digits += getChar();
+
+        if (digits.length) return digits;
+        checkUnexpectedEnd();
+        unexpectedToken();
+    }
+
+    function map(ptr, prop) {
+        mapLoc(ptr, prop, getLoc());
+    }
+
+    function mapLoc(ptr, prop, loc) {
+        pointers[ptr] = pointers[ptr] || {};
+        pointers[ptr][prop] = loc;
+    }
+
+    function getLoc() {
+        return {
+            line: line,
+            column: column,
+            pos: pos
+        };
+    }
+
+    function unexpectedToken() {
+        throw new SyntaxError('Unexpected token ' + source[pos] + ' in JSON at position ' + pos);
+    }
+
+    function wasUnexpectedToken() {
+        backChar();
+        unexpectedToken();
+    }
+
+    function checkUnexpectedEnd() {
+        if (pos >= source.length)
+            throw new SyntaxError('Unexpected end of JSON input');
+    }
+};
+
+
+function stringify(data, _, whitespace) {
+    if (!validType(data)) return;
+    var wsLine = 0;
+    var wsPos, wsColumn;
+    switch (typeof whitespace) {
+        case 'number':
+            var len = whitespace > 10 ? 10 : whitespace < 0 ? 0 : Math.floor(whitespace);
+            whitespace = len && repeat(len, ' ');
+            wsPos = len;
+            wsColumn = len;
+            break;
+        case 'string':
+            whitespace = whitespace.slice(0, 10);
+            wsPos = 0;
+            wsColumn = 0;
+            for (var j = 0; j < whitespace.length; j++) {
+                var char = whitespace[j];
+                switch (char) {
+                    case ' ':
+                        wsColumn++;
+                        break;
+                    case '\t':
+                        wsColumn += 4;
+                        break;
+                    case '\r':
+                        wsColumn = 0;
+                        break;
+                    case '\n':
+                        wsColumn = 0;
+                        wsLine++;
+                        break;
+                    default:
+                        throw new Error('whitespace characters not allowed in JSON');
+                }
+                wsPos++;
+            }
+            break;
+        default:
+            whitespace = undefined;
+    }
+
+    var json = '';
+    var pointers = {};
+    var line = 0;
+    var column = 0;
+    var pos = 0;
+    _stringify(data, 0, '');
+    return {
+        json: json,
+        pointers: pointers
+    };
+
+    function _stringify(_data, lvl, ptr) {
+        map(ptr, 'value');
+        switch (typeof _data) {
+            case 'number':
+            case 'boolean':
+                out('' + _data);
+                break;
+            case 'string':
+                out(quoted(_data));
+                break;
+            case 'object':
+                if (_data === null)
+                    out('null');
+                else if (typeof _data.toJSON == 'function')
+                    out(quoted(_data.toJSON()));
+                else if (Array.isArray(_data))
+                    stringifyArray();
+                else
+                    stringifyObject();
+        }
+        map(ptr, 'valueEnd');
+
+        function stringifyArray() {
+            if (_data.length) {
+                out('[');
+                var itemLvl = lvl + 1;
+                for (var i = 0; i < _data.length; i++) {
+                    if (i) out(',');
+                    indent(itemLvl);
+                    var item = validType(_data[i]) ? _data[i] : null;
+                    var itemPtr = ptr + '/' + i;
+                    _stringify(item, itemLvl, itemPtr);
+                }
+                indent(lvl);
+                out(']');
+            } else {
+                out('[]');
+            }
+        }
+
+        function stringifyObject() {
+            var keys = Object.keys(_data);
+            if (keys.length) {
+                out('{');
+                var propLvl = lvl + 1;
+                for (var i = 0; i < keys.length; i++) {
+                    var key = keys[i];
+                    var value = _data[key];
+                    if (validType(value)) {
+                        if (i) out(',');
+                        var propPtr = ptr + '/' + escapeJsonPointer(key);
+                        indent(propLvl);
+                        map(propPtr, 'key');
+                        out(quoted(key));
+                        map(propPtr, 'keyEnd');
+                        out(':');
+                        if (whitespace) out(' ');
+                        _stringify(value, propLvl, propPtr);
+                    }
+                }
+                indent(lvl);
+                out('}');
+            } else {
+                out('{}');
+            }
+        }
+    }
+
+    function out(str) {
+        column += str.length;
+        pos += str.length;
+        json += str;
+    }
+
+    function indent(lvl) {
+        if (whitespace) {
+            json += '\n' + repeat(lvl, whitespace);
+            line++;
+            column = 0;
+            while (lvl--) {
+                if (wsLine) {
+                    line += wsLine;
+                    column = wsColumn;
+                } else {
+                    column += wsColumn;
+                }
+                pos += wsPos;
+            }
+            pos += 1; // \n character
+        }
+    }
+
+    function map(ptr, prop) {
+        pointers[ptr] = pointers[ptr] || {};
+        pointers[ptr][prop] = {
+            line: line,
+            column: column,
+            pos: pos
+        };
+    }
+
+    function repeat(n, str) {
+        return Array(n + 1).join(str);
+    }
+};
+
+
+var VALID_TYPES = ['number', 'boolean', 'string', 'object'];
+
+function validType(data) {
+    return VALID_TYPES.indexOf(typeof data) >= 0;
+}
+
+
+var ESC_QUOTE = /"|\\/g;
+var ESC_B = /[\b]/g;
+var ESC_F = /\f/g;
+var ESC_N = /\n/g;
+var ESC_R = /\r/g;
+var ESC_T = /\t/g;
+
+function quoted(str) {
+    str = str.replace(ESC_QUOTE, '\\$&')
+        .replace(ESC_F, '\\f')
+        .replace(ESC_B, '\\b')
+        .replace(ESC_N, '\\n')
+        .replace(ESC_R, '\\r')
+        .replace(ESC_T, '\\t');
+    return '"' + str + '"';
+}
+
+
+var ESC_0 = /~/g;
+var ESC_1 = /\//g;
+
+function escapeJsonPointer(str) {
+    return str.replace(ESC_0, '~0')
+        .replace(ESC_1, '~1');
+}

Энэ ялгаанд хэт олон файл өөрчлөгдсөн тул зарим файлыг харуулаагүй болно