Merge branch 'develop' of https://github.com/josdejong/jsoneditor into develop
This commit is contained in:
commit
371a7b2d14
|
@ -1,4 +1,5 @@
|
||||||
.idea
|
.idea
|
||||||
|
.vscode
|
||||||
build
|
build
|
||||||
downloads
|
downloads
|
||||||
node_modules
|
node_modules
|
||||||
|
|
51
HISTORY.md
51
HISTORY.md
|
@ -3,6 +3,57 @@
|
||||||
https://github.com/josdejong/jsoneditor
|
https://github.com/josdejong/jsoneditor
|
||||||
|
|
||||||
|
|
||||||
|
## 2017-11-22, version 5.11.0
|
||||||
|
|
||||||
|
- Upgraded dependencies
|
||||||
|
- `ajv@5.4.0`
|
||||||
|
- `brace@0.11.0`
|
||||||
|
- Fixed dropdown for JSON Schema enums when defined inside pattern
|
||||||
|
properties. Thanks @alquist.
|
||||||
|
- Fixed code containing a non UTF-8 character. Thanks @alshakero.
|
||||||
|
|
||||||
|
|
||||||
|
## 2017-11-15, version 5.10.1
|
||||||
|
|
||||||
|
- Some styling tweaks in the navigation bar and status bar.
|
||||||
|
- Don't display status bar in `text` mode (which doesn't yet support
|
||||||
|
row and col counts).
|
||||||
|
|
||||||
|
|
||||||
|
## 2017-11-15, version 5.10.0
|
||||||
|
|
||||||
|
- Implemented a navigation bar showing the path. Thanks @meirotstein.
|
||||||
|
- Implemented a status bar showing cursor location.
|
||||||
|
Thanks @meirotstein.
|
||||||
|
- Implemented repairing JSON objects containing left and right single
|
||||||
|
and double quotes (which you get when typing a JSON object in Word)
|
||||||
|
in `text` and `code` mode.
|
||||||
|
- Implemented repairing JSON objects containing special white space
|
||||||
|
characters like non-breaking space.
|
||||||
|
- Upgraded dependency `ajv` to version `5.3.0`.
|
||||||
|
- Fixed #481: A polyfill required `DocumentType` which is not defined
|
||||||
|
in all environments.
|
||||||
|
|
||||||
|
|
||||||
|
## 2017-09-16, version 5.9.6
|
||||||
|
|
||||||
|
- Fixed displaying a dropdown for enums inside composite schemas.
|
||||||
|
Thanks @hachichaud.
|
||||||
|
- Fixed #461: Urls opening twice on Firefox and Safari.
|
||||||
|
|
||||||
|
|
||||||
|
## 2017-08-26, version 5.9.5
|
||||||
|
|
||||||
|
- Fixed a regression introduced in `v5.9.4`: after using the context
|
||||||
|
menu once, it was not possible to set focus to an other input field
|
||||||
|
anymore.
|
||||||
|
|
||||||
|
|
||||||
|
## 2017-08-20, version 5.9.4
|
||||||
|
|
||||||
|
- Fixed #447: context menus not working in Shadow DOM. Thanks @tomalec.
|
||||||
|
|
||||||
|
|
||||||
## 2017-07-24, version 5.9.3
|
## 2017-07-24, version 5.9.3
|
||||||
|
|
||||||
- Fixed broken multi-selection (regression).
|
- Fixed broken multi-selection (regression).
|
||||||
|
|
|
@ -1,5 +1,11 @@
|
||||||
# JSON Editor
|
# JSON Editor
|
||||||
|
|
||||||
|
[![Version](https://img.shields.io/npm/v/jsoneditor.svg)](https://www.npmjs.com/package/jsoneditor)
|
||||||
|
[![Downloads](https://img.shields.io/npm/dm/jsoneditor.svg)](https://www.npmjs.com/package/jsoneditor)
|
||||||
|
![Maintenance](https://img.shields.io/maintenance/yes/2017.svg)
|
||||||
|
[![License](https://img.shields.io/github/license/josdejong/jsoneditor.svg)](https://github.com/josdejong/jsoneditor/blob/master/LICENSE)
|
||||||
|
[![FOSSA Status](https://app.fossa.io/api/projects/git%2Bgithub.com%2Fjosdejong%2Fjsoneditor.svg?type=shield)](https://app.fossa.io/projects/git%2Bgithub.com%2Fjosdejong%2Fjsoneditor?ref=badge_shield)
|
||||||
|
|
||||||
JSON Editor is a web-based tool to view, edit, format, and validate JSON.
|
JSON Editor 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
|
It has various modes such as a tree editor, a code editor, and a plain text
|
||||||
editor.
|
editor.
|
||||||
|
|
|
@ -11,7 +11,7 @@
|
||||||
height="144"
|
height="144"
|
||||||
id="svg4136"
|
id="svg4136"
|
||||||
version="1.1"
|
version="1.1"
|
||||||
inkscape:version="0.91 r"
|
inkscape:version="0.91 r13725"
|
||||||
sodipodi:docname="jsoneditor-icons.svg">
|
sodipodi:docname="jsoneditor-icons.svg">
|
||||||
<title
|
<title
|
||||||
id="title6512">JSON Editor Icons</title>
|
id="title6512">JSON Editor Icons</title>
|
||||||
|
@ -39,12 +39,12 @@
|
||||||
inkscape:pageopacity="0"
|
inkscape:pageopacity="0"
|
||||||
inkscape:pageshadow="2"
|
inkscape:pageshadow="2"
|
||||||
inkscape:window-width="1920"
|
inkscape:window-width="1920"
|
||||||
inkscape:window-height="1028"
|
inkscape:window-height="1027"
|
||||||
id="namedview4144"
|
id="namedview4144"
|
||||||
showgrid="true"
|
showgrid="true"
|
||||||
inkscape:zoom="4"
|
inkscape:zoom="32"
|
||||||
inkscape:cx="97.217248"
|
inkscape:cx="101.13921"
|
||||||
inkscape:cy="59.950227"
|
inkscape:cy="34.512712"
|
||||||
inkscape:window-x="0"
|
inkscape:window-x="0"
|
||||||
inkscape:window-y="0"
|
inkscape:window-y="0"
|
||||||
inkscape:window-maximized="1"
|
inkscape:window-maximized="1"
|
||||||
|
@ -710,49 +710,49 @@
|
||||||
width="15.99999"
|
width="15.99999"
|
||||||
y="101"
|
y="101"
|
||||||
x="76.000008"
|
x="76.000008"
|
||||||
style="fill:#ffffff;fill-opacity:0.80000007;stroke:none;stroke-width:0" />
|
style="fill:#ffffff;fill-opacity:0.8;stroke:none;stroke-width:0" />
|
||||||
<rect
|
<rect
|
||||||
id="rect4761-0"
|
id="rect4761-0"
|
||||||
height="1.9999945"
|
height="1.9999945"
|
||||||
width="15.99999"
|
width="15.99999"
|
||||||
y="105"
|
y="105"
|
||||||
x="76.000008"
|
x="76.000008"
|
||||||
style="fill:#ffffff;fill-opacity:0.80000007;stroke:none;stroke-width:0" />
|
style="fill:#ffffff;fill-opacity:0.8;stroke:none;stroke-width:0" />
|
||||||
<rect
|
<rect
|
||||||
id="rect4761-7"
|
id="rect4761-7"
|
||||||
height="1.9999945"
|
height="1.9999945"
|
||||||
width="9"
|
width="9"
|
||||||
y="109"
|
y="109"
|
||||||
x="76.000008"
|
x="76.000008"
|
||||||
style="fill:#ffffff;fill-opacity:0.80000007;stroke:none;stroke-width:0" />
|
style="fill:#ffffff;fill-opacity:0.8;stroke:none;stroke-width:0" />
|
||||||
<rect
|
<rect
|
||||||
id="rect4761-1-1"
|
id="rect4761-1-1"
|
||||||
height="1.9999945"
|
height="1.9999945"
|
||||||
width="12"
|
width="12"
|
||||||
y="125"
|
y="125"
|
||||||
x="76.000008"
|
x="76.000008"
|
||||||
style="fill:#ffffff;fill-opacity:0.80000007;stroke:none;stroke-width:0" />
|
style="fill:#ffffff;fill-opacity:0.8;stroke:none;stroke-width:0" />
|
||||||
<rect
|
<rect
|
||||||
id="rect4761-1-1-4"
|
id="rect4761-1-1-4"
|
||||||
height="1.9999945"
|
height="1.9999945"
|
||||||
width="10"
|
width="10"
|
||||||
y="137"
|
y="137"
|
||||||
x="76.000008"
|
x="76.000008"
|
||||||
style="fill:#ffffff;fill-opacity:0.80000007;stroke:none;stroke-width:0" />
|
style="fill:#ffffff;fill-opacity:0.8;stroke:none;stroke-width:0" />
|
||||||
<rect
|
<rect
|
||||||
id="rect4761-1-1-4-4"
|
id="rect4761-1-1-4-4"
|
||||||
height="1.9999945"
|
height="1.9999945"
|
||||||
width="10"
|
width="10"
|
||||||
y="129"
|
y="129"
|
||||||
x="82"
|
x="82"
|
||||||
style="fill:#ffffff;fill-opacity:0.80000007;stroke:none;stroke-width:0" />
|
style="fill:#ffffff;fill-opacity:0.8;stroke:none;stroke-width:0" />
|
||||||
<rect
|
<rect
|
||||||
id="rect4761-1-1-4-4-3"
|
id="rect4761-1-1-4-4-3"
|
||||||
height="1.9999945"
|
height="1.9999945"
|
||||||
width="9"
|
width="9"
|
||||||
y="133"
|
y="133"
|
||||||
x="82"
|
x="82"
|
||||||
style="fill:#ffffff;fill-opacity:0.80000007;stroke:none;stroke-width:0" />
|
style="fill:#ffffff;fill-opacity:0.8;stroke:none;stroke-width:0" />
|
||||||
<path
|
<path
|
||||||
inkscape:connector-curvature="0"
|
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"
|
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"
|
||||||
|
@ -890,4 +890,10 @@
|
||||||
id="path4300-6"
|
id="path4300-6"
|
||||||
inkscape:connector-curvature="0"
|
inkscape:connector-curvature="0"
|
||||||
sodipodi:nodetypes="cccc" />
|
sodipodi:nodetypes="cccc" />
|
||||||
|
<path
|
||||||
|
style="fill:#ffffff;fill-opacity:0.8;fill-rule:evenodd;stroke:#ffffff;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:round;stroke-opacity:0.80000001"
|
||||||
|
d="M 99.994369,113.0221 102,114.98353 l 7,-6.9558 3,0.97227 2,-1 1,-2 0,-3 -3,3 -3,-3 3,-3 -3,0 -2,1 -1,2 0.99437,3.0221 z"
|
||||||
|
id="path4268"
|
||||||
|
inkscape:connector-curvature="0"
|
||||||
|
sodipodi:nodetypes="ccccccccccccccc" />
|
||||||
</svg>
|
</svg>
|
||||||
|
|
Before Width: | Height: | Size: 35 KiB After Width: | Height: | Size: 36 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
|
@ -228,6 +228,16 @@ div.jsoneditor-outer {
|
||||||
box-sizing: border-box;
|
box-sizing: border-box;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
div.jsoneditor-outer.has-nav-bar {
|
||||||
|
margin: -61px 0 0 0;
|
||||||
|
padding: 61px 0 0 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
div.jsoneditor-outer.has-status-bar {
|
||||||
|
margin: -35px 0 -26px 0;
|
||||||
|
padding: 35px 0 26px 0;
|
||||||
|
}
|
||||||
|
|
||||||
textarea.jsoneditor-text,
|
textarea.jsoneditor-text,
|
||||||
.ace-jsoneditor {
|
.ace-jsoneditor {
|
||||||
min-height: 150px;
|
min-height: 150px;
|
||||||
|
@ -255,7 +265,7 @@ textarea.jsoneditor-text {
|
||||||
|
|
||||||
tr.jsoneditor-highlight,
|
tr.jsoneditor-highlight,
|
||||||
tr.jsoneditor-selected {
|
tr.jsoneditor-selected {
|
||||||
background-color: #e6e6e6;
|
background-color: #d3d3d3;
|
||||||
}
|
}
|
||||||
|
|
||||||
tr.jsoneditor-selected button.jsoneditor-dragarea,
|
tr.jsoneditor-selected button.jsoneditor-dragarea,
|
||||||
|
@ -831,6 +841,10 @@ div.jsoneditor-menu > button.jsoneditor-format {
|
||||||
background-position: -72px -120px;
|
background-position: -72px -120px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
div.jsoneditor-menu > button.jsoneditor-repair {
|
||||||
|
background-position: -96px -96px;
|
||||||
|
}
|
||||||
|
|
||||||
div.jsoneditor-menu > div.jsoneditor-modes {
|
div.jsoneditor-menu > div.jsoneditor-modes {
|
||||||
display: inline-block;
|
display: inline-block;
|
||||||
float: left;
|
float: left;
|
||||||
|
@ -977,3 +991,75 @@ div.jsoneditor div.autocomplete.hint {
|
||||||
top: 4px;
|
top: 4px;
|
||||||
left: 4px;
|
left: 4px;
|
||||||
}
|
}
|
||||||
|
div.jsoneditor-treepath {
|
||||||
|
padding: 0 5px;
|
||||||
|
overflow: hidden;
|
||||||
|
}
|
||||||
|
|
||||||
|
div.jsoneditor-treepath div.jsoneditor-contextmenu-root {
|
||||||
|
position: absolute;
|
||||||
|
left: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
div.jsoneditor-treepath span.jsoneditor-treepath-element {
|
||||||
|
margin: 1px;
|
||||||
|
font-family: arial, sans-serif;
|
||||||
|
font-size: 10pt;
|
||||||
|
}
|
||||||
|
|
||||||
|
div.jsoneditor-treepath span.jsoneditor-treepath-seperator {
|
||||||
|
margin: 2px;
|
||||||
|
font-size: 9pt;
|
||||||
|
font-family: arial, sans-serif;
|
||||||
|
}
|
||||||
|
|
||||||
|
div.jsoneditor-treepath span.jsoneditor-treepath-element:hover,
|
||||||
|
div.jsoneditor-treepath span.jsoneditor-treepath-seperator:hover {
|
||||||
|
cursor: pointer;
|
||||||
|
text-decoration: underline;
|
||||||
|
}
|
||||||
|
div.jsoneditor-statusbar {
|
||||||
|
line-height: 26px;
|
||||||
|
height: 26px;
|
||||||
|
margin-top: -26px;
|
||||||
|
color: #808080;
|
||||||
|
background-color: #ebebeb;
|
||||||
|
border-top: 1px solid #d3d3d3;
|
||||||
|
-moz-box-sizing: border-box;
|
||||||
|
-webkit-box-sizing: border-box;
|
||||||
|
box-sizing: border-box;
|
||||||
|
font-size: 10pt;
|
||||||
|
}
|
||||||
|
|
||||||
|
div.jsoneditor-statusbar > .jsoneditor-curserinfo-label {
|
||||||
|
margin: 0 2px 0 4px;
|
||||||
|
}
|
||||||
|
|
||||||
|
div.jsoneditor-statusbar > .jsoneditor-curserinfo-val {
|
||||||
|
margin-right: 12px;
|
||||||
|
}
|
||||||
|
|
||||||
|
div.jsoneditor-statusbar > .jsoneditor-curserinfo-count {
|
||||||
|
margin-left: 4px;
|
||||||
|
}
|
||||||
|
div.jsoneditor-navigation-bar {
|
||||||
|
width: 100%;
|
||||||
|
height: 26px;
|
||||||
|
line-height: 26px;
|
||||||
|
padding: 0;
|
||||||
|
margin: 0;
|
||||||
|
border-bottom: 1px solid #d3d3d3;
|
||||||
|
-moz-box-sizing: border-box;
|
||||||
|
-webkit-box-sizing: border-box;
|
||||||
|
box-sizing: border-box;
|
||||||
|
color: #808080;
|
||||||
|
background-color: #ebebeb;
|
||||||
|
font-size: 10pt;
|
||||||
|
}
|
||||||
|
|
||||||
|
div.jsoneditor-navigation-bar.nav-bar-empty:after {
|
||||||
|
content: 'Select a node ...';
|
||||||
|
color: rgba(104, 104, 91, 0.56);
|
||||||
|
position: absolute;
|
||||||
|
margin-left: 5px;
|
||||||
|
}
|
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
File diff suppressed because one or more lines are too long
|
@ -180,6 +180,13 @@ Constructs a new JSONEditor.
|
||||||
- Can return an object `{startFrom: number, options: string[]}`. Here `startFrom` determines the start character from where the existing text will be replaced. `startFrom` is `0` by default, replacing the whole text.
|
- Can return an object `{startFrom: number, options: string[]}`. Here `startFrom` determines the start character from where the existing text will be replaced. `startFrom` is `0` by default, replacing the whole text.
|
||||||
- Can return a `Promise` resolving one of the return types above to support asynchronously retrieving a list with options.
|
- Can return a `Promise` resolving one of the return types above to support asynchronously retrieving a list with options.
|
||||||
|
|
||||||
|
- `{boolean} navigationBar`
|
||||||
|
|
||||||
|
Adds navigation bar to the menu - the navigation bar visualize the current position on the tree structure as well as allows breadcrumbs navigation. True by default. Only applicable when `mode` is 'tree', 'form' or 'view'.
|
||||||
|
|
||||||
|
- `{boolean} statusBar`
|
||||||
|
|
||||||
|
Adds status bar to the buttom of the editor - the status bar shows the cursor position (currently only for 'code' `mode`) and a count of the selected charcters. True by default. Only applicable when `mode` is 'code' or 'text'.
|
||||||
|
|
||||||
### Methods
|
### Methods
|
||||||
|
|
||||||
|
|
|
@ -131,7 +131,10 @@ gulp.task('bundle-css', ['mkdir'], function () {
|
||||||
'src/css/contextmenu.css',
|
'src/css/contextmenu.css',
|
||||||
'src/css/menu.css',
|
'src/css/menu.css',
|
||||||
'src/css/searchbox.css',
|
'src/css/searchbox.css',
|
||||||
'src/css/autocomplete.css'
|
'src/css/autocomplete.css',
|
||||||
|
'src/css/treepath.css',
|
||||||
|
'src/css/statusbar.css',
|
||||||
|
'src/css/navigationbar.css'
|
||||||
])
|
])
|
||||||
.pipe(concatCss(NAME + '.css'))
|
.pipe(concatCss(NAME + '.css'))
|
||||||
.pipe(gulp.dest(DIST))
|
.pipe(gulp.dest(DIST))
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
{
|
{
|
||||||
"name": "jsoneditor",
|
"name": "jsoneditor",
|
||||||
"version": "5.9.3",
|
"version": "5.11.0",
|
||||||
"main": "./index",
|
"main": "./index",
|
||||||
"description": "A web-based tool to view, edit, format, and validate JSON",
|
"description": "A web-based tool to view, edit, format, and validate JSON",
|
||||||
"tags": [
|
"tags": [
|
||||||
|
@ -23,8 +23,8 @@
|
||||||
"test": "mocha test"
|
"test": "mocha test"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"ajv": "5.2.0",
|
"ajv": "5.4.0",
|
||||||
"brace": "0.10.0",
|
"brace": "0.11.0",
|
||||||
"javascript-natural-sort": "0.7.1"
|
"javascript-natural-sort": "0.7.1"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
|
|
|
@ -11,7 +11,7 @@
|
||||||
height="144"
|
height="144"
|
||||||
id="svg4136"
|
id="svg4136"
|
||||||
version="1.1"
|
version="1.1"
|
||||||
inkscape:version="0.91 r"
|
inkscape:version="0.91 r13725"
|
||||||
sodipodi:docname="jsoneditor-icons.svg">
|
sodipodi:docname="jsoneditor-icons.svg">
|
||||||
<title
|
<title
|
||||||
id="title6512">JSON Editor Icons</title>
|
id="title6512">JSON Editor Icons</title>
|
||||||
|
@ -39,12 +39,12 @@
|
||||||
inkscape:pageopacity="0"
|
inkscape:pageopacity="0"
|
||||||
inkscape:pageshadow="2"
|
inkscape:pageshadow="2"
|
||||||
inkscape:window-width="1920"
|
inkscape:window-width="1920"
|
||||||
inkscape:window-height="1028"
|
inkscape:window-height="1027"
|
||||||
id="namedview4144"
|
id="namedview4144"
|
||||||
showgrid="true"
|
showgrid="true"
|
||||||
inkscape:zoom="4"
|
inkscape:zoom="32"
|
||||||
inkscape:cx="97.217248"
|
inkscape:cx="101.13921"
|
||||||
inkscape:cy="59.950227"
|
inkscape:cy="34.512712"
|
||||||
inkscape:window-x="0"
|
inkscape:window-x="0"
|
||||||
inkscape:window-y="0"
|
inkscape:window-y="0"
|
||||||
inkscape:window-maximized="1"
|
inkscape:window-maximized="1"
|
||||||
|
@ -710,49 +710,49 @@
|
||||||
width="15.99999"
|
width="15.99999"
|
||||||
y="101"
|
y="101"
|
||||||
x="76.000008"
|
x="76.000008"
|
||||||
style="fill:#ffffff;fill-opacity:0.80000007;stroke:none;stroke-width:0" />
|
style="fill:#ffffff;fill-opacity:0.8;stroke:none;stroke-width:0" />
|
||||||
<rect
|
<rect
|
||||||
id="rect4761-0"
|
id="rect4761-0"
|
||||||
height="1.9999945"
|
height="1.9999945"
|
||||||
width="15.99999"
|
width="15.99999"
|
||||||
y="105"
|
y="105"
|
||||||
x="76.000008"
|
x="76.000008"
|
||||||
style="fill:#ffffff;fill-opacity:0.80000007;stroke:none;stroke-width:0" />
|
style="fill:#ffffff;fill-opacity:0.8;stroke:none;stroke-width:0" />
|
||||||
<rect
|
<rect
|
||||||
id="rect4761-7"
|
id="rect4761-7"
|
||||||
height="1.9999945"
|
height="1.9999945"
|
||||||
width="9"
|
width="9"
|
||||||
y="109"
|
y="109"
|
||||||
x="76.000008"
|
x="76.000008"
|
||||||
style="fill:#ffffff;fill-opacity:0.80000007;stroke:none;stroke-width:0" />
|
style="fill:#ffffff;fill-opacity:0.8;stroke:none;stroke-width:0" />
|
||||||
<rect
|
<rect
|
||||||
id="rect4761-1-1"
|
id="rect4761-1-1"
|
||||||
height="1.9999945"
|
height="1.9999945"
|
||||||
width="12"
|
width="12"
|
||||||
y="125"
|
y="125"
|
||||||
x="76.000008"
|
x="76.000008"
|
||||||
style="fill:#ffffff;fill-opacity:0.80000007;stroke:none;stroke-width:0" />
|
style="fill:#ffffff;fill-opacity:0.8;stroke:none;stroke-width:0" />
|
||||||
<rect
|
<rect
|
||||||
id="rect4761-1-1-4"
|
id="rect4761-1-1-4"
|
||||||
height="1.9999945"
|
height="1.9999945"
|
||||||
width="10"
|
width="10"
|
||||||
y="137"
|
y="137"
|
||||||
x="76.000008"
|
x="76.000008"
|
||||||
style="fill:#ffffff;fill-opacity:0.80000007;stroke:none;stroke-width:0" />
|
style="fill:#ffffff;fill-opacity:0.8;stroke:none;stroke-width:0" />
|
||||||
<rect
|
<rect
|
||||||
id="rect4761-1-1-4-4"
|
id="rect4761-1-1-4-4"
|
||||||
height="1.9999945"
|
height="1.9999945"
|
||||||
width="10"
|
width="10"
|
||||||
y="129"
|
y="129"
|
||||||
x="82"
|
x="82"
|
||||||
style="fill:#ffffff;fill-opacity:0.80000007;stroke:none;stroke-width:0" />
|
style="fill:#ffffff;fill-opacity:0.8;stroke:none;stroke-width:0" />
|
||||||
<rect
|
<rect
|
||||||
id="rect4761-1-1-4-4-3"
|
id="rect4761-1-1-4-4-3"
|
||||||
height="1.9999945"
|
height="1.9999945"
|
||||||
width="9"
|
width="9"
|
||||||
y="133"
|
y="133"
|
||||||
x="82"
|
x="82"
|
||||||
style="fill:#ffffff;fill-opacity:0.80000007;stroke:none;stroke-width:0" />
|
style="fill:#ffffff;fill-opacity:0.8;stroke:none;stroke-width:0" />
|
||||||
<path
|
<path
|
||||||
inkscape:connector-curvature="0"
|
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"
|
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"
|
||||||
|
@ -890,4 +890,10 @@
|
||||||
id="path4300-6"
|
id="path4300-6"
|
||||||
inkscape:connector-curvature="0"
|
inkscape:connector-curvature="0"
|
||||||
sodipodi:nodetypes="cccc" />
|
sodipodi:nodetypes="cccc" />
|
||||||
|
<path
|
||||||
|
style="fill:#ffffff;fill-opacity:0.8;fill-rule:evenodd;stroke:#ffffff;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:round;stroke-opacity:0.80000001"
|
||||||
|
d="M 99.994369,113.0221 102,114.98353 l 7,-6.9558 3,0.97227 2,-1 1,-2 0,-3 -3,3 -3,-3 3,-3 -3,0 -2,1 -1,2 0.99437,3.0221 z"
|
||||||
|
id="path4268"
|
||||||
|
inkscape:connector-curvature="0"
|
||||||
|
sodipodi:nodetypes="ccccccccccccccc" />
|
||||||
</svg>
|
</svg>
|
||||||
|
|
Before Width: | Height: | Size: 35 KiB After Width: | Height: | Size: 36 KiB |
|
@ -210,6 +210,16 @@ div.jsoneditor-outer {
|
||||||
box-sizing: border-box;
|
box-sizing: border-box;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
div.jsoneditor-outer.has-nav-bar {
|
||||||
|
margin: -61px 0 0 0;
|
||||||
|
padding: 61px 0 0 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
div.jsoneditor-outer.has-status-bar {
|
||||||
|
margin: -35px 0 -26px 0;
|
||||||
|
padding: 35px 0 26px 0;
|
||||||
|
}
|
||||||
|
|
||||||
textarea.jsoneditor-text,
|
textarea.jsoneditor-text,
|
||||||
.ace-jsoneditor {
|
.ace-jsoneditor {
|
||||||
min-height: 150px;
|
min-height: 150px;
|
||||||
|
@ -239,7 +249,7 @@ textarea.jsoneditor-text {
|
||||||
|
|
||||||
tr.jsoneditor-highlight,
|
tr.jsoneditor-highlight,
|
||||||
tr.jsoneditor-selected {
|
tr.jsoneditor-selected {
|
||||||
background-color: #e6e6e6;
|
background-color: #d3d3d3;
|
||||||
}
|
}
|
||||||
|
|
||||||
tr.jsoneditor-selected button.jsoneditor-dragarea,
|
tr.jsoneditor-selected button.jsoneditor-dragarea,
|
||||||
|
|
|
@ -71,6 +71,9 @@ div.jsoneditor-menu > button.jsoneditor-compact {
|
||||||
div.jsoneditor-menu > button.jsoneditor-format {
|
div.jsoneditor-menu > button.jsoneditor-format {
|
||||||
background-position: -72px -120px;
|
background-position: -72px -120px;
|
||||||
}
|
}
|
||||||
|
div.jsoneditor-menu > button.jsoneditor-repair {
|
||||||
|
background-position: -96px -96px;
|
||||||
|
}
|
||||||
|
|
||||||
div.jsoneditor-menu > div.jsoneditor-modes {
|
div.jsoneditor-menu > div.jsoneditor-modes {
|
||||||
display: inline-block;
|
display: inline-block;
|
||||||
|
|
|
@ -0,0 +1,21 @@
|
||||||
|
div.jsoneditor-navigation-bar {
|
||||||
|
width: 100%;
|
||||||
|
height: 26px;
|
||||||
|
line-height: 26px;
|
||||||
|
padding: 0;
|
||||||
|
margin: 0;
|
||||||
|
border-bottom: 1px solid #d3d3d3;
|
||||||
|
-moz-box-sizing: border-box;
|
||||||
|
-webkit-box-sizing: border-box;
|
||||||
|
box-sizing: border-box;
|
||||||
|
color: #808080;
|
||||||
|
background-color: #ebebeb;
|
||||||
|
font-size: 10pt;
|
||||||
|
}
|
||||||
|
|
||||||
|
div.jsoneditor-navigation-bar.nav-bar-empty:after {
|
||||||
|
content: 'Select a node ...';
|
||||||
|
color: rgba(104, 104, 91, 0.56);
|
||||||
|
position: absolute;
|
||||||
|
margin-left: 5px;
|
||||||
|
}
|
|
@ -0,0 +1,25 @@
|
||||||
|
div.jsoneditor-statusbar {
|
||||||
|
line-height: 26px;
|
||||||
|
height: 26px;
|
||||||
|
margin-top: -26px;
|
||||||
|
color: #808080;
|
||||||
|
background-color: #ebebeb;
|
||||||
|
border-top: 1px solid #d3d3d3;
|
||||||
|
-moz-box-sizing: border-box;
|
||||||
|
-webkit-box-sizing: border-box;
|
||||||
|
box-sizing: border-box;
|
||||||
|
font-size: 10pt;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
div.jsoneditor-statusbar > .jsoneditor-curserinfo-label {
|
||||||
|
margin: 0 2px 0 4px;
|
||||||
|
}
|
||||||
|
|
||||||
|
div.jsoneditor-statusbar > .jsoneditor-curserinfo-val {
|
||||||
|
margin-right: 12px;
|
||||||
|
}
|
||||||
|
|
||||||
|
div.jsoneditor-statusbar > .jsoneditor-curserinfo-count {
|
||||||
|
margin-left: 4px;
|
||||||
|
}
|
|
@ -0,0 +1,27 @@
|
||||||
|
|
||||||
|
div.jsoneditor-treepath {
|
||||||
|
padding: 0 5px;
|
||||||
|
overflow: hidden;
|
||||||
|
}
|
||||||
|
|
||||||
|
div.jsoneditor-treepath div.jsoneditor-contextmenu-root {
|
||||||
|
position: absolute;
|
||||||
|
left: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
div.jsoneditor-treepath span.jsoneditor-treepath-element{
|
||||||
|
margin: 1px;
|
||||||
|
font-family: arial, sans-serif;
|
||||||
|
font-size: 10pt;
|
||||||
|
}
|
||||||
|
|
||||||
|
div.jsoneditor-treepath span.jsoneditor-treepath-seperator {
|
||||||
|
margin: 2px;
|
||||||
|
font-size: 9pt;
|
||||||
|
font-family: arial, sans-serif;
|
||||||
|
}
|
||||||
|
|
||||||
|
div.jsoneditor-treepath span.jsoneditor-treepath-element:hover, div.jsoneditor-treepath span.jsoneditor-treepath-seperator:hover {
|
||||||
|
cursor:pointer;
|
||||||
|
text-decoration:underline;
|
||||||
|
}
|
|
@ -2,6 +2,15 @@
|
||||||
|
|
||||||
var util = require('./util');
|
var util = require('./util');
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Node.getRootNode shim
|
||||||
|
* @param {Node} node node to check
|
||||||
|
* @return {Node} node's rootNode or `window` if there is ShadowDOM is not supported.
|
||||||
|
*/
|
||||||
|
function getRootNode(node){
|
||||||
|
return node.getRootNode && node.getRootNode() || window;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A context menu
|
* A context menu
|
||||||
* @param {Object[]} items Array containing the menu structure
|
* @param {Object[]} items Array containing the menu structure
|
||||||
|
@ -202,8 +211,12 @@ ContextMenu.prototype.show = function (anchor, contentWindow) {
|
||||||
|
|
||||||
// determine whether to display the menu below or above the anchor
|
// determine whether to display the menu below or above the anchor
|
||||||
var showBelow = true;
|
var showBelow = true;
|
||||||
if (contentWindow) {
|
var parent = anchor.parentNode;
|
||||||
var anchorRect = anchor.getBoundingClientRect();
|
var anchorRect = anchor.getBoundingClientRect();
|
||||||
|
var parentRect = parent.getBoundingClientRect()
|
||||||
|
|
||||||
|
if (contentWindow) {
|
||||||
|
|
||||||
var contentRect = contentWindow.getBoundingClientRect();
|
var contentRect = contentWindow.getBoundingClientRect();
|
||||||
|
|
||||||
if (anchorRect.bottom + this.maxHeight < contentRect.bottom) {
|
if (anchorRect.bottom + this.maxHeight < contentRect.bottom) {
|
||||||
|
@ -218,29 +231,34 @@ ContextMenu.prototype.show = function (anchor, contentWindow) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var leftGap = anchorRect.left - parentRect.left;
|
||||||
|
var topGap = anchorRect.top - parentRect.top;
|
||||||
|
|
||||||
// position the menu
|
// position the menu
|
||||||
if (showBelow) {
|
if (showBelow) {
|
||||||
// display the menu below the anchor
|
// display the menu below the anchor
|
||||||
var anchorHeight = anchor.offsetHeight;
|
var anchorHeight = anchor.offsetHeight;
|
||||||
this.dom.menu.style.left = '0px';
|
this.dom.menu.style.left = leftGap + 'px';
|
||||||
this.dom.menu.style.top = anchorHeight + 'px';
|
this.dom.menu.style.top = topGap + anchorHeight + 'px';
|
||||||
this.dom.menu.style.bottom = '';
|
this.dom.menu.style.bottom = '';
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
// display the menu above the anchor
|
// display the menu above the anchor
|
||||||
this.dom.menu.style.left = '0px';
|
this.dom.menu.style.left = leftGap + 'px';
|
||||||
this.dom.menu.style.top = '';
|
this.dom.menu.style.top = topGap + 'px';
|
||||||
this.dom.menu.style.bottom = '0px';
|
this.dom.menu.style.bottom = '0px';
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// find the root node of the page (window, or a shadow dom root element)
|
||||||
|
this.rootNode = getRootNode(anchor);
|
||||||
|
|
||||||
// attach the menu to the parent of the anchor
|
// attach the menu to the parent of the anchor
|
||||||
var parent = anchor.parentNode;
|
|
||||||
parent.insertBefore(this.dom.root, parent.firstChild);
|
parent.insertBefore(this.dom.root, parent.firstChild);
|
||||||
|
|
||||||
// create and attach event listeners
|
// create and attach event listeners
|
||||||
var me = this;
|
var me = this;
|
||||||
var list = this.dom.list;
|
var list = this.dom.list;
|
||||||
this.eventListeners.mousedown = util.addEventListener(window, 'mousedown', function (event) {
|
this.eventListeners.mousedown = util.addEventListener(this.rootNode, 'mousedown', function (event) {
|
||||||
// hide menu on click outside of the menu
|
// hide menu on click outside of the menu
|
||||||
var target = event.target;
|
var target = event.target;
|
||||||
if ((target != list) && !me._isChildOf(target, list)) {
|
if ((target != list) && !me._isChildOf(target, list)) {
|
||||||
|
@ -249,7 +267,7 @@ ContextMenu.prototype.show = function (anchor, contentWindow) {
|
||||||
event.preventDefault();
|
event.preventDefault();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
this.eventListeners.keydown = util.addEventListener(window, 'keydown', function (event) {
|
this.eventListeners.keydown = util.addEventListener(this.rootNode, 'keydown', function (event) {
|
||||||
me._onKeyDown(event);
|
me._onKeyDown(event);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -284,7 +302,7 @@ ContextMenu.prototype.hide = function () {
|
||||||
if (this.eventListeners.hasOwnProperty(name)) {
|
if (this.eventListeners.hasOwnProperty(name)) {
|
||||||
var fn = this.eventListeners[name];
|
var fn = this.eventListeners[name];
|
||||||
if (fn) {
|
if (fn) {
|
||||||
util.removeEventListener(window, name, fn);
|
util.removeEventListener(this.rootNode, name, fn);
|
||||||
}
|
}
|
||||||
delete this.eventListeners[name];
|
delete this.eventListeners[name];
|
||||||
}
|
}
|
||||||
|
|
|
@ -82,7 +82,8 @@ function JSONEditor (container, options, json) {
|
||||||
'ajv', 'schema', 'schemaRefs','templates',
|
'ajv', 'schema', 'schemaRefs','templates',
|
||||||
'ace', 'theme','autocomplete',
|
'ace', 'theme','autocomplete',
|
||||||
'onChange', 'onEditable', 'onError', 'onModeChange',
|
'onChange', 'onEditable', 'onError', 'onModeChange',
|
||||||
'escapeUnicode', 'history', 'search', 'mode', 'modes', 'name', 'indentation', 'sortObjectKeys'
|
'escapeUnicode', 'history', 'search', 'mode', 'modes', 'name', 'indentation',
|
||||||
|
'sortObjectKeys', 'navigationBar', 'statusBar'
|
||||||
];
|
];
|
||||||
|
|
||||||
Object.keys(options).forEach(function (option) {
|
Object.keys(options).forEach(function (option) {
|
||||||
|
|
|
@ -539,6 +539,20 @@ Node.prototype.hideChilds = function() {
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Goes through the path from the node to the root and ensures that it is expanded
|
||||||
|
*/
|
||||||
|
Node.prototype.expandTo = function() {
|
||||||
|
var currentNode = this.parent;
|
||||||
|
while (currentNode) {
|
||||||
|
if (!currentNode.expanded) {
|
||||||
|
currentNode.expand();
|
||||||
|
}
|
||||||
|
currentNode = currentNode.parent;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Add a new child to the node.
|
* Add a new child to the node.
|
||||||
* Only applicable when Node value is of type array or object
|
* Only applicable when Node value is of type array or object
|
||||||
|
@ -1998,7 +2012,9 @@ Node.prototype._updateSchema = function () {
|
||||||
//Locating the schema of the node and checking for any enum type
|
//Locating the schema of the node and checking for any enum type
|
||||||
if(this.editor && this.editor.options) {
|
if(this.editor && this.editor.options) {
|
||||||
// find the part of the json schema matching this nodes path
|
// find the part of the json schema matching this nodes path
|
||||||
this.schema = Node._findSchema(this.editor.options.schema, this.getPath());
|
this.schema = this.editor.options.schema
|
||||||
|
? Node._findSchema(this.editor.options.schema, this.getPath())
|
||||||
|
: null;
|
||||||
if (this.schema) {
|
if (this.schema) {
|
||||||
this.enum = Node._findEnum(this.schema);
|
this.enum = Node._findEnum(this.schema);
|
||||||
}
|
}
|
||||||
|
@ -2040,18 +2056,46 @@ Node._findEnum = function (schema) {
|
||||||
*/
|
*/
|
||||||
Node._findSchema = function (schema, path) {
|
Node._findSchema = function (schema, path) {
|
||||||
var childSchema = schema;
|
var childSchema = schema;
|
||||||
|
var foundSchema = childSchema;
|
||||||
|
|
||||||
|
var allSchemas = schema.oneOf || schema.anyOf || schema.allOf;
|
||||||
|
if (!allSchemas) {
|
||||||
|
allSchemas = [schema];
|
||||||
|
}
|
||||||
|
|
||||||
|
for (var j = 0; j < allSchemas.length; j++) {
|
||||||
|
childSchema = allSchemas[j];
|
||||||
|
|
||||||
for (var i = 0; i < path.length && childSchema; i++) {
|
for (var i = 0; i < path.length && childSchema; i++) {
|
||||||
var key = path[i];
|
var key = path[i];
|
||||||
if (typeof key === 'string' && childSchema.properties) {
|
|
||||||
childSchema = childSchema.properties[key] || null
|
if (typeof key === 'string' && childSchema.patternProperties && i == path.length - 1) {
|
||||||
|
for (var prop in childSchema.patternProperties) {
|
||||||
|
foundSchema = Node._findSchema(childSchema.patternProperties[prop], path.slice(i, path.length));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (childSchema.items && childSchema.items.properties) {
|
||||||
|
childSchema = childSchema.items.properties[key];
|
||||||
|
if (childSchema) {
|
||||||
|
foundSchema = Node._findSchema(childSchema, path.slice(i, path.length));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (typeof key === 'string' && childSchema.properties) {
|
||||||
|
childSchema = childSchema.properties[key] || null;
|
||||||
|
if (childSchema) {
|
||||||
|
foundSchema = Node._findSchema(childSchema, path.slice(i, path.length));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
else if (typeof key === 'number' && childSchema.items) {
|
else if (typeof key === 'number' && childSchema.items) {
|
||||||
childSchema = childSchema.items
|
childSchema = childSchema.items;
|
||||||
|
if (childSchema) {
|
||||||
|
foundSchema = Node._findSchema(childSchema, path.slice(i, path.length));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return childSchema
|
}
|
||||||
|
return foundSchema
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -2284,8 +2328,10 @@ Node.prototype.onEvent = function (event) {
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case 'click':
|
case 'click':
|
||||||
if (event.ctrlKey || !this.editable.value) {
|
if (event.ctrlKey && this.editable.value) {
|
||||||
|
// if read-only, we use the regular click behavior of an anchor
|
||||||
if (util.isUrl(this.value)) {
|
if (util.isUrl(this.value)) {
|
||||||
|
event.preventDefault();
|
||||||
window.open(this.value, '_blank');
|
window.open(this.value, '_blank');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,107 @@
|
||||||
|
'use strict';
|
||||||
|
|
||||||
|
var ContextMenu = require('./ContextMenu');
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a component that visualize path selection in tree based editors
|
||||||
|
* @param {HTMLElement} container
|
||||||
|
* @constructor
|
||||||
|
*/
|
||||||
|
function TreePath(container) {
|
||||||
|
if (container) {
|
||||||
|
this.path = document.createElement('div');
|
||||||
|
this.path.className = 'jsoneditor-treepath';
|
||||||
|
container.appendChild(this.path);
|
||||||
|
this.reset();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Reset component to initial status
|
||||||
|
*/
|
||||||
|
TreePath.prototype.reset = function () {
|
||||||
|
this.path.innerHTML = '';
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Renders the component UI according to a given path objects
|
||||||
|
* @param {Array<name: String, childs: Array>} pathObjs a list of path objects
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
TreePath.prototype.setPath = function (pathObjs) {
|
||||||
|
var me = this;
|
||||||
|
this.reset();
|
||||||
|
if (pathObjs && pathObjs.length) {
|
||||||
|
pathObjs.forEach(function (pathObj, idx) {
|
||||||
|
var pathEl = document.createElement('span');
|
||||||
|
var sepEl;
|
||||||
|
pathEl.className = 'jsoneditor-treepath-element';
|
||||||
|
pathEl.innerText = pathObj.name;
|
||||||
|
pathEl.onclick = _onSegmentClick.bind(me, pathObj);
|
||||||
|
|
||||||
|
me.path.appendChild(pathEl);
|
||||||
|
|
||||||
|
if (pathObj.children.length) {
|
||||||
|
sepEl = document.createElement('span');
|
||||||
|
sepEl.className = 'jsoneditor-treepath-seperator';
|
||||||
|
sepEl.innerHTML = '►';
|
||||||
|
|
||||||
|
sepEl.onclick = function () {
|
||||||
|
var items = [];
|
||||||
|
pathObj.children.forEach(function (child) {
|
||||||
|
items.push({
|
||||||
|
'text': child.name,
|
||||||
|
'className': 'jsoneditor-type-modes' + (pathObjs[idx + 1] + 1 && pathObjs[idx + 1].name === child.name ? ' jsoneditor-selected' : ''),
|
||||||
|
'click': _onContextMenuItemClick.bind(me, pathObj, child.name)
|
||||||
|
});
|
||||||
|
});
|
||||||
|
var menu = new ContextMenu(items);
|
||||||
|
menu.show(sepEl);
|
||||||
|
};
|
||||||
|
|
||||||
|
me.path.appendChild(sepEl, me.container);
|
||||||
|
}
|
||||||
|
|
||||||
|
if(idx === pathObjs.length - 1) {
|
||||||
|
var leftRectPos = (sepEl || pathEl).getBoundingClientRect().left;
|
||||||
|
if(me.path.offsetWidth < leftRectPos) {
|
||||||
|
me.path.scrollLeft = leftRectPos;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function _onSegmentClick(pathObj) {
|
||||||
|
if (this.selectionCallback) {
|
||||||
|
this.selectionCallback(pathObj);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
function _onContextMenuItemClick(pathObj, selection) {
|
||||||
|
if (this.contextMenuCallback) {
|
||||||
|
this.contextMenuCallback(pathObj, selection);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* set a callback function for selection of path section
|
||||||
|
* @param {Function} callback function to invoke when section is selected
|
||||||
|
*/
|
||||||
|
TreePath.prototype.onSectionSelected = function (callback) {
|
||||||
|
if (typeof callback === 'function') {
|
||||||
|
this.selectionCallback = callback;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* set a callback function for selection of path section
|
||||||
|
* @param {Function} callback function to invoke when section is selected
|
||||||
|
*/
|
||||||
|
TreePath.prototype.onContextMenuItemSelected = function (callback) {
|
||||||
|
if (typeof callback === 'function') {
|
||||||
|
this.contextMenuCallback = callback;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
module.exports = TreePath;
|
|
@ -1,4 +1,4 @@
|
||||||
'use strict';
|
'use strict';
|
||||||
|
|
||||||
function completely(config) {
|
function completely(config) {
|
||||||
config = config || {};
|
config = config || {};
|
||||||
|
|
|
@ -36,6 +36,11 @@ var DEFAULT_THEME = 'ace/theme/jsoneditor';
|
||||||
textmode.create = function (container, options) {
|
textmode.create = function (container, options) {
|
||||||
// read options
|
// read options
|
||||||
options = options || {};
|
options = options || {};
|
||||||
|
|
||||||
|
if(typeof options.statusBar === 'undefined') {
|
||||||
|
options.statusBar = true;
|
||||||
|
}
|
||||||
|
|
||||||
this.options = options;
|
this.options = options;
|
||||||
|
|
||||||
// indentation
|
// indentation
|
||||||
|
@ -131,6 +136,22 @@ textmode.create = function (container, options) {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// create repair button
|
||||||
|
var buttonRepair = document.createElement('button');
|
||||||
|
buttonRepair.type = 'button';
|
||||||
|
buttonRepair.className = 'jsoneditor-repair';
|
||||||
|
buttonRepair.title = 'Repair JSON: fix quotes and escape characters, remove comments and JSONP notation, turn JavaScript objects into JSON.';
|
||||||
|
this.menu.appendChild(buttonRepair);
|
||||||
|
buttonRepair.onclick = function () {
|
||||||
|
try {
|
||||||
|
me.repair();
|
||||||
|
me._onChange();
|
||||||
|
}
|
||||||
|
catch (err) {
|
||||||
|
me._onError(err);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
// create mode box
|
// create mode box
|
||||||
if (this.options && this.options.modes && this.options.modes.length) {
|
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) {
|
this.modeSwitcher = new ModeSwitcher(this.menu, this.options.modes, this.options.mode, function onSwitch(mode) {
|
||||||
|
@ -200,6 +221,7 @@ textmode.create = function (container, options) {
|
||||||
|
|
||||||
// register onchange event
|
// register onchange event
|
||||||
aceEditor.on('change', this._onChange.bind(this));
|
aceEditor.on('change', this._onChange.bind(this));
|
||||||
|
aceEditor.on('changeSelection', this._onSelect.bind(this));
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
// load a plain text textarea
|
// load a plain text textarea
|
||||||
|
@ -218,6 +240,62 @@ textmode.create = function (container, options) {
|
||||||
// oninput is undefined. For IE8-
|
// oninput is undefined. For IE8-
|
||||||
this.textarea.onchange = this._onChange.bind(this);
|
this.textarea.onchange = this._onChange.bind(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
textarea.onselect = this._onSelect.bind(this);
|
||||||
|
textarea.onmousedown = this._onMouseDown.bind(this);
|
||||||
|
textarea.onblur = this._onBlur.bind(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (options.statusBar) {
|
||||||
|
if (this.mode === 'code') {
|
||||||
|
util.addClassName(this.content, 'has-status-bar');
|
||||||
|
|
||||||
|
this.curserInfoElements = {};
|
||||||
|
var statusBar = document.createElement('div');
|
||||||
|
statusBar.className = 'jsoneditor-statusbar';
|
||||||
|
this.frame.appendChild(statusBar);
|
||||||
|
|
||||||
|
var lnLabel = document.createElement('span');
|
||||||
|
lnLabel.className = 'jsoneditor-curserinfo-label';
|
||||||
|
lnLabel.innerText = 'Ln:';
|
||||||
|
|
||||||
|
var lnVal = document.createElement('span');
|
||||||
|
lnVal.className = 'jsoneditor-curserinfo-val';
|
||||||
|
lnVal.innerText = 0;
|
||||||
|
|
||||||
|
statusBar.appendChild(lnLabel);
|
||||||
|
statusBar.appendChild(lnVal);
|
||||||
|
|
||||||
|
var colLabel = document.createElement('span');
|
||||||
|
colLabel.className = 'jsoneditor-curserinfo-label';
|
||||||
|
colLabel.innerText = 'Col:';
|
||||||
|
|
||||||
|
var colVal = document.createElement('span');
|
||||||
|
colVal.className = 'jsoneditor-curserinfo-val';
|
||||||
|
colVal.innerText = 0;
|
||||||
|
|
||||||
|
statusBar.appendChild(colLabel);
|
||||||
|
statusBar.appendChild(colVal);
|
||||||
|
|
||||||
|
this.curserInfoElements.colVal = colVal;
|
||||||
|
this.curserInfoElements.lnVal = lnVal;
|
||||||
|
|
||||||
|
var countLabel = document.createElement('span');
|
||||||
|
countLabel.className = 'jsoneditor-curserinfo-label';
|
||||||
|
countLabel.innerText = 'characters selected';
|
||||||
|
countLabel.style.display = 'none';
|
||||||
|
|
||||||
|
var countVal = document.createElement('span');
|
||||||
|
countVal.className = 'jsoneditor-curserinfo-count';
|
||||||
|
countVal.innerText = 0;
|
||||||
|
countVal.style.display = 'none';
|
||||||
|
|
||||||
|
this.curserInfoElements.countLabel = countLabel;
|
||||||
|
this.curserInfoElements.countVal = countVal;
|
||||||
|
|
||||||
|
statusBar.appendChild(countVal);
|
||||||
|
statusBar.appendChild(countLabel);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
this.setSchema(this.options.schema, this.options.schemaRefs);
|
this.setSchema(this.options.schema, this.options.schemaRefs);
|
||||||
|
@ -244,6 +322,29 @@ textmode._onChange = function () {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Handle text selection
|
||||||
|
* Calculates the cursor position and selection range and updates menu
|
||||||
|
* @private
|
||||||
|
*/
|
||||||
|
textmode._onSelect = function () {
|
||||||
|
if(this.options.statusBar) {
|
||||||
|
if (this.textarea) {
|
||||||
|
var selectionRange = util.getInputSelection(this.textarea);
|
||||||
|
if (selectionRange.start !== selectionRange.end) {
|
||||||
|
this._setSelectionCountDisplay(Math.abs(selectionRange.end - selectionRange.start));
|
||||||
|
}
|
||||||
|
} else if (this.aceEditor && this.curserInfoElements) {
|
||||||
|
var curserPos = this.aceEditor.getCursorPosition();
|
||||||
|
var selectedText = this.aceEditor.getSelectedText();
|
||||||
|
|
||||||
|
this.curserInfoElements.lnVal.innerText = curserPos.row + 1;
|
||||||
|
this.curserInfoElements.colVal.innerText = curserPos.column + 1;
|
||||||
|
this._setSelectionCountDisplay(selectedText.length);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Event handler for keydown. Handles shortcut keys
|
* Event handler for keydown. Handles shortcut keys
|
||||||
* @param {Event} event
|
* @param {Event} event
|
||||||
|
@ -269,6 +370,39 @@ textmode._onKeyDown = function (event) {
|
||||||
event.preventDefault();
|
event.preventDefault();
|
||||||
event.stopPropagation();
|
event.stopPropagation();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
this._setSelectionCountDisplay();
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Event handler for mousedown.
|
||||||
|
* @param {Event} event
|
||||||
|
* @private
|
||||||
|
*/
|
||||||
|
textmode._onMouseDown = function (event) {
|
||||||
|
this._setSelectionCountDisplay();
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Event handler for blur.
|
||||||
|
* @param {Event} event
|
||||||
|
* @private
|
||||||
|
*/
|
||||||
|
textmode._onBlur = function (event) {
|
||||||
|
this._setSelectionCountDisplay();
|
||||||
|
};
|
||||||
|
|
||||||
|
textmode._setSelectionCountDisplay = function (value) {
|
||||||
|
if (this.options.statusBar && this.curserInfoElements) {
|
||||||
|
if (value && this.curserInfoElements && this.curserInfoElements.countVal) {
|
||||||
|
this.curserInfoElements.countVal.innerText = value;
|
||||||
|
this.curserInfoElements.countVal.style.display = 'inline';
|
||||||
|
this.curserInfoElements.countLabel.style.display = 'inline';
|
||||||
|
} else {
|
||||||
|
this.curserInfoElements.countVal.style.display = 'none';
|
||||||
|
this.curserInfoElements.countLabel.style.display = 'none';
|
||||||
|
}
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -296,7 +430,7 @@ textmode.destroy = function () {
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Compact the code in the formatter
|
* Compact the code in the text editor
|
||||||
*/
|
*/
|
||||||
textmode.compact = function () {
|
textmode.compact = function () {
|
||||||
var json = this.get();
|
var json = this.get();
|
||||||
|
@ -305,7 +439,7 @@ textmode.compact = function () {
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Format the code in the formatter
|
* Format the code in the text editor
|
||||||
*/
|
*/
|
||||||
textmode.format = function () {
|
textmode.format = function () {
|
||||||
var json = this.get();
|
var json = this.get();
|
||||||
|
@ -313,6 +447,15 @@ textmode.format = function () {
|
||||||
this.setText(text);
|
this.setText(text);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Repair the code in the text editor
|
||||||
|
*/
|
||||||
|
textmode.repair = function () {
|
||||||
|
var text = this.getText();
|
||||||
|
var sanitizedText = util.sanitize(text);
|
||||||
|
this.setText(sanitizedText);
|
||||||
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Set focus to the formatter
|
* Set focus to the formatter
|
||||||
*/
|
*/
|
||||||
|
@ -405,7 +548,6 @@ textmode.setText = function(jsonText) {
|
||||||
|
|
||||||
this.options.onChange = originalOnChange;
|
this.options.onChange = originalOnChange;
|
||||||
}
|
}
|
||||||
|
|
||||||
// validate JSON schema
|
// validate JSON schema
|
||||||
this.validate();
|
this.validate();
|
||||||
};
|
};
|
||||||
|
|
|
@ -5,6 +5,7 @@ var Highlighter = require('./Highlighter');
|
||||||
var History = require('./History');
|
var History = require('./History');
|
||||||
var SearchBox = require('./SearchBox');
|
var SearchBox = require('./SearchBox');
|
||||||
var ContextMenu = require('./ContextMenu');
|
var ContextMenu = require('./ContextMenu');
|
||||||
|
var TreePath = require('./TreePath');
|
||||||
var Node = require('./Node');
|
var Node = require('./Node');
|
||||||
var ModeSwitcher = require('./ModeSwitcher');
|
var ModeSwitcher = require('./ModeSwitcher');
|
||||||
var util = require('./util');
|
var util = require('./util');
|
||||||
|
@ -113,7 +114,8 @@ treemode._setOptions = function (options) {
|
||||||
name: undefined, // field name of root node
|
name: undefined, // field name of root node
|
||||||
schema: null,
|
schema: null,
|
||||||
schemaRefs: null,
|
schemaRefs: null,
|
||||||
autocomplete: null
|
autocomplete: null,
|
||||||
|
navigationBar : true
|
||||||
};
|
};
|
||||||
|
|
||||||
// copy all options
|
// copy all options
|
||||||
|
@ -757,6 +759,17 @@ treemode._createFrame = function () {
|
||||||
if (this.options.search) {
|
if (this.options.search) {
|
||||||
this.searchBox = new SearchBox(this, this.menu);
|
this.searchBox = new SearchBox(this, this.menu);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if(this.options.navigationBar) {
|
||||||
|
// create second menu row for treepath
|
||||||
|
this.navBar = document.createElement('div');
|
||||||
|
this.navBar.className = 'jsoneditor-navigation-bar nav-bar-empty';
|
||||||
|
this.frame.appendChild(this.navBar);
|
||||||
|
|
||||||
|
this.treePath = new TreePath(this.navBar);
|
||||||
|
this.treePath.onSectionSelected(this._onTreePathSectionSelected.bind(this));
|
||||||
|
this.treePath.onContextMenuItemSelected(this._onTreePathMenuItemSelected.bind(this));
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -810,6 +823,10 @@ treemode._onEvent = function (event) {
|
||||||
|
|
||||||
var node = Node.getNodeFromTarget(event.target);
|
var node = Node.getNodeFromTarget(event.target);
|
||||||
|
|
||||||
|
if (this.options && this.options.navigationBar && node && (event.type == 'keydown' || event.type == 'mousedown')) {
|
||||||
|
this._updateTreePath(node.getNodePath());
|
||||||
|
}
|
||||||
|
|
||||||
if (node && node.selected) {
|
if (node && node.selected) {
|
||||||
if (event.type == 'click') {
|
if (event.type == 'click') {
|
||||||
if (event.target == node.dom.menu) {
|
if (event.target == node.dom.menu) {
|
||||||
|
@ -850,6 +867,73 @@ treemode._onEvent = function (event) {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Update TreePath components
|
||||||
|
* @param {Array<Node>} pathNodes list of nodes in path from root to selection
|
||||||
|
* @private
|
||||||
|
*/
|
||||||
|
treemode._updateTreePath = function (pathNodes) {
|
||||||
|
if (pathNodes && pathNodes.length) {
|
||||||
|
util.removeClassName(this.navBar, 'nav-bar-empty');
|
||||||
|
|
||||||
|
var pathObjs = [];
|
||||||
|
pathNodes.forEach(function (node) {
|
||||||
|
var pathObj = {
|
||||||
|
name: getName(node),
|
||||||
|
node: node,
|
||||||
|
children: []
|
||||||
|
}
|
||||||
|
if (node.childs && node.childs.length) {
|
||||||
|
node.childs.forEach(function (childNode) {
|
||||||
|
pathObj.children.push({
|
||||||
|
name: getName(childNode),
|
||||||
|
node: childNode
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
pathObjs.push(pathObj);
|
||||||
|
});
|
||||||
|
this.treePath.setPath(pathObjs);
|
||||||
|
} else {
|
||||||
|
util.addClassName(this.navBar, 'nav-bar-empty');
|
||||||
|
}
|
||||||
|
|
||||||
|
function getName(node) {
|
||||||
|
return node.field || (isNaN(node.index) ? node.type : node.index);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Callback for tree path section selection - focus the selected node in the tree
|
||||||
|
* @param {Object} pathObj path object that was represents the selected section node
|
||||||
|
* @private
|
||||||
|
*/
|
||||||
|
treemode._onTreePathSectionSelected = function (pathObj) {
|
||||||
|
if(pathObj && pathObj.node) {
|
||||||
|
pathObj.node.expandTo();
|
||||||
|
pathObj.node.focus();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Callback for tree path menu item selection - rebuild the path accrding to the new selection and focus the selected node in the tree
|
||||||
|
* @param {Object} pathObj path object that was represents the parent section node
|
||||||
|
* @param {String} selection selected section child
|
||||||
|
* @private
|
||||||
|
*/
|
||||||
|
treemode._onTreePathMenuItemSelected = function (pathObj, selection) {
|
||||||
|
if(pathObj && pathObj.children.length) {
|
||||||
|
var selectionObj = pathObj.children.find(function (obj) {
|
||||||
|
return obj.name === selection;
|
||||||
|
});
|
||||||
|
if(selectionObj && selectionObj.node) {
|
||||||
|
this._updateTreePath(selectionObj.node.getNodePath());
|
||||||
|
selectionObj.node.expandTo();
|
||||||
|
selectionObj.node.focus();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
treemode._startDragDistance = function (event) {
|
treemode._startDragDistance = function (event) {
|
||||||
this.dragDistanceEvent = {
|
this.dragDistanceEvent = {
|
||||||
initialTarget: event.target,
|
initialTarget: event.target,
|
||||||
|
@ -1162,6 +1246,9 @@ treemode._onKeyDown = function (event) {
|
||||||
treemode._createTable = function () {
|
treemode._createTable = function () {
|
||||||
var contentOuter = document.createElement('div');
|
var contentOuter = document.createElement('div');
|
||||||
contentOuter.className = 'jsoneditor-outer';
|
contentOuter.className = 'jsoneditor-outer';
|
||||||
|
if(this.options.navigationBar) {
|
||||||
|
util.addClassName(contentOuter, 'has-nav-bar');
|
||||||
|
}
|
||||||
this.contentOuter = contentOuter;
|
this.contentOuter = contentOuter;
|
||||||
|
|
||||||
this.content = document.createElement('div');
|
this.content = document.createElement('div');
|
||||||
|
|
114
src/js/util.js
114
src/js/util.js
|
@ -50,6 +50,15 @@ exports.sanitize = function (jsString) {
|
||||||
'\t': '\\t'
|
'\t': '\\t'
|
||||||
};
|
};
|
||||||
|
|
||||||
|
var quote = '\'';
|
||||||
|
var quoteDbl = '"';
|
||||||
|
var quoteLeft = '\u2018';
|
||||||
|
var quoteRight = '\u2019';
|
||||||
|
var quoteDblLeft = '\u201C';
|
||||||
|
var quoteDblRight = '\u201D';
|
||||||
|
var graveAccent = '\u0060';
|
||||||
|
var acuteAccent = '\u00B4';
|
||||||
|
|
||||||
// helper functions to get the current/prev/next character
|
// helper functions to get the current/prev/next character
|
||||||
function curr () { return jsString.charAt(i); }
|
function curr () { return jsString.charAt(i); }
|
||||||
function next() { return jsString.charAt(i + 1); }
|
function next() { return jsString.charAt(i + 1); }
|
||||||
|
@ -88,11 +97,11 @@ exports.sanitize = function (jsString) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// parse single or double quoted string
|
// parse single or double quoted string
|
||||||
function parseString(quote) {
|
function parseString(endQuote) {
|
||||||
chars.push('"');
|
chars.push('"');
|
||||||
i++;
|
i++;
|
||||||
var c = curr();
|
var c = curr();
|
||||||
while (i < jsString.length && c !== quote) {
|
while (i < jsString.length && c !== endQuote) {
|
||||||
if (c === '"' && prev() !== '\\') {
|
if (c === '"' && prev() !== '\\') {
|
||||||
// unescaped double quote, escape it
|
// unescaped double quote, escape it
|
||||||
chars.push('\\"');
|
chars.push('\\"');
|
||||||
|
@ -118,7 +127,7 @@ exports.sanitize = function (jsString) {
|
||||||
i++;
|
i++;
|
||||||
c = curr();
|
c = curr();
|
||||||
}
|
}
|
||||||
if (c === quote) {
|
if (c === endQuote) {
|
||||||
chars.push('"');
|
chars.push('"');
|
||||||
i++;
|
i++;
|
||||||
}
|
}
|
||||||
|
@ -154,8 +163,25 @@ exports.sanitize = function (jsString) {
|
||||||
else if (c === '/' && next() === '/') {
|
else if (c === '/' && next() === '/') {
|
||||||
skipComment();
|
skipComment();
|
||||||
}
|
}
|
||||||
else if (c === '\'' || c === '"') {
|
else if (c === '\u00A0' || (c >= '\u2000' && c <= '\u200A') || c === '\u202F' || c === '\u205F' || c === '\u3000') {
|
||||||
parseString(c);
|
// special white spaces (like non breaking space)
|
||||||
|
chars.push(' ')
|
||||||
|
i++
|
||||||
|
}
|
||||||
|
else if (c === quote) {
|
||||||
|
parseString(quote);
|
||||||
|
}
|
||||||
|
else if (c === quoteDbl) {
|
||||||
|
parseString(quoteDbl);
|
||||||
|
}
|
||||||
|
else if (c === graveAccent) {
|
||||||
|
parseString(acuteAccent);
|
||||||
|
}
|
||||||
|
else if (c === quoteLeft) {
|
||||||
|
parseString(quoteRight);
|
||||||
|
}
|
||||||
|
else if (c === quoteDblLeft) {
|
||||||
|
parseString(quoteDblRight);
|
||||||
}
|
}
|
||||||
else if (/[a-zA-Z_$]/.test(c) && ['{', ','].indexOf(lastNonWhitespace()) !== -1) {
|
else if (/[a-zA-Z_$]/.test(c) && ['{', ','].indexOf(lastNonWhitespace()) !== -1) {
|
||||||
// an unquoted object key (like a in '{a:2}')
|
// an unquoted object key (like a in '{a:2}')
|
||||||
|
@ -790,10 +816,64 @@ exports.textDiff = function textDiff(oldText, newText) {
|
||||||
return {start: start, end: newEnd};
|
return {start: start, end: newEnd};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return an object with the selection range or cursor position (if both have the same value)
|
||||||
|
* Support also old browsers (IE8-)
|
||||||
|
* Source: http://ourcodeworld.com/articles/read/282/how-to-get-the-current-cursor-position-and-selection-within-a-text-input-or-textarea-in-javascript
|
||||||
|
* @param {DOMElement} el A dom element of a textarea or input text.
|
||||||
|
* @return {Object} reference Object with 2 properties (start and end) with the identifier of the location of the cursor and selected text.
|
||||||
|
**/
|
||||||
|
exports.getInputSelection = function(el) {
|
||||||
|
var start = 0, end = 0, normalizedValue, range, textInputRange, len, endRange;
|
||||||
|
|
||||||
|
if (typeof el.selectionStart == "number" && typeof el.selectionEnd == "number") {
|
||||||
|
start = el.selectionStart;
|
||||||
|
end = el.selectionEnd;
|
||||||
|
} else {
|
||||||
|
range = document.selection.createRange();
|
||||||
|
|
||||||
|
if (range && range.parentElement() == el) {
|
||||||
|
len = el.value.length;
|
||||||
|
normalizedValue = el.value.replace(/\r\n/g, "\n");
|
||||||
|
|
||||||
|
// Create a working TextRange that lives only in the input
|
||||||
|
textInputRange = el.createTextRange();
|
||||||
|
textInputRange.moveToBookmark(range.getBookmark());
|
||||||
|
|
||||||
|
// Check if the start and end of the selection are at the very end
|
||||||
|
// of the input, since moveStart/moveEnd doesn't return what we want
|
||||||
|
// in those cases
|
||||||
|
endRange = el.createTextRange();
|
||||||
|
endRange.collapse(false);
|
||||||
|
|
||||||
|
if (textInputRange.compareEndPoints("StartToEnd", endRange) > -1) {
|
||||||
|
start = end = len;
|
||||||
|
} else {
|
||||||
|
start = -textInputRange.moveStart("character", -len);
|
||||||
|
start += normalizedValue.slice(0, start).split("\n").length - 1;
|
||||||
|
|
||||||
|
if (textInputRange.compareEndPoints("EndToEnd", endRange) > -1) {
|
||||||
|
end = len;
|
||||||
|
} else {
|
||||||
|
end = -textInputRange.moveEnd("character", -len);
|
||||||
|
end += normalizedValue.slice(0, end).split("\n").length - 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
start: start,
|
||||||
|
end: end
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
if (typeof Element !== 'undefined') {
|
if (typeof Element !== 'undefined') {
|
||||||
// Polyfill for array remove
|
// Polyfill for array remove
|
||||||
(function (arr) {
|
(function () {
|
||||||
arr.forEach(function (item) {
|
function polyfill (item) {
|
||||||
if (item.hasOwnProperty('remove')) {
|
if (item.hasOwnProperty('remove')) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -806,8 +886,12 @@ if (typeof Element !== 'undefined') {
|
||||||
this.parentNode.removeChild(this);
|
this.parentNode.removeChild(this);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
});
|
}
|
||||||
})([Element.prototype, CharacterData.prototype, DocumentType.prototype]);
|
|
||||||
|
if (typeof Element !== 'undefined') { polyfill(Element.prototype); }
|
||||||
|
if (typeof CharacterData !== 'undefined') { polyfill(CharacterData.prototype); }
|
||||||
|
if (typeof DocumentType !== 'undefined') { polyfill(DocumentType.prototype); }
|
||||||
|
})();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -818,3 +902,15 @@ if (!String.prototype.startsWith) {
|
||||||
return this.substr(position, searchString.length) === searchString;
|
return this.substr(position, searchString.length) === searchString;
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Polyfill for Array.find
|
||||||
|
if (!Array.prototype.find) {
|
||||||
|
Array.prototype.find = function(callback) {
|
||||||
|
for (var i = 0; i < this.length; i++) {
|
||||||
|
var element = this[i];
|
||||||
|
if ( callback.call(this, element, i, this) ) {
|
||||||
|
return element;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -14,6 +14,8 @@ describe('util', function () {
|
||||||
|
|
||||||
it('should replace JavaScript with JSON', function () {
|
it('should replace JavaScript with JSON', function () {
|
||||||
assert.equal(util.sanitize('{a:2}'), '{"a":2}');
|
assert.equal(util.sanitize('{a:2}'), '{"a":2}');
|
||||||
|
assert.equal(util.sanitize('{a: 2}'), '{"a": 2}');
|
||||||
|
assert.equal(util.sanitize('{\n a: 2\n}'), '{\n "a": 2\n}');
|
||||||
assert.equal(util.sanitize('{\'a\':2}'), '{"a":2}');
|
assert.equal(util.sanitize('{\'a\':2}'), '{"a":2}');
|
||||||
assert.equal(util.sanitize('{a:\'foo\'}'), '{"a":"foo"}');
|
assert.equal(util.sanitize('{a:\'foo\'}'), '{"a":"foo"}');
|
||||||
assert.equal(util.sanitize('{a:\'foo\',b:\'bar\'}'), '{"a":"foo","b":"bar"}');
|
assert.equal(util.sanitize('{a:\'foo\',b:\'bar\'}'), '{"a":"foo","b":"bar"}');
|
||||||
|
@ -30,6 +32,11 @@ describe('util', function () {
|
||||||
assert.equal(util.sanitize('"foo\\\'bar"'), '"foo\'bar"');
|
assert.equal(util.sanitize('"foo\\\'bar"'), '"foo\'bar"');
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('should replace special white characters', function () {
|
||||||
|
assert.equal(util.sanitize('{"a":\u00a0"foo\u00a0bar"}'), '{"a": "foo\u00a0bar"}');
|
||||||
|
assert.equal(util.sanitize('{"a":\u2009"foo"}'), '{"a": "foo"}');
|
||||||
|
});
|
||||||
|
|
||||||
it('should escape unescaped control characters', function () {
|
it('should escape unescaped control characters', function () {
|
||||||
assert.equal(util.sanitize('"hello\bworld"'), '"hello\\bworld"')
|
assert.equal(util.sanitize('"hello\bworld"'), '"hello\\bworld"')
|
||||||
assert.equal(util.sanitize('"hello\fworld"'), '"hello\\fworld"')
|
assert.equal(util.sanitize('"hello\fworld"'), '"hello\\fworld"')
|
||||||
|
@ -39,6 +46,12 @@ describe('util', function () {
|
||||||
assert.equal(util.sanitize('{"value\n": "dc=hcm,dc=com"}'), '{"value\\n": "dc=hcm,dc=com"}')
|
assert.equal(util.sanitize('{"value\n": "dc=hcm,dc=com"}'), '{"value\\n": "dc=hcm,dc=com"}')
|
||||||
})
|
})
|
||||||
|
|
||||||
|
it('should replace left/right quotes', function () {
|
||||||
|
assert.equal(util.sanitize('\u2018foo\u2019'), '"foo"')
|
||||||
|
assert.equal(util.sanitize('\u201Cfoo\u201D'), '"foo"')
|
||||||
|
assert.equal(util.sanitize('\u0060foo\u00B4'), '"foo"')
|
||||||
|
})
|
||||||
|
|
||||||
it('remove comments', function () {
|
it('remove comments', function () {
|
||||||
assert.equal(util.sanitize('/* foo */ {}'), ' {}');
|
assert.equal(util.sanitize('/* foo */ {}'), ' {}');
|
||||||
assert.equal(util.sanitize('/* foo */ {}'), ' {}');
|
assert.equal(util.sanitize('/* foo */ {}'), ' {}');
|
||||||
|
|
Loading…
Reference in New Issue