@softwarity/geojson-editor 1.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2024 Softwarity
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,276 @@
1
+ <p align="center">
2
+ <a href="https://www.softwarity.io/">
3
+ <img src="https://www.softwarity.io/img/softwarity.svg" alt="Softwarity" height="60">
4
+ </a>
5
+ </p>
6
+
7
+ # @softwarity/geojson-editor
8
+
9
+ <p align="center">
10
+ <a href="https://www.npmjs.com/package/@softwarity/geojson-editor">
11
+ <img src="https://img.shields.io/npm/v/@softwarity/geojson-editor?color=blue&label=npm" alt="npm version">
12
+ </a>
13
+ <a href="https://www.npmjs.com/package/@softwarity/geojson-editor">
14
+ <img src="https://img.shields.io/npm/dm/@softwarity/geojson-editor?color=green" alt="npm downloads">
15
+ </a>
16
+ <a href="https://bundlephobia.com/package/@softwarity/geojson-editor">
17
+ <img src="https://img.shields.io/bundlephobia/minzip/@softwarity/geojson-editor?label=size" alt="bundle size">
18
+ </a>
19
+ <a href="https://github.com/softwarity/geojson-editor/blob/main/LICENSE">
20
+ <img src="https://img.shields.io/npm/l/@softwarity/geojson-editor" alt="license">
21
+ </a>
22
+ </p>
23
+
24
+ A feature-rich, framework-agnostic **Web Component** for editing GeoJSON features with syntax highlighting, collapsible nodes, and integrated color picker.
25
+
26
+ **[🚀 Try the Live Demo](https://softwarity.github.io/geojson-editor/)**
27
+
28
+ ## Why not Monaco, CodeMirror, or Prism?
29
+
30
+ | | @softwarity/geojson-editor | Monaco Editor | CodeMirror | Prism.js |
31
+ |---|:---:|:---:|:---:|:---:|
32
+ | **Size (gzip)** | ~10 KB | ~2.5 MB | ~150 KB | ~15 KB + plugins |
33
+ | **GeoJSON validation** | ✅ Built-in | ❌ Manual | ❌ Manual | ❌ None |
34
+ | **Type highlighting** | ✅ Contextual | ⚠️ Generic JSON | ⚠️ Generic JSON | ⚠️ Generic JSON |
35
+ | **Invalid type detection** | ✅ Visual feedback | ❌ | ❌ | ❌ |
36
+ | **Collapsible nodes** | ✅ Native | ✅ | ✅ Plugin | ❌ |
37
+ | **Color picker** | ✅ Integrated | ❌ | ❌ | ❌ |
38
+ | **Auto-collapse coordinates** | ✅ | ❌ | ❌ | ❌ |
39
+ | **FeatureCollection mode** | ✅ | ❌ | ❌ | ❌ |
40
+ | **Dark mode detection** | ✅ Auto | ⚠️ Manual | ⚠️ Manual | ⚠️ Manual |
41
+ | **Dependencies** | 0 | Many | Few | 0 |
42
+ | **Setup complexity** | 1 line | Complex | Moderate | Simple |
43
+
44
+ **TL;DR**: If you're building a GeoJSON-focused application and need a lightweight, specialized editor with built-in validation and GeoJSON-aware features, this component does exactly that — without the overhead of a general-purpose code editor.
45
+
46
+ ## Features
47
+
48
+ - **GeoJSON-Aware Highlighting** - Distinct colors for GeoJSON keywords (`type`, `coordinates`, `geometry`, etc.)
49
+ - **GeoJSON Type Validation** - Valid types (`Point`, `LineString`, `Polygon`, etc.) highlighted distinctly; invalid types (`LinearRing`, unknown types) shown with error styling (colors configurable via theme)
50
+ - **Syntax Highlighting** - JSON syntax highlighting with customizable color schemes
51
+ - **Collapsible Nodes** - Collapse/expand JSON objects and arrays with visual indicators (`{...}` / `[...]`); `coordinates` auto-collapsed on load
52
+ - **Color Picker** - Built-in color picker for color properties in left gutter
53
+ - **Dark/Light Themes** - Automatic theme detection from parent page (Bootstrap, Tailwind, custom)
54
+ - **Auto-format** - Optional automatic JSON formatting in real-time
55
+ - **Readonly Mode** - Visual indicator with diagonal stripes when editing is disabled
56
+ - **Block Editing in Collapsed Areas** - Prevents accidental edits in collapsed sections
57
+ - **Smart Copy/Paste** - Copy includes expanded content even from collapsed nodes
58
+ - **FeatureCollection Mode** - Optional mode to auto-wrap features in a FeatureCollection structure
59
+
60
+ ## Installation
61
+
62
+ ### Option 1: CDN (No build step required)
63
+
64
+ Simply add a script tag to your HTML file:
65
+
66
+ ```html
67
+ <!-- Using unpkg -->
68
+ <script type="module" src="https://unpkg.com/@softwarity/geojson-editor"></script>
69
+
70
+ <!-- Or using jsDelivr -->
71
+ <script type="module" src="https://cdn.jsdelivr.net/npm/@softwarity/geojson-editor"></script>
72
+ ```
73
+
74
+ You can also specify a version:
75
+
76
+ ```html
77
+ <!-- Specific version -->
78
+ <script type="module" src="https://unpkg.com/@softwarity/geojson-editor@1.0.0"></script>
79
+
80
+ <!-- Latest minor/patch of v1 -->
81
+ <script type="module" src="https://unpkg.com/@softwarity/geojson-editor@1"></script>
82
+ ```
83
+
84
+ ### Option 2: NPM (With bundler)
85
+
86
+ If you're using a bundler (Vite, Webpack, Rollup, etc.):
87
+
88
+ ```bash
89
+ npm install @softwarity/geojson-editor
90
+ ```
91
+
92
+ Then import in your JavaScript:
93
+
94
+ ```javascript
95
+ import '@softwarity/geojson-editor';
96
+ ```
97
+
98
+ ## Usage
99
+
100
+ ### Basic Usage (FeatureCollection mode)
101
+
102
+ ```html
103
+ <!DOCTYPE html>
104
+ <html lang="en">
105
+ <head>
106
+ <script type="module" src="https://unpkg.com/@softwarity/geojson-editor"></script>
107
+ </head>
108
+ <body>
109
+ <!-- User edits features, component wraps in FeatureCollection -->
110
+ <geojson-editor
111
+ feature-collection
112
+ placeholder="Enter GeoJSON features here..."
113
+ ></geojson-editor>
114
+ </body>
115
+ </html>
116
+ ```
117
+
118
+ ### Standalone Mode (Full GeoJSON)
119
+
120
+ ```html
121
+ <!-- User edits a complete GeoJSON object (Feature or FeatureCollection) -->
122
+ <geojson-editor
123
+ placeholder="Enter GeoJSON here..."
124
+ ></geojson-editor>
125
+ ```
126
+
127
+ ### With Auto-format and Theme Detection
128
+
129
+ ```html
130
+ <geojson-editor
131
+ feature-collection
132
+ dark-selector="html.dark"
133
+ auto-format
134
+ ></geojson-editor>
135
+ ```
136
+
137
+ ### Listen to Changes
138
+
139
+ ```javascript
140
+ const editor = document.querySelector('geojson-editor');
141
+
142
+ // Valid GeoJSON emits change event with parsed object directly
143
+ editor.addEventListener('change', (e) => {
144
+ console.log('GeoJSON:', e.detail); // Parsed GeoJSON object
145
+ });
146
+
147
+ // Invalid JSON or GeoJSON validation error emits error event
148
+ editor.addEventListener('error', (e) => {
149
+ console.error('Error:', e.detail.error);
150
+ console.log('Errors:', e.detail.errors); // Array of validation errors (if GeoJSON validation)
151
+ });
152
+ ```
153
+
154
+ ## Attributes
155
+
156
+ | Attribute | Type | Default | Description |
157
+ |-----------|------|---------|-------------|
158
+ | `value` | `string` | `""` | Initial editor content |
159
+ | `placeholder` | `string` | `""` | Placeholder text |
160
+ | `readonly` | `boolean` | `false` | Make editor read-only |
161
+ | `auto-format` | `boolean` | `false` | Auto-format JSON on input |
162
+ | `dark-selector` | `string` | `".dark"` | CSS selector for dark theme (if matches → dark, else → light) |
163
+ | `feature-collection` | `boolean` | `false` | When set, wraps editor content in a FeatureCollection for validation/events |
164
+
165
+ **Note:** `coordinates` nodes are automatically collapsed when content is loaded to improve readability. All nodes can be manually expanded/collapsed by clicking the toggle button.
166
+
167
+ ### Dark Selector Syntax
168
+
169
+ The `dark-selector` attribute determines when the dark theme is active. If the selector matches, dark theme is applied; otherwise, light theme is used.
170
+
171
+ **Examples:**
172
+
173
+ - `.dark` - Component has `dark` class: `<geojson-editor class="dark">`
174
+ - `html.dark` - HTML element has `dark` class (Tailwind CSS): `<html class="dark">`
175
+ - `html[data-bs-theme=dark]` - HTML has Bootstrap theme attribute: `<html data-bs-theme="dark">`
176
+ - Empty string `""` - Uses component's `data-color-scheme` attribute as fallback
177
+
178
+ ## API Methods
179
+
180
+ ```javascript
181
+ const editor = document.querySelector('geojson-editor');
182
+
183
+ // Get/set theme
184
+ const themes = editor.getTheme();
185
+ editor.setTheme({
186
+ dark: { background: '#000', textColor: '#fff' },
187
+ light: { background: '#fff', textColor: '#000' }
188
+ });
189
+ editor.resetTheme();
190
+
191
+ // Get/set value
192
+ editor.setAttribute('value', JSON.stringify(data));
193
+ const value = editor.querySelector('#textarea').value; // via Shadow DOM
194
+ ```
195
+
196
+ ## Events
197
+
198
+ ### `change`
199
+
200
+ Fired when content changes and GeoJSON is valid (debounced 150ms).
201
+
202
+ ```javascript
203
+ editor.addEventListener('change', (e) => {
204
+ console.log(e.detail); // Parsed GeoJSON object directly
205
+ });
206
+ ```
207
+
208
+ **Event detail:** The parsed GeoJSON object directly. In `feature-collection` mode, the wrapper is included.
209
+
210
+ **Example with FeatureCollection mode:**
211
+
212
+ ```html
213
+ <geojson-editor feature-collection></geojson-editor>
214
+ ```
215
+
216
+ ```javascript
217
+ // User edits features only, but change event includes the FeatureCollection wrapper
218
+ editor.addEventListener('change', (e) => {
219
+ console.log(e.detail);
220
+ // → { type: "FeatureCollection", features: [{ type: "Feature", ... }] }
221
+ });
222
+ ```
223
+
224
+ ### `error`
225
+
226
+ Fired when content changes but JSON is invalid or GeoJSON validation fails (debounced 150ms).
227
+
228
+ ```javascript
229
+ editor.addEventListener('error', (e) => {
230
+ console.error(e.detail.error); // Error message
231
+ console.log(e.detail.errors); // Array of validation errors (GeoJSON validation only)
232
+ console.log(e.detail.content); // Raw content for debugging
233
+ });
234
+ ```
235
+
236
+ **Event detail properties:**
237
+
238
+ | Property | Type | Description |
239
+ |----------|------|-------------|
240
+ | `error` | `string` | Error message (JSON parse error or GeoJSON validation summary) |
241
+ | `errors` | `string[]` | Array of validation errors with paths (GeoJSON validation only) |
242
+ | `content` | `string` | Raw editor content (for debugging) |
243
+
244
+ **GeoJSON validation errors include:**
245
+ - Invalid types (e.g., `"LinearRing"`)
246
+ - Unknown types (any `type` value not in the GeoJSON specification)
247
+
248
+ ## Styling
249
+
250
+ The component uses Shadow DOM with CSS variables for theming. Themes can be customized via the `setTheme()` API.
251
+
252
+ ## Browser Support
253
+
254
+ Works in all modern browsers supporting:
255
+ - Web Components
256
+ - Shadow DOM
257
+ - ES6 Modules
258
+
259
+ ## Development
260
+
261
+ See [DEVELOPMENT.md](DEVELOPMENT.md) for detailed development guide.
262
+
263
+ ```bash
264
+ # Install dependencies
265
+ npm install
266
+
267
+ # Start dev server with live demo
268
+ npm run dev
269
+
270
+ # Build for production
271
+ npm run build
272
+ ```
273
+
274
+ ## License
275
+
276
+ MIT
@@ -0,0 +1,10 @@
1
+ var e=Object.defineProperty,t=(t,n,o)=>n in t?e(t,n,{enumerable:!0,configurable:!0,writable:!0,value:o}):t[n]=o,n=(e,n,o)=>t(e,"symbol"!=typeof n?n+"":n,o);
2
+ /**
3
+ * @license MIT
4
+ * @name @softwarity/geojson-editor
5
+ * @version 1.0.1
6
+ * @author Softwarity (https://www.softwarity.io/)
7
+ * @copyright 2024 Softwarity
8
+ * @see https://github.com/softwarity/geojson-editor
9
+ */
10
+ const o=class e extends HTMLElement{constructor(){super(),this.attachShadow({mode:"open"}),this.collapsedData=new Map,this.colorPositions=[],this.nodeTogglePositions=[],this.highlightTimer=null,this._cachedLineHeight=null,this._cachedPaddingTop=null,this.themes={dark:{...e.DEFAULT_THEMES.dark},light:{...e.DEFAULT_THEMES.light}}}static get observedAttributes(){return["readonly","value","placeholder","auto-format","dark-selector","feature-collection"]}connectedCallback(){this.render(),this.setupEventListeners(),this.updatePrefixSuffix(),this.updateThemeCSS(),this.value&&(this.updateHighlight(),requestAnimationFrame(()=>{this.applyAutoCollapsed()}))}attributeChangedCallback(e,t,n){var o;if(t!==n)if("value"===e)this.updateValue(n);else if("readonly"===e)this.updateReadonly();else if("placeholder"===e){const e=this.shadowRoot.querySelector("textarea");e&&(e.placeholder=n||"")}else if("dark-selector"===e)this.updateThemeCSS();else if("feature-collection"===e)this.updatePrefixSuffix();else if("auto-format"===e){const e=null==(o=this.shadowRoot)?void 0:o.getElementById("textarea");e&&e.value&&this.autoFormat&&(this.autoFormatContent(),this.updateHighlight())}}get readonly(){return this.hasAttribute("readonly")}get value(){return this.getAttribute("value")||""}get placeholder(){return this.getAttribute("placeholder")||""}get autoFormat(){return this.hasAttribute("auto-format")}get featureCollection(){return this.hasAttribute("feature-collection")}get prefix(){return this.featureCollection?e.FEATURE_COLLECTION_PREFIX:""}get suffix(){return this.featureCollection?e.FEATURE_COLLECTION_SUFFIX:""}render(){const e=`\n <div class="editor-prefix" id="editorPrefix"></div>\n <div class="editor-wrapper">\n <div class="gutter">\n <div class="gutter-content" id="gutterContent"></div>\n </div>\n <div class="editor-content">\n <div class="highlight-layer" id="highlightLayer"></div>\n <textarea\n id="textarea"\n spellcheck="false"\n autocomplete="off"\n autocorrect="off"\n autocapitalize="off"\n placeholder="${this.placeholder}"\n ></textarea>\n </div>\n </div>\n <div class="editor-suffix" id="editorSuffix"></div>\n `;this.shadowRoot.innerHTML="\n <style>\n /* Global reset with exact values to prevent external CSS interference */\n :host *,\n :host *::before,\n :host *::after {\n box-sizing: border-box;\n font-family: 'Courier New', Courier, monospace;\n font-size: 13px;\n font-weight: normal;\n font-style: normal;\n font-variant: normal;\n line-height: 1.5;\n letter-spacing: 0;\n text-transform: none;\n text-decoration: none;\n text-indent: 0;\n word-spacing: 0;\n }\n\n :host {\n display: flex;\n flex-direction: column;\n position: relative;\n width: 100%;\n height: 400px;\n font-family: 'Courier New', Courier, monospace;\n font-size: 13px;\n line-height: 1.5;\n border-radius: 4px;\n overflow: hidden;\n }\n\n :host([readonly]) .editor-wrapper::after {\n content: '';\n position: absolute;\n top: 0;\n left: 0;\n right: 0;\n bottom: 0;\n pointer-events: none;\n background: repeating-linear-gradient(\n -45deg,\n rgba(128, 128, 128, 0.08),\n rgba(128, 128, 128, 0.08) 3px,\n transparent 3px,\n transparent 12px\n );\n z-index: 1;\n }\n\n :host([readonly]) textarea {\n cursor: text;\n }\n\n .editor-wrapper {\n position: relative;\n width: 100%;\n flex: 1;\n background: var(--bg-color);\n display: flex;\n font-family: 'Courier New', Courier, monospace;\n font-size: 13px;\n line-height: 1.5;\n }\n\n .gutter {\n width: 24px;\n height: 100%;\n background: var(--gutter-bg);\n border-right: 1px solid var(--gutter-border);\n overflow: hidden;\n flex-shrink: 0;\n position: relative;\n }\n\n .gutter-content {\n position: absolute;\n top: 0;\n left: 0;\n width: 100%;\n padding: 8px 4px;\n }\n\n .gutter-line {\n position: absolute;\n left: 0;\n width: 100%;\n height: 1.5em;\n display: flex;\n align-items: center;\n justify-content: center;\n }\n\n .color-indicator {\n width: 12px;\n height: 12px;\n border-radius: 2px;\n border: 1px solid #555;\n cursor: pointer;\n transition: transform 0.1s;\n flex-shrink: 0;\n }\n\n .color-indicator:hover {\n transform: scale(1.2);\n border-color: #fff;\n }\n\n .collapse-button {\n width: 12px;\n height: 12px;\n background: var(--collapse-btn-bg);\n border: 1px solid var(--collapse-btn-border);\n border-radius: 2px;\n color: var(--collapse-btn);\n font-size: 8px;\n font-weight: bold;\n cursor: pointer;\n display: flex;\n align-items: center;\n justify-content: center;\n transition: all 0.1s;\n flex-shrink: 0;\n user-select: none;\n }\n\n .collapse-button:hover {\n background: var(--collapse-btn-bg);\n border-color: var(--collapse-btn);\n transform: scale(1.1);\n }\n\n .color-picker-popup {\n position: absolute;\n background: #2d2d30;\n border: 1px solid #555;\n border-radius: 4px;\n padding: 8px;\n z-index: 1000;\n box-shadow: 0 4px 12px rgba(0,0,0,0.5);\n }\n\n .color-picker-popup input[type=\"color\"] {\n width: 150px;\n height: 30px;\n border: none;\n cursor: pointer;\n }\n\n .editor-content {\n position: relative;\n flex: 1;\n overflow: hidden;\n }\n\n .highlight-layer {\n position: absolute;\n top: 0;\n left: 0;\n width: 100%;\n height: 100%;\n padding: 8px 12px;\n font-family: 'Courier New', Courier, monospace;\n font-size: 13px;\n font-weight: normal;\n font-style: normal;\n line-height: 1.5;\n white-space: pre-wrap;\n word-wrap: break-word;\n overflow: auto;\n pointer-events: none;\n z-index: 1;\n color: var(--text-color);\n }\n\n .highlight-layer::-webkit-scrollbar {\n display: none;\n }\n\n textarea {\n position: absolute;\n top: 0;\n left: 0;\n width: 100%;\n height: 100%;\n padding: 8px 12px;\n margin: 0;\n border: none;\n outline: none;\n background: transparent;\n color: transparent;\n caret-color: var(--caret-color);\n font-family: 'Courier New', Courier, monospace;\n font-size: 13px;\n font-weight: normal;\n font-style: normal;\n line-height: 1.5;\n white-space: pre-wrap;\n word-wrap: break-word;\n resize: none;\n overflow: auto;\n z-index: 2;\n box-sizing: border-box;\n }\n\n textarea::selection {\n background: rgba(51, 153, 255, 0.3);\n }\n\n textarea::placeholder {\n color: #6a6a6a;\n font-family: 'Courier New', Courier, monospace;\n font-size: 13px;\n font-weight: normal;\n font-style: normal;\n opacity: 1;\n }\n\n textarea:disabled {\n cursor: not-allowed;\n opacity: 0.6;\n }\n\n /* Syntax highlighting colors */\n .json-key {\n color: var(--json-key);\n }\n\n .json-string {\n color: var(--json-string);\n }\n\n .json-number {\n color: var(--json-number);\n }\n\n .json-boolean {\n color: var(--json-boolean);\n }\n\n .json-null {\n color: var(--json-null);\n }\n\n .json-punctuation {\n color: var(--json-punct);\n }\n\n /* GeoJSON-specific highlighting */\n .geojson-key {\n color: var(--geojson-key);\n font-weight: 600;\n }\n\n .geojson-type {\n color: var(--geojson-type);\n font-weight: 600;\n }\n\n .geojson-type-invalid {\n color: var(--geojson-type-invalid);\n font-weight: 600;\n }\n\n .json-key-invalid {\n color: var(--json-key-invalid);\n }\n\n /* Prefix and suffix styling */\n .editor-prefix,\n .editor-suffix {\n padding: 4px 12px;\n color: var(--text-color);\n background: var(--bg-color);\n user-select: none;\n white-space: pre-wrap;\n word-wrap: break-word;\n flex-shrink: 0;\n font-family: 'Courier New', Courier, monospace;\n font-size: 13px;\n line-height: 1.5;\n opacity: 0.6;\n border-left: 3px solid rgba(102, 126, 234, 0.5);\n }\n\n .editor-prefix {\n border-bottom: 1px solid rgba(255, 255, 255, 0.1);\n }\n\n .editor-suffix {\n border-top: 1px solid rgba(255, 255, 255, 0.1);\n }\n\n /* Scrollbar styling */\n textarea::-webkit-scrollbar {\n width: 10px;\n height: 10px;\n }\n\n textarea::-webkit-scrollbar-track {\n background: #1e1e1e;\n }\n\n textarea::-webkit-scrollbar-thumb {\n background: #424242;\n border-radius: 5px;\n }\n\n textarea::-webkit-scrollbar-thumb:hover {\n background: #4e4e4e;\n }\n </style>\n "+e}setupEventListeners(){const e=this.shadowRoot.getElementById("textarea"),t=this.shadowRoot.getElementById("highlightLayer");e.addEventListener("scroll",()=>{t.scrollTop=e.scrollTop,t.scrollLeft=e.scrollLeft,this.syncGutterScroll(e.scrollTop)}),e.addEventListener("input",()=>{clearTimeout(this.highlightTimer),this.highlightTimer=setTimeout(()=>{this.autoFormat&&this.autoFormatContentWithCursor(),this.updateHighlight(),this.emitChange()},150)}),e.addEventListener("paste",()=>{clearTimeout(this.highlightTimer),setTimeout(()=>{this.autoFormat&&this.autoFormatContentWithCursor(),this.updateHighlight(),this.emitChange()},10)}),this.shadowRoot.getElementById("gutterContent").addEventListener("click",e=>{if(e.target.classList.contains("color-indicator")){const t=parseInt(e.target.dataset.line),n=e.target.dataset.color,o=e.target.dataset.attributeName;this.showColorPicker(e.target,t,n,o)}else if(e.target.classList.contains("collapse-button")){const t=e.target.dataset.nodeKey,n=parseInt(e.target.dataset.line);this.toggleCollapse(t,n)}}),this.shadowRoot.querySelector(".gutter").addEventListener("wheel",t=>{t.preventDefault(),e.scrollTop+=t.deltaY}),e.addEventListener("keydown",e=>{this.handleKeydownInCollapsedArea(e)}),e.addEventListener("copy",e=>{this.handleCopyWithCollapsedContent(e)}),e.addEventListener("cut",e=>{this.handleCutWithCollapsedContent(e)}),this.updateReadonly()}syncGutterScroll(e){this.shadowRoot.getElementById("gutterContent").style.transform=`translateY(-${e}px)`}updateReadonly(){const e=this.shadowRoot.getElementById("textarea");e&&(e.disabled=this.readonly)}updateValue(e){const t=this.shadowRoot.getElementById("textarea");if(t&&t.value!==e){if(t.value=e||"",this.autoFormat&&e)try{const n=this.prefix,o=this.suffix,s=n.trimEnd().endsWith("["),i=o.trimStart().startsWith("]");if(s&&i){const n="["+e+"]",o=JSON.parse(n),s=JSON.stringify(o,null,2).split("\n");s.length>2?t.value=s.slice(1,-1).join("\n"):t.value=""}else if(!n&&!o){const n=JSON.parse(e);t.value=JSON.stringify(n,null,2)}}catch{}this.updateHighlight(),t.value&&requestAnimationFrame(()=>{this.applyAutoCollapsed()})}}updatePrefixSuffix(){const e=this.shadowRoot.getElementById("editorPrefix"),t=this.shadowRoot.getElementById("editorSuffix");e&&(this.prefix?(e.textContent=this.prefix,e.style.display="block"):(e.textContent="",e.style.display="none")),t&&(this.suffix?(t.textContent=this.suffix,t.style.display="block"):(t.textContent="",t.style.display="none"))}updateHighlight(){const e=this.shadowRoot.getElementById("textarea"),t=this.shadowRoot.getElementById("highlightLayer");if(!e||!t)return;const n=e.value,{highlighted:o,colors:s,toggles:i}=this.highlightJSON(n);t.innerHTML=o,this.colorPositions=s,this.nodeTogglePositions=i,this.updateGutter()}highlightJSON(t){if(!t.trim())return{highlighted:"",colors:[],toggles:[]};const n=t.split("\n"),o=[],s=[];let i=[];const a=this.buildContextMap(t);return n.forEach((t,n)=>{const r=e.REGEX;let l;for(r.colorInLine.lastIndex=0;null!==(l=r.colorInLine.exec(t));)o.push({line:n,color:l[2],attributeName:l[1]});const h=t.match(r.collapsibleNode);if(h){const e=h[2];t.includes("{...}")||t.includes("[...]")?s.push({line:n,nodeKey:e,isCollapsed:!0}):this.bracketClosesOnSameLine(t,h[3])||s.push({line:n,nodeKey:e,isCollapsed:!1})}const d=a.get(n);i.push(this.highlightSyntax(t,d))}),{highlighted:i.join("\n"),colors:o,toggles:s}}buildContextMap(t){var n;const o=t.split("\n"),s=new Map,i=[];let a=null;const r=this.featureCollection?"Feature":null;for(let t=0;t<o.length;t++){const l=o[t],h=i.length>0?null==(n=i[i.length-1])?void 0:n.context:r;s.set(t,h);for(let t=0;t<l.length;t++){const n=l[t];if('"'===n){const n=l.substring(t).match(/^"([^"]+)"\s*:/);if(n){const o=n[1];e.CONTEXT_CHANGING_KEYS[o]&&(a=e.CONTEXT_CHANGING_KEYS[o]),t+=n[0].length-1;continue}}if('"'===n&&i.length>0&&l.substring(0,t).match(/"type"\s*:\s*$/)){const n=l.substring(t).match(/^"([^"]+)"/);if(n&&e.GEOJSON_TYPES_ALL.includes(n[1])){const e=i[i.length-1];e&&(e.context=n[1])}}if("{"===n||"["===n){let e;if(a)e=a,a=null;else if(0===i.length)e=r;else{const t=i[i.length-1];e=t&&t.isArray?t.context:null}i.push({context:e,isArray:"["===n})}("}"===n||"]"===n)&&i.length>0&&i.pop()}}return s}highlightSyntax(t,n){if(!t.trim())return"";const o=n?e.VALID_KEYS_BY_CONTEXT[n]:null,s=e.REGEX;return t.replace(s.ampersand,"&amp;").replace(s.lessThan,"&lt;").replace(s.greaterThan,"&gt;").replace(s.jsonKey,(t,s)=>"properties"===n?`<span class="json-key">"${s}"</span>:`:e.GEOJSON_STRUCTURAL_KEYS.includes(s)?`<span class="geojson-key">"${s}"</span>:`:(t=>!(!e.GEOJSON_STRUCTURAL_KEYS.includes(t)&&n&&null!=o)||o.includes(t))(s)?`<span class="json-key">"${s}"</span>:`:`<span class="json-key-invalid">"${s}"</span>:`).replace(s.typeValue,(t,o)=>(t=>!n||"properties"===n||("geometry"===n||e.GEOJSON_TYPES_GEOMETRY.includes(n)?e.GEOJSON_TYPES_GEOMETRY.includes(t):"Feature"!==n&&"FeatureCollection"!==n||e.GEOJSON_TYPES_ALL.includes(t)))(o)?`<span class="geojson-key">"type"</span>: <span class="geojson-type">"${o}"</span>`:`<span class="geojson-key">"type"</span>: <span class="geojson-type-invalid">"${o}"</span>`).replace(s.stringValue,(e,t)=>e.includes("<span")?e:`: <span class="json-string">"${t}"</span>`).replace(s.numberAfterColon,': <span class="json-number">$1</span>').replace(s.boolean,': <span class="json-boolean">$1</span>').replace(s.nullValue,': <span class="json-null">$1</span>').replace(s.allNumbers,'<span class="json-number">$1</span>').replace(s.punctuation,'<span class="json-punctuation">$1</span>')}toggleCollapse(e,t){const n=this.shadowRoot.getElementById("textarea"),o=n.value.split("\n"),s=o[t];if(s.includes("{...}")||s.includes("[...]")){let n=null,i=null;const a=`${t}-${e}`;if(this.collapsedData.has(a))n=a,i=this.collapsedData.get(a);else for(const[t,o]of this.collapsedData.entries())if(o.nodeKey===e){const e=s.match(/^(\s*)/)[1].length;if(o.indent===e){n=t,i=o;break}}if(!n||!i)return;const{originalLine:r,content:l}=i;o[t]=r,o.splice(t+1,0,...l),this.collapsedData.delete(n)}else{const n=s.match(/^(\s*)"([^"]+)"\s*:\s*([{\[])/);if(!n)return;const i=n[1],a=n[3],r="{"===a?"}":"]";if(this.bracketClosesOnSameLine(s,a))return;let l=1,h=t;const d=[];for(let e=t+1;e<o.length;e++){const t=o[e];for(const e of t)e===a&&l++,e===r&&l--;if(d.push(t),0===l){h=e;break}}const c=`${t}-${e}`;this.collapsedData.set(c,{originalLine:s,content:d,indent:i.length,nodeKey:e});const p=s.substring(0,s.indexOf(a)),u=o[h]&&o[h].trim().endsWith(",");o[t]=`${p}${a}...${r}${u?",":""}`,o.splice(t+1,h-t)}n.value=o.join("\n"),this.updateHighlight()}applyAutoCollapsed(){const e=this.shadowRoot.getElementById("textarea");if(!e||!e.value)return;const t=e.value.split("\n");for(let e=t.length-1;e>=0;e--){const n=t[e],o=n.match(/^(\s*)"(\w+)"\s*:\s*([{\[])/);if(o){const s=o[2];if("coordinates"===s){const i=o[1],a=o[3],r="{"===a?"}":"]";if(this.bracketClosesOnSameLine(n,a))continue;let l=1,h=e;const d=[];for(let n=e+1;n<t.length;n++){const e=t[n];for(const t of e)t===a&&l++,t===r&&l--;if(d.push(e),0===l){h=n;break}}const c=`${e}-${s}`;this.collapsedData.set(c,{originalLine:n,content:d,indent:i.length,nodeKey:s});const p=n.substring(0,n.indexOf(a)),u=t[h]&&t[h].trim().endsWith(",");t[e]=`${p}${a}...${r}${u?",":""}`,t.splice(e+1,h-e)}}}e.value=t.join("\n"),this.updateHighlight()}updateGutter(){const e=this.shadowRoot.getElementById("gutterContent"),t=this.shadowRoot.getElementById("textarea");if(!t)return;if(null===this._cachedLineHeight){const e=getComputedStyle(t);this._cachedLineHeight=parseFloat(e.lineHeight),this._cachedPaddingTop=parseFloat(e.paddingTop)}const n=this._cachedLineHeight,o=this._cachedPaddingTop;e.textContent="";const s=new Map;this.colorPositions.forEach(({line:e,color:t,attributeName:n})=>{s.has(e)||s.set(e,{colors:[],buttons:[]}),s.get(e).colors.push({color:t,attributeName:n})}),this.nodeTogglePositions.forEach(({line:e,nodeKey:t,isCollapsed:n})=>{s.has(e)||s.set(e,{colors:[],buttons:[]}),s.get(e).buttons.push({nodeKey:t,isCollapsed:n})});const i=document.createDocumentFragment();s.forEach((e,t)=>{const s=document.createElement("div");s.className="gutter-line",s.style.top=`${o+t*n}px`,e.colors.forEach(({color:e,attributeName:n})=>{const o=document.createElement("div");o.className="color-indicator",o.style.backgroundColor=e,o.dataset.line=t,o.dataset.color=e,o.dataset.attributeName=n,o.title=`${n}: ${e}`,s.appendChild(o)}),e.buttons.forEach(({nodeKey:e,isCollapsed:n})=>{const o=document.createElement("div");o.className="collapse-button",o.textContent=n?"+":"-",o.dataset.line=t,o.dataset.nodeKey=e,o.title=n?"Expand":"Collapse",s.appendChild(o)}),i.appendChild(s)}),e.appendChild(i)}showColorPicker(e,t,n,o){const s=document.querySelector(".geojson-color-picker-input");s&&s.remove();const i=document.createElement("input");i.type="color",i.value=n,i.className="geojson-color-picker-input";const a=e.getBoundingClientRect();i.style.position="fixed",i.style.left=`${a.left}px`,i.style.top=`${a.top}px`,i.style.width="12px",i.style.height="12px",i.style.opacity="0.01",i.style.border="none",i.style.padding="0",i.style.zIndex="9999",i.addEventListener("input",e=>{this.updateColorValue(t,e.target.value,o)}),i.addEventListener("change",e=>{this.updateColorValue(t,e.target.value,o)});const r=e=>{e.target!==i&&!i.contains(e.target)&&(i.remove(),document.removeEventListener("click",r,!0))};document.body.appendChild(i),setTimeout(()=>{document.addEventListener("click",r,!0)},100),i.focus(),i.click()}updateColorValue(e,t,n){const o=this.shadowRoot.getElementById("textarea"),s=o.value.split("\n"),i=new RegExp(`"${n}"\\s*:\\s*"#[0-9a-fA-F]{6}"`);s[e]=s[e].replace(i,`"${n}": "${t}"`),o.value=s.join("\n"),this.updateHighlight(),this.emitChange()}handleKeydownInCollapsedArea(e){if(["ArrowUp","ArrowDown","ArrowLeft","ArrowRight","Home","End","PageUp","PageDown","Tab"].includes(e.key)||e.ctrlKey||e.metaKey)return;const t=this.shadowRoot.getElementById("textarea"),n=t.selectionStart,o=t.value.substring(0,n).split("\n").length-1,s=t.value.split("\n")[o];s&&(s.includes("{...}")||s.includes("[...]"))&&e.preventDefault()}handleCopyWithCollapsedContent(e){const t=this.shadowRoot.getElementById("textarea"),n=t.selectionStart,o=t.selectionEnd;if(n===o)return;const s=t.value.substring(n,o);if(!s.includes("{...}")&&!s.includes("[...]"))return;const i=this.expandCollapsedMarkersInText(s,n);e.preventDefault(),e.clipboardData.setData("text/plain",i)}expandCollapsedMarkersInText(e,t){const n=this.shadowRoot.getElementById("textarea").value.substring(0,t).split("\n").length-1,o=e.split("\n"),s=[];return o.forEach((e,t)=>{const o=n+t;if(e.includes("{...}")||e.includes("[...]")){let t=!1;this.collapsedData.forEach((e,n)=>{parseInt(n.split("-")[0])===o&&(s.push(e.originalLine),s.push(...e.content),t=!0)}),t||s.push(e)}else s.push(e)}),s.join("\n")}handleCutWithCollapsedContent(e){this.handleCopyWithCollapsedContent(e);const t=this.shadowRoot.getElementById("textarea"),n=t.selectionStart,o=t.selectionEnd;if(n!==o){const e=t.value;t.value=e.substring(0,n)+e.substring(o),t.selectionStart=t.selectionEnd=n,this.updateHighlight(),this.emitChange()}}emitChange(){const e=this.shadowRoot.getElementById("textarea"),t=this.expandAllCollapsed(e.value),n=this.prefix+t+this.suffix;try{const e=JSON.parse(n),o=this.validateGeoJSON(e);o.length>0?this.dispatchEvent(new CustomEvent("error",{detail:{timestamp:(new Date).toISOString(),error:`GeoJSON validation: ${o.join("; ")}`,errors:o,content:t},bubbles:!0,composed:!0})):this.dispatchEvent(new CustomEvent("change",{detail:e,bubbles:!0,composed:!0}))}catch(e){this.dispatchEvent(new CustomEvent("error",{detail:{timestamp:(new Date).toISOString(),error:e.message,content:t},bubbles:!0,composed:!0}))}}validateGeoJSON(t,n="",o="root"){const s=[];if(!t||"object"!=typeof t)return s;if("properties"!==o&&void 0!==t.type){const i=t.type;"string"==typeof i&&("geometry"===o?e.GEOJSON_TYPES_GEOMETRY.includes(i)||s.push(`Invalid geometry type "${i}" at ${n||"root"} (expected: ${e.GEOJSON_TYPES_GEOMETRY.join(", ")})`):e.GEOJSON_TYPES_FEATURE.includes(i)||s.push(`Invalid type "${i}" at ${n||"root"} (expected: ${e.GEOJSON_TYPES_FEATURE.join(", ")})`))}if(Array.isArray(t))t.forEach((e,t)=>{s.push(...this.validateGeoJSON(e,`${n}[${t}]`,o))});else for(const[e,i]of Object.entries(t))if("object"==typeof i&&null!==i){const t=n?`${n}.${e}`:e;let a=o;"properties"===e?a="properties":"geometry"===e||"geometries"===e?a="geometry":"features"===e&&(a="root"),s.push(...this.validateGeoJSON(i,t,a))}return s}bracketClosesOnSameLine(e,t){const n="{"===t?"}":"]",o=e.indexOf(t);if(-1===o)return!1;const s=e.substring(o+1);let i=1;for(const e of s)if(e===t&&i++,e===n&&i--,0===i)return!0;return!1}expandAllCollapsed(t){const n=e.REGEX;for(;t.includes("{...}")||t.includes("[...]");){const e=t.split("\n");let o=!1;for(let t=0;t<e.length;t++){const s=e[t];if(!s.includes("{...}")&&!s.includes("[...]"))continue;const i=s.match(n.collapsedMarker);if(!i)continue;const a=i[2],r=i[1].length,l=`${t}-${a}`;let h=this.collapsedData.has(l)?l:null;if(!h)for(const[e,t]of this.collapsedData.entries())if(t.nodeKey===a&&t.indent===r){h=e;break}if(h){const{originalLine:n,content:s}=this.collapsedData.get(h);e[t]=n,e.splice(t+1,0,...s),o=!0;break}}if(!o)break;t=e.join("\n")}return t}formatJSONContent(e){const t=this.prefix,n=this.suffix,o=t.trimEnd().endsWith("["),s=n.trimStart().startsWith("]");if(o&&s){const t="["+e+"]",n=JSON.parse(t),o=JSON.stringify(n,null,2).split("\n");return o.length>2?o.slice(1,-1).join("\n"):""}if(t||n){const o=t+e+n;return JSON.parse(o),e}{const t=JSON.parse(e);return JSON.stringify(t,null,2)}}autoFormatContentWithCursor(){const e=this.shadowRoot.getElementById("textarea"),t=e.selectionStart,n=e.value.substring(0,t).split("\n"),o=n.length-1,s=n[n.length-1].length,i=Array.from(this.collapsedData.values()).map(e=>({nodeKey:e.nodeKey,indent:e.indent})),a=this.expandAllCollapsed(e.value);try{const t=this.formatJSONContent(a);if(t!==a){this.collapsedData.clear(),e.value=t,i.length>0&&this.reapplyCollapsed(i);const n=e.value.split("\n");if(o<n.length){const t=Math.min(s,n[o].length);let i=0;for(let e=0;e<o;e++)i+=n[e].length+1;i+=t,e.setSelectionRange(i,i)}}}catch{}}autoFormatContent(){const e=this.shadowRoot.getElementById("textarea"),t=Array.from(this.collapsedData.values()).map(e=>({nodeKey:e.nodeKey,indent:e.indent})),n=this.expandAllCollapsed(e.value);try{const o=this.formatJSONContent(n);o!==n&&(this.collapsedData.clear(),e.value=o,t.length>0&&this.reapplyCollapsed(t))}catch{}}reapplyCollapsed(e){const t=this.shadowRoot.getElementById("textarea"),n=t.value.split("\n"),o=new Map;e.forEach(({nodeKey:e,indent:t})=>{const n=`${e}-${t}`;o.set(n,(o.get(n)||0)+1)});const s=new Map;for(let e=n.length-1;e>=0;e--){const t=n[e],i=t.match(/^(\s*)"(\w+)"\s*:\s*([{\[])/);if(i){const a=i[2],r=`${a}-${i[1].length}`;if(o.has(r)&&(s.set(r,(s.get(r)||0)+1),s.get(r)<=o.get(r))){const o=i[1],s=i[3],r="{"===s?"}":"]";if(this.bracketClosesOnSameLine(t,s))continue;let l=1,h=e;const d=[];for(let t=e+1;t<n.length;t++){const e=n[t];for(const t of e)t===s&&l++,t===r&&l--;if(d.push(e),0===l){h=t;break}}const c=`${e}-${a}`;this.collapsedData.set(c,{originalLine:t,content:d,indent:o.length,nodeKey:a});const p=t.substring(0,t.indexOf(s)),u=n[h]&&n[h].trim().endsWith(",");n[e]=`${p}${s}...${r}${u?",":""}`,n.splice(e+1,h-e)}}}t.value=n.join("\n")}parseSelectorToHostRule(e){return e&&""!==e?e.startsWith(".")&&!e.includes(" ")?`:host(${e})`:`:host-context(${e})`:':host([data-color-scheme="dark"])'}updateThemeCSS(){const e=this.getAttribute("dark-selector")||".dark",t=this.parseSelectorToHostRule(e);let n=this.shadowRoot.getElementById("theme-styles");n||(n=document.createElement("style"),n.id="theme-styles",this.shadowRoot.insertBefore(n,this.shadowRoot.firstChild));const o=`\n :host {\n --bg-color: ${this.themes.light.background};\n --text-color: ${this.themes.light.textColor};\n --caret-color: ${this.themes.light.caretColor};\n --gutter-bg: ${this.themes.light.gutterBackground};\n --gutter-border: ${this.themes.light.gutterBorder};\n --json-key: ${this.themes.light.jsonKey};\n --json-string: ${this.themes.light.jsonString};\n --json-number: ${this.themes.light.jsonNumber};\n --json-boolean: ${this.themes.light.jsonBoolean};\n --json-null: ${this.themes.light.jsonNull};\n --json-punct: ${this.themes.light.jsonPunctuation};\n --collapse-btn: ${this.themes.light.collapseButton};\n --collapse-btn-bg: ${this.themes.light.collapseButtonBg};\n --collapse-btn-border: ${this.themes.light.collapseButtonBorder};\n --geojson-key: ${this.themes.light.geojsonKey};\n --geojson-type: ${this.themes.light.geojsonType};\n --geojson-type-invalid: ${this.themes.light.geojsonTypeInvalid};\n --json-key-invalid: ${this.themes.light.jsonKeyInvalid};\n }\n\n ${t} {\n --bg-color: ${this.themes.dark.background};\n --text-color: ${this.themes.dark.textColor};\n --caret-color: ${this.themes.dark.caretColor};\n --gutter-bg: ${this.themes.dark.gutterBackground};\n --gutter-border: ${this.themes.dark.gutterBorder};\n --json-key: ${this.themes.dark.jsonKey};\n --json-string: ${this.themes.dark.jsonString};\n --json-number: ${this.themes.dark.jsonNumber};\n --json-boolean: ${this.themes.dark.jsonBoolean};\n --json-null: ${this.themes.dark.jsonNull};\n --json-punct: ${this.themes.dark.jsonPunctuation};\n --collapse-btn: ${this.themes.dark.collapseButton};\n --collapse-btn-bg: ${this.themes.dark.collapseButtonBg};\n --collapse-btn-border: ${this.themes.dark.collapseButtonBorder};\n --geojson-key: ${this.themes.dark.geojsonKey};\n --geojson-type: ${this.themes.dark.geojsonType};\n --geojson-type-invalid: ${this.themes.dark.geojsonTypeInvalid};\n --json-key-invalid: ${this.themes.dark.jsonKeyInvalid};\n }\n `;n.textContent=o}getTheme(){return{dark:{...this.themes.dark},light:{...this.themes.light}}}setTheme(e){e.dark&&(this.themes.dark={...this.themes.dark,...e.dark}),e.light&&(this.themes.light={...this.themes.light,...e.light}),this.updateThemeCSS()}resetTheme(){this.themes={dark:{...e.DEFAULT_THEMES.dark},light:{...e.DEFAULT_THEMES.light}},this.updateThemeCSS()}};n(o,"DEFAULT_THEMES",{dark:{background:"#1e1e1e",textColor:"#d4d4d4",caretColor:"#fff",gutterBackground:"#252526",gutterBorder:"#3e3e42",jsonKey:"#9cdcfe",jsonString:"#ce9178",jsonNumber:"#b5cea8",jsonBoolean:"#569cd6",jsonNull:"#569cd6",jsonPunctuation:"#d4d4d4",collapseButton:"#c586c0",collapseButtonBg:"#3e3e42",collapseButtonBorder:"#555",geojsonKey:"#c586c0",geojsonType:"#4ec9b0",geojsonTypeInvalid:"#f44747",jsonKeyInvalid:"#f44747"},light:{background:"#ffffff",textColor:"#333333",caretColor:"#000",gutterBackground:"#f5f5f5",gutterBorder:"#ddd",jsonKey:"#0000ff",jsonString:"#a31515",jsonNumber:"#098658",jsonBoolean:"#0000ff",jsonNull:"#0000ff",jsonPunctuation:"#333333",collapseButton:"#a31515",collapseButtonBg:"#e0e0e0",collapseButtonBorder:"#999",geojsonKey:"#af00db",geojsonType:"#267f99",geojsonTypeInvalid:"#d32f2f",jsonKeyInvalid:"#d32f2f"}}),n(o,"FEATURE_COLLECTION_PREFIX",'{"type": "FeatureCollection", "features": ['),n(o,"FEATURE_COLLECTION_SUFFIX","]}"),n(o,"REGEX",{ampersand:/&/g,lessThan:/</g,greaterThan:/>/g,jsonKey:/"([^"]+)"\s*:/g,typeValue:/<span class="geojson-key">"type"<\/span>:\s*"([^"]*)"/g,stringValue:/:\s*"([^"]*)"/g,numberAfterColon:/:\s*(-?\d+\.?\d*)/g,boolean:/:\s*(true|false)/g,nullValue:/:\s*(null)/g,allNumbers:/\b(-?\d+\.?\d*)\b/g,punctuation:/([{}[\],])/g,colorInLine:/"(\w+)"\s*:\s*"(#[0-9a-fA-F]{6})"/g,collapsibleNode:/^(\s*)"(\w+)"\s*:\s*([{\[])/,collapsedMarker:/^(\s*)"(\w+)"\s*:\s*([{\[])\.\.\.([\]\}])/}),n(o,"GEOJSON_TYPES_FEATURE",["Feature","FeatureCollection"]),n(o,"GEOJSON_TYPES_GEOMETRY",["Point","MultiPoint","LineString","MultiLineString","Polygon","MultiPolygon","GeometryCollection"]),n(o,"GEOJSON_TYPES_ALL",[...o.GEOJSON_TYPES_FEATURE,...o.GEOJSON_TYPES_GEOMETRY]),n(o,"VALID_KEYS_BY_CONTEXT",{Feature:["type","geometry","properties","id","bbox"],FeatureCollection:["type","features","bbox","properties"],Point:["type","coordinates","bbox"],MultiPoint:["type","coordinates","bbox"],LineString:["type","coordinates","bbox"],MultiLineString:["type","coordinates","bbox"],Polygon:["type","coordinates","bbox"],MultiPolygon:["type","coordinates","bbox"],GeometryCollection:["type","geometries","bbox"],properties:null,geometry:["type","coordinates","geometries","bbox"]}),n(o,"CONTEXT_CHANGING_KEYS",{geometry:"geometry",properties:"properties",features:"Feature",geometries:"geometry"}),n(o,"GEOJSON_STRUCTURAL_KEYS",["type","geometry","properties","features","geometries","coordinates","bbox","id","crs"]);let s=o;customElements.define("geojson-editor",s);
package/package.json ADDED
@@ -0,0 +1,44 @@
1
+ {
2
+ "name": "@softwarity/geojson-editor",
3
+ "version": "1.0.1",
4
+ "description": "A feature-rich GeoJSON editor Web Component with syntax highlighting, collapsible nodes, and color picker",
5
+ "type": "module",
6
+ "main": "./dist/geojson-editor.js",
7
+ "module": "./dist/geojson-editor.js",
8
+ "exports": {
9
+ ".": {
10
+ "import": "./dist/geojson-editor.js"
11
+ }
12
+ },
13
+ "files": [
14
+ "dist",
15
+ "src"
16
+ ],
17
+ "scripts": {
18
+ "dev": "vite",
19
+ "build": "vite build && terser dist/geojson-editor.js -o dist/geojson-editor.js --compress passes=3 --mangle toplevel=true --comments /license/i",
20
+ "preview": "vite preview"
21
+ },
22
+ "repository": {
23
+ "type": "git",
24
+ "url": "git+https://github.com/softwarity/geojson-editor.git"
25
+ },
26
+ "bugs": {
27
+ "url": "https://github.com/softwarity/geojson-editor/issues"
28
+ },
29
+ "homepage": "https://softwarity.github.io/geojson-editor/",
30
+ "keywords": [
31
+ "geojson",
32
+ "editor",
33
+ "web-component",
34
+ "custom-element",
35
+ "syntax-highlighting",
36
+ "json-editor"
37
+ ],
38
+ "author": "Softwarity",
39
+ "license": "MIT",
40
+ "devDependencies": {
41
+ "terser": "^5.44.1",
42
+ "vite": "^5.0.0"
43
+ }
44
+ }