├── src ├── css │ ├── css-editor-core.css │ ├── jsonvue-error.css │ ├── css-editor.css │ ├── options.css │ ├── jsonvue.css │ └── jsonvue-core.css ├── resources │ ├── jsonvue16.png │ ├── jsonvue48.png │ └── jsonvue128.png ├── manifest.json ├── css-editor.html ├── codemirror │ ├── LICENSE │ ├── codemirror.css │ └── css.js ├── options.html └── js │ ├── pages │ ├── options.js │ └── css-editor.js │ ├── background.js │ ├── workers │ ├── formatter.js │ ├── linter.js │ └── parser.js │ └── content.js ├── README.md ├── .eslintrc.js └── LICENSE.txt /src/css/css-editor-core.css: -------------------------------------------------------------------------------- 1 | body { 2 | font-family: initial; 3 | font-size: initial; 4 | } -------------------------------------------------------------------------------- /src/resources/jsonvue16.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gildas-lormeau/JSONVue/HEAD/src/resources/jsonvue16.png -------------------------------------------------------------------------------- /src/resources/jsonvue48.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gildas-lormeau/JSONVue/HEAD/src/resources/jsonvue48.png -------------------------------------------------------------------------------- /src/resources/jsonvue128.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gildas-lormeau/JSONVue/HEAD/src/resources/jsonvue128.png -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # JSONVue 2 | Fork of JSONView for Chromium-based browsers 3 | 4 | This is the source code of JSONVue on Chrome, see https://chrome.google.com/webstore/detail/jsonvue/chklaanhfefbnpoihckbnefhakgolnmc 5 | -------------------------------------------------------------------------------- /.eslintrc.js: -------------------------------------------------------------------------------- 1 | /* global module */ 2 | 3 | module.exports = { 4 | "env": { 5 | "es6": true, 6 | "node": false, 7 | "browser": false 8 | }, 9 | "globals": { 10 | "console": true 11 | }, 12 | "extends": "eslint:recommended", 13 | "parserOptions": { 14 | "ecmaVersion": 2017, 15 | "sourceType": "module" 16 | }, 17 | "ignorePatterns": [ 18 | "dist/" 19 | ], 20 | "rules": { 21 | "indent": [ 22 | "error", 23 | "tab", { 24 | "SwitchCase": 1 25 | } 26 | ], 27 | "linebreak-style": [ 28 | "error", 29 | "unix" 30 | ], 31 | "quotes": [ 32 | "error", 33 | "double" 34 | ], 35 | "semi": [ 36 | "error", 37 | "always" 38 | ], 39 | "no-console": [ 40 | "warn" 41 | ] 42 | } 43 | }; -------------------------------------------------------------------------------- /src/css/jsonvue-error.css: -------------------------------------------------------------------------------- 1 | .error-position { 2 | display: inline-block; 3 | vertical-align: middle; 4 | } 5 | 6 | .error-position .error-icon { 7 | color: fireBrick; 8 | width: 21px; 9 | display: inline-block; 10 | text-align: center; 11 | } 12 | 13 | .container { 14 | position: absolute; 15 | left: 0px; 16 | width: 100%; 17 | } 18 | 19 | .content { 20 | top: 3px; 21 | width: 30em; 22 | margin: 0 auto; 23 | background-color: #FEE; 24 | border: 2px solid #933; 25 | color: #933; 26 | padding: 20px; 27 | position: relative; 28 | box-shadow: #888 3px 3px 5px; 29 | } 30 | 31 | .close-error::after { 32 | content: "✖"; 33 | width: 16px; 34 | position: absolute; 35 | top: 3px; 36 | right: 3px; 37 | cursor: pointer; 38 | } -------------------------------------------------------------------------------- /src/manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "JSONVue", 3 | "icons": { 4 | "16": "resources/jsonvue16.png", 5 | "48": "resources/jsonvue48.png", 6 | "128": "resources/jsonvue128.png" 7 | }, 8 | "version": "0.2.3", 9 | "description": "Validate and view JSON documents", 10 | "options_ui": { 11 | "browser_style": true, 12 | "page": "options.html", 13 | "open_in_tab": false 14 | }, 15 | "background": { 16 | "service_worker": "js/background.js" 17 | }, 18 | "content_scripts": [ 19 | { 20 | "matches": [ 21 | "http://*/*", 22 | "https://*/*", 23 | "ftp://*/*", 24 | "file:///*" 25 | ], 26 | "js": [ 27 | "js/content.js" 28 | ], 29 | "run_at": "document_end", 30 | "all_frames": true 31 | } 32 | ], 33 | "permissions": [ 34 | "contextMenus", 35 | "storage" 36 | ], 37 | "manifest_version": 3 38 | } -------------------------------------------------------------------------------- /src/css-editor.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | Style editor - JSONVue 7 | 8 | 9 | 10 | 11 | 12 |
13 |
14 |

Style editor

