Compare commits

...

330 Commits

Author SHA1 Message Date
josdejong 52b6e94e39 Update dependencies 2020-06-03 15:50:57 +02:00
jos 38007b715d Update dependencies 2020-02-26 14:42:37 +01:00
jos 92297c0d2c Remove unused variable 2018-10-17 13:19:10 +02:00
jos ae1e39ba3f Implemented caret selection 2018-10-17 13:17:19 +02:00
jos 76e4bf5e16 Fix objects/arrays not copied correctly to clipboard 2018-10-03 18:05:21 +02:00
jos a869fa0f3a Cleanup old selection stuff 2018-10-03 15:18:12 +02:00
jos 17df475c11 Move some logic to `TreeModeMenu` and some small tweaks and fixes 2018-10-03 15:06:55 +02:00
jos 21d8db13d0 Update search results on patch/undo/redo 2018-10-03 12:32:48 +02:00
jos 7012de59d6 Select patched contents 2018-10-03 12:14:34 +02:00
jos ce37b88296 Cut/copy/paste/insert/duplicate/remove mostly working for selections (WIP) 2018-10-03 11:33:03 +02:00
jos f1ddc03c6d Create `before-childs` and `after-childs` 2018-09-26 21:40:33 +02:00
jos f33342b54b Rename selection areas to before, on, after 2018-09-26 17:54:04 +02:00
jos 0f24a9204f Implemented `findSelectionPointerFromEvent` (WIP) 2018-09-26 17:40:44 +02:00
jos 9a2c4f1e6a Use caret down instead of chevron down for dropdowns 2018-09-26 11:56:39 +02:00
jos aaa498b4ec Implemented `findSelectionPointerFromEvent` 2018-09-26 11:32:40 +02:00
jos 6c17d6256c Add custom svg logos to source code 2018-09-26 08:41:30 +02:00
jos a0de92b919 Move custom icons in a separate file 2018-09-19 21:39:06 +02:00
jos 3d2f9ce8df Created custom icons for format/compact 2018-09-19 21:33:46 +02:00
jos 4f86135050 Fixed broken unit tests 2018-09-19 15:16:45 +02:00
jos 3e795b3275 Order search results 2018-09-19 14:50:53 +02:00
jos e298d63311 Implemented result count in search box 2018-09-19 14:28:12 +02:00
jos 263f38f45c Search focus and quick keys working 2018-09-19 14:12:56 +02:00
jos b4a0dd6a3d Created insert dropdown 2018-09-19 13:27:36 +02:00
jos 2999fbc380 Refactor into a generic `DropDown` component 2018-09-19 13:05:15 +02:00
jos 2c560264c0 Refactor ModeDropDown 2018-09-19 11:43:40 +02:00
jos 0349f94ead Remove floating menu 2018-09-19 11:01:12 +02:00
jos 4c539cb4f5 Implement most menu functionality 2018-09-19 10:39:46 +02:00
jos 702dc5ed20 Use chevron icons in search and mode button 2018-09-19 10:04:51 +02:00
jos 37e22908c7 Fix react error on switching modes. Add more keys 2018-09-19 09:44:12 +02:00
jos 3865a1d365 Fix expand button losing focus when clicking it. Add missing `key` here and there 2018-09-19 09:23:40 +02:00
jos fdd44a276c Floating search box 2018-09-12 21:47:04 +02:00
jos 081cde4489 Replace svg sprite with fontawesome icons. Extend menu (WIP) 2018-09-12 17:54:20 +02:00
jos 0fb816d074 Moved tree mode menu in a separate component 2018-09-12 11:16:04 +02:00
jos 3028617ff7 Ignore css files 2018-09-12 10:18:29 +02:00
jos b6aea6df9d Remove css files from repo (only keep scss) 2018-09-12 10:17:14 +02:00
jos 088bee74cb Fix bundle not working due to undefined initial JSON 2018-09-05 17:59:15 +02:00
jos 80f45f39ec Refactored `findSelectionIndices` 2018-09-05 12:14:52 +02:00
jos 9b2b8233af Fix add in array reusing existing element as oldValue 2018-09-05 12:10:49 +02:00
jos e2ffd04e6e Fixed unit tests 2018-09-05 11:33:18 +02:00
jos 41a7069398 Fixing unit tests 2018-09-05 11:06:15 +02:00
jos a62b1e1c94 Fixed path not being updated in nodes 2018-09-05 09:30:00 +02:00
jos ec987385c8 Fix a bug in syncing an eson value. Cleanup old ESON types 2018-09-05 09:12:44 +02:00
jos 410353c86f Extended immutableJSONPatch with options fromJSON, toJSON, clone 2018-08-29 22:23:30 +02:00
jos dc814a3aa5 Rename terminology of JSONPatch 2018-08-29 12:27:33 +02:00
jos 56124cf17f Flatten `META` symbol, remove ordering of object keys from ESON model, simplify patch actions (WIP) 2018-08-19 20:36:57 +02:00
jos d6ad4c87d0 Revert react_demo (WIP, still broken) 2018-08-15 15:19:46 +02:00
jos fc1f73c4a6 Upgraded dependencies 2018-08-15 15:06:00 +02:00
jos b1f51b04a0 Update package-lock.json 2018-08-15 14:53:38 +02:00
jos cc3e441361 Update react example 2018-08-15 14:52:49 +02:00
jos 65df3428ac Position floating menu below for insert actions 2018-03-07 12:17:52 +01:00
jos 1b2bcd6dd9 Updated font-family 2018-01-18 20:32:01 +01:00
jos 950463f3a9 Oops fixed broken css of Mode Menu 2018-01-10 22:30:33 +01:00
jos 0ba51276f4 Fixed broken unit tests 2018-01-10 22:18:39 +01:00
jos 1127c15ea2 All floating menus working now 2018-01-10 22:11:23 +01:00
jos 7cdfb44345 Created insert buttons 2018-01-10 12:47:49 +01:00
jos 14e6315159 Highlight selected content only instead of whole width of editor 2018-01-03 12:06:54 +01:00
jos 2466b6d4da Minor css tweaks 2018-01-03 10:43:57 +01:00
jos 9eb944d926 Rendering more like JSON 2018-01-01 19:32:30 +01:00
jos d53e5360a3 In between menu large on the right side 2017-12-30 17:33:07 +01:00
jos 5d4a551526 Fixed ActionMenu not visible in arrays 2017-12-30 15:55:53 +01:00
jos d73b79cb4e Implemented top/bottom positioning of ActionMenu 2017-12-30 15:47:07 +01:00
jos ccf89162b7 Implement apply eson state with jsonpatch replace 2017-12-29 21:40:23 +01:00
jos 419bdc6307 Keep expanded state with copy/paste 2017-12-29 21:36:55 +01:00
jos 9a37fe0451 Moved webpack files under ./config 2017-12-29 20:41:37 +01:00
jos 8b2541e3d8 Improved expand function, fixed expand/collapse of TreeMode 2017-12-27 18:18:23 +01:00
jos e5c6459f73 Fixed search matching array indexes 2017-12-27 17:54:21 +01:00
jos 8bb9cb10cc Some refactoring 2017-12-27 17:33:40 +01:00
jos 46661177db Some refactoring 2017-12-27 17:16:40 +01:00
jos 1d4a5af82e Use event emitter. Cleanup old action menu 2017-12-27 16:58:52 +01:00
jos 9e0249f550 Fixed TreeMode rendering everything again when any options changed 2017-12-27 15:34:30 +01:00
jos 6385e4b193 Added index files 2017-12-26 21:21:39 +01:00
jos 29fe32da40 Fixes in .gitignore 2017-12-26 21:16:26 +01:00
jos 99790c0fac Updated readme 2017-12-26 21:14:33 +01:00
jos abfcca84d6 Update react demo (it works now :) ) 2017-12-26 21:09:23 +01:00
jos 8ffb7702ba update .babelrc, .gitignore, and others 2017-12-26 20:46:38 +01:00
jos 29fed7099f Changed to new build system, removed flow 2017-12-26 20:40:31 +01:00
jos b011b196e1 Some fixes and refactoring in sorting objects 2017-12-20 09:00:27 +01:00
jos 346517b947 Fixed some unit tests 2017-12-20 08:44:10 +01:00
jos 0e22497467 Implemented sort action that keeps state 2017-12-20 08:36:55 +01:00
jos 34ca56aee0 Cleanup 2017-12-17 21:52:21 +01:00
jos aee48b75db Removed a TODO 2017-12-17 21:38:23 +01:00
jos e69d962ada Some refactoring in applying META data 2017-12-17 21:33:45 +01:00
jos 9db7fdf0e5 Fixed keeping id's intact when replacing a value 2017-12-17 21:10:07 +01:00
jos dd22b97eb0 Renamed field 'jsoneditor' to 'meta' in extended JSON Patch 2017-12-17 13:53:06 +01:00
jos 1f5f85a4e0 Refactor patchEson to pass Path instead of String internally 2017-12-17 13:44:13 +01:00
jos e979e0f016 Fix many small issues in the UI 2017-12-16 14:13:52 +01:00
jos 7064578b31 Renamed keys to props in ESON model 2017-12-15 21:02:17 +01:00
jos 1a6661fbb5 Many patch action working again in UI 2017-12-15 20:34:07 +01:00
jos 156f330e4e Refactored ESON to use Symbols, refactored patchEson 2017-12-15 19:57:21 +01:00
jos 53b20e2f59 Refactored model to use Symbol 2017-12-15 14:02:42 +01:00
jos a059eb844e Implemented spliceEsonArray 2017-12-15 12:59:45 +01:00
jos f491a00575 Selection working again 2017-12-14 16:40:55 +01:00
jos 378c8ef250 Fixed expand/collapse all 2017-12-14 14:37:25 +01:00
jos 338d19b5a9 JSON Schema errors working again 2017-12-14 14:30:02 +01:00
jos 040fe12d75 Fix clearing search result 2017-12-13 17:03:26 +01:00
jos 5fb69ffcc5 Search working in the editor again 2017-12-13 16:53:08 +01:00
jos e30674a971 Refactor search, previousSearchResult, nextSearchResult 2017-12-13 16:48:22 +01:00
jos 49cc7eb288 Implemented transform and expand for new ESON model 2017-12-13 11:47:12 +01:00
jos c19334894c New ESON model (WIP) 2017-11-29 21:52:18 +01:00
jos a9174edf16 Fixed performance issue 2017-11-29 11:04:19 +01:00
jos 745b77725b Fixed removing multiple items from an array 2017-11-22 13:38:18 +01:00
jos 0a7df1dad9 Remove old comment 2017-11-22 13:06:06 +01:00
jos b3c41bce03 Remove `simplifyPatch`, it doesn't always work correctly 2017-11-22 13:05:53 +01:00
jos b71afda45a Fixed enter key in search field 2017-11-22 12:02:27 +01:00
jos 157f63d11a Fixed insert menu missing before arrays and objects 2017-11-22 11:36:47 +01:00
jos e69868da85 Some refactoring: created `getInEson`, `setInEson`, `updateInEson`, `deleteInEson` 2017-11-22 10:45:00 +01:00
jos 56528dc054 Fixed unit tests 2017-11-22 10:12:59 +01:00
jos bac12dcc5a Replace/insert working. Insert before instead of after 2017-11-22 10:09:53 +01:00
jos 2e75e5a9cf Initialize selection as null 2017-11-08 16:12:00 +01:00
jos 8cded5496c Fixed not removing selection before duplication 2017-11-08 16:10:39 +01:00
jos cb354f2331 Implemented duplicate for multiple nodes 2017-11-08 15:41:40 +01:00
jos dd989f9a64 Cut/Copy/Paste selection starting to work (WIP) 2017-11-08 13:13:01 +01:00
jos 8425579718 Floating menu starting to work (WIP) 2017-11-03 14:23:04 +01:00
jos a2f7f61389 Show floating menu on selection 2017-10-14 21:57:59 +02:00
jos cea4e2c101 Floating menu (WIP) 2017-10-13 13:27:33 +02:00
jos 0910aa5a63 Move favicon to root 2017-10-06 10:55:31 +02:00
jos 26fa339c35 Implemented cut/copy/paste (via quickkeys) 2017-09-29 21:52:17 +02:00
jos 375ea56316 Some refactoring in `applySelection` 2017-09-29 11:08:44 +02:00
jos 52ec02e3e1 Implemented selecting nodes 2017-09-29 10:06:14 +02:00
jos eee0b55636 Refactored unit tests (moved json objects into separate files) 2017-09-22 12:24:13 +02:00
jos 943d721d84 Implemented support for selection in eson 2017-09-22 11:38:23 +02:00
jos f3313158db Fixed issue with escaping spaces 2017-09-18 21:05:26 +02:00
jos 6f9776a07a Fixed broken JSONSchema errors 2017-09-17 21:03:33 +02:00
jos 432e169f32 Renamed 'jsonData' to 'ESON' 2017-09-08 13:36:15 +02:00
jos 2ee11399ec Fixed optional types of flow 2017-09-08 11:30:33 +02:00
jos 86f8aa56d0 Moved all JSON Patch methods into a separate file 2017-09-08 11:14:41 +02:00
jos 12f1543ef9 Add package-lock.json 2017-09-08 10:29:39 +02:00
jos 598fe63d80 Don't expand search result itself when focusing it 2017-09-08 10:29:09 +02:00
jos 99cc07577d Move react to devDependencies 2017-09-03 17:07:17 +02:00
jos 8462bcda2c Implemented custom key bindings 2017-07-24 15:50:18 +02:00
jos 239b702040 Implemented key bindings in the mode menu 2017-07-24 14:35:27 +02:00
jos 91c1fd823f Updated package-lock.json 2017-07-24 14:09:46 +02:00
jos 7380be631c Updated docs on key bindings 2017-07-24 14:09:37 +02:00
jos 1c37b303b9 Created key bindings for `text` mode 2017-07-24 14:09:10 +02:00
jos e069d3f0b3 Implemented key bindings `Alt+Home` and `Alt+End` 2017-07-24 13:39:34 +02:00
jos 633daf6c95 Implemented key bindings in Menu 2017-07-19 20:50:05 +02:00
jos 3ca23568cf Fixed `Ctrl+E` not working anymore 2017-07-19 14:36:04 +02:00
jos a03d962daa Some refactoring 2017-07-19 14:31:26 +02:00
jos c5ce6525a5 Implemented key bindings for undo/redo 2017-07-19 13:56:14 +02:00
jos 52977b009c Updated package-lock.json 2017-07-19 13:52:50 +02:00
jos b162f5ed13 Implemented key bindings for search 2017-07-19 13:52:28 +02:00
jos 5b1426eb7c Remove not yet used ref 2017-07-13 19:49:45 +02:00
jos f786940d8d Some refactoring 2017-07-13 12:20:48 +02:00
jos 94b7be90d3 Refactored action menus, added quick keys Ctrl+M, Ctrl+E 2017-07-13 12:10:42 +02:00
jos bb8734707e Set focus to correct input fields after insert/append/duplicate/remove 2017-07-12 17:09:03 +02:00
jos bb6565f3b3 Implemented quickkeys to move up/down/left/right 2017-07-12 15:01:19 +02:00
jos 111b85a4cb Fixed invalid dependency version number 2017-07-12 09:47:42 +02:00
jos fb71b61ba5 Implemented basic support for key bindings 2017-06-11 17:14:46 +02:00
jos 82ff880c27 Added missing dependencies 2017-06-11 15:06:25 +02:00
jos 82ddb965fb Updated dependencies 2017-06-11 13:44:22 +02:00
jos 7eb9464474 Upgraded dependencies 2017-06-09 09:42:03 +02:00
Jos de Jong 1c09829622 Fixed version number for gulp-multi-process 2017-01-18 09:37:54 +01:00
jos 1cd6d71dda Upgrade to `gulp-multi-process v0.4.0` which fixes a windows issue (see #352) 2017-01-17 20:05:43 +01:00
jos b730cb29bf Fixed broken ModeMenu mouse click 2017-01-10 20:14:26 +01:00
jos 07f92467e7 Try get react bundle working (js is ok, css still broken) 2017-01-10 20:03:09 +01:00
jos ba1eb2a837 A bit of refactoring 2017-01-08 20:51:08 +01:00
jos 6f1fbac9ac Fixed ModeMenu broken after using it when bundled with `preact-compat` 2017-01-08 15:34:01 +01:00
jos 675432c52d Fixed broken modes `form` and `view` 2017-01-08 15:23:17 +01:00
jos ce484c670a Use react for development for the great error messages, preact for bundling for production 2017-01-07 19:48:17 +01:00
jos 984c8bac3d Upgraded dependencies 2017-01-06 23:18:05 +01:00
jos ea01e581cd Switched to `preact-compat` 2017-01-06 23:17:09 +01:00
jos 840e5f41a7 Fixed broken undo 2017-01-06 23:00:36 +01:00
jos 85ae6d3b5e Replaced `getPath()` and `props.parent` with `props.path` 2017-01-06 20:33:01 +01:00
jos 1cce254e9c Fixed losing focus when editing property 2017-01-06 20:17:31 +01:00
jos ab8d4b4be6 Changed data model to items and props having an id 2017-01-06 13:57:16 +01:00
jos 0f7c601635 Fixed broken unit test 2017-01-05 15:15:53 +01:00
jos b256e1c8ce Better scrollTo offset 2017-01-05 15:04:39 +01:00
jos a9a453f51f Fixed expanding root node too when expanding path 2017-01-05 15:00:05 +01:00
jos d5500bef89 Implemented scrolling to active search result 2017-01-05 14:47:20 +01:00
jos 65e868b1c3 Upgraded dependencies 2017-01-05 11:39:01 +01:00
jos afcf19bac5 A bit more refactoring 2017-01-05 11:18:38 +01:00
jos 9f81dfb2f6 Refactoring ul/li 2017-01-05 10:48:32 +01:00
jos 0e490fdeee expand the active search result if not expanded 2017-01-01 21:13:14 +01:00
jos bef37648b7 Go to next search result on enter 2017-01-01 20:20:17 +01:00
jos d2e1bed9d6 Created gulp task `compile-es5-lib` (WIP, not yet compiling less into css) 2017-01-01 20:08:10 +01:00
jos 349e6015a3 Some preparations for moveTo active search result (WIP) 2016-12-31 12:32:24 +01:00
jos fec1bb8f23 Implemented highlighting prev/next search result 2016-12-30 14:41:37 +01:00
jos 6ba53f7364 Implemented functions `nextSearchResult` and `previousSearchResult` 2016-12-30 14:05:11 +01:00
jos 100efb35ae Restructured search data model 2016-12-30 13:45:43 +01:00
jos 198e8edf85 Fixed ordering of search results 2016-12-30 10:52:48 +01:00
jos aa4b963592 Changed the structure of SearchResult. Highlight active search result 2016-12-30 10:44:57 +01:00
jos 95f0a31731 Added search icon, added prev/next button (not yet doing anything) 2016-12-26 14:37:52 +01:00
jos d1f35c6214 Show search result count 2016-12-26 11:21:52 +01:00
jos c7418807d8 Updated examples 2016-12-26 10:41:07 +01:00
jos f209df27a9 Updated readme 2016-12-26 10:26:33 +01:00
jos 2e434f49f9 Worked out react bundle (not yet working though) 2016-12-26 10:18:26 +01:00
jos 785ab5205c React API mostly working (WIP) 2016-12-22 12:08:32 +01:00
jos 98f56efc47 Finished switch to React 2016-12-04 22:14:21 +01:00
jos 4208dff7b9 Merge branch 'next_react' into next
# Conflicts:
#	package.json
#	src/components/CodeMode.js
#	src/components/JSONNode.js
#	src/components/TextMode.js
#	src/components/TreeMode.js
#	src/index.js
2016-12-02 11:46:46 +01:00
jos 8824cd10f8 Start with being able to set focus (WIP) 2016-12-02 11:38:54 +01:00
jos 37f2f77124 Refactored `search` to return an array with results, implemented `addSearchResults` 2016-11-27 21:16:17 +01:00
jos 939ad792d6 First basic implementation of search (WIP) 2016-11-27 15:41:10 +01:00
jos e5e61b71e3 Implemented function search 2016-11-19 21:21:09 +01:00
jos 96186b836a Replace `expandRecursive` with `transform` 2016-11-19 20:49:01 +01:00
jos e6cb225514 Implemented function `transform` 2016-11-19 20:44:58 +01:00
jos 70810655b8 Implemented JSON schema support for tree/form/view mode 2016-11-12 15:22:55 +01:00
jos fe3bc56d53 Implemented addErrors and removeErrors 2016-11-12 14:10:10 +01:00
jos 8da5ece3b8 Some styling improvements for errors 2016-11-12 13:23:53 +01:00
jos e45aa82b4b Cleanup reference of aceEditor 2016-11-12 11:36:48 +01:00
jos 6120949acf Some refactoring 2016-11-12 10:58:06 +01:00
jos 2bc4cf7dfe Fixed mode code not reckoning with option escapeUnicode 2016-11-12 10:38:04 +01:00
jos f01f52094e Add utf-8 meta tag in examples 2016-11-11 20:26:32 +01:00
jos 68a5b1476f Implemented JSON Schema support for mode `text` and `code` 2016-11-11 20:18:31 +01:00
jos 18d63fcd9a Updated dependencies 2016-11-11 09:34:17 +01:00
jos 116d5fe620 Added css for drag button 2016-11-11 09:32:02 +01:00
jos a70ba17bc7 Little refactoring 2016-10-30 17:38:21 +01:00
jos f882756dda Keep expanded state when patching data 2016-10-30 17:31:16 +01:00
jos a06f7ac5d1 No react warnings in bundles 2016-10-28 23:06:49 +02:00
jos a7c77ff9d1 Try react 2016-10-28 22:21:44 +02:00
jos 3f3fef9b40 Updated explanation in example 08 2016-10-28 21:30:01 +02:00
jos a5145ca004 Fixed minimalist bundle not excluding ace 2016-10-28 21:28:15 +02:00
jos b179318dc6 Upgraded dependencies 2016-10-28 21:21:59 +02:00
jos b73ff81e19 Implemented option `history` 2016-10-28 21:14:22 +02:00
jos 9cbb7574c0 Implemented option `escapeUnicode` 2016-10-28 14:00:44 +02:00
jos 8a3fffcd24 Fixed broken imports 2016-10-28 13:44:41 +02:00
jos 076853bf04 Moved all components in a folder components 2016-10-28 13:33:52 +02:00
jos f2f1636ef0 Added some headers in examples 2016-10-28 13:31:13 +02:00
jos 8f6ddf91bc Implemented `isValueEditable` and `isPropertyEditable` 2016-10-28 13:24:51 +02:00
jos 4a12eed14f Updated dark theme example 2016-10-28 12:14:47 +02:00
jos 9722414728 Remove semicolons from examples 2016-10-28 11:46:46 +02:00
jos 099c5ee8ba Fixed insert not working 2016-10-28 11:04:06 +02:00
jos 9d05195aec Moved large json from develop.html in a separate file 2016-10-28 09:39:47 +02:00
jos 25ecb2dfab Added example synchronizing two editors 2016-10-28 09:36:05 +02:00
jos a399aef0ea Updated dependencies 2016-10-22 10:28:50 +02:00
jos f6dfe874db Fixed action menu of root node not working 2016-10-17 20:38:35 +02:00
jos dc8bb9349c Ignore `dist` folder 2016-10-14 17:47:27 +02:00
jos 30aa73d3a3 Dropped support for bower (don't commit bundles anymore) 2016-10-14 17:46:00 +02:00
jos 3b1a5c2d24 Hide undo/redo menu items when in mode 'view'. Updated some examples 2016-10-14 17:30:16 +02:00
jos 6cf85d2ce9 Implemented mode form 2016-10-14 17:10:09 +02:00
jos 482720b28c Implemented mode `view` 2016-10-14 16:05:52 +02:00
jos bb62795b6b Add graceful-fs (else yarn doesn't work) 2016-10-14 15:06:52 +02:00
jos bde1c9c4d1 Fixed broken build script 2016-10-14 14:59:46 +02:00
jos 1cad4fc8ea Implemented `onChangeText` 2016-10-14 13:46:15 +02:00
jos 5ff41f2d9d Implemented mode `code` (using ace editor) 2016-10-14 12:28:33 +02:00
jos a607d2c2f4 Improved watch script 2016-10-14 09:54:37 +02:00
jos ceea59b30d Aways package jsonlint, it's essential. Also some small fixes. 2016-09-30 17:05:23 +02:00
jos ae90077ed6 Removed semi colons 2016-09-30 16:51:43 +02:00
jos b5ce5cb2ae Removed a redundant variable in the gulp file 2016-09-30 16:43:09 +02:00
jos adb3c6bdd9 Integrated jsonlint 2016-09-30 16:42:04 +02:00
jos 5fc5e302dc Refactored ActionMenu and AppendActionMenu 2016-09-30 13:40:14 +02:00
jos 1bcc6382aa Handle error when switching mode 2016-09-29 14:26:20 +02:00
jos 873d5f8ae2 Implemented switch mode menu 2016-09-29 14:19:01 +02:00
jos 88aed193c6 Refactored menus 2016-09-29 10:33:32 +02:00
jos 339b73fe51 Added jsonlint asset (not yet used) 2016-09-24 19:15:35 +02:00
jos d755ca9d03 Implemented patch for TextMode 2016-09-23 20:09:49 +02:00
jos 0c3faa03ea Implemented option onError 2016-09-23 19:53:57 +02:00
jos 804c68010f Implemented setMode, setText, getText 2016-09-23 19:46:04 +02:00
jos b44a465207 Implemented mode `text` 2016-09-23 14:46:59 +02:00
jos 84031dc3ef Define keys 2016-09-23 14:02:08 +02:00
jos 10e9f16b75 Removed a todo 2016-09-23 12:07:44 +02:00
jos 6db127739c Some refactoring 2016-09-23 12:01:39 +02:00
jos 75c1497de1 Minor refactoring 2016-09-23 11:53:25 +02:00
jos 54788aa593 Created immutability function `insertAt` 2016-09-23 11:41:00 +02:00
jos 2a1fabc6ac Fixed the need for try/catch around oldValue 2016-09-23 11:17:53 +02:00
jos 291cfbfa22 Improved errors. Fixed move before 2016-09-23 11:15:56 +02:00
jos 515e7a6bf2 Fixed undo move losing position 2016-09-23 09:33:27 +02:00
jos 7f979b97dd Changed the signature of `jsonToData` to pass the json value first and make the other args optional 2016-09-18 21:54:39 +02:00
jos 0a857aaad6 Simplify patch 2016-09-18 15:40:40 +02:00
jos ad36723618 Fixed `move` 2016-09-18 15:30:57 +02:00
jos c8b3bd2d7a Extra info in JSONPatch mostly working 2016-09-17 16:46:58 +02:00
jos 7b6a3747d2 Refactored 'object' and 'array' to 'Object' and 'Array' respectively 2016-09-17 15:40:17 +02:00
jos fb758d3135 Turn on uglify again 2016-09-17 15:25:26 +02:00
jos 83ae2bab07 Fixed duplicate variables 2016-09-17 15:23:31 +02:00
jos d235425f36 Some refactoring 2016-09-17 15:12:25 +02:00
jos ff7f683b11 Fixed some issues with history 2016-09-17 14:18:54 +02:00
jos de6d7c9551 Reordered functions 2016-09-16 20:39:42 +02:00
jos 611db9c431 Removed `name` from global options 2016-09-16 20:37:07 +02:00
jos 3d467e65e1 History starts to work with JSONPatch 2016-09-16 15:09:31 +02:00
jos 678db6bced Use JSONPatch actions internally 2016-09-16 14:41:51 +02:00
jos 44183e01bd More refactoring 2016-09-09 15:19:57 +02:00
jos 7adc760b33 Refactored API of `remove` and `replace` 2016-09-09 14:43:58 +02:00
jos ec63a9a759 Make `add` standalone 2016-09-09 14:30:07 +02:00
jos bb8850fb91 immutabilityHelpers don't accept non-existing paths 2016-09-09 11:43:36 +02:00
jos fe0f98dfe0 Implemented jsonpatch operations 2016-09-09 11:01:06 +02:00
jos 94671507b5 Prevent duplicate property names 2016-08-26 13:25:34 +02:00
jos 9350debba2 Fixed property names not being escaped when rendering 2016-08-26 12:25:51 +02:00
jos 7280239771 Fixed losing caret position whilst typing 2016-08-26 12:16:43 +02:00
jos ea6b00b67d Fixed build script not outputting errors 2016-08-26 09:19:15 +02:00
jos 71c38f07af Created helper function to find a unique name (not yet used) 2016-08-26 08:57:27 +02:00
jos 123b0c7cb4 Implemented simple undo/redo support 2016-08-21 14:15:30 +02:00
jos 56fc3164ec Implemented menu with expand all / collapse all buttons 2016-08-21 13:43:28 +02:00
jos 737963b908 Remove console outputs 2016-08-21 13:14:18 +02:00
jos b6d622e0d0 Fixed array/object not being rendered empty when removing the last item 2016-08-21 13:12:47 +02:00
jos c5a68b1da3 Implemented expand/collapse methods and ctrl+expand/ctrl+collapse 2016-08-21 12:45:25 +02:00
jos d8a0079032 Show placeholders when a `prop` or `value` is empty 2016-08-20 11:34:37 +02:00
jos 069d35ace4 Moved the logic for manipulating JSONData object into a separate file 2016-08-20 11:14:28 +02:00
jos c8a5614511 Renamed `watch` script to `start` 2016-08-20 10:06:18 +02:00
jos 41821ef825 The factory function now creates a neat wrapper object instead of returning a TreeMode object 2016-08-19 21:14:09 +02:00
jos 422315dba2 Some refactoring 2016-08-14 14:00:14 +02:00
jos ebca411384 Moved functions from objectUtils.js to immutabilityHelpers.js 2016-08-13 21:45:09 +02:00
jos 24ab2899dc Some early refactoring of docs and examples 2016-08-13 21:38:32 +02:00
jos a12505b017 Updated license information on some other places too 2016-08-13 21:23:43 +02:00
jos 25dfa2c0e1 Put new build script into place, update unit testing, changed license to MIT, merged css and image into the bundled js file 2016-08-13 21:15:52 +02:00
jos d014146956 Added some todo's 2016-08-12 13:47:35 +02:00
jos 1099e3859f Set up a structure for JSONEditor base class (instead of Main) 2016-08-07 13:50:46 +02:00
jos 608f45c8e7 Changed singnature of `handleInsert`, `handleDuplicate`, and `handleRemove` 2016-08-06 14:18:15 +02:00
jos a065fbb09e Create a function `getPath` instead of passing path via props 2016-08-06 14:08:57 +02:00
jos ab37f5ba9a Handle state of context menus in the JSONNodes. Append context menu working now 2016-08-05 20:23:23 +02:00
jos 9d61ce001a Halfway implementing AppendContextMenu. Upgraded to preact v5.6.0 2016-08-05 14:17:24 +02:00
jos 7f6e7459df Fixed preact errors regarding `contentEditable` 2016-08-05 11:21:16 +02:00
jos 4baa98c961 Some refactoring. Pass options `name` and `expand` via `.set` 2016-07-31 22:02:51 +02:00
jos 274a99ddef Removed helper functions in Main 2016-07-31 21:42:24 +02:00
jos 525a41a7d5 Implemented data conversion when switching type 2016-07-31 15:25:49 +02:00
jos 49f0ed3e4f Implemented support for type 'string' 2016-07-31 14:41:28 +02:00
jos 38250a38ba Refactored the internal data structure 2016-07-31 14:27:20 +02:00
jos 224e436828 Sort objects by property instead of value 2016-07-30 20:37:00 +02:00
jos 5416676735 Changed internal path to Array again 2016-07-30 20:30:08 +02:00
jos 667d3f32aa Implemented actions for ContextMenu (not all fully working yet) 2016-07-30 14:40:58 +02:00
jos c3c836fa89 Fixes in life cycle of ContextMenu. Adjust top/bottom orientation 2016-07-24 22:14:45 +02:00
jos a9f0fe07c1 Completed displaying of context menu 2016-07-24 15:31:16 +02:00
jos a1859a9aff Fixed showing multiple context menus 2016-07-16 14:31:01 +02:00
jos cd08e16789 Refactored path to be a JSON pointer 2016-07-16 14:24:28 +02:00
jos 37ae2111ee Created context menu button. Some refactoring 2016-07-16 13:48:20 +02:00
jos fda4f94655 Implemented a data model, fixed ordering of object properties, implemented options `name` and `expand` 2016-07-15 22:43:59 +02:00
jos fd631cd34c Keep state of value in JSONNode whilst typing (for live updating of colors) 2016-07-14 15:35:37 +02:00
jos d0abc3124a Fixed array index not being rendered when an array item is an object or array 2016-07-14 15:23:16 +02:00
jos b796de2128 Implemented expand/collapse 2016-07-14 15:15:50 +02:00
jos 5adf72dcc1 Some refactoring in the build scripts 2016-07-13 15:09:56 +02:00
jos 32853ea0d8 Some refactoring 2016-07-13 14:50:31 +02:00
jos 7fb5ca6517 Some refactoring 2016-07-13 14:32:43 +02:00
jos c0f075a9e6 Largely fixed jumping of fields when renaming. Some refactoring. 2016-07-13 14:26:11 +02:00
jos 8723855bdf Added `react-dev-server` dependency 2016-07-12 21:45:38 +02:00
jos 22785614af Created temporary scripts 2016-07-12 21:43:32 +02:00
jos c46f27334c Css fixes and improvements 2016-07-12 21:35:43 +02:00
jos 1c3a323387 Refactored `escape` utils 2016-07-12 21:20:11 +02:00
jos f011e3f107 coloring fields, urls, and a refactoring 2016-07-12 21:07:50 +02:00
jos 89657a539f escape/unescape fields and values 2016-07-12 16:19:57 +02:00
jos 4e0aa5659c Apply changes in fields 2016-07-12 15:43:42 +02:00
jos 0d250cbdcd Basic editor using preact 2016-07-12 13:56:40 +02:00
jos b4cf47b06f Moved old source code to `src_old` 2016-07-12 13:56:04 +02:00
153 changed files with 55441 additions and 58840 deletions

20
.babelrc Normal file
View File

@ -0,0 +1,20 @@
{
"plugins": [
"transform-class-properties",
"transform-object-rest-spread",
"transform-react-jsx",
["transform-runtime", {
"polyfill": false,
"regenerator": true
}]
],
"presets": [
["env", {
"targets": {
"browsers": ["last 2 versions", "ie >= 9", "safari >= 7"]
},
"modules": "commonjs",
"loose": true
}]
]
}

35
.gitignore vendored
View File

@ -1,6 +1,31 @@
# See https://help.github.com/ignore-files/ for more about ignoring files.
# Ignore css files generated from scss
/src/**/*.css
# dependencies
/node_modules
# testing
/coverage
# production
/lib
/dist
# misc
.DS_Store
.env.local
.env.development.local
.env.test.local
.env.production.local
# webstorm
.idea
build
downloads
node_modules
*.zip
npm-debug.log
# visual studio code
.vscode
npm-debug.log*
yarn-debug.log*
yarn-error.log*

View File

@ -9,7 +9,7 @@ There are a few preferences regarding code contributions:
- `jsoneditor` follows the node.js code style as described
[here](http://nodeguide.com/style.html).
- Send pull requests to the `develop` branch, not the `master` branch.
- Only commit changes done in the source files under `./src`, not to the builds
which are located under the `./dist` folder.
- Only commit changes done in the source files under `./src`, do not
commit builds located in the `./lib` and `./dist` folders.
Thanks!

177
LICENSE
View File

@ -1,176 +1,7 @@
Apache License
Version 2.0, January 2004
http://www.apache.org/licenses/
Copyright 2015-2017 Jos de Jong
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
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:
1. Definitions.
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
"License" shall mean the terms and conditions for use, reproduction,
and distribution as defined by Sections 1 through 9 of this document.
"Licensor" shall mean the copyright owner or entity authorized by
the copyright owner that is granting the License.
"Legal Entity" shall mean the union of the acting entity and all
other entities that control, are controlled by, or are under common
control with that entity. For the purposes of this definition,
"control" means (i) the power, direct or indirect, to cause the
direction or management of such entity, whether by contract or
otherwise, or (ii) ownership of fifty percent (50%) or more of the
outstanding shares, or (iii) beneficial ownership of such entity.
"You" (or "Your") shall mean an individual or Legal Entity
exercising permissions granted by this License.
"Source" form shall mean the preferred form for making modifications,
including but not limited to software source code, documentation
source, and configuration files.
"Object" form shall mean any form resulting from mechanical
transformation or translation of a Source form, including but
not limited to compiled object code, generated documentation,
and conversions to other media types.
"Work" shall mean the work of authorship, whether in Source or
Object form, made available under the License, as indicated by a
copyright notice that is included in or attached to the work
(an example is provided in the Appendix below).
"Derivative Works" shall mean any work, whether in Source or Object
form, that is based on (or derived from) the Work and for which the
editorial revisions, annotations, elaborations, or other modifications
represent, as a whole, an original work of authorship. For the purposes
of this License, Derivative Works shall not include works that remain
separable from, or merely link (or bind by name) to the interfaces of,
the Work and Derivative Works thereof.
"Contribution" shall mean any work of authorship, including
the original version of the Work and any modifications or additions
to that Work or Derivative Works thereof, that is intentionally
submitted to Licensor for inclusion in the Work by the copyright owner
or by an individual or Legal Entity authorized to submit on behalf of
the copyright owner. For the purposes of this definition, "submitted"
means any form of electronic, verbal, or written communication sent
to the Licensor or its representatives, including but not limited to
communication on electronic mailing lists, source code control systems,
and issue tracking systems that are managed by, or on behalf of, the
Licensor for the purpose of discussing and improving the Work, but
excluding communication that is conspicuously marked or otherwise
designated in writing by the copyright owner as "Not a Contribution."
"Contributor" shall mean Licensor and any individual or Legal Entity
on behalf of whom a Contribution has been received by Licensor and
subsequently incorporated within the Work.
2. Grant of Copyright License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
copyright license to reproduce, prepare Derivative Works of,
publicly display, publicly perform, sublicense, and distribute the
Work and such Derivative Works in Source or Object form.
3. Grant of Patent License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
(except as stated in this section) patent license to make, have made,
use, offer to sell, sell, import, and otherwise transfer the Work,
where such license applies only to those patent claims licensable
by such Contributor that are necessarily infringed by their
Contribution(s) alone or by combination of their Contribution(s)
with the Work to which such Contribution(s) was submitted. If You
institute patent litigation against any entity (including a
cross-claim or counterclaim in a lawsuit) alleging that the Work
or a Contribution incorporated within the Work constitutes direct
or contributory patent infringement, then any patent licenses
granted to You under this License for that Work shall terminate
as of the date such litigation is filed.
4. Redistribution. You may reproduce and distribute copies of the
Work or Derivative Works thereof in any medium, with or without
modifications, and in Source or Object form, provided that You
meet the following conditions:
(a) You must give any other recipients of the Work or
Derivative Works a copy of this License; and
(b) You must cause any modified files to carry prominent notices
stating that You changed the files; and
(c) You must retain, in the Source form of any Derivative Works
that You distribute, all copyright, patent, trademark, and
attribution notices from the Source form of the Work,
excluding those notices that do not pertain to any part of
the Derivative Works; and
(d) If the Work includes a "NOTICE" text file as part of its
distribution, then any Derivative Works that You distribute must
include a readable copy of the attribution notices contained
within such NOTICE file, excluding those notices that do not
pertain to any part of the Derivative Works, in at least one
of the following places: within a NOTICE text file distributed
as part of the Derivative Works; within the Source form or
documentation, if provided along with the Derivative Works; or,
within a display generated by the Derivative Works, if and
wherever such third-party notices normally appear. The contents
of the NOTICE file are for informational purposes only and
do not modify the License. You may add Your own attribution
notices within Derivative Works that You distribute, alongside
or as an addendum to the NOTICE text from the Work, provided
that such additional attribution notices cannot be construed
as modifying the License.
You may add Your own copyright statement to Your modifications and
may provide additional or different license terms and conditions
for use, reproduction, or distribution of Your modifications, or
for any such Derivative Works as a whole, provided Your use,
reproduction, and distribution of the Work otherwise complies with
the conditions stated in this License.
5. Submission of Contributions. Unless You explicitly state otherwise,
any Contribution intentionally submitted for inclusion in the Work
by You to the Licensor shall be under the terms and conditions of
this License, without any additional terms or conditions.
Notwithstanding the above, nothing herein shall supersede or modify
the terms of any separate license agreement you may have executed
with Licensor regarding such Contributions.
6. Trademarks. This License does not grant permission to use the trade
names, trademarks, service marks, or product names of the Licensor,
except as required for reasonable and customary use in describing the
origin of the Work and reproducing the content of the NOTICE file.
7. Disclaimer of Warranty. Unless required by applicable law or
agreed to in writing, Licensor provides the Work (and each
Contributor provides its Contributions) on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
implied, including, without limitation, any warranties or conditions
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
PARTICULAR PURPOSE. You are solely responsible for determining the
appropriateness of using or redistributing the Work and assume any
risks associated with Your exercise of permissions under this License.
8. Limitation of Liability. In no event and under no legal theory,
whether in tort (including negligence), contract, or otherwise,
unless required by applicable law (such as deliberate and grossly
negligent acts) or agreed to in writing, shall any Contributor be
liable to You for damages, including any direct, indirect, special,
incidental, or consequential damages of any character arising as a
result of this License or out of the use or inability to use the
Work (including but not limited to damages for loss of goodwill,
work stoppage, computer failure or malfunction, or any and all
other commercial damages or losses), even if such Contributor
has been advised of the possibility of such damages.
9. Accepting Warranty or Additional Liability. While redistributing
the Work or Derivative Works thereof, You may choose to offer,
and charge a fee for, acceptance of support, warranty, indemnity,
or other liability obligations and/or rights consistent with this
License. However, in accepting such obligations, You may act only
on Your own behalf and on Your sole responsibility, not on behalf
of any other Contributor, and only if You agree to indemnify,
defend, and hold each Contributor harmless for any liability
incurred by, or claims asserted against, such Contributor by reason
of your accepting any such warranty or additional liability.
END OF TERMS AND CONDITIONS
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.

17
NOTICE
View File

@ -1,17 +0,0 @@
JSON Editor
https://github.com/josdejong/jsoneditor
Copyright (C) 2011-2015 Jos de Jong
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.

View File

@ -47,18 +47,39 @@ Supported browsers: Chrome, Firefox, Safari, Opera, Internet Explorer 9+.
## Install
with npm (recommended):
Install via npm:
npm install jsoneditor
with bower:
### Versions
bower install jsoneditor
There are two versions of jsoneditor available: a full version and
a minimalist version.
#### Full version
If you're not sure which version to use, use the full version: jsoneditor.js.
#### Minimalist version
The minimalist version, jsoneditor-minimalist.js, has excluded the following libraries:
- `ace` (via `brace`), used for the code editor.
- `ajv`, used for JSON schema validation.
This reduces the the size of the minified and gzipped JavaScript considerably.
When to use the minimalist version?
- If you don't need the mode "code" and don't need JSON schema validation.
- Or if you want to provide `ace` and/or `ajv` yourself via the configuration
options, for example when you already use Ace in other parts of your
web application too and don't want to bundle the library twice.
#### More
### More
There is a directive available for using JSONEditor in Angular.js:
There is a directive available for using `jsoneditor` in Angular.js:
[https://github.com/angular-tools/ng-jsoneditor](https://github.com/angular-tools/ng-jsoneditor)
@ -72,17 +93,16 @@ There is a directive available for using JSONEditor in Angular.js:
<!-- when using the mode "code", it's important to specify charset utf-8 -->
<meta http-equiv="Content-Type" content="text/html;charset=utf-8">
<link href="jsoneditor/dist/jsoneditor.min.css" rel="stylesheet" type="text/css">
<script src="jsoneditor/dist/jsoneditor.min.js"></script>
<script src="jsoneditor/dist/jsoneditor.js"></script>
</head>
<body>
<div id="jsoneditor" style="width: 400px; height: 400px;"></div>
<script>
// create the editor
var container = document.getElementById("jsoneditor");
var container = document.getElementById('jsoneditor');
var options = {};
var editor = new JSONEditor(container, options);
var editor = jsoneditor(container, options);
// set json
var json = {
@ -120,36 +140,23 @@ jsoneditor:
npm run build
```
This will generate the files `./jsoneditor.js`, `./jsoneditor.css`, and
minified versions in the dist of the project.
This will generate the file `./dist/jsoneditor.js` and
`./dist/jsoneditor-minimalist.js` and corresponding source maps.
- To automatically build when a source file has changed:
- For development, start a develop server which automatically reloads
when a source file has changed:
```
npm run watch
npm start
```
This will update `./jsoneditor.js` and `./jsoneditor.css` in the dist folder
on every change, but it will **NOT** update the minified versions as that's
an expensive operation.
- Run unit tests (Jest):
```
npm test
```
## Custom builds
The source code of JSONEditor consists of CommonJS modules. JSONEditor can be bundled in a customized way using a module bundler like [browserify](http://browserify.org/) or [webpack](http://webpack.github.io/). First, install all dependencies of jsoneditor:
npm install
To create a custom bundle of the source code using browserify:
browserify ./index.js -o ./jsoneditor.custom.js -s JSONEditor
The Ace editor, used in mode `code`, accounts for about 75% of the total
size of the library. To exclude the Ace editor from the bundle:
browserify ./index.js -o ./jsoneditor.custom.js -s JSONEditor -x brace -x brace/mode/json -x brace/ext/searchbox
To minify the generated bundle, use [uglifyjs](https://github.com/mishoo/UglifyJS2):
uglifyjs ./jsoneditor.custom.js -o ./jsoneditor.custom.min.js -m -c
## License
MIT

View File

@ -1,32 +0,0 @@
{
"name": "jsoneditor",
"description": "A web-based tool to view, edit and format JSON",
"tags": [
"json",
"editor",
"viewer",
"formatter"
],
"homepage": "http://jsoneditoronline.org/",
"repository": {
"type": "git",
"url": "https://github.com/josdejong/jsoneditor.git"
},
"main": [
"./dist/jsoneditor.min.js",
"./dist/jsoneditor.min.css"
],
"bugs": "https://github.com/josdejong/jsoneditor/issues",
"ignore": [
"misc",
"node_modules",
"test",
"tools",
"gulpfile.js",
"npm-debug.log",
".idea",
".npmignore",
".gitignore"
],
"dependencies": {}
}

53
config/webpack.config.js Normal file
View File

@ -0,0 +1,53 @@
const webpack = require('webpack')
const minifyPlugin = new webpack.optimize.UglifyJsPlugin({ sourceMap: true })
const productionEnvPlugin = new webpack.DefinePlugin({
'process.env': {
NODE_ENV: JSON.stringify('production')
}
})
module.exports = {
entry: './src/jsoneditor/index.vanilla.js',
devtool: 'source-map',
cache: true,
bail: true,
output: {
library: 'jsoneditor',
libraryTarget: 'umd',
filename: 'dist/jsoneditor.js'
},
plugins: [
// bannerPlugin,
productionEnvPlugin,
minifyPlugin
],
module: {
rules: [
{
test: /\.js$/,
exclude: /node_modules/,
use: { loader: 'babel-loader'}
},
{
test: /\.css$/,
use: [ 'style-loader', 'css-loader' ]
},
{
test: /\.svg/,
use: {
loader: 'svg-url-loader',
options: {}
}
}
]
},
// using preact saves in the order of 25 kB
resolve: {
'alias': {
'react': 'preact-compat',
'react-dom': 'preact-compat'
}
}
}

View File

@ -0,0 +1,19 @@
const webpack = require('webpack')
const config = require('./webpack.config')
const emptyFile = __dirname + '/../src/jsoneditor/utils/empty.js'
const excludeAcePlugin = new webpack.NormalModuleReplacementPlugin(new RegExp('assets\/ace'), emptyFile)
const excludeAjvPlugin = new webpack.NormalModuleReplacementPlugin(new RegExp('^ajv$'), emptyFile)
const configMinimalist = Object.assign({}, config, {
output: Object.assign({}, config.output, {
filename: 'dist/jsoneditor-minimalist.js'
}),
plugins: [
excludeAcePlugin,
excludeAjvPlugin
].concat(config.plugins)
})
module.exports = configMinimalist

View File

@ -1,893 +0,0 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<svg
xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:cc="http://creativecommons.org/ns#"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:svg="http://www.w3.org/2000/svg"
xmlns="http://www.w3.org/2000/svg"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
width="216"
height="144"
id="svg4136"
version="1.1"
inkscape:version="0.91 r"
sodipodi:docname="jsoneditor-icons.svg">
<title
id="title6512">JSON Editor Icons</title>
<metadata
id="metadata4148">
<rdf:RDF>
<cc:Work
rdf:about="">
<dc:format>image/svg+xml</dc:format>
<dc:type
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
<dc:title>JSON Editor Icons</dc:title>
</cc:Work>
</rdf:RDF>
</metadata>
<defs
id="defs4146" />
<sodipodi:namedview
pagecolor="#ffffff"
bordercolor="#666666"
borderopacity="1"
objecttolerance="10"
gridtolerance="10"
guidetolerance="10"
inkscape:pageopacity="0"
inkscape:pageshadow="2"
inkscape:window-width="1920"
inkscape:window-height="1028"
id="namedview4144"
showgrid="true"
inkscape:zoom="4"
inkscape:cx="97.217248"
inkscape:cy="59.950227"
inkscape:window-x="0"
inkscape:window-y="0"
inkscape:window-maximized="1"
inkscape:current-layer="svg4136"
showguides="false"
borderlayer="false"
inkscape:showpageshadow="true"
showborder="true">
<inkscape:grid
type="xygrid"
id="grid4640"
empspacing="24" />
</sodipodi:namedview>
<!-- Created with SVG-edit - http://svg-edit.googlecode.com/ -->
<g
id="g4394">
<rect
x="4"
y="4"
width="16"
height="16"
id="svg_1"
style="fill:#1aae1c;fill-opacity:1;stroke:none;stroke-width:0" />
<rect
style="fill:#ec3f29;fill-opacity:0.94117647;stroke:none;stroke-width:0"
x="28.000006"
y="3.999995"
width="16"
height="16"
id="svg_1-7" />
<rect
id="rect4165"
height="16"
width="16"
y="3.999995"
x="52.000004"
style="fill:#4c4c4c;fill-opacity:1;stroke:none;stroke-width:0" />
<rect
style="fill:#4c4c4c;fill-opacity:1;stroke:none;stroke-width:0"
x="172.00002"
y="3.9999852"
width="16"
height="16"
id="rect4175" />
<rect
style="fill:#4c4c4c;fill-opacity:1;stroke:none;stroke-width:0"
x="196"
y="3.999995"
width="16"
height="16"
id="rect4175-3" />
<g
style="stroke:none"
id="g4299">
<rect
style="fill:#ffffff;fill-opacity:1;stroke:none;stroke-width:0"
id="svg_1-1"
height="1.9999986"
width="9.9999924"
y="10.999998"
x="7.0000048" />
<rect
style="fill:#ffffff;fill-opacity:1;stroke:none;stroke-width:0"
id="svg_1-1-1"
height="9.9999838"
width="1.9999955"
y="7.0000114"
x="11.000005" />
</g>
<g
style="stroke:none"
transform="matrix(0.70710678,-0.70710678,0.70710678,0.70710678,19.029435,12.000001)"
id="g4299-3">
<rect
style="fill:#ffffff;fill-opacity:1;stroke:none;stroke-width:0"
id="svg_1-1-0"
height="1.9999986"
width="9.9999924"
y="10.999998"
x="7.0000048" />
<rect
style="fill:#ffffff;fill-opacity:1;stroke:none;stroke-width:0"
id="svg_1-1-1-9"
height="9.9999838"
width="1.9999955"
y="7.0000114"
x="11.000005" />
</g>
<rect
style="fill:#ffffff;fill-opacity:1;stroke:#000000;stroke-width:0"
x="55.000004"
y="7.0000048"
width="6.9999909"
height="6.9999905"
id="svg_1-7-5" />
<rect
id="rect4354"
height="6.9999905"
width="6.9999909"
y="10.00001"
x="58"
style="fill:#ffffff;fill-opacity:1;stroke:#4c4c4c;stroke-width:2;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" />
<rect
style="fill:#ffffff;fill-opacity:1;stroke:#3c80df;stroke-width:0;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:0.94117647"
x="58.000004"
y="10.000005"
width="6.9999909"
height="6.9999905"
id="svg_1-7-5-7" />
<g
id="g4378">
<rect
id="svg_1-7-5-3"
height="1.9999965"
width="7.9999909"
y="10.999999"
x="198"
style="fill:#ffffff;fill-opacity:1;stroke:#000000;stroke-width:0" />
<rect
style="fill:#ffffff;fill-opacity:1;stroke:#000000;stroke-width:0"
x="198"
y="7.0000005"
width="11.999995"
height="1.9999946"
id="rect4374" />
<rect
style="fill:#ffffff;fill-opacity:1;stroke:#000000;stroke-width:0"
x="198"
y="14.999996"
width="3.9999928"
height="1.9999995"
id="rect4376" />
</g>
<g
id="g4383"
transform="matrix(1,0,0,-1,-23.999995,23.999995)">
<rect
style="fill:#ffffff;fill-opacity:1;stroke:#000000;stroke-width:0"
x="198"
y="10.999999"
width="7.9999909"
height="1.9999965"
id="rect4385" />
<rect
id="rect4387"
height="1.9999946"
width="11.999995"
y="7.0000005"
x="198"
style="fill:#ffffff;fill-opacity:1;stroke:#000000;stroke-width:0" />
<rect
id="rect4389"
height="1.9999995"
width="3.9999928"
y="14.999996"
x="198"
style="fill:#ffffff;fill-opacity:1;stroke:#000000;stroke-width:0" />
</g>
<rect
y="3.9999199"
x="76"
height="16"
width="16"
id="rect3754-4"
style="fill:#4c4c4c;fill-opacity:1;stroke:none" />
<path
sodipodi:nodetypes="cccccccc"
inkscape:connector-curvature="0"
id="path4351"
d="m 85.10447,6.0157384 -0.0156,1.4063 c 3.02669,-0.2402 0.33008,3.6507996 2.48438,4.5780996 -2.18694,1.0938 0.49191,4.9069 -2.45313,4.5781 l -0.0156,1.4219 c 5.70828,0.559 1.03264,-5.1005 4.70313,-5.2656 l 0,-1.4063 c -3.61303,-0.027 1.11893,-5.7069996 -4.70313,-5.3124996 z"
style="fill:#ffffff;fill-opacity:1;stroke:#ffffff;stroke-width:0.2;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" />
<path
sodipodi:nodetypes="cccccccc"
inkscape:connector-curvature="0"
id="path4351-9"
d="m 82.78125,5.9984384 0.0156,1.4063 c -3.02668,-0.2402 -0.33007,3.6506996 -2.48437,4.5780996 2.18694,1.0938 -0.49192,4.9069 2.45312,4.5781 l 0.0156,1.4219 c -5.70827,0.559 -1.03263,-5.1004 -4.70312,-5.2656 l 0,-1.4063 c 3.61303,-0.027 -1.11894,-5.7070996 4.70312,-5.3124996 z"
style="fill:#ffffff;fill-opacity:1;stroke:#ffffff;stroke-width:0.2;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" />
<rect
y="3.9999199"
x="100"
height="16"
width="16"
id="rect3754-25"
style="fill:#4c4c4c;fill-opacity:1;stroke:none" />
<path
inkscape:connector-curvature="0"
id="path2987"
d="m 103.719,5.6719384 0,12.7187996 3.03125,0 0,-1.5313 -1.34375,0 0,-9.6249996 1.375,0 0,-1.5625 z"
style="fill:#ffffff;fill-opacity:1;stroke:none" />
<path
inkscape:connector-curvature="0"
id="path2987-1"
d="m 112.2185,5.6721984 0,12.7187996 -3.03125,0 0,-1.5313 1.34375,0 0,-9.6249996 -1.375,0 0,-1.5625 z"
style="fill:#ffffff;fill-opacity:1;stroke:none" />
<rect
y="3.9999199"
x="124"
height="16"
width="16"
id="rect3754-73"
style="fill:#4c4c4c;fill-opacity:1;stroke:none" />
<path
sodipodi:nodetypes="ccccccccc"
inkscape:connector-curvature="0"
id="path3780"
d="m 126.2824,17.602938 1.78957,0 1.14143,-2.8641 5.65364,0 1.14856,2.8641 1.76565,0 -4.78687,-11.1610996 -1.91903,0 z"
style="fill:#ffffff;fill-opacity:1;stroke:none" />
<path
inkscape:connector-curvature="0"
id="path3782"
d="m 129.72704,13.478838 4.60852,0.01 -2.30426,-5.5497996 z"
style="fill:#4c4c4c;fill-opacity:1;stroke:none" />
<rect
y="3.9999199"
x="148"
height="16"
width="16"
id="rect3754-35"
style="fill:#4c4c4c;fill-opacity:1;stroke:none" />
<path
sodipodi:nodetypes="ccccccc"
inkscape:connector-curvature="0"
id="path5008-2"
d="m 156.47655,5.8917384 0,2.1797 0.46093,2.3983996 1.82813,0 0.39844,-2.3983996 0,-2.1797 z"
style="fill:#ffffff;fill-opacity:1;stroke:none" />
<path
sodipodi:nodetypes="ccccccc"
inkscape:connector-curvature="0"
id="path5008-2-8"
d="m 152.51561,5.8906384 0,2.1797 0.46094,2.3983996 1.82812,0 0.39844,-2.3983996 0,-2.1797 z"
style="fill:#ffffff;fill-opacity:1;stroke:none" />
</g>
<rect
x="4"
y="27.999994"
width="16"
height="16"
id="rect4432"
style="fill:#d3d3d3;fill-opacity:1;stroke:none;stroke-width:0" />
<rect
style="fill:#d3d3d3;fill-opacity:1;stroke:none;stroke-width:0"
x="28.000006"
y="27.99999"
width="16"
height="16"
id="rect4434" />
<rect
id="rect4436"
height="16"
width="16"
y="27.99999"
x="52.000004"
style="fill:#d3d3d3;fill-opacity:1;stroke:#000000;stroke-width:0" />
<rect
style="fill:#d3d3d3;stroke:#000000;stroke-width:0"
x="172.00002"
y="27.999981"
width="16"
height="16"
id="rect4446" />
<rect
style="fill:#d3d3d3;stroke:#000000;stroke-width:0"
x="196"
y="27.99999"
width="16"
height="16"
id="rect4448" />
<g
id="g4466"
style="stroke:none"
transform="translate(0,23.999995)">
<rect
style="fill:#ffffff;fill-opacity:1;stroke:none;stroke-width:0"
id="rect4468"
height="1.9999986"
width="9.9999924"
y="10.999998"
x="7.0000048" />
<rect
style="fill:#ffffff;fill-opacity:1;stroke:none;stroke-width:0"
id="rect4470"
height="9.9999838"
width="1.9999955"
y="7.0000114"
x="11.000005" />
</g>
<g
transform="matrix(0.70710678,-0.70710678,0.70710678,0.70710678,19.029435,35.999996)"
id="g4472"
style="stroke:none">
<rect
style="fill:#ffffff;fill-opacity:1;stroke:none;stroke-width:0"
id="rect4474"
height="1.9999986"
width="9.9999924"
y="10.999998"
x="7.0000048" />
<rect
style="fill:#ffffff;fill-opacity:1;stroke:none;stroke-width:0"
id="rect4476"
height="9.9999838"
width="1.9999955"
y="7.0000114"
x="11.000005" />
</g>
<rect
style="fill:#ffffff;fill-opacity:1;stroke:#000000;stroke-width:0"
x="55.000004"
y="31"
width="6.9999909"
height="6.9999905"
id="rect4478" />
<rect
id="rect4480"
height="6.9999905"
width="6.9999909"
y="34.000008"
x="58"
style="fill:#ffffff;fill-opacity:1;stroke:#d3d3d3;stroke-width:2;stroke-miterlimit:4;stroke-dasharray:none" />
<rect
style="fill:#ffffff;fill-opacity:1;stroke:#d3d3d3;stroke-width:0;stroke-miterlimit:4;stroke-dasharray:none"
x="58.000004"
y="34.000004"
width="6.9999909"
height="6.9999905"
id="rect4482" />
<g
id="g4484"
transform="translate(0,23.999995)">
<rect
id="rect4486"
height="1.9999965"
width="7.9999909"
y="10.999999"
x="198"
style="fill:#ffffff;fill-opacity:1;stroke:#000000;stroke-width:0" />
<rect
style="fill:#ffffff;fill-opacity:1;stroke:#000000;stroke-width:0"
x="198"
y="7.0000005"
width="11.999995"
height="1.9999946"
id="rect4488" />
<rect
style="fill:#ffffff;fill-opacity:1;stroke:#000000;stroke-width:0"
x="198"
y="14.999996"
width="3.9999928"
height="1.9999995"
id="rect4490" />
</g>
<g
id="g4492"
transform="matrix(1,0,0,-1,-23.999995,47.99999)">
<rect
style="fill:#ffffff;fill-opacity:1;stroke:#000000;stroke-width:0"
x="198"
y="10.999999"
width="7.9999909"
height="1.9999965"
id="rect4494" />
<rect
id="rect4496"
height="1.9999946"
width="11.999995"
y="7.0000005"
x="198"
style="fill:#ffffff;fill-opacity:1;stroke:#000000;stroke-width:0" />
<rect
id="rect4498"
height="1.9999995"
width="3.9999928"
y="14.999996"
x="198"
style="fill:#ffffff;fill-opacity:1;stroke:#000000;stroke-width:0" />
</g>
<rect
style="fill:#d3d3d3;fill-opacity:1;stroke:none"
id="rect3754-8"
width="16"
height="16"
x="76"
y="27.99992" />
<path
style="fill:#ffffff;fill-opacity:1;stroke:#ffffff;stroke-width:0.2;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
d="m 85.10448,30.015537 -0.0156,1.4063 c 3.02668,-0.2402 0.33007,3.6508 2.48438,4.5781 -2.18695,1.0938 0.49191,4.90688 -2.45313,4.57808 l -0.0156,1.4219 c 5.70827,0.559 1.03263,-5.10048 4.70313,-5.26558 l 0,-1.4063 c -3.61304,-0.027 1.11893,-5.707 -4.70313,-5.3125 z"
id="path4351-1"
inkscape:connector-curvature="0"
sodipodi:nodetypes="cccccccc" />
<path
style="fill:#ffffff;fill-opacity:1;stroke:#ffffff;stroke-width:0.2;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
d="m 82.78126,29.998237 0.0156,1.4063 c -3.02668,-0.2402 -0.33008,3.6507 -2.48438,4.5781 2.18694,1.0938 -0.49191,4.90688 2.45313,4.57808 l 0.0156,1.4219 c -5.70828,0.559 -1.03264,-5.10038 -4.70313,-5.26558 l 0,-1.4063 c 3.61303,-0.027 -1.11893,-5.7071 4.70313,-5.3125 z"
id="path4351-9-5"
inkscape:connector-curvature="0"
sodipodi:nodetypes="cccccccc" />
<rect
style="fill:#d3d3d3;fill-opacity:1;stroke:none"
id="rect3754-65"
width="16"
height="16"
x="100"
y="27.99992" />
<path
style="fill:#ffffff;fill-opacity:1;stroke:none"
d="m 103.719,29.671937 0,12.71878 3.03125,0 0,-1.5313 -1.34375,0 0,-9.62498 1.375,0 0,-1.5625 z"
id="path2987-8"
inkscape:connector-curvature="0" />
<path
style="fill:#ffffff;fill-opacity:1;stroke:none"
d="m 112.2185,29.671937 0,12.71878 -3.03125,0 0,-1.5313 1.34375,0 0,-9.62498 -1.375,0 0,-1.5625 z"
id="path2987-1-9"
inkscape:connector-curvature="0" />
<rect
style="fill:#d3d3d3;fill-opacity:1;stroke:none"
id="rect3754-92"
width="16"
height="16"
x="124"
y="27.99992" />
<path
style="fill:#ffffff;fill-opacity:1;stroke:none"
d="m 126.2824,41.602917 1.78957,0 1.14143,-2.86408 5.65364,0 1.14856,2.86408 1.76565,0 -4.78687,-11.16108 -1.91902,0 z"
id="path3780-9"
inkscape:connector-curvature="0"
sodipodi:nodetypes="ccccccccc" />
<path
style="fill:#d3d3d3;fill-opacity:1;stroke:none"
d="m 129.72704,37.478837 4.60852,0.01 -2.30426,-5.5498 z"
id="path3782-2"
inkscape:connector-curvature="0" />
<rect
style="fill:#d3d3d3;fill-opacity:1;stroke:none"
id="rect3754-47"
width="16"
height="16"
x="148"
y="27.99992" />
<path
style="fill:#ffffff;fill-opacity:1;stroke:none"
d="m 156.47656,29.891737 0,2.1797 0.46093,2.3984 1.82813,0 0.39844,-2.3984 0,-2.1797 z"
id="path5008-2-1"
inkscape:connector-curvature="0"
sodipodi:nodetypes="ccccccc" />
<path
style="fill:#ffffff;fill-opacity:1;stroke:none"
d="m 152.51562,29.890637 0,2.1797 0.46094,2.3984 1.82812,0 0.39844,-2.3984 0,-2.1797 z"
id="path5008-2-8-8"
inkscape:connector-curvature="0"
sodipodi:nodetypes="ccccccc" />
<rect
id="svg_1-7-2"
height="1.9999961"
width="11.999996"
y="64"
x="54"
style="fill:#4c4c4c;fill-opacity:0.98431373;stroke:none;stroke-width:0" />
<rect
id="svg_1-7-2-2"
height="2.9999905"
width="2.9999907"
y="52"
x="80.000008"
style="fill:#4c4c4c;fill-opacity:0.98431373;stroke:none;stroke-width:0" />
<rect
style="fill:#4c4c4c;fill-opacity:0.98431373;stroke:none;stroke-width:0"
x="85.000008"
y="52"
width="2.9999907"
height="2.9999905"
id="rect4561" />
<rect
style="fill:#4c4c4c;fill-opacity:0.98431373;stroke:none;stroke-width:0"
x="80.000008"
y="58"
width="2.9999907"
height="2.9999905"
id="rect4563" />
<rect
id="rect4565"
height="2.9999905"
width="2.9999907"
y="58"
x="85.000008"
style="fill:#4c4c4c;fill-opacity:0.98431373;stroke:none;stroke-width:0" />
<rect
id="rect4567"
height="2.9999905"
width="2.9999907"
y="64"
x="80.000008"
style="fill:#4c4c4c;fill-opacity:0.98431373;stroke:none;stroke-width:0" />
<rect
style="fill:#4c4c4c;fill-opacity:0.98431373;stroke:none;stroke-width:0"
x="85.000008"
y="64"
width="2.9999907"
height="2.9999905"
id="rect4569" />
<circle
style="opacity:1;fill:none;fill-opacity:1;stroke:#4c4c4c;stroke-width:2;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none"
id="path4571"
cx="110.06081"
cy="57.939209"
r="4.7438836" />
<rect
style="fill:#4c4c4c;fill-opacity:0.98431373;stroke:none;stroke-width:0"
x="116.64566"
y="-31.79752"
width="4.229713"
height="6.4053884"
id="rect4563-2"
transform="matrix(0.70710678,0.70710678,-0.70710678,0.70710678,0,0)" />
<path
style="fill:#4c4c4c;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:0;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
d="M 125,56 138.77027,56.095 132,64 Z"
id="path4613"
inkscape:connector-curvature="0"
sodipodi:nodetypes="cccc" />
<path
sodipodi:nodetypes="cccc"
inkscape:connector-curvature="0"
id="path4615"
d="M 149,64 162.77027,63.905 156,56 Z"
style="fill:#4c4c4c;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:0;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" />
<rect
style="fill:#4c4c4c;fill-opacity:0.98431373;stroke:none;stroke-width:0"
x="54"
y="53"
width="11.999996"
height="1.9999961"
id="rect4638" />
<rect
id="svg_1-7-2-24"
height="1.9999957"
width="12.99999"
y="-56"
x="53"
style="fill:#4c4c4c;fill-opacity:0.98431373;stroke:none;stroke-width:0"
transform="matrix(0,1,-1,0,0,0)" />
<rect
transform="matrix(0,1,-1,0,0,0)"
style="fill:#4c4c4c;fill-opacity:0.98431373;stroke:none;stroke-width:0"
x="53"
y="-66"
width="12.99999"
height="1.9999957"
id="rect4657" />
<rect
id="rect4659"
height="0.99999291"
width="11.999999"
y="57"
x="54"
style="fill:#4c4c4c;fill-opacity:0.98431373;stroke:none;stroke-width:0" />
<rect
style="fill:#d3d3d3;fill-opacity:1;stroke:none;stroke-width:0;stroke-opacity:1"
x="54"
y="88.000122"
width="11.999996"
height="1.9999961"
id="rect4661" />
<rect
style="fill:#d3d3d3;fill-opacity:1;stroke:none;stroke-width:0;stroke-opacity:1"
x="80.000008"
y="76.000122"
width="2.9999907"
height="2.9999905"
id="rect4663" />
<rect
id="rect4665"
height="2.9999905"
width="2.9999907"
y="76.000122"
x="85.000008"
style="fill:#d3d3d3;fill-opacity:1;stroke:none;stroke-width:0;stroke-opacity:1" />
<rect
id="rect4667"
height="2.9999905"
width="2.9999907"
y="82.000122"
x="80.000008"
style="fill:#d3d3d3;fill-opacity:1;stroke:none;stroke-width:0;stroke-opacity:1" />
<rect
style="fill:#d3d3d3;fill-opacity:1;stroke:none;stroke-width:0;stroke-opacity:1"
x="85.000008"
y="82.000122"
width="2.9999907"
height="2.9999905"
id="rect4669" />
<rect
style="fill:#d3d3d3;fill-opacity:1;stroke:none;stroke-width:0;stroke-opacity:1"
x="80.000008"
y="88.000122"
width="2.9999907"
height="2.9999905"
id="rect4671" />
<rect
id="rect4673"
height="2.9999905"
width="2.9999907"
y="88.000122"
x="85.000008"
style="fill:#d3d3d3;fill-opacity:1;stroke:none;stroke-width:0;stroke-opacity:1" />
<circle
r="4.7438836"
cy="81.939331"
cx="110.06081"
id="circle4675"
style="opacity:1;fill:none;fill-opacity:1;stroke:#d3d3d3;stroke-width:2;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" />
<rect
transform="matrix(0.70710678,0.70710678,-0.70710678,0.70710678,0,0)"
id="rect4677"
height="6.4053884"
width="4.229713"
y="-14.826816"
x="133.6163"
style="fill:#d3d3d3;fill-opacity:1;stroke:#d3d3d3;stroke-width:0;stroke-opacity:1" />
<path
sodipodi:nodetypes="cccc"
inkscape:connector-curvature="0"
id="path4679"
d="m 125,80.000005 13.77027,0.09499 L 132,87.999992 Z"
style="fill:#d3d3d3;fill-opacity:1;fill-rule:evenodd;stroke:#d3d3d3;stroke-width:0;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" />
<path
style="fill:#d3d3d3;fill-opacity:1;fill-rule:evenodd;stroke:#d3d3d3;stroke-width:0;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
d="M 149,88.0002 162.77027,87.9052 156,80.0002 Z"
id="path4681"
inkscape:connector-curvature="0"
sodipodi:nodetypes="cccc" />
<rect
id="rect4683"
height="1.9999961"
width="11.999996"
y="77.000122"
x="54"
style="fill:#d3d3d3;fill-opacity:1;stroke:none;stroke-width:0;stroke-opacity:1" />
<rect
transform="matrix(0,1,-1,0,0,0)"
style="fill:#d3d3d3;fill-opacity:1;stroke:none;stroke-width:0;stroke-opacity:1"
x="77.000122"
y="-56"
width="12.99999"
height="1.9999957"
id="rect4685" />
<rect
id="rect4687"
height="1.9999957"
width="12.99999"
y="-66"
x="77.000122"
style="fill:#d3d3d3;fill-opacity:1;stroke:none;stroke-width:0;stroke-opacity:1"
transform="matrix(0,1,-1,0,0,0)" />
<rect
style="fill:#d3d3d3;fill-opacity:1;stroke:none;stroke-width:0;stroke-opacity:1"
x="54"
y="81.000122"
width="11.999999"
height="0.99999291"
id="rect4689" />
<rect
id="rect4761-1"
height="1.9999945"
width="15.99999"
y="101"
x="76.000008"
style="fill:#ffffff;fill-opacity:0.80000007;stroke:none;stroke-width:0" />
<rect
id="rect4761-0"
height="1.9999945"
width="15.99999"
y="105"
x="76.000008"
style="fill:#ffffff;fill-opacity:0.80000007;stroke:none;stroke-width:0" />
<rect
id="rect4761-7"
height="1.9999945"
width="9"
y="109"
x="76.000008"
style="fill:#ffffff;fill-opacity:0.80000007;stroke:none;stroke-width:0" />
<rect
id="rect4761-1-1"
height="1.9999945"
width="12"
y="125"
x="76.000008"
style="fill:#ffffff;fill-opacity:0.80000007;stroke:none;stroke-width:0" />
<rect
id="rect4761-1-1-4"
height="1.9999945"
width="10"
y="137"
x="76.000008"
style="fill:#ffffff;fill-opacity:0.80000007;stroke:none;stroke-width:0" />
<rect
id="rect4761-1-1-4-4"
height="1.9999945"
width="10"
y="129"
x="82"
style="fill:#ffffff;fill-opacity:0.80000007;stroke:none;stroke-width:0" />
<rect
id="rect4761-1-1-4-4-3"
height="1.9999945"
width="9"
y="133"
x="82"
style="fill:#ffffff;fill-opacity:0.80000007;stroke:none;stroke-width:0" />
<path
inkscape:connector-curvature="0"
style="color:#000000;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:medium;line-height:normal;font-family:sans-serif;text-indent:0;text-align:start;text-decoration:none;text-decoration-line:none;text-decoration-style:solid;text-decoration-color:#000000;letter-spacing:normal;word-spacing:normal;text-transform:none;direction:ltr;block-progression:tb;writing-mode:lr-tb;baseline-shift:baseline;text-anchor:start;white-space:normal;clip-rule:nonzero;display:inline;overflow:visible;visibility:visible;opacity:0.8;isolation:auto;mix-blend-mode:normal;color-interpolation:sRGB;color-interpolation-filters:linearRGB;solid-color:#000000;solid-opacity:1;fill:#ffffff;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:2.66157866;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto;enable-background:accumulate"
d="m 36.398438,100.0254 c -0.423362,-0.013 -0.846847,0.01 -1.265626,0.062 -1.656562,0.2196 -3.244567,0.9739 -4.507812,2.2266 L 29,100.5991 l -2.324219,7.7129 7.826172,-1.9062 -1.804687,-1.9063 c 1.597702,-1.5308 4.048706,-1.8453 5.984375,-0.7207 1.971162,1.1452 2.881954,3.3975 2.308593,5.5508 -0.573361,2.1533 -2.533865,3.6953 -4.830078,3.6953 l 0,3.0742 c 3.550756,0 6.710442,-2.4113 7.650391,-5.9414 0.939949,-3.5301 -0.618463,-7.2736 -3.710938,-9.0703 -1.159678,-0.6738 -2.431087,-1.0231 -3.701171,-1.0625 z"
id="path4138" />
<path
inkscape:connector-curvature="0"
style="color:#000000;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:medium;line-height:normal;font-family:sans-serif;text-indent:0;text-align:start;text-decoration:none;text-decoration-line:none;text-decoration-style:solid;text-decoration-color:#000000;letter-spacing:normal;word-spacing:normal;text-transform:none;direction:ltr;block-progression:tb;writing-mode:lr-tb;baseline-shift:baseline;text-anchor:start;white-space:normal;clip-rule:nonzero;display:inline;overflow:visible;visibility:visible;opacity:0.8;isolation:auto;mix-blend-mode:normal;color-interpolation:sRGB;color-interpolation-filters:linearRGB;solid-color:#000000;solid-opacity:1;fill:#ffffff;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:2.66157866;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto;enable-background:accumulate"
d="m 59.722656,99.9629 c -1.270084,0.039 -2.541493,0.3887 -3.701172,1.0625 -3.092475,1.7967 -4.650886,5.5402 -3.710937,9.0703 0.939949,3.5301 4.09768,5.9414 7.648437,5.9414 l 0,-3.0742 c -2.296214,0 -4.256717,-1.542 -4.830078,-3.6953 -0.573361,-2.1533 0.337432,-4.4056 2.308594,-5.5508 1.935731,-1.1246 4.38863,-0.8102 5.986326,0.7207 l -1.806638,1.9063 7.828128,1.9062 -2.32422,-7.7129 -1.62696,1.7168 c -1.26338,-1.2531 -2.848917,-2.0088 -4.505855,-2.2285 -0.418778,-0.055 -0.842263,-0.076 -1.265625,-0.062 z"
id="path4138-1" />
<path
inkscape:connector-curvature="0"
style="opacity:0.8;fill:#ffffff;fill-opacity:1;stroke:none;stroke-width:1.966;stroke-miterlimit:4;stroke-dasharray:none"
d="m 10.5,100 0,2 -2.4999996,0 L 12,107 l 4,-5 -2.5,0 0,-2 -3,0 z"
id="path3055-0-77" />
<path
style="opacity:0.8;fill:none;stroke:#ffffff;stroke-width:1.966;stroke-linecap:square;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
d="m 4.9850574,108.015 14.0298856,-0.03"
id="path5244-5-0-5"
inkscape:connector-curvature="0"
sodipodi:nodetypes="cc" />
<path
style="opacity:0.8;fill:none;stroke:#ffffff;stroke-width:1.966;stroke-linecap:square;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
d="m 4.9849874,132.015 14.0298866,-0.03"
id="path5244-5-0-5-8"
inkscape:connector-curvature="0"
sodipodi:nodetypes="cc" />
<path
inkscape:connector-curvature="0"
style="color:#000000;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:medium;line-height:normal;font-family:sans-serif;text-indent:0;text-align:start;text-decoration:none;text-decoration-line:none;text-decoration-style:solid;text-decoration-color:#000000;letter-spacing:normal;word-spacing:normal;text-transform:none;direction:ltr;block-progression:tb;writing-mode:lr-tb;baseline-shift:baseline;text-anchor:start;white-space:normal;clip-rule:nonzero;display:inline;overflow:visible;visibility:visible;opacity:0.4;isolation:auto;mix-blend-mode:normal;color-interpolation:sRGB;color-interpolation-filters:linearRGB;solid-color:#000000;solid-opacity:1;fill:#4d4d4d;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:2.66157866;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto;enable-background:accumulate"
d="m 36.398438,123.9629 c -0.423362,-0.013 -0.846847,0.01 -1.265626,0.062 -1.656562,0.2196 -3.244567,0.9739 -4.507812,2.2266 L 29,124.5366 l -2.324219,7.7129 7.826172,-1.9062 -1.804687,-1.9063 c 1.597702,-1.5308 4.048706,-1.8453 5.984375,-0.7207 1.971162,1.1453 2.881954,3.3975 2.308593,5.5508 -0.573361,2.1533 -2.533864,3.6953 -4.830078,3.6953 l 0,3.0742 c 3.550757,0 6.710442,-2.4093 7.650391,-5.9394 0.939949,-3.5301 -0.618463,-7.2756 -3.710938,-9.0723 -1.159678,-0.6737 -2.431087,-1.0231 -3.701171,-1.0625 z"
id="path4138-12" />
<path
inkscape:connector-curvature="0"
style="color:#000000;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:medium;line-height:normal;font-family:sans-serif;text-indent:0;text-align:start;text-decoration:none;text-decoration-line:none;text-decoration-style:solid;text-decoration-color:#000000;letter-spacing:normal;word-spacing:normal;text-transform:none;direction:ltr;block-progression:tb;writing-mode:lr-tb;baseline-shift:baseline;text-anchor:start;white-space:normal;clip-rule:nonzero;display:inline;overflow:visible;visibility:visible;opacity:0.4;isolation:auto;mix-blend-mode:normal;color-interpolation:sRGB;color-interpolation-filters:linearRGB;solid-color:#000000;solid-opacity:1;fill:#4d4d4d;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:2.66157866;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto;enable-background:accumulate"
d="m 59.722656,123.9629 c -1.270084,0.039 -2.541493,0.3888 -3.701172,1.0625 -3.092475,1.7967 -4.650886,5.5422 -3.710937,9.0723 0.939949,3.5301 4.09768,5.9394 7.648437,5.9394 l 0,-3.0742 c -2.296214,0 -4.256717,-1.542 -4.830078,-3.6953 -0.573361,-2.1533 0.337432,-4.4055 2.308594,-5.5508 1.935731,-1.1246 4.38863,-0.8102 5.986326,0.7207 l -1.806638,1.9063 7.828128,1.9062 -2.32422,-7.7129 -1.62696,1.7168 c -1.26338,-1.2531 -2.848917,-2.0088 -4.505855,-2.2285 -0.418778,-0.055 -0.842263,-0.076 -1.265625,-0.062 z"
id="path4138-1-3" />
<path
id="path6191"
d="m 10.5,116 0,-2 -2.4999996,0 L 12,109 l 4,5 -2.5,0 0,2 -3,0 z"
style="opacity:0.8;fill:#ffffff;fill-opacity:1;stroke:none;stroke-width:1.966;stroke-miterlimit:4;stroke-dasharray:none"
inkscape:connector-curvature="0" />
<path
inkscape:connector-curvature="0"
style="opacity:0.8;fill:#ffffff;fill-opacity:1;stroke:none;stroke-width:1.966;stroke-miterlimit:4;stroke-dasharray:none"
d="m 10.5,129 0,-2 -2.4999996,0 L 12,122 l 4,5 -2.5,0 0,2 -3,0 z"
id="path6193" />
<path
id="path6195"
d="m 10.5,135 0,2 -2.4999996,0 L 12,142 l 4,-5 -2.5,0 0,-2 -3,0 z"
style="opacity:0.8;fill:#ffffff;fill-opacity:1;stroke:none;stroke-width:1.966;stroke-miterlimit:4;stroke-dasharray:none"
inkscape:connector-curvature="0" />
<path
sodipodi:type="star"
style="fill:#4d4d4d;fill-opacity:0.90196078;stroke:#d3d3d3;stroke-width:0;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none"
id="path4500"
sodipodi:sides="3"
sodipodi:cx="11.55581"
sodipodi:cy="60.073242"
sodipodi:r1="5.1116104"
sodipodi:r2="2.5558052"
sodipodi:arg1="0"
sodipodi:arg2="1.0471976"
inkscape:flatsided="false"
inkscape:rounded="0"
inkscape:randomized="0"
d="m 16.66742,60.073242 -3.833708,2.213392 -3.8337072,2.213393 0,-4.426785 0,-4.426784 3.8337082,2.213392 z"
inkscape:transform-center-x="-1.2779026" />
<path
inkscape:transform-center-x="1.277902"
d="m -31.500004,60.073242 -3.833708,2.213392 -3.833707,2.213393 0,-4.426785 0,-4.426784 3.833707,2.213392 z"
inkscape:randomized="0"
inkscape:rounded="0"
inkscape:flatsided="false"
sodipodi:arg2="1.0471976"
sodipodi:arg1="0"
sodipodi:r2="2.5558052"
sodipodi:r1="5.1116104"
sodipodi:cy="60.073242"
sodipodi:cx="-36.611614"
sodipodi:sides="3"
id="path4502"
style="fill:#4d4d4d;fill-opacity:0.90196078;stroke:#d3d3d3;stroke-width:0;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none"
sodipodi:type="star"
transform="scale(-1,1)" />
<path
d="m 16.66742,60.073212 -3.833708,2.213392 -3.8337072,2.213392 0,-4.426784 0,-4.426785 3.8337082,2.213392 z"
inkscape:randomized="0"
inkscape:rounded="0"
inkscape:flatsided="false"
sodipodi:arg2="1.0471976"
sodipodi:arg1="0"
sodipodi:r2="2.5558052"
sodipodi:r1="5.1116104"
sodipodi:cy="60.073212"
sodipodi:cx="11.55581"
sodipodi:sides="3"
id="path4504"
style="fill:#4d4d4d;fill-opacity:0.90196078;stroke:#d3d3d3;stroke-width:0;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none"
sodipodi:type="star"
transform="matrix(0,1,-1,0,72.0074,71.7877)"
inkscape:transform-center-y="1.2779029" />
<path
inkscape:transform-center-y="-1.2779026"
transform="matrix(0,-1,-1,0,96,96)"
sodipodi:type="star"
style="fill:#4d4d4d;fill-opacity:0.90196078;stroke:#d3d3d3;stroke-width:0;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none"
id="path4506"
sodipodi:sides="3"
sodipodi:cx="11.55581"
sodipodi:cy="60.073212"
sodipodi:r1="5.1116104"
sodipodi:r2="2.5558052"
sodipodi:arg1="0"
sodipodi:arg2="1.0471976"
inkscape:flatsided="false"
inkscape:rounded="0"
inkscape:randomized="0"
d="m 16.66742,60.073212 -3.833708,2.213392 -3.8337072,2.213392 0,-4.426784 0,-4.426785 3.8337082,2.213392 z" />
<path
sodipodi:nodetypes="cccc"
inkscape:connector-curvature="0"
id="path4615-5"
d="m 171.82574,65.174193 16.34854,0 -8.17427,-13.348454 z"
style="fill:#fbb917;fill-opacity:1;fill-rule:evenodd;stroke:#fbb917;stroke-width:1.65161395;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" />
<path
style="opacity:1;fill:#ffffff;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
d="m 179,55 0,6 2,0 0,-6"
id="path4300"
inkscape:connector-curvature="0"
sodipodi:nodetypes="cccc" />
<path
style="opacity:1;fill:#ffffff;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
d="m 179,62 0,2 2,0 0,-2"
id="path4300-6"
inkscape:connector-curvature="0"
sodipodi:nodetypes="cccc" />
</svg>

Before

Width:  |  Height:  |  Size: 35 KiB

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

929
dist/jsoneditor.css vendored
View File

@ -1,929 +0,0 @@
/* reset styling (prevent conflicts with bootstrap, materialize.css, etc.) */
div.jsoneditor input {
height: auto;
border: inherit;
}
div.jsoneditor input:focus {
border: none !important;
box-shadow: none !important;
}
div.jsoneditor table {
border-collapse: collapse;
width: auto;
}
div.jsoneditor td,
div.jsoneditor th {
padding: 0;
display: table-cell;
text-align: left;
vertical-align: inherit;
border-radius: inherit;
}
div.jsoneditor-field,
div.jsoneditor-value,
div.jsoneditor-readonly {
border: 1px solid transparent;
min-height: 16px;
min-width: 32px;
padding: 2px;
margin: 1px;
word-wrap: break-word;
float: left;
}
/* adjust margin of p elements inside editable divs, needed for Opera, IE */
div.jsoneditor-field p,
div.jsoneditor-value p {
margin: 0;
}
div.jsoneditor-value {
word-break: break-word;
}
div.jsoneditor-readonly {
min-width: 16px;
color: gray;
}
div.jsoneditor-empty {
border-color: lightgray;
border-style: dashed;
border-radius: 2px;
}
div.jsoneditor-field.jsoneditor-empty::after,
div.jsoneditor-value.jsoneditor-empty::after {
pointer-events: none;
color: lightgray;
font-size: 8pt;
}
div.jsoneditor-field.jsoneditor-empty::after {
content: "field";
}
div.jsoneditor-value.jsoneditor-empty::after {
content: "value";
}
div.jsoneditor-value.jsoneditor-url,
a.jsoneditor-value.jsoneditor-url {
color: green;
text-decoration: underline;
}
a.jsoneditor-value.jsoneditor-url {
display: inline-block;
padding: 2px;
margin: 2px;
}
a.jsoneditor-value.jsoneditor-url:hover,
a.jsoneditor-value.jsoneditor-url:focus {
color: #ee422e;
}
div.jsoneditor td.jsoneditor-separator {
padding: 3px 0;
vertical-align: top;
color: gray;
}
div.jsoneditor-field[contenteditable=true]:focus,
div.jsoneditor-field[contenteditable=true]:hover,
div.jsoneditor-value[contenteditable=true]:focus,
div.jsoneditor-value[contenteditable=true]:hover,
div.jsoneditor-field.jsoneditor-highlight,
div.jsoneditor-value.jsoneditor-highlight {
background-color: #FFFFAB;
border: 1px solid yellow;
border-radius: 2px;
}
div.jsoneditor-field.jsoneditor-highlight-active,
div.jsoneditor-field.jsoneditor-highlight-active:focus,
div.jsoneditor-field.jsoneditor-highlight-active:hover,
div.jsoneditor-value.jsoneditor-highlight-active,
div.jsoneditor-value.jsoneditor-highlight-active:focus,
div.jsoneditor-value.jsoneditor-highlight-active:hover {
background-color: #ffee00;
border: 1px solid #ffc700;
border-radius: 2px;
}
div.jsoneditor-value.jsoneditor-string {
color: #008000;
}
div.jsoneditor-value.jsoneditor-object,
div.jsoneditor-value.jsoneditor-array {
min-width: 16px;
color: #808080;
}
div.jsoneditor-value.jsoneditor-number {
color: #ee422e;
}
div.jsoneditor-value.jsoneditor-boolean {
color: #ff8c00;
}
div.jsoneditor-value.jsoneditor-null {
color: #004ED0;
}
div.jsoneditor-value.jsoneditor-invalid {
color: #000000;
}
div.jsoneditor-tree button {
width: 24px;
height: 24px;
padding: 0;
margin: 0;
border: none;
cursor: pointer;
background: transparent url("img/jsoneditor-icons.svg");
}
div.jsoneditor-mode-view tr.jsoneditor-expandable td.jsoneditor-tree,
div.jsoneditor-mode-form tr.jsoneditor-expandable td.jsoneditor-tree {
cursor: pointer;
}
div.jsoneditor-tree button.jsoneditor-collapsed {
background-position: 0 -48px;
}
div.jsoneditor-tree button.jsoneditor-expanded {
background-position: 0 -72px;
}
div.jsoneditor-tree button.jsoneditor-contextmenu {
background-position: -48px -72px;
}
div.jsoneditor-tree button.jsoneditor-contextmenu:hover,
div.jsoneditor-tree button.jsoneditor-contextmenu:focus,
div.jsoneditor-tree button.jsoneditor-contextmenu.jsoneditor-selected,
tr.jsoneditor-selected.jsoneditor-first button.jsoneditor-contextmenu {
background-position: -48px -48px;
}
div.jsoneditor-tree *:focus {
outline: none;
}
div.jsoneditor-tree button:focus {
/* TODO: nice outline for buttons with focus
outline: #97B0F8 solid 2px;
box-shadow: 0 0 8px #97B0F8;
*/
background-color: #f5f5f5;
outline: #e5e5e5 solid 1px;
}
div.jsoneditor-tree button.jsoneditor-invisible {
visibility: hidden;
background: none;
}
div.jsoneditor {
color: #1A1A1A;
border: 1px solid #3883fa;
-moz-box-sizing: border-box;
-webkit-box-sizing: border-box;
box-sizing: border-box;
width: 100%;
height: 100%;
overflow: hidden;
position: relative;
padding: 0;
line-height: 100%;
}
div.jsoneditor-tree table.jsoneditor-tree {
border-collapse: collapse;
border-spacing: 0;
width: 100%;
margin: 0;
}
div.jsoneditor-outer {
width: 100%;
height: 100%;
margin: -35px 0 0 0;
padding: 35px 0 0 0;
-moz-box-sizing: border-box;
-webkit-box-sizing: border-box;
box-sizing: border-box;
}
textarea.jsoneditor-text,
.ace-jsoneditor {
min-height: 150px;
}
div.jsoneditor-tree {
width: 100%;
height: 100%;
position: relative;
overflow: auto;
}
textarea.jsoneditor-text {
width: 100%;
height: 100%;
margin: 0;
-moz-box-sizing: border-box;
-webkit-box-sizing: border-box;
box-sizing: border-box;
outline-width: 0;
border: none;
background-color: white;
resize: none;
}
tr.jsoneditor-highlight,
tr.jsoneditor-selected {
background-color: #e6e6e6;
}
tr.jsoneditor-selected button.jsoneditor-dragarea,
tr.jsoneditor-selected button.jsoneditor-contextmenu {
visibility: hidden;
}
tr.jsoneditor-selected.jsoneditor-first button.jsoneditor-dragarea,
tr.jsoneditor-selected.jsoneditor-first button.jsoneditor-contextmenu {
visibility: visible;
}
div.jsoneditor-tree button.jsoneditor-dragarea {
background: url("img/jsoneditor-icons.svg") -72px -72px;
cursor: move;
}
div.jsoneditor-tree button.jsoneditor-dragarea:hover,
div.jsoneditor-tree button.jsoneditor-dragarea:focus,
tr.jsoneditor-selected.jsoneditor-first button.jsoneditor-dragarea {
background-position: -72px -48px;
}
div.jsoneditor tr,
div.jsoneditor th,
div.jsoneditor td {
padding: 0;
margin: 0;
}
div.jsoneditor td {
vertical-align: top;
}
div.jsoneditor td.jsoneditor-tree {
vertical-align: top;
}
div.jsoneditor-field,
div.jsoneditor-value,
div.jsoneditor td,
div.jsoneditor th,
div.jsoneditor textarea,
.jsoneditor-schema-error {
font-family: droid sans mono, consolas, monospace, courier new, courier, sans-serif;
font-size: 10pt;
color: #1A1A1A;
}
/* popover */
.jsoneditor-schema-error {
cursor: default;
display: inline-block;
/*font-family: arial, sans-serif;*/
height: 24px;
line-height: 24px;
position: relative;
text-align: center;
width: 24px;
}
div.jsoneditor-tree .jsoneditor-schema-error {
width: 24px;
height: 24px;
padding: 0;
margin: 0 4px 0 0;
background: url("img/jsoneditor-icons.svg") -168px -48px;
}
.jsoneditor-schema-error .jsoneditor-popover {
background-color: #4c4c4c;
border-radius: 3px;
box-shadow: 0 0 5px rgba(0,0,0,0.4);
color: #fff;
display: none;
padding: 7px 10px;
position: absolute;
width: 200px;
z-index: 4;
}
.jsoneditor-schema-error .jsoneditor-popover.jsoneditor-above {
bottom: 32px;
left: -98px;
}
.jsoneditor-schema-error .jsoneditor-popover.jsoneditor-below {
top: 32px;
left: -98px;
}
.jsoneditor-schema-error .jsoneditor-popover.jsoneditor-left {
top: -7px;
right: 32px;
}
.jsoneditor-schema-error .jsoneditor-popover.jsoneditor-right {
top: -7px;
left: 32px;
}
.jsoneditor-schema-error .jsoneditor-popover:before {
border-right: 7px solid transparent;
border-left: 7px solid transparent;
content: '';
display: block;
left: 50%;
margin-left: -7px;
position: absolute;
}
.jsoneditor-schema-error .jsoneditor-popover.jsoneditor-above:before {
border-top: 7px solid #4c4c4c;
bottom: -7px;
}
.jsoneditor-schema-error .jsoneditor-popover.jsoneditor-below:before {
border-bottom: 7px solid #4c4c4c;
top: -7px;
}
.jsoneditor-schema-error .jsoneditor-popover.jsoneditor-left:before {
border-left: 7px solid #4c4c4c;
border-top: 7px solid transparent;
border-bottom: 7px solid transparent;
content: '';
top: 19px;
right: -14px;
left: inherit;
margin-left: inherit;
margin-top: -7px;
position: absolute;
}
.jsoneditor-schema-error .jsoneditor-popover.jsoneditor-right:before {
border-right: 7px solid #4c4c4c;
border-top: 7px solid transparent;
border-bottom: 7px solid transparent;
content: '';
top: 19px;
left: -14px;
margin-left: inherit;
margin-top: -7px;
position: absolute;
}
.jsoneditor-schema-error:hover .jsoneditor-popover,
.jsoneditor-schema-error:focus .jsoneditor-popover {
display: block;
-webkit-animation: fade-in .3s linear 1, move-up .3s linear 1;
-moz-animation: fade-in .3s linear 1, move-up .3s linear 1;
-ms-animation: fade-in .3s linear 1, move-up .3s linear 1;
}
@-webkit-keyframes fade-in {
from {
opacity: 0;
}
to {
opacity: 1;
}
}
@-moz-keyframes fade-in {
from {
opacity: 0;
}
to {
opacity: 1;
}
}
@-ms-keyframes fade-in {
from {
opacity: 0;
}
to {
opacity: 1;
}
}
/*@-webkit-keyframes move-up {*/
/*from { bottom: 24px; }*/
/*to { bottom: 32px; }*/
/*}*/
/*@-moz-keyframes move-up {*/
/*from { bottom: 24px; }*/
/*to { bottom: 32px; }*/
/*}*/
/*@-ms-keyframes move-up {*/
/*from { bottom: 24px; }*/
/*to { bottom: 32px; }*/
/*}*/
/* JSON schema errors displayed at the bottom of the editor in mode text and code */
.jsoneditor .jsoneditor-text-errors {
width: 100%;
border-collapse: collapse;
background-color: #ffef8b;
border-top: 1px solid #ffd700;
}
.jsoneditor .jsoneditor-text-errors td {
padding: 3px 6px;
vertical-align: middle;
}
.jsoneditor-text-errors .jsoneditor-schema-error {
border: none;
width: 24px;
height: 24px;
padding: 0;
margin: 0 4px 0 0;
background: url("img/jsoneditor-icons.svg") -168px -48px;
}
/* ContextMenu - main menu */
div.jsoneditor-contextmenu-root {
position: relative;
width: 0;
height: 0;
}
div.jsoneditor-contextmenu {
position: absolute;
box-sizing: content-box;
z-index: 99999;
}
div.jsoneditor-contextmenu ul,
div.jsoneditor-contextmenu li {
box-sizing: content-box;
}
div.jsoneditor-contextmenu ul {
position: relative;
left: 0;
top: 0;
width: 124px;
background: white;
border: 1px solid #d3d3d3;
box-shadow: 2px 2px 12px rgba(128, 128, 128, 0.3);
list-style: none;
margin: 0;
padding: 0;
}
div.jsoneditor-contextmenu ul li button {
padding: 0;
margin: 0;
width: 124px;
height: 24px;
border: none;
cursor: pointer;
color: #4d4d4d;
background: transparent;
font-size: 10pt;
font-family: arial, sans-serif;
box-sizing: border-box;
line-height: 26px;
text-align: left;
}
/* Fix button padding in firefox */
div.jsoneditor-contextmenu ul li button::-moz-focus-inner {
padding: 0;
border: 0;
}
div.jsoneditor-contextmenu ul li button:hover,
div.jsoneditor-contextmenu ul li button:focus {
color: #1a1a1a;
background-color: #f5f5f5;
outline: none;
}
div.jsoneditor-contextmenu ul li button.jsoneditor-default {
width: 92px;
}
div.jsoneditor-contextmenu ul li button.jsoneditor-expand {
float: right;
width: 32px;
height: 24px;
border-left: 1px solid #e5e5e5;
}
div.jsoneditor-contextmenu div.jsoneditor-icon {
float: left;
width: 24px;
height: 24px;
border: none;
padding: 0;
margin: 0;
background-image: url("img/jsoneditor-icons.svg");
}
div.jsoneditor-contextmenu ul li button div.jsoneditor-expand {
float: right;
width: 24px;
height: 24px;
padding: 0;
margin: 0 4px 0 0;
background: url("img/jsoneditor-icons.svg") 0 -72px;
opacity: 0.4;
}
div.jsoneditor-contextmenu ul li button:hover div.jsoneditor-expand,
div.jsoneditor-contextmenu ul li button:focus div.jsoneditor-expand,
div.jsoneditor-contextmenu ul li.jsoneditor-selected div.jsoneditor-expand,
div.jsoneditor-contextmenu ul li button.jsoneditor-expand:hover div.jsoneditor-expand,
div.jsoneditor-contextmenu ul li button.jsoneditor-expand:focus div.jsoneditor-expand {
opacity: 1;
}
div.jsoneditor-contextmenu div.jsoneditor-separator {
height: 0;
border-top: 1px solid #e5e5e5;
padding-top: 5px;
margin-top: 5px;
}
div.jsoneditor-contextmenu button.jsoneditor-remove > div.jsoneditor-icon {
background-position: -24px -24px;
}
div.jsoneditor-contextmenu button.jsoneditor-remove:hover > div.jsoneditor-icon,
div.jsoneditor-contextmenu button.jsoneditor-remove:focus > div.jsoneditor-icon {
background-position: -24px 0;
}
div.jsoneditor-contextmenu button.jsoneditor-append > div.jsoneditor-icon {
background-position: 0 -24px;
}
div.jsoneditor-contextmenu button.jsoneditor-append:hover > div.jsoneditor-icon,
div.jsoneditor-contextmenu button.jsoneditor-append:focus > div.jsoneditor-icon {
background-position: 0 0;
}
div.jsoneditor-contextmenu button.jsoneditor-insert > div.jsoneditor-icon {
background-position: 0 -24px;
}
div.jsoneditor-contextmenu button.jsoneditor-insert:hover > div.jsoneditor-icon,
div.jsoneditor-contextmenu button.jsoneditor-insert:focus > div.jsoneditor-icon {
background-position: 0 0;
}
div.jsoneditor-contextmenu button.jsoneditor-duplicate > div.jsoneditor-icon {
background-position: -48px -24px;
}
div.jsoneditor-contextmenu button.jsoneditor-duplicate:hover > div.jsoneditor-icon,
div.jsoneditor-contextmenu button.jsoneditor-duplicate:focus > div.jsoneditor-icon {
background-position: -48px 0;
}
div.jsoneditor-contextmenu button.jsoneditor-sort-asc > div.jsoneditor-icon {
background-position: -168px -24px;
}
div.jsoneditor-contextmenu button.jsoneditor-sort-asc:hover > div.jsoneditor-icon,
div.jsoneditor-contextmenu button.jsoneditor-sort-asc:focus > div.jsoneditor-icon {
background-position: -168px 0;
}
div.jsoneditor-contextmenu button.jsoneditor-sort-desc > div.jsoneditor-icon {
background-position: -192px -24px;
}
div.jsoneditor-contextmenu button.jsoneditor-sort-desc:hover > div.jsoneditor-icon,
div.jsoneditor-contextmenu button.jsoneditor-sort-desc:focus > div.jsoneditor-icon {
background-position: -192px 0;
}
/* ContextMenu - sub menu */
div.jsoneditor-contextmenu ul li button.jsoneditor-selected,
div.jsoneditor-contextmenu ul li button.jsoneditor-selected:hover,
div.jsoneditor-contextmenu ul li button.jsoneditor-selected:focus {
color: white;
background-color: #ee422e;
}
div.jsoneditor-contextmenu ul li {
overflow: hidden;
}
div.jsoneditor-contextmenu ul li ul {
display: none;
position: relative;
left: -10px;
top: 0;
border: none;
box-shadow: inset 0 0 10px rgba(128, 128, 128, 0.5);
padding: 0 10px;
/* TODO: transition is not supported on IE8-9 */
-webkit-transition: all 0.3s ease-out;
-moz-transition: all 0.3s ease-out;
-o-transition: all 0.3s ease-out;
transition: all 0.3s ease-out;
}
div.jsoneditor-contextmenu ul li ul li button {
padding-left: 24px;
animation: all ease-in-out 1s;
}
div.jsoneditor-contextmenu ul li ul li button:hover,
div.jsoneditor-contextmenu ul li ul li button:focus {
background-color: #f5f5f5;
}
div.jsoneditor-contextmenu button.jsoneditor-type-string > div.jsoneditor-icon {
background-position: -144px -24px;
}
div.jsoneditor-contextmenu button.jsoneditor-type-string:hover > div.jsoneditor-icon,
div.jsoneditor-contextmenu button.jsoneditor-type-string:focus > div.jsoneditor-icon,
div.jsoneditor-contextmenu button.jsoneditor-type-string.jsoneditor-selected > div.jsoneditor-icon {
background-position: -144px 0;
}
div.jsoneditor-contextmenu button.jsoneditor-type-auto > div.jsoneditor-icon {
background-position: -120px -24px;
}
div.jsoneditor-contextmenu button.jsoneditor-type-auto:hover > div.jsoneditor-icon,
div.jsoneditor-contextmenu button.jsoneditor-type-auto:focus > div.jsoneditor-icon,
div.jsoneditor-contextmenu button.jsoneditor-type-auto.jsoneditor-selected > div.jsoneditor-icon {
background-position: -120px 0;
}
div.jsoneditor-contextmenu button.jsoneditor-type-object > div.jsoneditor-icon {
background-position: -72px -24px;
}
div.jsoneditor-contextmenu button.jsoneditor-type-object:hover > div.jsoneditor-icon,
div.jsoneditor-contextmenu button.jsoneditor-type-object:focus > div.jsoneditor-icon,
div.jsoneditor-contextmenu button.jsoneditor-type-object.jsoneditor-selected > div.jsoneditor-icon {
background-position: -72px 0;
}
div.jsoneditor-contextmenu button.jsoneditor-type-array > div.jsoneditor-icon {
background-position: -96px -24px;
}
div.jsoneditor-contextmenu button.jsoneditor-type-array:hover > div.jsoneditor-icon,
div.jsoneditor-contextmenu button.jsoneditor-type-array:focus > div.jsoneditor-icon,
div.jsoneditor-contextmenu button.jsoneditor-type-array.jsoneditor-selected > div.jsoneditor-icon {
background-position: -96px 0;
}
div.jsoneditor-contextmenu button.jsoneditor-type-modes > div.jsoneditor-icon {
background-image: none;
width: 6px;
}
div.jsoneditor-menu {
width: 100%;
height: 35px;
padding: 2px;
margin: 0;
-moz-box-sizing: border-box;
-webkit-box-sizing: border-box;
box-sizing: border-box;
color: white;
background-color: #3883fa;
border-bottom: 1px solid #3883fa;
}
div.jsoneditor-menu > button,
div.jsoneditor-menu > div.jsoneditor-modes > button {
width: 26px;
height: 26px;
margin: 2px;
padding: 0;
border-radius: 2px;
border: 1px solid transparent;
background: transparent url("img/jsoneditor-icons.svg");
color: white;
opacity: 0.8;
font-family: arial, sans-serif;
font-size: 10pt;
float: left;
}
div.jsoneditor-menu > button:hover,
div.jsoneditor-menu > div.jsoneditor-modes > button:hover {
background-color: rgba(255,255,255,0.2);
border: 1px solid rgba(255,255,255,0.4);
}
div.jsoneditor-menu > button:focus,
div.jsoneditor-menu > button:active,
div.jsoneditor-menu > div.jsoneditor-modes > button:focus,
div.jsoneditor-menu > div.jsoneditor-modes > button:active {
background-color: rgba(255,255,255,0.3);
}
div.jsoneditor-menu > button:disabled,
div.jsoneditor-menu > div.jsoneditor-modes > button:disabled {
opacity: 0.5;
}
div.jsoneditor-menu > button.jsoneditor-collapse-all {
background-position: 0 -96px;
}
div.jsoneditor-menu > button.jsoneditor-expand-all {
background-position: 0 -120px;
}
div.jsoneditor-menu > button.jsoneditor-undo {
background-position: -24px -96px;
}
div.jsoneditor-menu > button.jsoneditor-undo:disabled {
background-position: -24px -120px;
}
div.jsoneditor-menu > button.jsoneditor-redo {
background-position: -48px -96px;
}
div.jsoneditor-menu > button.jsoneditor-redo:disabled {
background-position: -48px -120px;
}
div.jsoneditor-menu > button.jsoneditor-compact {
background-position: -72px -96px;
}
div.jsoneditor-menu > button.jsoneditor-format {
background-position: -72px -120px;
}
div.jsoneditor-menu > div.jsoneditor-modes {
display: inline-block;
float: left;
}
div.jsoneditor-menu > div.jsoneditor-modes > button {
background-image: none;
width: auto;
padding-left: 6px;
padding-right: 6px;
}
div.jsoneditor-menu > button.jsoneditor-separator,
div.jsoneditor-menu > div.jsoneditor-modes > button.jsoneditor-separator {
margin-left: 10px;
}
div.jsoneditor-menu a {
font-family: arial, sans-serif;
font-size: 10pt;
color: white;
opacity: 0.8;
vertical-align: middle;
}
div.jsoneditor-menu a:hover {
opacity: 1;
}
div.jsoneditor-menu a.jsoneditor-poweredBy {
font-size: 8pt;
position: absolute;
right: 0;
top: 0;
padding: 10px;
}
table.jsoneditor-search input,
table.jsoneditor-search div.jsoneditor-results {
font-family: arial, sans-serif;
font-size: 10pt;
color: #1A1A1A;
background: transparent;
/* For Firefox */
}
table.jsoneditor-search div.jsoneditor-results {
color: white;
padding-right: 5px;
line-height: 24px;
}
table.jsoneditor-search {
position: absolute;
right: 4px;
top: 4px;
border-collapse: collapse;
border-spacing: 0;
}
table.jsoneditor-search div.jsoneditor-frame {
border: 1px solid transparent;
background-color: white;
padding: 0 2px;
margin: 0;
}
table.jsoneditor-search div.jsoneditor-frame table {
border-collapse: collapse;
}
table.jsoneditor-search input {
width: 120px;
border: none;
outline: none;
margin: 1px;
line-height: 20px;
}
table.jsoneditor-search button {
width: 16px;
height: 24px;
padding: 0;
margin: 0;
border: none;
background: url("img/jsoneditor-icons.svg");
vertical-align: top;
}
table.jsoneditor-search button:hover {
background-color: transparent;
}
table.jsoneditor-search button.jsoneditor-refresh {
width: 18px;
background-position: -99px -73px;
}
table.jsoneditor-search button.jsoneditor-next {
cursor: pointer;
background-position: -124px -73px;
}
table.jsoneditor-search button.jsoneditor-next:hover {
background-position: -124px -49px;
}
table.jsoneditor-search button.jsoneditor-previous {
cursor: pointer;
background-position: -148px -73px;
margin-right: 2px;
}
table.jsoneditor-search button.jsoneditor-previous:hover {
background-position: -148px -49px;
}

36348
dist/jsoneditor.js vendored

File diff suppressed because one or more lines are too long

1
dist/jsoneditor.map vendored

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@ -1,41 +0,0 @@
# Which files do I need?
Ehhh, that's quite some files in this dist folder. Which files do I need?
## Full version
If you're not sure which version to use, use the full version.
Which files are needed when using the full version?
- jsoneditor.min.js
- jsoneditor.map (optional, for debugging purposes only)
- jsoneditor.min.css
- img/jsoneditor-icons.svg
## Minimalist version
The minimalist version has excluded the following libraries:
- `ace` (via `brace`), used for the code editor.
- `ajv`, used for JSON schema validation.
This reduces the the size of the minified and gzipped JavaScript file from
about 160 kB to about 40 kB.
When to use the minimalist version?
- If you don't need the mode "code" and don't need JSON schema validation.
- Or if you want to provide `ace` and/or `ajv` yourself via the configuration
options, for example when you already use Ace in other parts of your
web application too and don't want to bundle the library twice.
Which files are needed when using the minimalist version?
- jsoneditor-minimalist.min.js
- jsoneditor-minimalist.map (optional, for debugging purposes only)
- jsoneditor.min.css
- img/jsoneditor-icons.svg

View File

@ -2,9 +2,9 @@
## JSONEditor
### Constructor
### Construction
#### `JSONEditor(container [, options [, json]])`
#### `jsoneditor(container [, options])`
Constructs a new JSONEditor.
@ -19,13 +19,9 @@ Constructs a new JSONEditor.
Optional object with options. The available options are described under
[Configuration options](#configuration-options).
- `{JSON} json`
Initial JSON data to be loaded into the JSONEditor. Alternatively, the method `JSONEditor.set(json)` can be used to load JSON data into the editor.
*Returns:*
- `{JSONEditor} editor`
- `{Object} editor`
New instance of a JSONEditor.
@ -41,7 +37,7 @@ Constructs a new JSONEditor.
library used for JSON schema validation. Example:
```js
var options = {
const options = {
ajv: Ajv({ allErrors: true, verbose: true })
}
```
@ -76,6 +72,25 @@ Constructs a new JSONEditor.
Enables history, adds a button Undo and Redo to the menu of the JSONEditor. True by default. Only applicable when `mode` is 'tree' or 'form'.
- `{Object<String, String[]>} keyBindings`
Override default key bindings. For example to replace the binding to duplicate a node from `Ctrl+D` to `Ctrl+Shift+D`:
```js
const options = {
keyBindings: {
duplicate: ['Ctrl+Shift+D', 'Command+Shift+D']
}
}
```
It's important to define bindings for both Windows and Mac. The meta keys on Windows are `Ctrl`, `Shift`, `Alt`, and on Mac they are respectively `Command`, `Shift`, and `Option`.
All available key bindings are described on the page [Key Bindings](#key_bindings.md).
Key bindings are case insensitive.
- `{String} mode`
Set the editor mode. Available values: 'tree' (default), 'view', 'form', 'code', 'text'. In 'view' mode, the data and datastructure is read-only. In 'form' mode, only the value can be changed, the datastructure is read-only. Mode 'code' requires the Ace editor to be loaded on the page. Mode 'text' shows the data as plain text.
@ -233,44 +248,44 @@ Get JSON data as string.
A tree editor:
```js
var options = {
"mode": "tree",
"search": true
};
var editor = new JSONEditor(container, options);
var json = {
const options = {
mode: 'tree',
search: true
}
const editor = new JSONEditor(container, options)
let json = {
"Array": [1, 2, 3],
"Boolean": true,
"Null": null,
"Number": 123,
"Object": {"a": "b", "c": "d"},
"String": "Hello World"
};
editor.set(json);
editor.expandAll();
}
editor.set(json)
editor.expandAll()
var json = editor.get(json);
json = editor.get(json)
```
A text editor:
```js
var options = {
"mode": "text",
"indentation": 2
};
var editor = new JSONEditor(container, options);
var json = {
const options = {
mode: 'text',
indentation: 2
}
const editor = new JSONEditor(container, options)
let json = {
"Array": [1, 2, 3],
"Boolean": true,
"Null": null,
"Number": 123,
"Object": {"a": "b", "c": "d"},
"String": "Hello World"
};
editor.set(json);
}
editor.set(json)
var json = editor.get();
json = editor.get()
```
## JSON parsing and stringification
@ -278,17 +293,17 @@ var json = editor.get();
In general to parse or stringify JSON data, the browsers built in JSON parser can be used. To create a formatted string from a JSON object, use:
```js
var formattedString = JSON.stringify(json, null, 2);
const formattedString = JSON.stringify(json, null, 2)
```
to create a compacted string from a JSON object, use:
```js
var compactString = JSON.stringify(json);
const compactString = JSON.stringify(json)
```
To parse a String to a JSON object, use:
```js
var json = JSON.parse(string);
const json = JSON.parse(string)
```

35
docs/key_bindings.md Normal file
View File

@ -0,0 +1,35 @@
# Key bindings
## Tree Editor
Name | Windows key combination | Mac key combination | Description
----------------------------- | ----------------------- | -------------------------- | ------------------------------------------------
`up`, `down`, `left`, `right` | Alt+Arrows | Option+Arrows | Move the caret up/down/left/right between fields
`duplicate` | Ctrl+D | Command+D | Duplicate field
`remove` | Ctrl+Del | Command+Del | Remove field
`openUrl` | Ctrl+Enter | Command+Enter | Open link when on a field containing an url
`insert` | Ctrl+Ins | Command+Ins | Insert a new field with type auto
`expand` | Ctrl+E | Command+E | Expand or collapse field
`end` | Alt+End | Option+End | Move the caret to the last field
`find` | Ctrl+F | Command+F | Find
`findNext` | F3, Ctrl+G | F3, Command+G | Find next
`findPrevious` | Shift+F3, Ctrl+Shift+G | Shift+F3, Command+Shift+G | Find previous
`home` | Alt+Home | Option+Home | Move the caret to the first field
`actionMenu` | Ctrl+M | Command+M | Show actions menu
`undo` | Ctrl+Z | Command+Z | Undo last action
`redo` | Ctrl+Shift+Z | Command+Shift+Z | Redo
## Code Editor
The code editor is powered by [Ace Editor](http://ace.c9.io/). This editor's
shortcut keys are described here:
https://github.com/ajaxorg/ace/wiki/Default-Keyboard-Shortcuts
Additionally, there are shortcut keys to format/compact the code:
Name | Windows key combination | Mac key combination | Description
--------- | ----------------------- | ------------------- | ------------------------------------------------
`format` | Ctrl+\ | Command+\ | Format JSON data, set proper indentation
`compact` | Ctrl+Shift+\ | Command+Shift+\ | Compact JSON data, remove all whitespace

View File

@ -1,38 +1,3 @@
# Shortcut keys
## Tree Editor
Key | Description
----------------------- | ------------------------------------------------
Alt+Arrows | Move the caret up/down/left/right between fields
Ctrl+Shift+Arrow Up/Down| Select multiple fields
Shift+Alt+Arrows | Move current field or selected fields up/down/left/right
Ctrl+D | Duplicate field
Ctrl+Del | Remove field
Ctrl+Enter | Open link when on a field containing an url
Ctrl+Ins | Insert a new field with type auto
Ctrl+Shift+Ins | Append a new field with type auto
Ctrl+E | Expand or collapse field
Alt+End | Move the caret to the last field
Ctrl+F | Find
F3, Ctrl+G | Find next
Shift+F3, Ctrl+Shift+G | Find previous
Alt+Home | Move the caret to the first field
Ctrl+M | Show actions menu
Ctrl+Z | Undo last action
Ctrl+Shift+Z | Redo
## Code Editor
The code editor is powered by [Ace Editor](http://ace.c9.io/). This editor's
shortcut keys are described here:
https://github.com/ajaxorg/ace/wiki/Default-Keyboard-Shortcuts
Additionally, there are shortcut keys to format/compact the code:
Key | Description
----------------------- | ------------------------------------------------
Ctrl+\ | Format JSON data, set proper indentation
Ctrl+Shift+\ | Compact JSON data, remove all whitespace
This page has been renamed to [Key Bindings](#key_bindings.md).

View File

@ -2,29 +2,17 @@
### Install
with npm:
using npm:
npm install jsoneditor
with bower:
bower install jsoneditor
download:
[http://jsoneditoronline.org/downloads/](http://jsoneditoronline.org/downloads/)
The library consists of three files: one javascript file, one css file and an
image. Both full and minified version are available.
## Load
To implement JSONEditor in a web application, load the javascript and css file
To implement JSONEditor in a web application, load the javascript file
in the head of the HTML page:
```html
<link href="jsoneditor/dist/jsoneditor.min.css" rel="stylesheet" type="text/css">
<script src="jsoneditor/dist/jsoneditor.min.js"></script>
```html\
<script src="jsoneditor/dist/jsoneditor.js"></script>
```
## Use
@ -38,11 +26,11 @@ In the body, create an div element with an id and a size:
After the page is loaded, load the editor with javascript:
```js
var container = document.getElementById("jsoneditor");
var container = document.getElementById("jsoneditor")
var options = {
mode: 'tree'
};
var editor = new JSONEditor(container, options);
}
var editor = jsoneditor(container, options)
```
To set JSON data in the editor:
@ -55,14 +43,14 @@ var json = {
"Number": 123,
"Object": {"a": "b", "c": "d"},
"String": "Hello World"
};
editor.set(json);
}
editor.set(json)
```
To get JSON data from the editor:
```js
var json = editor.get();
var json = editor.get()
```
@ -75,20 +63,19 @@ var json = editor.get();
<!-- when using the mode "code", it's important to specify charset utf-8 -->
<meta http-equiv="Content-Type" content="text/html;charset=utf-8">
<link href="jsoneditor/dist/jsoneditor.min.css" rel="stylesheet" type="text/css">
<script src="jsoneditor/dist/jsoneditor.min.js"></script>
<script src="jsoneditor/dist/jsoneditor.js"></script>
</head>
<body>
<p>
<button onclick="setJSON();">Set JSON</button>
<button onclick="getJSON();">Get JSON</button>
<button onclick="setJSON()">Set JSON</button>
<button onclick="getJSON()">Get JSON</button>
</p>
<div id="jsoneditor" style="width: 400px; height: 400px;"></div>
<script>
// create the editor
var container = document.getElementById("jsoneditor");
var editor = new JSONEditor(container);
var container = document.getElementById('jsoneditor')
var editor = jsoneditor(container)
// set json
function setJSON () {
@ -99,14 +86,14 @@ var json = editor.get();
"Number": 123,
"Object": {"a": "b", "c": "d"},
"String": "Hello World"
};
editor.set(json);
}
editor.set(json)
}
// get json
function getJSON() {
var json = editor.get();
alert(JSON.stringify(json, null, 2));
var json = editor.get()
alert(JSON.stringify(json, null, 2))
}
</script>
</body>

View File

@ -1,9 +1,9 @@
<!DOCTYPE HTML>
<html>
<head>
<title>JSONEditor | Basic usage</title>
<meta charset="UTF-8">
<title>Basic usage | JSONEditor</title>
<link href="../dist/jsoneditor.css" rel="stylesheet" type="text/css">
<script src="../dist/jsoneditor.js"></script>
<style type="text/css">
@ -22,9 +22,9 @@
<script>
// create the editor
var container = document.getElementById('jsoneditor');
var options = {};
var editor = new JSONEditor(container, options);
var container = document.getElementById('jsoneditor')
var options = {}
var editor = jsoneditor(container, options)
// set json
document.getElementById('setJSON').onclick = function () {
@ -35,15 +35,15 @@
'number': 123,
'object': {'a': 'b', 'c': 'd'},
'string': 'Hello World'
};
editor.set(json);
};
}
editor.set(json)
}
// get json
document.getElementById('getJSON').onclick = function () {
var json = editor.get();
alert(JSON.stringify(json, null, 2));
};
var json = editor.get()
alert(JSON.stringify(json, null, 2))
}
</script>
</body>
</html>

View File

@ -1,9 +1,9 @@
<!DOCTYPE HTML>
<html>
<head>
<title>JSONEditor | Viewer</title>
<meta charset="UTF-8">
<title>Viewer | JSONEditor</title>
<link href="../dist/jsoneditor.css" rel="stylesheet" type="text/css">
<script src="../dist/jsoneditor.js"></script>
@ -18,16 +18,18 @@
</head>
<body>
<p>
This editor is read-only (mode='viewer').
This editor is read-only (mode='view').
</p>
<div id="jsoneditor"></div>
<script>
var container = document.getElementById('jsoneditor');
var container = document.getElementById('jsoneditor')
var options = {
mode: 'view'
};
}
var editor = jsoneditor(container, options)
var json = {
'array': [1, 2, 3],
@ -36,9 +38,9 @@
'number': 123,
'object': {'a': 'b', 'c': 'd'},
'string': 'Hello World'
};
}
editor.set(json)
var editor = new JSONEditor(container, options, json);
</script>
</body>
</html>

View File

@ -1,12 +1,11 @@
<!DOCTYPE HTML>
<html>
<head>
<title>JSONEditor | Switch mode</title>
<title>Switch mode | JSONEditor</title>
<!-- when using the mode "code", it's important to specify charset utf-8 -->
<meta http-equiv="Content-Type" content="text/html;charset=utf-8">
<link href="../dist/jsoneditor.css" rel="stylesheet" type="text/css">
<script src="../dist/jsoneditor.js"></script>
<style type="text/css">
@ -29,6 +28,7 @@
</head>
<body>
<h1>Switch mode</h1>
<p>
Switch editor mode using the mode box.
Note that the mode can be changed programmatically as well using the method
@ -38,18 +38,20 @@
<div id="jsoneditor"></div>
<script>
var container = document.getElementById('jsoneditor');
var container = document.getElementById('jsoneditor')
var options = {
mode: 'tree',
modes: ['code', 'form', 'text', 'tree', 'view'], // allowed modes
modes: ['text', 'code', 'tree', 'form', 'view'], // allowed modes
onError: function (err) {
alert(err.toString());
alert(err.toString())
},
onModeChange: function (newMode, oldMode) {
console.log('Mode switched from', oldMode, 'to', newMode);
onChangeMode: function (mode, prevMode) {
console.log('Mode switched from', prevMode, 'to', mode)
}
};
}
var editor = jsoneditor(container, options)
var json = {
"array": [1, 2, 3],
@ -58,9 +60,9 @@
"number": 123,
"object": {"a": "b", "c": "d"},
"string": "Hello World"
};
}
var editor = new JSONEditor(container, options, json);
editor.set(json)
</script>
</body>
</html>

View File

@ -1,9 +1,9 @@
<!DOCTYPE HTML>
<html>
<head>
<title>JSONEditor | Load and save</title>
<meta charset="UTF-8">
<title>Load and save | JSONEditor</title>
<link href="../dist/jsoneditor.css" rel="stylesheet" type="text/css">
<script src="../dist/jsoneditor.js"></script>
<script src="https://bgrins.github.io/filereader.js/filereader.js"></script>
@ -38,36 +38,36 @@
<script>
// create the editor
var editor = new JSONEditor(document.getElementById('jsoneditor'));
var editor = jsoneditor(document.getElementById('jsoneditor'))
// Load a JSON document
FileReaderJS.setupInput(document.getElementById('loadDocument'), {
readAsDefault: 'Text',
on: {
load: function (event, file) {
editor.setText(event.target.result);
editor.setText(event.target.result)
}
}
});
})
// Save a JSON document
document.getElementById('saveDocument').onclick = function () {
// Save Dialog
fname = window.prompt("Save as...");
fname = window.prompt('Save as...')
// Check json extension in file name
if(fname.indexOf(".")==-1){
fname = fname + ".json";
}else{
if(fname.split('.').pop().toLowerCase() == "json"){
if (fname.indexOf('.') == -1) {
fname = fname + '.json'
} else {
if (fname.split('.').pop().toLowerCase() == 'json'){
// Nothing to do
}else{
fname = fname.split('.')[0] + ".json";
} else {
fname = fname.split('.')[0] + '.json'
}
}
var blob = new Blob([editor.getText()], {type: 'application/json;charset=utf-8'});
saveAs(blob, fname);
};
var blob = new Blob([editor.getText()], {type: 'application/json;charset=utf-8'})
saveAs(blob, fname)
}
</script>
</body>
</html>

View File

@ -1,18 +1,19 @@
<!DOCTYPE HTML>
<html>
<head>
<title>JSONEditor | Custom editable fields</title>
<meta charset="UTF-8">
<title>Read-only properties and values | JSONEditor</title>
<link href="../dist/jsoneditor.css" rel="stylesheet" type="text/css">
<script src="../dist/jsoneditor.js"></script>
<style type="text/css">
#jsoneditor {
width: 500px;
width: 300px;
}
</style>
</head>
<body>
<h1>Read-only properties and values</h1>
<p>
In this example:
</p>
@ -28,36 +29,33 @@
var container = document.getElementById('jsoneditor');
var options = {
onEditable: function (node) {
// node is an object like:
// {
// field: 'FIELD',
// value: 'VALUE',
// path: ['PATH', 'TO', 'NODE']
// }
switch (node.field) {
case '_id':
return false;
// all properties are editable except '_id' and 'name'
isPropertyEditable: function (path) {
if (path.length === 1 && path[0] === '_id' || path[0] === 'name') {
return false
}
case 'name':
return {
field: false,
value: true
};
return true
},
default:
return true;
// all values are editable except '_id'
isValueEditable: function (path) {
if (path.length === 1 && path[0] === '_id') {
return false
}
return true
}
}
};
var json = {
_id: 123456,
name: 'John',
age: 32
};
}
var editor = new JSONEditor(container, options, json);
var editor = jsoneditor(container, options)
editor.set(json)
</script>
</body>
</html>

View File

@ -1,9 +1,9 @@
<!DOCTYPE HTML>
<html>
<head>
<title>JSONEditor | Custom styling</title>
<meta charset="UTF-8">
<title>Custom styling | JSONEditor</title>
<link href="../dist/jsoneditor.css" rel="stylesheet" type="text/css">
<script src="../dist/jsoneditor.js"></script>
<style type="text/css">
@ -22,6 +22,8 @@
</head>
<body>
<h1>Custom styling</h1>
<p>
This example demonstrates how to customize the look of JSONEditor,
the editor below has a dark theme. Note that the example isn't worked
@ -33,10 +35,11 @@
<script>
// create the editor
var container = document.getElementById('jsoneditor');
var options = {
var editor = jsoneditor(document.getElementById('jsoneditor'), {
modes: ['text', 'tree']
};
})
// load json
var json = {
'array': [1, 2, 3],
'boolean': true,
@ -44,8 +47,8 @@
'number': 123,
'object': {'a': 'b', 'c': 'd'},
'string': 'Hello World'
};
var editor = new JSONEditor(container, options, json);
}
editor.set(json)
</script>
</body>
</html>

View File

@ -1,9 +1,9 @@
<!DOCTYPE HTML>
<html>
<head>
<title>JSONEditor | JSON schema validation</title>
<meta charset="UTF-8">
<title>JSON schema validation | JSONEditor</title>
<link href="../dist/jsoneditor.css" rel="stylesheet" type="text/css">
<script src="../dist/jsoneditor.js"></script>
<style type="text/css">
@ -50,22 +50,24 @@
}
},
"required": ["firstName", "lastName"]
};
}
var json = {
firstName: 'John',
lastName: 'Doe',
gender: null,
age: 28
};
}
var options = {
schema: schema
};
modes: ['code', 'tree']
}
// create the editor
var container = document.getElementById('jsoneditor');
var editor = new JSONEditor(container, options, json);
var container = document.getElementById('jsoneditor')
var editor = jsoneditor(container, options)
editor.setSchema(schema)
editor.set(json)
</script>
</body>
</html>

View File

@ -0,0 +1,63 @@
<!DOCTYPE HTML>
<html>
<head>
<meta charset="UTF-8">
<title>Custom Ace Editor | JSONEditor</title>
<!-- we use the minimalist jsoneditor, which doesn't have Ace Editor included -->
<script src="../dist/jsoneditor-minimalist.js"></script>
<!-- load your own instance of Ace Editor and all plugins that you need -->
<!-- jsoneditor requires ext-searchbox and mode-json -->
<script src="https://cdnjs.cloudflare.com/ajax/libs/ace/1.2.5/ace.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/ace/1.2.5/ext-searchbox.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/ace/1.2.5/mode-json.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/ace/1.2.5/theme-twilight.js"></script>
<style type="text/css">
#jsoneditor {
width: 500px;
height: 500px;
}
</style>
</head>
<body>
<h1>Custom Ace Editor</h1>
<p>
In this example, the we use the minimalist version of jsoneditor and load
and configure Ace editor our selves: we set a different theme and font size.
</p>
<div id="jsoneditor"></div>
<script>
// create the editor, set mode to 'code' (powered by ace editor)
var container = document.getElementById('jsoneditor')
var options = {
mode: 'code',
onLoadAce: function (aceEditor, container) {
// we can adjust configuration of the ace editor,
// or create a completely new instance of ace editor.
// let's set a custom theme and font size
aceEditor.setTheme('ace/theme/twilight')
aceEditor.setFontSize(16)
return aceEditor
}
}
var editor = jsoneditor(container, options)
var json = {
'array': [1, 2, 3],
'boolean': true,
'null': null,
'number': 123,
'object': {'a': 'b', 'c': 'd'},
'string': 'Hello World'
}
editor.set(json)
</script>
</body>
</html>

View File

@ -0,0 +1,64 @@
<!DOCTYPE HTML>
<html>
<head>
<meta charset="UTF-8">
<title>Synchronize two editors | JSONEditor</title>
<script src="../dist/jsoneditor.js"></script>
<style type="text/css">
body {
font-family: arial, sans-serif;
}
#jsoneditor1,
#jsoneditor2 {
display: inline-block;
width: 350px;
height: 500px;
margin-right: 24px;
}
code {
background: #e5e5e5;
}
</style>
</head>
<body>
<h1>Synchronize two editors</h1>
<p>
This example demonstrates how to keep two editors synchronized by listening for
the <code>onPatch</code> event. Other related events are <code>onChange</code> and <code>onChangeText</code>.
</p>
<div id="jsoneditor1"></div>
<div id="jsoneditor2"></div>
<script>
var json = {
'array': [1, 2, 3],
'boolean': true,
'null': null,
'number': 123,
'object': {'a': 'b', 'c': 'd'},
'string': 'Hello World'
}
var editor1 = jsoneditor(document.getElementById('jsoneditor1'), {
onPatch: function (patch, revert) {
editor2.patch(patch)
}
})
editor1.set(json)
var editor2 = jsoneditor(document.getElementById('jsoneditor2'), {
onPatch: function (patch, revert) {
editor1.patch(patch)
}
})
editor2.set(json)
</script>
</body>
</html>

View File

@ -0,0 +1,56 @@
<!DOCTYPE HTML>
<html>
<head>
<meta charset="UTF-8">
<title>Custom key bindings | JSONEditor</title>
<script src="../dist/jsoneditor.js"></script>
<style type="text/css">
body {
width: 100%;
max-width: 500px;
margin: 0;
padding: 10px;
box-sizing: border-box;
font-family: sans-serif;
}
h1 {
font-size: 120%;
}
</style>
</head>
<body>
<h1>Custom key bindings</h1>
<p>
In this example, the key bindings for <code>format</code> and <code>compact</code> are changed to respectively <code>Ctrl+Alt+1</code> and <code>Ctrl+Alt+2</code> instead of the default <code>Ctrl+\</code> and <code>Ctrl+Shift+\</code>.
</p>
<p>
It's important to define key bindings for both Windows and Mac: <code>Command+Option+1</code> and <code>Command+Option+2</code> in this case.
</p>
<div id="jsoneditor"></div>
<script>
// create the editor
var container = document.getElementById('jsoneditor')
var options = {
modes: ['code', 'text'],
keyBindings: {
compact: ['Ctrl+Alt+1', 'Command+Option+1'],
format: ['Ctrl+Alt+2', 'Command+Option+2']
}
}
var json = {
'array': [1, 2, 3],
'boolean': true,
'null': null,
'number': 123,
'object': {'a': 'b', 'c': 'd'},
'string': 'Hello World'
}
var editor = jsoneditor(container, options)
editor.set(json)
</script>
</body>
</html>

View File

@ -6,22 +6,31 @@ div.jsoneditor-menu {
div.jsoneditor-menu {
background-color: #4b4b4b;
}
div.jsoneditor-tree,
div.jsoneditor textarea.jsoneditor-text {
div.jsoneditor-tree-contents,
textarea.jsoneditor-text {
background-color: #666666;
color: #ffffff;
}
div.jsoneditor-field,
.jsoneditor-menu button.jsoneditor-undo:disabled {
background-position: -24px -96px;
}
.jsoneditor-menu button.jsoneditor-redo:disabled {
background-position: -48px -96px;
}
div.jsoneditor-property,
div.jsoneditor-value {
color: #ffffff;
}
table.jsoneditor-search div.jsoneditor-frame {
background: #808080;
.jsoneditor-property:hover,
.jsoneditor-value:hover {
background-color: #808080;
}
tr.jsoneditor-highlight,
tr.jsoneditor-selected {
background-color: #808080;
.jsoneditor-readonly:hover {
background-color: transparent;
}
div.jsoneditor-field[contenteditable=true]:focus,
@ -31,21 +40,17 @@ div.jsoneditor-value[contenteditable=true]:hover,
div.jsoneditor-field.jsoneditor-highlight,
div.jsoneditor-value.jsoneditor-highlight {
background-color: #808080;
border-color: #808080;
}
div.jsoneditor-field.highlight-active,
div.jsoneditor-field.highlight-active:focus,
div.jsoneditor-field.highlight-active:hover,
div.jsoneditor-value.highlight-active,
div.jsoneditor-value.highlight-active:focus,
div.jsoneditor-value.highlight-active:hover {
background-color: #b1b1b1;
border-color: #b1b1b1;
button.jsoneditor-button.jsoneditor-actionmenu {
background-position: -50px -50px;
}
div.jsoneditor-tree button:focus {
background-color: #868686;
button.jsoneditor-button:hover,
button.jsoneditor-button:focus,
button.jsoneditor-button:active {
background-color: #808080;
outline: #808080 solid 1px;
}
/* coloring of JSON in tree mode */
@ -56,7 +61,7 @@ div.jsoneditor td.jsoneditor-separator {
color: #acacac;
}
div.jsoneditor-value.jsoneditor-string {
color: #00ff88;
color: #8bb54b;
}
div.jsoneditor-value.jsoneditor-object,
div.jsoneditor-value.jsoneditor-array {
@ -69,7 +74,7 @@ div.jsoneditor-value.jsoneditor-boolean {
color: #ff8048;
}
div.jsoneditor-value.jsoneditor-null {
color: #49a7fc;
color: #3B8EEA;
}
div.jsoneditor-value.jsoneditor-invalid {
color: white;

17
examples/react_demo/.gitignore vendored Normal file
View File

@ -0,0 +1,17 @@
# See http://help.github.com/ignore-files/ for more about ignoring files.
# dependencies
node_modules
# testing
coverage
# production
build
# misc
.DS_Store
.env
npm-debug.log
.vscode
.idea

File diff suppressed because it is too large Load Diff

24607
examples/react_demo/package-lock.json generated Normal file

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,19 @@
{
"name": "react_demo",
"version": "0.1.0",
"private": true,
"devDependencies": {
"react-scripts": "1.1.4"
},
"dependencies": {
"jsoneditor": "file:../..",
"react": "16.4.2",
"react-dom": "16.4.2"
},
"scripts": {
"start": "react-scripts start",
"build": "react-scripts build",
"test": "react-scripts test --env=jsdom",
"eject": "react-scripts eject"
}
}

View File

@ -0,0 +1,12 @@
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>React demo | JSONEditor</title>
</head>
<body>
<div id="root"></div>
</body>
</html>

View File

@ -0,0 +1,7 @@
.app {
width: 100%;
max-width: 600px;
}
.app h1 {
font-size: 120%;
}

View File

@ -0,0 +1,47 @@
import React, { Component } from 'react'
import './App.css'
// Load the react version of JSONEditor
import JSONEditor from 'jsoneditor/react'
const json = {
'array': [1, 2, 3],
'boolean': true,
'null': null,
'number': 123,
'object': {'a': 'b', 'c': 'd'},
'string': 'Hello World'
}
class App extends Component {
state = {
json
}
render() {
return (
<div className="app">
<h1>JSONEditor React demo</h1>
<JSONEditor
mode="tree"
modes={['text', 'code', 'tree', 'form', 'view']}
json={this.state.json}
onChange={this.onChange}
onChangeText={this.onChangeText}
/>
</div>
)
}
onChange = (json) => {
console.log('onChange', json)
}
onChangeText = (text) => {
console.log('onChangeText', text)
this.setState({ text })
}
}
export default App

View File

@ -0,0 +1,5 @@
body {
margin: 0;
padding: 0;
font-family: sans-serif;
}

View File

@ -0,0 +1,9 @@
import React from 'react'
import ReactDOM from 'react-dom'
import App from './App'
import './index.css'
ReactDOM.render(
<App />,
document.getElementById('root')
)

View File

@ -8,8 +8,7 @@
height: 500px;
}
</style>
<link rel="stylesheet" type="text/css" href="../../dist/jsoneditor.css">
<script data-main="scripts/main" src="scripts/require.js"></script>
<script data-main="scripts/main" src="https://cdnjs.cloudflare.com/ajax/libs/require.js/2.3.2/require.min.js"></script>
</head>
<body>
<p>

View File

@ -1,8 +1,9 @@
var module = '../../../dist/jsoneditor';
require([module], function (JSONEditor) {
var module = '../../../dist/jsoneditor'
require([module], function (jsoneditor) {
// create the editor
var container = document.getElementById('jsoneditor');
var editor = new JSONEditor(container);
var container = document.getElementById('jsoneditor')
var editor = jsoneditor(container)
// set json
document.getElementById('setJSON').onclick = function () {
@ -13,13 +14,13 @@ require([module], function (JSONEditor) {
'number': 123,
'object': {'a': 'b', 'c': 'd'},
'string': 'Hello World'
};
editor.set(json);
};
}
editor.set(json)
}
// get json
document.getElementById('getJSON').onclick = function () {
var json = editor.get();
alert(JSON.stringify(json, null, 2));
};
});
var json = editor.get()
alert(JSON.stringify(json, null, 2))
}
})

View File

@ -1,36 +0,0 @@
/*
RequireJS 2.1.13 Copyright (c) 2010-2014, The Dojo Foundation All Rights Reserved.
Available via the MIT or new BSD license.
see: http://github.com/jrburke/requirejs for details
*/
var requirejs,require,define;
(function(ba){function G(b){return"[object Function]"===K.call(b)}function H(b){return"[object Array]"===K.call(b)}function v(b,c){if(b){var d;for(d=0;d<b.length&&(!b[d]||!c(b[d],d,b));d+=1);}}function T(b,c){if(b){var d;for(d=b.length-1;-1<d&&(!b[d]||!c(b[d],d,b));d-=1);}}function t(b,c){return fa.call(b,c)}function m(b,c){return t(b,c)&&b[c]}function B(b,c){for(var d in b)if(t(b,d)&&c(b[d],d))break}function U(b,c,d,e){c&&B(c,function(c,g){if(d||!t(b,g))e&&"object"===typeof c&&c&&!H(c)&&!G(c)&&!(c instanceof
RegExp)?(b[g]||(b[g]={}),U(b[g],c,d,e)):b[g]=c});return b}function u(b,c){return function(){return c.apply(b,arguments)}}function ca(b){throw b;}function da(b){if(!b)return b;var c=ba;v(b.split("."),function(b){c=c[b]});return c}function C(b,c,d,e){c=Error(c+"\nhttp://requirejs.org/docs/errors.html#"+b);c.requireType=b;c.requireModules=e;d&&(c.originalError=d);return c}function ga(b){function c(a,k,b){var f,l,c,d,e,g,i,p,k=k&&k.split("/"),h=j.map,n=h&&h["*"];if(a){a=a.split("/");l=a.length-1;j.nodeIdCompat&&
Q.test(a[l])&&(a[l]=a[l].replace(Q,""));"."===a[0].charAt(0)&&k&&(l=k.slice(0,k.length-1),a=l.concat(a));l=a;for(c=0;c<l.length;c++)if(d=l[c],"."===d)l.splice(c,1),c-=1;else if(".."===d&&!(0===c||1==c&&".."===l[2]||".."===l[c-1])&&0<c)l.splice(c-1,2),c-=2;a=a.join("/")}if(b&&h&&(k||n)){l=a.split("/");c=l.length;a:for(;0<c;c-=1){e=l.slice(0,c).join("/");if(k)for(d=k.length;0<d;d-=1)if(b=m(h,k.slice(0,d).join("/")))if(b=m(b,e)){f=b;g=c;break a}!i&&(n&&m(n,e))&&(i=m(n,e),p=c)}!f&&i&&(f=i,g=p);f&&(l.splice(0,
g,f),a=l.join("/"))}return(f=m(j.pkgs,a))?f:a}function d(a){z&&v(document.getElementsByTagName("script"),function(k){if(k.getAttribute("data-requiremodule")===a&&k.getAttribute("data-requirecontext")===i.contextName)return k.parentNode.removeChild(k),!0})}function e(a){var k=m(j.paths,a);if(k&&H(k)&&1<k.length)return k.shift(),i.require.undef(a),i.makeRequire(null,{skipMap:!0})([a]),!0}function n(a){var k,c=a?a.indexOf("!"):-1;-1<c&&(k=a.substring(0,c),a=a.substring(c+1,a.length));return[k,a]}function p(a,
k,b,f){var l,d,e=null,g=k?k.name:null,j=a,p=!0,h="";a||(p=!1,a="_@r"+(K+=1));a=n(a);e=a[0];a=a[1];e&&(e=c(e,g,f),d=m(r,e));a&&(e?h=d&&d.normalize?d.normalize(a,function(a){return c(a,g,f)}):c(a,g,f):(h=c(a,g,f),a=n(h),e=a[0],h=a[1],b=!0,l=i.nameToUrl(h)));b=e&&!d&&!b?"_unnormalized"+(O+=1):"";return{prefix:e,name:h,parentMap:k,unnormalized:!!b,url:l,originalName:j,isDefine:p,id:(e?e+"!"+h:h)+b}}function s(a){var k=a.id,b=m(h,k);b||(b=h[k]=new i.Module(a));return b}function q(a,k,b){var f=a.id,c=m(h,
f);if(t(r,f)&&(!c||c.defineEmitComplete))"defined"===k&&b(r[f]);else if(c=s(a),c.error&&"error"===k)b(c.error);else c.on(k,b)}function w(a,b){var c=a.requireModules,f=!1;if(b)b(a);else if(v(c,function(b){if(b=m(h,b))b.error=a,b.events.error&&(f=!0,b.emit("error",a))}),!f)g.onError(a)}function x(){R.length&&(ha.apply(A,[A.length,0].concat(R)),R=[])}function y(a){delete h[a];delete V[a]}function F(a,b,c){var f=a.map.id;a.error?a.emit("error",a.error):(b[f]=!0,v(a.depMaps,function(f,d){var e=f.id,g=
m(h,e);g&&(!a.depMatched[d]&&!c[e])&&(m(b,e)?(a.defineDep(d,r[e]),a.check()):F(g,b,c))}),c[f]=!0)}function D(){var a,b,c=(a=1E3*j.waitSeconds)&&i.startTime+a<(new Date).getTime(),f=[],l=[],g=!1,h=!0;if(!W){W=!0;B(V,function(a){var i=a.map,j=i.id;if(a.enabled&&(i.isDefine||l.push(a),!a.error))if(!a.inited&&c)e(j)?g=b=!0:(f.push(j),d(j));else if(!a.inited&&(a.fetched&&i.isDefine)&&(g=!0,!i.prefix))return h=!1});if(c&&f.length)return a=C("timeout","Load timeout for modules: "+f,null,f),a.contextName=
i.contextName,w(a);h&&v(l,function(a){F(a,{},{})});if((!c||b)&&g)if((z||ea)&&!X)X=setTimeout(function(){X=0;D()},50);W=!1}}function E(a){t(r,a[0])||s(p(a[0],null,!0)).init(a[1],a[2])}function I(a){var a=a.currentTarget||a.srcElement,b=i.onScriptLoad;a.detachEvent&&!Y?a.detachEvent("onreadystatechange",b):a.removeEventListener("load",b,!1);b=i.onScriptError;(!a.detachEvent||Y)&&a.removeEventListener("error",b,!1);return{node:a,id:a&&a.getAttribute("data-requiremodule")}}function J(){var a;for(x();A.length;){a=
A.shift();if(null===a[0])return w(C("mismatch","Mismatched anonymous define() module: "+a[a.length-1]));E(a)}}var W,Z,i,L,X,j={waitSeconds:7,baseUrl:"./",paths:{},bundles:{},pkgs:{},shim:{},config:{}},h={},V={},$={},A=[],r={},S={},aa={},K=1,O=1;L={require:function(a){return a.require?a.require:a.require=i.makeRequire(a.map)},exports:function(a){a.usingExports=!0;if(a.map.isDefine)return a.exports?r[a.map.id]=a.exports:a.exports=r[a.map.id]={}},module:function(a){return a.module?a.module:a.module=
{id:a.map.id,uri:a.map.url,config:function(){return m(j.config,a.map.id)||{}},exports:a.exports||(a.exports={})}}};Z=function(a){this.events=m($,a.id)||{};this.map=a;this.shim=m(j.shim,a.id);this.depExports=[];this.depMaps=[];this.depMatched=[];this.pluginMaps={};this.depCount=0};Z.prototype={init:function(a,b,c,f){f=f||{};if(!this.inited){this.factory=b;if(c)this.on("error",c);else this.events.error&&(c=u(this,function(a){this.emit("error",a)}));this.depMaps=a&&a.slice(0);this.errback=c;this.inited=
!0;this.ignore=f.ignore;f.enabled||this.enabled?this.enable():this.check()}},defineDep:function(a,b){this.depMatched[a]||(this.depMatched[a]=!0,this.depCount-=1,this.depExports[a]=b)},fetch:function(){if(!this.fetched){this.fetched=!0;i.startTime=(new Date).getTime();var a=this.map;if(this.shim)i.makeRequire(this.map,{enableBuildCallback:!0})(this.shim.deps||[],u(this,function(){return a.prefix?this.callPlugin():this.load()}));else return a.prefix?this.callPlugin():this.load()}},load:function(){var a=
this.map.url;S[a]||(S[a]=!0,i.load(this.map.id,a))},check:function(){if(this.enabled&&!this.enabling){var a,b,c=this.map.id;b=this.depExports;var f=this.exports,l=this.factory;if(this.inited)if(this.error)this.emit("error",this.error);else{if(!this.defining){this.defining=!0;if(1>this.depCount&&!this.defined){if(G(l)){if(this.events.error&&this.map.isDefine||g.onError!==ca)try{f=i.execCb(c,l,b,f)}catch(d){a=d}else f=i.execCb(c,l,b,f);this.map.isDefine&&void 0===f&&((b=this.module)?f=b.exports:this.usingExports&&
(f=this.exports));if(a)return a.requireMap=this.map,a.requireModules=this.map.isDefine?[this.map.id]:null,a.requireType=this.map.isDefine?"define":"require",w(this.error=a)}else f=l;this.exports=f;if(this.map.isDefine&&!this.ignore&&(r[c]=f,g.onResourceLoad))g.onResourceLoad(i,this.map,this.depMaps);y(c);this.defined=!0}this.defining=!1;this.defined&&!this.defineEmitted&&(this.defineEmitted=!0,this.emit("defined",this.exports),this.defineEmitComplete=!0)}}else this.fetch()}},callPlugin:function(){var a=
this.map,b=a.id,d=p(a.prefix);this.depMaps.push(d);q(d,"defined",u(this,function(f){var l,d;d=m(aa,this.map.id);var e=this.map.name,P=this.map.parentMap?this.map.parentMap.name:null,n=i.makeRequire(a.parentMap,{enableBuildCallback:!0});if(this.map.unnormalized){if(f.normalize&&(e=f.normalize(e,function(a){return c(a,P,!0)})||""),f=p(a.prefix+"!"+e,this.map.parentMap),q(f,"defined",u(this,function(a){this.init([],function(){return a},null,{enabled:!0,ignore:!0})})),d=m(h,f.id)){this.depMaps.push(f);
if(this.events.error)d.on("error",u(this,function(a){this.emit("error",a)}));d.enable()}}else d?(this.map.url=i.nameToUrl(d),this.load()):(l=u(this,function(a){this.init([],function(){return a},null,{enabled:!0})}),l.error=u(this,function(a){this.inited=!0;this.error=a;a.requireModules=[b];B(h,function(a){0===a.map.id.indexOf(b+"_unnormalized")&&y(a.map.id)});w(a)}),l.fromText=u(this,function(f,c){var d=a.name,e=p(d),P=M;c&&(f=c);P&&(M=!1);s(e);t(j.config,b)&&(j.config[d]=j.config[b]);try{g.exec(f)}catch(h){return w(C("fromtexteval",
"fromText eval for "+b+" failed: "+h,h,[b]))}P&&(M=!0);this.depMaps.push(e);i.completeLoad(d);n([d],l)}),f.load(a.name,n,l,j))}));i.enable(d,this);this.pluginMaps[d.id]=d},enable:function(){V[this.map.id]=this;this.enabling=this.enabled=!0;v(this.depMaps,u(this,function(a,b){var c,f;if("string"===typeof a){a=p(a,this.map.isDefine?this.map:this.map.parentMap,!1,!this.skipMap);this.depMaps[b]=a;if(c=m(L,a.id)){this.depExports[b]=c(this);return}this.depCount+=1;q(a,"defined",u(this,function(a){this.defineDep(b,
a);this.check()}));this.errback&&q(a,"error",u(this,this.errback))}c=a.id;f=h[c];!t(L,c)&&(f&&!f.enabled)&&i.enable(a,this)}));B(this.pluginMaps,u(this,function(a){var b=m(h,a.id);b&&!b.enabled&&i.enable(a,this)}));this.enabling=!1;this.check()},on:function(a,b){var c=this.events[a];c||(c=this.events[a]=[]);c.push(b)},emit:function(a,b){v(this.events[a],function(a){a(b)});"error"===a&&delete this.events[a]}};i={config:j,contextName:b,registry:h,defined:r,urlFetched:S,defQueue:A,Module:Z,makeModuleMap:p,
nextTick:g.nextTick,onError:w,configure:function(a){a.baseUrl&&"/"!==a.baseUrl.charAt(a.baseUrl.length-1)&&(a.baseUrl+="/");var b=j.shim,c={paths:!0,bundles:!0,config:!0,map:!0};B(a,function(a,b){c[b]?(j[b]||(j[b]={}),U(j[b],a,!0,!0)):j[b]=a});a.bundles&&B(a.bundles,function(a,b){v(a,function(a){a!==b&&(aa[a]=b)})});a.shim&&(B(a.shim,function(a,c){H(a)&&(a={deps:a});if((a.exports||a.init)&&!a.exportsFn)a.exportsFn=i.makeShimExports(a);b[c]=a}),j.shim=b);a.packages&&v(a.packages,function(a){var b,
a="string"===typeof a?{name:a}:a;b=a.name;a.location&&(j.paths[b]=a.location);j.pkgs[b]=a.name+"/"+(a.main||"main").replace(ia,"").replace(Q,"")});B(h,function(a,b){!a.inited&&!a.map.unnormalized&&(a.map=p(b))});if(a.deps||a.callback)i.require(a.deps||[],a.callback)},makeShimExports:function(a){return function(){var b;a.init&&(b=a.init.apply(ba,arguments));return b||a.exports&&da(a.exports)}},makeRequire:function(a,e){function j(c,d,m){var n,q;e.enableBuildCallback&&(d&&G(d))&&(d.__requireJsBuild=
!0);if("string"===typeof c){if(G(d))return w(C("requireargs","Invalid require call"),m);if(a&&t(L,c))return L[c](h[a.id]);if(g.get)return g.get(i,c,a,j);n=p(c,a,!1,!0);n=n.id;return!t(r,n)?w(C("notloaded",'Module name "'+n+'" has not been loaded yet for context: '+b+(a?"":". Use require([])"))):r[n]}J();i.nextTick(function(){J();q=s(p(null,a));q.skipMap=e.skipMap;q.init(c,d,m,{enabled:!0});D()});return j}e=e||{};U(j,{isBrowser:z,toUrl:function(b){var d,e=b.lastIndexOf("."),k=b.split("/")[0];if(-1!==
e&&(!("."===k||".."===k)||1<e))d=b.substring(e,b.length),b=b.substring(0,e);return i.nameToUrl(c(b,a&&a.id,!0),d,!0)},defined:function(b){return t(r,p(b,a,!1,!0).id)},specified:function(b){b=p(b,a,!1,!0).id;return t(r,b)||t(h,b)}});a||(j.undef=function(b){x();var c=p(b,a,!0),e=m(h,b);d(b);delete r[b];delete S[c.url];delete $[b];T(A,function(a,c){a[0]===b&&A.splice(c,1)});e&&(e.events.defined&&($[b]=e.events),y(b))});return j},enable:function(a){m(h,a.id)&&s(a).enable()},completeLoad:function(a){var b,
c,d=m(j.shim,a)||{},g=d.exports;for(x();A.length;){c=A.shift();if(null===c[0]){c[0]=a;if(b)break;b=!0}else c[0]===a&&(b=!0);E(c)}c=m(h,a);if(!b&&!t(r,a)&&c&&!c.inited){if(j.enforceDefine&&(!g||!da(g)))return e(a)?void 0:w(C("nodefine","No define call for "+a,null,[a]));E([a,d.deps||[],d.exportsFn])}D()},nameToUrl:function(a,b,c){var d,e,h;(d=m(j.pkgs,a))&&(a=d);if(d=m(aa,a))return i.nameToUrl(d,b,c);if(g.jsExtRegExp.test(a))d=a+(b||"");else{d=j.paths;a=a.split("/");for(e=a.length;0<e;e-=1)if(h=a.slice(0,
e).join("/"),h=m(d,h)){H(h)&&(h=h[0]);a.splice(0,e,h);break}d=a.join("/");d+=b||(/^data\:|\?/.test(d)||c?"":".js");d=("/"===d.charAt(0)||d.match(/^[\w\+\.\-]+:/)?"":j.baseUrl)+d}return j.urlArgs?d+((-1===d.indexOf("?")?"?":"&")+j.urlArgs):d},load:function(a,b){g.load(i,a,b)},execCb:function(a,b,c,d){return b.apply(d,c)},onScriptLoad:function(a){if("load"===a.type||ja.test((a.currentTarget||a.srcElement).readyState))N=null,a=I(a),i.completeLoad(a.id)},onScriptError:function(a){var b=I(a);if(!e(b.id))return w(C("scripterror",
"Script error for: "+b.id,a,[b.id]))}};i.require=i.makeRequire();return i}var g,x,y,D,I,E,N,J,s,O,ka=/(\/\*([\s\S]*?)\*\/|([^:]|^)\/\/(.*)$)/mg,la=/[^.]\s*require\s*\(\s*["']([^'"\s]+)["']\s*\)/g,Q=/\.js$/,ia=/^\.\//;x=Object.prototype;var K=x.toString,fa=x.hasOwnProperty,ha=Array.prototype.splice,z=!!("undefined"!==typeof window&&"undefined"!==typeof navigator&&window.document),ea=!z&&"undefined"!==typeof importScripts,ja=z&&"PLAYSTATION 3"===navigator.platform?/^complete$/:/^(complete|loaded)$/,
Y="undefined"!==typeof opera&&"[object Opera]"===opera.toString(),F={},q={},R=[],M=!1;if("undefined"===typeof define){if("undefined"!==typeof requirejs){if(G(requirejs))return;q=requirejs;requirejs=void 0}"undefined"!==typeof require&&!G(require)&&(q=require,require=void 0);g=requirejs=function(b,c,d,e){var n,p="_";!H(b)&&"string"!==typeof b&&(n=b,H(c)?(b=c,c=d,d=e):b=[]);n&&n.context&&(p=n.context);(e=m(F,p))||(e=F[p]=g.s.newContext(p));n&&e.configure(n);return e.require(b,c,d)};g.config=function(b){return g(b)};
g.nextTick="undefined"!==typeof setTimeout?function(b){setTimeout(b,4)}:function(b){b()};require||(require=g);g.version="2.1.13";g.jsExtRegExp=/^\/|:|\?|\.js$/;g.isBrowser=z;x=g.s={contexts:F,newContext:ga};g({});v(["toUrl","undef","defined","specified"],function(b){g[b]=function(){var c=F._;return c.require[b].apply(c,arguments)}});if(z&&(y=x.head=document.getElementsByTagName("head")[0],D=document.getElementsByTagName("base")[0]))y=x.head=D.parentNode;g.onError=ca;g.createNode=function(b){var c=
b.xhtml?document.createElementNS("http://www.w3.org/1999/xhtml","html:script"):document.createElement("script");c.type=b.scriptType||"text/javascript";c.charset="utf-8";c.async=!0;return c};g.load=function(b,c,d){var e=b&&b.config||{};if(z)return e=g.createNode(e,c,d),e.setAttribute("data-requirecontext",b.contextName),e.setAttribute("data-requiremodule",c),e.attachEvent&&!(e.attachEvent.toString&&0>e.attachEvent.toString().indexOf("[native code"))&&!Y?(M=!0,e.attachEvent("onreadystatechange",b.onScriptLoad)):
(e.addEventListener("load",b.onScriptLoad,!1),e.addEventListener("error",b.onScriptError,!1)),e.src=d,J=e,D?y.insertBefore(e,D):y.appendChild(e),J=null,e;if(ea)try{importScripts(d),b.completeLoad(c)}catch(m){b.onError(C("importscripts","importScripts failed for "+c+" at "+d,m,[c]))}};z&&!q.skipDataMain&&T(document.getElementsByTagName("script"),function(b){y||(y=b.parentNode);if(I=b.getAttribute("data-main"))return s=I,q.baseUrl||(E=s.split("/"),s=E.pop(),O=E.length?E.join("/")+"/":"./",q.baseUrl=
O),s=s.replace(Q,""),g.jsExtRegExp.test(s)&&(s=I),q.deps=q.deps?q.deps.concat(s):[s],!0});define=function(b,c,d){var e,g;"string"!==typeof b&&(d=c,c=b,b=null);H(c)||(d=c,c=null);!c&&G(d)&&(c=[],d.length&&(d.toString().replace(ka,"").replace(la,function(b,d){c.push(d)}),c=(1===d.length?["require"]:["require","exports","module"]).concat(c)));if(M){if(!(e=J))N&&"interactive"===N.readyState||T(document.getElementsByTagName("script"),function(b){if("interactive"===b.readyState)return N=b}),e=N;e&&(b||
(b=e.getAttribute("data-requiremodule")),g=F[e.getAttribute("data-requirecontext")])}(g?g.defQueue:R).push([b,c,d])};define.amd={jQuery:!0};g.exec=function(b){return eval(b)};g(q)}})(this);

View File

@ -1,188 +0,0 @@
var fs = require('fs');
var gulp = require('gulp');
var gutil = require('gulp-util');
var concatCss = require('gulp-concat-css');
var minifyCSS = require('gulp-clean-css');
var shell = require('gulp-shell');
var mkdirp = require('mkdirp');
var webpack = require('webpack');
var uglify = require('uglify-js');
var NAME = 'jsoneditor';
var NAME_MINIMALIST = 'jsoneditor-minimalist';
var ENTRY = './src/js/JSONEditor.js';
var HEADER = './src/js/header.js';
var IMAGE = './src/css/img/jsoneditor-icons.svg';
var DOCS = './src/docs/*';
var DIST = './dist';
// generate banner with today's date and correct version
function createBanner() {
var today = gutil.date(new Date(), 'yyyy-mm-dd'); // today, formatted as yyyy-mm-dd
var version = require('./package.json').version; // math.js version
return String(fs.readFileSync(HEADER))
.replace('@@date', today)
.replace('@@version', version);
}
var bannerPlugin = new webpack.BannerPlugin(createBanner(), {
entryOnly: true,
raw: true
});
// create a single instance of the compiler to allow caching
var compiler = webpack({
entry: ENTRY,
output: {
library: 'JSONEditor',
libraryTarget: 'umd',
path: DIST,
filename: NAME + '.js'
},
plugins: [ bannerPlugin ],
module: {
loaders: [
{ test: /\.json$/, loader: "json" }
]
},
cache: true
});
// create a single instance of the compiler to allow caching
var compilerMinimalist = webpack({
entry: ENTRY,
output: {
library: 'JSONEditor',
libraryTarget: 'umd',
path: DIST,
filename: NAME_MINIMALIST + '.js'
},
plugins: [
bannerPlugin,
new webpack.IgnorePlugin(new RegExp('^brace$')),
new webpack.IgnorePlugin(new RegExp('^ajv'))
],
cache: true
});
function minify(name) {
var result = uglify.minify([DIST + '/' + name + '.js'], {
outSourceMap: name + '.map',
output: {
comments: /@license/
}
});
var fileMin = DIST + '/' + name + '.min.js';
var fileMap = DIST + '/' + name + '.map';
fs.writeFileSync(fileMin, result.code);
fs.writeFileSync(fileMap, result.map);
gutil.log('Minified ' + fileMin);
gutil.log('Mapped ' + fileMap);
}
// make dist and dist/img folders
gulp.task('mkdir', function () {
mkdirp.sync(DIST);
mkdirp.sync(DIST + '/img');
});
// bundle javascript
gulp.task('bundle', ['mkdir'], function (done) {
// update the banner contents (has a date in it which should stay up to date)
bannerPlugin.banner = createBanner();
compiler.run(function (err, stats) {
if (err) {
gutil.log(err);
}
gutil.log('bundled ' + NAME + '.js');
done();
});
});
// bundle minimalist version of javascript
gulp.task('bundle-minimalist', ['mkdir'], function (done) {
// update the banner contents (has a date in it which should stay up to date)
bannerPlugin.banner = createBanner();
compilerMinimalist.run(function (err, stats) {
if (err) {
gutil.log(err);
}
gutil.log('bundled ' + NAME_MINIMALIST + '.js');
done();
});
});
// bundle css
gulp.task('bundle-css', ['mkdir'], function () {
gulp.src([
'src/css/reset.css',
'src/css/jsoneditor.css',
'src/css/contextmenu.css',
'src/css/menu.css',
'src/css/searchbox.css'
])
.pipe(concatCss(NAME + '.css'))
.pipe(gulp.dest(DIST))
.pipe(concatCss(NAME + '.min.css'))
.pipe(minifyCSS())
.pipe(gulp.dest(DIST));
gutil.log('bundled ' + DIST + '/' + NAME + '.css');
gutil.log('bundled ' + DIST + '/' + NAME + '.min.css');
});
// create a folder img and copy the icons
gulp.task('copy-img', ['mkdir'], function () {
gulp.src(IMAGE)
.pipe(gulp.dest(DIST +'/img'));
gutil.log('Copied images');
});
// create a folder img and copy the icons
gulp.task('copy-docs', ['mkdir'], function () {
gulp.src(DOCS)
.pipe(gulp.dest(DIST));
gutil.log('Copied doc');
});
gulp.task('minify', ['bundle'], function () {
minify(NAME)
});
gulp.task('minify-minimalist', ['bundle-minimalist'], function () {
minify(NAME_MINIMALIST)
});
// TODO: zip file using archiver
var pkg = 'jsoneditor-' + require('./package.json').version + '.zip';
gulp.task('zip', shell.task([
'zip ' + pkg + ' ' + 'README.md NOTICE LICENSE HISTORY.md index.html src dist docs examples -r '
]));
// The watch task (to automatically rebuild when the source code changes)
// Does only generate jsoneditor.js and jsoneditor.css, and copy the image
// Does NOT minify the code and does NOT generate the minimalist version
gulp.task('watch', ['bundle', 'bundle-css', 'copy-img'], function () {
gulp.watch(['src/**/*'], ['bundle', 'bundle-css', 'copy-img']);
});
// The default task (called when you run `gulp`)
gulp.task('default', [
'bundle',
'bundle-minimalist',
'bundle-css',
'copy-img',
'copy-docs',
'minify',
'minify-minimalist'
]);

View File

@ -1 +1 @@
module.exports = require('./src/js/JSONEditor');
module.exports = require('./dist/jsoneditor')

18157
package-lock.json generated Normal file

File diff suppressed because it is too large Load Diff

View File

@ -1,7 +1,7 @@
{
"name": "jsoneditor",
"version": "5.5.6",
"main": "./index",
"version": "6.0.0-BETA",
"main": "./index.js",
"description": "A web-based tool to view, edit, format, and validate JSON",
"tags": [
"json",
@ -10,33 +10,78 @@
"formatter"
],
"author": "Jos de Jong <wjosdejong@gmail.com>",
"license": "Apache-2.0",
"license": "MIT",
"homepage": "https://github.com/josdejong/jsoneditor",
"repository": {
"type": "git",
"url": "https://github.com/josdejong/jsoneditor.git"
},
"bugs": "https://github.com/josdejong/jsoneditor/issues",
"scripts": {
"build": "gulp",
"watch": "gulp watch",
"test": "mocha test"
},
"private": false,
"dependencies": {
"ajv": "3.8.8",
"brace": "0.8.0",
"javascript-natural-sort": "0.7.1"
"@fortawesome/fontawesome": "1.1.8",
"@fortawesome/fontawesome-free-solid": "5.0.13",
"ajv": "6.12.2",
"brace": "0.11.1",
"javascript-natural-sort": "0.7.1",
"jest": "24.9.0",
"lodash": "4.17.15",
"mitt": "1.2.0",
"prop-types": "15.7.2",
"react-hammerjs": "1.0.1"
},
"peerDependencies": {
"react": "^16.13.1",
"react-dom": "^16.13.1"
},
"devDependencies": {
"gulp": "3.9.1",
"gulp-clean-css": "2.0.5",
"gulp-concat-css": "2.2.0",
"gulp-shell": "0.5.2",
"gulp-util": "3.0.7",
"json-loader": "0.5.4",
"mkdirp": "0.5.1",
"mocha": "2.4.5",
"uglify-js": "2.6.2",
"webpack": "1.12.14"
"babel-cli": "6.26.0",
"babel-plugin-external-helpers": "6.22.0",
"babel-plugin-transform-class-properties": "6.24.1",
"babel-plugin-transform-object-rest-spread": "6.26.0",
"babel-plugin-transform-react-jsx": "6.24.1",
"babel-preset-env": "1.7.0",
"console.table": "0.10.0",
"cpy-cli": "3.1.1",
"css-loader": "3.5.3",
"npm-run-all": "4.1.5",
"node-sass-chokidar": "1.4.0",
"preact": "10.4.4",
"preact-compat": "3.19.0",
"react": "16.13.1",
"react-dom": "16.13.1",
"react-scripts": "3.4.1",
"rollup": "2.12.1",
"rollup-plugin-babel": "4.4.0",
"rollup-plugin-commonjs": "10.1.0",
"rollup-plugin-node-resolve": "5.2.0",
"rollup-plugin-postcss": "3.1.1",
"svg-url-loader": "6.0.0"
},
"scripts": {
"start": "npm-run-all -p watch-css start-js",
"build-css": "node-sass-chokidar src/ -o src/",
"watch-css": "npm run build-css && node-sass-chokidar src/ -o src/ --watch --recursive",
"start-js": "react-scripts start",
"build-js": "react-scripts build",
"copy-css-lib": "cpy '**/*.css' '**/*.svg' '../../lib' --cwd='src/jsoneditor' --parents",
"build-js-lib": "babel src/jsoneditor --out-dir lib --ignore spec.js,test.js",
"build-js-bundle": "webpack --config ./config/webpack.config.js",
"build-js-minimalist": "webpack --config ./config/webpack.config.minimalist.js",
"build": "npm-run-all build-css copy-css-lib build-js-lib build-js-bundle build-js-minimalist",
"test": "react-scripts test --env=jsdom",
"eject": "react-scripts eject"
},
"browserslist": {
"production": [
">0.2%",
"not dead",
"not op_mini all"
],
"development": [
"last 1 chrome version",
"last 1 firefox version",
"last 1 safari version"
]
}
}

BIN
public/favicon.ico Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

40
public/index.html Normal file
View File

@ -0,0 +1,40 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
<meta name="theme-color" content="#000000">
<!--
manifest.json provides metadata used when your web app is added to the
homescreen on Android. See https://developers.google.com/web/fundamentals/engage-and-retain/web-app-manifest/
-->
<link rel="manifest" href="%PUBLIC_URL%/manifest.json">
<link rel="shortcut icon" href="%PUBLIC_URL%/favicon.ico">
<!--
Notice the use of %PUBLIC_URL% in the tags above.
It will be replaced with the URL of the `public` folder during the build.
Only files inside the `public` folder can be referenced from the HTML.
Unlike "/favicon.ico" or "favicon.ico", "%PUBLIC_URL%/favicon.ico" will
work correctly both with client-side routing and a non-root public URL.
Learn how to configure a non-root public URL by running `npm run build`.
-->
<title>JSONEditor demo</title>
</head>
<body>
<noscript>
You need to enable JavaScript to run this app.
</noscript>
<div id="root"></div>
<!--
This HTML file is a template.
If you open it directly in the browser, you will see an empty page.
You can add webfonts, meta tags, or analytics to this file.
The build step will place the bundled scripts into the <body> tag.
To begin the development, run `npm start` or `yarn start`.
To create a production bundle, use `npm run build` or `yarn build`.
-->
</body>
</html>

15
public/manifest.json Normal file
View File

@ -0,0 +1,15 @@
{
"short_name": "JSONEditor",
"name": "JSONEditor demo",
"icons": [
{
"src": "favicon.ico",
"sizes": "64x64 32x32 24x24 16x16",
"type": "image/x-icon"
}
],
"start_url": "./index.html",
"display": "standalone",
"theme_color": "#3883fa",
"background_color": "#ffffff"
}

1
react.js vendored Normal file
View File

@ -0,0 +1 @@
module.exports = require('./lib/index.react')

View File

@ -1,244 +0,0 @@
/* ContextMenu - main menu */
div.jsoneditor-contextmenu-root {
position: relative;
width: 0;
height: 0;
}
div.jsoneditor-contextmenu {
position: absolute;
box-sizing: content-box;
z-index: 99999;
}
div.jsoneditor-contextmenu ul,
div.jsoneditor-contextmenu li {
box-sizing: content-box;
}
div.jsoneditor-contextmenu ul {
position: relative;
left: 0;
top: 0;
width: 124px;
background: white;
border: 1px solid #d3d3d3;
box-shadow: 2px 2px 12px rgba(128, 128, 128, 0.3);
list-style: none;
margin: 0;
padding: 0;
}
div.jsoneditor-contextmenu ul li button {
padding: 0;
margin: 0;
width: 124px;
height: 24px;
border: none;
cursor: pointer;
color: #4d4d4d;
background: transparent;
font-size: 10pt;
font-family: arial, sans-serif;
box-sizing: border-box;
line-height: 26px;
text-align: left;
}
/* Fix button padding in firefox */
div.jsoneditor-contextmenu ul li button::-moz-focus-inner {
padding: 0;
border: 0;
}
div.jsoneditor-contextmenu ul li button:hover,
div.jsoneditor-contextmenu ul li button:focus {
color: #1a1a1a;
background-color: #f5f5f5;
outline: none;
}
div.jsoneditor-contextmenu ul li button.jsoneditor-default {
width: 92px;
}
div.jsoneditor-contextmenu ul li button.jsoneditor-expand {
float: right;
width: 32px;
height: 24px;
border-left: 1px solid #e5e5e5;
}
div.jsoneditor-contextmenu div.jsoneditor-icon {
float: left;
width: 24px;
height: 24px;
border: none;
padding: 0;
margin: 0;
background-image: url('img/jsoneditor-icons.svg');
}
div.jsoneditor-contextmenu ul li button div.jsoneditor-expand {
float: right;
width: 24px;
height: 24px;
padding: 0;
margin: 0 4px 0 0;
background: url('img/jsoneditor-icons.svg') 0 -72px;
opacity: 0.4;
}
div.jsoneditor-contextmenu ul li button:hover div.jsoneditor-expand,
div.jsoneditor-contextmenu ul li button:focus div.jsoneditor-expand,
div.jsoneditor-contextmenu ul li.jsoneditor-selected div.jsoneditor-expand,
div.jsoneditor-contextmenu ul li button.jsoneditor-expand:hover div.jsoneditor-expand,
div.jsoneditor-contextmenu ul li button.jsoneditor-expand:focus div.jsoneditor-expand {
opacity: 1;
}
div.jsoneditor-contextmenu div.jsoneditor-separator {
height: 0;
border-top: 1px solid #e5e5e5;
padding-top: 5px;
margin-top: 5px;
}
div.jsoneditor-contextmenu button.jsoneditor-remove > div.jsoneditor-icon {
background-position: -24px -24px;
}
div.jsoneditor-contextmenu button.jsoneditor-remove:hover > div.jsoneditor-icon,
div.jsoneditor-contextmenu button.jsoneditor-remove:focus > div.jsoneditor-icon {
background-position: -24px 0;
}
div.jsoneditor-contextmenu button.jsoneditor-append > div.jsoneditor-icon {
background-position: 0 -24px;
}
div.jsoneditor-contextmenu button.jsoneditor-append:hover > div.jsoneditor-icon,
div.jsoneditor-contextmenu button.jsoneditor-append:focus > div.jsoneditor-icon {
background-position: 0 0;
}
div.jsoneditor-contextmenu button.jsoneditor-insert > div.jsoneditor-icon {
background-position: 0 -24px;
}
div.jsoneditor-contextmenu button.jsoneditor-insert:hover > div.jsoneditor-icon,
div.jsoneditor-contextmenu button.jsoneditor-insert:focus > div.jsoneditor-icon {
background-position: 0 0;
}
div.jsoneditor-contextmenu button.jsoneditor-duplicate > div.jsoneditor-icon {
background-position: -48px -24px;
}
div.jsoneditor-contextmenu button.jsoneditor-duplicate:hover > div.jsoneditor-icon,
div.jsoneditor-contextmenu button.jsoneditor-duplicate:focus > div.jsoneditor-icon {
background-position: -48px 0;
}
div.jsoneditor-contextmenu button.jsoneditor-sort-asc > div.jsoneditor-icon {
background-position: -168px -24px;
}
div.jsoneditor-contextmenu button.jsoneditor-sort-asc:hover > div.jsoneditor-icon,
div.jsoneditor-contextmenu button.jsoneditor-sort-asc:focus > div.jsoneditor-icon {
background-position: -168px 0;
}
div.jsoneditor-contextmenu button.jsoneditor-sort-desc > div.jsoneditor-icon {
background-position: -192px -24px;
}
div.jsoneditor-contextmenu button.jsoneditor-sort-desc:hover > div.jsoneditor-icon,
div.jsoneditor-contextmenu button.jsoneditor-sort-desc:focus > div.jsoneditor-icon {
background-position: -192px 0;
}
/* ContextMenu - sub menu */
div.jsoneditor-contextmenu ul li button.jsoneditor-selected,
div.jsoneditor-contextmenu ul li button.jsoneditor-selected:hover,
div.jsoneditor-contextmenu ul li button.jsoneditor-selected:focus {
color: white;
background-color: #ee422e;
}
div.jsoneditor-contextmenu ul li {
overflow: hidden;
}
div.jsoneditor-contextmenu ul li ul {
display: none;
position: relative;
left: -10px;
top: 0;
border: none;
box-shadow: inset 0 0 10px rgba(128, 128, 128, 0.5);
padding: 0 10px;
/* TODO: transition is not supported on IE8-9 */
-webkit-transition: all 0.3s ease-out;
-moz-transition: all 0.3s ease-out;
-o-transition: all 0.3s ease-out;
transition: all 0.3s ease-out;
}
div.jsoneditor-contextmenu ul li.jsoneditor-selected ul {
}
div.jsoneditor-contextmenu ul li ul li button {
padding-left: 24px;
animation: all ease-in-out 1s;
}
div.jsoneditor-contextmenu ul li ul li button:hover,
div.jsoneditor-contextmenu ul li ul li button:focus {
background-color: #f5f5f5;
}
div.jsoneditor-contextmenu button.jsoneditor-type-string > div.jsoneditor-icon {
background-position: -144px -24px;
}
div.jsoneditor-contextmenu button.jsoneditor-type-string:hover > div.jsoneditor-icon,
div.jsoneditor-contextmenu button.jsoneditor-type-string:focus > div.jsoneditor-icon,
div.jsoneditor-contextmenu button.jsoneditor-type-string.jsoneditor-selected > div.jsoneditor-icon{
background-position: -144px 0;
}
div.jsoneditor-contextmenu button.jsoneditor-type-auto > div.jsoneditor-icon {
background-position: -120px -24px;
}
div.jsoneditor-contextmenu button.jsoneditor-type-auto:hover > div.jsoneditor-icon,
div.jsoneditor-contextmenu button.jsoneditor-type-auto:focus > div.jsoneditor-icon,
div.jsoneditor-contextmenu button.jsoneditor-type-auto.jsoneditor-selected > div.jsoneditor-icon {
background-position: -120px 0;
}
div.jsoneditor-contextmenu button.jsoneditor-type-object > div.jsoneditor-icon {
background-position: -72px -24px;
}
div.jsoneditor-contextmenu button.jsoneditor-type-object:hover > div.jsoneditor-icon,
div.jsoneditor-contextmenu button.jsoneditor-type-object:focus > div.jsoneditor-icon,
div.jsoneditor-contextmenu button.jsoneditor-type-object.jsoneditor-selected > div.jsoneditor-icon{
background-position: -72px 0;
}
div.jsoneditor-contextmenu button.jsoneditor-type-array > div.jsoneditor-icon {
background-position: -96px -24px;
}
div.jsoneditor-contextmenu button.jsoneditor-type-array:hover > div.jsoneditor-icon,
div.jsoneditor-contextmenu button.jsoneditor-type-array:focus > div.jsoneditor-icon,
div.jsoneditor-contextmenu button.jsoneditor-type-array.jsoneditor-selected > div.jsoneditor-icon{
background-position: -96px 0;
}
div.jsoneditor-contextmenu button.jsoneditor-type-modes > div.jsoneditor-icon {
background-image: none;
width: 6px;
}

View File

@ -1,449 +0,0 @@
div.jsoneditor {
}
div.jsoneditor-field,
div.jsoneditor-value,
div.jsoneditor-readonly {
border: 1px solid transparent;
min-height: 16px;
min-width: 32px;
padding: 2px;
margin: 1px;
word-wrap: break-word;
float: left;
}
/* adjust margin of p elements inside editable divs, needed for Opera, IE */
div.jsoneditor-field p,
div.jsoneditor-value p {
margin: 0;
}
div.jsoneditor-value {
word-break: break-word;
}
div.jsoneditor-readonly {
min-width: 16px;
color: gray;
}
div.jsoneditor-empty {
border-color: lightgray;
border-style: dashed;
border-radius: 2px;
}
div.jsoneditor-field.jsoneditor-empty::after,
div.jsoneditor-value.jsoneditor-empty::after {
pointer-events: none;
color: lightgray;
font-size: 8pt;
}
div.jsoneditor-field.jsoneditor-empty::after {
content: "field";
}
div.jsoneditor-value.jsoneditor-empty::after {
content: "value";
}
div.jsoneditor-value.jsoneditor-url,
a.jsoneditor-value.jsoneditor-url {
color: green;
text-decoration: underline;
}
a.jsoneditor-value.jsoneditor-url {
display: inline-block;
padding: 2px;
margin: 2px;
}
a.jsoneditor-value.jsoneditor-url:hover,
a.jsoneditor-value.jsoneditor-url:focus {
color: #ee422e;
}
div.jsoneditor td.jsoneditor-separator {
padding: 3px 0;
vertical-align: top;
color: gray;
}
div.jsoneditor-field[contenteditable=true]:focus,
div.jsoneditor-field[contenteditable=true]:hover,
div.jsoneditor-value[contenteditable=true]:focus,
div.jsoneditor-value[contenteditable=true]:hover,
div.jsoneditor-field.jsoneditor-highlight,
div.jsoneditor-value.jsoneditor-highlight {
background-color: #FFFFAB;
border: 1px solid yellow;
border-radius: 2px;
}
div.jsoneditor-field.jsoneditor-highlight-active,
div.jsoneditor-field.jsoneditor-highlight-active:focus,
div.jsoneditor-field.jsoneditor-highlight-active:hover,
div.jsoneditor-value.jsoneditor-highlight-active,
div.jsoneditor-value.jsoneditor-highlight-active:focus,
div.jsoneditor-value.jsoneditor-highlight-active:hover {
background-color: #ffee00;
border: 1px solid #ffc700;
border-radius: 2px;
}
div.jsoneditor-value.jsoneditor-string {
color: #008000;
}
div.jsoneditor-value.jsoneditor-object,
div.jsoneditor-value.jsoneditor-array {
min-width: 16px;
color: #808080;
}
div.jsoneditor-value.jsoneditor-number {
color: #ee422e;
}
div.jsoneditor-value.jsoneditor-boolean {
color: #ff8c00;
}
div.jsoneditor-value.jsoneditor-null {
color: #004ED0;
}
div.jsoneditor-value.jsoneditor-invalid {
color: #000000;
}
div.jsoneditor-tree button {
width: 24px;
height: 24px;
padding: 0;
margin: 0;
border: none;
cursor: pointer;
background: transparent url('img/jsoneditor-icons.svg');
}
div.jsoneditor-mode-view tr.jsoneditor-expandable td.jsoneditor-tree,
div.jsoneditor-mode-form tr.jsoneditor-expandable td.jsoneditor-tree {
cursor: pointer;
}
div.jsoneditor-tree button.jsoneditor-collapsed {
background-position: 0 -48px;
}
div.jsoneditor-tree button.jsoneditor-expanded {
background-position: 0 -72px;
}
div.jsoneditor-tree button.jsoneditor-contextmenu {
background-position: -48px -72px;
}
div.jsoneditor-tree button.jsoneditor-contextmenu:hover,
div.jsoneditor-tree button.jsoneditor-contextmenu:focus,
div.jsoneditor-tree button.jsoneditor-contextmenu.jsoneditor-selected,
tr.jsoneditor-selected.jsoneditor-first button.jsoneditor-contextmenu {
background-position: -48px -48px;
}
div.jsoneditor-tree *:focus {
outline: none;
}
div.jsoneditor-tree button:focus {
/* TODO: nice outline for buttons with focus
outline: #97B0F8 solid 2px;
box-shadow: 0 0 8px #97B0F8;
*/
background-color: #f5f5f5;
outline: #e5e5e5 solid 1px;
}
div.jsoneditor-tree button.jsoneditor-invisible {
visibility: hidden;
background: none;
}
div.jsoneditor {
color: #1A1A1A;
border: 1px solid #3883fa;
-moz-box-sizing: border-box;
-webkit-box-sizing: border-box;
box-sizing: border-box;
width: 100%;
height: 100%;
overflow: hidden;
position: relative;
padding: 0;
line-height: 100%;
}
div.jsoneditor-tree table.jsoneditor-tree {
border-collapse: collapse;
border-spacing: 0;
width: 100%;
margin: 0;
}
div.jsoneditor-outer {
width: 100%;
height: 100%;
margin: -35px 0 0 0;
padding: 35px 0 0 0;
-moz-box-sizing: border-box;
-webkit-box-sizing: border-box;
box-sizing: border-box;
}
textarea.jsoneditor-text,
.ace-jsoneditor {
min-height: 150px;
}
div.jsoneditor-tree {
width: 100%;
height: 100%;
position: relative;
overflow: auto;
}
textarea.jsoneditor-text {
width: 100%;
height: 100%;
margin: 0;
-moz-box-sizing: border-box;
-webkit-box-sizing: border-box;
box-sizing: border-box;
outline-width: 0;
border: none;
background-color: white;
resize: none;
}
tr.jsoneditor-highlight,
tr.jsoneditor-selected {
background-color: #e6e6e6;
}
tr.jsoneditor-selected button.jsoneditor-dragarea,
tr.jsoneditor-selected button.jsoneditor-contextmenu {
visibility: hidden;
}
tr.jsoneditor-selected.jsoneditor-first button.jsoneditor-dragarea,
tr.jsoneditor-selected.jsoneditor-first button.jsoneditor-contextmenu {
visibility: visible;
}
div.jsoneditor-tree button.jsoneditor-dragarea {
background: url('img/jsoneditor-icons.svg') -72px -72px;
cursor: move;
}
div.jsoneditor-tree button.jsoneditor-dragarea:hover,
div.jsoneditor-tree button.jsoneditor-dragarea:focus,
tr.jsoneditor-selected.jsoneditor-first button.jsoneditor-dragarea {
background-position: -72px -48px;
}
div.jsoneditor tr,
div.jsoneditor th,
div.jsoneditor td {
padding: 0;
margin: 0;
}
div.jsoneditor td {
vertical-align: top;
}
div.jsoneditor td.jsoneditor-tree {
vertical-align: top;
}
div.jsoneditor-field,
div.jsoneditor-value,
div.jsoneditor td,
div.jsoneditor th,
div.jsoneditor textarea,
.jsoneditor-schema-error {
font-family: droid sans mono, consolas, monospace, courier new, courier, sans-serif;
font-size: 10pt;
color: #1A1A1A;
}
/* popover */
.jsoneditor-schema-error {
cursor: default;
display: inline-block;
/*font-family: arial, sans-serif;*/
height: 24px;
line-height: 24px;
position: relative;
text-align: center;
width: 24px;
}
div.jsoneditor-tree .jsoneditor-schema-error {
width: 24px;
height: 24px;
padding: 0;
margin: 0 4px 0 0;
background: url('img/jsoneditor-icons.svg') -168px -48px;
}
.jsoneditor-schema-error .jsoneditor-popover {
background-color: #4c4c4c;
border-radius: 3px;
box-shadow: 0 0 5px rgba(0,0,0,0.4);
color: #fff;
display: none;
padding: 7px 10px;
position: absolute;
width: 200px;
z-index: 4;
}
.jsoneditor-schema-error .jsoneditor-popover.jsoneditor-above {
bottom: 32px;
left: -98px;
}
.jsoneditor-schema-error .jsoneditor-popover.jsoneditor-below {
top: 32px;
left: -98px;
}
.jsoneditor-schema-error .jsoneditor-popover.jsoneditor-left {
top: -7px;
right: 32px;
}
.jsoneditor-schema-error .jsoneditor-popover.jsoneditor-right {
top: -7px;
left: 32px;
}
.jsoneditor-schema-error .jsoneditor-popover:before {
border-right: 7px solid transparent;
border-left: 7px solid transparent;
content: '';
display: block;
left: 50%;
margin-left: -7px;
position: absolute;
}
.jsoneditor-schema-error .jsoneditor-popover.jsoneditor-above:before {
border-top: 7px solid #4c4c4c;
bottom: -7px;
}
.jsoneditor-schema-error .jsoneditor-popover.jsoneditor-below:before {
border-bottom: 7px solid #4c4c4c;
top: -7px;
}
.jsoneditor-schema-error .jsoneditor-popover.jsoneditor-left:before {
border-left: 7px solid #4c4c4c;
border-top: 7px solid transparent;
border-bottom: 7px solid transparent;
content: '';
top: 19px;
right: -14px;
left: inherit;
margin-left: inherit;
margin-top: -7px;
position: absolute;
}
.jsoneditor-schema-error .jsoneditor-popover.jsoneditor-right:before {
border-right: 7px solid #4c4c4c;
border-top: 7px solid transparent;
border-bottom: 7px solid transparent;
content: '';
top: 19px;
left: -14px;
margin-left: inherit;
margin-top: -7px;
position: absolute;
}
.jsoneditor-schema-error:hover .jsoneditor-popover,
.jsoneditor-schema-error:focus .jsoneditor-popover {
display: block;
-webkit-animation: fade-in .3s linear 1, move-up .3s linear 1;
-moz-animation: fade-in .3s linear 1, move-up .3s linear 1;
-ms-animation: fade-in .3s linear 1, move-up .3s linear 1;
}
@-webkit-keyframes fade-in {
from { opacity: 0; }
to { opacity: 1; }
}
@-moz-keyframes fade-in {
from { opacity: 0; }
to { opacity: 1; }
}
@-ms-keyframes fade-in {
from { opacity: 0; }
to { opacity: 1; }
}
/*@-webkit-keyframes move-up {*/
/*from { bottom: 24px; }*/
/*to { bottom: 32px; }*/
/*}*/
/*@-moz-keyframes move-up {*/
/*from { bottom: 24px; }*/
/*to { bottom: 32px; }*/
/*}*/
/*@-ms-keyframes move-up {*/
/*from { bottom: 24px; }*/
/*to { bottom: 32px; }*/
/*}*/
/* JSON schema errors displayed at the bottom of the editor in mode text and code */
.jsoneditor .jsoneditor-text-errors {
width: 100%;
border-collapse: collapse;
background-color: #ffef8b;
border-top: 1px solid #ffd700;
}
.jsoneditor .jsoneditor-text-errors td {
padding: 3px 6px;
vertical-align: middle;
}
.jsoneditor-text-errors .jsoneditor-schema-error {
border: none;
width: 24px;
height: 24px;
padding: 0;
margin: 0 4px 0 0;
background: url('img/jsoneditor-icons.svg') -168px -48px;
}

View File

@ -1,110 +0,0 @@
div.jsoneditor-menu {
width: 100%;
height: 35px;
padding: 2px;
margin: 0;
-moz-box-sizing: border-box;
-webkit-box-sizing: border-box;
box-sizing: border-box;
color: white;
background-color: #3883fa;
border-bottom: 1px solid #3883fa;
}
div.jsoneditor-menu > button,
div.jsoneditor-menu > div.jsoneditor-modes > button {
width: 26px;
height: 26px;
margin: 2px;
padding: 0;
border-radius: 2px;
border: 1px solid transparent;
background: transparent url('img/jsoneditor-icons.svg');
color: white;
opacity: 0.8;
font-family: arial, sans-serif;
font-size: 10pt;
float: left;
}
div.jsoneditor-menu > button:hover,
div.jsoneditor-menu > div.jsoneditor-modes > button:hover {
background-color: rgba(255,255,255,0.2);
border: 1px solid rgba(255,255,255,0.4);
}
div.jsoneditor-menu > button:focus,
div.jsoneditor-menu > button:active,
div.jsoneditor-menu > div.jsoneditor-modes > button:focus,
div.jsoneditor-menu > div.jsoneditor-modes > button:active {
background-color: rgba(255,255,255,0.3);
}
div.jsoneditor-menu > button:disabled,
div.jsoneditor-menu > div.jsoneditor-modes > button:disabled {
opacity: 0.5;
}
div.jsoneditor-menu > button.jsoneditor-collapse-all {
background-position: 0 -96px;
}
div.jsoneditor-menu > button.jsoneditor-expand-all {
background-position: 0 -120px;
}
div.jsoneditor-menu > button.jsoneditor-undo {
background-position: -24px -96px;
}
div.jsoneditor-menu > button.jsoneditor-undo:disabled {
background-position: -24px -120px;
}
div.jsoneditor-menu > button.jsoneditor-redo {
background-position: -48px -96px;
}
div.jsoneditor-menu > button.jsoneditor-redo:disabled {
background-position: -48px -120px;
}
div.jsoneditor-menu > button.jsoneditor-compact {
background-position: -72px -96px;
}
div.jsoneditor-menu > button.jsoneditor-format {
background-position: -72px -120px;
}
div.jsoneditor-menu > div.jsoneditor-modes {
display: inline-block;
float: left;
}
div.jsoneditor-menu > div.jsoneditor-modes > button {
background-image: none;
width: auto;
padding-left: 6px;
padding-right: 6px;
}
div.jsoneditor-menu > button.jsoneditor-separator,
div.jsoneditor-menu > div.jsoneditor-modes > button.jsoneditor-separator {
margin-left: 10px;
}
div.jsoneditor-menu a {
font-family: arial, sans-serif;
font-size: 10pt;
color: white;
opacity: 0.8;
vertical-align: middle;
}
div.jsoneditor-menu a:hover {
opacity: 1;
}
div.jsoneditor-menu a.jsoneditor-poweredBy {
font-size: 8pt;
position: absolute;
right: 0;
top: 0;
padding: 10px;
}

View File

@ -1,25 +0,0 @@
/* reset styling (prevent conflicts with bootstrap, materialize.css, etc.) */
div.jsoneditor input {
height: auto;
border: inherit;
}
div.jsoneditor input:focus {
border: none !important;
box-shadow: none !important;
}
div.jsoneditor table {
border-collapse: collapse;
width: auto;
}
div.jsoneditor td,
div.jsoneditor th {
padding: 0;
display: table-cell;
text-align: left;
vertical-align: inherit;
border-radius: inherit;
}

View File

@ -1,77 +0,0 @@
table.jsoneditor-search input,
table.jsoneditor-search div.jsoneditor-results {
font-family: arial, sans-serif;
font-size: 10pt;
color: #1A1A1A;
background: transparent; /* For Firefox */
}
table.jsoneditor-search div.jsoneditor-results {
color: white;
padding-right: 5px;
line-height: 24px;
}
table.jsoneditor-search {
position: absolute;
right: 4px;
top: 4px;
border-collapse: collapse;
border-spacing: 0;
}
table.jsoneditor-search div.jsoneditor-frame {
border: 1px solid transparent;
background-color: white;
padding: 0 2px;
margin: 0;
}
table.jsoneditor-search div.jsoneditor-frame table {
border-collapse: collapse;
}
table.jsoneditor-search input {
width: 120px;
border: none;
outline: none;
margin: 1px;
line-height: 20px;
}
table.jsoneditor-search button {
width: 16px;
height: 24px;
padding: 0;
margin: 0;
border: none;
background: url('img/jsoneditor-icons.svg');
vertical-align: top;
}
table.jsoneditor-search button:hover {
background-color: transparent;
}
table.jsoneditor-search button.jsoneditor-refresh {
width: 18px;
background-position: -99px -73px;
}
table.jsoneditor-search button.jsoneditor-next {
cursor: pointer;
background-position: -124px -73px;
}
table.jsoneditor-search button.jsoneditor-next:hover {
background-position: -124px -49px;
}
table.jsoneditor-search button.jsoneditor-previous {
cursor: pointer;
background-position: -148px -73px;
margin-right: 2px;
}
table.jsoneditor-search button.jsoneditor-previous:hover {
background-position: -148px -49px;
}

16
src/demo/Demo.css Normal file
View File

@ -0,0 +1,16 @@
body, input, select {
font-family: sans-serif;
font-size: 11pt; }
body {
height: 2000px; }
.demo .menu {
margin: 20px 0; }
.demo .menu button, .demo .menu label {
margin-right: 10px; }
.demo .contents {
height: 400px;
width: 100%;
max-width: 800px; }

197
src/demo/Demo.js Normal file
View File

@ -0,0 +1,197 @@
import React, { Component } from 'react'
import JSONEditor from '../jsoneditor/index.react'
import { setIn } from '../jsoneditor/utils/immutabilityHelpers'
import { largeJson } from './resources/largeJson'
import './Demo.css'
const schema = {
"title": "Example Schema",
"type": "object",
"properties": {
"firstName": {
"type": "string"
},
"lastName": {
"type": "string"
},
"gender": {
"enum": ["male", "female"]
},
"age": {
"description": "Age in years",
"type": "integer",
"minimum": 0
}
},
"required": ["firstName", "lastName"]
}
const json = {
'array': [1, 2, 3],
'emptyArray': [],
'emptyObject': {},
'firstName': null,
'boolean': true,
'null': null,
'number': 123,
'object': {'a': 'b', 'c': 'd', 'e': [{"first": true}, {"second": true}]},
'string': 'Hello World',
'unicode': 'A unicode character: \u260E',
'url': 'http://jsoneditoronline.org',
'largeArray': [0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20],
'structureArray': [
{name: 'Joe', age: 24},
{name: 'Sarah', age: 28},
{name: 'Brett', age: 21},
{name: 'Emma', age: 31},
]
}
function expandAll (path) {
return true
}
class App extends Component {
constructor (props) {
super(props)
this.state = {
logging: false,
editorProps: {
json,
schema: null,
onPatch: this.handlePatch,
onPatchText: this.handlePatchText,
onChange: this.handleChange,
onChangeText: this.handleChangeText,
onChangeMode: this.handleChangeMode,
onError: this.handleError,
name: 'myObject',
mode: 'tree',
modes: ['text', 'code', 'tree', 'form', 'view'],
keyBindings: {
compact: ['Ctrl+\\', 'Command+\\', 'Ctrl+Alt+1', 'Command+Option+1'],
format: ['Ctrl+Shift+\\', 'Command+Shift+\\', 'Ctrl+Alt+2', 'Command+Option+2'],
duplicate: ['Ctrl+D', 'Ctrl+Shift+D', 'Command+D', 'Command+Shift+D']
},
indentation: 4,
escapeUnicode: true,
history: true,
search: true,
expand: expandAll
}
}
}
render() {
return <div className="demo">
<div className="menu">
<button onClick={this.handleSetJson}>Set JSON</button>
<button onClick={this.handleGetJson}>Get JSON</button>
<label>mode:
<select value={this.state.editorProps.mode} onChange={this.handleSetMode}>
<option value="text">text</option>
<option value="code">code</option>
<option value="tree">tree</option>
<option value="form">form</option>
<option value="view">view</option>
</select>
</label>
<label>
<input type="checkbox"
value={this.state.editorProps.schema !== null}
onChange={this.handleToggleJSONSchema} /> JSON Schema
</label>
<label>
<input type="checkbox"
value={this.state.logging}
onChange={this.handleToggleLogging} /> Log events
</label>
</div>
<div className="contents">
<JSONEditor {...this.state.editorProps} />
</div>
</div>
}
handleSetJson = () => {
this.setState({
editorProps: setIn(this.state.editorProps, ['json'], largeJson)
})
}
handleGetJson = () => {
const json = this.state.editorProps.json
alert(JSON.stringify(json, null, 2))
}
handleSetMode = (event) => {
const mode = event.target.value
this.setState({
editorProps: setIn(this.state.editorProps, ['mode'], mode)
})
}
handleToggleLogging = (event) => {
const logging = event.target.checked
this.setState({ logging })
}
handleToggleJSONSchema = (event) => {
const value = event.target.checked ? schema : null
this.setState({
editorProps: setIn(this.state.editorProps, ['schema'], value)
})
}
handleChange = (json) => {
this.log('onChange json=', json)
this.setState({
editorProps: setIn(this.state.editorProps, ['json'], json)
})
}
handleChangeText = (text) => {
this.log('onChangeText', text)
}
handlePatch = (patch, revert) => {
this.log('onPatch patch=', patch, ', revert=', revert)
window.immutableJSONPatch = patch
window.revert = revert
}
handlePatchText = (patch, revert) => {
// FIXME: implement onPatchText
this.log('onPatchText patch=', patch, ', revert=', revert)
}
handleChangeMode = (mode, prevMode) => {
this.log('switched mode from', prevMode, 'to', mode)
this.setState({
editorProps: setIn(this.state.editorProps, ['mode'], mode)
})
}
handleError = (err) => {
console.error(err)
alert(err)
}
log (...args) {
if (this.state.logging) {
console.log(...args)
}
}
}
export default App

26
src/demo/Demo.scss Normal file
View File

@ -0,0 +1,26 @@
body, input, select {
font-family: sans-serif;
font-size: 11pt;
}
body {
height: 2000px;
}
.demo {
.menu {
margin: 20px 0;
button, label {
margin-right: 10px;
}
}
.contents {
height: 400px;
width: 100%;
max-width : 800px;
}
}

10
src/demo/Demo.test.js Normal file
View File

@ -0,0 +1,10 @@
import React from 'react';
import ReactDOM from 'react-dom';
import Demo from './Demo';
test('renders without crashing', () => {
const div = document.createElement('div');
ReactDOM.render(<Demo />, div);
});

179
src/demo/demo.vanilla.html Normal file
View File

@ -0,0 +1,179 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>JSONEditor vanilla demo</title>
<!-- For IE and Edge -->
<!--<script src="https://cdnjs.cloudflare.com/ajax/libs/es6-shim/0.35.1/es6-shim.min.js"></script>-->
<script src="../../dist/jsoneditor.js"></script>
<script src="./resources/largeJson.js"></script>
<style>
body, input, select {
font-family: sans-serif;
font-size: 11pt;
}
#container {
height: 400px;
width: 100%;
max-width : 800px;
}
</style>
</head>
<body>
<p>
<button id="setJSON">Set JSON</button>
<button id="getJSON">Get JSON</button>
<button id="toggleSchema">Toggle JSON Schema</button>
<label for="mode">mode:
<select id="mode">
<option value="text">text</option>
<option value="code">code</option>
<option value="tree" selected>tree</option>
<option value="form">form</option>
<option value="view">view</option>
</select>
</label>
<label>
<input type="checkbox" id="logEvents" /> Log events
</label>
</p>
<div id="container"></div>
<script>
// prevent contentEditable warnings of React
// https://github.com/facebook/draft-js/issues/53#issuecomment-188280259
console.error = (function() {
const error = console.error
return function(exception) {
if ((exception + '').indexOf('Warning: A component is `contentEditable`') !== 0) {
error.apply(console, arguments)
}
}
})()
// create the editor
const mode = document.getElementById('mode').value
const container = document.getElementById('container')
const options = {
name: 'myObject',
onPatch: function (patch, revert) {
log('onPatch patch=', patch, ', revert=', revert)
window.immutableJSONPatch = patch
window.revert = revert
},
onPatchText: function (patch, revert) {
// FIXME: implement onPatchText
log('onPatchText patch=', patch, ', revert=', revert)
},
onChange: function (json) {
log('onChange json=', json)
},
onChangeText: function (text) {
log('onChangeText', text)
},
onChangeMode: function (mode, prevMode) {
log('switched mode from', prevMode, 'to', mode)
document.getElementById('mode').value = mode
},
onError: function (err) {
console.error(err)
alert(err)
},
mode: mode,
modes: ['text', 'code', 'tree', 'form', 'view'], keyBindings: {
compact: ['Ctrl+\\', 'Command+\\', 'Ctrl+Alt+1', 'Command+Option+1'],
format: ['Ctrl+Shift+\\', 'Command+Shift+\\', 'Ctrl+Alt+2', 'Command+Option+2'],
duplicate: ['Ctrl+D', 'Ctrl+Shift+D', 'Command+D', 'Command+Shift+D']
},
indentation: 4,
escapeUnicode: true,
history: true,
search: true,
expand: function (path) {
return true
}
}
const editor = jsoneditor(container, options)
const json = {
'array': [1, 2, 3],
'emptyArray': [],
'emptyObject': {},
'firstName': null,
'boolean': true,
'null': null,
'number': 123,
'object': {'a': 'b', 'c': 'd', 'e': [{"first": true}, {"second": true}]},
'string': 'Hello World',
'unicode': 'A unicode character: \u260E',
'url': 'http://jsoneditoronline.org'
}
editor.set(json)
const schema = {
"title": "Example Schema",
"type": "object",
"properties": {
"firstName": {
"type": "string"
},
"lastName": {
"type": "string"
},
"gender": {
"enum": ["male", "female"]
},
"age": {
"description": "Age in years",
"type": "integer",
"minimum": 0
}
},
"required": ["firstName", "lastName"]
}
// set json
document.getElementById('setJSON').onclick = async function () {
editor.set(largeJson, {
expand: function (path) {
return true
}
})
}
// set schema
let hasSchema = false
document.getElementById('toggleSchema').onclick = function () {
editor.setSchema(hasSchema ? null : schema)
hasSchema = !hasSchema
}
// get json
document.getElementById('getJSON').onclick = function () {
const json = editor.get()
alert(JSON.stringify(json, null, 2))
}
// change mode
document.getElementById('mode').onchange = function (event) {
const mode = event.target.value
editor.setMode(mode)
}
function log (...args) {
if (document.getElementById('logEvents').checked) {
console.log(...args)
}
}
</script>
</body>
</html>

View File

@ -1,4 +1,4 @@
{
const largeJson = {
"version": "1.0",
"encoding": "UTF-8",
"feed": {
@ -12603,3 +12603,12 @@
]
}
}
// export for node.js or web
if (typeof module !== 'undefined' && typeof exports !== 'undefined') {
exports.largeJson = largeJson
}
else {
window.largeJson = largeJson
}

View File

@ -1,41 +0,0 @@
# Which files do I need?
Ehhh, that's quite some files in this dist folder. Which files do I need?
## Full version
If you're not sure which version to use, use the full version.
Which files are needed when using the full version?
- jsoneditor.min.js
- jsoneditor.map (optional, for debugging purposes only)
- jsoneditor.min.css
- img/jsoneditor-icons.svg
## Minimalist version
The minimalist version has excluded the following libraries:
- `ace` (via `brace`), used for the code editor.
- `ajv`, used for JSON schema validation.
This reduces the the size of the minified and gzipped JavaScript file from
about 160 kB to about 40 kB.
When to use the minimalist version?
- If you don't need the mode "code" and don't need JSON schema validation.
- Or if you want to provide `ace` and/or `ajv` yourself via the configuration
options, for example when you already use Ace in other parts of your
web application too and don't want to bundle the library twice.
Which files are needed when using the minimalist version?
- jsoneditor-minimalist.min.js
- jsoneditor-minimalist.map (optional, for debugging purposes only)
- jsoneditor.min.css
- img/jsoneditor-icons.svg

6
src/index.js Normal file
View File

@ -0,0 +1,6 @@
import React from 'react';
import ReactDOM from 'react-dom';
import Demo from './demo/Demo';
ReactDOM.render(<Demo />, document.getElementById('root'));

View File

@ -1,457 +0,0 @@
'use strict';
var util = require('./util');
/**
* A context menu
* @param {Object[]} items Array containing the menu structure
* TODO: describe structure
* @param {Object} [options] Object with options. Available options:
* {function} close Callback called when the
* context menu is being closed.
* @constructor
*/
function ContextMenu (items, options) {
this.dom = {};
var me = this;
var dom = this.dom;
this.anchor = undefined;
this.items = items;
this.eventListeners = {};
this.selection = undefined; // holds the selection before the menu was opened
this.onClose = options ? options.close : undefined;
// create root element
var root = document.createElement('div');
root.className = 'jsoneditor-contextmenu-root';
dom.root = root;
// create a container element
var menu = document.createElement('div');
menu.className = 'jsoneditor-contextmenu';
dom.menu = menu;
root.appendChild(menu);
// create a list to hold the menu items
var list = document.createElement('ul');
list.className = 'jsoneditor-menu';
menu.appendChild(list);
dom.list = list;
dom.items = []; // list with all buttons
// create a (non-visible) button to set the focus to the menu
var focusButton = document.createElement('button');
dom.focusButton = focusButton;
var li = document.createElement('li');
li.style.overflow = 'hidden';
li.style.height = '0';
li.appendChild(focusButton);
list.appendChild(li);
function createMenuItems (list, domItems, items) {
items.forEach(function (item) {
if (item.type == 'separator') {
// create a separator
var separator = document.createElement('div');
separator.className = 'jsoneditor-separator';
li = document.createElement('li');
li.appendChild(separator);
list.appendChild(li);
}
else {
var domItem = {};
// create a menu item
var li = document.createElement('li');
list.appendChild(li);
// create a button in the menu item
var button = document.createElement('button');
button.className = item.className;
domItem.button = button;
if (item.title) {
button.title = item.title;
}
if (item.click) {
button.onclick = function (event) {
event.preventDefault();
me.hide();
item.click();
};
}
li.appendChild(button);
// create the contents of the button
if (item.submenu) {
// add the icon to the button
var divIcon = document.createElement('div');
divIcon.className = 'jsoneditor-icon';
button.appendChild(divIcon);
button.appendChild(document.createTextNode(item.text));
var buttonSubmenu;
if (item.click) {
// submenu and a button with a click handler
button.className += ' jsoneditor-default';
var buttonExpand = document.createElement('button');
domItem.buttonExpand = buttonExpand;
buttonExpand.className = 'jsoneditor-expand';
buttonExpand.innerHTML = '<div class="jsoneditor-expand"></div>';
li.appendChild(buttonExpand);
if (item.submenuTitle) {
buttonExpand.title = item.submenuTitle;
}
buttonSubmenu = buttonExpand;
}
else {
// submenu and a button without a click handler
var divExpand = document.createElement('div');
divExpand.className = 'jsoneditor-expand';
button.appendChild(divExpand);
buttonSubmenu = button;
}
// attach a handler to expand/collapse the submenu
buttonSubmenu.onclick = function (event) {
event.preventDefault();
me._onExpandItem(domItem);
buttonSubmenu.focus();
};
// create the submenu
var domSubItems = [];
domItem.subItems = domSubItems;
var ul = document.createElement('ul');
domItem.ul = ul;
ul.className = 'jsoneditor-menu';
ul.style.height = '0';
li.appendChild(ul);
createMenuItems(ul, domSubItems, item.submenu);
}
else {
// no submenu, just a button with clickhandler
button.innerHTML = '<div class="jsoneditor-icon"></div>' + item.text;
}
domItems.push(domItem);
}
});
}
createMenuItems(list, this.dom.items, items);
// TODO: when the editor is small, show the submenu on the right instead of inline?
// calculate the max height of the menu with one submenu expanded
this.maxHeight = 0; // height in pixels
items.forEach(function (item) {
var height = (items.length + (item.submenu ? item.submenu.length : 0)) * 24;
me.maxHeight = Math.max(me.maxHeight, height);
});
}
/**
* Get the currently visible buttons
* @return {Array.<HTMLElement>} buttons
* @private
*/
ContextMenu.prototype._getVisibleButtons = function () {
var buttons = [];
var me = this;
this.dom.items.forEach(function (item) {
buttons.push(item.button);
if (item.buttonExpand) {
buttons.push(item.buttonExpand);
}
if (item.subItems && item == me.expandedItem) {
item.subItems.forEach(function (subItem) {
buttons.push(subItem.button);
if (subItem.buttonExpand) {
buttons.push(subItem.buttonExpand);
}
// TODO: change to fully recursive method
});
}
});
return buttons;
};
// currently displayed context menu, a singleton. We may only have one visible context menu
ContextMenu.visibleMenu = undefined;
/**
* Attach the menu to an anchor
* @param {HTMLElement} anchor Anchor where the menu will be attached
* as sibling.
* @param {HTMLElement} [contentWindow] The DIV with with the (scrollable) contents
*/
ContextMenu.prototype.show = function (anchor, contentWindow) {
this.hide();
// determine whether to display the menu below or above the anchor
var showBelow = true;
if (contentWindow) {
var anchorRect = anchor.getBoundingClientRect();
var contentRect = contentWindow.getBoundingClientRect();
if (anchorRect.bottom + this.maxHeight < contentRect.bottom) {
// fits below -> show below
}
else if (anchorRect.top - this.maxHeight > contentRect.top) {
// fits above -> show above
showBelow = false;
}
else {
// doesn't fit above nor below -> show below
}
}
// position the menu
if (showBelow) {
// display the menu below the anchor
var anchorHeight = anchor.offsetHeight;
this.dom.menu.style.left = '0px';
this.dom.menu.style.top = anchorHeight + 'px';
this.dom.menu.style.bottom = '';
}
else {
// display the menu above the anchor
this.dom.menu.style.left = '0px';
this.dom.menu.style.top = '';
this.dom.menu.style.bottom = '0px';
}
// attach the menu to the parent of the anchor
var parent = anchor.parentNode;
parent.insertBefore(this.dom.root, parent.firstChild);
// create and attach event listeners
var me = this;
var list = this.dom.list;
this.eventListeners.mousedown = util.addEventListener(window, 'mousedown', function (event) {
// hide menu on click outside of the menu
var target = event.target;
if ((target != list) && !me._isChildOf(target, list)) {
me.hide();
event.stopPropagation();
event.preventDefault();
}
});
this.eventListeners.keydown = util.addEventListener(window, 'keydown', function (event) {
me._onKeyDown(event);
});
// move focus to the first button in the context menu
this.selection = util.getSelection();
this.anchor = anchor;
setTimeout(function () {
me.dom.focusButton.focus();
}, 0);
if (ContextMenu.visibleMenu) {
ContextMenu.visibleMenu.hide();
}
ContextMenu.visibleMenu = this;
};
/**
* Hide the context menu if visible
*/
ContextMenu.prototype.hide = function () {
// remove the menu from the DOM
if (this.dom.root.parentNode) {
this.dom.root.parentNode.removeChild(this.dom.root);
if (this.onClose) {
this.onClose();
}
}
// remove all event listeners
// all event listeners are supposed to be attached to document.
for (var name in this.eventListeners) {
if (this.eventListeners.hasOwnProperty(name)) {
var fn = this.eventListeners[name];
if (fn) {
util.removeEventListener(window, name, fn);
}
delete this.eventListeners[name];
}
}
if (ContextMenu.visibleMenu == this) {
ContextMenu.visibleMenu = undefined;
}
};
/**
* Expand a submenu
* Any currently expanded submenu will be hided.
* @param {Object} domItem
* @private
*/
ContextMenu.prototype._onExpandItem = function (domItem) {
var me = this;
var alreadyVisible = (domItem == this.expandedItem);
// hide the currently visible submenu
var expandedItem = this.expandedItem;
if (expandedItem) {
//var ul = expandedItem.ul;
expandedItem.ul.style.height = '0';
expandedItem.ul.style.padding = '';
setTimeout(function () {
if (me.expandedItem != expandedItem) {
expandedItem.ul.style.display = '';
util.removeClassName(expandedItem.ul.parentNode, 'jsoneditor-selected');
}
}, 300); // timeout duration must match the css transition duration
this.expandedItem = undefined;
}
if (!alreadyVisible) {
var ul = domItem.ul;
ul.style.display = 'block';
var height = ul.clientHeight; // force a reflow in Firefox
setTimeout(function () {
if (me.expandedItem == domItem) {
ul.style.height = (ul.childNodes.length * 24) + 'px';
ul.style.padding = '5px 10px';
}
}, 0);
util.addClassName(ul.parentNode, 'jsoneditor-selected');
this.expandedItem = domItem;
}
};
/**
* Handle onkeydown event
* @param {Event} event
* @private
*/
ContextMenu.prototype._onKeyDown = function (event) {
var target = event.target;
var keynum = event.which;
var handled = false;
var buttons, targetIndex, prevButton, nextButton;
if (keynum == 27) { // ESC
// hide the menu on ESC key
// restore previous selection and focus
if (this.selection) {
util.setSelection(this.selection);
}
if (this.anchor) {
this.anchor.focus();
}
this.hide();
handled = true;
}
else if (keynum == 9) { // Tab
if (!event.shiftKey) { // Tab
buttons = this._getVisibleButtons();
targetIndex = buttons.indexOf(target);
if (targetIndex == buttons.length - 1) {
// move to first button
buttons[0].focus();
handled = true;
}
}
else { // Shift+Tab
buttons = this._getVisibleButtons();
targetIndex = buttons.indexOf(target);
if (targetIndex == 0) {
// move to last button
buttons[buttons.length - 1].focus();
handled = true;
}
}
}
else if (keynum == 37) { // Arrow Left
if (target.className == 'jsoneditor-expand') {
buttons = this._getVisibleButtons();
targetIndex = buttons.indexOf(target);
prevButton = buttons[targetIndex - 1];
if (prevButton) {
prevButton.focus();
}
}
handled = true;
}
else if (keynum == 38) { // Arrow Up
buttons = this._getVisibleButtons();
targetIndex = buttons.indexOf(target);
prevButton = buttons[targetIndex - 1];
if (prevButton && prevButton.className == 'jsoneditor-expand') {
// skip expand button
prevButton = buttons[targetIndex - 2];
}
if (!prevButton) {
// move to last button
prevButton = buttons[buttons.length - 1];
}
if (prevButton) {
prevButton.focus();
}
handled = true;
}
else if (keynum == 39) { // Arrow Right
buttons = this._getVisibleButtons();
targetIndex = buttons.indexOf(target);
nextButton = buttons[targetIndex + 1];
if (nextButton && nextButton.className == 'jsoneditor-expand') {
nextButton.focus();
}
handled = true;
}
else if (keynum == 40) { // Arrow Down
buttons = this._getVisibleButtons();
targetIndex = buttons.indexOf(target);
nextButton = buttons[targetIndex + 1];
if (nextButton && nextButton.className == 'jsoneditor-expand') {
// skip expand button
nextButton = buttons[targetIndex + 2];
}
if (!nextButton) {
// move to first button
nextButton = buttons[0];
}
if (nextButton) {
nextButton.focus();
handled = true;
}
handled = true;
}
// TODO: arrow left and right
if (handled) {
event.stopPropagation();
event.preventDefault();
}
};
/**
* Test if an element is a child of a parent element.
* @param {Element} child
* @param {Element} parent
* @return {boolean} isChild
*/
ContextMenu.prototype._isChildOf = function (child, parent) {
var e = child.parentNode;
while (e) {
if (e == parent) {
return true;
}
e = e.parentNode;
}
return false;
};
module.exports = ContextMenu;

View File

@ -1,86 +0,0 @@
'use strict';
/**
* The highlighter can highlight/unhighlight a node, and
* animate the visibility of a context menu.
* @constructor Highlighter
*/
function Highlighter () {
this.locked = false;
}
/**
* Hightlight given node and its childs
* @param {Node} node
*/
Highlighter.prototype.highlight = function (node) {
if (this.locked) {
return;
}
if (this.node != node) {
// unhighlight current node
if (this.node) {
this.node.setHighlight(false);
}
// highlight new node
this.node = node;
this.node.setHighlight(true);
}
// cancel any current timeout
this._cancelUnhighlight();
};
/**
* Unhighlight currently highlighted node.
* Will be done after a delay
*/
Highlighter.prototype.unhighlight = function () {
if (this.locked) {
return;
}
var me = this;
if (this.node) {
this._cancelUnhighlight();
// do the unhighlighting after a small delay, to prevent re-highlighting
// the same node when moving from the drag-icon to the contextmenu-icon
// or vice versa.
this.unhighlightTimer = setTimeout(function () {
me.node.setHighlight(false);
me.node = undefined;
me.unhighlightTimer = undefined;
}, 0);
}
};
/**
* Cancel an unhighlight action (if before the timeout of the unhighlight action)
* @private
*/
Highlighter.prototype._cancelUnhighlight = function () {
if (this.unhighlightTimer) {
clearTimeout(this.unhighlightTimer);
this.unhighlightTimer = undefined;
}
};
/**
* Lock highlighting or unhighlighting nodes.
* methods highlight and unhighlight do not work while locked.
*/
Highlighter.prototype.lock = function () {
this.locked = true;
};
/**
* Unlock highlighting or unhighlighting nodes
*/
Highlighter.prototype.unlock = function () {
this.locked = false;
};
module.exports = Highlighter;

View File

@ -1,267 +0,0 @@
'use strict';
var util = require('./util');
/**
* @constructor History
* Store action history, enables undo and redo
* @param {JSONEditor} editor
*/
function History (editor) {
this.editor = editor;
this.history = [];
this.index = -1;
this.clear();
// map with all supported actions
this.actions = {
'editField': {
'undo': function (params) {
params.node.updateField(params.oldValue);
},
'redo': function (params) {
params.node.updateField(params.newValue);
}
},
'editValue': {
'undo': function (params) {
params.node.updateValue(params.oldValue);
},
'redo': function (params) {
params.node.updateValue(params.newValue);
}
},
'changeType': {
'undo': function (params) {
params.node.changeType(params.oldType);
},
'redo': function (params) {
params.node.changeType(params.newType);
}
},
'appendNodes': {
'undo': function (params) {
params.nodes.forEach(function (node) {
params.parent.removeChild(node);
});
},
'redo': function (params) {
params.nodes.forEach(function (node) {
params.parent.appendChild(node);
});
}
},
'insertBeforeNodes': {
'undo': function (params) {
params.nodes.forEach(function (node) {
params.parent.removeChild(node);
});
},
'redo': function (params) {
params.nodes.forEach(function (node) {
params.parent.insertBefore(node, params.beforeNode);
});
}
},
'insertAfterNodes': {
'undo': function (params) {
params.nodes.forEach(function (node) {
params.parent.removeChild(node);
});
},
'redo': function (params) {
var afterNode = params.afterNode;
params.nodes.forEach(function (node) {
params.parent.insertAfter(params.node, afterNode);
afterNode = node;
});
}
},
'removeNodes': {
'undo': function (params) {
var parent = params.parent;
var beforeNode = parent.childs[params.index] || parent.append;
params.nodes.forEach(function (node) {
parent.insertBefore(node, beforeNode);
});
},
'redo': function (params) {
params.nodes.forEach(function (node) {
params.parent.removeChild(node);
});
}
},
'duplicateNodes': {
'undo': function (params) {
params.nodes.forEach(function (node) {
params.parent.removeChild(node);
});
},
'redo': function (params) {
var afterNode = params.afterNode;
params.nodes.forEach(function (node) {
params.parent.insertAfter(node, afterNode);
afterNode = node;
});
}
},
'moveNodes': {
'undo': function (params) {
params.nodes.forEach(function (node) {
params.oldBeforeNode.parent.moveBefore(node, params.oldBeforeNode);
});
},
'redo': function (params) {
params.nodes.forEach(function (node) {
params.newBeforeNode.parent.moveBefore(node, params.newBeforeNode);
});
}
},
'sort': {
'undo': function (params) {
var node = params.node;
node.hideChilds();
node.sort = params.oldSort;
node.childs = params.oldChilds;
node.showChilds();
},
'redo': function (params) {
var node = params.node;
node.hideChilds();
node.sort = params.newSort;
node.childs = params.newChilds;
node.showChilds();
}
}
// TODO: restore the original caret position and selection with each undo
// TODO: implement history for actions "expand", "collapse", "scroll", "setDocument"
};
}
/**
* The method onChange is executed when the History is changed, and can
* be overloaded.
*/
History.prototype.onChange = function () {};
/**
* Add a new action to the history
* @param {String} action The executed action. Available actions: "editField",
* "editValue", "changeType", "appendNode",
* "removeNode", "duplicateNode", "moveNode"
* @param {Object} params Object containing parameters describing the change.
* The parameters in params depend on the action (for
* example for "editValue" the Node, old value, and new
* value are provided). params contains all information
* needed to undo or redo the action.
*/
History.prototype.add = function (action, params) {
this.index++;
this.history[this.index] = {
'action': action,
'params': params,
'timestamp': new Date()
};
// remove redo actions which are invalid now
if (this.index < this.history.length - 1) {
this.history.splice(this.index + 1, this.history.length - this.index - 1);
}
// fire onchange event
this.onChange();
};
/**
* Clear history
*/
History.prototype.clear = function () {
this.history = [];
this.index = -1;
// fire onchange event
this.onChange();
};
/**
* Check if there is an action available for undo
* @return {Boolean} canUndo
*/
History.prototype.canUndo = function () {
return (this.index >= 0);
};
/**
* Check if there is an action available for redo
* @return {Boolean} canRedo
*/
History.prototype.canRedo = function () {
return (this.index < this.history.length - 1);
};
/**
* Undo the last action
*/
History.prototype.undo = function () {
if (this.canUndo()) {
var obj = this.history[this.index];
if (obj) {
var action = this.actions[obj.action];
if (action && action.undo) {
action.undo(obj.params);
if (obj.params.oldSelection) {
this.editor.setSelection(obj.params.oldSelection);
}
}
else {
console.error(new Error('unknown action "' + obj.action + '"'));
}
}
this.index--;
// fire onchange event
this.onChange();
}
};
/**
* Redo the last action
*/
History.prototype.redo = function () {
if (this.canRedo()) {
this.index++;
var obj = this.history[this.index];
if (obj) {
var action = this.actions[obj.action];
if (action && action.redo) {
action.redo(obj.params);
if (obj.params.newSelection) {
this.editor.setSelection(obj.params.newSelection);
}
}
else {
console.error(new Error('unknown action "' + obj.action + '"'));
}
}
// fire onchange event
this.onChange();
}
};
/**
* Destroy history
*/
History.prototype.destroy = function () {
this.editor = null;
this.history = [];
this.index = -1;
};
module.exports = History;

View File

@ -1,385 +0,0 @@
'use strict';
var Ajv;
try {
Ajv = require('ajv');
}
catch (err) {
// no problem... when we need Ajv we will throw a neat exception
}
var treemode = require('./treemode');
var textmode = require('./textmode');
var util = require('./util');
/**
* @constructor JSONEditor
* @param {Element} container Container element
* @param {Object} [options] Object with options. available options:
* {String} mode Editor mode. Available values:
* 'tree' (default), 'view',
* 'form', 'text', and 'code'.
* {function} onChange Callback method, triggered
* on change of contents
* {function} onError Callback method, triggered
* when an error occurs
* {Boolean} search Enable search box.
* True by default
* Only applicable for modes
* 'tree', 'view', and 'form'
* {Boolean} history Enable history (undo/redo).
* True by default
* Only applicable for modes
* 'tree', 'view', and 'form'
* {String} name Field name for the root node.
* Only applicable for modes
* 'tree', 'view', and 'form'
* {Number} indentation Number of indentation
* spaces. 4 by default.
* Only applicable for
* modes 'text' and 'code'
* {boolean} escapeUnicode If true, unicode
* characters are escaped.
* false by default.
* {boolean} sortObjectKeys If true, object keys are
* sorted before display.
* false by default.
* @param {Object | undefined} json JSON object
*/
function JSONEditor (container, options, json) {
if (!(this instanceof JSONEditor)) {
throw new Error('JSONEditor constructor called without "new".');
}
// check for unsupported browser (IE8 and older)
var ieVersion = util.getInternetExplorerVersion();
if (ieVersion != -1 && ieVersion < 9) {
throw new Error('Unsupported browser, IE9 or newer required. ' +
'Please install the newest version of your browser.');
}
if (options) {
// check for deprecated options
if (options.error) {
console.warn('Option "error" has been renamed to "onError"');
options.onError = options.error;
delete options.error;
}
if (options.change) {
console.warn('Option "change" has been renamed to "onChange"');
options.onChange = options.change;
delete options.change;
}
if (options.editable) {
console.warn('Option "editable" has been renamed to "onEditable"');
options.onEditable = options.editable;
delete options.editable;
}
// validate options
if (options) {
var VALID_OPTIONS = [
'ace', 'theme',
'ajv', 'schema',
'onChange', 'onEditable', 'onError', 'onModeChange',
'escapeUnicode', 'history', 'search', 'mode', 'modes', 'name', 'indentation', 'sortObjectKeys'
];
Object.keys(options).forEach(function (option) {
if (VALID_OPTIONS.indexOf(option) === -1) {
console.warn('Unknown option "' + option + '". This option will be ignored');
}
});
}
}
if (arguments.length) {
this._create(container, options, json);
}
}
/**
* Configuration for all registered modes. Example:
* {
* tree: {
* mixin: TreeEditor,
* data: 'json'
* },
* text: {
* mixin: TextEditor,
* data: 'text'
* }
* }
*
* @type { Object.<String, {mixin: Object, data: String} > }
*/
JSONEditor.modes = {};
// debounce interval for JSON schema vaidation in milliseconds
JSONEditor.prototype.DEBOUNCE_INTERVAL = 150;
/**
* Create the JSONEditor
* @param {Element} container Container element
* @param {Object} [options] See description in constructor
* @param {Object | undefined} json JSON object
* @private
*/
JSONEditor.prototype._create = function (container, options, json) {
this.container = container;
this.options = options || {};
this.json = json || {};
var mode = this.options.mode || 'tree';
this.setMode(mode);
};
/**
* Destroy the editor. Clean up DOM, event listeners, and web workers.
*/
JSONEditor.prototype.destroy = function () {};
/**
* Set JSON object in editor
* @param {Object | undefined} json JSON data
*/
JSONEditor.prototype.set = function (json) {
this.json = json;
};
/**
* Get JSON from the editor
* @returns {Object} json
*/
JSONEditor.prototype.get = function () {
return this.json;
};
/**
* Set string containing JSON for the editor
* @param {String | undefined} jsonText
*/
JSONEditor.prototype.setText = function (jsonText) {
this.json = util.parse(jsonText);
};
/**
* Get stringified JSON contents from the editor
* @returns {String} jsonText
*/
JSONEditor.prototype.getText = function () {
return JSON.stringify(this.json);
};
/**
* Set a field name for the root node.
* @param {String | undefined} name
*/
JSONEditor.prototype.setName = function (name) {
if (!this.options) {
this.options = {};
}
this.options.name = name;
};
/**
* Get the field name for the root node.
* @return {String | undefined} name
*/
JSONEditor.prototype.getName = function () {
return this.options && this.options.name;
};
/**
* Change the mode of the editor.
* JSONEditor will be extended with all methods needed for the chosen mode.
* @param {String} mode Available modes: 'tree' (default), 'view', 'form',
* 'text', and 'code'.
*/
JSONEditor.prototype.setMode = function (mode) {
var container = this.container;
var options = util.extend({}, this.options);
var oldMode = options.mode;
var data;
var name;
options.mode = mode;
var config = JSONEditor.modes[mode];
if (config) {
try {
var asText = (config.data == 'text');
name = this.getName();
data = this[asText ? 'getText' : 'get'](); // get text or json
this.destroy();
util.clear(this);
util.extend(this, config.mixin);
this.create(container, options);
this.setName(name);
this[asText ? 'setText' : 'set'](data); // set text or json
if (typeof config.load === 'function') {
try {
config.load.call(this);
}
catch (err) {
console.error(err);
}
}
if (typeof options.onModeChange === 'function' && mode !== oldMode) {
try {
options.onModeChange(mode, oldMode);
}
catch (err) {
console.error(err);
}
}
}
catch (err) {
this._onError(err);
}
}
else {
throw new Error('Unknown mode "' + options.mode + '"');
}
};
/**
* Get the current mode
* @return {string}
*/
JSONEditor.prototype.getMode = function () {
return this.options.mode;
};
/**
* Throw an error. If an error callback is configured in options.error, this
* callback will be invoked. Else, a regular error is thrown.
* @param {Error} err
* @private
*/
JSONEditor.prototype._onError = function(err) {
if (this.options && typeof this.options.onError === 'function') {
this.options.onError(err);
}
else {
throw err;
}
};
/**
* Set a JSON schema for validation of the JSON object.
* To remove the schema, call JSONEditor.setSchema(null)
* @param {Object | null} schema
*/
JSONEditor.prototype.setSchema = function (schema) {
// compile a JSON schema validator if a JSON schema is provided
if (schema) {
var ajv;
try {
// grab ajv from options if provided, else create a new instance
ajv = this.options.ajv || Ajv({ allErrors: true, verbose: true });
}
catch (err) {
console.warn('Failed to create an instance of Ajv, JSON Schema validation is not available. Please use a JSONEditor bundle including Ajv, or pass an instance of Ajv as via the configuration option `ajv`.');
}
if (ajv) {
this.validateSchema = ajv.compile(schema);
// add schema to the options, so that when switching to an other mode,
// the set schema is not lost
this.options.schema = schema;
// validate now
this.validate();
}
this.refresh(); // update DOM
}
else {
// remove current schema
this.validateSchema = null;
this.options.schema = null;
this.validate(); // to clear current error messages
this.refresh(); // update DOM
}
};
/**
* Validate current JSON object against the configured JSON schema
* Throws an exception when no JSON schema is configured
*/
JSONEditor.prototype.validate = function () {
// must be implemented by treemode and textmode
};
/**
* Refresh the rendered contents
*/
JSONEditor.prototype.refresh = function () {
// can be implemented by treemode and textmode
};
/**
* Register a plugin with one ore multiple modes for the JSON Editor.
*
* A mode is described as an object with properties:
*
* - `mode: String` The name of the mode.
* - `mixin: Object` An object containing the mixin functions which
* will be added to the JSONEditor. Must contain functions
* create, get, getText, set, and setText. May have
* additional functions.
* When the JSONEditor switches to a mixin, all mixin
* functions are added to the JSONEditor, and then
* the function `create(container, options)` is executed.
* - `data: 'text' | 'json'` The type of data that will be used to load the mixin.
* - `[load: function]` An optional function called after the mixin
* has been loaded.
*
* @param {Object | Array} mode A mode object or an array with multiple mode objects.
*/
JSONEditor.registerMode = function (mode) {
var i, prop;
if (util.isArray(mode)) {
// multiple modes
for (i = 0; i < mode.length; i++) {
JSONEditor.registerMode(mode[i]);
}
}
else {
// validate the new mode
if (!('mode' in mode)) throw new Error('Property "mode" missing');
if (!('mixin' in mode)) throw new Error('Property "mixin" missing');
if (!('data' in mode)) throw new Error('Property "data" missing');
var name = mode.mode;
if (name in JSONEditor.modes) {
throw new Error('Mode "' + name + '" already registered');
}
// validate the mixin
if (typeof mode.mixin.create !== 'function') {
throw new Error('Required function "create" missing on mixin');
}
var reserved = ['setMode', 'registerMode', 'modes'];
for (i = 0; i < reserved.length; i++) {
prop = reserved[i];
if (prop in mode.mixin) {
throw new Error('Reserved property "' + prop + '" not allowed in mixin');
}
}
JSONEditor.modes[name] = mode;
}
};
// register tree and text modes
JSONEditor.registerMode(treemode);
JSONEditor.registerMode(textmode);
module.exports = JSONEditor;

View File

@ -1,114 +0,0 @@
'use strict';
var ContextMenu = require('./ContextMenu');
/**
* Create a select box to be used in the editor menu's, which allows to switch mode
* @param {HTMLElement} container
* @param {String[]} modes Available modes: 'code', 'form', 'text', 'tree', 'view'
* @param {String} current Available modes: 'code', 'form', 'text', 'tree', 'view'
* @param {function(mode: string)} onSwitch Callback invoked on switch
* @constructor
*/
function ModeSwitcher(container, modes, current, onSwitch) {
// available modes
var availableModes = {
code: {
'text': 'Code',
'title': 'Switch to code highlighter',
'click': function () {
onSwitch('code')
}
},
form: {
'text': 'Form',
'title': 'Switch to form editor',
'click': function () {
onSwitch('form');
}
},
text: {
'text': 'Text',
'title': 'Switch to plain text editor',
'click': function () {
onSwitch('text');
}
},
tree: {
'text': 'Tree',
'title': 'Switch to tree editor',
'click': function () {
onSwitch('tree');
}
},
view: {
'text': 'View',
'title': 'Switch to tree view',
'click': function () {
onSwitch('view');
}
}
};
// list the selected modes
var items = [];
for (var i = 0; i < modes.length; i++) {
var mode = modes[i];
var item = availableModes[mode];
if (!item) {
throw new Error('Unknown mode "' + mode + '"');
}
item.className = 'jsoneditor-type-modes' + ((current == mode) ? ' jsoneditor-selected' : '');
items.push(item);
}
// retrieve the title of current mode
var currentMode = availableModes[current];
if (!currentMode) {
throw new Error('Unknown mode "' + current + '"');
}
var currentTitle = currentMode.text;
// create the html element
var box = document.createElement('button');
box.className = 'jsoneditor-modes jsoneditor-separator';
box.innerHTML = currentTitle + ' &#x25BE;';
box.title = 'Switch editor mode';
box.onclick = function () {
var menu = new ContextMenu(items);
menu.show(box);
};
var frame = document.createElement('div');
frame.className = 'jsoneditor-modes';
frame.style.position = 'relative';
frame.appendChild(box);
container.appendChild(frame);
this.dom = {
container: container,
box: box,
frame: frame
};
}
/**
* Set focus to switcher
*/
ModeSwitcher.prototype.focus = function () {
this.dom.box.focus();
};
/**
* Destroy the ModeSwitcher, remove from DOM
*/
ModeSwitcher.prototype.destroy = function () {
if (this.dom && this.dom.frame && this.dom.frame.parentNode) {
this.dom.frame.parentNode.removeChild(this.dom.frame);
}
this.dom = null;
};
module.exports = ModeSwitcher;

File diff suppressed because it is too large Load Diff

View File

@ -1,312 +0,0 @@
'use strict';
/**
* @constructor SearchBox
* Create a search box in given HTML container
* @param {JSONEditor} editor The JSON Editor to attach to
* @param {Element} container HTML container element of where to
* create the search box
*/
function SearchBox (editor, container) {
var searchBox = this;
this.editor = editor;
this.timeout = undefined;
this.delay = 200; // ms
this.lastText = undefined;
this.dom = {};
this.dom.container = container;
var table = document.createElement('table');
this.dom.table = table;
table.className = 'jsoneditor-search';
container.appendChild(table);
var tbody = document.createElement('tbody');
this.dom.tbody = tbody;
table.appendChild(tbody);
var tr = document.createElement('tr');
tbody.appendChild(tr);
var td = document.createElement('td');
tr.appendChild(td);
var results = document.createElement('div');
this.dom.results = results;
results.className = 'jsoneditor-results';
td.appendChild(results);
td = document.createElement('td');
tr.appendChild(td);
var divInput = document.createElement('div');
this.dom.input = divInput;
divInput.className = 'jsoneditor-frame';
divInput.title = 'Search fields and values';
td.appendChild(divInput);
// table to contain the text input and search button
var tableInput = document.createElement('table');
divInput.appendChild(tableInput);
var tbodySearch = document.createElement('tbody');
tableInput.appendChild(tbodySearch);
tr = document.createElement('tr');
tbodySearch.appendChild(tr);
var refreshSearch = document.createElement('button');
refreshSearch.className = 'jsoneditor-refresh';
td = document.createElement('td');
td.appendChild(refreshSearch);
tr.appendChild(td);
var search = document.createElement('input');
this.dom.search = search;
search.oninput = function (event) {
searchBox._onDelayedSearch(event);
};
search.onchange = function (event) { // For IE 9
searchBox._onSearch();
};
search.onkeydown = function (event) {
searchBox._onKeyDown(event);
};
search.onkeyup = function (event) {
searchBox._onKeyUp(event);
};
refreshSearch.onclick = function (event) {
search.select();
};
// TODO: ESC in FF restores the last input, is a FF bug, https://bugzilla.mozilla.org/show_bug.cgi?id=598819
td = document.createElement('td');
td.appendChild(search);
tr.appendChild(td);
var searchNext = document.createElement('button');
searchNext.title = 'Next result (Enter)';
searchNext.className = 'jsoneditor-next';
searchNext.onclick = function () {
searchBox.next();
};
td = document.createElement('td');
td.appendChild(searchNext);
tr.appendChild(td);
var searchPrevious = document.createElement('button');
searchPrevious.title = 'Previous result (Shift+Enter)';
searchPrevious.className = 'jsoneditor-previous';
searchPrevious.onclick = function () {
searchBox.previous();
};
td = document.createElement('td');
td.appendChild(searchPrevious);
tr.appendChild(td);
}
/**
* Go to the next search result
* @param {boolean} [focus] If true, focus will be set to the next result
* focus is false by default.
*/
SearchBox.prototype.next = function(focus) {
if (this.results != undefined) {
var index = (this.resultIndex != undefined) ? this.resultIndex + 1 : 0;
if (index > this.results.length - 1) {
index = 0;
}
this._setActiveResult(index, focus);
}
};
/**
* Go to the prevous search result
* @param {boolean} [focus] If true, focus will be set to the next result
* focus is false by default.
*/
SearchBox.prototype.previous = function(focus) {
if (this.results != undefined) {
var max = this.results.length - 1;
var index = (this.resultIndex != undefined) ? this.resultIndex - 1 : max;
if (index < 0) {
index = max;
}
this._setActiveResult(index, focus);
}
};
/**
* Set new value for the current active result
* @param {Number} index
* @param {boolean} [focus] If true, focus will be set to the next result.
* focus is false by default.
* @private
*/
SearchBox.prototype._setActiveResult = function(index, focus) {
// de-activate current active result
if (this.activeResult) {
var prevNode = this.activeResult.node;
var prevElem = this.activeResult.elem;
if (prevElem == 'field') {
delete prevNode.searchFieldActive;
}
else {
delete prevNode.searchValueActive;
}
prevNode.updateDom();
}
if (!this.results || !this.results[index]) {
// out of range, set to undefined
this.resultIndex = undefined;
this.activeResult = undefined;
return;
}
this.resultIndex = index;
// set new node active
var node = this.results[this.resultIndex].node;
var elem = this.results[this.resultIndex].elem;
if (elem == 'field') {
node.searchFieldActive = true;
}
else {
node.searchValueActive = true;
}
this.activeResult = this.results[this.resultIndex];
node.updateDom();
// TODO: not so nice that the focus is only set after the animation is finished
node.scrollTo(function () {
if (focus) {
node.focus(elem);
}
});
};
/**
* Cancel any running onDelayedSearch.
* @private
*/
SearchBox.prototype._clearDelay = function() {
if (this.timeout != undefined) {
clearTimeout(this.timeout);
delete this.timeout;
}
};
/**
* Start a timer to execute a search after a short delay.
* Used for reducing the number of searches while typing.
* @param {Event} event
* @private
*/
SearchBox.prototype._onDelayedSearch = function (event) {
// execute the search after a short delay (reduces the number of
// search actions while typing in the search text box)
this._clearDelay();
var searchBox = this;
this.timeout = setTimeout(function (event) {
searchBox._onSearch();
},
this.delay);
};
/**
* Handle onSearch event
* @param {boolean} [forceSearch] If true, search will be executed again even
* when the search text is not changed.
* Default is false.
* @private
*/
SearchBox.prototype._onSearch = function (forceSearch) {
this._clearDelay();
var value = this.dom.search.value;
var text = (value.length > 0) ? value : undefined;
if (text != this.lastText || forceSearch) {
// only search again when changed
this.lastText = text;
this.results = this.editor.search(text);
this._setActiveResult(undefined);
// display search results
if (text != undefined) {
var resultCount = this.results.length;
switch (resultCount) {
case 0: this.dom.results.innerHTML = 'no&nbsp;results'; break;
case 1: this.dom.results.innerHTML = '1&nbsp;result'; break;
default: this.dom.results.innerHTML = resultCount + '&nbsp;results'; break;
}
}
else {
this.dom.results.innerHTML = '';
}
}
};
/**
* Handle onKeyDown event in the input box
* @param {Event} event
* @private
*/
SearchBox.prototype._onKeyDown = function (event) {
var keynum = event.which;
if (keynum == 27) { // ESC
this.dom.search.value = ''; // clear search
this._onSearch();
event.preventDefault();
event.stopPropagation();
}
else if (keynum == 13) { // Enter
if (event.ctrlKey) {
// force to search again
this._onSearch(true);
}
else if (event.shiftKey) {
// move to the previous search result
this.previous();
}
else {
// move to the next search result
this.next();
}
event.preventDefault();
event.stopPropagation();
}
};
/**
* Handle onKeyUp event in the input box
* @param {Event} event
* @private
*/
SearchBox.prototype._onKeyUp = function (event) {
var keynum = event.keyCode;
if (keynum != 27 && keynum != 13) { // !show and !Enter
this._onDelayedSearch(event); // For IE 9
}
};
/**
* Clear the search results
*/
SearchBox.prototype.clear = function () {
this.dom.search.value = '';
this._onSearch();
};
/**
* Destroy the search box
*/
SearchBox.prototype.destroy = function () {
this.editor = null;
this.dom.container.removeChild(this.dom.table);
this.dom = null;
this.results = null;
this.activeResult = null;
this._clearDelay();
};
module.exports = SearchBox;

View File

@ -1,9 +0,0 @@
// load brace
var ace = require('brace');
// load required ace modules
require('brace/mode/json');
require('brace/ext/searchbox');
require('./theme-jsoneditor');
module.exports = ace;

View File

@ -1,144 +0,0 @@
/* ***** BEGIN LICENSE BLOCK *****
* Distributed under the BSD license:
*
* Copyright (c) 2010, Ajax.org B.V.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* * Neither the name of Ajax.org B.V. nor the
* names of its contributors may be used to endorse or promote products
* derived from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL AJAX.ORG B.V. BE LIABLE FOR ANY
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
* ***** END LICENSE BLOCK ***** */
ace.define('ace/theme/jsoneditor', ['require', 'exports', 'module', 'ace/lib/dom'], function(acequire, exports, module) {
exports.isDark = false;
exports.cssClass = "ace-jsoneditor";
exports.cssText = ".ace-jsoneditor .ace_gutter {\
background: #ebebeb;\
color: #333\
}\
\
.ace-jsoneditor.ace_editor {\
font-family: droid sans mono, consolas, monospace, courier new, courier, sans-serif;\
line-height: 1.3;\
}\
.ace-jsoneditor .ace_print-margin {\
width: 1px;\
background: #e8e8e8\
}\
.ace-jsoneditor .ace_scroller {\
background-color: #FFFFFF\
}\
.ace-jsoneditor .ace_text-layer {\
color: gray\
}\
.ace-jsoneditor .ace_variable {\
color: #1a1a1a\
}\
.ace-jsoneditor .ace_cursor {\
border-left: 2px solid #000000\
}\
.ace-jsoneditor .ace_overwrite-cursors .ace_cursor {\
border-left: 0px;\
border-bottom: 1px solid #000000\
}\
.ace-jsoneditor .ace_marker-layer .ace_selection {\
background: lightgray\
}\
.ace-jsoneditor.ace_multiselect .ace_selection.ace_start {\
box-shadow: 0 0 3px 0px #FFFFFF;\
border-radius: 2px\
}\
.ace-jsoneditor .ace_marker-layer .ace_step {\
background: rgb(255, 255, 0)\
}\
.ace-jsoneditor .ace_marker-layer .ace_bracket {\
margin: -1px 0 0 -1px;\
border: 1px solid #BFBFBF\
}\
.ace-jsoneditor .ace_marker-layer .ace_active-line {\
background: #FFFBD1\
}\
.ace-jsoneditor .ace_gutter-active-line {\
background-color : #dcdcdc\
}\
.ace-jsoneditor .ace_marker-layer .ace_selected-word {\
border: 1px solid lightgray\
}\
.ace-jsoneditor .ace_invisible {\
color: #BFBFBF\
}\
.ace-jsoneditor .ace_keyword,\
.ace-jsoneditor .ace_meta,\
.ace-jsoneditor .ace_support.ace_constant.ace_property-value {\
color: #AF956F\
}\
.ace-jsoneditor .ace_keyword.ace_operator {\
color: #484848\
}\
.ace-jsoneditor .ace_keyword.ace_other.ace_unit {\
color: #96DC5F\
}\
.ace-jsoneditor .ace_constant.ace_language {\
color: darkorange\
}\
.ace-jsoneditor .ace_constant.ace_numeric {\
color: red\
}\
.ace-jsoneditor .ace_constant.ace_character.ace_entity {\
color: #BF78CC\
}\
.ace-jsoneditor .ace_invalid {\
color: #FFFFFF;\
background-color: #FF002A;\
}\
.ace-jsoneditor .ace_fold {\
background-color: #AF956F;\
border-color: #000000\
}\
.ace-jsoneditor .ace_storage,\
.ace-jsoneditor .ace_support.ace_class,\
.ace-jsoneditor .ace_support.ace_function,\
.ace-jsoneditor .ace_support.ace_other,\
.ace-jsoneditor .ace_support.ace_type {\
color: #C52727\
}\
.ace-jsoneditor .ace_string {\
color: green\
}\
.ace-jsoneditor .ace_comment {\
color: #BCC8BA\
}\
.ace-jsoneditor .ace_entity.ace_name.ace_tag,\
.ace-jsoneditor .ace_entity.ace_other.ace_attribute-name {\
color: #606060\
}\
.ace-jsoneditor .ace_markup.ace_underline {\
text-decoration: underline\
}\
.ace-jsoneditor .ace_indent-guide {\
background: url(\"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAACCAYAAACZgbYnAAAAE0lEQVQImWP4////f4bLly//BwAmVgd1/w11/gAAAABJRU5ErkJggg==\") right repeat-y\
}";
var dom = acequire("../lib/dom");
dom.importCssString(exports.cssText, exports.cssClass);
});

View File

@ -1,228 +0,0 @@
'use strict';
var util = require('./util');
var ContextMenu = require('./ContextMenu');
/**
* A factory function to create an AppendNode, which depends on a Node
* @param {Node} Node
*/
function appendNodeFactory(Node) {
/**
* @constructor AppendNode
* @extends Node
* @param {TreeEditor} editor
* Create a new AppendNode. This is a special node which is created at the
* end of the list with childs for an object or array
*/
function AppendNode (editor) {
/** @type {TreeEditor} */
this.editor = editor;
this.dom = {};
}
AppendNode.prototype = new Node();
/**
* Return a table row with an append button.
* @return {Element} dom TR element
*/
AppendNode.prototype.getDom = function () {
// TODO: implement a new solution for the append node
var dom = this.dom;
if (dom.tr) {
return dom.tr;
}
this._updateEditability();
// a row for the append button
var trAppend = document.createElement('tr');
trAppend.node = this;
dom.tr = trAppend;
// TODO: consistent naming
if (this.editor.options.mode === 'tree') {
// a cell for the dragarea column
dom.tdDrag = document.createElement('td');
// create context menu
var tdMenu = document.createElement('td');
dom.tdMenu = tdMenu;
var menu = document.createElement('button');
menu.className = 'jsoneditor-contextmenu';
menu.title = 'Click to open the actions menu (Ctrl+M)';
dom.menu = menu;
tdMenu.appendChild(dom.menu);
}
// a cell for the contents (showing text 'empty')
var tdAppend = document.createElement('td');
var domText = document.createElement('div');
domText.innerHTML = '(empty)';
domText.className = 'jsoneditor-readonly';
tdAppend.appendChild(domText);
dom.td = tdAppend;
dom.text = domText;
this.updateDom();
return trAppend;
};
/**
* Update the HTML dom of the Node
*/
AppendNode.prototype.updateDom = function () {
var dom = this.dom;
var tdAppend = dom.td;
if (tdAppend) {
tdAppend.style.paddingLeft = (this.getLevel() * 24 + 26) + 'px';
// TODO: not so nice hard coded offset
}
var domText = dom.text;
if (domText) {
domText.innerHTML = '(empty ' + this.parent.type + ')';
}
// attach or detach the contents of the append node:
// hide when the parent has childs, show when the parent has no childs
var trAppend = dom.tr;
if (!this.isVisible()) {
if (dom.tr.firstChild) {
if (dom.tdDrag) {
trAppend.removeChild(dom.tdDrag);
}
if (dom.tdMenu) {
trAppend.removeChild(dom.tdMenu);
}
trAppend.removeChild(tdAppend);
}
}
else {
if (!dom.tr.firstChild) {
if (dom.tdDrag) {
trAppend.appendChild(dom.tdDrag);
}
if (dom.tdMenu) {
trAppend.appendChild(dom.tdMenu);
}
trAppend.appendChild(tdAppend);
}
}
};
/**
* Check whether the AppendNode is currently visible.
* the AppendNode is visible when its parent has no childs (i.e. is empty).
* @return {boolean} isVisible
*/
AppendNode.prototype.isVisible = function () {
return (this.parent.childs.length == 0);
};
/**
* Show a contextmenu for this node
* @param {HTMLElement} anchor The element to attach the menu to.
* @param {function} [onClose] Callback method called when the context menu
* is being closed.
*/
AppendNode.prototype.showContextMenu = function (anchor, onClose) {
var node = this;
var titles = Node.TYPE_TITLES;
var items = [
// create append button
{
'text': 'Append',
'title': 'Append a new field with type \'auto\' (Ctrl+Shift+Ins)',
'submenuTitle': 'Select the type of the field to be appended',
'className': 'jsoneditor-insert',
'click': function () {
node._onAppend('', '', 'auto');
},
'submenu': [
{
'text': 'Auto',
'className': 'jsoneditor-type-auto',
'title': titles.auto,
'click': function () {
node._onAppend('', '', 'auto');
}
},
{
'text': 'Array',
'className': 'jsoneditor-type-array',
'title': titles.array,
'click': function () {
node._onAppend('', []);
}
},
{
'text': 'Object',
'className': 'jsoneditor-type-object',
'title': titles.object,
'click': function () {
node._onAppend('', {});
}
},
{
'text': 'String',
'className': 'jsoneditor-type-string',
'title': titles.string,
'click': function () {
node._onAppend('', '', 'string');
}
}
]
}
];
var menu = new ContextMenu(items, {close: onClose});
menu.show(anchor, this.editor.content);
};
/**
* Handle an event. The event is catched centrally by the editor
* @param {Event} event
*/
AppendNode.prototype.onEvent = function (event) {
var type = event.type;
var target = event.target || event.srcElement;
var dom = this.dom;
// highlight the append nodes parent
var menu = dom.menu;
if (target == menu) {
if (type == 'mouseover') {
this.editor.highlighter.highlight(this.parent);
}
else if (type == 'mouseout') {
this.editor.highlighter.unhighlight();
}
}
// context menu events
if (type == 'click' && target == dom.menu) {
var highlighter = this.editor.highlighter;
highlighter.highlight(this.parent);
highlighter.lock();
util.addClassName(dom.menu, 'jsoneditor-selected');
this.showContextMenu(dom.menu, function () {
util.removeClassName(dom.menu, 'jsoneditor-selected');
highlighter.unlock();
highlighter.unhighlight();
});
}
if (type == 'keydown') {
this.onKeyDown(event);
}
};
return AppendNode;
}
module.exports = appendNodeFactory;

View File

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

View File

@ -1,29 +0,0 @@
/*!
* jsoneditor.js
*
* @brief
* JSONEditor is a web-based tool to view, edit, format, and validate JSON.
* It has various modes such as a tree editor, a code editor, and a plain text
* editor.
*
* Supported browsers: Chrome, Firefox, Safari, Opera, Internet Explorer 8+
*
* @license
* Licensed under the Apache License, Version 2.0 (the "License"); you may not
* use this file except in compliance with the License. You may obtain a copy
* of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations under
* the License.
*
* Copyright (c) 2011-2016 Jos de Jong, http://jsoneditoronline.org
*
* @author Jos de Jong, <wjosdejong@gmail.com>
* @version @@version
* @date @@date
*/

View File

@ -1,489 +0,0 @@
'use strict';
var ace;
try {
ace = require('./ace');
}
catch (err) {
// failed to load ace, no problem, we will fall back to plain text
}
var ModeSwitcher = require('./ModeSwitcher');
var util = require('./util');
// create a mixin with the functions for text mode
var textmode = {};
var MAX_ERRORS = 3; // maximum number of displayed errors at the bottom
/**
* Create a text editor
* @param {Element} container
* @param {Object} [options] Object with options. available options:
* {String} mode Available values:
* "text" (default)
* or "code".
* {Number} indentation Number of indentation
* spaces. 2 by default.
* {function} onChange Callback method
* triggered on change
* {function} onModeChange Callback method
* triggered after setMode
* {Object} ace A custom instance of
* Ace editor.
* {boolean} escapeUnicode If true, unicode
* characters are escaped.
* false by default.
* @private
*/
textmode.create = function (container, options) {
// read options
options = options || {};
this.options = options;
// indentation
if (options.indentation) {
this.indentation = Number(options.indentation);
}
else {
this.indentation = 2; // number of spaces
}
// grab ace from options if provided
var _ace = options.ace ? options.ace : ace;
// determine mode
this.mode = (options.mode == 'code') ? 'code' : 'text';
if (this.mode == 'code') {
// verify whether Ace editor is available and supported
if (typeof _ace === 'undefined') {
this.mode = 'text';
console.warn('Failed to load Ace editor, falling back to plain text mode. Please use a JSONEditor bundle including Ace, or pass Ace as via the configuration option `ace`.');
}
}
// determine theme
this.theme = options.theme || 'ace/theme/jsoneditor';
var me = this;
this.container = container;
this.dom = {};
this.aceEditor = undefined; // ace code editor
this.textarea = undefined; // plain text editor (fallback when Ace is not available)
this.validateSchema = null;
// create a debounced validate function
this._debouncedValidate = util.debounce(this.validate.bind(this), this.DEBOUNCE_INTERVAL);
this.width = container.clientWidth;
this.height = container.clientHeight;
this.frame = document.createElement('div');
this.frame.className = 'jsoneditor jsoneditor-mode-' + this.options.mode;
this.frame.onclick = function (event) {
// prevent default submit action when the editor is located inside a form
event.preventDefault();
};
this.frame.onkeydown = function (event) {
me._onKeyDown(event);
};
// create menu
this.menu = document.createElement('div');
this.menu.className = 'jsoneditor-menu';
this.frame.appendChild(this.menu);
// create format button
var buttonFormat = document.createElement('button');
buttonFormat.className = 'jsoneditor-format';
buttonFormat.title = 'Format JSON data, with proper indentation and line feeds (Ctrl+\\)';
this.menu.appendChild(buttonFormat);
buttonFormat.onclick = function () {
try {
me.format();
me._onChange();
}
catch (err) {
me._onError(err);
}
};
// create compact button
var buttonCompact = document.createElement('button');
buttonCompact.className = 'jsoneditor-compact';
buttonCompact.title = 'Compact JSON data, remove all whitespaces (Ctrl+Shift+\\)';
this.menu.appendChild(buttonCompact);
buttonCompact.onclick = function () {
try {
me.compact();
me._onChange();
}
catch (err) {
me._onError(err);
}
};
// create mode box
if (this.options && this.options.modes && this.options.modes.length) {
this.modeSwitcher = new ModeSwitcher(this.menu, this.options.modes, this.options.mode, function onSwitch(mode) {
// switch mode and restore focus
me.setMode(mode);
me.modeSwitcher.focus();
});
}
this.content = document.createElement('div');
this.content.className = 'jsoneditor-outer';
this.frame.appendChild(this.content);
this.container.appendChild(this.frame);
if (this.mode == 'code') {
this.editorDom = document.createElement('div');
this.editorDom.style.height = '100%'; // TODO: move to css
this.editorDom.style.width = '100%'; // TODO: move to css
this.content.appendChild(this.editorDom);
var aceEditor = _ace.edit(this.editorDom);
aceEditor.$blockScrolling = Infinity;
aceEditor.setTheme(this.theme);
aceEditor.setShowPrintMargin(false);
aceEditor.setFontSize(13);
aceEditor.getSession().setMode('ace/mode/json');
aceEditor.getSession().setTabSize(this.indentation);
aceEditor.getSession().setUseSoftTabs(true);
aceEditor.getSession().setUseWrapMode(true);
aceEditor.commands.bindKey('Ctrl-L', null); // disable Ctrl+L (is used by the browser to select the address bar)
aceEditor.commands.bindKey('Command-L', null); // disable Ctrl+L (is used by the browser to select the address bar)
this.aceEditor = aceEditor;
// TODO: deprecated since v5.0.0. Cleanup backward compatibility some day
if (!this.hasOwnProperty('editor')) {
Object.defineProperty(this, 'editor', {
get: function () {
console.warn('Property "editor" has been renamed to "aceEditor".');
return me.aceEditor;
},
set: function (aceEditor) {
console.warn('Property "editor" has been renamed to "aceEditor".');
me.aceEditor = aceEditor;
}
});
}
var poweredBy = document.createElement('a');
poweredBy.appendChild(document.createTextNode('powered by ace'));
poweredBy.href = 'http://ace.ajax.org';
poweredBy.target = '_blank';
poweredBy.className = 'jsoneditor-poweredBy';
poweredBy.onclick = function () {
// TODO: this anchor falls below the margin of the content,
// therefore the normal a.href does not work. We use a click event
// for now, but this should be fixed.
window.open(poweredBy.href, poweredBy.target);
};
this.menu.appendChild(poweredBy);
// register onchange event
aceEditor.on('change', this._onChange.bind(this));
}
else {
// load a plain text textarea
var textarea = document.createElement('textarea');
textarea.className = 'jsoneditor-text';
textarea.spellcheck = false;
this.content.appendChild(textarea);
this.textarea = textarea;
// register onchange event
if (this.textarea.oninput === null) {
this.textarea.oninput = this._onChange.bind(this);
}
else {
// oninput is undefined. For IE8-
this.textarea.onchange = this._onChange.bind(this);
}
}
this.setSchema(this.options.schema);
};
/**
* Handle a change:
* - Validate JSON schema
* - Send a callback to the onChange listener if provided
* @private
*/
textmode._onChange = function () {
// validate JSON schema (if configured)
this._debouncedValidate();
// trigger the onChange callback
if (this.options.onChange) {
try {
this.options.onChange();
}
catch (err) {
console.error('Error in onChange callback: ', err);
}
}
};
/**
* Event handler for keydown. Handles shortcut keys
* @param {Event} event
* @private
*/
textmode._onKeyDown = function (event) {
var keynum = event.which || event.keyCode;
var handled = false;
if (keynum == 220 && event.ctrlKey) {
if (event.shiftKey) { // Ctrl+Shift+\
this.compact();
this._onChange();
}
else { // Ctrl+\
this.format();
this._onChange();
}
handled = true;
}
if (handled) {
event.preventDefault();
event.stopPropagation();
}
};
/**
* Destroy the editor. Clean up DOM, event listeners, and web workers.
*/
textmode.destroy = function () {
// remove old ace editor
if (this.aceEditor) {
this.aceEditor.destroy();
this.aceEditor = null;
}
if (this.frame && this.container && this.frame.parentNode == this.container) {
this.container.removeChild(this.frame);
}
if (this.modeSwitcher) {
this.modeSwitcher.destroy();
this.modeSwitcher = null;
}
this.textarea = null;
this._debouncedValidate = null;
};
/**
* Compact the code in the formatter
*/
textmode.compact = function () {
var json = this.get();
var text = JSON.stringify(json);
this.setText(text);
};
/**
* Format the code in the formatter
*/
textmode.format = function () {
var json = this.get();
var text = JSON.stringify(json, null, this.indentation);
this.setText(text);
};
/**
* Set focus to the formatter
*/
textmode.focus = function () {
if (this.textarea) {
this.textarea.focus();
}
if (this.aceEditor) {
this.aceEditor.focus();
}
};
/**
* Resize the formatter
*/
textmode.resize = function () {
if (this.aceEditor) {
var force = false;
this.aceEditor.resize(force);
}
};
/**
* Set json data in the formatter
* @param {Object} json
*/
textmode.set = function(json) {
this.setText(JSON.stringify(json, null, this.indentation));
};
/**
* Get json data from the formatter
* @return {Object} json
*/
textmode.get = function() {
var text = this.getText();
var json;
try {
json = util.parse(text); // this can throw an error
}
catch (err) {
// try to sanitize json, replace JavaScript notation with JSON notation
text = util.sanitize(text);
// try to parse again
json = util.parse(text); // this can throw an error
}
return json;
};
/**
* Get the text contents of the editor
* @return {String} jsonText
*/
textmode.getText = function() {
if (this.textarea) {
return this.textarea.value;
}
if (this.aceEditor) {
return this.aceEditor.getValue();
}
return '';
};
/**
* Set the text contents of the editor
* @param {String} jsonText
*/
textmode.setText = function(jsonText) {
var text;
if (this.options.escapeUnicode === true) {
text = util.escapeUnicodeChars(jsonText);
}
else {
text = jsonText;
}
if (this.textarea) {
this.textarea.value = text;
}
if (this.aceEditor) {
// prevent emitting onChange events while setting new text
var originalOnChange = this.options.onChange;
this.options.onChange = null;
this.aceEditor.setValue(text, -1);
this.options.onChange = originalOnChange;
}
// validate JSON schema
this.validate();
};
/**
* Validate current JSON object against the configured JSON schema
* Throws an exception when no JSON schema is configured
*/
textmode.validate = function () {
// clear all current errors
if (this.dom.validationErrors) {
this.dom.validationErrors.parentNode.removeChild(this.dom.validationErrors);
this.dom.validationErrors = null;
this.content.style.marginBottom = '';
this.content.style.paddingBottom = '';
}
var doValidate = false;
var errors = [];
var json;
try {
json = this.get(); // this can fail when there is no valid json
doValidate = true;
}
catch (err) {
// no valid JSON, don't validate
}
// only validate the JSON when parsing the JSON succeeded
if (doValidate && this.validateSchema) {
var valid = this.validateSchema(json);
if (!valid) {
errors = this.validateSchema.errors.map(function (error) {
return util.improveSchemaError(error);
});
}
}
if (errors.length > 0) {
// limit the number of displayed errors
var limit = errors.length > MAX_ERRORS;
if (limit) {
errors = errors.slice(0, MAX_ERRORS);
var hidden = this.validateSchema.errors.length - MAX_ERRORS;
errors.push('(' + hidden + ' more errors...)')
}
var validationErrors = document.createElement('div');
validationErrors.innerHTML = '<table class="jsoneditor-text-errors">' +
'<tbody>' +
errors.map(function (error) {
var message;
if (typeof error === 'string') {
message = '<td colspan="2"><pre>' + error + '</pre></td>';
}
else {
message = '<td>' + error.dataPath + '</td>' +
'<td>' + error.message + '</td>';
}
return '<tr><td><button class="jsoneditor-schema-error"></button></td>' + message + '</tr>'
}).join('') +
'</tbody>' +
'</table>';
this.dom.validationErrors = validationErrors;
this.frame.appendChild(validationErrors);
var height = validationErrors.clientHeight;
this.content.style.marginBottom = (-height) + 'px';
this.content.style.paddingBottom = height + 'px';
}
// update the height of the ace editor
if (this.aceEditor) {
var force = false;
this.aceEditor.resize(force);
}
};
// define modes
module.exports = [
{
mode: 'text',
mixin: textmode,
data: 'text',
load: textmode.format
},
{
mode: 'code',
mixin: textmode,
data: 'text',
load: textmode.format
}
];

File diff suppressed because it is too large Load Diff

View File

@ -1,772 +0,0 @@
'use strict';
var jsonlint = require('./assets/jsonlint/jsonlint');
/**
* Parse JSON using the parser built-in in the browser.
* On exception, the jsonString is validated and a detailed error is thrown.
* @param {String} jsonString
* @return {JSON} json
*/
exports.parse = function parse(jsonString) {
try {
return JSON.parse(jsonString);
}
catch (err) {
// try to throw a more detailed error message using validate
exports.validate(jsonString);
// rethrow the original error
throw err;
}
};
/**
* Sanitize a JSON-like string containing. For example changes JavaScript
* notation into JSON notation.
* This function for example changes a string like "{a: 2, 'b': {c: 'd'}"
* into '{"a": 2, "b": {"c": "d"}'
* @param {string} jsString
* @returns {string} json
*/
exports.sanitize = function (jsString) {
// escape all single and double quotes inside strings
var chars = [];
var i = 0;
//If JSON starts with a function (characters/digits/"_-"), remove this function.
//This is useful for "stripping" JSONP objects to become JSON
//For example: /* some comment */ function_12321321 ( [{"a":"b"}] ); => [{"a":"b"}]
var match = jsString.match(/^\s*(\/\*(.|[\r\n])*?\*\/)?\s*[\da-zA-Z_$]+\s*\(([\s\S]*)\)\s*;?\s*$/);
if (match) {
jsString = match[3];
}
// helper functions to get the current/prev/next character
function curr () { return jsString.charAt(i); }
function next() { return jsString.charAt(i + 1); }
function prev() { return jsString.charAt(i - 1); }
// get the last parsed non-whitespace character
function lastNonWhitespace () {
var p = chars.length - 1;
while (p >= 0) {
var pp = chars[p];
if (pp !== ' ' && pp !== '\n' && pp !== '\r' && pp !== '\t') { // non whitespace
return pp;
}
p--;
}
return '';
}
// skip a block comment '/* ... */'
function skipBlockComment () {
i += 2;
while (i < jsString.length && (curr() !== '*' || next() !== '/')) {
i++;
}
i += 2;
}
// skip a comment '// ...'
function skipComment () {
i += 2;
while (i < jsString.length && (curr() !== '\n')) {
i++;
}
}
// parse single or double quoted string
function parseString(quote) {
chars.push('"');
i++;
var c = curr();
while (i < jsString.length && c !== quote) {
if (c === '"' && prev() !== '\\') {
// unescaped double quote, escape it
chars.push('\\');
}
// handle escape character
if (c === '\\') {
i++;
c = curr();
// remove the escape character when followed by a single quote ', not needed
if (c !== '\'') {
chars.push('\\');
}
}
chars.push(c);
i++;
c = curr();
}
if (c === quote) {
chars.push('"');
i++;
}
}
// parse an unquoted key
function parseKey() {
var specialValues = ['null', 'true', 'false'];
var key = '';
var c = curr();
var regexp = /[a-zA-Z_$\d]/; // letter, number, underscore, dollar character
while (regexp.test(c)) {
key += c;
i++;
c = curr();
}
if (specialValues.indexOf(key) === -1) {
chars.push('"' + key + '"');
}
else {
chars.push(key);
}
}
while(i < jsString.length) {
var c = curr();
if (c === '/' && next() === '*') {
skipBlockComment();
}
else if (c === '/' && next() === '/') {
skipComment();
}
else if (c === '\'' || c === '"') {
parseString(c);
}
else if (/[a-zA-Z_$]/.test(c) && ['{', ','].indexOf(lastNonWhitespace()) !== -1) {
// an unquoted object key (like a in '{a:2}')
parseKey();
}
else {
chars.push(c);
i++;
}
}
return chars.join('');
};
/**
* Escape unicode characters.
* For example input '\u2661' (length 1) will output '\\u2661' (length 5).
* @param {string} text
* @return {string}
*/
exports.escapeUnicodeChars = function (text) {
// see https://www.wikiwand.com/en/UTF-16
// note: we leave surrogate pairs as two individual chars,
// as JSON doesn't interpret them as a single unicode char.
return text.replace(/[\u007F-\uFFFF]/g, function(c) {
return '\\u'+('0000' + c.charCodeAt(0).toString(16)).slice(-4);
})
};
/**
* Validate a string containing a JSON object
* This method uses JSONLint to validate the String. If JSONLint is not
* available, the built-in JSON parser of the browser is used.
* @param {String} jsonString String with an (invalid) JSON object
* @throws Error
*/
exports.validate = function validate(jsonString) {
if (typeof(jsonlint) != 'undefined') {
jsonlint.parse(jsonString);
}
else {
JSON.parse(jsonString);
}
};
/**
* Extend object a with the properties of object b
* @param {Object} a
* @param {Object} b
* @return {Object} a
*/
exports.extend = function extend(a, b) {
for (var prop in b) {
if (b.hasOwnProperty(prop)) {
a[prop] = b[prop];
}
}
return a;
};
/**
* Remove all properties from object a
* @param {Object} a
* @return {Object} a
*/
exports.clear = function clear (a) {
for (var prop in a) {
if (a.hasOwnProperty(prop)) {
delete a[prop];
}
}
return a;
};
/**
* Get the type of an object
* @param {*} object
* @return {String} type
*/
exports.type = function type (object) {
if (object === null) {
return 'null';
}
if (object === undefined) {
return 'undefined';
}
if ((object instanceof Number) || (typeof object === 'number')) {
return 'number';
}
if ((object instanceof String) || (typeof object === 'string')) {
return 'string';
}
if ((object instanceof Boolean) || (typeof object === 'boolean')) {
return 'boolean';
}
if ((object instanceof RegExp) || (typeof object === 'regexp')) {
return 'regexp';
}
if (exports.isArray(object)) {
return 'array';
}
return 'object';
};
/**
* Test whether a text contains a url (matches when a string starts
* with 'http://*' or 'https://*' and has no whitespace characters)
* @param {String} text
*/
var isUrlRegex = /^https?:\/\/\S+$/;
exports.isUrl = function isUrl (text) {
return (typeof text == 'string' || text instanceof String) &&
isUrlRegex.test(text);
};
/**
* Tes whether given object is an Array
* @param {*} obj
* @returns {boolean} returns true when obj is an array
*/
exports.isArray = function (obj) {
return Object.prototype.toString.call(obj) === '[object Array]';
};
/**
* Retrieve the absolute left value of a DOM element
* @param {Element} elem A dom element, for example a div
* @return {Number} left The absolute left position of this element
* in the browser page.
*/
exports.getAbsoluteLeft = function getAbsoluteLeft(elem) {
var rect = elem.getBoundingClientRect();
return rect.left + window.pageXOffset || document.scrollLeft || 0;
};
/**
* Retrieve the absolute top value of a DOM element
* @param {Element} elem A dom element, for example a div
* @return {Number} top The absolute top position of this element
* in the browser page.
*/
exports.getAbsoluteTop = function getAbsoluteTop(elem) {
var rect = elem.getBoundingClientRect();
return rect.top + window.pageYOffset || document.scrollTop || 0;
};
/**
* add a className to the given elements style
* @param {Element} elem
* @param {String} className
*/
exports.addClassName = function addClassName(elem, className) {
var classes = elem.className.split(' ');
if (classes.indexOf(className) == -1) {
classes.push(className); // add the class to the array
elem.className = classes.join(' ');
}
};
/**
* add a className to the given elements style
* @param {Element} elem
* @param {String} className
*/
exports.removeClassName = function removeClassName(elem, className) {
var classes = elem.className.split(' ');
var index = classes.indexOf(className);
if (index != -1) {
classes.splice(index, 1); // remove the class from the array
elem.className = classes.join(' ');
}
};
/**
* Strip the formatting from the contents of a div
* the formatting from the div itself is not stripped, only from its childs.
* @param {Element} divElement
*/
exports.stripFormatting = function stripFormatting(divElement) {
var childs = divElement.childNodes;
for (var i = 0, iMax = childs.length; i < iMax; i++) {
var child = childs[i];
// remove the style
if (child.style) {
// TODO: test if child.attributes does contain style
child.removeAttribute('style');
}
// remove all attributes
var attributes = child.attributes;
if (attributes) {
for (var j = attributes.length - 1; j >= 0; j--) {
var attribute = attributes[j];
if (attribute.specified === true) {
child.removeAttribute(attribute.name);
}
}
}
// recursively strip childs
exports.stripFormatting(child);
}
};
/**
* Set focus to the end of an editable div
* code from Nico Burns
* http://stackoverflow.com/users/140293/nico-burns
* http://stackoverflow.com/questions/1125292/how-to-move-cursor-to-end-of-contenteditable-entity
* @param {Element} contentEditableElement A content editable div
*/
exports.setEndOfContentEditable = function setEndOfContentEditable(contentEditableElement) {
var range, selection;
if(document.createRange) {
range = document.createRange();//Create a range (a range is a like the selection but invisible)
range.selectNodeContents(contentEditableElement);//Select the entire contents of the element with the range
range.collapse(false);//collapse the range to the end point. false means collapse to end rather than the start
selection = window.getSelection();//get the selection object (allows you to change selection)
selection.removeAllRanges();//remove any selections already made
selection.addRange(range);//make the range you have just created the visible selection
}
};
/**
* Select all text of a content editable div.
* http://stackoverflow.com/a/3806004/1262753
* @param {Element} contentEditableElement A content editable div
*/
exports.selectContentEditable = function selectContentEditable(contentEditableElement) {
if (!contentEditableElement || contentEditableElement.nodeName != 'DIV') {
return;
}
var sel, range;
if (window.getSelection && document.createRange) {
range = document.createRange();
range.selectNodeContents(contentEditableElement);
sel = window.getSelection();
sel.removeAllRanges();
sel.addRange(range);
}
};
/**
* Get text selection
* http://stackoverflow.com/questions/4687808/contenteditable-selected-text-save-and-restore
* @return {Range | TextRange | null} range
*/
exports.getSelection = function getSelection() {
if (window.getSelection) {
var sel = window.getSelection();
if (sel.getRangeAt && sel.rangeCount) {
return sel.getRangeAt(0);
}
}
return null;
};
/**
* Set text selection
* http://stackoverflow.com/questions/4687808/contenteditable-selected-text-save-and-restore
* @param {Range | TextRange | null} range
*/
exports.setSelection = function setSelection(range) {
if (range) {
if (window.getSelection) {
var sel = window.getSelection();
sel.removeAllRanges();
sel.addRange(range);
}
}
};
/**
* Get selected text range
* @return {Object} params object containing parameters:
* {Number} startOffset
* {Number} endOffset
* {Element} container HTML element holding the
* selected text element
* Returns null if no text selection is found
*/
exports.getSelectionOffset = function getSelectionOffset() {
var range = exports.getSelection();
if (range && 'startOffset' in range && 'endOffset' in range &&
range.startContainer && (range.startContainer == range.endContainer)) {
return {
startOffset: range.startOffset,
endOffset: range.endOffset,
container: range.startContainer.parentNode
};
}
return null;
};
/**
* Set selected text range in given element
* @param {Object} params An object containing:
* {Element} container
* {Number} startOffset
* {Number} endOffset
*/
exports.setSelectionOffset = function setSelectionOffset(params) {
if (document.createRange && window.getSelection) {
var selection = window.getSelection();
if(selection) {
var range = document.createRange();
if (!params.container.firstChild) {
params.container.appendChild(document.createTextNode(''));
}
// TODO: do not suppose that the first child of the container is a textnode,
// but recursively find the textnodes
range.setStart(params.container.firstChild, params.startOffset);
range.setEnd(params.container.firstChild, params.endOffset);
exports.setSelection(range);
}
}
};
/**
* Get the inner text of an HTML element (for example a div element)
* @param {Element} element
* @param {Object} [buffer]
* @return {String} innerText
*/
exports.getInnerText = function getInnerText(element, buffer) {
var first = (buffer == undefined);
if (first) {
buffer = {
'text': '',
'flush': function () {
var text = this.text;
this.text = '';
return text;
},
'set': function (text) {
this.text = text;
}
};
}
// text node
if (element.nodeValue) {
return buffer.flush() + element.nodeValue;
}
// divs or other HTML elements
if (element.hasChildNodes()) {
var childNodes = element.childNodes;
var innerText = '';
for (var i = 0, iMax = childNodes.length; i < iMax; i++) {
var child = childNodes[i];
if (child.nodeName == 'DIV' || child.nodeName == 'P') {
var prevChild = childNodes[i - 1];
var prevName = prevChild ? prevChild.nodeName : undefined;
if (prevName && prevName != 'DIV' && prevName != 'P' && prevName != 'BR') {
innerText += '\n';
buffer.flush();
}
innerText += exports.getInnerText(child, buffer);
buffer.set('\n');
}
else if (child.nodeName == 'BR') {
innerText += buffer.flush();
buffer.set('\n');
}
else {
innerText += exports.getInnerText(child, buffer);
}
}
return innerText;
}
else {
if (element.nodeName == 'P' && exports.getInternetExplorerVersion() != -1) {
// On Internet Explorer, a <p> with hasChildNodes()==false is
// rendered with a new line. Note that a <p> with
// hasChildNodes()==true is rendered without a new line
// Other browsers always ensure there is a <br> inside the <p>,
// and if not, the <p> does not render a new line
return buffer.flush();
}
}
// br or unknown
return '';
};
/**
* Returns the version of Internet Explorer or a -1
* (indicating the use of another browser).
* Source: http://msdn.microsoft.com/en-us/library/ms537509(v=vs.85).aspx
* @return {Number} Internet Explorer version, or -1 in case of an other browser
*/
exports.getInternetExplorerVersion = function getInternetExplorerVersion() {
if (_ieVersion == -1) {
var rv = -1; // Return value assumes failure.
if (navigator.appName == 'Microsoft Internet Explorer')
{
var ua = navigator.userAgent;
var re = new RegExp("MSIE ([0-9]{1,}[\.0-9]{0,})");
if (re.exec(ua) != null) {
rv = parseFloat( RegExp.$1 );
}
}
_ieVersion = rv;
}
return _ieVersion;
};
/**
* Test whether the current browser is Firefox
* @returns {boolean} isFirefox
*/
exports.isFirefox = function isFirefox () {
return (navigator.userAgent.indexOf("Firefox") != -1);
};
/**
* cached internet explorer version
* @type {Number}
* @private
*/
var _ieVersion = -1;
/**
* Add and event listener. Works for all browsers
* @param {Element} element An html element
* @param {string} action The action, for example "click",
* without the prefix "on"
* @param {function} listener The callback function to be executed
* @param {boolean} [useCapture] false by default
* @return {function} the created event listener
*/
exports.addEventListener = function addEventListener(element, action, listener, useCapture) {
if (element.addEventListener) {
if (useCapture === undefined)
useCapture = false;
if (action === "mousewheel" && exports.isFirefox()) {
action = "DOMMouseScroll"; // For Firefox
}
element.addEventListener(action, listener, useCapture);
return listener;
} else if (element.attachEvent) {
// Old IE browsers
var f = function () {
return listener.call(element, window.event);
};
element.attachEvent("on" + action, f);
return f;
}
};
/**
* Remove an event listener from an element
* @param {Element} element An html dom element
* @param {string} action The name of the event, for example "mousedown"
* @param {function} listener The listener function
* @param {boolean} [useCapture] false by default
*/
exports.removeEventListener = function removeEventListener(element, action, listener, useCapture) {
if (element.removeEventListener) {
if (useCapture === undefined)
useCapture = false;
if (action === "mousewheel" && exports.isFirefox()) {
action = "DOMMouseScroll"; // For Firefox
}
element.removeEventListener(action, listener, useCapture);
} else if (element.detachEvent) {
// Old IE browsers
element.detachEvent("on" + action, listener);
}
};
/**
* Parse a JSON path like '.items[3].name' into an array
* @param {string} jsonPath
* @return {Array}
*/
exports.parsePath = function parsePath(jsonPath) {
var prop, remainder;
if (jsonPath.length === 0) {
return [];
}
// find a match like '.prop'
var match = jsonPath.match(/^\.(\w+)/);
if (match) {
prop = match[1];
remainder = jsonPath.substr(prop.length + 1);
}
else if (jsonPath[0] === '[') {
// find a match like
var end = jsonPath.indexOf(']');
if (end === -1) {
throw new SyntaxError('Character ] expected in path');
}
if (end === 1) {
throw new SyntaxError('Index expected after [');
}
var value = jsonPath.substring(1, end);
prop = value === '*' ? value : JSON.parse(value); // parse string and number
remainder = jsonPath.substr(end + 1);
}
else {
throw new SyntaxError('Failed to parse path');
}
return [prop].concat(parsePath(remainder))
};
/**
* Improve the error message of a JSON schema error
* @param {Object} error
* @return {Object} The error
*/
exports.improveSchemaError = function (error) {
if (error.keyword === 'enum' && Array.isArray(error.schema)) {
var enums = error.schema;
if (enums) {
enums = enums.map(function (value) {
return JSON.stringify(value);
});
if (enums.length > 5) {
var more = ['(' + (enums.length - 5) + ' more...)'];
enums = enums.slice(0, 5);
enums.push(more);
}
error.message = 'should be equal to one of: ' + enums.join(', ');
}
}
if (error.keyword === 'additionalProperties') {
error.message = 'should NOT have additional property: ' + error.params.additionalProperty;
}
return error;
};
/**
* Test whether the child rect fits completely inside the parent rect.
* @param {ClientRect} parent
* @param {ClientRect} child
* @param {number} margin
*/
exports.insideRect = function (parent, child, margin) {
var _margin = margin !== undefined ? margin : 0;
return child.left - _margin >= parent.left
&& child.right + _margin <= parent.right
&& child.top - _margin >= parent.top
&& child.bottom + _margin <= parent.bottom;
};
/**
* Returns a function, that, as long as it continues to be invoked, will not
* be triggered. The function will be called after it stops being called for
* N milliseconds.
*
* Source: https://davidwalsh.name/javascript-debounce-function
*
* @param {function} func
* @param {number} wait Number in milliseconds
* @param {boolean} [immediate=false] If `immediate` is passed, trigger the
* function on the leading edge, instead
* of the trailing.
* @return {function} Return the debounced function
*/
exports.debounce = function debounce(func, wait, immediate) {
var timeout;
return function() {
var context = this, args = arguments;
var later = function() {
timeout = null;
if (!immediate) func.apply(context, args);
};
var callNow = immediate && !timeout;
clearTimeout(timeout);
timeout = setTimeout(later, wait);
if (callNow) func.apply(context, args);
};
};
/**
* Determines the difference between two texts.
* Can only detect one removed or inserted block of characters.
* @param {string} oldText
* @param {string} newText
* @return {{start: number, end: number}} Returns the start and end
* of the changed part in newText.
*/
exports.textDiff = function textDiff(oldText, newText) {
var len = newText.length;
var start = 0;
var oldEnd = oldText.length;
var newEnd = newText.length;
while (newText.charAt(start) === oldText.charAt(start)
&& start < len) {
start++;
}
while (newText.charAt(newEnd - 1) === oldText.charAt(oldEnd - 1)
&& newEnd > start && oldEnd > 0) {
newEnd--;
oldEnd--;
}
return {start: start, end: newEnd};
};

513
src/jsoneditor/actions.js Normal file
View File

@ -0,0 +1,513 @@
import last from 'lodash/last'
import initial from 'lodash/initial'
import isEmpty from 'lodash/isEmpty'
import first from 'lodash/first'
import { findRootPath, toJSON } from './eson'
import { getIn } from './utils/immutabilityHelpers'
import { duplicateInText, findUniqueName } from './utils/stringUtils'
import { isObject, stringConvert } from './utils/typeUtils'
import { compareAsc, compareDesc } from './utils/arrayUtils'
import { compileJSONPointer, parseJSONPointer } from './jsonPointer'
/**
* Create a JSONPatch to change the value of a property or item
* @param {JSON} eson
* @param {Path} path
* @param {*} value
* @return {Array}
*/
export function changeValue (eson, path, value) {
// console.log('changeValue', data, value)
return [{
op: 'replace',
path: compileJSONPointer(path),
value
}]
}
/**
* Create a JSONPatch to change a property name
* @param {JSON} json
* @param {Path} parentPath
* @param {string} oldProp
* @param {string} newProp
* @return {Array}
*/
export function changeProperty (json, parentPath, oldProp, newProp) {
// console.log('changeProperty', parentPath, oldProp, newProp)
const parent = getIn(json, parentPath)
// prevent duplicate property names
const uniqueNewProp = findUniqueName(newProp, parent)
return [{
op: 'move',
from: compileJSONPointer(parentPath.concat(oldProp)),
path: compileJSONPointer(parentPath.concat(uniqueNewProp))
}]
}
/**
* Create a JSONPatch to change the type of a property or item
* @param {JSON} json
* @param {Path} path
* @param {ESONType} type
* @return {Array}
*/
export function changeType (json, path, type) {
const oldValue = getIn(json, path)
const newValue = convertType(oldValue, type)
// console.log('changeType', path, type, oldValue, newValue)
return [{
op: 'replace',
path: compileJSONPointer(path),
value: newValue
}]
}
/**
* Create a JSONPatch for a duplicate action.
*
* This function needs the current data in order to be able to determine
* a unique property name for the duplicated node in case of duplicating
* and object property
*
* @param {JSON} json
* @param {Selection} selection
* @return {Array}
*/
export function duplicate (json, selection) {
if (!isEmpty(selection.multi)) {
const rootPath = findRootPath(selection)
const root = getIn(json, rootPath)
const paths = selection.multi.map(parseJSONPointer)
if (Array.isArray(root)) {
const lastPath = last(paths)
const offset = lastPath ? (parseInt(last(lastPath), 10) + 1) : 0
return paths.map((path, index) => ({
op: 'copy',
from: compileJSONPointer(path),
path: compileJSONPointer(rootPath.concat(index + offset))
}))
}
else { // 'object'
return paths.map(path => {
const prop = last(path)
const newProp = findUniqueName(prop, root)
return {
op: 'copy',
from: compileJSONPointer(path),
path: compileJSONPointer(rootPath.concat(newProp))
}
})
}
}
if (selection.type === 'caret') {
if (selection.anchorOffset === selection.focusOffset) {
// no text selected -> duplicate the current node
return duplicate(json, {
type: 'multi',
multi: [selection.path]
})
}
else {
// no text selected -> duplicate selected text
if (selection.input === 'property') {
const path = parseJSONPointer(selection.path)
const parentPath = initial(path)
const oldProperty = last(path)
const newProperty = duplicateInText(oldProperty, selection.anchorOffset, selection.focusOffset)
return changeProperty(json, parentPath, oldProperty, newProperty)
}
else { // selection.input === 'value'
const oldValue = String(toJSON(getIn(json, parseJSONPointer(selection.path))))
const newValue = duplicateInText(oldValue, selection.anchorOffset, selection.focusOffset)
return changeValue(json, parseJSONPointer(selection.path), newValue)
}
}
}
return []
}
/**
* Create a JSONPatch for an insert action.
*
* This function needs the current data in order to be able to determine
* a unique property name for the inserted node in case of duplicating
* and object property
*
* @param {JSON} json
* @param {Path} path
* @param {Array.<{name?: string, value: JSON}>} values
* @return {Array}
*/
export function insertBefore (json, path, values) { // TODO: find a better name and define datastructure for values
// TODO: refactor. path should be parent path
const parentPath = initial(path)
const parent = getIn(json, parentPath)
if (Array.isArray(parent)) {
const startIndex = parseInt(last(path), 10)
return values.map((entry, offset) => ({
op: 'add',
path: compileJSONPointer(parentPath.concat(startIndex + offset)),
value: entry.value
}))
}
else { // 'object'
return values.map(entry => {
const newProp = findUniqueName(entry.name, parent)
return {
op: 'add',
path: compileJSONPointer(parentPath.concat(newProp)),
value: entry.value
}
})
}
}
/**
* Create a JSONPatch for an insert action.
*
* This function needs the current data in order to be able to determine
* a unique property name for the inserted node in case of duplicating
* and object property
*
* @param {JSON} json
* @param {Path} path
* @param {Array.<{name?: string, value: JSON}>} values
* @return {Array}
*/
export function insertAfter (json, path, values) { // TODO: find a better name and define datastructure for values
// TODO: refactor. path should be parent path
const parentPath = initial(path)
const parent = getIn(json, parentPath)
if (Array.isArray(parent)) {
const startIndex = parseInt(last(path), 10)
return values.map((entry, offset) => ({
op: 'add',
path: compileJSONPointer(parentPath.concat(startIndex + 1 + offset)), // +1 to insert after
value: entry.value
}))
}
else { // 'object'
return values.map(entry => {
const newProp = findUniqueName(entry.name, parent)
return {
op: 'add',
path: compileJSONPointer(parentPath.concat(newProp)),
value: entry.value
}
})
}
}
/**
* Insert values at the start of an Object or Array
* @param {JSON} json
* @param {Path} parentPath
* @param {Array.<{name?: string, value: JSON}>} values
* @return {Array}
*/
export function insertInside (json, parentPath, values) {
const parent = getIn(json, parentPath)
if (Array.isArray(parent)) {
return insertBefore(json, parentPath.concat('0'), values)
}
else if (parent && typeof parent === 'object') {
// TODO: refactor. path should be parent path
return insertBefore(json, parentPath.concat('foobar'), values)
}
else {
throw new Error('Cannot insert in a value, only in an Object or Array')
}
}
/**
* Create a JSONPatch for an insert action.
*
* This function needs the current data in order to be able to determine
* a unique property name for the inserted node in case of duplicating
* and object property
*
* @param {JSON} json
* @param {Selection} selection
* @param {Array.<{name?: string, value: JSON}>} values
* @return {Array}
*/
export function replace (json, selection, values) { // TODO: find a better name and define datastructure for values
const rootPath = findRootPath(selection)
const root = getIn(json, rootPath)
const paths = selection.multi
? selection.multi.map(parseJSONPointer)
: []
if (Array.isArray(root)) {
const firstPath = first(paths)
const offset = firstPath ? parseInt(last(firstPath), 10) : 0
const removeActions = removeAll(paths)
const insertActions = values.map((entry, index) => ({
op: 'add',
path: compileJSONPointer(rootPath.concat(index + offset)),
value: entry.value
}))
return removeActions.concat(insertActions)
}
else { // root is Object
const removeActions = removeAll(paths)
const insertActions = values.map(entry => {
const newProp = findUniqueName(entry.name, root)
return {
op: 'add',
path: compileJSONPointer(rootPath.concat(newProp)),
value: entry.value
}
})
return removeActions.concat(insertActions)
}
}
/**
* Create a JSONPatch for an append action.
*
* This function needs the current data in order to be able to determine
* a unique property name for the inserted node in case of duplicating
* and object property
*
* @param {JSON} json
* @param {Path} parentPath
* @param {ESONType} type
* @return {Array}
*/
export function append (json, parentPath, type) {
// console.log('append', parentPath, value)
const parent = getIn(json, parentPath)
const value = createEntry(type)
if (Array.isArray(parent)) {
return [{
op: 'add',
path: compileJSONPointer(parentPath.concat('-')),
value
}]
}
else { // 'object'
const newProp = findUniqueName('', parent)
return [{
op: 'add',
path: compileJSONPointer(parentPath.concat(newProp)),
value
}]
}
}
/**
* Create a JSONPatch for a remove action
* @param {Path} path
* @return {JSONPatchDocument}
*/
export function remove (path) {
return [{
op: 'remove',
path: compileJSONPointer(path)
}]
}
/**
* Create a JSONPatch for a multiple remove action
* @param {Path[]} paths
* @return {JSONPatchDocument}
*/
export function removeAll (paths) {
return paths
.map(path => ({
op: 'remove',
path: compileJSONPointer(path)
}))
.reverse() // reverse is needed for arrays: delete the last index first
}
// TODO: test removeAll
/**
* Create a JSONPatch to order the items of an array or the properties of an object in ascending
* or descending order. In case of ESON, this will maintain state of expanded items
* @param {JSON} json
* @param {Path} path
* @param {'asc' | 'desc' | null} [order=null] If not provided, will toggle current ordering
* @return {Array}
*/
export function sort (json, path, order = null) {
const compare = order === 'desc' ? compareDesc : compareAsc
const reverseCompare = (a, b) => -compare(a, b)
const object = getIn(json, path)
if (Array.isArray(object)) {
const createAction = ({item, fromIndex, toIndex}) => ({
op: 'move',
from: compileJSONPointer(path.concat(String(fromIndex))),
path: compileJSONPointer(path.concat(String(toIndex)))
})
const actions = sortWithComparator(object, compare).map(createAction)
// when no order is provided, test whether ordering ascending
// changed anything. If not, sort descending
if (!order && isEmpty(actions)) {
return sortWithComparator(object, reverseCompare).map(createAction)
}
return actions
}
else { // object is an Object, we don't allow sorting properties
return []
}
}
/**
* Sort an array with items using given comparator, and generate move actions
* (json patch) to apply the ordering.
*
* @param {Array} items
* @param {function (a, b)} comparator Accepts to values,
* returns 1 when a is larger than b
* returns 0 when a is equal to b
* returns -1 when a is smaller than b
* @return {Array.<{item: *, beforeItem: *, fromIndex: number, toIndex: number}>}
* Returns an array with move actions that need to be
* performed to order the items of the array.
* This can be turned into json-patch actions
*/
function sortWithComparator (items, comparator) {
const orderedItems = items.slice()
let moveActions = []
for (let i = 0; i < orderedItems.length; i++) {
let firstIndex = i
for (let j = i; j < orderedItems.length; j++) {
if (comparator(orderedItems[firstIndex], orderedItems[j]) > 0) {
firstIndex = j
}
}
if (i !== firstIndex) {
const item = orderedItems[firstIndex]
orderedItems.splice(firstIndex, 1)
orderedItems.unshift(item)
moveActions.push({
item,
fromIndex: firstIndex,
toIndex: i
})
}
}
return moveActions
}
/**
* Create a JSON entry
* @param {ESONType} type
* @return {Array | Object | string}
*/
export function createEntry (type) {
if (type === 'array') {
return []
}
else if (type === 'object') {
return {}
}
else {
return ''
}
}
/**
* Convert a JSON object into a different type. When possible, data is retained
* @param {*} value
* @param {ESONType} type
* @return {*}
*/
export function convertType (value, type) {
// convert contents from old value to new value where possible
if (type === 'value') {
if (typeof value === 'string') {
return stringConvert(value)
}
else {
return ''
}
}
if (type === 'string') {
if (!isObject(value) && !Array.isArray(value)) {
return value + ''
}
else {
return ''
}
}
if (type === 'object') {
let object = {}
if (Array.isArray(value)) {
value.forEach((item, index) => object[index] = item)
}
return object
}
if (type === 'array') {
let array = []
if (isObject(value)) {
Object.keys(value).forEach(key => {
array.push(value[key])
})
}
return array
}
throw new Error(`Unknown type '${type}'`)
}
/**
* Extract the patched nodes and create a selection
* @param {JSONPatchDocument} operations
* @return {Selection | null}
*/
export function getSelectionFromPatch (operations) {
const paths = operations
.filter(operation => operation.op !== 'remove' && operation.op !== 'test')
.map(operation => operation.path)
if (!isEmpty(paths)) {
console.log('selectPatchedPaths', paths)
return {
type: 'multi', multi: paths }
}
// TODO: after a remove, select after?
return null
}

View File

@ -0,0 +1,72 @@
'use strict'
import { sort } from './actions'
import { createAssertEqualEson } from './utils/assertEqualEson'
import { ID, syncEson } from './eson'
import { immutableJSONPatch } from './immutableJSONPatch'
const assertEqualEson = createAssertEqualEson(expect)
// TODO: test changeValue
// TODO: test changeProperty
// TODO: test changeType (or cleanup the function)
// TODO: test duplicate
// TODO: test insertBefore
// TODO: test replace
// TODO: test append
// TODO: test remove
// TODO: test removeAll
it('sort root Array (json)', () => {
const json = [1,3,2]
const sorted1 = immutableJSONPatch(json, sort(json, [])).json
const sorted2 = immutableJSONPatch(json, sort(json, [], 'asc')).json
const sorted3 = immutableJSONPatch(json, sort(json, [], 'desc')).json
assertEqualEson(json, [1,3, 2]) // should be untouched
assertEqualEson(sorted1, [1,2,3])
assertEqualEson(sorted2, [1,2,3])
assertEqualEson(sorted3, [3,2,1])
})
it('sort root Array (eson)', () => {
const eson = syncEson([1,3,2])
const sorted1 = immutableJSONPatch(eson, sort(eson, [])).json
const sorted2 = immutableJSONPatch(eson, sort(eson, [], 'asc')).json
const sorted3 = immutableJSONPatch(eson, sort(eson, [], 'desc')).json
assertEqualEson(sorted1, syncEson([1, 2, 3]))
assertEqualEson(sorted1[0], eson[0], false)
assertEqualEson(sorted1[1], eson[2], false)
assertEqualEson(sorted1[2], eson[1], false)
expect(sorted1).not.toBe(eson)
assertEqualEson(sorted2[0], eson[0], false)
assertEqualEson(sorted2[1], eson[2], false)
assertEqualEson(sorted2[2], eson[1], false)
expect(sorted2).not.toBe(eson)
assertEqualEson(sorted3[0], eson[1], false)
assertEqualEson(sorted3[1], eson[2], false)
assertEqualEson(sorted3[2], eson[0], false)
expect(sorted3).not.toBe(eson)
})
it('sort nested Array', () => {
const eson = syncEson({arr: [4,1,8,5,3,9,2,7,6]})
const actual = immutableJSONPatch(eson, sort(eson, ['arr'])).json
const expected = syncEson({arr: [1,2,3,4,5,6,7,8,9]})
assertEqualEson(actual, expected)
})
it('sort nested Array reverse order', () => {
// no order provided -> order ascending, but if nothing changes, order descending
const eson = syncEson({arr: [1,2,3,4,5,6,7,8,9]})
const actual = immutableJSONPatch(eson, sort(eson, ['arr'])).json
const expected = syncEson({arr: [9,8,7,6,5,4,3,2,1]})
assertEqualEson(actual, expected)
// id's and META should be the same
expect(actual.arr[ID]).toEqual(eson.arr[ID])
expect(actual.arr[7][ID]).toEqual(eson.arr[1][ID])
})

View File

@ -0,0 +1,8 @@
// load brace
import ace from 'brace'
// load required ace plugins
import 'brace/mode/json'
import 'brace/ext/searchbox'
export default ace

View File

@ -0,0 +1,144 @@
/* ***** BEGIN LICENSE BLOCK *****
* Distributed under the BSD license:
*
* Copyright (c) 2010, Ajax.org B.V.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* * Neither the name of Ajax.org B.V. nor the
* names of its contributors may be used to endorse or promote products
* derived from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL AJAX.ORG B.V. BE LIABLE FOR ANY
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
* ***** END LICENSE BLOCK ***** */
window.ace.define('ace/theme/jsoneditor', ['require', 'exports', 'module', 'ace/lib/dom'], function(acequire, exports, module) {
exports.isDark = false
exports.cssClass = 'ace-jsoneditor'
exports.cssText = `.ace-jsoneditor .ace_gutter {
background: #ebebeb;
color: #333
}
.ace-jsoneditor.ace_editor {
font-family: "dejavu sans mono", "droid sans mono", consolas, monaco, "lucida console", "courier new", courier, monospace, sans-serif;
line-height: 1.3;
}
.ace-jsoneditor .ace_print-margin {
width: 1px;
background: #e8e8e8
}
.ace-jsoneditor .ace_scroller {
background-color: #FFFFFF
}
.ace-jsoneditor .ace_text-layer {
color: gray
}
.ace-jsoneditor .ace_variable {
color: #1a1a1a
}
.ace-jsoneditor .ace_cursor {
border-left: 2px solid #000000
}
.ace-jsoneditor .ace_overwrite-cursors .ace_cursor {
border-left: 0px;
border-bottom: 1px solid #000000
}
.ace-jsoneditor .ace_marker-layer .ace_selection {
background: lightgray
}
.ace-jsoneditor.ace_multiselect .ace_selection.ace_start {
box-shadow: 0 0 3px 0px #FFFFFF;
border-radius: 2px
}
.ace-jsoneditor .ace_marker-layer .ace_step {
background: rgb(255, 255, 0)
}
.ace-jsoneditor .ace_marker-layer .ace_bracket {
margin: -1px 0 0 -1px;
border: 1px solid #BFBFBF
}
.ace-jsoneditor .ace_marker-layer .ace_active-line {
background: #FFFBD1
}
.ace-jsoneditor .ace_gutter-active-line {
background-color : #dcdcdc
}
.ace-jsoneditor .ace_marker-layer .ace_selected-word {
border: 1px solid lightgray
}
.ace-jsoneditor .ace_invisible {
color: #BFBFBF
}
.ace-jsoneditor .ace_keyword,
.ace-jsoneditor .ace_meta,
.ace-jsoneditor .ace_support.ace_constant.ace_property-value {
color: #AF956F
}
.ace-jsoneditor .ace_keyword.ace_operator {
color: #484848
}
.ace-jsoneditor .ace_keyword.ace_other.ace_unit {
color: #96DC5F
}
.ace-jsoneditor .ace_constant.ace_language {
color: darkorange
}
.ace-jsoneditor .ace_constant.ace_numeric {
color: red
}
.ace-jsoneditor .ace_constant.ace_character.ace_entity {
color: #BF78CC
}
.ace-jsoneditor .ace_invalid {
color: #FFFFFF;
background-color: #FF002A;
}
.ace-jsoneditor .ace_fold {
background-color: #AF956F;
border-color: #000000
}
.ace-jsoneditor .ace_storage,
.ace-jsoneditor .ace_support.ace_class,
.ace-jsoneditor .ace_support.ace_function,
.ace-jsoneditor .ace_support.ace_other,
.ace-jsoneditor .ace_support.ace_type {
color: #C52727
}
.ace-jsoneditor .ace_string {
color: green
}
.ace-jsoneditor .ace_comment {
color: #BCC8BA
}
.ace-jsoneditor .ace_entity.ace_name.ace_tag,
.ace-jsoneditor .ace_entity.ace_other.ace_attribute-name {
color: #606060
}
.ace-jsoneditor .ace_markup.ace_underline {
text-decoration: underline
}
.ace-jsoneditor .ace_indent-guide {
background: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAACCAYAAACZgbYnAAAAE0lEQVQImWP4////f4bLly//BwAmVgd1/w11/gAAAABJRU5ErkJggg==") right repeat-y
}`
var dom = acequire('../lib/dom')
dom.importCssString(exports.cssText, exports.cssClass)
})

View File

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

View File

@ -0,0 +1,204 @@
# Jump.js
[![Jump.js on NPM](https://img.shields.io/npm/v/jump.js.svg?style=flat-square)](https://www.npmjs.com/package/jump.js)
A small, modern, dependency-free smooth scrolling library.
* [Demo Page](http://callmecavs.github.io/jump.js/) (Click the arrows!)
## Usage
Jump was developed with a modern JavaScript workflow in mind. To use it, it's recommended you have a build system in place that can transpile ES6, and bundle modules. For a minimal boilerplate that fulfills those requirements, check out [outset](https://github.com/callmecavs/outset).
Follow these steps to get started:
1. [Install](#install)
2. [Import](#import)
3. [Call](#call)
4. [Review Options](#options)
### Install
Using NPM, install Jump, and save it to your `package.json` dependencies.
```bash
$ npm install jump.js --save
```
### Import
Import Jump, naming it according to your preference.
```es6
// import Jump
import jump from 'jump.js'
```
### Call
Jump exports a _singleton_, so there's no need to create an instance. Just call it, passing a [target](#target).
```es6
// call Jump, passing a target
jump('.target')
```
Note that the singleton can make an infinite number of jumps.
## Options
All options, **except [target](#target)**, are optional, and have sensible defaults. The defaults are shown below:
```es6
jump('.target', {
duration: 1000,
offset: 0,
callback: undefined,
easing: easeInOutQuad,
a11y: false
})
```
Explanation of each option follows:
* [target](#target)
* [duration](#duration)
* [offset](#offset)
* [callback](#callback)
* [easing](#easing)
* [a11y](#a11y)
### target
Scroll _from the current position_ by passing a number of pixels.
```es6
// scroll down 100px
jump(100)
// scroll up 100px
jump(-100)
```
Or, scroll _to an element_, by passing either:
* a node, or
* a CSS selector
```es6
// passing a node
const node = document.querySelector('.target')
jump(node)
// passing a CSS selector
// the element referenced by the selector is determined using document.querySelector
jump('.target')
```
### duration
Pass the time the `jump()` takes, in milliseconds.
```es6
jump('.target', {
duration: 1000
})
```
Or, pass a function that returns the duration of the `jump()` in milliseconds. This function is passed the `jump()` `distance`, in `px`, as a parameter.
```es6
jump('.target', {
duration: distance => Math.abs(distance)
})
```
### offset
Offset a `jump()`, _only if to an element_, by a number of pixels.
```es6
// stop 10px before the top of the element
jump('.target', {
offset: -10
})
// stop 10px after the top of the element
jump('.target', {
offset: 10
})
```
Note that this option is useful for accommodating `position: fixed` elements.
### callback
Pass a function that will be called after the `jump()` has been completed.
```es6
// in both regular and arrow functions, this === window
jump('.target', {
callback: () => console.log('Jump completed!')
})
```
### easing
Easing function used to transition the `jump()`.
```es6
jump('.target', {
easing: easeInOutQuad
})
```
See [easing.js](https://github.com/callmecavs/jump.js/blob/master/src/easing.js) for the definition of `easeInOutQuad`, the default easing function. Credit for this function goes to Robert Penner.
### a11y
If enabled, _and scrolling to an element_:
* add a [`tabindex`](https://developer.mozilla.org/en-US/docs/Web/HTML/Global_attributes/tabindex) to, and
* [`focus`](https://developer.mozilla.org/en-US/docs/Web/API/HTMLElement/focus) the element
```es6
jump('.target', {
a11y: true
})
```
Note that this option is disabled by default because it has _visual implications_ in many browsers. Focusing an element triggers the `:focus` CSS state selector, and is often accompanied by an `outline`.
## Browser Support
Jump depends on the following browser APIs:
* [requestAnimationFrame](https://developer.mozilla.org/en-US/docs/Web/API/window/requestAnimationFrame)
Consequently, it supports the following natively:
* Chrome 24+
* Firefox 23+
* Safari 6.1+
* Opera 15+
* IE 10+
* iOS Safari 7.1+
* Android Browser 4.4+
To add support for older browsers, consider including polyfills/shims for the APIs listed above. There are no plans to include any in the library, in the interest of file size.
## License
[MIT](https://opensource.org/licenses/MIT). © 2016 Michael Cavalea
[![Built With Love](http://forthebadge.com/images/badges/built-with-love.svg)](http://forthebadge.com)

View File

@ -0,0 +1,11 @@
// Robert Penner's easeInOutQuad
// find the rest of his easing functions here: http://robertpenner.com/easing/
// find them exported for ES6 consumption here: https://github.com/jaxgeller/ez.js
export default (t, b, c, d) => {
t /= d / 2
if(t < 1) return c / 2 * t * t + b
t--
return -c / 2 * (t * (t - 2) - 1) + b
}

View File

@ -0,0 +1,196 @@
import easeInOutQuad from './easing.js'
const jumper = () => {
// private variable cache
// no variables are created during a jump, preventing memory leaks
let container // container element to be scrolled (node)
let element // element to scroll to (node)
let start // where scroll starts (px)
let stop // where scroll stops (px)
let offset // adjustment from the stop position (px)
let easing // easing function (function)
let a11y // accessibility support flag (boolean)
let distance // distance of scroll (px)
let duration // scroll duration (ms)
let timeStart // time scroll started (ms)
let timeElapsed // time spent scrolling thus far (ms)
let next // next scroll position (px)
let callback // to call when done scrolling (function)
let scrolling // true whilst scrolling (boolean)
// scroll position helper
function location() {
return container.scrollY || container.pageYOffset || container.scrollTop
}
// element offset helper
function top(element) {
const elementTop = element.getBoundingClientRect().top
const containerTop = container.getBoundingClientRect
? container.getBoundingClientRect().top
: 0
return elementTop - containerTop + start
}
// scrollTo helper
function scrollTo(top) {
container.scrollTo
? container.scrollTo(0, top) // window
: container.scrollTop = top // custom container
}
// rAF loop helper
function loop(timeCurrent) {
// store time scroll started, if not started already
if(!timeStart) {
timeStart = timeCurrent
}
// determine time spent scrolling so far
timeElapsed = timeCurrent - timeStart
// calculate next scroll position
next = easing(timeElapsed, start, distance, duration)
// scroll to it
scrollTo(next)
scrolling = true
// check progress
timeElapsed < duration
? requestAnimationFrame(loop) // continue scroll loop
: done() // scrolling is done
}
// scroll finished helper
function done() {
// account for rAF time rounding inaccuracies
scrollTo(start + distance)
// if scrolling to an element, and accessibility is enabled
if(element && a11y) {
// add tabindex indicating programmatic focus
element.setAttribute('tabindex', '-1')
// focus the element
element.focus()
}
// if it exists, fire the callback
if(typeof callback === 'function') {
callback()
}
// reset time for next jump
timeStart = false
// we're done scrolling
scrolling = false
}
// API
function jump(target, options = {}) {
// resolve options, or use defaults
duration = options.duration || 1000
offset = options.offset || 0
callback = options.callback // "undefined" is a suitable default, and won't be called
easing = options.easing || easeInOutQuad
a11y = options.a11y || false
// resolve container
switch(typeof options.container) {
case 'object':
// we assume container is an HTML element (Node)
container = options.container
break
case 'string':
container = document.querySelector(options.container)
break
default:
container = window
}
// cache starting position
start = location()
// resolve target
switch(typeof target) {
// scroll from current position
case 'number':
element = undefined // no element to scroll to
a11y = false // make sure accessibility is off
stop = start + target
break
// scroll to element (node)
// bounding rect is relative to the viewport
case 'object':
element = target
stop = top(element)
break
// scroll to element (selector)
// bounding rect is relative to the viewport
case 'string':
element = document.querySelector(target)
stop = top(element)
break
default:
}
// resolve scroll distance, accounting for offset
distance = stop - start + offset
// resolve duration
switch(typeof options.duration) {
// number in ms
case 'number':
duration = options.duration
break
// function passed the distance of the scroll
case 'function':
duration = options.duration(distance)
break
default:
}
// start the loop if we're not already scrolling
if (!scrolling) {
requestAnimationFrame(loop)
}
else {
// reset time for next jump
timeStart = false
}
}
// expose only the jump method
return jump
}
// export singleton
const singleton = jumper()
export default singleton

View File

@ -0,0 +1,115 @@
import { createElement as h, Component } from 'react'
import ace from '../assets/ace'
import PropTypes from 'prop-types'
/**
* Usage:
*
* <Ace value={"{}"}
* ace={Object}
* indentation={2}
* onChange={function(value: String)}
* onLoadAce={function(aceEditor, container)} />
*
*/
export default class Ace extends Component {
static propTypes = {
value: PropTypes.string.isRequired,
indentation: PropTypes.number
}
aceEditor = null
settingValue = false // Used to prevent Ace from emitting onChange event whilst we're setting a value programmatically
render () {
return h('div', {ref: 'container', className: 'jsoneditor-code'})
}
shouldComponentUpdate () {
// always prevent rerendering, that would destroy the DOM of the Ace editor
return false
}
componentDidMount () {
const container = this.refs.container
// use ace from bundle, and if not available
// try to use from options or else from global
const _ace = ace || this.props.ace || window['ace']
let aceEditor = null
if (_ace && _ace.edit) {
// create ace editor
aceEditor = _ace.edit(container)
// bundle and load jsoneditor theme for ace editor
require('../assets/ace/theme-jsoneditor')
// configure ace editor
aceEditor.$blockScrolling = Infinity
aceEditor.setTheme('ace/theme/jsoneditor')
aceEditor.setShowPrintMargin(false)
aceEditor.setFontSize(13)
aceEditor.getSession().setMode('ace/mode/json')
aceEditor.getSession().setTabSize(this.props.indentation || 2)
aceEditor.getSession().setUseSoftTabs(true)
aceEditor.getSession().setUseWrapMode(true)
aceEditor.commands.bindKey('Ctrl-L', null) // disable Ctrl+L (is used by the browser to select the address bar)
aceEditor.commands.bindKey('Command-L', null) // disable Ctrl+L (is used by the browser to select the address bar)
}
else {
// ace is excluded from the bundle.
}
// allow changing the config or completely replacing aceEditor
this.aceEditor = this.props.onLoadAce
? this.props.onLoadAce(aceEditor, container) || aceEditor
: aceEditor
// register onchange event
if (this.aceEditor) {
this.aceEditor.on('change', this.handleChange)
}
// set value, the text contents for the editor
if (this.aceEditor) {
this.aceEditor.setValue(this.props.value || '', -1)
}
}
componentWillReceiveProps (nextProps) {
if (this.aceEditor && nextProps.value !== this.aceEditor.getValue()) {
this.settingValue = true
this.aceEditor.setValue(nextProps.value, -1)
this.settingValue = false
}
if (this.aceEditor &&
(typeof nextProps.indentation === 'number' || typeof nextProps.indentation === 'string')) {
this.aceEditor.getSession().setTabSize(this.props.indentation)
}
// TODO: only resize only when needed
setTimeout(() => {
if (this.aceEditor) {
this.aceEditor.resize(false);
}
}, 0)
}
componentWillUnmount () {
// neatly destroy ace editor instance
if (this.aceEditor) {
this.aceEditor.destroy()
this.aceEditor = null
}
}
handleChange = () => {
if (this.props && this.props.onChange && this.aceEditor && !this.settingValue) {
// TODO: pass a diff
this.props.onChange(this.aceEditor.getValue())
}
}
}

View File

@ -0,0 +1,59 @@
import { createElement as h } from 'react'
import TextMode from './TextMode'
import Ace from './Ace'
/**
* CodeMode (powered by Ace editor)
*
* Usage:
*
* <CodeMode
* options={Object}
* onChange={function(text: string)}
* onChangeMode={function(mode: string)}
* onError={function(error: Error)}
* onLoadAce={function(aceEditor: Object, container: Element) : Object}
* />
*
* Methods:
*
* setText(text)
* getText() : text
* set(json : JSON)
* get() : JSON
* patch(actions: JSONPatch)
* format()
* compact()
* destroy()
*
*/
export default class CodeMode extends TextMode {
// TODO: work out propTypes
state = {
text: '{}',
compiledSchema: null
}
render () {
return h('div', {
className: 'jsoneditor jsoneditor-mode-code',
onKeyDown: this.handleKeyDown
}, [
this.renderMenu(),
h('div', {key: 'contents', className: 'jsoneditor-contents'}, h(Ace, {
value: this.state.text,
onChange: this.handleChangeText,
onLoadAce: this.props.onLoadAce,
indentation: this.props.indentation,
ace: this.props.ace
})),
this.renderSchemaErrors ()
])
}
}
// TODO: define propTypes

View File

@ -0,0 +1,57 @@
import { createElement as h, PureComponent } from 'react'
import CodeMode from './CodeMode'
import TextMode from './TextMode'
import TreeMode from './TreeMode'
import './jsoneditor.css'
const DEFAULT_MODE = 'tree'
export default class JSONEditor extends PureComponent {
// TODO: work out prop types
// static propTypes = {
// ...
// }
static modeConstructors = {
code: CodeMode,
form: TreeMode,
text: TextMode,
tree: TreeMode,
view: TreeMode
}
render () {
const mode = this.props.mode || DEFAULT_MODE // We use mode from state, not from props!
const ModeConstructor = JSONEditor.modeConstructors[mode]
if (!ModeConstructor) {
// TODO: show an on screen error instead of throwing an error?
throw new Error('Unknown mode "' + mode + '". ' +
'Choose from: ' + Object.keys(this.props.modes).join(', ')) // FIXME: this.props.modes may be undefined
}
return h(ModeConstructor, {
...this.props,
mode,
onError: this.handleError,
onChangeMode: this.handleChangeMode
})
}
handleError = (err) => {
if (this.props.onError) {
this.props.onError(err)
}
else {
console.error(err)
}
}
handleChangeMode = (mode) => {
if (this.props.onChangeMode) {
this.props.onChangeMode(mode, this.props.mode)
}
}
}

View File

@ -0,0 +1,697 @@
import { createElement as h, PureComponent } from 'react'
import PropTypes from 'prop-types'
import initial from 'lodash/initial'
import isEqual from 'lodash/isEqual'
import naturalSort from 'javascript-natural-sort'
import { escapeHTML, unescapeHTML } from '../utils/stringUtils'
import { getInnerText, insideRect } from '../utils/domUtils'
import { isUrl, stringConvert, valueType } from '../utils/typeUtils'
import {
ERROR,
EXPANDED,
ID,
SEARCH_PROPERTY,
SEARCH_VALUE,
SELECTED_AFTER, SELECTED_BEFORE_CHILDS,
SELECTED_INSIDE,
SELECTION,
TYPE,
VALUE
} from '../eson'
import { compileJSONPointer } from '../jsonPointer'
import fontawesome from '@fortawesome/fontawesome'
import faExclamationTriangle from '@fortawesome/fontawesome-free-solid/faExclamationTriangle'
import faCaretRight from '@fortawesome/fontawesome-free-solid/faCaretRight'
import faCaretDown from '@fortawesome/fontawesome-free-solid/faCaretDown'
fontawesome.library.add(faExclamationTriangle, faCaretRight, faCaretDown)
export default class JSONNode extends PureComponent {
static URL_TITLE = 'Ctrl+Click or Ctrl+Enter to open url'
static propTypes = {
parentPath: PropTypes.array,
prop: PropTypes.string, // in case of an object property
index: PropTypes.number, // in case of an array item
eson: PropTypes.any, // enriched JSON object: Object, Array, number, string, or null
emit: PropTypes.func.isRequired,
findKeyBinding: PropTypes.func.isRequired,
// options
options: PropTypes.shape({
isPropertyEditable: PropTypes.func,
isValueEditable: PropTypes.func,
escapeUnicode: PropTypes.bool
})
}
constructor(props) {
super(props)
this.state = {
path: null // initialized via getDerivedStateFromProps
}
}
static getDerivedStateFromProps(props, state) {
const path = props.parentPath
? props.parentPath.concat('index' in props ? props.index : props.prop)
: []
// only update the path in the state if there is actually something changed,
// else we get unnecessary re-rendering of all nodes
return isEqual(path, state.path)
? null
: { path }
}
componentWillUnmount () {
if (hoveredNode === this) {
hoveredNode = null
}
}
render () {
if (this.props.eson[TYPE] === 'array') {
return this.renderJSONArray()
}
else if (this.props.eson[TYPE] === 'object') {
return this.renderJSONObject()
}
else { // no Object or Array
return this.renderJSONValue()
}
}
renderJSONObject () {
// TODO: refactor renderJSONObject (too large/complex)
const eson = this.props.eson
const jsonProps = Object.keys(eson).sort(naturalSort)
const jsonPropsCount = jsonProps.length
const nodeStart = h('div', {
key: 'node',
onKeyDown: this.handleKeyDown,
'data-area': 'inside',
className: 'jsoneditor-node jsoneditor-object'
}, [
this.renderExpandButton(),
// this.renderDelimiter('\u2610'),
this.renderProperty(),
this.renderSeparator(),
this.renderDelimiter('{', 'jsoneditor-delimiter-start'),
!this.props.eson[EXPANDED]
? [
this.renderTag(`${jsonPropsCount} ${jsonPropsCount === 1 ? 'prop' : 'props'}`,
`Object containing ${jsonPropsCount} ${jsonPropsCount === 1 ? 'property' : 'properties'}`),
this.renderDelimiter('}', 'jsoneditor-delimiter-end jsoneditor-delimiter-collapsed')
]
: null,
this.renderError(this.props.eson[ERROR]),
this.renderBeforeChilds()
])
let childs
if (this.props.eson[EXPANDED]) {
if (jsonPropsCount > 0) {
const propsChilds = jsonProps.map((prop) => h(this.constructor, {
key: eson[prop][ID],
parentPath: this.state.path,
prop,
eson: eson[prop],
emit: this.props.emit,
findKeyBinding: this.props.findKeyBinding,
options: this.props.options
}))
childs = h('div', {
key: 'childs',
'data-area': 'before',
className: 'jsoneditor-list'
}, propsChilds)
}
else {
childs = h('div', {
key: 'childs',
className: 'jsoneditor-list',
'data-area': 'before'
},
this.renderEmpty('(empty object)')
)
}
}
const nodeEnd = this.props.eson[EXPANDED]
? h('div', {
key: 'node-end',
className: 'jsoneditor-node-end',
'data-area': 'after',
}, [
this.renderDelimiter('}', 'jsoneditor-delimiter-end', 'after-childs')
])
: null
return h('div', {
'data-path': compileJSONPointer(this.state.path),
'data-area': this.props.eson[EXPANDED] ? 'before-childs' : 'after',
className: this.getContainerClassName(this.props.eson[SELECTION]),
// onMouseOver: this.handleMouseOver,
// onMouseLeave: this.handleMouseLeave
}, [nodeStart, childs, nodeEnd])
}
renderJSONArray () {
// TODO: refactor renderJSONArray (too large/complex)
const count = this.props.eson.length
const nodeStart = h('div', {
key: 'node',
onKeyDown: this.handleKeyDown,
'data-area': 'inside',
className: 'jsoneditor-node jsoneditor-array'
}, [
this.renderExpandButton(),
this.renderProperty(),
this.renderSeparator(),
this.renderDelimiter('[', 'jsoneditor-delimiter-start'),
!this.props.eson[EXPANDED]
? [
this.renderTag(`${count} ${count === 1 ? 'item' : 'items'}`,
`Array containing ${count} ${count === 1 ? 'item' : 'items'}`),
this.renderDelimiter(']', 'jsoneditor-delimiter-end jsoneditor-delimiter-collapsed'),
]
: null,
this.renderError(this.props.eson[ERROR]),
this.renderBeforeChilds()
])
let childs
if (this.props.eson[EXPANDED]) {
if (count > 0) {
const items = this.props.eson.map((item, index) => h(this.constructor, {
key: item[ID],
parentPath: this.state.path,
index,
eson: item,
options: this.props.options,
emit: this.props.emit,
findKeyBinding: this.props.findKeyBinding
}))
childs = h('div', {
key: 'childs',
'data-area': 'before',
className: 'jsoneditor-list'
}, items)
}
else {
childs = h('div', {
key: 'childs',
className: 'jsoneditor-list',
'data-area': 'before',
},
this.renderEmpty('(empty array)')
)
}
}
const nodeEnd = this.props.eson[EXPANDED]
? h('div', {
key: 'node-end',
className: 'jsoneditor-node-end',
'data-area': 'after'
}, [
this.renderDelimiter(']', 'jsoneditor-delimiter-end', 'after-childs')
])
: null
return h('div', {
'data-path': compileJSONPointer(this.state.path),
'data-area': this.props.eson[EXPANDED] ? 'before-childs' : 'after',
className: this.getContainerClassName(this.props.eson[SELECTION]),
// onMouseOver: this.handleMouseOver,
// onMouseLeave: this.handleMouseLeave
}, [nodeStart, childs, nodeEnd])
}
renderJSONValue () {
const node = h('div', {
key: 'node',
onKeyDown: this.handleKeyDown,
'data-area': 'inside',
className: 'jsoneditor-node'
}, [
this.renderPlaceholder(),
this.renderProperty(),
this.renderSeparator(),
this.renderValue(this.props.eson[VALUE], this.props.eson[SEARCH_VALUE], this.props.options), // FIXME
this.renderError(this.props.eson[ERROR])
])
// const insertArea = this.renderInsertBeforeArea()
return h('div', {
'data-path': compileJSONPointer(this.state.path),
'data-area': 'after',
className: this.getContainerClassName(this.props.eson[SELECTION]),
// onMouseOver: this.handleMouseOver,
// onMouseLeave: this.handleMouseLeave
}, [node])
}
/**
* Render contents for an empty object or array
* @param {string} text
* @return {*}
*/
renderEmpty (text) {
return h('div', {
'data-path': compileJSONPointer(this.state.path) + '/-',
'data-area': 'after',
className: 'jsoneditor-node-container'
}, h('div', {
className: 'jsoneditor-node',
'data-area': 'inside',
onKeyDown: this.handleKeyDownAppend
}, [
this.renderPlaceholder(),
this.renderReadonly(text)
]))
}
renderPlaceholder () {
return h('div', {
key: 'placeholder',
'data-area': 'before',
className: 'jsoneditor-button-placeholder'
})
}
renderReadonly (text, title = null) {
return h('div', {
key: 'readonly',
className: 'jsoneditor-readonly',
title
}, text)
}
renderTag (text, title = null) {
return h('div', {
key: 'readonly',
className: 'jsoneditor-tag',
onClick: this.handleExpand,
title
}, text)
}
// TODO: simplify the method renderProperty
/**
* Render a property field of a JSONNode
*/
renderProperty () {
const isProp = typeof this.props.prop === 'string'
if (!isProp) {
return null
}
const editable = !this.props.options.isPropertyEditable ||
this.props.options.isPropertyEditable(this.state.path)
const emptyClassName = (this.props.prop != null && this.props.prop.length === 0) ? ' jsoneditor-empty' : ''
const searchClassName = this.props.prop != null ? JSONNode.getSearchResultClass(this.props.eson[SEARCH_PROPERTY]) : ''
const escapedPropName = this.props.prop != null ? escapeHTML(this.props.prop, this.props.options.escapeUnicode) : null
if (editable) {
return [
h('div', {
key: 'property',
className: 'jsoneditor-property' + emptyClassName + searchClassName,
'data-input': 'property',
contentEditable: 'true',
suppressContentEditableWarning: true,
spellCheck: 'false',
onBlur: this.handleChangeProperty
}, escapedPropName),
]
}
else {
return h('div', {
key: 'property',
className: 'jsoneditor-property jsoneditor-readonly' + searchClassName,
spellCheck: 'false'
}, escapedPropName)
}
}
renderBeforeChilds () {
return h('div', {
key: 'before-childs',
className: 'jsoneditor-before-childs',
'data-area': 'before-childs'
})
}
renderSeparator() {
const isProp = typeof this.props.prop === 'string'
if (!isProp) {
return null
}
return h('div', {
key: 'separator',
className: 'jsoneditor-delimiter',
}, ':')
}
renderDelimiter (text, className = '', dataArea) {
return h('div', {
key: text,
'data-area': dataArea,
className: 'jsoneditor-delimiter ' + className
}, text)
}
renderValue (value, searchResult, options) {
const escapedValue = escapeHTML(value, options.escapeUnicode)
const type = valueType (value)
const itsAnUrl = isUrl(value)
const isEmpty = escapedValue.length === 0
const editable = !options.isValueEditable || options.isValueEditable(this.state.path)
if (editable) {
return h('div', {
key: 'value',
className: JSONNode.getValueClass(type, itsAnUrl, isEmpty) +
JSONNode.getSearchResultClass(searchResult),
'data-input': 'value',
contentEditable: 'true',
suppressContentEditableWarning: true,
spellCheck: 'false',
onBlur: this.handleChangeValue,
onInput: this.updateValueStyling,
onClick: this.handleClickValue,
onKeyDown: this.handleKeyDownValue,
title: itsAnUrl ? JSONNode.URL_TITLE : null
}, escapedValue)
}
else {
return h('div', {
key: 'value',
className: 'jsoneditor-readonly',
title: itsAnUrl ? JSONNode.URL_TITLE : null
}, escapedValue)
}
}
renderError (error) {
if (error) {
return h('button', {
key: 'error',
type: 'button',
className: 'jsoneditor-schema-error',
onFocus: this.updatePopoverDirection,
onMouseOver: this.updatePopoverDirection
},
[
h('i', {className: 'fa fa-exclamation-triangle', key: 'icon'}),
h('div', {className: 'jsoneditor-popover jsoneditor-right', 'key': 'message'}, error.message)
]
)
}
else {
return null
}
}
getContainerClassName (selected) {
let classNames = [
'jsoneditor-node-container',
// `jsoneditor-node-${this.props.eson[TYPE]}`
this.props.eson[EXPANDED] ? 'jsoneditor-node-expanded' : 'jsoneditor-node-collapsed'
]
if (selected === SELECTED_INSIDE) {
classNames.push('jsoneditor-selected')
}
if (selected === SELECTED_AFTER) {
classNames.push('jsoneditor-selected-after')
}
if (selected === SELECTED_BEFORE_CHILDS) {
classNames.push('jsoneditor-selected-before-childs')
}
return classNames.join(' ')
}
/**
* Find the best position for the popover: right, above, below, or left
* from the warning icon.
* @param event
*/
updatePopoverDirection = (event) => {
if (event.target.nodeName === 'BUTTON') {
const popover = event.target.lastChild
const directions = ['right', 'above', 'below', 'left']
for (let i = 0; i < directions.length; i++) {
const direction = directions[i]
popover.className = 'jsoneditor-popover jsoneditor-' + direction
const contents = event.target.parentNode.parentNode.parentNode
const contentRect = contents.getBoundingClientRect()
const popoverRect = popover.getBoundingClientRect()
const margin = 20 // account for a scroll bar
if (insideRect(contentRect, popoverRect, margin)) {
// we found a location that fits, stop here
break
}
}
}
}
/**
* Note: this function manipulates the className and title of the editable div
* outside of Preact, so the user gets immediate feedback
* @param event
*/
updateValueStyling = (event) => {
const value = this.getValueFromEvent(event)
const type = valueType (value)
const itsAnUrl = isUrl(value)
const isEmpty = false // not needed, our div has a border and is clearly visible
// find the editable div, the root
let target = event.target
while (target.contentEditable !== 'true') {
target = target.parentNode
}
target.className = JSONNode.getValueClass(type, itsAnUrl, isEmpty) +
JSONNode.getSearchResultClass(this.props.eson[SEARCH_VALUE])
target.title = itsAnUrl ? JSONNode.URL_TITLE : ''
// remove all classNames from childs (needed for IE and Edge)
JSONNode.removeChildClasses(target)
}
/**
* Create the className for the property value
* @param {string} type
* @param {boolean} isUrl
* @param {boolean} isEmpty
* @return {string}
* @public
*/
static getValueClass (type, isUrl, isEmpty) {
return 'jsoneditor-value ' +
'jsoneditor-' + type +
(isUrl ? ' jsoneditor-url' : '') +
(isEmpty ? ' jsoneditor-empty' : '')
}
/**
* Get the css style given a search result type
* @param {SearchResultStatus} [searchResultStatus]
*/
static getSearchResultClass (searchResultStatus) {
if (searchResultStatus === 'active') {
return ' jsoneditor-highlight-active'
}
if (searchResultStatus === 'normal') {
return ' jsoneditor-highlight'
}
return ''
}
/**
* Recursively remove all classes from the childs of this element
* @param elem
* @public
*/
static removeChildClasses (elem) {
for (let i = 0; i < elem.childNodes.length; i++) {
const child = elem.childNodes[i]
if (child.class) {
child.class = ''
}
JSONNode.removeChildClasses(child)
}
}
renderExpandButton () {
const expanded = this.props.eson[EXPANDED]
const className = `jsoneditor-button jsoneditor-${expanded ? 'expanded' : 'collapsed'}`
return h('div', {key: 'expand', className: 'jsoneditor-button-container'},
h('button', {
'data-area': 'before',
className: className,
onClick: this.handleExpand,
title:
'Click to expand/collapse this field. \n' +
'Ctrl+Click to expand/collapse including all childs.'
}, h('span', {
key: expanded, // to force the fontawesome icon to update
}, h('i', {
className: expanded ? 'fa fa-caret-down' : 'fa fa-caret-right'
})))
)
}
/** @private */
handleChangeProperty = (event) => {
const parentPath = initial(this.state.path)
const oldProp = this.props.prop
const newProp = unescapeHTML(getInnerText(event.target))
if (newProp !== oldProp) {
this.props.emit('changeProperty', {parentPath, oldProp, newProp})
}
}
/** @private */
handleChangeValue = (event) => {
const value = this.getValueFromEvent(event)
const path = this.state.path
if (value !== this.props.eson[VALUE]) {
this.props.emit('changeValue', {path, value})
}
}
/** @private */
handleClickValue = (event) => {
if (event.ctrlKey && event.button === 0) { // Ctrl+Left click
this.openLinkIfUrl(event)
}
}
/** @private */
handleKeyDown = (event) => {
const keyBinding = this.props.findKeyBinding(event)
const path = this.state.path
if (keyBinding === 'duplicate') {
event.preventDefault()
this.props.emit('duplicate', {path})
}
if (keyBinding === 'insert') {
event.preventDefault()
this.props.emit('insert', {path, type: 'value'})
}
if (keyBinding === 'remove') {
event.preventDefault()
this.props.emit('remove', {path})
}
if (keyBinding === 'expand') {
event.preventDefault()
const recurse = false
const expanded = !this.props.eson[EXPANDED]
this.props.emit('expand', {path, expanded, recurse})
}
if (keyBinding === 'actionMenu') {
event.preventDefault()
// FIXME: open floating menu
}
}
/** @private */
handleKeyDownAppend = (event) => {
const keyBinding = this.props.findKeyBinding(event)
const path = this.state.path
if (keyBinding === 'insert') {
event.preventDefault()
this.props.emit('append', {path, type: 'value'})
}
if (keyBinding === 'actionMenu') {
event.preventDefault()
// FIXME: open floating menu
}
}
/** @private */
handleKeyDownValue = (event) => {
const keyBinding = this.props.findKeyBinding(event)
if (keyBinding === 'openUrl') {
this.openLinkIfUrl(event)
}
}
/** @private */
handleExpand = (event) => {
const recurse = event.ctrlKey
const path = this.state.path
const expanded = !this.props.eson[EXPANDED]
this.props.emit('expand', {path, expanded, recurse})
}
/**
* When this JSONNode holds an URL as value, open this URL in a new browser tab
* @param event
* @protected
*/
openLinkIfUrl (event) {
const value = this.getValueFromEvent(event)
if (isUrl(value)) {
event.preventDefault()
event.stopPropagation()
window.open(value, '_blank')
}
}
/**
* Get the value of the target of an event, and convert it to it's type
* @param event
* @return {string | number | boolean | null}
* @private
*/
getValueFromEvent (event) {
const stringValue = unescapeHTML(getInnerText(event.target))
return this.state.type === 'string' // FIXME
? stringValue
: stringConvert(stringValue)
}
}
// singleton holding the node that's currently being hovered
let hoveredNode = null

View File

@ -0,0 +1,36 @@
import JSONNode from './JSONNode'
/**
* JSONNodeForm
*
* Creates JSONNodes without action menus and with readonly properties
*/
export default class JSONNodeForm extends JSONNode {
// render no action menu...
renderActionMenuButton () {
return null
}
// render no append menu...
renderAppendMenuButton () {
return null
}
/**
* Render a property field of a JSONNode
* @param {string} [prop]
* @param {string} [index]
* @param {ESON} eson
* @param {{escapeUnicode: boolean, isPropertyEditable: function(Path) : boolean}} options
*/
renderProperty (prop, index, eson, options) {
const formOptions = Object.assign({}, options, { isPropertyEditable })
return JSONNode.prototype.renderProperty.call(this, prop, index, eson, formOptions)
}
}
function isPropertyEditable () {
return false
}

View File

@ -0,0 +1,49 @@
import { createElement as h } from 'react'
import { escapeHTML } from '../utils/stringUtils'
import { valueType, isUrl } from '../utils/typeUtils'
import JSONNode from './JSONNode'
import JSONNodeForm from './JSONNodeForm'
/**
* JSONNodeView
*
* Creates JSONNodes without action menus and with readonly properties and values
*/
export default class JSONNodeView extends JSONNodeForm {
// render a readonly value but with colors
renderValue (value, searchResult, options) {
const escapedValue = escapeHTML(value, options.escapeUnicode)
const type = valueType (value)
const itsAnUrl = isUrl(value)
const isEmpty = escapedValue.length === 0
const editable = !options.isValueEditable || options.isValueEditable(this.props.path)
if (editable) {
return h('div', {
key: 'value',
ref: 'value',
className: JSONNode.getValueClass(type, itsAnUrl, isEmpty) +
JSONNode.getSearchResultClass(searchResult),
contentEditable: 'false',
spellCheck: 'false',
onClick: this.handleClickValue,
title: itsAnUrl ? JSONNode.URL_TITLE : null
}, escapedValue)
}
else {
return h('div', {
key: 'value',
className: 'jsoneditor-readonly',
title: itsAnUrl ? JSONNode.URL_TITLE : null
}, escapedValue)
}
}
handleClickValue = (event) => {
if (event.button === 0) { // Left click
this.openLinkIfUrl(event)
}
}
}

View File

@ -0,0 +1,376 @@
import { Component, createElement as h } from 'react'
import Ajv from 'ajv'
import { parseJSON } from '../utils/jsonUtils'
import { escapeUnicodeChars } from '../utils/stringUtils'
import { enrichSchemaError, limitErrors } from '../utils/schemaUtils'
import { createFindKeyBinding } from '../utils/keyBindings'
import { KEY_BINDINGS } from '../constants'
import { immutableJSONPatch } from '../immutableJSONPatch'
import TextModeMenu from './menu/TextModeMenu'
import fontawesome from '@fortawesome/fontawesome'
import faExclamationTriangle from '@fortawesome/fontawesome-free-solid/faExclamationTriangle'
fontawesome.library.add(faExclamationTriangle)
const AJV_OPTIONS = {
allErrors: true,
verbose: true,
jsonPointers: true
}
/**
* TextMode
*
* Usage:
*
* <TextMode
* text={string}
* json={JSON}
* ...options
* onChange={function(text: string)}
* onChangeMode={function(mode: string)}
* onError={function(error: Error)}
* />
*
* Methods:
*
* setText(text)
* getText() : text
* set(json : JSON)
* get() : JSON
* patch(actions: JSONPatch)
* format()
* compact()
* destroy()
*
*/
export default class TextMode extends Component {
state = {
text: '{}',
compiledSchema: null
}
keyDownActions = {
'format': (event) => this.handleCompact(),
'compact': (event) => this.handleFormat()
}
componentWillMount () {
this.applyProps(this.props, {})
}
componentWillReceiveProps (nextProps) {
this.applyProps(nextProps, this.props)
}
// TODO: create some sort of watcher structure for these props? Is there a React pattern for that?
applyProps (nextProps, currentProps) {
// Apply text
if (nextProps.text !== currentProps.text) {
this.setText(nextProps.text)
}
// Apply json
if (nextProps.json !== currentProps.json) {
this.set(nextProps.json)
}
// Apply JSON Schema
if (nextProps.schema !== currentProps.schema) {
this.setSchema(nextProps.schema)
}
// Apply key bindings
if (!this.findKeyBinding ||
JSON.stringify(nextProps.keyBindings) !== JSON.stringify(currentProps.keyBindings)) {
// merge default and custom key bindings
const keyBindings = Object.assign({}, KEY_BINDINGS, nextProps.keyBindings)
this.findKeyBinding = createFindKeyBinding(keyBindings)
}
// TODO: apply patch
// TODO: apply patchText
}
render () {
return h('div', {
className: 'jsoneditor jsoneditor-mode-text',
onKeyDown: this.handleKeyDown
}, [
this.renderMenu(),
h('div', {key: 'contents', className: 'jsoneditor-contents'},
h('textarea', {
className: 'jsoneditor-text',
value: this.state.text,
onChange: this.handleChange,
onInput: this.handleInput
})
),
this.renderSchemaErrors ()
])
}
/** @protected */
renderMenu () {
return h(TextModeMenu, {
key: 'menu',
mode: this.props.mode,
modes: this.props.modes,
onChangeMode: this.props.onChangeMode,
onFormat: this.handleFormat,
onCompact: this.handleCompact,
onRepair: this.handleRepair
})
}
/** @protected */
renderSchemaErrors () {
// TODO: move the JSON Schema stuff into a separate Component
try {
// TODO: only validate again when json is changed since last validation
const json = this.get(); // this can fail when there is no valid json
const valid = this.state.compiledSchema
? this.state.compiledSchema(json)
: true
if (!valid) {
const allErrors = this.state.compiledSchema.errors.map(enrichSchemaError)
const limitedErrors = limitErrors(allErrors)
return h('div', { key: 'errors', className: 'jsoneditor-errors'},
h('table', {},
h('tbody', {}, limitedErrors.map(TextMode.renderSchemaError))
)
)
}
else {
return null
}
}
catch (err) {
// no valid JSON
// TODO: display errors in text mode somehow? shouldn't be too much in your face
// maybe a warning icon top right?
// return h('table', {className: 'jsoneditor-text-errors'},
// h('tbody', {}, TextMode.renderSchemaError(err))
// )
return null
}
}
/**
* Render a table row of a single JSON schema error
* @param {Error | Object | string} error
* @param {number} index
* @return {JSX.Element}
*/
static renderSchemaError (error, index) {
const icon = h('button', {className: 'jsoneditor-schema-error'},
h('i', {className: 'fa fa-exclamation-triangle'}))
if (error && error.schema && error.schemaPath) {
// this is an ajv error message
return h('tr', { key: index }, [
h('td', {key: 'icon'}, icon),
h('td', {key: 'path'}, error.esonPath),
h('td', {key: 'message'}, error.message)
])
}
else {
// any other error message
console.log('error???', error)
return h('tr', { key: index },
h('td', {key: 'icon'}, icon),
h('td', {key: 'message', colSpan: 2}, h('code', {}, String(error)))
)
}
}
/**
* Set a JSON schema for validation of the JSON object.
* To remove the schema, call JSONEditor.setSchema(null)
* @param {Object | null} schema
*/
setSchema (schema) {
if (schema) {
const ajv = this.props.ajv || (Ajv && Ajv(AJV_OPTIONS))
if (!ajv) {
throw new Error('Cannot validate JSON: ajv not available. ' +
'Provide ajv via options or use a JSONEditor bundle including ajv.')
}
this.setState({
compiledSchema: ajv.compile(schema)
})
}
else {
this.setState({
compiledSchema: null
})
}
}
/**
* Get the configured indentation. When not configured, returns the default value 2
* @param {{indentation?: number}} props
* @return {number}
*/
static getIndentation (props) {
return (props && props.indentation) || 2
}
static format (text, indentation) {
const json = parseJSON(text)
return JSON.stringify(json, null, indentation)
}
static compact (text) {
const json = parseJSON(text)
return JSON.stringify(json)
}
// TODO: move the static functions above into a separate util file
handleChange = (event) => {
// do nothing...
}
findKeyBinding = createFindKeyBinding(KEY_BINDINGS)
handleKeyDown = (event) => {
const keyBinding = this.findKeyBinding(event)
const action = this.keyDownActions[keyBinding]
if (action) {
event.preventDefault()
action(event)
}
}
/**
* handle changed text input in the textarea
* @param {Event} event
* @protected
*/
handleInput = (event) => {
this.handleChangeText(event.target.value)
}
/** @protected */
handleFormat = () => {
try {
const formatted = TextMode.format(this.getText(), TextMode.getIndentation(this.props))
this.handleChangeText(formatted)
}
catch (err) {
this.props.onError(err)
}
}
/** @protected */
handleCompact = () => {
try {
const compacted = TextMode.compact(this.getText())
this.handleChangeText(compacted)
}
catch (err) {
this.props.onError(err)
}
}
/** @protected */
handleRepair = () => {
// FIXME: implement repair button
console.log('handleRepair not yet implemented')
alert('sorry, not yet implemented...')
}
/**
* Apply new text to the state, and emit an onChangeText event if there is a change
* @param {string} text
*/
handleChangeText = (text) => {
if (this.props.onChangeText && text !== this.state.text) {
const appliedText = this.setText(text)
this.props.onChangeText(appliedText)
}
else {
this.setText(text)
}
// TODO: also invoke a patch action
}
// TODO: implement method patchText
// TODO: implement callback onPatchText
/**
* Apply a JSONPatch to the current JSON document
* @param {JSONPatchDocument} operations JSONPatch operations
* @return {JSONPatchResult} Returns a patch result containing the
* patch, a patch to revert the action, and
* an error object which is null when successful
*/
patch (operations) {
const json = this.get()
const result = immutableJSONPatch(json, operations)
this.set(result.data)
return {
patch: operations,
revert: result.revert,
error: result.error
}
}
/**
* Set JSON object in editor
* @param {Object | Array | string | number | boolean | null} json JSON data
*/
set (json) {
this.setText(JSON.stringify(json, null, TextMode.getIndentation(this.props)))
}
/**
* Get JSON from the editor
* @returns {Object | Array | string | number | boolean | null} json
*/
get () {
return parseJSON(this.getText())
}
/**
* Set a string containing a JSON document
* @param {string} text
* @return {string}
*/
setText (text) {
const normalizedText = this.props.escapeUnicode
? escapeUnicodeChars(text)
: text
this.setState({ text: normalizedText })
return normalizedText
}
/**
* Get the JSON document as text
* @return {string} text
*/
getText () {
return this.state.text
}
}
// TODO: define propTypes

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,23 @@
// custom fontawesome icons
export const faCompact = {
prefix: 'fa',
iconName: 'compact',
icon: [
16, 16,
[],
null,
'm 0,2 0,2 16,0 0,-2 m -16,4 0,2 16,0 0,-2 m -16,4 0,2 9,0 0,-2'
]
}
export const faFormat = {
prefix: 'fa',
iconName: 'format',
icon: [
16, 16,
[],
null,
'm 0,2 0,2 12,0 0,-2 m -6,4 0,2 10,0 0,-2 m -10,4 0,2 9,0 0,-2 m -15,4 0,2 10,0 0,-2'
]
}

Some files were not shown because too many files have changed in this diff Show More