15 |
16 | 17 | 18 |
19 |
20 |
21 |
22 |
23 |
CSS
24 | 25 |
26 |
27 |
Preview
28 | 29 |
30 |
31 |
32 | 33 | 34 | 35 | 36 | 37 | -------------------------------------------------------------------------------- /src/codemirror/LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (C) 2017 by Marijn Haverbeke and others 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in 13 | all copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 21 | THE SOFTWARE. 22 | -------------------------------------------------------------------------------- /LICENSE.txt: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Original work Copyright (c) 2009 Benjamin Hollis 4 | Modified work Copyright (c) 2012 Gildas Lormeau 5 | 6 | Permission is hereby granted, free of charge, to any person obtaining a copy 7 | of this software and associated documentation files (the "Software"), to deal 8 | in the Software without restriction, including without limitation the rights 9 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | copies of the Software, and to permit persons to whom the Software is 11 | furnished to do so, subject to the following conditions: 12 | 13 | The above copyright notice and this permission notice shall be included in 14 | all copies or substantial portions of the Software. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 22 | THE SOFTWARE. -------------------------------------------------------------------------------- /src/css/css-editor.css: -------------------------------------------------------------------------------- 1 | body { 2 | font-size: 10pt; 3 | font-family: sans-serif; 4 | } 5 | 6 | .container { 7 | margin-left: auto; 8 | margin-right: auto; 9 | padding-left: 10px; 10 | padding-right: 10px; 11 | max-width: 1200px; 12 | } 13 | 14 | header { 15 | display: flex; 16 | align-items: center; 17 | } 18 | 19 | h2 { 20 | flex: 1; 21 | } 22 | 23 | main { 24 | display: flex; 25 | } 26 | 27 | .panel { 28 | display: flex; 29 | flex-direction: column; 30 | width: 50%; 31 | margin: 5px; 32 | } 33 | 34 | .panel-label { 35 | padding-left: 2px; 36 | padding-bottom: 5px; 37 | } 38 | 39 | #previewer, .CodeMirror { 40 | background-color: white; 41 | border-width: 1px; 42 | border-style: solid; 43 | border-color: #aaa; 44 | height: 500px; 45 | } 46 | 47 | @media (orientation: portrait) { 48 | main { 49 | flex-direction: column; 50 | } 51 | 52 | .panel { 53 | width: 100%; 54 | } 55 | 56 | #previewer, .CodeMirror { 57 | height: 40vh; 58 | } 59 | 60 | #previewer { 61 | max-height: 300px; 62 | } 63 | } 64 | 65 | @media (prefers-color-scheme: dark) { 66 | body { 67 | background-color: #282828; 68 | color: lightgrey; 69 | } 70 | 71 | .CodeMirror { 72 | filter: invert(1); 73 | background-color: #d7d7d7; 74 | } 75 | } -------------------------------------------------------------------------------- /src/css/options.css: -------------------------------------------------------------------------------- 1 | body { 2 | font-size: 13px; 3 | } 4 | 5 | form { 6 | margin-top: 0px; 7 | margin-left: auto; 8 | margin-right: auto; 9 | } 10 | 11 | fieldset { 12 | border-width: 0px; 13 | } 14 | 15 | section { 16 | display: flex; 17 | align-items: end; 18 | } 19 | 20 | section.vertical { 21 | align-items: flex-start; 22 | flex-direction: column; 23 | } 24 | 25 | section:last-child { 26 | flex-direction: column; 27 | margin-top: 20px; 28 | } 29 | 30 | section label { 31 | flex-grow: 1; 32 | user-select: none; 33 | margin-bottom: 5px; 34 | } 35 | 36 | section:not(:first-child) label { 37 | margin-top: 10px; 38 | } 39 | 40 | input { 41 | margin-bottom: 5px; 42 | } 43 | 44 | legend { 45 | font-size: 15px; 46 | font-weight: 700; 47 | padding-left: 10px; 48 | padding-right: 10px; 49 | margin-bottom: 5px; 50 | } 51 | 52 | input[type=checkbox] { 53 | margin-right: 0px; 54 | } 55 | 56 | input[type=number] { 57 | width: 35px; 58 | text-align: right; 59 | } 60 | 61 | input[type=text] { 62 | width: 100%; 63 | width: calc(100% - 8px); 64 | } 65 | 66 | button { 67 | max-height: 21px; 68 | } 69 | 70 | .icon { 71 | width: 20px; 72 | padding-right: 5px; 73 | vertical-align: bottom; 74 | } 75 | 76 | @media (prefers-color-scheme: dark) { 77 | body { 78 | background-color: #292a2d; 79 | color: lightgrey; 80 | } 81 | } -------------------------------------------------------------------------------- /src/options.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | JSONVue options 7 | 8 | 9 | 10 | 11 |
12 |
13 |
14 | 15 | 16 |
17 |
18 | 19 | 20 |
21 |
22 | 23 | 24 |
25 |
26 | 27 | 28 |
29 |
30 | 31 | 32 |
33 |
34 | 35 | 36 |
37 |
38 | 39 |
40 |
41 |
42 | 43 | 44 | 45 | -------------------------------------------------------------------------------- /src/css/jsonvue.css: -------------------------------------------------------------------------------- 1 | body { 2 | white-space: pre-wrap; 3 | word-break: break-word; 4 | font-family: monospace; 5 | } 6 | 7 | .property { 8 | font-weight: bold; 9 | } 10 | 11 | .type-null { 12 | color: gray; 13 | } 14 | 15 | .type-boolean { 16 | color: firebrick; 17 | } 18 | 19 | .type-number { 20 | color: blue; 21 | } 22 | 23 | .type-string { 24 | color: green; 25 | } 26 | 27 | .callback-function { 28 | color: gray; 29 | } 30 | 31 | .ellipsis::after { 32 | content: " … "; 33 | } 34 | 35 | .collapser { 36 | position: absolute; 37 | top: .1em; 38 | left: -2ch; 39 | cursor: default; 40 | user-select: none; 41 | padding-inline: 0.5ch; 42 | } 43 | 44 | .collapser::after { 45 | cursor: pointer; 46 | content: "-"; 47 | } 48 | 49 | .collapsed > .collapser::after { 50 | content: "+"; 51 | } 52 | 53 | .collapsible { 54 | margin-inline-start: 4ch; 55 | } 56 | 57 | .collapsible .collapsible::before { 58 | content: ""; 59 | position: absolute; 60 | left: 0.5ch; 61 | top: 1.8em; 62 | height: calc(100% - 3.6em); 63 | border: 0; 64 | border-inline-start: #d3d3d3 0.2ch dashed; 65 | } 66 | 67 | .hoverable { 68 | display: inline-block; 69 | padding: 0.1em; 70 | transition: background-color 0.2s ease-out 0s; 71 | } 72 | 73 | .hovered { 74 | background-color: rgba(235, 238, 249, 1); 75 | transition-delay: 0.2s; 76 | } 77 | 78 | .selected { 79 | outline: dotted 0.1ch; 80 | } 81 | 82 | @media (prefers-color-scheme: dark) { 83 | body { 84 | background-color: #282828; 85 | color: lightgrey; 86 | } 87 | 88 | .hovered { 89 | background-color: #424242; 90 | } 91 | 92 | .type-boolean { 93 | color: tomato; 94 | } 95 | 96 | .type-number { 97 | color: dodgerblue; 98 | } 99 | 100 | .type-string { 101 | color: yellowgreen; 102 | } 103 | 104 | a { 105 | color: dodgerblue; 106 | } 107 | 108 | .collapsible .collapsible::before { 109 | border-color: #484848; 110 | } 111 | } -------------------------------------------------------------------------------- /src/js/pages/options.js: -------------------------------------------------------------------------------- 1 | /* global document, chrome, open */ 2 | 3 | const injectInFrameInput = document.getElementById("injectInFrameInput"); 4 | const supportBigIntInput = document.getElementById("supportBigIntInput"); 5 | const addContextMenuInput = document.getElementById("addContextMenuInput"); 6 | const maxDepthLevelExpandedInput = document.getElementById("maxDepthLevelExpandedInput"); 7 | const jsonPrefixInput = document.getElementById("jsonPrefixInput"); 8 | document.getElementById("openEditorButton").addEventListener("click", event => { 9 | open("css-editor.html", "jsonvue-css-editor"); 10 | event.stopPropagation(); 11 | }, false); 12 | document.getElementById("resetButton").addEventListener("click", event => { 13 | chrome.runtime.sendMessage({ resetOptions: true }, init); 14 | event.stopPropagation(); 15 | }, false); 16 | init(); 17 | 18 | function init() { 19 | chrome.runtime.sendMessage({ getOptions: true }, options => { 20 | injectInFrameInput.checked = options.injectInFrame; 21 | supportBigIntInput.checked = options.supportBigInt; 22 | addContextMenuInput.checked = options.addContextMenu; 23 | maxDepthLevelExpandedInput.valueAsNumber = options.maxDepthLevelExpanded; 24 | jsonPrefixInput.value = options.jsonPrefix; 25 | injectInFrameInput.onchange = () => { 26 | options.injectInFrame = injectInFrameInput.checked; 27 | chrome.runtime.sendMessage({ setSetting: true, name: "options", value: options }); 28 | }; 29 | supportBigIntInput.onchange = () => { 30 | options.supportBigInt = supportBigIntInput.checked; 31 | chrome.runtime.sendMessage({ setSetting: true, name: "options", value: options }); 32 | }; 33 | addContextMenuInput.onchange = () => { 34 | options.addContextMenu = addContextMenuInput.checked; 35 | chrome.runtime.sendMessage({ setSetting: true, refreshMenuEntry: true, name: "options", value: options }); 36 | }; 37 | maxDepthLevelExpandedInput.onchange = () => { 38 | options.maxDepthLevelExpanded = maxDepthLevelExpandedInput.valueAsNumber; 39 | chrome.runtime.sendMessage({ setSetting: true, name: "options", value: options }); 40 | }; 41 | jsonPrefixInput.onchange = () => { 42 | options.jsonPrefix = jsonPrefixInput.value; 43 | chrome.runtime.sendMessage({ setSetting: true, name: "options", value: options }); 44 | }; 45 | }); 46 | } -------------------------------------------------------------------------------- /src/css/jsonvue-core.css: -------------------------------------------------------------------------------- 1 | body { 2 | margin-bottom: 23px; 3 | } 4 | 5 | ul { 6 | list-style-type: none; 7 | padding: 0; 8 | margin: 0 0 0 2em; 9 | } 10 | 11 | li { 12 | position: relative; 13 | } 14 | 15 | .hoverable { 16 | display: inline-block; 17 | } 18 | 19 | .collapsed>.collapsible { 20 | display: none; 21 | } 22 | 23 | .ellipsis { 24 | display: none; 25 | } 26 | 27 | .collapsed>.ellipsis { 28 | display: inherit; 29 | } 30 | 31 | .collapser { 32 | position: absolute; 33 | top: .1em; 34 | left: -1.4em; 35 | cursor: default; 36 | } 37 | 38 | .collapser::after { 39 | cursor: pointer; 40 | } 41 | 42 | .collapsible .collapsible::before { 43 | top: 1.8em; 44 | } 45 | 46 | .status { 47 | position: fixed; 48 | left: 0; 49 | bottom: 0; 50 | border-color: #c2c2c2; 51 | border-top-width: 1px; 52 | border-right-width: 1px; 53 | border-bottom-width: 0px; 54 | border-left-width: 0px; 55 | border-style: solid; 56 | border-top-right-radius: 4px; 57 | height: 16px; 58 | padding-top: 2px; 59 | padding-bottom: 2px; 60 | padding-right: 7px; 61 | padding-left: 4px; 62 | font-family: sans-serif; 63 | font-size: 12px; 64 | opacity: 0; 65 | background-color: #d2d2f6; 66 | color: #696969; 67 | transition: opacity .2s ease-out; 68 | user-select: none; 69 | } 70 | 71 | .status:not(:empty) { 72 | opacity: 1; 73 | } 74 | 75 | .toolbox { 76 | padding-right: 3px; 77 | font-family: sans-serif; 78 | font-size: 15px; 79 | opacity: .25; 80 | background-color: #d2d2f6; 81 | position: fixed; 82 | right: 0px; 83 | top: 0px; 84 | border-color: #c2c2c2; 85 | border-bottom-width: 1px; 86 | border-left-width: 1px; 87 | border-top-width: 0px; 88 | border-right-width: 0px; 89 | border-style: solid; 90 | border-bottom-left-radius: 4px; 91 | padding-bottom: 3px; 92 | transition: opacity .2s ease-out; 93 | cursor: default; 94 | user-select: none; 95 | padding-left: 2px; 96 | } 97 | 98 | .toolbox:hover { 99 | opacity: 1; 100 | } 101 | 102 | .toolbox>* { 103 | display: inline-block; 104 | padding-left: 3px; 105 | padding-right: 3px; 106 | cursor: pointer; 107 | min-width: 14px; 108 | text-align: center; 109 | } 110 | 111 | .toolbox>a { 112 | font-size: 13px; 113 | } 114 | 115 | @media (prefers-color-scheme: dark) { 116 | .toolbox { 117 | background-color: #484848; 118 | border-color: #242424; 119 | } 120 | 121 | .status { 122 | background-color: #484848; 123 | border-color: #242424; 124 | color: lightgrey; 125 | } 126 | } -------------------------------------------------------------------------------- /src/js/pages/css-editor.js: -------------------------------------------------------------------------------- 1 | /* global document, chrome, fetch, setTimeout, clearTimeout, CodeMirror */ 2 | 3 | const SAMPLE_PART1 = "
{}
"; 5 | const PASSIVE_KEYS = ["ArrowDown", "ArrowUp", "ArrowLeft", "ArrowRight", "End", "Home", "PageDown", "PageUp", "ControlLeft", "ControlRight", "AltLeft", "ShiftLeft", "ShiftRight", "Insert"]; 6 | 7 | const editor = document.getElementById("editor"); 8 | const resetButton = document.getElementById("reset-button"); 9 | const saveButton = document.getElementById("save-button"); 10 | const previewer = document.getElementById("previewer").contentWindow; 11 | 12 | let timeoutOnKey; 13 | const codemirror = CodeMirror.fromTextArea(editor); 14 | codemirror.on("keyup", onKeyUpEditor); 15 | resetButton.addEventListener("click", resetTheme, false); 16 | saveButton.addEventListener("click", () => chrome.runtime.sendMessage({ setSetting: true, name: "theme", value: codemirror.getValue() }), false); 17 | chrome.runtime.sendMessage({ getTheme: true }, init); 18 | 19 | function init(theme) { 20 | codemirror.setValue(theme); 21 | updatePreview(); 22 | } 23 | 24 | async function resetTheme() { 25 | codemirror.setValue(await (await fetch("css/jsonvue.css")).text()); 26 | updatePreview(); 27 | } 28 | 29 | function onKeyUpEditor(editor, event) { 30 | if (PASSIVE_KEYS.indexOf(event.code) == -1) { 31 | if (timeoutOnKey) { 32 | clearTimeout(timeoutOnKey); 33 | } 34 | timeoutOnKey = setTimeout(updatePreview, 500); 35 | } 36 | } 37 | 38 | function updatePreview() { 39 | previewer.document.open(); 40 | previewer.document.write(SAMPLE_PART1); 41 | previewer.document.write(codemirror.getValue()); 42 | previewer.document.write(SAMPLE_PART2); 43 | previewer.document.close(); 44 | } -------------------------------------------------------------------------------- /src/js/background.js: -------------------------------------------------------------------------------- 1 | /* global chrome, fetch, Worker, localStorage, importScripts, formatter, linter, TextEncoder, crypto */ 2 | 3 | const WORKER_API_AVAILABLE = typeof Worker != "undefined"; 4 | const LOCAL_STORAGE_API_AVAILABLE = typeof localStorage != "undefined"; 5 | const MENU_ID_COPY_PATH = "copy-path"; 6 | const MENU_ID_COPY_VALUE = "copy-value"; 7 | const MENU_ID_COPY_JSON_VALUE = "copy-json-value"; 8 | const DEFAULT_OPTIONS = { 9 | maxDepthLevelExpanded: 0, 10 | addContextMenu: true, 11 | jsonPrefix: "^\\)]}',|for\\s*\\(;;\\);|while\\s*(1);" 12 | }; 13 | const LEGACY_STYLESHEET_HASH = "[217,103,31,97,255,43,250,60,65,196,134,101,148,173,69,129,51,72,223,43]"; 14 | 15 | if (!WORKER_API_AVAILABLE) { 16 | importScripts("/js/workers/formatter.js"); 17 | importScripts("/js/workers/linter.js"); 18 | } 19 | 20 | let extensionReady; 21 | chrome.runtime.onMessage.addListener((message, sender, sendResponse) => { 22 | onMessage(message).then(sendResponse); 23 | return true; 24 | }); 25 | chrome.contextMenus.onClicked.addListener((info, tab) => chrome.tabs.sendMessage(tab.id, { 26 | copy: true, 27 | type: info.menuItemId 28 | })); 29 | addMenuEntry(true); 30 | init(); 31 | 32 | async function init() { 33 | extensionReady = migrateSettings(); 34 | await initDefaultSettings(await getSettings()); 35 | await refreshMenuEntry(); 36 | } 37 | 38 | async function migrateSettings() { 39 | const promises = []; 40 | if (LOCAL_STORAGE_API_AVAILABLE) { 41 | if (localStorage.options) { 42 | promises.push(new Promise(resolve => { 43 | chrome.storage.local.set({ options: JSON.parse(localStorage.options) }, () => resolve()); 44 | delete localStorage.options; 45 | })); 46 | } 47 | if (localStorage.theme) { 48 | promises.push(new Promise(resolve => { 49 | chrome.storage.local.set({ theme: localStorage.theme }, () => resolve()); 50 | delete localStorage.theme; 51 | })); 52 | } 53 | } 54 | await Promise.all(promises); 55 | } 56 | 57 | async function initDefaultSettings(settings) { 58 | let optionsChanged; 59 | if (!settings.options) { 60 | settings.options = {}; 61 | optionsChanged = true; 62 | } 63 | const options = settings.options; 64 | if (typeof options.maxDepthLevelExpanded == "undefined") { 65 | options.maxDepthLevelExpanded = DEFAULT_OPTIONS.maxDepthLevelExpanded; 66 | optionsChanged = true; 67 | } 68 | if (typeof options.addContextMenu == "undefined") { 69 | options.addContextMenu = DEFAULT_OPTIONS.addContextMenu; 70 | optionsChanged = true; 71 | } 72 | if (typeof options.jsonPrefix == "undefined") { 73 | options.jsonPrefix = DEFAULT_OPTIONS.jsonPrefix; 74 | optionsChanged = true; 75 | } 76 | if (optionsChanged) { 77 | await setSetting("options", options); 78 | } 79 | if (settings.theme) { 80 | const encoder = new TextEncoder(); 81 | const hash = JSON.stringify(Array.from(new Uint8Array(await crypto.subtle.digest("SHA-1", encoder.encode(settings.theme))))); 82 | if (hash == LEGACY_STYLESHEET_HASH) { 83 | await setSetting("theme", await getDefaultTheme()); 84 | } 85 | } else { 86 | await setSetting("theme", await getDefaultTheme()); 87 | } 88 | } 89 | 90 | async function onMessage(message) { 91 | let result; 92 | if (message.getTheme) { 93 | result = (await getSettings()).theme; 94 | } 95 | if (message.getOptions) { 96 | result = (await getSettings()).options; 97 | } 98 | if (message.setSetting) { 99 | await setSetting(message.name, message.value); 100 | } 101 | if (message.jsonToHTML) { 102 | result = formatHTML(message.json, message.functionName, message.supportBigInt); 103 | } 104 | if (message.resetOptions) { 105 | await setSetting("options", DEFAULT_OPTIONS); 106 | } 107 | if (message.refreshMenuEntry) { 108 | await refreshMenuEntry(); 109 | } 110 | return result || {}; 111 | } 112 | 113 | async function formatHTML(json, functionName, supportBigInt) { 114 | const result = await Promise.all([formatHTMLAsync(json, functionName, supportBigInt), getContentStylesheet()]); 115 | result[0].stylesheet = result[1]; 116 | return result[0]; 117 | } 118 | 119 | async function formatHTMLAsync(json, functionName, supportBigInt) { 120 | if (WORKER_API_AVAILABLE) { 121 | const response = await executeWorker("js/workers/formatter.js", { json: json, functionName, supportBigInt }); 122 | if (response.html) { 123 | return { html: response.html }; 124 | } 125 | if (response.error) { 126 | const response = await executeWorker("js/workers/linter.js", json); 127 | return { error: response.error, loc: response.loc }; 128 | } 129 | } else { 130 | try { 131 | return { html: formatter.format(json, functionName, supportBigInt) }; 132 | } catch (error) { 133 | const response = linter.lint(json); 134 | return { error: response.error, loc: response.loc }; 135 | } 136 | } 137 | } 138 | 139 | function executeWorker(path, message) { 140 | return new Promise((resolve, reject) => { 141 | const worker = new Worker(path); 142 | worker.addEventListener("message", onMessage, false); 143 | worker.addEventListener("error", onError, false); 144 | worker.postMessage(message); 145 | 146 | function onMessage(event) { 147 | worker.removeEventListener("message", onMessage, false); 148 | worker.removeEventListener("error", onError, false); 149 | worker.terminate(); 150 | resolve(event.data); 151 | } 152 | 153 | function onError(event) { 154 | reject(event.detail.error); 155 | } 156 | }); 157 | } 158 | 159 | async function refreshMenuEntry() { 160 | const settings = await getSettings(); 161 | chrome.contextMenus.removeAll(); 162 | if (settings.options.addContextMenu) { 163 | addMenuEntry(); 164 | } 165 | } 166 | 167 | function addMenuEntry(removeAll) { 168 | if (removeAll) { 169 | chrome.contextMenus.removeAll(); 170 | } 171 | chrome.contextMenus.create({ 172 | id: MENU_ID_COPY_PATH, 173 | title: "Copy path", 174 | contexts: ["page", "selection", "link"] 175 | }); 176 | chrome.contextMenus.create({ 177 | id: MENU_ID_COPY_VALUE, 178 | title: "Copy value", 179 | contexts: ["page", "selection", "link"] 180 | }); 181 | chrome.contextMenus.create({ 182 | id: MENU_ID_COPY_JSON_VALUE, 183 | title: "Copy JSON value", 184 | contexts: ["page", "selection", "link"] 185 | }); 186 | } 187 | 188 | async function getDefaultTheme() { 189 | return (await fetch("/css/jsonvue.css")).text(); 190 | } 191 | 192 | async function getContentStylesheet() { 193 | return (await Promise.all([ 194 | (await fetch("/css/jsonvue-error.css")).text(), 195 | (await fetch("/css/jsonvue-core.css")).text(), 196 | (await getSettings()).theme 197 | ])).join("\n"); 198 | } 199 | 200 | async function getSettings() { 201 | await extensionReady; 202 | return new Promise(resolve => chrome.storage.local.get(["options", "theme"], result => resolve(result))); 203 | } 204 | 205 | async function setSetting(name, value) { 206 | await extensionReady; 207 | return new Promise(resolve => chrome.storage.local.set({ [name]: value }, result => resolve(result))); 208 | } -------------------------------------------------------------------------------- /src/codemirror/codemirror.css: -------------------------------------------------------------------------------- 1 | /* BASICS */ 2 | 3 | .CodeMirror { 4 | /* Set height, width, borders, and global font properties here */ 5 | font-family: monospace; 6 | height: 300px; 7 | color: black; 8 | direction: ltr; 9 | } 10 | 11 | /* PADDING */ 12 | 13 | .CodeMirror-lines { 14 | padding: 4px 0; /* Vertical padding around content */ 15 | } 16 | .CodeMirror pre.CodeMirror-line, 17 | .CodeMirror pre.CodeMirror-line-like { 18 | padding: 0 4px; /* Horizontal padding of content */ 19 | } 20 | 21 | .CodeMirror-scrollbar-filler, .CodeMirror-gutter-filler { 22 | background-color: white; /* The little square between H and V scrollbars */ 23 | } 24 | 25 | /* GUTTER */ 26 | 27 | .CodeMirror-gutters { 28 | border-right: 1px solid #ddd; 29 | background-color: #f7f7f7; 30 | white-space: nowrap; 31 | } 32 | .CodeMirror-linenumbers {} 33 | .CodeMirror-linenumber { 34 | padding: 0 3px 0 5px; 35 | min-width: 20px; 36 | text-align: right; 37 | color: #999; 38 | white-space: nowrap; 39 | } 40 | 41 | .CodeMirror-guttermarker { color: black; } 42 | .CodeMirror-guttermarker-subtle { color: #999; } 43 | 44 | /* CURSOR */ 45 | 46 | .CodeMirror-cursor { 47 | border-left: 1px solid black; 48 | border-right: none; 49 | width: 0; 50 | } 51 | /* Shown when moving in bi-directional text */ 52 | .CodeMirror div.CodeMirror-secondarycursor { 53 | border-left: 1px solid silver; 54 | } 55 | .cm-fat-cursor .CodeMirror-cursor { 56 | width: auto; 57 | border: 0 !important; 58 | background: #7e7; 59 | } 60 | .cm-fat-cursor div.CodeMirror-cursors { 61 | z-index: 1; 62 | } 63 | .cm-fat-cursor .CodeMirror-line::selection, 64 | .cm-fat-cursor .CodeMirror-line > span::selection, 65 | .cm-fat-cursor .CodeMirror-line > span > span::selection { background: transparent; } 66 | .cm-fat-cursor .CodeMirror-line::-moz-selection, 67 | .cm-fat-cursor .CodeMirror-line > span::-moz-selection, 68 | .cm-fat-cursor .CodeMirror-line > span > span::-moz-selection { background: transparent; } 69 | .cm-fat-cursor { caret-color: transparent; } 70 | @-moz-keyframes blink { 71 | 0% {} 72 | 50% { background-color: transparent; } 73 | 100% {} 74 | } 75 | @-webkit-keyframes blink { 76 | 0% {} 77 | 50% { background-color: transparent; } 78 | 100% {} 79 | } 80 | @keyframes blink { 81 | 0% {} 82 | 50% { background-color: transparent; } 83 | 100% {} 84 | } 85 | 86 | /* Can style cursor different in overwrite (non-insert) mode */ 87 | .CodeMirror-overwrite .CodeMirror-cursor {} 88 | 89 | .cm-tab { display: inline-block; text-decoration: inherit; } 90 | 91 | .CodeMirror-rulers { 92 | position: absolute; 93 | left: 0; right: 0; top: -50px; bottom: 0; 94 | overflow: hidden; 95 | } 96 | .CodeMirror-ruler { 97 | border-left: 1px solid #ccc; 98 | top: 0; bottom: 0; 99 | position: absolute; 100 | } 101 | 102 | /* DEFAULT THEME */ 103 | 104 | .cm-s-default .cm-header {color: blue;} 105 | .cm-s-default .cm-quote {color: #090;} 106 | .cm-negative {color: #d44;} 107 | .cm-positive {color: #292;} 108 | .cm-header, .cm-strong {font-weight: bold;} 109 | .cm-em {font-style: italic;} 110 | .cm-link {text-decoration: underline;} 111 | .cm-strikethrough {text-decoration: line-through;} 112 | 113 | .cm-s-default .cm-keyword {color: #708;} 114 | .cm-s-default .cm-atom {color: #219;} 115 | .cm-s-default .cm-number {color: #164;} 116 | .cm-s-default .cm-def {color: #00f;} 117 | .cm-s-default .cm-variable, 118 | .cm-s-default .cm-punctuation, 119 | .cm-s-default .cm-property, 120 | .cm-s-default .cm-operator {} 121 | .cm-s-default .cm-variable-2 {color: #05a;} 122 | .cm-s-default .cm-variable-3, .cm-s-default .cm-type {color: #085;} 123 | .cm-s-default .cm-comment {color: #a50;} 124 | .cm-s-default .cm-string {color: #a11;} 125 | .cm-s-default .cm-string-2 {color: #f50;} 126 | .cm-s-default .cm-meta {color: #555;} 127 | .cm-s-default .cm-qualifier {color: #555;} 128 | .cm-s-default .cm-builtin {color: #30a;} 129 | .cm-s-default .cm-bracket {color: #997;} 130 | .cm-s-default .cm-tag {color: #170;} 131 | .cm-s-default .cm-attribute {color: #00c;} 132 | .cm-s-default .cm-hr {color: #999;} 133 | .cm-s-default .cm-link {color: #00c;} 134 | 135 | .cm-s-default .cm-error {color: #f00;} 136 | .cm-invalidchar {color: #f00;} 137 | 138 | .CodeMirror-composing { border-bottom: 2px solid; } 139 | 140 | /* Default styles for common addons */ 141 | 142 | div.CodeMirror span.CodeMirror-matchingbracket {color: #0b0;} 143 | div.CodeMirror span.CodeMirror-nonmatchingbracket {color: #a22;} 144 | .CodeMirror-matchingtag { background: rgba(255, 150, 0, .3); } 145 | .CodeMirror-activeline-background {background: #e8f2ff;} 146 | 147 | /* STOP */ 148 | 149 | /* The rest of this file contains styles related to the mechanics of 150 | the editor. You probably shouldn't touch them. */ 151 | 152 | .CodeMirror { 153 | position: relative; 154 | overflow: hidden; 155 | background: white; 156 | } 157 | 158 | .CodeMirror-scroll { 159 | overflow: scroll !important; /* Things will break if this is overridden */ 160 | /* 50px is the magic margin used to hide the element's real scrollbars */ 161 | /* See overflow: hidden in .CodeMirror */ 162 | margin-bottom: -50px; margin-right: -50px; 163 | padding-bottom: 50px; 164 | height: 100%; 165 | outline: none; /* Prevent dragging from highlighting the element */ 166 | position: relative; 167 | } 168 | .CodeMirror-sizer { 169 | position: relative; 170 | border-right: 50px solid transparent; 171 | } 172 | 173 | /* The fake, visible scrollbars. Used to force redraw during scrolling 174 | before actual scrolling happens, thus preventing shaking and 175 | flickering artifacts. */ 176 | .CodeMirror-vscrollbar, .CodeMirror-hscrollbar, .CodeMirror-scrollbar-filler, .CodeMirror-gutter-filler { 177 | position: absolute; 178 | z-index: 6; 179 | display: none; 180 | outline: none; 181 | } 182 | .CodeMirror-vscrollbar { 183 | right: 0; top: 0; 184 | overflow-x: hidden; 185 | overflow-y: scroll; 186 | } 187 | .CodeMirror-hscrollbar { 188 | bottom: 0; left: 0; 189 | overflow-y: hidden; 190 | overflow-x: scroll; 191 | } 192 | .CodeMirror-scrollbar-filler { 193 | right: 0; bottom: 0; 194 | } 195 | .CodeMirror-gutter-filler { 196 | left: 0; bottom: 0; 197 | } 198 | 199 | .CodeMirror-gutters { 200 | position: absolute; left: 0; top: 0; 201 | min-height: 100%; 202 | z-index: 3; 203 | } 204 | .CodeMirror-gutter { 205 | white-space: normal; 206 | height: 100%; 207 | display: inline-block; 208 | vertical-align: top; 209 | margin-bottom: -50px; 210 | } 211 | .CodeMirror-gutter-wrapper { 212 | position: absolute; 213 | z-index: 4; 214 | background: none !important; 215 | border: none !important; 216 | } 217 | .CodeMirror-gutter-background { 218 | position: absolute; 219 | top: 0; bottom: 0; 220 | z-index: 4; 221 | } 222 | .CodeMirror-gutter-elt { 223 | position: absolute; 224 | cursor: default; 225 | z-index: 4; 226 | } 227 | .CodeMirror-gutter-wrapper ::selection { background-color: transparent } 228 | .CodeMirror-gutter-wrapper ::-moz-selection { background-color: transparent } 229 | 230 | .CodeMirror-lines { 231 | cursor: text; 232 | min-height: 1px; /* prevents collapsing before first draw */ 233 | } 234 | .CodeMirror pre.CodeMirror-line, 235 | .CodeMirror pre.CodeMirror-line-like { 236 | /* Reset some styles that the rest of the page might have set */ 237 | -moz-border-radius: 0; -webkit-border-radius: 0; border-radius: 0; 238 | border-width: 0; 239 | background: transparent; 240 | font-family: inherit; 241 | font-size: inherit; 242 | margin: 0; 243 | white-space: pre; 244 | word-wrap: normal; 245 | line-height: inherit; 246 | color: inherit; 247 | z-index: 2; 248 | position: relative; 249 | overflow: visible; 250 | -webkit-tap-highlight-color: transparent; 251 | -webkit-font-variant-ligatures: contextual; 252 | font-variant-ligatures: contextual; 253 | } 254 | .CodeMirror-wrap pre.CodeMirror-line, 255 | .CodeMirror-wrap pre.CodeMirror-line-like { 256 | word-wrap: break-word; 257 | white-space: pre-wrap; 258 | word-break: normal; 259 | } 260 | 261 | .CodeMirror-linebackground { 262 | position: absolute; 263 | left: 0; right: 0; top: 0; bottom: 0; 264 | z-index: 0; 265 | } 266 | 267 | .CodeMirror-linewidget { 268 | position: relative; 269 | z-index: 2; 270 | padding: 0.1px; /* Force widget margins to stay inside of the container */ 271 | } 272 | 273 | .CodeMirror-widget {} 274 | 275 | .CodeMirror-rtl pre { direction: rtl; } 276 | 277 | .CodeMirror-code { 278 | outline: none; 279 | } 280 | 281 | /* Force content-box sizing for the elements where we expect it */ 282 | .CodeMirror-scroll, 283 | .CodeMirror-sizer, 284 | .CodeMirror-gutter, 285 | .CodeMirror-gutters, 286 | .CodeMirror-linenumber { 287 | -moz-box-sizing: content-box; 288 | box-sizing: content-box; 289 | } 290 | 291 | .CodeMirror-measure { 292 | position: absolute; 293 | width: 100%; 294 | height: 0; 295 | overflow: hidden; 296 | visibility: hidden; 297 | } 298 | 299 | .CodeMirror-cursor { 300 | position: absolute; 301 | pointer-events: none; 302 | } 303 | .CodeMirror-measure pre { position: static; } 304 | 305 | div.CodeMirror-cursors { 306 | visibility: hidden; 307 | position: relative; 308 | z-index: 3; 309 | } 310 | div.CodeMirror-dragcursors { 311 | visibility: visible; 312 | } 313 | 314 | .CodeMirror-focused div.CodeMirror-cursors { 315 | visibility: visible; 316 | } 317 | 318 | .CodeMirror-selected { background: #d9d9d9; } 319 | .CodeMirror-focused .CodeMirror-selected { background: #d7d4f0; } 320 | .CodeMirror-crosshair { cursor: crosshair; } 321 | .CodeMirror-line::selection, .CodeMirror-line > span::selection, .CodeMirror-line > span > span::selection { background: #d7d4f0; } 322 | .CodeMirror-line::-moz-selection, .CodeMirror-line > span::-moz-selection, .CodeMirror-line > span > span::-moz-selection { background: #d7d4f0; } 323 | 324 | .cm-searching { 325 | background-color: #ffa; 326 | background-color: rgba(255, 255, 0, .4); 327 | } 328 | 329 | /* Used to force a border model for a node */ 330 | .cm-force-border { padding-right: .1px; } 331 | 332 | @media print { 333 | /* Hide the cursor when printing */ 334 | .CodeMirror div.CodeMirror-cursors { 335 | visibility: hidden; 336 | } 337 | } 338 | 339 | /* See issue #2901 */ 340 | .cm-tab-wrap-hack:after { content: ''; } 341 | 342 | /* Help users use markselection to safely style text background */ 343 | span.CodeMirror-selectedtext { background: none; } 344 | -------------------------------------------------------------------------------- /src/js/workers/formatter.js: -------------------------------------------------------------------------------- 1 | /* global globalThis, addEventListener, postMessage, BigInt */ 2 | 3 | /** 4 | * Adapted the code in to order to run in a web worker. 5 | * 6 | * Original author: Benjamin Hollis 7 | */ 8 | 9 | function htmlEncode(t) { 10 | return t != null ? t.toString().replace(/&/g, "&").replace(/"/g, """).replace(//g, ">") : ""; 11 | } 12 | 13 | function decorateWithSpan(value, className) { 14 | return "" + htmlEncode(value) + ""; 15 | } 16 | 17 | function valueToHTML(value) { 18 | const valueType = typeof value; 19 | let output = ""; 20 | if (value == null) 21 | output += decorateWithSpan("null", "type-null"); 22 | else if (value && value.constructor == Array) 23 | output += arrayToHTML(value); 24 | else if (valueType == "object") 25 | output += objectToHTML(value); 26 | else if (valueType == "number" || valueType == "bigint") 27 | output += decorateWithSpan(value, "type-number"); 28 | else if (valueType == "string") 29 | if (/^https?:\/\/[^\s]+$/.test(value)) 30 | output += decorateWithSpan("\"", "type-string") + "" + htmlEncode(value) + "" + decorateWithSpan("\"", "type-string"); 31 | else 32 | output += decorateWithSpan("\"" + value + "\"", "type-string"); 33 | else if (valueType == "boolean") 34 | output += decorateWithSpan(value, "type-boolean"); 35 | 36 | return output; 37 | } 38 | 39 | function arrayToHTML(json) { 40 | let indexValue, length, output = "
[]"; 50 | if (!hasContents) 51 | output = "[ ]"; 52 | return output; 53 | } 54 | 55 | function objectToHTML(json) { 56 | let indexKey, key, length, keys = Object.keys(json), output = "
{}"; 68 | if (!hasContents) 69 | output = "{ }"; 70 | return output; 71 | } 72 | 73 | function jsonToHTML(json, functionName) { 74 | let output = ""; 75 | if (functionName) 76 | output += "
" + htmlEncode(functionName) + "(
"; 77 | output += "
"; 78 | output += valueToHTML(json); 79 | output += "
"; 80 | if (functionName) 81 | output += "
)
"; 82 | return output; 83 | } 84 | 85 | // regexpxs extracted from 86 | // (c) BSD-3-Clause 87 | // https://github.com/fastify/secure-json-parse/graphs/contributors and https://github.com/hapijs/bourne/graphs/contributors 88 | 89 | const suspectProtoRx = /(?:_|\\u005[Ff])(?:_|\\u005[Ff])(?:p|\\u0070)(?:r|\\u0072)(?:o|\\u006[Ff])(?:t|\\u0074)(?:o|\\u006[Ff])(?:_|\\u005[Ff])(?:_|\\u005[Ff])/; 90 | const suspectConstructorRx = /(?:c|\\u0063)(?:o|\\u006[Ff])(?:n|\\u006[Ee])(?:s|\\u0073)(?:t|\\u0074)(?:r|\\u0072)(?:u|\\u0075)(?:c|\\u0063)(?:t|\\u0074)(?:o|\\u006[Ff])(?:r|\\u0072)/; 91 | 92 | /* 93 | json_parse.js 94 | 2012-06-20 95 | 96 | Public Domain. 97 | 98 | NO WARRANTY EXPRESSED OR IMPLIED. USE AT YOUR OWN RISK. 99 | 100 | This file creates a json_parse function. 101 | During create you can (optionally) specify some behavioural switches 102 | 103 | require('json-bigint')(options) 104 | 105 | The optional options parameter holds switches that drive certain 106 | aspects of the parsing process: 107 | * options.strict = true will warn about duplicate-key usage in the json. 108 | The default (strict = false) will silently ignore those and overwrite 109 | values for keys that are in duplicate use. 110 | 111 | The resulting function follows this signature: 112 | json_parse(text, reviver) 113 | This method parses a JSON text to produce an object or array. 114 | It can throw a SyntaxError exception. 115 | 116 | The optional reviver parameter is a function that can filter and 117 | transform the results. It receives each of the keys and values, 118 | and its return value is used instead of the original value. 119 | If it returns what it received, then the structure is not modified. 120 | If it returns undefined then the member is deleted. 121 | 122 | Example: 123 | 124 | // Parse the text. Values that look like ISO date strings will 125 | // be converted to Date objects. 126 | 127 | myData = json_parse(text, function (key, value) { 128 | var a; 129 | if (typeof value === 'string') { 130 | a = 131 | /^(\d{4})-(\d{2})-(\d{2})T(\d{2}):(\d{2}):(\d{2}(?:\.\d*)?)Z$/.exec(value); 132 | if (a) { 133 | return new Date(Date.UTC(+a[1], +a[2] - 1, +a[3], +a[4], 134 | +a[5], +a[6])); 135 | } 136 | } 137 | return value; 138 | }); 139 | 140 | This is a reference implementation. You are free to copy, modify, or 141 | redistribute. 142 | 143 | This code should be minified before deployment. 144 | See http://javascript.crockford.com/jsmin.html 145 | 146 | USE YOUR OWN COPY. IT IS EXTREMELY UNWISE TO LOAD CODE FROM SERVERS YOU DO 147 | NOT CONTROL. 148 | */ 149 | 150 | /*members "", "\"", "\/", "\\", at, b, call, charAt, f, fromCharCode, 151 | hasOwnProperty, message, n, name, prototype, push, r, t, text 152 | */ 153 | 154 | var json_parse = function (options) { 155 | "use strict"; 156 | 157 | // This is a function that can parse a JSON text, producing a JavaScript 158 | // data structure. It is a simple, recursive descent parser. It does not use 159 | // eval or regular expressions, so it can be used as a model for implementing 160 | // a JSON parser in other languages. 161 | 162 | // We are defining the function inside of another function to avoid creating 163 | // global variables. 164 | 165 | // Default options one can override by passing options to the parse() 166 | var _options = { 167 | strict: false, // not being strict means do not generate syntax errors for "duplicate key" 168 | storeAsString: false, // toggles whether the values should be stored as BigNumber (default) or a string 169 | alwaysParseAsBig: false, // toggles whether all numbers should be Big 170 | useNativeBigInt: false, // toggles whether to use native BigInt instead of bignumber.js 171 | protoAction: "error", 172 | constructorAction: "error", 173 | }; 174 | 175 | // If there are options, then use them to override the default _options 176 | if (options !== undefined && options !== null) { 177 | if (options.strict === true) { 178 | _options.strict = true; 179 | } 180 | if (options.storeAsString === true) { 181 | _options.storeAsString = true; 182 | } 183 | _options.alwaysParseAsBig = 184 | options.alwaysParseAsBig === true ? options.alwaysParseAsBig : false; 185 | _options.useNativeBigInt = 186 | options.useNativeBigInt === true ? options.useNativeBigInt : false; 187 | 188 | if (typeof options.constructorAction !== "undefined") { 189 | if ( 190 | options.constructorAction === "error" || 191 | options.constructorAction === "ignore" || 192 | options.constructorAction === "preserve" 193 | ) { 194 | _options.constructorAction = options.constructorAction; 195 | } else { 196 | throw new Error( 197 | `Incorrect value for constructorAction option, must be "error", "ignore" or undefined but passed ${options.constructorAction}` 198 | ); 199 | } 200 | } 201 | 202 | if (typeof options.protoAction !== "undefined") { 203 | if ( 204 | options.protoAction === "error" || 205 | options.protoAction === "ignore" || 206 | options.protoAction === "preserve" 207 | ) { 208 | _options.protoAction = options.protoAction; 209 | } else { 210 | throw new Error( 211 | `Incorrect value for protoAction option, must be "error", "ignore" or undefined but passed ${options.protoAction}` 212 | ); 213 | } 214 | } 215 | } 216 | 217 | var at, // The index of the current character 218 | ch, // The current character 219 | escapee = { 220 | "\"": "\"", 221 | "\\": "\\", 222 | "/": "/", 223 | b: "\b", 224 | f: "\f", 225 | n: "\n", 226 | r: "\r", 227 | t: "\t", 228 | }, 229 | text, 230 | error = function (m) { 231 | // Call error when something is wrong. 232 | 233 | throw { 234 | name: "SyntaxError", 235 | message: m, 236 | at: at, 237 | text: text, 238 | }; 239 | }, 240 | next = function (c) { 241 | // If a c parameter is provided, verify that it matches the current character. 242 | 243 | if (c && c !== ch) { 244 | error("Expected '" + c + "' instead of '" + ch + "'"); 245 | } 246 | 247 | // Get the next character. When there are no more characters, 248 | // return the empty string. 249 | 250 | ch = text.charAt(at); 251 | at += 1; 252 | return ch; 253 | }, 254 | number = function () { 255 | // Parse a number value. 256 | 257 | var number, 258 | string = ""; 259 | 260 | if (ch === "-") { 261 | string = "-"; 262 | next("-"); 263 | } 264 | while (ch >= "0" && ch <= "9") { 265 | string += ch; 266 | next(); 267 | } 268 | if (ch === ".") { 269 | string += "."; 270 | while (next() && ch >= "0" && ch <= "9") { 271 | string += ch; 272 | } 273 | } 274 | if (ch === "e" || ch === "E") { 275 | string += ch; 276 | next(); 277 | if (ch === "-" || ch === "+") { 278 | string += ch; 279 | next(); 280 | } 281 | while (ch >= "0" && ch <= "9") { 282 | string += ch; 283 | next(); 284 | } 285 | } 286 | number = +string; 287 | if (!isFinite(number)) { 288 | error("Bad number"); 289 | } else { 290 | // if (BigNumber == null) BigNumber = require("bignumber.js"); 291 | if (Number.isSafeInteger(number)) 292 | return !_options.alwaysParseAsBig 293 | ? number 294 | : BigInt(number); 295 | else 296 | // Number with fractional part should be treated as number(double) including big integers in scientific notation, i.e 1.79e+308 297 | return _options.storeAsString 298 | ? string 299 | : /[.eE]/.test(string) 300 | ? number 301 | : BigInt(string); 302 | } 303 | }, 304 | string = function () { 305 | // Parse a string value. 306 | 307 | var hex, 308 | i, 309 | string = "", 310 | uffff; 311 | 312 | // When parsing for string values, we must look for " and \ characters. 313 | 314 | if (ch === "\"") { 315 | var startAt = at; 316 | while (next()) { 317 | if (ch === "\"") { 318 | if (at - 1 > startAt) string += text.substring(startAt, at - 1); 319 | next(); 320 | return string; 321 | } 322 | if (ch === "\\") { 323 | if (at - 1 > startAt) string += text.substring(startAt, at - 1); 324 | next(); 325 | if (ch === "u") { 326 | uffff = 0; 327 | for (i = 0; i < 4; i += 1) { 328 | hex = parseInt(next(), 16); 329 | if (!isFinite(hex)) { 330 | break; 331 | } 332 | uffff = uffff * 16 + hex; 333 | } 334 | string += String.fromCharCode(uffff); 335 | } else if (typeof escapee[ch] === "string") { 336 | string += escapee[ch]; 337 | } else { 338 | break; 339 | } 340 | startAt = at; 341 | } 342 | } 343 | } 344 | error("Bad string"); 345 | }, 346 | white = function () { 347 | // Skip whitespace. 348 | 349 | while (ch && ch <= " ") { 350 | next(); 351 | } 352 | }, 353 | word = function () { 354 | // true, false, or null. 355 | 356 | switch (ch) { 357 | case "t": 358 | next("t"); 359 | next("r"); 360 | next("u"); 361 | next("e"); 362 | return true; 363 | case "f": 364 | next("f"); 365 | next("a"); 366 | next("l"); 367 | next("s"); 368 | next("e"); 369 | return false; 370 | case "n": 371 | next("n"); 372 | next("u"); 373 | next("l"); 374 | next("l"); 375 | return null; 376 | } 377 | error("Unexpected '" + ch + "'"); 378 | }, 379 | value, // Place holder for the value function. 380 | array = function () { 381 | // Parse an array value. 382 | 383 | var array = []; 384 | 385 | if (ch === "[") { 386 | next("["); 387 | white(); 388 | if (ch === "]") { 389 | next("]"); 390 | return array; // empty array 391 | } 392 | while (ch) { 393 | array.push(value()); 394 | white(); 395 | if (ch === "]") { 396 | next("]"); 397 | return array; 398 | } 399 | next(","); 400 | white(); 401 | } 402 | } 403 | error("Bad array"); 404 | }, 405 | object = function () { 406 | // Parse an object value. 407 | 408 | var key, 409 | object = Object.create(null); 410 | 411 | if (ch === "{") { 412 | next("{"); 413 | white(); 414 | if (ch === "}") { 415 | next("}"); 416 | return object; // empty object 417 | } 418 | while (ch) { 419 | key = string(); 420 | white(); 421 | next(":"); 422 | if ( 423 | _options.strict === true && 424 | Object.hasOwnProperty.call(object, key) 425 | ) { 426 | error("Duplicate key \"" + key + "\""); 427 | } 428 | 429 | if (suspectProtoRx.test(key) === true) { 430 | if (_options.protoAction === "error") { 431 | error("Object contains forbidden prototype property"); 432 | } else if (_options.protoAction === "ignore") { 433 | value(); 434 | } else { 435 | object[key] = value(); 436 | } 437 | } else if (suspectConstructorRx.test(key) === true) { 438 | if (_options.constructorAction === "error") { 439 | error("Object contains forbidden constructor property"); 440 | } else if (_options.constructorAction === "ignore") { 441 | value(); 442 | } else { 443 | object[key] = value(); 444 | } 445 | } else { 446 | object[key] = value(); 447 | } 448 | 449 | white(); 450 | if (ch === "}") { 451 | next("}"); 452 | return object; 453 | } 454 | next(","); 455 | white(); 456 | } 457 | } 458 | error("Bad object"); 459 | }; 460 | 461 | value = function () { 462 | // Parse a JSON value. It could be an object, an array, a string, a number, 463 | // or a word. 464 | 465 | white(); 466 | switch (ch) { 467 | case "{": 468 | return object(); 469 | case "[": 470 | return array(); 471 | case "\"": 472 | return string(); 473 | case "-": 474 | return number(); 475 | default: 476 | return ch >= "0" && ch <= "9" ? number() : word(); 477 | } 478 | }; 479 | 480 | // Return the json_parse function. It will have access to all of the above 481 | // functions and variables. 482 | 483 | return function (source, reviver) { 484 | var result; 485 | 486 | text = source + ""; 487 | at = 0; 488 | ch = " "; 489 | result = value(); 490 | white(); 491 | if (ch) { 492 | error("Syntax error"); 493 | } 494 | 495 | // If there is a reviver function, we recursively walk the new structure, 496 | // passing each name/value pair to the reviver function for possible 497 | // transformation, starting with a temporary root object that holds the result 498 | // in an empty key. If there is not a reviver function, we simply return the 499 | // result. 500 | 501 | return typeof reviver === "function" 502 | ? (function walk(holder, key) { 503 | var v, 504 | value = holder[key]; 505 | if (value && typeof value === "object") { 506 | Object.keys(value).forEach(function (k) { 507 | v = walk(value, k); 508 | if (v !== undefined) { 509 | value[k] = v; 510 | } else { 511 | delete value[k]; 512 | } 513 | }); 514 | } 515 | return reviver.call(holder, key, value); 516 | })({ "": result }, "") 517 | : result; 518 | }; 519 | }; 520 | 521 | const parseJson = json_parse(); 522 | 523 | function format(json, functionName, supportBigInt) { 524 | let object; 525 | if (supportBigInt) { 526 | object = parseJson(json); 527 | } else { 528 | object = JSON.parse(json); 529 | } 530 | return jsonToHTML(object, functionName); 531 | } 532 | 533 | if (typeof postMessage == "undefined") { 534 | globalThis.formatter = { format }; 535 | } else { 536 | addEventListener("message", event => { 537 | try { 538 | postMessage({ 539 | onjsonToHTML: true, 540 | html: format(event.data.json, event.data.functionName, event.data.supportBigInt) 541 | }); 542 | } catch (error) { 543 | postMessage({ error: true }); 544 | return; 545 | } 546 | }, false); 547 | } 548 | 549 | -------------------------------------------------------------------------------- /src/js/workers/linter.js: -------------------------------------------------------------------------------- 1 | /* global globalThis, addEventListener, postMessage */ 2 | 3 | /* 4 | MIT License 5 | Copyright (C) 2012 Zachary Carter 6 | 7 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: 8 | 9 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 10 | 11 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 12 | */ 13 | /* Jison generated parser */ 14 | var jsonlint = (function () { 15 | var parser = { 16 | trace: function trace() { }, 17 | yy: {}, 18 | 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 }, 19 | terminals_: { 2: "error", 4: "STRING", 6: "NUMBER", 8: "NULL", 10: "TRUE", 11: "FALSE", 14: "EOF", 17: "{", 18: "}", 21: ":", 22: ",", 23: "[", 24: "]" }, 20 | 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]], 21 | performAction: function anonymous(yytext, yyleng, yylineno, yy, yystate, $$) { 22 | 23 | var $0 = $$.length - 1; 24 | switch (yystate) { 25 | case 1: // replace escaped characters with actual character 26 | this.$ = yytext.replace(/\\(\\|")/g, "$" + "1") 27 | .replace(/\\n/g, "\n") 28 | .replace(/\\r/g, "\r") 29 | .replace(/\\t/g, "\t") 30 | .replace(/\\v/g, "\v") 31 | .replace(/\\f/g, "\f") 32 | .replace(/\\b/g, "\b"); 33 | 34 | break; 35 | case 2: this.$ = Number(yytext); 36 | break; 37 | case 3: this.$ = null; 38 | break; 39 | case 4: this.$ = true; 40 | break; 41 | case 5: this.$ = false; 42 | break; 43 | case 6: return this.$ = $$[$0 - 1]; 44 | case 13: this.$ = {}; 45 | break; 46 | case 14: this.$ = $$[$0 - 1]; 47 | break; 48 | case 15: this.$ = [$$[$0 - 2], $$[$0]]; 49 | break; 50 | case 16: this.$ = {}; this.$[$$[$0][0]] = $$[$0][1]; 51 | break; 52 | case 17: this.$ = $$[$0 - 2]; $$[$0 - 2][$$[$0][0]] = $$[$0][1]; 53 | break; 54 | case 18: this.$ = []; 55 | break; 56 | case 19: this.$ = $$[$0 - 1]; 57 | break; 58 | case 20: this.$ = [$$[$0]]; 59 | break; 60 | case 21: this.$ = $$[$0 - 2]; $$[$0 - 2].push($$[$0]); 61 | break; 62 | } 63 | }, 64 | 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] }], 65 | defaultActions: { 16: [2, 6] }, 66 | parseError: function parseError(str) { 67 | throw new Error(str); 68 | }, 69 | parse: function parse(input) { 70 | var self = this, 71 | stack = [0], 72 | vstack = [null], // semantic value stack 73 | lstack = [], // location stack 74 | table = this.table, 75 | yytext = "", 76 | yylineno = 0, 77 | yyleng = 0, 78 | recovering = 0, 79 | TERROR = 2, 80 | EOF = 1; 81 | 82 | //this.reductionCount = this.shiftCount = 0; 83 | 84 | this.lexer.setInput(input); 85 | this.lexer.yy = this.yy; 86 | this.yy.lexer = this.lexer; 87 | if (typeof this.lexer.yylloc == "undefined") 88 | this.lexer.yylloc = {}; 89 | var yyloc = this.lexer.yylloc; 90 | lstack.push(yyloc); 91 | 92 | if (typeof this.yy.parseError === "function") 93 | this.parseError = this.yy.parseError; 94 | 95 | function popStack(n) { 96 | stack.length = stack.length - 2 * n; 97 | vstack.length = vstack.length - n; 98 | lstack.length = lstack.length - n; 99 | } 100 | 101 | function lex() { 102 | var token; 103 | token = self.lexer.lex() || 1; // $end = 1 104 | // if token isn't its numeric value, convert 105 | if (typeof token !== "number") { 106 | token = self.symbols_[token] || token; 107 | } 108 | return token; 109 | } 110 | 111 | var symbol, preErrorSymbol, state, action, r, yyval = {}, p, len, newState, expected; 112 | // eslint-disable-next-line no-constant-condition 113 | while (true) { 114 | // retreive state number from top of stack 115 | state = stack[stack.length - 1]; 116 | 117 | // use default actions if available 118 | if (this.defaultActions[state]) { 119 | action = this.defaultActions[state]; 120 | } else { 121 | if (symbol == null) 122 | symbol = lex(); 123 | // read action for current state and first input 124 | action = table[state] && table[state][symbol]; 125 | } 126 | 127 | // handle parse error 128 | if (typeof action === "undefined" || !action.length || !action[0]) { 129 | 130 | if (!recovering) { 131 | // Report error 132 | expected = []; 133 | for (p in table[state]) if (this.terminals_[p] && p > 2) { 134 | expected.push("'" + this.terminals_[p] + "'"); 135 | } 136 | var errStr = ""; 137 | if (this.lexer.showPosition) { 138 | errStr = "Parse error on line " + (yylineno + 1) + ":\n" + this.lexer.showPosition() + "\nExpecting " + expected.join(", ") + ", got '" + this.terminals_[symbol] + "'"; 139 | } else { 140 | errStr = "Parse error on line " + (yylineno + 1) + ": Unexpected " + 141 | (symbol == 1 /*EOF*/ ? "end of input" : ("'" + (this.terminals_[symbol] || symbol) + "'")); 142 | } 143 | this.parseError(errStr, 144 | { text: this.lexer.match, token: this.terminals_[symbol] || symbol, line: this.lexer.yylineno, loc: yyloc, expected: expected }); 145 | } 146 | 147 | // just recovered from another error 148 | if (recovering == 3) { 149 | if (symbol == EOF) { 150 | throw new Error(errStr || "Parsing halted."); 151 | } 152 | 153 | // discard current lookahead and grab another 154 | yyleng = this.lexer.yyleng; 155 | yytext = this.lexer.yytext; 156 | yylineno = this.lexer.yylineno; 157 | yyloc = this.lexer.yylloc; 158 | symbol = lex(); 159 | } 160 | 161 | // try to recover from error 162 | // eslint-disable-next-line no-constant-condition 163 | while (true) { 164 | // check for error recovery rule in this state 165 | if ((TERROR.toString()) in table[state]) { 166 | break; 167 | } 168 | if (state == 0) { 169 | throw new Error(errStr || "Parsing halted."); 170 | } 171 | popStack(1); 172 | state = stack[stack.length - 1]; 173 | } 174 | 175 | preErrorSymbol = symbol; // save the lookahead token 176 | symbol = TERROR; // insert generic error symbol as new lookahead 177 | state = stack[stack.length - 1]; 178 | action = table[state] && table[state][TERROR]; 179 | recovering = 3; // allow 3 real symbols to be shifted before reporting a new error 180 | } 181 | 182 | // this shouldn't happen, unless resolve defaults are off 183 | if (action[0] instanceof Array && action.length > 1) { 184 | throw new Error("Parse Error: multiple actions possible at state: " + state + ", token: " + symbol); 185 | } 186 | 187 | switch (action[0]) { 188 | 189 | case 1: // shift 190 | //this.shiftCount++; 191 | 192 | stack.push(symbol); 193 | vstack.push(this.lexer.yytext); 194 | lstack.push(this.lexer.yylloc); 195 | stack.push(action[1]); // push state 196 | symbol = null; 197 | if (!preErrorSymbol) { // normal execution/no error 198 | yyleng = this.lexer.yyleng; 199 | yytext = this.lexer.yytext; 200 | yylineno = this.lexer.yylineno; 201 | yyloc = this.lexer.yylloc; 202 | if (recovering > 0) 203 | recovering--; 204 | } else { // error just occurred, resume old lookahead f/ before error 205 | symbol = preErrorSymbol; 206 | preErrorSymbol = null; 207 | } 208 | break; 209 | 210 | case 2: // reduce 211 | //this.reductionCount++; 212 | 213 | len = this.productions_[action[1]][1]; 214 | 215 | // perform semantic action 216 | yyval.$ = vstack[vstack.length - len]; // default to $$ = $1 217 | // default location, uses first token for firsts, last for lasts 218 | yyval._$ = { 219 | first_line: lstack[lstack.length - (len || 1)].first_line, 220 | last_line: lstack[lstack.length - 1].last_line, 221 | first_column: lstack[lstack.length - (len || 1)].first_column, 222 | last_column: lstack[lstack.length - 1].last_column 223 | }; 224 | r = this.performAction.call(yyval, yytext, yyleng, yylineno, this.yy, action[1], vstack, lstack); 225 | 226 | if (typeof r !== "undefined") { 227 | return r; 228 | } 229 | 230 | // pop off stack 231 | if (len) { 232 | stack = stack.slice(0, -1 * len * 2); 233 | vstack = vstack.slice(0, -1 * len); 234 | lstack = lstack.slice(0, -1 * len); 235 | } 236 | 237 | stack.push(this.productions_[action[1]][0]); // push nonterminal (reduce) 238 | vstack.push(yyval.$); 239 | lstack.push(yyval._$); 240 | // goto new state = table[STATE][NONTERMINAL] 241 | newState = table[stack[stack.length - 2]][stack[stack.length - 1]]; 242 | stack.push(newState); 243 | break; 244 | 245 | case 3: // accept 246 | return true; 247 | } 248 | 249 | } 250 | } 251 | }; 252 | /* Jison generated lexer */ 253 | var lexer = (function () { 254 | var lexer = ({ 255 | EOF: 1, 256 | parseError: function parseError(str, hash) { 257 | if (this.yy.parseError) { 258 | this.yy.parseError(str, hash); 259 | } else { 260 | throw new Error(str); 261 | } 262 | }, 263 | setInput: function (input) { 264 | this._input = input; 265 | this._more = this._less = this.done = false; 266 | this.yylineno = this.yyleng = 0; 267 | this.yytext = this.matched = this.match = ""; 268 | this.conditionStack = ["INITIAL"]; 269 | this.yylloc = { first_line: 1, first_column: 0, last_line: 1, last_column: 0 }; 270 | return this; 271 | }, 272 | input: function () { 273 | var ch = this._input[0]; 274 | this.yytext += ch; 275 | this.yyleng++; 276 | this.match += ch; 277 | this.matched += ch; 278 | var lines = ch.match(/\n/); 279 | if (lines) this.yylineno++; 280 | this._input = this._input.slice(1); 281 | return ch; 282 | }, 283 | unput: function (ch) { 284 | this._input = ch + this._input; 285 | return this; 286 | }, 287 | more: function () { 288 | this._more = true; 289 | return this; 290 | }, 291 | less: function (n) { 292 | this._input = this.match.slice(n) + this._input; 293 | }, 294 | pastInput: function () { 295 | var past = this.matched.substr(0, this.matched.length - this.match.length); 296 | return (past.length > 20 ? "..." : "") + past.substr(-20).replace(/\n/g, ""); 297 | }, 298 | upcomingInput: function () { 299 | var next = this.match; 300 | if (next.length < 20) { 301 | next += this._input.substr(0, 20 - next.length); 302 | } 303 | return (next.substr(0, 20) + (next.length > 20 ? "..." : "")).replace(/\n/g, ""); 304 | }, 305 | showPosition: function () { 306 | var pre = this.pastInput(); 307 | var c = new Array(pre.length + 1).join("-"); 308 | return pre + this.upcomingInput() + "\n" + c + "^"; 309 | }, 310 | next: function () { 311 | if (this.done) { 312 | return this.EOF; 313 | } 314 | if (!this._input) this.done = true; 315 | 316 | var token, 317 | match, 318 | tempMatch, 319 | index, 320 | lines; 321 | if (!this._more) { 322 | this.yytext = ""; 323 | this.match = ""; 324 | } 325 | var rules = this._currentRules(); 326 | for (var i = 0; i < rules.length; i++) { 327 | tempMatch = this._input.match(this.rules[rules[i]]); 328 | if (tempMatch && (!match || tempMatch[0].length > match[0].length)) { 329 | match = tempMatch; 330 | index = i; 331 | if (!this.options.flex) break; 332 | } 333 | } 334 | if (match) { 335 | lines = match[0].match(/\n.*/g); 336 | if (lines) this.yylineno += lines.length; 337 | this.yylloc = { 338 | first_line: this.yylloc.last_line, 339 | last_line: this.yylineno + 1, 340 | first_column: this.yylloc.last_column, 341 | last_column: lines ? lines[lines.length - 1].length - 1 : this.yylloc.last_column + match[0].length 342 | }; 343 | this.yytext += match[0]; 344 | this.match += match[0]; 345 | this.yyleng = this.yytext.length; 346 | this._more = false; 347 | this._input = this._input.slice(match[0].length); 348 | this.matched += match[0]; 349 | token = this.performAction.call(this, this.yy, this, rules[index], this.conditionStack[this.conditionStack.length - 1]); 350 | if (this.done && this._input) this.done = false; 351 | if (token) return token; 352 | else return; 353 | } 354 | if (this._input === "") { 355 | return this.EOF; 356 | } else { 357 | this.parseError("Lexical error on line " + (this.yylineno + 1) + ". Unrecognized text.\n" + this.showPosition(), 358 | { text: "", token: null, line: this.yylineno }); 359 | } 360 | }, 361 | lex: function lex() { 362 | var r = this.next(); 363 | if (typeof r !== "undefined") { 364 | return r; 365 | } else { 366 | return this.lex(); 367 | } 368 | }, 369 | begin: function begin(condition) { 370 | this.conditionStack.push(condition); 371 | }, 372 | popState: function popState() { 373 | return this.conditionStack.pop(); 374 | }, 375 | _currentRules: function _currentRules() { 376 | return this.conditions[this.conditionStack[this.conditionStack.length - 1]].rules; 377 | }, 378 | topState: function () { 379 | return this.conditionStack[this.conditionStack.length - 2]; 380 | }, 381 | pushState: function begin(condition) { 382 | this.begin(condition); 383 | } 384 | }); 385 | lexer.options = {}; 386 | lexer.performAction = function anonymous(yy, yy_, $avoiding_name_collisions) { 387 | switch ($avoiding_name_collisions) { 388 | case 0:/* skip whitespace */ 389 | break; 390 | case 1: return 6; 391 | case 2: yy_.yytext = yy_.yytext.substr(1, yy_.yyleng - 2); return 4; 392 | case 3: return 17; 393 | case 4: return 18; 394 | case 5: return 23; 395 | case 6: return 24; 396 | case 7: return 22; 397 | case 8: return 21; 398 | case 9: return 10; 399 | case 10: return 11; 400 | case 11: return 8; 401 | case 12: return 14; 402 | case 13: return "INVALID"; 403 | } 404 | }; 405 | // eslint-disable-next-line no-control-regex 406 | 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)/, /^(?:$)/, /^(?:.)/]; 407 | lexer.conditions = { "INITIAL": { "rules": [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13], "inclusive": true } }; 408 | 409 | 410 | 411 | return lexer; 412 | })(); 413 | parser.lexer = lexer; 414 | return parser; 415 | })(); 416 | 417 | function lint(json) { 418 | try { 419 | jsonlint.parse(json); 420 | return {}; 421 | } catch (errorMessage) { 422 | return { error: errorMessage.toString(), loc: jsonlint.lexer.yylloc }; 423 | } 424 | } 425 | 426 | if (typeof postMessage == "undefined") { 427 | globalThis.linter = { lint }; 428 | } else { 429 | addEventListener("message", event => postMessage(lint(event.data)), false); 430 | } -------------------------------------------------------------------------------- /src/js/workers/parser.js: -------------------------------------------------------------------------------- 1 | /* global globalThis, addEventListener, postMessage, BigInt */ 2 | 3 | // regexpxs extracted from 4 | // (c) BSD-3-Clause 5 | // https://github.com/fastify/secure-json-parse/graphs/contributors and https://github.com/hapijs/bourne/graphs/contributors 6 | 7 | const suspectProtoRx = /(?:_|\\u005[Ff])(?:_|\\u005[Ff])(?:p|\\u0070)(?:r|\\u0072)(?:o|\\u006[Ff])(?:t|\\u0074)(?:o|\\u006[Ff])(?:_|\\u005[Ff])(?:_|\\u005[Ff])/; 8 | const suspectConstructorRx = /(?:c|\\u0063)(?:o|\\u006[Ff])(?:n|\\u006[Ee])(?:s|\\u0073)(?:t|\\u0074)(?:r|\\u0072)(?:u|\\u0075)(?:c|\\u0063)(?:t|\\u0074)(?:o|\\u006[Ff])(?:r|\\u0072)/; 9 | 10 | /* 11 | json_parse.js 12 | 2012-06-20 13 | 14 | Public Domain. 15 | 16 | NO WARRANTY EXPRESSED OR IMPLIED. USE AT YOUR OWN RISK. 17 | 18 | This file creates a json_parse function. 19 | During create you can (optionally) specify some behavioural switches 20 | 21 | require('json-bigint')(options) 22 | 23 | The optional options parameter holds switches that drive certain 24 | aspects of the parsing process: 25 | * options.strict = true will warn about duplicate-key usage in the json. 26 | The default (strict = false) will silently ignore those and overwrite 27 | values for keys that are in duplicate use. 28 | 29 | The resulting function follows this signature: 30 | json_parse(text, reviver) 31 | This method parses a JSON text to produce an object or array. 32 | It can throw a SyntaxError exception. 33 | 34 | The optional reviver parameter is a function that can filter and 35 | transform the results. It receives each of the keys and values, 36 | and its return value is used instead of the original value. 37 | If it returns what it received, then the structure is not modified. 38 | If it returns undefined then the member is deleted. 39 | 40 | Example: 41 | 42 | // Parse the text. Values that look like ISO date strings will 43 | // be converted to Date objects. 44 | 45 | myData = json_parse(text, function (key, value) { 46 | var a; 47 | if (typeof value === 'string') { 48 | a = 49 | /^(\d{4})-(\d{2})-(\d{2})T(\d{2}):(\d{2}):(\d{2}(?:\.\d*)?)Z$/.exec(value); 50 | if (a) { 51 | return new Date(Date.UTC(+a[1], +a[2] - 1, +a[3], +a[4], 52 | +a[5], +a[6])); 53 | } 54 | } 55 | return value; 56 | }); 57 | 58 | This is a reference implementation. You are free to copy, modify, or 59 | redistribute. 60 | 61 | This code should be minified before deployment. 62 | See http://javascript.crockford.com/jsmin.html 63 | 64 | USE YOUR OWN COPY. IT IS EXTREMELY UNWISE TO LOAD CODE FROM SERVERS YOU DO 65 | NOT CONTROL. 66 | */ 67 | 68 | /*members "", "\"", "\/", "\\", at, b, call, charAt, f, fromCharCode, 69 | hasOwnProperty, message, n, name, prototype, push, r, t, text 70 | */ 71 | 72 | var json_parse = function (options) { 73 | "use strict"; 74 | 75 | // This is a function that can parse a JSON text, producing a JavaScript 76 | // data structure. It is a simple, recursive descent parser. It does not use 77 | // eval or regular expressions, so it can be used as a model for implementing 78 | // a JSON parser in other languages. 79 | 80 | // We are defining the function inside of another function to avoid creating 81 | // global variables. 82 | 83 | // Default options one can override by passing options to the parse() 84 | var _options = { 85 | strict: false, // not being strict means do not generate syntax errors for "duplicate key" 86 | storeAsString: false, // toggles whether the values should be stored as BigNumber (default) or a string 87 | alwaysParseAsBig: false, // toggles whether all numbers should be Big 88 | useNativeBigInt: false, // toggles whether to use native BigInt instead of bignumber.js 89 | protoAction: "error", 90 | constructorAction: "error", 91 | }; 92 | 93 | // If there are options, then use them to override the default _options 94 | if (options !== undefined && options !== null) { 95 | if (options.strict === true) { 96 | _options.strict = true; 97 | } 98 | if (options.storeAsString === true) { 99 | _options.storeAsString = true; 100 | } 101 | _options.alwaysParseAsBig = 102 | options.alwaysParseAsBig === true ? options.alwaysParseAsBig : false; 103 | _options.useNativeBigInt = 104 | options.useNativeBigInt === true ? options.useNativeBigInt : false; 105 | 106 | if (typeof options.constructorAction !== "undefined") { 107 | if ( 108 | options.constructorAction === "error" || 109 | options.constructorAction === "ignore" || 110 | options.constructorAction === "preserve" 111 | ) { 112 | _options.constructorAction = options.constructorAction; 113 | } else { 114 | throw new Error( 115 | `Incorrect value for constructorAction option, must be "error", "ignore" or undefined but passed ${options.constructorAction}` 116 | ); 117 | } 118 | } 119 | 120 | if (typeof options.protoAction !== "undefined") { 121 | if ( 122 | options.protoAction === "error" || 123 | options.protoAction === "ignore" || 124 | options.protoAction === "preserve" 125 | ) { 126 | _options.protoAction = options.protoAction; 127 | } else { 128 | throw new Error( 129 | `Incorrect value for protoAction option, must be "error", "ignore" or undefined but passed ${options.protoAction}` 130 | ); 131 | } 132 | } 133 | } 134 | 135 | var at, // The index of the current character 136 | ch, // The current character 137 | escapee = { 138 | "\"": "\"", 139 | "\\": "\\", 140 | "/": "/", 141 | b: "\b", 142 | f: "\f", 143 | n: "\n", 144 | r: "\r", 145 | t: "\t", 146 | }, 147 | text, 148 | error = function (m) { 149 | // Call error when something is wrong. 150 | 151 | throw { 152 | name: "SyntaxError", 153 | message: m, 154 | at: at, 155 | text: text, 156 | }; 157 | }, 158 | next = function (c) { 159 | // If a c parameter is provided, verify that it matches the current character. 160 | 161 | if (c && c !== ch) { 162 | error("Expected '" + c + "' instead of '" + ch + "'"); 163 | } 164 | 165 | // Get the next character. When there are no more characters, 166 | // return the empty string. 167 | 168 | ch = text.charAt(at); 169 | at += 1; 170 | return ch; 171 | }, 172 | number = function () { 173 | // Parse a number value. 174 | 175 | var number, 176 | string = ""; 177 | 178 | if (ch === "-") { 179 | string = "-"; 180 | next("-"); 181 | } 182 | while (ch >= "0" && ch <= "9") { 183 | string += ch; 184 | next(); 185 | } 186 | if (ch === ".") { 187 | string += "."; 188 | while (next() && ch >= "0" && ch <= "9") { 189 | string += ch; 190 | } 191 | } 192 | if (ch === "e" || ch === "E") { 193 | string += ch; 194 | next(); 195 | if (ch === "-" || ch === "+") { 196 | string += ch; 197 | next(); 198 | } 199 | while (ch >= "0" && ch <= "9") { 200 | string += ch; 201 | next(); 202 | } 203 | } 204 | number = +string; 205 | if (!isFinite(number)) { 206 | error("Bad number"); 207 | } else { 208 | // if (BigNumber == null) BigNumber = require("bignumber.js"); 209 | if (Number.isSafeInteger(number)) 210 | return !_options.alwaysParseAsBig 211 | ? number 212 | : BigInt(number); 213 | else 214 | // Number with fractional part should be treated as number(double) including big integers in scientific notation, i.e 1.79e+308 215 | return _options.storeAsString 216 | ? string 217 | : /[.eE]/.test(string) 218 | ? number 219 | : BigInt(string); 220 | } 221 | }, 222 | string = function () { 223 | // Parse a string value. 224 | 225 | var hex, 226 | i, 227 | string = "", 228 | uffff; 229 | 230 | // When parsing for string values, we must look for " and \ characters. 231 | 232 | if (ch === "\"") { 233 | var startAt = at; 234 | while (next()) { 235 | if (ch === "\"") { 236 | if (at - 1 > startAt) string += text.substring(startAt, at - 1); 237 | next(); 238 | return string; 239 | } 240 | if (ch === "\\") { 241 | if (at - 1 > startAt) string += text.substring(startAt, at - 1); 242 | next(); 243 | if (ch === "u") { 244 | uffff = 0; 245 | for (i = 0; i < 4; i += 1) { 246 | hex = parseInt(next(), 16); 247 | if (!isFinite(hex)) { 248 | break; 249 | } 250 | uffff = uffff * 16 + hex; 251 | } 252 | string += String.fromCharCode(uffff); 253 | } else if (typeof escapee[ch] === "string") { 254 | string += escapee[ch]; 255 | } else { 256 | break; 257 | } 258 | startAt = at; 259 | } 260 | } 261 | } 262 | error("Bad string"); 263 | }, 264 | white = function () { 265 | // Skip whitespace. 266 | 267 | while (ch && ch <= " ") { 268 | next(); 269 | } 270 | }, 271 | word = function () { 272 | // true, false, or null. 273 | 274 | switch (ch) { 275 | case "t": 276 | next("t"); 277 | next("r"); 278 | next("u"); 279 | next("e"); 280 | return true; 281 | case "f": 282 | next("f"); 283 | next("a"); 284 | next("l"); 285 | next("s"); 286 | next("e"); 287 | return false; 288 | case "n": 289 | next("n"); 290 | next("u"); 291 | next("l"); 292 | next("l"); 293 | return null; 294 | } 295 | error("Unexpected '" + ch + "'"); 296 | }, 297 | value, // Place holder for the value function. 298 | array = function () { 299 | // Parse an array value. 300 | 301 | var array = []; 302 | 303 | if (ch === "[") { 304 | next("["); 305 | white(); 306 | if (ch === "]") { 307 | next("]"); 308 | return array; // empty array 309 | } 310 | while (ch) { 311 | array.push(value()); 312 | white(); 313 | if (ch === "]") { 314 | next("]"); 315 | return array; 316 | } 317 | next(","); 318 | white(); 319 | } 320 | } 321 | error("Bad array"); 322 | }, 323 | object = function () { 324 | // Parse an object value. 325 | 326 | var key, 327 | object = Object.create(null); 328 | 329 | if (ch === "{") { 330 | next("{"); 331 | white(); 332 | if (ch === "}") { 333 | next("}"); 334 | return object; // empty object 335 | } 336 | while (ch) { 337 | key = string(); 338 | white(); 339 | next(":"); 340 | if ( 341 | _options.strict === true && 342 | Object.hasOwnProperty.call(object, key) 343 | ) { 344 | error("Duplicate key \"" + key + "\""); 345 | } 346 | 347 | if (suspectProtoRx.test(key) === true) { 348 | if (_options.protoAction === "error") { 349 | error("Object contains forbidden prototype property"); 350 | } else if (_options.protoAction === "ignore") { 351 | value(); 352 | } else { 353 | object[key] = value(); 354 | } 355 | } else if (suspectConstructorRx.test(key) === true) { 356 | if (_options.constructorAction === "error") { 357 | error("Object contains forbidden constructor property"); 358 | } else if (_options.constructorAction === "ignore") { 359 | value(); 360 | } else { 361 | object[key] = value(); 362 | } 363 | } else { 364 | object[key] = value(); 365 | } 366 | 367 | white(); 368 | if (ch === "}") { 369 | next("}"); 370 | return object; 371 | } 372 | next(","); 373 | white(); 374 | } 375 | } 376 | error("Bad object"); 377 | }; 378 | 379 | value = function () { 380 | // Parse a JSON value. It could be an object, an array, a string, a number, 381 | // or a word. 382 | 383 | white(); 384 | switch (ch) { 385 | case "{": 386 | return object(); 387 | case "[": 388 | return array(); 389 | case "\"": 390 | return string(); 391 | case "-": 392 | return number(); 393 | default: 394 | return ch >= "0" && ch <= "9" ? number() : word(); 395 | } 396 | }; 397 | 398 | // Return the json_parse function. It will have access to all of the above 399 | // functions and variables. 400 | 401 | return function (source, reviver) { 402 | var result; 403 | 404 | text = source + ""; 405 | at = 0; 406 | ch = " "; 407 | result = value(); 408 | white(); 409 | if (ch) { 410 | error("Syntax error"); 411 | } 412 | 413 | // If there is a reviver function, we recursively walk the new structure, 414 | // passing each name/value pair to the reviver function for possible 415 | // transformation, starting with a temporary root object that holds the result 416 | // in an empty key. If there is not a reviver function, we simply return the 417 | // result. 418 | 419 | return typeof reviver === "function" 420 | ? (function walk(holder, key) { 421 | var v, 422 | value = holder[key]; 423 | if (value && typeof value === "object") { 424 | Object.keys(value).forEach(function (k) { 425 | v = walk(value, k); 426 | if (v !== undefined) { 427 | value[k] = v; 428 | } else { 429 | delete value[k]; 430 | } 431 | }); 432 | } 433 | return reviver.call(holder, key, value); 434 | })({ "": result }, "") 435 | : result; 436 | }; 437 | }; 438 | 439 | /* 440 | json2.js 441 | 2013-05-26 442 | 443 | Public Domain. 444 | 445 | NO WARRANTY EXPRESSED OR IMPLIED. USE AT YOUR OWN RISK. 446 | 447 | See http://www.JSON.org/js.html 448 | 449 | 450 | This code should be minified before deployment. 451 | See http://javascript.crockford.com/jsmin.html 452 | 453 | USE YOUR OWN COPY. IT IS EXTREMELY UNWISE TO LOAD CODE FROM SERVERS YOU DO 454 | NOT CONTROL. 455 | 456 | 457 | This file creates a global JSON object containing two methods: stringify 458 | and parse. 459 | 460 | JSON.stringify(value, replacer, space) 461 | value any JavaScript value, usually an object or array. 462 | 463 | replacer an optional parameter that determines how object 464 | values are stringified for objects. It can be a 465 | function or an array of strings. 466 | 467 | space an optional parameter that specifies the indentation 468 | of nested structures. If it is omitted, the text will 469 | be packed without extra whitespace. If it is a number, 470 | it will specify the number of spaces to indent at each 471 | level. If it is a string (such as '\t' or ' '), 472 | it contains the characters used to indent at each level. 473 | 474 | This method produces a JSON text from a JavaScript value. 475 | 476 | When an object value is found, if the object contains a toJSON 477 | method, its toJSON method will be called and the result will be 478 | stringified. A toJSON method does not serialize: it returns the 479 | value represented by the name/value pair that should be serialized, 480 | or undefined if nothing should be serialized. The toJSON method 481 | will be passed the key associated with the value, and this will be 482 | bound to the value 483 | 484 | For example, this would serialize Dates as ISO strings. 485 | 486 | Date.prototype.toJSON = function (key) { 487 | function f(n) { 488 | // Format integers to have at least two digits. 489 | return n < 10 ? '0' + n : n; 490 | } 491 | 492 | return this.getUTCFullYear() + '-' + 493 | f(this.getUTCMonth() + 1) + '-' + 494 | f(this.getUTCDate()) + 'T' + 495 | f(this.getUTCHours()) + ':' + 496 | f(this.getUTCMinutes()) + ':' + 497 | f(this.getUTCSeconds()) + 'Z'; 498 | }; 499 | 500 | You can provide an optional replacer method. It will be passed the 501 | key and value of each member, with this bound to the containing 502 | object. The value that is returned from your method will be 503 | serialized. If your method returns undefined, then the member will 504 | be excluded from the serialization. 505 | 506 | If the replacer parameter is an array of strings, then it will be 507 | used to select the members to be serialized. It filters the results 508 | such that only members with keys listed in the replacer array are 509 | stringified. 510 | 511 | Values that do not have JSON representations, such as undefined or 512 | functions, will not be serialized. Such values in objects will be 513 | dropped; in arrays they will be replaced with null. You can use 514 | a replacer function to replace those with JSON values. 515 | JSON.stringify(undefined) returns undefined. 516 | 517 | The optional space parameter produces a stringification of the 518 | value that is filled with line breaks and indentation to make it 519 | easier to read. 520 | 521 | If the space parameter is a non-empty string, then that string will 522 | be used for indentation. If the space parameter is a number, then 523 | the indentation will be that many spaces. 524 | 525 | Example: 526 | 527 | text = JSON.stringify(['e', {pluribus: 'unum'}]); 528 | // text is '["e",{"pluribus":"unum"}]' 529 | 530 | 531 | text = JSON.stringify(['e', {pluribus: 'unum'}], null, '\t'); 532 | // text is '[\n\t"e",\n\t{\n\t\t"pluribus": "unum"\n\t}\n]' 533 | 534 | text = JSON.stringify([new Date()], function (key, value) { 535 | return this[key] instanceof Date ? 536 | 'Date(' + this[key] + ')' : value; 537 | }); 538 | // text is '["Date(---current time---)"]' 539 | 540 | 541 | JSON.parse(text, reviver) 542 | This method parses a JSON text to produce an object or array. 543 | It can throw a SyntaxError exception. 544 | 545 | The optional reviver parameter is a function that can filter and 546 | transform the results. It receives each of the keys and values, 547 | and its return value is used instead of the original value. 548 | If it returns what it received, then the structure is not modified. 549 | If it returns undefined then the member is deleted. 550 | 551 | Example: 552 | 553 | // Parse the text. Values that look like ISO date strings will 554 | // be converted to Date objects. 555 | 556 | myData = JSON.parse(text, function (key, value) { 557 | var a; 558 | if (typeof value === 'string') { 559 | a = 560 | /^(\d{4})-(\d{2})-(\d{2})T(\d{2}):(\d{2}):(\d{2}(?:\.\d*)?)Z$/.exec(value); 561 | if (a) { 562 | return new Date(Date.UTC(+a[1], +a[2] - 1, +a[3], +a[4], 563 | +a[5], +a[6])); 564 | } 565 | } 566 | return value; 567 | }); 568 | 569 | myData = JSON.parse('["Date(09/09/2001)"]', function (key, value) { 570 | var d; 571 | if (typeof value === 'string' && 572 | value.slice(0, 5) === 'Date(' && 573 | value.slice(-1) === ')') { 574 | d = new Date(value.slice(5, -1)); 575 | if (d) { 576 | return d; 577 | } 578 | } 579 | return value; 580 | }); 581 | 582 | 583 | This is a reference implementation. You are free to copy, modify, or 584 | redistribute. 585 | */ 586 | 587 | /*jslint evil: true, regexp: true */ 588 | 589 | /*members "", "\b", "\t", "\n", "\f", "\r", "\"", JSON, "\\", apply, 590 | call, charCodeAt, getUTCDate, getUTCFullYear, getUTCHours, 591 | getUTCMinutes, getUTCMonth, getUTCSeconds, hasOwnProperty, join, 592 | lastIndex, length, parse, prototype, push, replace, slice, stringify, 593 | test, toJSON, toString, valueOf 594 | */ 595 | 596 | 597 | // Create a JSON object only if one does not already exist. We create the 598 | // methods in a closure to avoid creating global variables. 599 | 600 | (function () { 601 | "use strict"; 602 | 603 | // eslint-disable-next-line no-control-regex, no-misleading-character-class, no-useless-escape 604 | var escapable = /[\\\"\x00-\x1f\x7f-\x9f\u00ad\u0600-\u0604\u070f\u17b4\u17b5\u200c-\u200f\u2028-\u202f\u2060-\u206f\ufeff\ufff0-\uffff]/g, 605 | gap, 606 | indent, 607 | meta = { // table of character substitutions 608 | "\b": "\\b", 609 | "\t": "\\t", 610 | "\n": "\\n", 611 | "\f": "\\f", 612 | "\r": "\\r", 613 | "\"": "\\\"", 614 | "\\": "\\\\" 615 | }, 616 | rep; 617 | 618 | 619 | function quote(string) { 620 | 621 | // If the string contains no control characters, no quote characters, and no 622 | // backslash characters, then we can safely slap some quotes around it. 623 | // Otherwise we must also replace the offending characters with safe escape 624 | // sequences. 625 | 626 | escapable.lastIndex = 0; 627 | return escapable.test(string) ? "\"" + string.replace(escapable, function (a) { 628 | var c = meta[a]; 629 | return typeof c === "string" 630 | ? c 631 | : "\\u" + ("0000" + a.charCodeAt(0).toString(16)).slice(-4); 632 | }) + "\"" : "\"" + string + "\""; 633 | } 634 | 635 | 636 | function str(key, holder) { 637 | 638 | // Produce a string from holder[key]. 639 | 640 | var i, // The loop counter. 641 | k, // The member key. 642 | v, // The member value. 643 | length, 644 | mind = gap, 645 | partial, 646 | value = holder[key], 647 | // eslint-disable-next-line valid-typeof 648 | isBigInt = value != null && (typeof value == "bitint"); 649 | 650 | // If the value has a toJSON method, call it to obtain a replacement value. 651 | 652 | if (value && typeof value === "object" && 653 | typeof value.toJSON === "function") { 654 | value = value.toJSON(key); 655 | } 656 | 657 | // If we were called with a replacer function, then call the replacer to 658 | // obtain a replacement value. 659 | 660 | if (typeof rep === "function") { 661 | value = rep.call(holder, key, value); 662 | } 663 | 664 | // What happens next depends on the value's type. 665 | 666 | switch (typeof value) { 667 | case "string": 668 | if (isBigInt) { 669 | return value; 670 | } else { 671 | return quote(value); 672 | } 673 | 674 | case "number": 675 | 676 | // JSON numbers must be finite. Encode non-finite numbers as null. 677 | 678 | return isFinite(value) ? String(value) : "null"; 679 | 680 | case "boolean": 681 | case "null": 682 | case "bigint": 683 | 684 | // If the value is a boolean or null, convert it to a string. Note: 685 | // typeof null does not produce 'null'. The case is included here in 686 | // the remote chance that this gets fixed someday. 687 | 688 | return String(value); 689 | 690 | // If the type is 'object', we might be dealing with an object or an array or 691 | // null. 692 | 693 | case "object": 694 | 695 | // Due to a specification blunder in ECMAScript, typeof null is 'object', 696 | // so watch out for that case. 697 | 698 | if (!value) { 699 | return "null"; 700 | } 701 | 702 | // Make an array to hold the partial results of stringifying this object value. 703 | 704 | gap += indent; 705 | partial = []; 706 | 707 | // Is the value an array? 708 | 709 | if (Object.prototype.toString.apply(value) === "[object Array]") { 710 | 711 | // The value is an array. Stringify every element. Use null as a placeholder 712 | // for non-JSON values. 713 | 714 | length = value.length; 715 | for (i = 0; i < length; i += 1) { 716 | partial[i] = str(i, value) || "null"; 717 | } 718 | 719 | // Join all of the elements together, separated with commas, and wrap them in 720 | // brackets. 721 | 722 | v = partial.length === 0 723 | ? "[]" 724 | : gap 725 | ? "[\n" + gap + partial.join(",\n" + gap) + "\n" + mind + "]" 726 | : "[" + partial.join(",") + "]"; 727 | gap = mind; 728 | return v; 729 | } 730 | 731 | // If the replacer is an array, use it to select the members to be stringified. 732 | 733 | if (rep && typeof rep === "object") { 734 | length = rep.length; 735 | for (i = 0; i < length; i += 1) { 736 | if (typeof rep[i] === "string") { 737 | k = rep[i]; 738 | v = str(k, value); 739 | if (v) { 740 | partial.push(quote(k) + (gap ? ": " : ":") + v); 741 | } 742 | } 743 | } 744 | } else { 745 | 746 | // Otherwise, iterate through all of the keys in the object. 747 | 748 | Object.keys(value).forEach(function (k) { 749 | var v = str(k, value); 750 | if (v) { 751 | partial.push(quote(k) + (gap ? ": " : ":") + v); 752 | } 753 | }); 754 | } 755 | 756 | // Join all of the member texts together, separated with commas, 757 | // and wrap them in braces. 758 | 759 | v = partial.length === 0 760 | ? "{}" 761 | : gap 762 | ? "{\n" + gap + partial.join(",\n" + gap) + "\n" + mind + "}" 763 | : "{" + partial.join(",") + "}"; 764 | gap = mind; 765 | return v; 766 | } 767 | } 768 | 769 | // If the JSON object does not yet have a stringify method, give it one. 770 | 771 | if (typeof JSON.stringify !== "function") { 772 | JSON.stringify = function (value, replacer, space) { 773 | 774 | // The stringify method takes a value and an optional replacer, and an optional 775 | // space parameter, and returns a JSON text. The replacer can be a function 776 | // that can replace values, or an array of strings that will select the keys. 777 | // A default replacer method can be provided. Use of the space parameter can 778 | // produce text that is more easily readable. 779 | 780 | var i; 781 | gap = ""; 782 | indent = ""; 783 | 784 | // If the space parameter is a number, make an indent string containing that 785 | // many spaces. 786 | 787 | if (typeof space === "number") { 788 | for (i = 0; i < space; i += 1) { 789 | indent += " "; 790 | } 791 | 792 | // If the space parameter is a string, it will be used as the indent string. 793 | 794 | } else if (typeof space === "string") { 795 | indent = space; 796 | } 797 | 798 | // If there is a replacer, it must be a function or an array. 799 | // Otherwise, throw an error. 800 | 801 | rep = replacer; 802 | if (replacer && typeof replacer !== "function" && 803 | (typeof replacer !== "object" || 804 | typeof replacer.length !== "number")) { 805 | throw new Error("JSON.stringify"); 806 | } 807 | 808 | // Make a fake root object containing our value under the key of ''. 809 | // Return the result of stringifying the value. 810 | 811 | return str("", { "": value }); 812 | }; 813 | } 814 | }()); 815 | 816 | 817 | const parseJson = json_parse(); 818 | 819 | function parse(json) { 820 | return parseJson(json); 821 | } 822 | 823 | if (typeof postMessage == "undefined") { 824 | globalThis.parser = { parse }; 825 | } else { 826 | addEventListener("message", event => postMessage(parse(event.data)), false); 827 | } -------------------------------------------------------------------------------- /src/codemirror/css.js: -------------------------------------------------------------------------------- 1 | // CodeMirror, copyright (c) by Marijn Haverbeke and others 2 | // Distributed under an MIT license: https://codemirror.net/LICENSE 3 | 4 | (function(mod) { 5 | if (typeof exports == "object" && typeof module == "object") // CommonJS 6 | mod(require("../../lib/codemirror")); 7 | else if (typeof define == "function" && define.amd) // AMD 8 | define(["../../lib/codemirror"], mod); 9 | else // Plain browser env 10 | mod(CodeMirror); 11 | })(function(CodeMirror) { 12 | "use strict"; 13 | 14 | CodeMirror.defineMode("css", function(config, parserConfig) { 15 | var inline = parserConfig.inline 16 | if (!parserConfig.propertyKeywords) parserConfig = CodeMirror.resolveMode("text/css"); 17 | 18 | var indentUnit = config.indentUnit, 19 | tokenHooks = parserConfig.tokenHooks, 20 | documentTypes = parserConfig.documentTypes || {}, 21 | mediaTypes = parserConfig.mediaTypes || {}, 22 | mediaFeatures = parserConfig.mediaFeatures || {}, 23 | mediaValueKeywords = parserConfig.mediaValueKeywords || {}, 24 | propertyKeywords = parserConfig.propertyKeywords || {}, 25 | nonStandardPropertyKeywords = parserConfig.nonStandardPropertyKeywords || {}, 26 | fontProperties = parserConfig.fontProperties || {}, 27 | counterDescriptors = parserConfig.counterDescriptors || {}, 28 | colorKeywords = parserConfig.colorKeywords || {}, 29 | valueKeywords = parserConfig.valueKeywords || {}, 30 | allowNested = parserConfig.allowNested, 31 | lineComment = parserConfig.lineComment, 32 | supportsAtComponent = parserConfig.supportsAtComponent === true, 33 | highlightNonStandardPropertyKeywords = config.highlightNonStandardPropertyKeywords !== false; 34 | 35 | var type, override; 36 | function ret(style, tp) { type = tp; return style; } 37 | 38 | // Tokenizers 39 | 40 | function tokenBase(stream, state) { 41 | var ch = stream.next(); 42 | if (tokenHooks[ch]) { 43 | var result = tokenHooks[ch](stream, state); 44 | if (result !== false) return result; 45 | } 46 | if (ch == "@") { 47 | stream.eatWhile(/[\w\\\-]/); 48 | return ret("def", stream.current()); 49 | } else if (ch == "=" || (ch == "~" || ch == "|") && stream.eat("=")) { 50 | return ret(null, "compare"); 51 | } else if (ch == "\"" || ch == "'") { 52 | state.tokenize = tokenString(ch); 53 | return state.tokenize(stream, state); 54 | } else if (ch == "#") { 55 | stream.eatWhile(/[\w\\\-]/); 56 | return ret("atom", "hash"); 57 | } else if (ch == "!") { 58 | stream.match(/^\s*\w*/); 59 | return ret("keyword", "important"); 60 | } else if (/\d/.test(ch) || ch == "." && stream.eat(/\d/)) { 61 | stream.eatWhile(/[\w.%]/); 62 | return ret("number", "unit"); 63 | } else if (ch === "-") { 64 | if (/[\d.]/.test(stream.peek())) { 65 | stream.eatWhile(/[\w.%]/); 66 | return ret("number", "unit"); 67 | } else if (stream.match(/^-[\w\\\-]*/)) { 68 | stream.eatWhile(/[\w\\\-]/); 69 | if (stream.match(/^\s*:/, false)) 70 | return ret("variable-2", "variable-definition"); 71 | return ret("variable-2", "variable"); 72 | } else if (stream.match(/^\w+-/)) { 73 | return ret("meta", "meta"); 74 | } 75 | } else if (/[,+>*\/]/.test(ch)) { 76 | return ret(null, "select-op"); 77 | } else if (ch == "." && stream.match(/^-?[_a-z][_a-z0-9-]*/i)) { 78 | return ret("qualifier", "qualifier"); 79 | } else if (/[:;{}\[\]\(\)]/.test(ch)) { 80 | return ret(null, ch); 81 | } else if (stream.match(/^[\w-.]+(?=\()/)) { 82 | if (/^(url(-prefix)?|domain|regexp)$/i.test(stream.current())) { 83 | state.tokenize = tokenParenthesized; 84 | } 85 | return ret("variable callee", "variable"); 86 | } else if (/[\w\\\-]/.test(ch)) { 87 | stream.eatWhile(/[\w\\\-]/); 88 | return ret("property", "word"); 89 | } else { 90 | return ret(null, null); 91 | } 92 | } 93 | 94 | function tokenString(quote) { 95 | return function(stream, state) { 96 | var escaped = false, ch; 97 | while ((ch = stream.next()) != null) { 98 | if (ch == quote && !escaped) { 99 | if (quote == ")") stream.backUp(1); 100 | break; 101 | } 102 | escaped = !escaped && ch == "\\"; 103 | } 104 | if (ch == quote || !escaped && quote != ")") state.tokenize = null; 105 | return ret("string", "string"); 106 | }; 107 | } 108 | 109 | function tokenParenthesized(stream, state) { 110 | stream.next(); // Must be '(' 111 | if (!stream.match(/^\s*[\"\')]/, false)) 112 | state.tokenize = tokenString(")"); 113 | else 114 | state.tokenize = null; 115 | return ret(null, "("); 116 | } 117 | 118 | // Context management 119 | 120 | function Context(type, indent, prev) { 121 | this.type = type; 122 | this.indent = indent; 123 | this.prev = prev; 124 | } 125 | 126 | function pushContext(state, stream, type, indent) { 127 | state.context = new Context(type, stream.indentation() + (indent === false ? 0 : indentUnit), state.context); 128 | return type; 129 | } 130 | 131 | function popContext(state) { 132 | if (state.context.prev) 133 | state.context = state.context.prev; 134 | return state.context.type; 135 | } 136 | 137 | function pass(type, stream, state) { 138 | return states[state.context.type](type, stream, state); 139 | } 140 | function popAndPass(type, stream, state, n) { 141 | for (var i = n || 1; i > 0; i--) 142 | state.context = state.context.prev; 143 | return pass(type, stream, state); 144 | } 145 | 146 | // Parser 147 | 148 | function wordAsValue(stream) { 149 | var word = stream.current().toLowerCase(); 150 | if (valueKeywords.hasOwnProperty(word)) 151 | override = "atom"; 152 | else if (colorKeywords.hasOwnProperty(word)) 153 | override = "keyword"; 154 | else 155 | override = "variable"; 156 | } 157 | 158 | var states = {}; 159 | 160 | states.top = function(type, stream, state) { 161 | if (type == "{") { 162 | return pushContext(state, stream, "block"); 163 | } else if (type == "}" && state.context.prev) { 164 | return popContext(state); 165 | } else if (supportsAtComponent && /@component/i.test(type)) { 166 | return pushContext(state, stream, "atComponentBlock"); 167 | } else if (/^@(-moz-)?document$/i.test(type)) { 168 | return pushContext(state, stream, "documentTypes"); 169 | } else if (/^@(media|supports|(-moz-)?document|import)$/i.test(type)) { 170 | return pushContext(state, stream, "atBlock"); 171 | } else if (/^@(font-face|counter-style)/i.test(type)) { 172 | state.stateArg = type; 173 | return "restricted_atBlock_before"; 174 | } else if (/^@(-(moz|ms|o|webkit)-)?keyframes$/i.test(type)) { 175 | return "keyframes"; 176 | } else if (type && type.charAt(0) == "@") { 177 | return pushContext(state, stream, "at"); 178 | } else if (type == "hash") { 179 | override = "builtin"; 180 | } else if (type == "word") { 181 | override = "tag"; 182 | } else if (type == "variable-definition") { 183 | return "maybeprop"; 184 | } else if (type == "interpolation") { 185 | return pushContext(state, stream, "interpolation"); 186 | } else if (type == ":") { 187 | return "pseudo"; 188 | } else if (allowNested && type == "(") { 189 | return pushContext(state, stream, "parens"); 190 | } 191 | return state.context.type; 192 | }; 193 | 194 | states.block = function(type, stream, state) { 195 | if (type == "word") { 196 | var word = stream.current().toLowerCase(); 197 | if (propertyKeywords.hasOwnProperty(word)) { 198 | override = "property"; 199 | return "maybeprop"; 200 | } else if (nonStandardPropertyKeywords.hasOwnProperty(word)) { 201 | override = highlightNonStandardPropertyKeywords ? "string-2" : "property"; 202 | return "maybeprop"; 203 | } else if (allowNested) { 204 | override = stream.match(/^\s*:(?:\s|$)/, false) ? "property" : "tag"; 205 | return "block"; 206 | } else { 207 | override += " error"; 208 | return "maybeprop"; 209 | } 210 | } else if (type == "meta") { 211 | return "block"; 212 | } else if (!allowNested && (type == "hash" || type == "qualifier")) { 213 | override = "error"; 214 | return "block"; 215 | } else { 216 | return states.top(type, stream, state); 217 | } 218 | }; 219 | 220 | states.maybeprop = function(type, stream, state) { 221 | if (type == ":") return pushContext(state, stream, "prop"); 222 | return pass(type, stream, state); 223 | }; 224 | 225 | states.prop = function(type, stream, state) { 226 | if (type == ";") return popContext(state); 227 | if (type == "{" && allowNested) return pushContext(state, stream, "propBlock"); 228 | if (type == "}" || type == "{") return popAndPass(type, stream, state); 229 | if (type == "(") return pushContext(state, stream, "parens"); 230 | 231 | if (type == "hash" && !/^#([0-9a-fA-f]{3,4}|[0-9a-fA-f]{6}|[0-9a-fA-f]{8})$/.test(stream.current())) { 232 | override += " error"; 233 | } else if (type == "word") { 234 | wordAsValue(stream); 235 | } else if (type == "interpolation") { 236 | return pushContext(state, stream, "interpolation"); 237 | } 238 | return "prop"; 239 | }; 240 | 241 | states.propBlock = function(type, _stream, state) { 242 | if (type == "}") return popContext(state); 243 | if (type == "word") { override = "property"; return "maybeprop"; } 244 | return state.context.type; 245 | }; 246 | 247 | states.parens = function(type, stream, state) { 248 | if (type == "{" || type == "}") return popAndPass(type, stream, state); 249 | if (type == ")") return popContext(state); 250 | if (type == "(") return pushContext(state, stream, "parens"); 251 | if (type == "interpolation") return pushContext(state, stream, "interpolation"); 252 | if (type == "word") wordAsValue(stream); 253 | return "parens"; 254 | }; 255 | 256 | states.pseudo = function(type, stream, state) { 257 | if (type == "meta") return "pseudo"; 258 | 259 | if (type == "word") { 260 | override = "variable-3"; 261 | return state.context.type; 262 | } 263 | return pass(type, stream, state); 264 | }; 265 | 266 | states.documentTypes = function(type, stream, state) { 267 | if (type == "word" && documentTypes.hasOwnProperty(stream.current())) { 268 | override = "tag"; 269 | return state.context.type; 270 | } else { 271 | return states.atBlock(type, stream, state); 272 | } 273 | }; 274 | 275 | states.atBlock = function(type, stream, state) { 276 | if (type == "(") return pushContext(state, stream, "atBlock_parens"); 277 | if (type == "}" || type == ";") return popAndPass(type, stream, state); 278 | if (type == "{") return popContext(state) && pushContext(state, stream, allowNested ? "block" : "top"); 279 | 280 | if (type == "interpolation") return pushContext(state, stream, "interpolation"); 281 | 282 | if (type == "word") { 283 | var word = stream.current().toLowerCase(); 284 | if (word == "only" || word == "not" || word == "and" || word == "or") 285 | override = "keyword"; 286 | else if (mediaTypes.hasOwnProperty(word)) 287 | override = "attribute"; 288 | else if (mediaFeatures.hasOwnProperty(word)) 289 | override = "property"; 290 | else if (mediaValueKeywords.hasOwnProperty(word)) 291 | override = "keyword"; 292 | else if (propertyKeywords.hasOwnProperty(word)) 293 | override = "property"; 294 | else if (nonStandardPropertyKeywords.hasOwnProperty(word)) 295 | override = highlightNonStandardPropertyKeywords ? "string-2" : "property"; 296 | else if (valueKeywords.hasOwnProperty(word)) 297 | override = "atom"; 298 | else if (colorKeywords.hasOwnProperty(word)) 299 | override = "keyword"; 300 | else 301 | override = "error"; 302 | } 303 | return state.context.type; 304 | }; 305 | 306 | states.atComponentBlock = function(type, stream, state) { 307 | if (type == "}") 308 | return popAndPass(type, stream, state); 309 | if (type == "{") 310 | return popContext(state) && pushContext(state, stream, allowNested ? "block" : "top", false); 311 | if (type == "word") 312 | override = "error"; 313 | return state.context.type; 314 | }; 315 | 316 | states.atBlock_parens = function(type, stream, state) { 317 | if (type == ")") return popContext(state); 318 | if (type == "{" || type == "}") return popAndPass(type, stream, state, 2); 319 | return states.atBlock(type, stream, state); 320 | }; 321 | 322 | states.restricted_atBlock_before = function(type, stream, state) { 323 | if (type == "{") 324 | return pushContext(state, stream, "restricted_atBlock"); 325 | if (type == "word" && state.stateArg == "@counter-style") { 326 | override = "variable"; 327 | return "restricted_atBlock_before"; 328 | } 329 | return pass(type, stream, state); 330 | }; 331 | 332 | states.restricted_atBlock = function(type, stream, state) { 333 | if (type == "}") { 334 | state.stateArg = null; 335 | return popContext(state); 336 | } 337 | if (type == "word") { 338 | if ((state.stateArg == "@font-face" && !fontProperties.hasOwnProperty(stream.current().toLowerCase())) || 339 | (state.stateArg == "@counter-style" && !counterDescriptors.hasOwnProperty(stream.current().toLowerCase()))) 340 | override = "error"; 341 | else 342 | override = "property"; 343 | return "maybeprop"; 344 | } 345 | return "restricted_atBlock"; 346 | }; 347 | 348 | states.keyframes = function(type, stream, state) { 349 | if (type == "word") { override = "variable"; return "keyframes"; } 350 | if (type == "{") return pushContext(state, stream, "top"); 351 | return pass(type, stream, state); 352 | }; 353 | 354 | states.at = function(type, stream, state) { 355 | if (type == ";") return popContext(state); 356 | if (type == "{" || type == "}") return popAndPass(type, stream, state); 357 | if (type == "word") override = "tag"; 358 | else if (type == "hash") override = "builtin"; 359 | return "at"; 360 | }; 361 | 362 | states.interpolation = function(type, stream, state) { 363 | if (type == "}") return popContext(state); 364 | if (type == "{" || type == ";") return popAndPass(type, stream, state); 365 | if (type == "word") override = "variable"; 366 | else if (type != "variable" && type != "(" && type != ")") override = "error"; 367 | return "interpolation"; 368 | }; 369 | 370 | return { 371 | startState: function(base) { 372 | return {tokenize: null, 373 | state: inline ? "block" : "top", 374 | stateArg: null, 375 | context: new Context(inline ? "block" : "top", base || 0, null)}; 376 | }, 377 | 378 | token: function(stream, state) { 379 | if (!state.tokenize && stream.eatSpace()) return null; 380 | var style = (state.tokenize || tokenBase)(stream, state); 381 | if (style && typeof style == "object") { 382 | type = style[1]; 383 | style = style[0]; 384 | } 385 | override = style; 386 | if (type != "comment") 387 | state.state = states[state.state](type, stream, state); 388 | return override; 389 | }, 390 | 391 | indent: function(state, textAfter) { 392 | var cx = state.context, ch = textAfter && textAfter.charAt(0); 393 | var indent = cx.indent; 394 | if (cx.type == "prop" && (ch == "}" || ch == ")")) cx = cx.prev; 395 | if (cx.prev) { 396 | if (ch == "}" && (cx.type == "block" || cx.type == "top" || 397 | cx.type == "interpolation" || cx.type == "restricted_atBlock")) { 398 | // Resume indentation from parent context. 399 | cx = cx.prev; 400 | indent = cx.indent; 401 | } else if (ch == ")" && (cx.type == "parens" || cx.type == "atBlock_parens") || 402 | ch == "{" && (cx.type == "at" || cx.type == "atBlock")) { 403 | // Dedent relative to current context. 404 | indent = Math.max(0, cx.indent - indentUnit); 405 | } 406 | } 407 | return indent; 408 | }, 409 | 410 | electricChars: "}", 411 | blockCommentStart: "/*", 412 | blockCommentEnd: "*/", 413 | blockCommentContinue: " * ", 414 | lineComment: lineComment, 415 | fold: "brace" 416 | }; 417 | }); 418 | 419 | function keySet(array) { 420 | var keys = {}; 421 | for (var i = 0; i < array.length; ++i) { 422 | keys[array[i].toLowerCase()] = true; 423 | } 424 | return keys; 425 | } 426 | 427 | var documentTypes_ = [ 428 | "domain", "regexp", "url", "url-prefix" 429 | ], documentTypes = keySet(documentTypes_); 430 | 431 | var mediaTypes_ = [ 432 | "all", "aural", "braille", "handheld", "print", "projection", "screen", 433 | "tty", "tv", "embossed" 434 | ], mediaTypes = keySet(mediaTypes_); 435 | 436 | var mediaFeatures_ = [ 437 | "width", "min-width", "max-width", "height", "min-height", "max-height", 438 | "device-width", "min-device-width", "max-device-width", "device-height", 439 | "min-device-height", "max-device-height", "aspect-ratio", 440 | "min-aspect-ratio", "max-aspect-ratio", "device-aspect-ratio", 441 | "min-device-aspect-ratio", "max-device-aspect-ratio", "color", "min-color", 442 | "max-color", "color-index", "min-color-index", "max-color-index", 443 | "monochrome", "min-monochrome", "max-monochrome", "resolution", 444 | "min-resolution", "max-resolution", "scan", "grid", "orientation", 445 | "device-pixel-ratio", "min-device-pixel-ratio", "max-device-pixel-ratio", 446 | "pointer", "any-pointer", "hover", "any-hover", "prefers-color-scheme", 447 | "dynamic-range", "video-dynamic-range" 448 | ], mediaFeatures = keySet(mediaFeatures_); 449 | 450 | var mediaValueKeywords_ = [ 451 | "landscape", "portrait", "none", "coarse", "fine", "on-demand", "hover", 452 | "interlace", "progressive", 453 | "dark", "light", 454 | "standard", "high" 455 | ], mediaValueKeywords = keySet(mediaValueKeywords_); 456 | 457 | var propertyKeywords_ = [ 458 | "align-content", "align-items", "align-self", "alignment-adjust", 459 | "alignment-baseline", "all", "anchor-point", "animation", "animation-delay", 460 | "animation-direction", "animation-duration", "animation-fill-mode", 461 | "animation-iteration-count", "animation-name", "animation-play-state", 462 | "animation-timing-function", "appearance", "azimuth", "backdrop-filter", 463 | "backface-visibility", "background", "background-attachment", 464 | "background-blend-mode", "background-clip", "background-color", 465 | "background-image", "background-origin", "background-position", 466 | "background-position-x", "background-position-y", "background-repeat", 467 | "background-size", "baseline-shift", "binding", "bleed", "block-size", 468 | "bookmark-label", "bookmark-level", "bookmark-state", "bookmark-target", 469 | "border", "border-bottom", "border-bottom-color", "border-bottom-left-radius", 470 | "border-bottom-right-radius", "border-bottom-style", "border-bottom-width", 471 | "border-collapse", "border-color", "border-image", "border-image-outset", 472 | "border-image-repeat", "border-image-slice", "border-image-source", 473 | "border-image-width", "border-left", "border-left-color", "border-left-style", 474 | "border-left-width", "border-radius", "border-right", "border-right-color", 475 | "border-right-style", "border-right-width", "border-spacing", "border-style", 476 | "border-top", "border-top-color", "border-top-left-radius", 477 | "border-top-right-radius", "border-top-style", "border-top-width", 478 | "border-width", "bottom", "box-decoration-break", "box-shadow", "box-sizing", 479 | "break-after", "break-before", "break-inside", "caption-side", "caret-color", 480 | "clear", "clip", "color", "color-profile", "column-count", "column-fill", 481 | "column-gap", "column-rule", "column-rule-color", "column-rule-style", 482 | "column-rule-width", "column-span", "column-width", "columns", "contain", 483 | "content", "counter-increment", "counter-reset", "crop", "cue", "cue-after", 484 | "cue-before", "cursor", "direction", "display", "dominant-baseline", 485 | "drop-initial-after-adjust", "drop-initial-after-align", 486 | "drop-initial-before-adjust", "drop-initial-before-align", "drop-initial-size", 487 | "drop-initial-value", "elevation", "empty-cells", "fit", "fit-content", "fit-position", 488 | "flex", "flex-basis", "flex-direction", "flex-flow", "flex-grow", 489 | "flex-shrink", "flex-wrap", "float", "float-offset", "flow-from", "flow-into", 490 | "font", "font-family", "font-feature-settings", "font-kerning", 491 | "font-language-override", "font-optical-sizing", "font-size", 492 | "font-size-adjust", "font-stretch", "font-style", "font-synthesis", 493 | "font-variant", "font-variant-alternates", "font-variant-caps", 494 | "font-variant-east-asian", "font-variant-ligatures", "font-variant-numeric", 495 | "font-variant-position", "font-variation-settings", "font-weight", "gap", 496 | "grid", "grid-area", "grid-auto-columns", "grid-auto-flow", "grid-auto-rows", 497 | "grid-column", "grid-column-end", "grid-column-gap", "grid-column-start", 498 | "grid-gap", "grid-row", "grid-row-end", "grid-row-gap", "grid-row-start", 499 | "grid-template", "grid-template-areas", "grid-template-columns", 500 | "grid-template-rows", "hanging-punctuation", "height", "hyphens", "icon", 501 | "image-orientation", "image-rendering", "image-resolution", "inline-box-align", 502 | "inset", "inset-block", "inset-block-end", "inset-block-start", "inset-inline", 503 | "inset-inline-end", "inset-inline-start", "isolation", "justify-content", 504 | "justify-items", "justify-self", "left", "letter-spacing", "line-break", 505 | "line-height", "line-height-step", "line-stacking", "line-stacking-ruby", 506 | "line-stacking-shift", "line-stacking-strategy", "list-style", 507 | "list-style-image", "list-style-position", "list-style-type", "margin", 508 | "margin-bottom", "margin-left", "margin-right", "margin-top", "marks", 509 | "marquee-direction", "marquee-loop", "marquee-play-count", "marquee-speed", 510 | "marquee-style", "mask-clip", "mask-composite", "mask-image", "mask-mode", 511 | "mask-origin", "mask-position", "mask-repeat", "mask-size","mask-type", 512 | "max-block-size", "max-height", "max-inline-size", 513 | "max-width", "min-block-size", "min-height", "min-inline-size", "min-width", 514 | "mix-blend-mode", "move-to", "nav-down", "nav-index", "nav-left", "nav-right", 515 | "nav-up", "object-fit", "object-position", "offset", "offset-anchor", 516 | "offset-distance", "offset-path", "offset-position", "offset-rotate", 517 | "opacity", "order", "orphans", "outline", "outline-color", "outline-offset", 518 | "outline-style", "outline-width", "overflow", "overflow-style", 519 | "overflow-wrap", "overflow-x", "overflow-y", "padding", "padding-bottom", 520 | "padding-left", "padding-right", "padding-top", "page", "page-break-after", 521 | "page-break-before", "page-break-inside", "page-policy", "pause", 522 | "pause-after", "pause-before", "perspective", "perspective-origin", "pitch", 523 | "pitch-range", "place-content", "place-items", "place-self", "play-during", 524 | "position", "presentation-level", "punctuation-trim", "quotes", 525 | "region-break-after", "region-break-before", "region-break-inside", 526 | "region-fragment", "rendering-intent", "resize", "rest", "rest-after", 527 | "rest-before", "richness", "right", "rotate", "rotation", "rotation-point", 528 | "row-gap", "ruby-align", "ruby-overhang", "ruby-position", "ruby-span", 529 | "scale", "scroll-behavior", "scroll-margin", "scroll-margin-block", 530 | "scroll-margin-block-end", "scroll-margin-block-start", "scroll-margin-bottom", 531 | "scroll-margin-inline", "scroll-margin-inline-end", 532 | "scroll-margin-inline-start", "scroll-margin-left", "scroll-margin-right", 533 | "scroll-margin-top", "scroll-padding", "scroll-padding-block", 534 | "scroll-padding-block-end", "scroll-padding-block-start", 535 | "scroll-padding-bottom", "scroll-padding-inline", "scroll-padding-inline-end", 536 | "scroll-padding-inline-start", "scroll-padding-left", "scroll-padding-right", 537 | "scroll-padding-top", "scroll-snap-align", "scroll-snap-type", 538 | "shape-image-threshold", "shape-inside", "shape-margin", "shape-outside", 539 | "size", "speak", "speak-as", "speak-header", "speak-numeral", 540 | "speak-punctuation", "speech-rate", "stress", "string-set", "tab-size", 541 | "table-layout", "target", "target-name", "target-new", "target-position", 542 | "text-align", "text-align-last", "text-combine-upright", "text-decoration", 543 | "text-decoration-color", "text-decoration-line", "text-decoration-skip", 544 | "text-decoration-skip-ink", "text-decoration-style", "text-emphasis", 545 | "text-emphasis-color", "text-emphasis-position", "text-emphasis-style", 546 | "text-height", "text-indent", "text-justify", "text-orientation", 547 | "text-outline", "text-overflow", "text-rendering", "text-shadow", 548 | "text-size-adjust", "text-space-collapse", "text-transform", 549 | "text-underline-position", "text-wrap", "top", "touch-action", "transform", "transform-origin", 550 | "transform-style", "transition", "transition-delay", "transition-duration", 551 | "transition-property", "transition-timing-function", "translate", 552 | "unicode-bidi", "user-select", "vertical-align", "visibility", "voice-balance", 553 | "voice-duration", "voice-family", "voice-pitch", "voice-range", "voice-rate", 554 | "voice-stress", "voice-volume", "volume", "white-space", "widows", "width", 555 | "will-change", "word-break", "word-spacing", "word-wrap", "writing-mode", "z-index", 556 | // SVG-specific 557 | "clip-path", "clip-rule", "mask", "enable-background", "filter", "flood-color", 558 | "flood-opacity", "lighting-color", "stop-color", "stop-opacity", "pointer-events", 559 | "color-interpolation", "color-interpolation-filters", 560 | "color-rendering", "fill", "fill-opacity", "fill-rule", "image-rendering", 561 | "marker", "marker-end", "marker-mid", "marker-start", "paint-order", "shape-rendering", "stroke", 562 | "stroke-dasharray", "stroke-dashoffset", "stroke-linecap", "stroke-linejoin", 563 | "stroke-miterlimit", "stroke-opacity", "stroke-width", "text-rendering", 564 | "baseline-shift", "dominant-baseline", "glyph-orientation-horizontal", 565 | "glyph-orientation-vertical", "text-anchor", "writing-mode", 566 | ], propertyKeywords = keySet(propertyKeywords_); 567 | 568 | var nonStandardPropertyKeywords_ = [ 569 | "accent-color", "aspect-ratio", "border-block", "border-block-color", "border-block-end", 570 | "border-block-end-color", "border-block-end-style", "border-block-end-width", 571 | "border-block-start", "border-block-start-color", "border-block-start-style", 572 | "border-block-start-width", "border-block-style", "border-block-width", 573 | "border-inline", "border-inline-color", "border-inline-end", 574 | "border-inline-end-color", "border-inline-end-style", 575 | "border-inline-end-width", "border-inline-start", "border-inline-start-color", 576 | "border-inline-start-style", "border-inline-start-width", 577 | "border-inline-style", "border-inline-width", "content-visibility", "margin-block", 578 | "margin-block-end", "margin-block-start", "margin-inline", "margin-inline-end", 579 | "margin-inline-start", "overflow-anchor", "overscroll-behavior", "padding-block", "padding-block-end", 580 | "padding-block-start", "padding-inline", "padding-inline-end", 581 | "padding-inline-start", "scroll-snap-stop", "scrollbar-3d-light-color", 582 | "scrollbar-arrow-color", "scrollbar-base-color", "scrollbar-dark-shadow-color", 583 | "scrollbar-face-color", "scrollbar-highlight-color", "scrollbar-shadow-color", 584 | "scrollbar-track-color", "searchfield-cancel-button", "searchfield-decoration", 585 | "searchfield-results-button", "searchfield-results-decoration", "shape-inside", "zoom" 586 | ], nonStandardPropertyKeywords = keySet(nonStandardPropertyKeywords_); 587 | 588 | var fontProperties_ = [ 589 | "font-display", "font-family", "src", "unicode-range", "font-variant", 590 | "font-feature-settings", "font-stretch", "font-weight", "font-style" 591 | ], fontProperties = keySet(fontProperties_); 592 | 593 | var counterDescriptors_ = [ 594 | "additive-symbols", "fallback", "negative", "pad", "prefix", "range", 595 | "speak-as", "suffix", "symbols", "system" 596 | ], counterDescriptors = keySet(counterDescriptors_); 597 | 598 | var colorKeywords_ = [ 599 | "aliceblue", "antiquewhite", "aqua", "aquamarine", "azure", "beige", 600 | "bisque", "black", "blanchedalmond", "blue", "blueviolet", "brown", 601 | "burlywood", "cadetblue", "chartreuse", "chocolate", "coral", "cornflowerblue", 602 | "cornsilk", "crimson", "cyan", "darkblue", "darkcyan", "darkgoldenrod", 603 | "darkgray", "darkgreen", "darkgrey", "darkkhaki", "darkmagenta", "darkolivegreen", 604 | "darkorange", "darkorchid", "darkred", "darksalmon", "darkseagreen", 605 | "darkslateblue", "darkslategray", "darkslategrey", "darkturquoise", "darkviolet", 606 | "deeppink", "deepskyblue", "dimgray", "dimgrey", "dodgerblue", "firebrick", 607 | "floralwhite", "forestgreen", "fuchsia", "gainsboro", "ghostwhite", 608 | "gold", "goldenrod", "gray", "grey", "green", "greenyellow", "honeydew", 609 | "hotpink", "indianred", "indigo", "ivory", "khaki", "lavender", 610 | "lavenderblush", "lawngreen", "lemonchiffon", "lightblue", "lightcoral", 611 | "lightcyan", "lightgoldenrodyellow", "lightgray", "lightgreen", "lightgrey", "lightpink", 612 | "lightsalmon", "lightseagreen", "lightskyblue", "lightslategray", "lightslategrey", 613 | "lightsteelblue", "lightyellow", "lime", "limegreen", "linen", "magenta", 614 | "maroon", "mediumaquamarine", "mediumblue", "mediumorchid", "mediumpurple", 615 | "mediumseagreen", "mediumslateblue", "mediumspringgreen", "mediumturquoise", 616 | "mediumvioletred", "midnightblue", "mintcream", "mistyrose", "moccasin", 617 | "navajowhite", "navy", "oldlace", "olive", "olivedrab", "orange", "orangered", 618 | "orchid", "palegoldenrod", "palegreen", "paleturquoise", "palevioletred", 619 | "papayawhip", "peachpuff", "peru", "pink", "plum", "powderblue", 620 | "purple", "rebeccapurple", "red", "rosybrown", "royalblue", "saddlebrown", 621 | "salmon", "sandybrown", "seagreen", "seashell", "sienna", "silver", "skyblue", 622 | "slateblue", "slategray", "slategrey", "snow", "springgreen", "steelblue", "tan", 623 | "teal", "thistle", "tomato", "turquoise", "violet", "wheat", "white", 624 | "whitesmoke", "yellow", "yellowgreen" 625 | ], colorKeywords = keySet(colorKeywords_); 626 | 627 | var valueKeywords_ = [ 628 | "above", "absolute", "activeborder", "additive", "activecaption", "afar", 629 | "after-white-space", "ahead", "alias", "all", "all-scroll", "alphabetic", "alternate", 630 | "always", "amharic", "amharic-abegede", "antialiased", "appworkspace", 631 | "arabic-indic", "armenian", "asterisks", "attr", "auto", "auto-flow", "avoid", "avoid-column", "avoid-page", 632 | "avoid-region", "axis-pan", "background", "backwards", "baseline", "below", "bidi-override", "binary", 633 | "bengali", "blink", "block", "block-axis", "blur", "bold", "bolder", "border", "border-box", 634 | "both", "bottom", "break", "break-all", "break-word", "brightness", "bullets", "button", "button-bevel", 635 | "buttonface", "buttonhighlight", "buttonshadow", "buttontext", "calc", "cambodian", 636 | "capitalize", "caps-lock-indicator", "caption", "captiontext", "caret", 637 | "cell", "center", "checkbox", "circle", "cjk-decimal", "cjk-earthly-branch", 638 | "cjk-heavenly-stem", "cjk-ideographic", "clear", "clip", "close-quote", 639 | "col-resize", "collapse", "color", "color-burn", "color-dodge", "column", "column-reverse", 640 | "compact", "condensed", "contain", "content", "contents", 641 | "content-box", "context-menu", "continuous", "contrast", "copy", "counter", "counters", "cover", "crop", 642 | "cross", "crosshair", "cubic-bezier", "currentcolor", "cursive", "cyclic", "darken", "dashed", "decimal", 643 | "decimal-leading-zero", "default", "default-button", "dense", "destination-atop", 644 | "destination-in", "destination-out", "destination-over", "devanagari", "difference", 645 | "disc", "discard", "disclosure-closed", "disclosure-open", "document", 646 | "dot-dash", "dot-dot-dash", 647 | "dotted", "double", "down", "drop-shadow", "e-resize", "ease", "ease-in", "ease-in-out", "ease-out", 648 | "element", "ellipse", "ellipsis", "embed", "end", "ethiopic", "ethiopic-abegede", 649 | "ethiopic-abegede-am-et", "ethiopic-abegede-gez", "ethiopic-abegede-ti-er", 650 | "ethiopic-abegede-ti-et", "ethiopic-halehame-aa-er", 651 | "ethiopic-halehame-aa-et", "ethiopic-halehame-am-et", 652 | "ethiopic-halehame-gez", "ethiopic-halehame-om-et", 653 | "ethiopic-halehame-sid-et", "ethiopic-halehame-so-et", 654 | "ethiopic-halehame-ti-er", "ethiopic-halehame-ti-et", "ethiopic-halehame-tig", 655 | "ethiopic-numeric", "ew-resize", "exclusion", "expanded", "extends", "extra-condensed", 656 | "extra-expanded", "fantasy", "fast", "fill", "fill-box", "fixed", "flat", "flex", "flex-end", "flex-start", "footnotes", 657 | "forwards", "from", "geometricPrecision", "georgian", "grayscale", "graytext", "grid", "groove", 658 | "gujarati", "gurmukhi", "hand", "hangul", "hangul-consonant", "hard-light", "hebrew", 659 | "help", "hidden", "hide", "higher", "highlight", "highlighttext", 660 | "hiragana", "hiragana-iroha", "horizontal", "hsl", "hsla", "hue", "hue-rotate", "icon", "ignore", 661 | "inactiveborder", "inactivecaption", "inactivecaptiontext", "infinite", 662 | "infobackground", "infotext", "inherit", "initial", "inline", "inline-axis", 663 | "inline-block", "inline-flex", "inline-grid", "inline-table", "inset", "inside", "intrinsic", "invert", 664 | "italic", "japanese-formal", "japanese-informal", "justify", "kannada", 665 | "katakana", "katakana-iroha", "keep-all", "khmer", 666 | "korean-hangul-formal", "korean-hanja-formal", "korean-hanja-informal", 667 | "landscape", "lao", "large", "larger", "left", "level", "lighter", "lighten", 668 | "line-through", "linear", "linear-gradient", "lines", "list-item", "listbox", "listitem", 669 | "local", "logical", "loud", "lower", "lower-alpha", "lower-armenian", 670 | "lower-greek", "lower-hexadecimal", "lower-latin", "lower-norwegian", 671 | "lower-roman", "lowercase", "ltr", "luminosity", "malayalam", "manipulation", "match", "matrix", "matrix3d", 672 | "media-controls-background", "media-current-time-display", 673 | "media-fullscreen-button", "media-mute-button", "media-play-button", 674 | "media-return-to-realtime-button", "media-rewind-button", 675 | "media-seek-back-button", "media-seek-forward-button", "media-slider", 676 | "media-sliderthumb", "media-time-remaining-display", "media-volume-slider", 677 | "media-volume-slider-container", "media-volume-sliderthumb", "medium", 678 | "menu", "menulist", "menulist-button", "menulist-text", 679 | "menulist-textfield", "menutext", "message-box", "middle", "min-intrinsic", 680 | "mix", "mongolian", "monospace", "move", "multiple", "multiple_mask_images", "multiply", "myanmar", "n-resize", 681 | "narrower", "ne-resize", "nesw-resize", "no-close-quote", "no-drop", 682 | "no-open-quote", "no-repeat", "none", "normal", "not-allowed", "nowrap", 683 | "ns-resize", "numbers", "numeric", "nw-resize", "nwse-resize", "oblique", "octal", "opacity", "open-quote", 684 | "optimizeLegibility", "optimizeSpeed", "oriya", "oromo", "outset", 685 | "outside", "outside-shape", "overlay", "overline", "padding", "padding-box", 686 | "painted", "page", "paused", "persian", "perspective", "pinch-zoom", "plus-darker", "plus-lighter", 687 | "pointer", "polygon", "portrait", "pre", "pre-line", "pre-wrap", "preserve-3d", 688 | "progress", "push-button", "radial-gradient", "radio", "read-only", 689 | "read-write", "read-write-plaintext-only", "rectangle", "region", 690 | "relative", "repeat", "repeating-linear-gradient", 691 | "repeating-radial-gradient", "repeat-x", "repeat-y", "reset", "reverse", 692 | "rgb", "rgba", "ridge", "right", "rotate", "rotate3d", "rotateX", "rotateY", 693 | "rotateZ", "round", "row", "row-resize", "row-reverse", "rtl", "run-in", "running", 694 | "s-resize", "sans-serif", "saturate", "saturation", "scale", "scale3d", "scaleX", "scaleY", "scaleZ", "screen", 695 | "scroll", "scrollbar", "scroll-position", "se-resize", "searchfield", 696 | "searchfield-cancel-button", "searchfield-decoration", 697 | "searchfield-results-button", "searchfield-results-decoration", "self-start", "self-end", 698 | "semi-condensed", "semi-expanded", "separate", "sepia", "serif", "show", "sidama", 699 | "simp-chinese-formal", "simp-chinese-informal", "single", 700 | "skew", "skewX", "skewY", "skip-white-space", "slide", "slider-horizontal", 701 | "slider-vertical", "sliderthumb-horizontal", "sliderthumb-vertical", "slow", 702 | "small", "small-caps", "small-caption", "smaller", "soft-light", "solid", "somali", 703 | "source-atop", "source-in", "source-out", "source-over", "space", "space-around", "space-between", "space-evenly", "spell-out", "square", 704 | "square-button", "start", "static", "status-bar", "stretch", "stroke", "stroke-box", "sub", 705 | "subpixel-antialiased", "svg_masks", "super", "sw-resize", "symbolic", "symbols", "system-ui", "table", 706 | "table-caption", "table-cell", "table-column", "table-column-group", 707 | "table-footer-group", "table-header-group", "table-row", "table-row-group", 708 | "tamil", 709 | "telugu", "text", "text-bottom", "text-top", "textarea", "textfield", "thai", 710 | "thick", "thin", "threeddarkshadow", "threedface", "threedhighlight", 711 | "threedlightshadow", "threedshadow", "tibetan", "tigre", "tigrinya-er", 712 | "tigrinya-er-abegede", "tigrinya-et", "tigrinya-et-abegede", "to", "top", 713 | "trad-chinese-formal", "trad-chinese-informal", "transform", 714 | "translate", "translate3d", "translateX", "translateY", "translateZ", 715 | "transparent", "ultra-condensed", "ultra-expanded", "underline", "unidirectional-pan", "unset", "up", 716 | "upper-alpha", "upper-armenian", "upper-greek", "upper-hexadecimal", 717 | "upper-latin", "upper-norwegian", "upper-roman", "uppercase", "urdu", "url", 718 | "var", "vertical", "vertical-text", "view-box", "visible", "visibleFill", "visiblePainted", 719 | "visibleStroke", "visual", "w-resize", "wait", "wave", "wider", 720 | "window", "windowframe", "windowtext", "words", "wrap", "wrap-reverse", "x-large", "x-small", "xor", 721 | "xx-large", "xx-small" 722 | ], valueKeywords = keySet(valueKeywords_); 723 | 724 | var allWords = documentTypes_.concat(mediaTypes_).concat(mediaFeatures_).concat(mediaValueKeywords_) 725 | .concat(propertyKeywords_).concat(nonStandardPropertyKeywords_).concat(colorKeywords_) 726 | .concat(valueKeywords_); 727 | CodeMirror.registerHelper("hintWords", "css", allWords); 728 | 729 | function tokenCComment(stream, state) { 730 | var maybeEnd = false, ch; 731 | while ((ch = stream.next()) != null) { 732 | if (maybeEnd && ch == "/") { 733 | state.tokenize = null; 734 | break; 735 | } 736 | maybeEnd = (ch == "*"); 737 | } 738 | return ["comment", "comment"]; 739 | } 740 | 741 | CodeMirror.defineMIME("text/css", { 742 | documentTypes: documentTypes, 743 | mediaTypes: mediaTypes, 744 | mediaFeatures: mediaFeatures, 745 | mediaValueKeywords: mediaValueKeywords, 746 | propertyKeywords: propertyKeywords, 747 | nonStandardPropertyKeywords: nonStandardPropertyKeywords, 748 | fontProperties: fontProperties, 749 | counterDescriptors: counterDescriptors, 750 | colorKeywords: colorKeywords, 751 | valueKeywords: valueKeywords, 752 | tokenHooks: { 753 | "/": function(stream, state) { 754 | if (!stream.eat("*")) return false; 755 | state.tokenize = tokenCComment; 756 | return tokenCComment(stream, state); 757 | } 758 | }, 759 | name: "css" 760 | }); 761 | 762 | CodeMirror.defineMIME("text/x-scss", { 763 | mediaTypes: mediaTypes, 764 | mediaFeatures: mediaFeatures, 765 | mediaValueKeywords: mediaValueKeywords, 766 | propertyKeywords: propertyKeywords, 767 | nonStandardPropertyKeywords: nonStandardPropertyKeywords, 768 | colorKeywords: colorKeywords, 769 | valueKeywords: valueKeywords, 770 | fontProperties: fontProperties, 771 | allowNested: true, 772 | lineComment: "//", 773 | tokenHooks: { 774 | "/": function(stream, state) { 775 | if (stream.eat("/")) { 776 | stream.skipToEnd(); 777 | return ["comment", "comment"]; 778 | } else if (stream.eat("*")) { 779 | state.tokenize = tokenCComment; 780 | return tokenCComment(stream, state); 781 | } else { 782 | return ["operator", "operator"]; 783 | } 784 | }, 785 | ":": function(stream) { 786 | if (stream.match(/^\s*\{/, false)) 787 | return [null, null] 788 | return false; 789 | }, 790 | "$": function(stream) { 791 | stream.match(/^[\w-]+/); 792 | if (stream.match(/^\s*:/, false)) 793 | return ["variable-2", "variable-definition"]; 794 | return ["variable-2", "variable"]; 795 | }, 796 | "#": function(stream) { 797 | if (!stream.eat("{")) return false; 798 | return [null, "interpolation"]; 799 | } 800 | }, 801 | name: "css", 802 | helperType: "scss" 803 | }); 804 | 805 | CodeMirror.defineMIME("text/x-less", { 806 | mediaTypes: mediaTypes, 807 | mediaFeatures: mediaFeatures, 808 | mediaValueKeywords: mediaValueKeywords, 809 | propertyKeywords: propertyKeywords, 810 | nonStandardPropertyKeywords: nonStandardPropertyKeywords, 811 | colorKeywords: colorKeywords, 812 | valueKeywords: valueKeywords, 813 | fontProperties: fontProperties, 814 | allowNested: true, 815 | lineComment: "//", 816 | tokenHooks: { 817 | "/": function(stream, state) { 818 | if (stream.eat("/")) { 819 | stream.skipToEnd(); 820 | return ["comment", "comment"]; 821 | } else if (stream.eat("*")) { 822 | state.tokenize = tokenCComment; 823 | return tokenCComment(stream, state); 824 | } else { 825 | return ["operator", "operator"]; 826 | } 827 | }, 828 | "@": function(stream) { 829 | if (stream.eat("{")) return [null, "interpolation"]; 830 | if (stream.match(/^(charset|document|font-face|import|(-(moz|ms|o|webkit)-)?keyframes|media|namespace|page|supports)\b/i, false)) return false; 831 | stream.eatWhile(/[\w\\\-]/); 832 | if (stream.match(/^\s*:/, false)) 833 | return ["variable-2", "variable-definition"]; 834 | return ["variable-2", "variable"]; 835 | }, 836 | "&": function() { 837 | return ["atom", "atom"]; 838 | } 839 | }, 840 | name: "css", 841 | helperType: "less" 842 | }); 843 | 844 | CodeMirror.defineMIME("text/x-gss", { 845 | documentTypes: documentTypes, 846 | mediaTypes: mediaTypes, 847 | mediaFeatures: mediaFeatures, 848 | propertyKeywords: propertyKeywords, 849 | nonStandardPropertyKeywords: nonStandardPropertyKeywords, 850 | fontProperties: fontProperties, 851 | counterDescriptors: counterDescriptors, 852 | colorKeywords: colorKeywords, 853 | valueKeywords: valueKeywords, 854 | supportsAtComponent: true, 855 | tokenHooks: { 856 | "/": function(stream, state) { 857 | if (!stream.eat("*")) return false; 858 | state.tokenize = tokenCComment; 859 | return tokenCComment(stream, state); 860 | } 861 | }, 862 | name: "css", 863 | helperType: "gss" 864 | }); 865 | 866 | }); 867 | -------------------------------------------------------------------------------- /src/js/content.js: -------------------------------------------------------------------------------- 1 | /* global window, document, chrome, top, BigInt */ 2 | 3 | const CLASS_COLLAPSED = "collapsed"; 4 | const CLASS_HOVERED = "hovered"; 5 | const CLASS_SELECTED = "selected"; 6 | const TAG_LIST_ITEM = "LI"; 7 | const TITLE_OPEN_COLLAPSIBLES = "expand all"; 8 | const LABEL_OPEN_COLLAPSIBLES = "+"; 9 | const TITLE_CLOSE_COLLAPSIBLES = "collapse all"; 10 | const LABEL_CLOSE_COLLAPSIBLES = "-"; 11 | const TITLE_VIEW_SOURCE = "view unformatted source"; 12 | const LABEL_VIEW_SOURCE = "view source"; 13 | const MENU_ID_COPY_PATH = "copy-path"; 14 | const MENU_ID_COPY_VALUE = "copy-value"; 15 | const MENU_ID_COPY_JSON_VALUE = "copy-json-value"; 16 | // cf. https://github.com/mathiasbynens/mothereff.in/blob/master/js-variables/eff.js 17 | // eslint-disable-next-line no-misleading-character-class 18 | const REGEXP_IDENTIFIER = /^(?:[$A-Z_a-z\xAA\xB5\xBA\xC0-\xD6\xD8-\xF6\xF8-\u02C1\u02C6-\u02D1\u02E0-\u02E4\u02EC\u02EE\u0370-\u0374\u0376\u0377\u037A-\u037D\u037F\u0386\u0388-\u038A\u038C\u038E-\u03A1\u03A3-\u03F5\u03F7-\u0481\u048A-\u052F\u0531-\u0556\u0559\u0561-\u0587\u05D0-\u05EA\u05F0-\u05F2\u0620-\u064A\u066E\u066F\u0671-\u06D3\u06D5\u06E5\u06E6\u06EE\u06EF\u06FA-\u06FC\u06FF\u0710\u0712-\u072F\u074D-\u07A5\u07B1\u07CA-\u07EA\u07F4\u07F5\u07FA\u0800-\u0815\u081A\u0824\u0828\u0840-\u0858\u08A0-\u08B4\u0904-\u0939\u093D\u0950\u0958-\u0961\u0971-\u0980\u0985-\u098C\u098F\u0990\u0993-\u09A8\u09AA-\u09B0\u09B2\u09B6-\u09B9\u09BD\u09CE\u09DC\u09DD\u09DF-\u09E1\u09F0\u09F1\u0A05-\u0A0A\u0A0F\u0A10\u0A13-\u0A28\u0A2A-\u0A30\u0A32\u0A33\u0A35\u0A36\u0A38\u0A39\u0A59-\u0A5C\u0A5E\u0A72-\u0A74\u0A85-\u0A8D\u0A8F-\u0A91\u0A93-\u0AA8\u0AAA-\u0AB0\u0AB2\u0AB3\u0AB5-\u0AB9\u0ABD\u0AD0\u0AE0\u0AE1\u0AF9\u0B05-\u0B0C\u0B0F\u0B10\u0B13-\u0B28\u0B2A-\u0B30\u0B32\u0B33\u0B35-\u0B39\u0B3D\u0B5C\u0B5D\u0B5F-\u0B61\u0B71\u0B83\u0B85-\u0B8A\u0B8E-\u0B90\u0B92-\u0B95\u0B99\u0B9A\u0B9C\u0B9E\u0B9F\u0BA3\u0BA4\u0BA8-\u0BAA\u0BAE-\u0BB9\u0BD0\u0C05-\u0C0C\u0C0E-\u0C10\u0C12-\u0C28\u0C2A-\u0C39\u0C3D\u0C58-\u0C5A\u0C60\u0C61\u0C85-\u0C8C\u0C8E-\u0C90\u0C92-\u0CA8\u0CAA-\u0CB3\u0CB5-\u0CB9\u0CBD\u0CDE\u0CE0\u0CE1\u0CF1\u0CF2\u0D05-\u0D0C\u0D0E-\u0D10\u0D12-\u0D3A\u0D3D\u0D4E\u0D5F-\u0D61\u0D7A-\u0D7F\u0D85-\u0D96\u0D9A-\u0DB1\u0DB3-\u0DBB\u0DBD\u0DC0-\u0DC6\u0E01-\u0E30\u0E32\u0E33\u0E40-\u0E46\u0E81\u0E82\u0E84\u0E87\u0E88\u0E8A\u0E8D\u0E94-\u0E97\u0E99-\u0E9F\u0EA1-\u0EA3\u0EA5\u0EA7\u0EAA\u0EAB\u0EAD-\u0EB0\u0EB2\u0EB3\u0EBD\u0EC0-\u0EC4\u0EC6\u0EDC-\u0EDF\u0F00\u0F40-\u0F47\u0F49-\u0F6C\u0F88-\u0F8C\u1000-\u102A\u103F\u1050-\u1055\u105A-\u105D\u1061\u1065\u1066\u106E-\u1070\u1075-\u1081\u108E\u10A0-\u10C5\u10C7\u10CD\u10D0-\u10FA\u10FC-\u1248\u124A-\u124D\u1250-\u1256\u1258\u125A-\u125D\u1260-\u1288\u128A-\u128D\u1290-\u12B0\u12B2-\u12B5\u12B8-\u12BE\u12C0\u12C2-\u12C5\u12C8-\u12D6\u12D8-\u1310\u1312-\u1315\u1318-\u135A\u1380-\u138F\u13A0-\u13F5\u13F8-\u13FD\u1401-\u166C\u166F-\u167F\u1681-\u169A\u16A0-\u16EA\u16EE-\u16F8\u1700-\u170C\u170E-\u1711\u1720-\u1731\u1740-\u1751\u1760-\u176C\u176E-\u1770\u1780-\u17B3\u17D7\u17DC\u1820-\u1877\u1880-\u18A8\u18AA\u18B0-\u18F5\u1900-\u191E\u1950-\u196D\u1970-\u1974\u1980-\u19AB\u19B0-\u19C9\u1A00-\u1A16\u1A20-\u1A54\u1AA7\u1B05-\u1B33\u1B45-\u1B4B\u1B83-\u1BA0\u1BAE\u1BAF\u1BBA-\u1BE5\u1C00-\u1C23\u1C4D-\u1C4F\u1C5A-\u1C7D\u1CE9-\u1CEC\u1CEE-\u1CF1\u1CF5\u1CF6\u1D00-\u1DBF\u1E00-\u1F15\u1F18-\u1F1D\u1F20-\u1F45\u1F48-\u1F4D\u1F50-\u1F57\u1F59\u1F5B\u1F5D\u1F5F-\u1F7D\u1F80-\u1FB4\u1FB6-\u1FBC\u1FBE\u1FC2-\u1FC4\u1FC6-\u1FCC\u1FD0-\u1FD3\u1FD6-\u1FDB\u1FE0-\u1FEC\u1FF2-\u1FF4\u1FF6-\u1FFC\u2071\u207F\u2090-\u209C\u2102\u2107\u210A-\u2113\u2115\u2118-\u211D\u2124\u2126\u2128\u212A-\u2139\u213C-\u213F\u2145-\u2149\u214E\u2160-\u2188\u2C00-\u2C2E\u2C30-\u2C5E\u2C60-\u2CE4\u2CEB-\u2CEE\u2CF2\u2CF3\u2D00-\u2D25\u2D27\u2D2D\u2D30-\u2D67\u2D6F\u2D80-\u2D96\u2DA0-\u2DA6\u2DA8-\u2DAE\u2DB0-\u2DB6\u2DB8-\u2DBE\u2DC0-\u2DC6\u2DC8-\u2DCE\u2DD0-\u2DD6\u2DD8-\u2DDE\u3005-\u3007\u3021-\u3029\u3031-\u3035\u3038-\u303C\u3041-\u3096\u309B-\u309F\u30A1-\u30FA\u30FC-\u30FF\u3105-\u312D\u3131-\u318E\u31A0-\u31BA\u31F0-\u31FF\u3400-\u4DB5\u4E00-\u9FD5\uA000-\uA48C\uA4D0-\uA4FD\uA500-\uA60C\uA610-\uA61F\uA62A\uA62B\uA640-\uA66E\uA67F-\uA69D\uA6A0-\uA6EF\uA717-\uA71F\uA722-\uA788\uA78B-\uA7AD\uA7B0-\uA7B7\uA7F7-\uA801\uA803-\uA805\uA807-\uA80A\uA80C-\uA822\uA840-\uA873\uA882-\uA8B3\uA8F2-\uA8F7\uA8FB\uA8FD\uA90A-\uA925\uA930-\uA946\uA960-\uA97C\uA984-\uA9B2\uA9CF\uA9E0-\uA9E4\uA9E6-\uA9EF\uA9FA-\uA9FE\uAA00-\uAA28\uAA40-\uAA42\uAA44-\uAA4B\uAA60-\uAA76\uAA7A\uAA7E-\uAAAF\uAAB1\uAAB5\uAAB6\uAAB9-\uAABD\uAAC0\uAAC2\uAADB-\uAADD\uAAE0-\uAAEA\uAAF2-\uAAF4\uAB01-\uAB06\uAB09-\uAB0E\uAB11-\uAB16\uAB20-\uAB26\uAB28-\uAB2E\uAB30-\uAB5A\uAB5C-\uAB65\uAB70-\uABE2\uAC00-\uD7A3\uD7B0-\uD7C6\uD7CB-\uD7FB\uF900-\uFA6D\uFA70-\uFAD9\uFB00-\uFB06\uFB13-\uFB17\uFB1D\uFB1F-\uFB28\uFB2A-\uFB36\uFB38-\uFB3C\uFB3E\uFB40\uFB41\uFB43\uFB44\uFB46-\uFBB1\uFBD3-\uFD3D\uFD50-\uFD8F\uFD92-\uFDC7\uFDF0-\uFDFB\uFE70-\uFE74\uFE76-\uFEFC\uFF21-\uFF3A\uFF41-\uFF5A\uFF66-\uFFBE\uFFC2-\uFFC7\uFFCA-\uFFCF\uFFD2-\uFFD7\uFFDA-\uFFDC]|\uD800[\uDC00-\uDC0B\uDC0D-\uDC26\uDC28-\uDC3A\uDC3C\uDC3D\uDC3F-\uDC4D\uDC50-\uDC5D\uDC80-\uDCFA\uDD40-\uDD74\uDE80-\uDE9C\uDEA0-\uDED0\uDF00-\uDF1F\uDF30-\uDF4A\uDF50-\uDF75\uDF80-\uDF9D\uDFA0-\uDFC3\uDFC8-\uDFCF\uDFD1-\uDFD5]|\uD801[\uDC00-\uDC9D\uDD00-\uDD27\uDD30-\uDD63\uDE00-\uDF36\uDF40-\uDF55\uDF60-\uDF67]|\uD802[\uDC00-\uDC05\uDC08\uDC0A-\uDC35\uDC37\uDC38\uDC3C\uDC3F-\uDC55\uDC60-\uDC76\uDC80-\uDC9E\uDCE0-\uDCF2\uDCF4\uDCF5\uDD00-\uDD15\uDD20-\uDD39\uDD80-\uDDB7\uDDBE\uDDBF\uDE00\uDE10-\uDE13\uDE15-\uDE17\uDE19-\uDE33\uDE60-\uDE7C\uDE80-\uDE9C\uDEC0-\uDEC7\uDEC9-\uDEE4\uDF00-\uDF35\uDF40-\uDF55\uDF60-\uDF72\uDF80-\uDF91]|\uD803[\uDC00-\uDC48\uDC80-\uDCB2\uDCC0-\uDCF2]|\uD804[\uDC03-\uDC37\uDC83-\uDCAF\uDCD0-\uDCE8\uDD03-\uDD26\uDD50-\uDD72\uDD76\uDD83-\uDDB2\uDDC1-\uDDC4\uDDDA\uDDDC\uDE00-\uDE11\uDE13-\uDE2B\uDE80-\uDE86\uDE88\uDE8A-\uDE8D\uDE8F-\uDE9D\uDE9F-\uDEA8\uDEB0-\uDEDE\uDF05-\uDF0C\uDF0F\uDF10\uDF13-\uDF28\uDF2A-\uDF30\uDF32\uDF33\uDF35-\uDF39\uDF3D\uDF50\uDF5D-\uDF61]|\uD805[\uDC80-\uDCAF\uDCC4\uDCC5\uDCC7\uDD80-\uDDAE\uDDD8-\uDDDB\uDE00-\uDE2F\uDE44\uDE80-\uDEAA\uDF00-\uDF19]|\uD806[\uDCA0-\uDCDF\uDCFF\uDEC0-\uDEF8]|\uD808[\uDC00-\uDF99]|\uD809[\uDC00-\uDC6E\uDC80-\uDD43]|[\uD80C\uD840-\uD868\uD86A-\uD86C\uD86F-\uD872][\uDC00-\uDFFF]|\uD80D[\uDC00-\uDC2E]|\uD811[\uDC00-\uDE46]|\uD81A[\uDC00-\uDE38\uDE40-\uDE5E\uDED0-\uDEED\uDF00-\uDF2F\uDF40-\uDF43\uDF63-\uDF77\uDF7D-\uDF8F]|\uD81B[\uDF00-\uDF44\uDF50\uDF93-\uDF9F]|\uD82C[\uDC00\uDC01]|\uD82F[\uDC00-\uDC6A\uDC70-\uDC7C\uDC80-\uDC88\uDC90-\uDC99]|\uD835[\uDC00-\uDC54\uDC56-\uDC9C\uDC9E\uDC9F\uDCA2\uDCA5\uDCA6\uDCA9-\uDCAC\uDCAE-\uDCB9\uDCBB\uDCBD-\uDCC3\uDCC5-\uDD05\uDD07-\uDD0A\uDD0D-\uDD14\uDD16-\uDD1C\uDD1E-\uDD39\uDD3B-\uDD3E\uDD40-\uDD44\uDD46\uDD4A-\uDD50\uDD52-\uDEA5\uDEA8-\uDEC0\uDEC2-\uDEDA\uDEDC-\uDEFA\uDEFC-\uDF14\uDF16-\uDF34\uDF36-\uDF4E\uDF50-\uDF6E\uDF70-\uDF88\uDF8A-\uDFA8\uDFAA-\uDFC2\uDFC4-\uDFCB]|\uD83A[\uDC00-\uDCC4]|\uD83B[\uDE00-\uDE03\uDE05-\uDE1F\uDE21\uDE22\uDE24\uDE27\uDE29-\uDE32\uDE34-\uDE37\uDE39\uDE3B\uDE42\uDE47\uDE49\uDE4B\uDE4D-\uDE4F\uDE51\uDE52\uDE54\uDE57\uDE59\uDE5B\uDE5D\uDE5F\uDE61\uDE62\uDE64\uDE67-\uDE6A\uDE6C-\uDE72\uDE74-\uDE77\uDE79-\uDE7C\uDE7E\uDE80-\uDE89\uDE8B-\uDE9B\uDEA1-\uDEA3\uDEA5-\uDEA9\uDEAB-\uDEBB]|\uD869[\uDC00-\uDED6\uDF00-\uDFFF]|\uD86D[\uDC00-\uDF34\uDF40-\uDFFF]|\uD86E[\uDC00-\uDC1D\uDC20-\uDFFF]|\uD873[\uDC00-\uDEA1]|\uD87E[\uDC00-\uDE1D])(?:[$0-9A-Z_a-z\xAA\xB5\xB7\xBA\xC0-\xD6\xD8-\xF6\xF8-\u02C1\u02C6-\u02D1\u02E0-\u02E4\u02EC\u02EE\u0300-\u0374\u0376\u0377\u037A-\u037D\u037F\u0386-\u038A\u038C\u038E-\u03A1\u03A3-\u03F5\u03F7-\u0481\u0483-\u0487\u048A-\u052F\u0531-\u0556\u0559\u0561-\u0587\u0591-\u05BD\u05BF\u05C1\u05C2\u05C4\u05C5\u05C7\u05D0-\u05EA\u05F0-\u05F2\u0610-\u061A\u0620-\u0669\u066E-\u06D3\u06D5-\u06DC\u06DF-\u06E8\u06EA-\u06FC\u06FF\u0710-\u074A\u074D-\u07B1\u07C0-\u07F5\u07FA\u0800-\u082D\u0840-\u085B\u08A0-\u08B4\u08E3-\u0963\u0966-\u096F\u0971-\u0983\u0985-\u098C\u098F\u0990\u0993-\u09A8\u09AA-\u09B0\u09B2\u09B6-\u09B9\u09BC-\u09C4\u09C7\u09C8\u09CB-\u09CE\u09D7\u09DC\u09DD\u09DF-\u09E3\u09E6-\u09F1\u0A01-\u0A03\u0A05-\u0A0A\u0A0F\u0A10\u0A13-\u0A28\u0A2A-\u0A30\u0A32\u0A33\u0A35\u0A36\u0A38\u0A39\u0A3C\u0A3E-\u0A42\u0A47\u0A48\u0A4B-\u0A4D\u0A51\u0A59-\u0A5C\u0A5E\u0A66-\u0A75\u0A81-\u0A83\u0A85-\u0A8D\u0A8F-\u0A91\u0A93-\u0AA8\u0AAA-\u0AB0\u0AB2\u0AB3\u0AB5-\u0AB9\u0ABC-\u0AC5\u0AC7-\u0AC9\u0ACB-\u0ACD\u0AD0\u0AE0-\u0AE3\u0AE6-\u0AEF\u0AF9\u0B01-\u0B03\u0B05-\u0B0C\u0B0F\u0B10\u0B13-\u0B28\u0B2A-\u0B30\u0B32\u0B33\u0B35-\u0B39\u0B3C-\u0B44\u0B47\u0B48\u0B4B-\u0B4D\u0B56\u0B57\u0B5C\u0B5D\u0B5F-\u0B63\u0B66-\u0B6F\u0B71\u0B82\u0B83\u0B85-\u0B8A\u0B8E-\u0B90\u0B92-\u0B95\u0B99\u0B9A\u0B9C\u0B9E\u0B9F\u0BA3\u0BA4\u0BA8-\u0BAA\u0BAE-\u0BB9\u0BBE-\u0BC2\u0BC6-\u0BC8\u0BCA-\u0BCD\u0BD0\u0BD7\u0BE6-\u0BEF\u0C00-\u0C03\u0C05-\u0C0C\u0C0E-\u0C10\u0C12-\u0C28\u0C2A-\u0C39\u0C3D-\u0C44\u0C46-\u0C48\u0C4A-\u0C4D\u0C55\u0C56\u0C58-\u0C5A\u0C60-\u0C63\u0C66-\u0C6F\u0C81-\u0C83\u0C85-\u0C8C\u0C8E-\u0C90\u0C92-\u0CA8\u0CAA-\u0CB3\u0CB5-\u0CB9\u0CBC-\u0CC4\u0CC6-\u0CC8\u0CCA-\u0CCD\u0CD5\u0CD6\u0CDE\u0CE0-\u0CE3\u0CE6-\u0CEF\u0CF1\u0CF2\u0D01-\u0D03\u0D05-\u0D0C\u0D0E-\u0D10\u0D12-\u0D3A\u0D3D-\u0D44\u0D46-\u0D48\u0D4A-\u0D4E\u0D57\u0D5F-\u0D63\u0D66-\u0D6F\u0D7A-\u0D7F\u0D82\u0D83\u0D85-\u0D96\u0D9A-\u0DB1\u0DB3-\u0DBB\u0DBD\u0DC0-\u0DC6\u0DCA\u0DCF-\u0DD4\u0DD6\u0DD8-\u0DDF\u0DE6-\u0DEF\u0DF2\u0DF3\u0E01-\u0E3A\u0E40-\u0E4E\u0E50-\u0E59\u0E81\u0E82\u0E84\u0E87\u0E88\u0E8A\u0E8D\u0E94-\u0E97\u0E99-\u0E9F\u0EA1-\u0EA3\u0EA5\u0EA7\u0EAA\u0EAB\u0EAD-\u0EB9\u0EBB-\u0EBD\u0EC0-\u0EC4\u0EC6\u0EC8-\u0ECD\u0ED0-\u0ED9\u0EDC-\u0EDF\u0F00\u0F18\u0F19\u0F20-\u0F29\u0F35\u0F37\u0F39\u0F3E-\u0F47\u0F49-\u0F6C\u0F71-\u0F84\u0F86-\u0F97\u0F99-\u0FBC\u0FC6\u1000-\u1049\u1050-\u109D\u10A0-\u10C5\u10C7\u10CD\u10D0-\u10FA\u10FC-\u1248\u124A-\u124D\u1250-\u1256\u1258\u125A-\u125D\u1260-\u1288\u128A-\u128D\u1290-\u12B0\u12B2-\u12B5\u12B8-\u12BE\u12C0\u12C2-\u12C5\u12C8-\u12D6\u12D8-\u1310\u1312-\u1315\u1318-\u135A\u135D-\u135F\u1369-\u1371\u1380-\u138F\u13A0-\u13F5\u13F8-\u13FD\u1401-\u166C\u166F-\u167F\u1681-\u169A\u16A0-\u16EA\u16EE-\u16F8\u1700-\u170C\u170E-\u1714\u1720-\u1734\u1740-\u1753\u1760-\u176C\u176E-\u1770\u1772\u1773\u1780-\u17D3\u17D7\u17DC\u17DD\u17E0-\u17E9\u180B-\u180D\u1810-\u1819\u1820-\u1877\u1880-\u18AA\u18B0-\u18F5\u1900-\u191E\u1920-\u192B\u1930-\u193B\u1946-\u196D\u1970-\u1974\u1980-\u19AB\u19B0-\u19C9\u19D0-\u19DA\u1A00-\u1A1B\u1A20-\u1A5E\u1A60-\u1A7C\u1A7F-\u1A89\u1A90-\u1A99\u1AA7\u1AB0-\u1ABD\u1B00-\u1B4B\u1B50-\u1B59\u1B6B-\u1B73\u1B80-\u1BF3\u1C00-\u1C37\u1C40-\u1C49\u1C4D-\u1C7D\u1CD0-\u1CD2\u1CD4-\u1CF6\u1CF8\u1CF9\u1D00-\u1DF5\u1DFC-\u1F15\u1F18-\u1F1D\u1F20-\u1F45\u1F48-\u1F4D\u1F50-\u1F57\u1F59\u1F5B\u1F5D\u1F5F-\u1F7D\u1F80-\u1FB4\u1FB6-\u1FBC\u1FBE\u1FC2-\u1FC4\u1FC6-\u1FCC\u1FD0-\u1FD3\u1FD6-\u1FDB\u1FE0-\u1FEC\u1FF2-\u1FF4\u1FF6-\u1FFC\u200C\u200D\u203F\u2040\u2054\u2071\u207F\u2090-\u209C\u20D0-\u20DC\u20E1\u20E5-\u20F0\u2102\u2107\u210A-\u2113\u2115\u2118-\u211D\u2124\u2126\u2128\u212A-\u2139\u213C-\u213F\u2145-\u2149\u214E\u2160-\u2188\u2C00-\u2C2E\u2C30-\u2C5E\u2C60-\u2CE4\u2CEB-\u2CF3\u2D00-\u2D25\u2D27\u2D2D\u2D30-\u2D67\u2D6F\u2D7F-\u2D96\u2DA0-\u2DA6\u2DA8-\u2DAE\u2DB0-\u2DB6\u2DB8-\u2DBE\u2DC0-\u2DC6\u2DC8-\u2DCE\u2DD0-\u2DD6\u2DD8-\u2DDE\u2DE0-\u2DFF\u3005-\u3007\u3021-\u302F\u3031-\u3035\u3038-\u303C\u3041-\u3096\u3099-\u309F\u30A1-\u30FA\u30FC-\u30FF\u3105-\u312D\u3131-\u318E\u31A0-\u31BA\u31F0-\u31FF\u3400-\u4DB5\u4E00-\u9FD5\uA000-\uA48C\uA4D0-\uA4FD\uA500-\uA60C\uA610-\uA62B\uA640-\uA66F\uA674-\uA67D\uA67F-\uA6F1\uA717-\uA71F\uA722-\uA788\uA78B-\uA7AD\uA7B0-\uA7B7\uA7F7-\uA827\uA840-\uA873\uA880-\uA8C4\uA8D0-\uA8D9\uA8E0-\uA8F7\uA8FB\uA8FD\uA900-\uA92D\uA930-\uA953\uA960-\uA97C\uA980-\uA9C0\uA9CF-\uA9D9\uA9E0-\uA9FE\uAA00-\uAA36\uAA40-\uAA4D\uAA50-\uAA59\uAA60-\uAA76\uAA7A-\uAAC2\uAADB-\uAADD\uAAE0-\uAAEF\uAAF2-\uAAF6\uAB01-\uAB06\uAB09-\uAB0E\uAB11-\uAB16\uAB20-\uAB26\uAB28-\uAB2E\uAB30-\uAB5A\uAB5C-\uAB65\uAB70-\uABEA\uABEC\uABED\uABF0-\uABF9\uAC00-\uD7A3\uD7B0-\uD7C6\uD7CB-\uD7FB\uF900-\uFA6D\uFA70-\uFAD9\uFB00-\uFB06\uFB13-\uFB17\uFB1D-\uFB28\uFB2A-\uFB36\uFB38-\uFB3C\uFB3E\uFB40\uFB41\uFB43\uFB44\uFB46-\uFBB1\uFBD3-\uFD3D\uFD50-\uFD8F\uFD92-\uFDC7\uFDF0-\uFDFB\uFE00-\uFE0F\uFE20-\uFE2F\uFE33\uFE34\uFE4D-\uFE4F\uFE70-\uFE74\uFE76-\uFEFC\uFF10-\uFF19\uFF21-\uFF3A\uFF3F\uFF41-\uFF5A\uFF66-\uFFBE\uFFC2-\uFFC7\uFFCA-\uFFCF\uFFD2-\uFFD7\uFFDA-\uFFDC]|\uD800[\uDC00-\uDC0B\uDC0D-\uDC26\uDC28-\uDC3A\uDC3C\uDC3D\uDC3F-\uDC4D\uDC50-\uDC5D\uDC80-\uDCFA\uDD40-\uDD74\uDDFD\uDE80-\uDE9C\uDEA0-\uDED0\uDEE0\uDF00-\uDF1F\uDF30-\uDF4A\uDF50-\uDF7A\uDF80-\uDF9D\uDFA0-\uDFC3\uDFC8-\uDFCF\uDFD1-\uDFD5]|\uD801[\uDC00-\uDC9D\uDCA0-\uDCA9\uDD00-\uDD27\uDD30-\uDD63\uDE00-\uDF36\uDF40-\uDF55\uDF60-\uDF67]|\uD802[\uDC00-\uDC05\uDC08\uDC0A-\uDC35\uDC37\uDC38\uDC3C\uDC3F-\uDC55\uDC60-\uDC76\uDC80-\uDC9E\uDCE0-\uDCF2\uDCF4\uDCF5\uDD00-\uDD15\uDD20-\uDD39\uDD80-\uDDB7\uDDBE\uDDBF\uDE00-\uDE03\uDE05\uDE06\uDE0C-\uDE13\uDE15-\uDE17\uDE19-\uDE33\uDE38-\uDE3A\uDE3F\uDE60-\uDE7C\uDE80-\uDE9C\uDEC0-\uDEC7\uDEC9-\uDEE6\uDF00-\uDF35\uDF40-\uDF55\uDF60-\uDF72\uDF80-\uDF91]|\uD803[\uDC00-\uDC48\uDC80-\uDCB2\uDCC0-\uDCF2]|\uD804[\uDC00-\uDC46\uDC66-\uDC6F\uDC7F-\uDCBA\uDCD0-\uDCE8\uDCF0-\uDCF9\uDD00-\uDD34\uDD36-\uDD3F\uDD50-\uDD73\uDD76\uDD80-\uDDC4\uDDCA-\uDDCC\uDDD0-\uDDDA\uDDDC\uDE00-\uDE11\uDE13-\uDE37\uDE80-\uDE86\uDE88\uDE8A-\uDE8D\uDE8F-\uDE9D\uDE9F-\uDEA8\uDEB0-\uDEEA\uDEF0-\uDEF9\uDF00-\uDF03\uDF05-\uDF0C\uDF0F\uDF10\uDF13-\uDF28\uDF2A-\uDF30\uDF32\uDF33\uDF35-\uDF39\uDF3C-\uDF44\uDF47\uDF48\uDF4B-\uDF4D\uDF50\uDF57\uDF5D-\uDF63\uDF66-\uDF6C\uDF70-\uDF74]|\uD805[\uDC80-\uDCC5\uDCC7\uDCD0-\uDCD9\uDD80-\uDDB5\uDDB8-\uDDC0\uDDD8-\uDDDD\uDE00-\uDE40\uDE44\uDE50-\uDE59\uDE80-\uDEB7\uDEC0-\uDEC9\uDF00-\uDF19\uDF1D-\uDF2B\uDF30-\uDF39]|\uD806[\uDCA0-\uDCE9\uDCFF\uDEC0-\uDEF8]|\uD808[\uDC00-\uDF99]|\uD809[\uDC00-\uDC6E\uDC80-\uDD43]|[\uD80C\uD840-\uD868\uD86A-\uD86C\uD86F-\uD872][\uDC00-\uDFFF]|\uD80D[\uDC00-\uDC2E]|\uD811[\uDC00-\uDE46]|\uD81A[\uDC00-\uDE38\uDE40-\uDE5E\uDE60-\uDE69\uDED0-\uDEED\uDEF0-\uDEF4\uDF00-\uDF36\uDF40-\uDF43\uDF50-\uDF59\uDF63-\uDF77\uDF7D-\uDF8F]|\uD81B[\uDF00-\uDF44\uDF50-\uDF7E\uDF8F-\uDF9F]|\uD82C[\uDC00\uDC01]|\uD82F[\uDC00-\uDC6A\uDC70-\uDC7C\uDC80-\uDC88\uDC90-\uDC99\uDC9D\uDC9E]|\uD834[\uDD65-\uDD69\uDD6D-\uDD72\uDD7B-\uDD82\uDD85-\uDD8B\uDDAA-\uDDAD\uDE42-\uDE44]|\uD835[\uDC00-\uDC54\uDC56-\uDC9C\uDC9E\uDC9F\uDCA2\uDCA5\uDCA6\uDCA9-\uDCAC\uDCAE-\uDCB9\uDCBB\uDCBD-\uDCC3\uDCC5-\uDD05\uDD07-\uDD0A\uDD0D-\uDD14\uDD16-\uDD1C\uDD1E-\uDD39\uDD3B-\uDD3E\uDD40-\uDD44\uDD46\uDD4A-\uDD50\uDD52-\uDEA5\uDEA8-\uDEC0\uDEC2-\uDEDA\uDEDC-\uDEFA\uDEFC-\uDF14\uDF16-\uDF34\uDF36-\uDF4E\uDF50-\uDF6E\uDF70-\uDF88\uDF8A-\uDFA8\uDFAA-\uDFC2\uDFC4-\uDFCB\uDFCE-\uDFFF]|\uD836[\uDE00-\uDE36\uDE3B-\uDE6C\uDE75\uDE84\uDE9B-\uDE9F\uDEA1-\uDEAF]|\uD83A[\uDC00-\uDCC4\uDCD0-\uDCD6]|\uD83B[\uDE00-\uDE03\uDE05-\uDE1F\uDE21\uDE22\uDE24\uDE27\uDE29-\uDE32\uDE34-\uDE37\uDE39\uDE3B\uDE42\uDE47\uDE49\uDE4B\uDE4D-\uDE4F\uDE51\uDE52\uDE54\uDE57\uDE59\uDE5B\uDE5D\uDE5F\uDE61\uDE62\uDE64\uDE67-\uDE6A\uDE6C-\uDE72\uDE74-\uDE77\uDE79-\uDE7C\uDE7E\uDE80-\uDE89\uDE8B-\uDE9B\uDEA1-\uDEA3\uDEA5-\uDEA9\uDEAB-\uDEBB]|\uD869[\uDC00-\uDED6\uDF00-\uDFFF]|\uD86D[\uDC00-\uDF34\uDF40-\uDFFF]|\uD86E[\uDC00-\uDC1D\uDC20-\uDFFF]|\uD873[\uDC00-\uDEA1]|\uD87E[\uDC00-\uDE1D]|\uDB40[\uDD00-\uDDEF])*$/; 19 | 20 | // regexpxs extracted from 21 | // (c) BSD-3-Clause 22 | // https://github.com/fastify/secure-json-parse/graphs/contributors and https://github.com/hapijs/bourne/graphs/contributors 23 | 24 | const suspectProtoRx = /(?:_|\\u005[Ff])(?:_|\\u005[Ff])(?:p|\\u0070)(?:r|\\u0072)(?:o|\\u006[Ff])(?:t|\\u0074)(?:o|\\u006[Ff])(?:_|\\u005[Ff])(?:_|\\u005[Ff])/; 25 | const suspectConstructorRx = /(?:c|\\u0063)(?:o|\\u006[Ff])(?:n|\\u006[Ee])(?:s|\\u0073)(?:t|\\u0074)(?:r|\\u0072)(?:u|\\u0075)(?:c|\\u0063)(?:t|\\u0074)(?:o|\\u006[Ff])(?:r|\\u0072)/; 26 | 27 | /* 28 | json_parse.js 29 | 2012-06-20 30 | 31 | Public Domain. 32 | 33 | NO WARRANTY EXPRESSED OR IMPLIED. USE AT YOUR OWN RISK. 34 | 35 | This file creates a json_parse function. 36 | During create you can (optionally) specify some behavioural switches 37 | 38 | require('json-bigint')(options) 39 | 40 | The optional options parameter holds switches that drive certain 41 | aspects of the parsing process: 42 | * options.strict = true will warn about duplicate-key usage in the json. 43 | The default (strict = false) will silently ignore those and overwrite 44 | values for keys that are in duplicate use. 45 | 46 | The resulting function follows this signature: 47 | json_parse(text, reviver) 48 | This method parses a JSON text to produce an object or array. 49 | It can throw a SyntaxError exception. 50 | 51 | The optional reviver parameter is a function that can filter and 52 | transform the results. It receives each of the keys and values, 53 | and its return value is used instead of the original value. 54 | If it returns what it received, then the structure is not modified. 55 | If it returns undefined then the member is deleted. 56 | 57 | Example: 58 | 59 | // Parse the text. Values that look like ISO date strings will 60 | // be converted to Date objects. 61 | 62 | myData = json_parse(text, function (key, value) { 63 | var a; 64 | if (typeof value === 'string') { 65 | a = 66 | /^(\d{4})-(\d{2})-(\d{2})T(\d{2}):(\d{2}):(\d{2}(?:\.\d*)?)Z$/.exec(value); 67 | if (a) { 68 | return new Date(Date.UTC(+a[1], +a[2] - 1, +a[3], +a[4], 69 | +a[5], +a[6])); 70 | } 71 | } 72 | return value; 73 | }); 74 | 75 | This is a reference implementation. You are free to copy, modify, or 76 | redistribute. 77 | 78 | This code should be minified before deployment. 79 | See http://javascript.crockford.com/jsmin.html 80 | 81 | USE YOUR OWN COPY. IT IS EXTREMELY UNWISE TO LOAD CODE FROM SERVERS YOU DO 82 | NOT CONTROL. 83 | */ 84 | 85 | /*members "", "\"", "\/", "\\", at, b, call, charAt, f, fromCharCode, 86 | hasOwnProperty, message, n, name, prototype, push, r, t, text 87 | */ 88 | 89 | var json_parse = function (options) { 90 | "use strict"; 91 | 92 | // This is a function that can parse a JSON text, producing a JavaScript 93 | // data structure. It is a simple, recursive descent parser. It does not use 94 | // eval or regular expressions, so it can be used as a model for implementing 95 | // a JSON parser in other languages. 96 | 97 | // We are defining the function inside of another function to avoid creating 98 | // global variables. 99 | 100 | // Default options one can override by passing options to the parse() 101 | var _options = { 102 | strict: false, // not being strict means do not generate syntax errors for "duplicate key" 103 | storeAsString: false, // toggles whether the values should be stored as BigNumber (default) or a string 104 | alwaysParseAsBig: false, // toggles whether all numbers should be Big 105 | useNativeBigInt: false, // toggles whether to use native BigInt instead of bignumber.js 106 | protoAction: "error", 107 | constructorAction: "error", 108 | }; 109 | 110 | // If there are options, then use them to override the default _options 111 | if (options !== undefined && options !== null) { 112 | if (options.strict === true) { 113 | _options.strict = true; 114 | } 115 | if (options.storeAsString === true) { 116 | _options.storeAsString = true; 117 | } 118 | _options.alwaysParseAsBig = 119 | options.alwaysParseAsBig === true ? options.alwaysParseAsBig : false; 120 | _options.useNativeBigInt = 121 | options.useNativeBigInt === true ? options.useNativeBigInt : false; 122 | 123 | if (typeof options.constructorAction !== "undefined") { 124 | if ( 125 | options.constructorAction === "error" || 126 | options.constructorAction === "ignore" || 127 | options.constructorAction === "preserve" 128 | ) { 129 | _options.constructorAction = options.constructorAction; 130 | } else { 131 | throw new Error( 132 | `Incorrect value for constructorAction option, must be "error", "ignore" or undefined but passed ${options.constructorAction}` 133 | ); 134 | } 135 | } 136 | 137 | if (typeof options.protoAction !== "undefined") { 138 | if ( 139 | options.protoAction === "error" || 140 | options.protoAction === "ignore" || 141 | options.protoAction === "preserve" 142 | ) { 143 | _options.protoAction = options.protoAction; 144 | } else { 145 | throw new Error( 146 | `Incorrect value for protoAction option, must be "error", "ignore" or undefined but passed ${options.protoAction}` 147 | ); 148 | } 149 | } 150 | } 151 | 152 | var at, // The index of the current character 153 | ch, // The current character 154 | escapee = { 155 | "\"": "\"", 156 | "\\": "\\", 157 | "/": "/", 158 | b: "\b", 159 | f: "\f", 160 | n: "\n", 161 | r: "\r", 162 | t: "\t", 163 | }, 164 | text, 165 | error = function (m) { 166 | // Call error when something is wrong. 167 | 168 | throw { 169 | name: "SyntaxError", 170 | message: m, 171 | at: at, 172 | text: text, 173 | }; 174 | }, 175 | next = function (c) { 176 | // If a c parameter is provided, verify that it matches the current character. 177 | 178 | if (c && c !== ch) { 179 | error("Expected '" + c + "' instead of '" + ch + "'"); 180 | } 181 | 182 | // Get the next character. When there are no more characters, 183 | // return the empty string. 184 | 185 | ch = text.charAt(at); 186 | at += 1; 187 | return ch; 188 | }, 189 | number = function () { 190 | // Parse a number value. 191 | 192 | var number, 193 | string = ""; 194 | 195 | if (ch === "-") { 196 | string = "-"; 197 | next("-"); 198 | } 199 | while (ch >= "0" && ch <= "9") { 200 | string += ch; 201 | next(); 202 | } 203 | if (ch === ".") { 204 | string += "."; 205 | while (next() && ch >= "0" && ch <= "9") { 206 | string += ch; 207 | } 208 | } 209 | if (ch === "e" || ch === "E") { 210 | string += ch; 211 | next(); 212 | if (ch === "-" || ch === "+") { 213 | string += ch; 214 | next(); 215 | } 216 | while (ch >= "0" && ch <= "9") { 217 | string += ch; 218 | next(); 219 | } 220 | } 221 | number = +string; 222 | if (!isFinite(number)) { 223 | error("Bad number"); 224 | } else { 225 | // if (BigNumber == null) BigNumber = require("bignumber.js"); 226 | if (Number.isSafeInteger(number)) 227 | return !_options.alwaysParseAsBig 228 | ? number 229 | : BigInt(number); 230 | else 231 | // Number with fractional part should be treated as number(double) including big integers in scientific notation, i.e 1.79e+308 232 | return _options.storeAsString 233 | ? string 234 | : /[.eE]/.test(string) 235 | ? number 236 | : BigInt(string); 237 | } 238 | }, 239 | string = function () { 240 | // Parse a string value. 241 | 242 | var hex, 243 | i, 244 | string = "", 245 | uffff; 246 | 247 | // When parsing for string values, we must look for " and \ characters. 248 | 249 | if (ch === "\"") { 250 | var startAt = at; 251 | while (next()) { 252 | if (ch === "\"") { 253 | if (at - 1 > startAt) string += text.substring(startAt, at - 1); 254 | next(); 255 | return string; 256 | } 257 | if (ch === "\\") { 258 | if (at - 1 > startAt) string += text.substring(startAt, at - 1); 259 | next(); 260 | if (ch === "u") { 261 | uffff = 0; 262 | for (i = 0; i < 4; i += 1) { 263 | hex = parseInt(next(), 16); 264 | if (!isFinite(hex)) { 265 | break; 266 | } 267 | uffff = uffff * 16 + hex; 268 | } 269 | string += String.fromCharCode(uffff); 270 | } else if (typeof escapee[ch] === "string") { 271 | string += escapee[ch]; 272 | } else { 273 | break; 274 | } 275 | startAt = at; 276 | } 277 | } 278 | } 279 | error("Bad string"); 280 | }, 281 | white = function () { 282 | // Skip whitespace. 283 | 284 | while (ch && ch <= " ") { 285 | next(); 286 | } 287 | }, 288 | word = function () { 289 | // true, false, or null. 290 | 291 | switch (ch) { 292 | case "t": 293 | next("t"); 294 | next("r"); 295 | next("u"); 296 | next("e"); 297 | return true; 298 | case "f": 299 | next("f"); 300 | next("a"); 301 | next("l"); 302 | next("s"); 303 | next("e"); 304 | return false; 305 | case "n": 306 | next("n"); 307 | next("u"); 308 | next("l"); 309 | next("l"); 310 | return null; 311 | } 312 | error("Unexpected '" + ch + "'"); 313 | }, 314 | value, // Place holder for the value function. 315 | array = function () { 316 | // Parse an array value. 317 | 318 | var array = []; 319 | 320 | if (ch === "[") { 321 | next("["); 322 | white(); 323 | if (ch === "]") { 324 | next("]"); 325 | return array; // empty array 326 | } 327 | while (ch) { 328 | array.push(value()); 329 | white(); 330 | if (ch === "]") { 331 | next("]"); 332 | return array; 333 | } 334 | next(","); 335 | white(); 336 | } 337 | } 338 | error("Bad array"); 339 | }, 340 | object = function () { 341 | // Parse an object value. 342 | 343 | var key, 344 | object = Object.create(null); 345 | 346 | if (ch === "{") { 347 | next("{"); 348 | white(); 349 | if (ch === "}") { 350 | next("}"); 351 | return object; // empty object 352 | } 353 | while (ch) { 354 | key = string(); 355 | white(); 356 | next(":"); 357 | if ( 358 | _options.strict === true && 359 | Object.hasOwnProperty.call(object, key) 360 | ) { 361 | error("Duplicate key \"" + key + "\""); 362 | } 363 | 364 | if (suspectProtoRx.test(key) === true) { 365 | if (_options.protoAction === "error") { 366 | error("Object contains forbidden prototype property"); 367 | } else if (_options.protoAction === "ignore") { 368 | value(); 369 | } else { 370 | object[key] = value(); 371 | } 372 | } else if (suspectConstructorRx.test(key) === true) { 373 | if (_options.constructorAction === "error") { 374 | error("Object contains forbidden constructor property"); 375 | } else if (_options.constructorAction === "ignore") { 376 | value(); 377 | } else { 378 | object[key] = value(); 379 | } 380 | } else { 381 | object[key] = value(); 382 | } 383 | 384 | white(); 385 | if (ch === "}") { 386 | next("}"); 387 | return object; 388 | } 389 | next(","); 390 | white(); 391 | } 392 | } 393 | error("Bad object"); 394 | }; 395 | 396 | value = function () { 397 | // Parse a JSON value. It could be an object, an array, a string, a number, 398 | // or a word. 399 | 400 | white(); 401 | switch (ch) { 402 | case "{": 403 | return object(); 404 | case "[": 405 | return array(); 406 | case "\"": 407 | return string(); 408 | case "-": 409 | return number(); 410 | default: 411 | return ch >= "0" && ch <= "9" ? number() : word(); 412 | } 413 | }; 414 | 415 | // Return the json_parse function. It will have access to all of the above 416 | // functions and variables. 417 | 418 | return function (source, reviver) { 419 | var result; 420 | 421 | text = source + ""; 422 | at = 0; 423 | ch = " "; 424 | result = value(); 425 | white(); 426 | if (ch) { 427 | error("Syntax error"); 428 | } 429 | 430 | // If there is a reviver function, we recursively walk the new structure, 431 | // passing each name/value pair to the reviver function for possible 432 | // transformation, starting with a temporary root object that holds the result 433 | // in an empty key. If there is not a reviver function, we simply return the 434 | // result. 435 | 436 | return typeof reviver === "function" 437 | ? (function walk(holder, key) { 438 | var v, 439 | value = holder[key]; 440 | if (value && typeof value === "object") { 441 | Object.keys(value).forEach(function (k) { 442 | v = walk(value, k); 443 | if (v !== undefined) { 444 | value[k] = v; 445 | } else { 446 | delete value[k]; 447 | } 448 | }); 449 | } 450 | return reviver.call(holder, key, value); 451 | })({ "": result }, "") 452 | : result; 453 | }; 454 | }; 455 | 456 | /* 457 | json2.js 458 | 2013-05-26 459 | 460 | Public Domain. 461 | 462 | NO WARRANTY EXPRESSED OR IMPLIED. USE AT YOUR OWN RISK. 463 | 464 | See http://www.JSON.org/js.html 465 | 466 | 467 | This code should be minified before deployment. 468 | See http://javascript.crockford.com/jsmin.html 469 | 470 | USE YOUR OWN COPY. IT IS EXTREMELY UNWISE TO LOAD CODE FROM SERVERS YOU DO 471 | NOT CONTROL. 472 | 473 | 474 | This file creates a global JSON object containing two methods: stringify 475 | and parse. 476 | 477 | JSON.stringify(value, replacer, space) 478 | value any JavaScript value, usually an object or array. 479 | 480 | replacer an optional parameter that determines how object 481 | values are stringified for objects. It can be a 482 | function or an array of strings. 483 | 484 | space an optional parameter that specifies the indentation 485 | of nested structures. If it is omitted, the text will 486 | be packed without extra whitespace. If it is a number, 487 | it will specify the number of spaces to indent at each 488 | level. If it is a string (such as '\t' or ' '), 489 | it contains the characters used to indent at each level. 490 | 491 | This method produces a JSON text from a JavaScript value. 492 | 493 | When an object value is found, if the object contains a toJSON 494 | method, its toJSON method will be called and the result will be 495 | stringified. A toJSON method does not serialize: it returns the 496 | value represented by the name/value pair that should be serialized, 497 | or undefined if nothing should be serialized. The toJSON method 498 | will be passed the key associated with the value, and this will be 499 | bound to the value 500 | 501 | For example, this would serialize Dates as ISO strings. 502 | 503 | Date.prototype.toJSON = function (key) { 504 | function f(n) { 505 | // Format integers to have at least two digits. 506 | return n < 10 ? '0' + n : n; 507 | } 508 | 509 | return this.getUTCFullYear() + '-' + 510 | f(this.getUTCMonth() + 1) + '-' + 511 | f(this.getUTCDate()) + 'T' + 512 | f(this.getUTCHours()) + ':' + 513 | f(this.getUTCMinutes()) + ':' + 514 | f(this.getUTCSeconds()) + 'Z'; 515 | }; 516 | 517 | You can provide an optional replacer method. It will be passed the 518 | key and value of each member, with this bound to the containing 519 | object. The value that is returned from your method will be 520 | serialized. If your method returns undefined, then the member will 521 | be excluded from the serialization. 522 | 523 | If the replacer parameter is an array of strings, then it will be 524 | used to select the members to be serialized. It filters the results 525 | such that only members with keys listed in the replacer array are 526 | stringified. 527 | 528 | Values that do not have JSON representations, such as undefined or 529 | functions, will not be serialized. Such values in objects will be 530 | dropped; in arrays they will be replaced with null. You can use 531 | a replacer function to replace those with JSON values. 532 | JSON.stringify(undefined) returns undefined. 533 | 534 | The optional space parameter produces a stringification of the 535 | value that is filled with line breaks and indentation to make it 536 | easier to read. 537 | 538 | If the space parameter is a non-empty string, then that string will 539 | be used for indentation. If the space parameter is a number, then 540 | the indentation will be that many spaces. 541 | 542 | Example: 543 | 544 | text = JSON.stringify(['e', {pluribus: 'unum'}]); 545 | // text is '["e",{"pluribus":"unum"}]' 546 | 547 | 548 | text = JSON.stringify(['e', {pluribus: 'unum'}], null, '\t'); 549 | // text is '[\n\t"e",\n\t{\n\t\t"pluribus": "unum"\n\t}\n]' 550 | 551 | text = JSON.stringify([new Date()], function (key, value) { 552 | return this[key] instanceof Date ? 553 | 'Date(' + this[key] + ')' : value; 554 | }); 555 | // text is '["Date(---current time---)"]' 556 | 557 | 558 | JSON.parse(text, reviver) 559 | This method parses a JSON text to produce an object or array. 560 | It can throw a SyntaxError exception. 561 | 562 | The optional reviver parameter is a function that can filter and 563 | transform the results. It receives each of the keys and values, 564 | and its return value is used instead of the original value. 565 | If it returns what it received, then the structure is not modified. 566 | If it returns undefined then the member is deleted. 567 | 568 | Example: 569 | 570 | // Parse the text. Values that look like ISO date strings will 571 | // be converted to Date objects. 572 | 573 | myData = JSON.parse(text, function (key, value) { 574 | var a; 575 | if (typeof value === 'string') { 576 | a = 577 | /^(\d{4})-(\d{2})-(\d{2})T(\d{2}):(\d{2}):(\d{2}(?:\.\d*)?)Z$/.exec(value); 578 | if (a) { 579 | return new Date(Date.UTC(+a[1], +a[2] - 1, +a[3], +a[4], 580 | +a[5], +a[6])); 581 | } 582 | } 583 | return value; 584 | }); 585 | 586 | myData = JSON.parse('["Date(09/09/2001)"]', function (key, value) { 587 | var d; 588 | if (typeof value === 'string' && 589 | value.slice(0, 5) === 'Date(' && 590 | value.slice(-1) === ')') { 591 | d = new Date(value.slice(5, -1)); 592 | if (d) { 593 | return d; 594 | } 595 | } 596 | return value; 597 | }); 598 | 599 | 600 | This is a reference implementation. You are free to copy, modify, or 601 | redistribute. 602 | */ 603 | 604 | /*jslint evil: true, regexp: true */ 605 | 606 | /*members "", "\b", "\t", "\n", "\f", "\r", "\"", JSON, "\\", apply, 607 | call, charCodeAt, getUTCDate, getUTCFullYear, getUTCHours, 608 | getUTCMinutes, getUTCMonth, getUTCSeconds, hasOwnProperty, join, 609 | lastIndex, length, parse, prototype, push, replace, slice, stringify, 610 | test, toJSON, toString, valueOf 611 | */ 612 | 613 | 614 | // Create a JSON object only if one does not already exist. We create the 615 | // methods in a closure to avoid creating global variables. 616 | 617 | const jsonStringify = (function () { 618 | "use strict"; 619 | 620 | // eslint-disable-next-line 621 | var escapable = /[\\\"\x00-\x1f\x7f-\x9f\u00ad\u0600-\u0604\u070f\u17b4\u17b5\u200c-\u200f\u2028-\u202f\u2060-\u206f\ufeff\ufff0-\uffff]/g, 622 | gap, 623 | indent, 624 | meta = { // table of character substitutions 625 | "\b": "\\b", 626 | "\t": "\\t", 627 | "\n": "\\n", 628 | "\f": "\\f", 629 | "\r": "\\r", 630 | "\"": "\\\"", 631 | "\\": "\\\\" 632 | }, 633 | rep; 634 | 635 | 636 | function quote(string) { 637 | 638 | // If the string contains no control characters, no quote characters, and no 639 | // backslash characters, then we can safely slap some quotes around it. 640 | // Otherwise we must also replace the offending characters with safe escape 641 | // sequences. 642 | 643 | escapable.lastIndex = 0; 644 | return escapable.test(string) ? "\"" + string.replace(escapable, function (a) { 645 | var c = meta[a]; 646 | return typeof c === "string" 647 | ? c 648 | : "\\u" + ("0000" + a.charCodeAt(0).toString(16)).slice(-4); 649 | }) + "\"" : "\"" + string + "\""; 650 | } 651 | 652 | 653 | function str(key, holder) { 654 | 655 | // Produce a string from holder[key]. 656 | 657 | var i, // The loop counter. 658 | k, // The member key. 659 | v, // The member value. 660 | length, 661 | mind = gap, 662 | partial, 663 | value = holder[key], 664 | isBigInt = value != null && (typeof value == "bigint"); 665 | 666 | // If the value has a toJSON method, call it to obtain a replacement value. 667 | 668 | if (value && typeof value === "object" && 669 | typeof value.toJSON === "function") { 670 | value = value.toJSON(key); 671 | } 672 | 673 | // If we were called with a replacer function, then call the replacer to 674 | // obtain a replacement value. 675 | 676 | if (typeof rep === "function") { 677 | value = rep.call(holder, key, value); 678 | } 679 | 680 | // What happens next depends on the value's type. 681 | 682 | switch (typeof value) { 683 | case "string": 684 | if (isBigInt) { 685 | return value; 686 | } else { 687 | return quote(value); 688 | } 689 | 690 | case "number": 691 | 692 | // JSON numbers must be finite. Encode non-finite numbers as null. 693 | 694 | return isFinite(value) ? String(value) : "null"; 695 | 696 | case "boolean": 697 | case "null": 698 | case "bigint": 699 | 700 | // If the value is a boolean or null, convert it to a string. Note: 701 | // typeof null does not produce 'null'. The case is included here in 702 | // the remote chance that this gets fixed someday. 703 | 704 | // If the type is 'object', we might be dealing with an object or an array or 705 | // null. 706 | 707 | return String(value); 708 | 709 | case "object": 710 | 711 | // Due to a specification blunder in ECMAScript, typeof null is 'object', 712 | // so watch out for that case. 713 | 714 | if (!value) { 715 | return "null"; 716 | } 717 | 718 | // Make an array to hold the partial results of stringifying this object value. 719 | 720 | gap += indent; 721 | partial = []; 722 | 723 | // Is the value an array? 724 | 725 | if (Object.prototype.toString.apply(value) === "[object Array]") { 726 | 727 | // The value is an array. Stringify every element. Use null as a placeholder 728 | // for non-JSON values. 729 | 730 | length = value.length; 731 | for (i = 0; i < length; i += 1) { 732 | partial[i] = str(i, value) || "null"; 733 | } 734 | 735 | // Join all of the elements together, separated with commas, and wrap them in 736 | // brackets. 737 | 738 | v = partial.length === 0 739 | ? "[]" 740 | : gap 741 | ? "[\n" + gap + partial.join(",\n" + gap) + "\n" + mind + "]" 742 | : "[" + partial.join(",") + "]"; 743 | gap = mind; 744 | return v; 745 | } 746 | 747 | // If the replacer is an array, use it to select the members to be stringified. 748 | 749 | if (rep && typeof rep === "object") { 750 | length = rep.length; 751 | for (i = 0; i < length; i += 1) { 752 | if (typeof rep[i] === "string") { 753 | k = rep[i]; 754 | v = str(k, value); 755 | if (v) { 756 | partial.push(quote(k) + (gap ? ": " : ":") + v); 757 | } 758 | } 759 | } 760 | } else { 761 | 762 | // Otherwise, iterate through all of the keys in the object. 763 | 764 | Object.keys(value).forEach(function (k) { 765 | var v = str(k, value); 766 | if (v) { 767 | partial.push(quote(k) + (gap ? ": " : ":") + v); 768 | } 769 | }); 770 | } 771 | 772 | // Join all of the member texts together, separated with commas, 773 | // and wrap them in braces. 774 | 775 | v = partial.length === 0 776 | ? "{}" 777 | : gap 778 | ? "{\n" + gap + partial.join(",\n" + gap) + "\n" + mind + "}" 779 | : "{" + partial.join(",") + "}"; 780 | gap = mind; 781 | return v; 782 | } 783 | } 784 | 785 | // If the JSON object does not yet have a stringify method, give it one. 786 | 787 | return function (value, replacer, space) { 788 | 789 | // The stringify method takes a value and an optional replacer, and an optional 790 | // space parameter, and returns a JSON text. The replacer can be a function 791 | // that can replace values, or an array of strings that will select the keys. 792 | // A default replacer method can be provided. Use of the space parameter can 793 | // produce text that is more easily readable. 794 | 795 | var i; 796 | gap = ""; 797 | indent = ""; 798 | 799 | // If the space parameter is a number, make an indent string containing that 800 | // many spaces. 801 | 802 | if (typeof space === "number") { 803 | for (i = 0; i < space; i += 1) { 804 | indent += " "; 805 | } 806 | 807 | // If the space parameter is a string, it will be used as the indent string. 808 | 809 | } else if (typeof space === "string") { 810 | indent = space; 811 | } 812 | 813 | // If there is a replacer, it must be a function or an array. 814 | // Otherwise, throw an error. 815 | 816 | rep = replacer; 817 | if (replacer && typeof replacer !== "function" && 818 | (typeof replacer !== "object" || 819 | typeof replacer.length !== "number")) { 820 | throw new Error("JSON.stringify"); 821 | } 822 | 823 | // Make a fake root object containing our value under the key of ''. 824 | // Return the result of stringifying the value. 825 | 826 | return str("", { "": value }); 827 | }; 828 | 829 | }()); 830 | 831 | const parseJson = json_parse(); 832 | 833 | let collapserElements, statusElement, jsonObject, copiedSelector, jsonSelector, jsonPath, selectedListItem, hoveredListItem, originalBody, supportBigInt; 834 | chrome.runtime.onMessage.addListener(message => { 835 | if (message.copy) { 836 | if (message.type == MENU_ID_COPY_PATH && jsonPath) { 837 | copyText(jsonPath); 838 | } else if (message.type == MENU_ID_COPY_VALUE || message.type == MENU_ID_COPY_JSON_VALUE) { 839 | let value = jsonObject; 840 | copiedSelector.forEach(propertyName => value = value[propertyName]); 841 | if (value) { 842 | if (supportBigInt) { 843 | value = jsonStringify(value); 844 | } else { 845 | value = JSON.stringify(value); 846 | } 847 | if (message.type == MENU_ID_COPY_VALUE) { 848 | copyText(value); 849 | } else if (message.type == MENU_ID_COPY_JSON_VALUE) { 850 | copyText(JSON.stringify(value)); 851 | } 852 | } 853 | } 854 | } 855 | }); 856 | init(); 857 | 858 | async function init() { 859 | let preElement = document.body.childNodes[0]; 860 | if (preElement && preElement.tagName != "PRE") { 861 | preElement = document.body.childNodes[1]; 862 | } 863 | if (document.body && (preElement && preElement.tagName == "PRE" || document.body.children.length == 0)) { 864 | const textElement = document.body.children.length ? preElement : document.body; 865 | const options = await sendMessage({ getOptions: true }); 866 | supportBigInt = options.supportBigInt; 867 | const jsonInfo = extractJsonInfo(textElement.innerText, options); 868 | if (jsonInfo) { 869 | originalBody = document.body.cloneNode(true); 870 | await processData(jsonInfo, options); 871 | } 872 | } 873 | } 874 | 875 | function extractJsonInfo(rawText, options) { 876 | const initialRawText = rawText; 877 | rawText = rawText.trim().replace(new RegExp(options.jsonPrefix), "").trim(); 878 | let tokens; 879 | if (detectJson(rawText)) { 880 | return { 881 | text: rawText, 882 | offset: initialRawText.indexOf(rawText) 883 | }; 884 | } else { 885 | tokens = rawText.match(/^([^\s(]*)\s*\(([\s\S]*)\)\s*;?$/); 886 | if (tokens && tokens[1] && tokens[2]) { 887 | if (detectJson(tokens[2].trim())) { 888 | return { 889 | functionName: tokens[1], 890 | text: tokens[2], 891 | offset: initialRawText.indexOf(tokens[2]) 892 | }; 893 | } 894 | } 895 | } 896 | 897 | function detectJson(text) { 898 | return ( 899 | (text.charAt(0) == "[" && text.charAt(text.length - 1) == "]") || 900 | (text.charAt(0) == "{" && text.charAt(1) != "-" && text.charAt(1) != "%" && text.charAt(text.length - 1) == "}") 901 | ); 902 | } 903 | } 904 | 905 | async function processData(jsonInfo, options) { 906 | if ((window == top || options.injectInFrame) && jsonInfo && jsonInfo.text) { 907 | const json = jsonInfo.text; 908 | const result = await sendMessage({ 909 | jsonToHTML: true, 910 | json, 911 | functionName: jsonInfo.functionName, 912 | supportBigInt 913 | }); 914 | if (result.html) { 915 | displayUI(result.stylesheet, result.html, options); 916 | try { 917 | if (supportBigInt) { 918 | jsonObject = parseJson(json); 919 | } else { 920 | jsonObject = JSON.parse(json); 921 | } 922 | } catch (error) { 923 | // ignored 924 | } 925 | } 926 | if (result.error) { 927 | displayError(result.stylesheet, result.error, result.loc, jsonInfo.offset); 928 | } 929 | } 930 | } 931 | 932 | function displayError(theme, error, loc, offset) { 933 | const userStyleElement = document.createElement("style"); 934 | const preElement = document.body.firstChild.firstChild; 935 | const textElement = preElement.textContent.substring(offset); 936 | const iconElement = document.createElement("span"); 937 | const contentElement = document.createElement("div"); 938 | const errorPositionElement = document.createElement("span"); 939 | const containerElement = document.createElement("div"); 940 | const closeButtonElement = document.createElement("div"); 941 | const range = document.createRange(); 942 | const ranges = []; 943 | let startRange = 0, indexRange = 0; 944 | userStyleElement.appendChild(document.createTextNode(theme)); 945 | document.head.appendChild(userStyleElement); 946 | while (indexRange != -1) { 947 | indexRange = textElement.indexOf("\n", startRange); 948 | ranges.push(startRange); 949 | startRange = indexRange + 1; 950 | } 951 | startRange = ranges[loc.first_line - 1] + loc.first_column + offset; 952 | const endRange = ranges[loc.last_line - 1] + loc.last_column + offset; 953 | range.setStart(preElement, startRange); 954 | if (startRange == endRange - 1) { 955 | range.setEnd(preElement, startRange); 956 | } else { 957 | range.setEnd(preElement, endRange); 958 | } 959 | errorPositionElement.className = "error-position"; 960 | range.surroundContents(errorPositionElement); 961 | iconElement.className = "error-icon"; 962 | iconElement.textContent = "⚠"; 963 | errorPositionElement.insertBefore(iconElement, errorPositionElement.firstChild); 964 | contentElement.className = "content"; 965 | closeButtonElement.className = "close-error"; 966 | closeButtonElement.addEventListener("click", onCloseError, false); 967 | contentElement.textContent = error; 968 | contentElement.appendChild(closeButtonElement); 969 | containerElement.className = "container"; 970 | containerElement.appendChild(contentElement); 971 | errorPositionElement.parentNode.insertBefore(containerElement, errorPositionElement.nextSibling); 972 | displayToolbox(true); 973 | 974 | function onCloseError(event) { 975 | if (event.isTrusted) { 976 | contentElement.parentElement.removeChild(contentElement); 977 | } 978 | } 979 | } 980 | 981 | function displayUI(stylesheet, html, options) { 982 | document.body.removeAttribute("style"); 983 | const userStyleElement = document.createElement("style"); 984 | statusElement = document.createElement("div"); 985 | userStyleElement.appendChild(document.createTextNode(stylesheet)); 986 | document.head.appendChild(userStyleElement); 987 | document.body.innerHTML = html; 988 | collapserElements = document.querySelectorAll("#json .collapsible .collapsible"); 989 | if (options.maxDepthLevelExpanded) { 990 | let selectorsCollapsedElements = "#json .collapsible "; 991 | for (let indexLevel = 0; indexLevel < options.maxDepthLevelExpanded; indexLevel++) { 992 | selectorsCollapsedElements += ".collapsible "; 993 | } 994 | document.querySelectorAll(selectorsCollapsedElements).forEach(element => element.parentElement.classList.add("collapsed")); 995 | } 996 | statusElement.className = "status"; 997 | document.body.appendChild(statusElement); 998 | document.body.addEventListener("mouseover", onMouseMove, false); 999 | document.body.addEventListener("click", onMouseClick, false); 1000 | document.body.addEventListener("contextmenu", onContextMenu, false); 1001 | document.body.addEventListener("click", onToggleCollapsible, false); 1002 | displayToolbox(); 1003 | } 1004 | 1005 | function displayToolbox(onlyViewSource) { 1006 | const viewSourceElement = document.createElement("a"); 1007 | const toolboxElement = document.createElement("div"); 1008 | let openCollapsiblesElement, closeCollapsiblesElement; 1009 | if (!onlyViewSource) { 1010 | openCollapsiblesElement = document.createElement("span"); 1011 | closeCollapsiblesElement = document.createElement("span"); 1012 | closeCollapsiblesElement.title = TITLE_CLOSE_COLLAPSIBLES; 1013 | closeCollapsiblesElement.innerText = LABEL_CLOSE_COLLAPSIBLES; 1014 | openCollapsiblesElement.title = TITLE_OPEN_COLLAPSIBLES; 1015 | openCollapsiblesElement.innerText = LABEL_OPEN_COLLAPSIBLES; 1016 | openCollapsiblesElement.addEventListener("click", onOpenCollapsibles, false); 1017 | closeCollapsiblesElement.addEventListener("click", onCloseCollapsibles, false); 1018 | toolboxElement.appendChild(openCollapsiblesElement); 1019 | } 1020 | toolboxElement.appendChild(viewSourceElement); 1021 | if (!onlyViewSource) { 1022 | toolboxElement.appendChild(closeCollapsiblesElement); 1023 | } 1024 | document.body.appendChild(toolboxElement); 1025 | toolboxElement.className = "toolbox"; 1026 | viewSourceElement.title = TITLE_VIEW_SOURCE; 1027 | viewSourceElement.innerText = LABEL_VIEW_SOURCE; 1028 | viewSourceElement.addEventListener("click", onViewSource, false); 1029 | } 1030 | 1031 | function onToggleCollapsible(event) { 1032 | if (event.isTrusted) { 1033 | const target = event.target; 1034 | if (target.className == "collapser") { 1035 | const collapsed = target.parentNode.getElementsByClassName("collapsible")[0]; 1036 | collapsed.parentNode.classList.toggle(CLASS_COLLAPSED); 1037 | event.stopImmediatePropagation(); 1038 | } 1039 | } 1040 | } 1041 | 1042 | function onOpenCollapsibles(event) { 1043 | if (event.isTrusted) { 1044 | collapserElements.forEach(collapsed => collapsed.parentNode.classList.remove(CLASS_COLLAPSED)); 1045 | } 1046 | } 1047 | 1048 | function onCloseCollapsibles(event) { 1049 | if (event.isTrusted) { 1050 | collapserElements.forEach(collapsed => collapsed.parentNode.classList.add(CLASS_COLLAPSED)); 1051 | } 1052 | } 1053 | 1054 | function onViewSource(event) { 1055 | if (event.isTrusted) { 1056 | document.body.replaceWith(originalBody); 1057 | } 1058 | } 1059 | 1060 | function onMouseMove(event) { 1061 | if (event.isTrusted) { 1062 | jsonPath = ""; 1063 | let element = getParentListItem(event.target); 1064 | if (element && event.target.tagName != TAG_LIST_ITEM) { 1065 | jsonSelector = []; 1066 | if (hoveredListItem) { 1067 | hoveredListItem.firstChild.classList.remove(CLASS_HOVERED); 1068 | } 1069 | hoveredListItem = element; 1070 | element.firstChild.classList.add(CLASS_HOVERED); 1071 | do { 1072 | if (element.parentNode.classList.contains("array")) { 1073 | const index = [].indexOf.call(element.parentNode.children, element); 1074 | jsonPath = "[" + index + "]" + jsonPath; 1075 | jsonSelector.unshift(index); 1076 | } 1077 | if (element.parentNode.classList.contains("obj")) { 1078 | const key = element.firstChild.firstChild.innerText; 1079 | if (REGEXP_IDENTIFIER.test(key)) { 1080 | jsonPath = "." + key + jsonPath; 1081 | } else { 1082 | jsonPath = "[" + JSON.stringify(key) + "]" + jsonPath; 1083 | } 1084 | jsonSelector.unshift(key); 1085 | } 1086 | element = element.parentNode.parentNode.parentNode; 1087 | } while (element.tagName == TAG_LIST_ITEM); 1088 | if (jsonPath.charAt(0) == ".") { 1089 | jsonPath = jsonPath.substring(1); 1090 | } 1091 | statusElement.innerText = jsonPath; 1092 | } else if (hoveredListItem) { 1093 | hoveredListItem.firstChild.classList.remove(CLASS_HOVERED); 1094 | hoveredListItem = null; 1095 | statusElement.innerText = ""; 1096 | jsonSelector = []; 1097 | } 1098 | } 1099 | } 1100 | 1101 | function onMouseClick(event) { 1102 | if (event.isTrusted) { 1103 | const previousSelectedListItem = selectedListItem; 1104 | if (selectedListItem) { 1105 | selectedListItem.firstChild.classList.remove(CLASS_SELECTED); 1106 | selectedListItem = null; 1107 | } 1108 | const newSelectedListItem = getParentListItem(event.target); 1109 | if (newSelectedListItem && previousSelectedListItem != newSelectedListItem) { 1110 | selectedListItem = newSelectedListItem; 1111 | selectedListItem.firstChild.classList.add(CLASS_SELECTED); 1112 | } 1113 | } 1114 | } 1115 | 1116 | function onContextMenu(event) { 1117 | if (event.isTrusted) { 1118 | copiedSelector = jsonSelector ? Array.from(jsonSelector) : []; 1119 | } 1120 | } 1121 | 1122 | function getParentListItem(element) { 1123 | if (element.tagName != TAG_LIST_ITEM) { 1124 | while (element && element.tagName != TAG_LIST_ITEM) { 1125 | element = element.parentNode; 1126 | } 1127 | } 1128 | if (element && element.tagName == TAG_LIST_ITEM) { 1129 | return element; 1130 | } 1131 | } 1132 | 1133 | function copyText(value) { 1134 | const command = "copy"; 1135 | document.addEventListener(command, listener); 1136 | document.execCommand(command); 1137 | document.removeEventListener(command, listener); 1138 | 1139 | function listener(event) { 1140 | event.clipboardData.setData("text/plain", value); 1141 | event.preventDefault(); 1142 | } 1143 | } 1144 | 1145 | function sendMessage(message) { 1146 | return new Promise(resolve => chrome.runtime.sendMessage(message, result => resolve(result))); 1147 | } --------------------------------------------------------------------------------