Initial commit with a new Svelte setup
This commit is contained in:
parent
cfa5575884
commit
1bc28041d3
|
@ -1,10 +1,5 @@
|
||||||
|
/node_modules/
|
||||||
|
/public/build/
|
||||||
|
|
||||||
|
.DS_Store
|
||||||
.idea
|
.idea
|
||||||
*.iml
|
|
||||||
.vscode
|
|
||||||
build
|
|
||||||
dist
|
|
||||||
downloads
|
|
||||||
node_modules
|
|
||||||
*.zip
|
|
||||||
npm-debug.log
|
|
||||||
/.vs
|
|
||||||
|
|
|
@ -0,0 +1,3 @@
|
||||||
|
{
|
||||||
|
"test": "./src/**/*.test.js"
|
||||||
|
}
|
13
.npmignore
13
.npmignore
|
@ -1,13 +0,0 @@
|
||||||
bower.json
|
|
||||||
CONTRIBUTING.md
|
|
||||||
downloads
|
|
||||||
misc
|
|
||||||
node_modules
|
|
||||||
test
|
|
||||||
tools
|
|
||||||
.idea
|
|
||||||
component.json
|
|
||||||
.npmignore
|
|
||||||
.gitignore
|
|
||||||
*.zip
|
|
||||||
npm-debug.log
|
|
|
@ -1,5 +0,0 @@
|
||||||
language: node_js
|
|
||||||
node_js:
|
|
||||||
- "lts/*"
|
|
||||||
|
|
||||||
script: npm test && npm run lint
|
|
|
@ -1,11 +0,0 @@
|
||||||
{
|
|
||||||
"groups": {
|
|
||||||
"default": {
|
|
||||||
"packages": [
|
|
||||||
"examples/react_advanced_demo/package.json",
|
|
||||||
"examples/react_demo/package.json",
|
|
||||||
"package.json"
|
|
||||||
]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
238
gulpfile.js
238
gulpfile.js
|
@ -1,238 +0,0 @@
|
||||||
const fs = require('fs')
|
|
||||||
const path = require('path')
|
|
||||||
const gulp = require('gulp')
|
|
||||||
const log = require('fancy-log')
|
|
||||||
const format = require('date-format')
|
|
||||||
const concatCss = require('gulp-concat-css')
|
|
||||||
const minifyCSS = require('gulp-clean-css')
|
|
||||||
const sass = require('gulp-sass')
|
|
||||||
const mkdirp = require('mkdirp')
|
|
||||||
const webpack = require('webpack')
|
|
||||||
const uglify = require('uglify-js')
|
|
||||||
const btoa = require('btoa')
|
|
||||||
|
|
||||||
const NAME = 'jsoneditor'
|
|
||||||
const NAME_MINIMALIST = 'jsoneditor-minimalist'
|
|
||||||
const ENTRY = './src/js/JSONEditor.js'
|
|
||||||
const HEADER = './src/js/header.js'
|
|
||||||
const IMAGE = './src/scss/img/jsoneditor-icons.svg'
|
|
||||||
const DOCS = './src/docs/*'
|
|
||||||
const DIST = path.join(__dirname, 'dist')
|
|
||||||
|
|
||||||
// generate banner with today's date and correct version
|
|
||||||
function createBanner () {
|
|
||||||
const today = format.asString('yyyy-MM-dd', new Date()) // today, formatted as yyyy-MM-dd
|
|
||||||
const version = require('./package.json').version // math.js version
|
|
||||||
|
|
||||||
return String(fs.readFileSync(HEADER))
|
|
||||||
.replace('@@date', today)
|
|
||||||
.replace('@@version', version)
|
|
||||||
}
|
|
||||||
|
|
||||||
const bannerPlugin = new webpack.BannerPlugin({
|
|
||||||
banner: createBanner(),
|
|
||||||
entryOnly: true,
|
|
||||||
raw: true
|
|
||||||
})
|
|
||||||
|
|
||||||
const webpackConfigModule = {
|
|
||||||
rules: [
|
|
||||||
{
|
|
||||||
test: /\.m?js$/,
|
|
||||||
exclude: /node_modules/,
|
|
||||||
use: {
|
|
||||||
loader: 'babel-loader'
|
|
||||||
}
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
|
|
||||||
// create a single instance of the compiler to allow caching
|
|
||||||
const compiler = webpack({
|
|
||||||
entry: ENTRY,
|
|
||||||
output: {
|
|
||||||
library: 'JSONEditor',
|
|
||||||
libraryTarget: 'umd',
|
|
||||||
path: DIST,
|
|
||||||
filename: NAME + '.js'
|
|
||||||
},
|
|
||||||
plugins: [bannerPlugin],
|
|
||||||
optimization: {
|
|
||||||
// We no not want to minimize our code.
|
|
||||||
minimize: false
|
|
||||||
},
|
|
||||||
module: webpackConfigModule,
|
|
||||||
resolve: {
|
|
||||||
extensions: ['.js'],
|
|
||||||
mainFields: ['main'] // pick ES5 version of vanilla-picker
|
|
||||||
},
|
|
||||||
cache: true
|
|
||||||
})
|
|
||||||
|
|
||||||
// create a single instance of the compiler to allow caching
|
|
||||||
const compilerMinimalist = webpack({
|
|
||||||
entry: ENTRY,
|
|
||||||
output: {
|
|
||||||
library: 'JSONEditor',
|
|
||||||
libraryTarget: 'umd',
|
|
||||||
path: DIST,
|
|
||||||
filename: NAME_MINIMALIST + '.js'
|
|
||||||
},
|
|
||||||
module: webpackConfigModule,
|
|
||||||
plugins: [
|
|
||||||
bannerPlugin,
|
|
||||||
new webpack.IgnorePlugin(new RegExp('^ace-builds')),
|
|
||||||
new webpack.IgnorePlugin(new RegExp('worker-json-data-url')),
|
|
||||||
new webpack.IgnorePlugin(new RegExp('^ajv')),
|
|
||||||
new webpack.IgnorePlugin(new RegExp('^vanilla-picker'))
|
|
||||||
],
|
|
||||||
optimization: {
|
|
||||||
// We no not want to minimize our code.
|
|
||||||
minimize: false
|
|
||||||
},
|
|
||||||
cache: true
|
|
||||||
})
|
|
||||||
|
|
||||||
function minify (name) {
|
|
||||||
const code = String(fs.readFileSync(DIST + '/' + name + '.js'))
|
|
||||||
const result = uglify.minify(code, {
|
|
||||||
sourceMap: {
|
|
||||||
url: name + '.map'
|
|
||||||
},
|
|
||||||
output: {
|
|
||||||
comments: /@license/,
|
|
||||||
max_line_len: 64000 // extra large because we have embedded code for workers
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
if (result.error) {
|
|
||||||
throw result.error
|
|
||||||
}
|
|
||||||
|
|
||||||
const fileMin = DIST + '/' + name + '.min.js'
|
|
||||||
const fileMap = DIST + '/' + name + '.map'
|
|
||||||
|
|
||||||
fs.writeFileSync(fileMin, result.code)
|
|
||||||
fs.writeFileSync(fileMap, result.map)
|
|
||||||
|
|
||||||
log('Minified ' + fileMin)
|
|
||||||
log('Mapped ' + fileMap)
|
|
||||||
}
|
|
||||||
|
|
||||||
// make dist folder structure
|
|
||||||
gulp.task('mkdir', function (done) {
|
|
||||||
mkdirp.sync(DIST)
|
|
||||||
mkdirp.sync(DIST + '/img')
|
|
||||||
|
|
||||||
done()
|
|
||||||
})
|
|
||||||
|
|
||||||
// Create an embedded version of the json worker code: a data url
|
|
||||||
gulp.task('embed-json-worker', function (done) {
|
|
||||||
const workerBundleFile = './node_modules/ace-builds/src-noconflict/worker-json.js'
|
|
||||||
const workerEmbeddedFile = './src/js/generated/worker-json-data-url.js'
|
|
||||||
const workerScript = String(fs.readFileSync(workerBundleFile))
|
|
||||||
|
|
||||||
const workerDataUrl = 'data:application/javascript;base64,' + btoa(workerScript)
|
|
||||||
|
|
||||||
fs.writeFileSync(workerEmbeddedFile, 'module.exports = \'' + workerDataUrl + '\'\n')
|
|
||||||
|
|
||||||
done()
|
|
||||||
})
|
|
||||||
|
|
||||||
// bundle javascript
|
|
||||||
gulp.task('bundle', 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) {
|
|
||||||
log(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
log('bundled ' + NAME + '.js')
|
|
||||||
|
|
||||||
done()
|
|
||||||
})
|
|
||||||
})
|
|
||||||
|
|
||||||
// bundle minimalist version of javascript
|
|
||||||
gulp.task('bundle-minimalist', 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) {
|
|
||||||
log(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
log('bundled ' + NAME_MINIMALIST + '.js')
|
|
||||||
|
|
||||||
done()
|
|
||||||
})
|
|
||||||
})
|
|
||||||
|
|
||||||
// bundle css
|
|
||||||
gulp.task('bundle-css', function (done) {
|
|
||||||
gulp
|
|
||||||
.src(['src/scss/jsoneditor.scss'])
|
|
||||||
.pipe(
|
|
||||||
sass({
|
|
||||||
// importer: tildeImporter
|
|
||||||
})
|
|
||||||
)
|
|
||||||
.pipe(concatCss(NAME + '.css'))
|
|
||||||
.pipe(gulp.dest(DIST))
|
|
||||||
.pipe(concatCss(NAME + '.min.css'))
|
|
||||||
.pipe(minifyCSS())
|
|
||||||
.pipe(gulp.dest(DIST))
|
|
||||||
done()
|
|
||||||
})
|
|
||||||
|
|
||||||
// create a folder img and copy the icons
|
|
||||||
gulp.task('copy-img', function (done) {
|
|
||||||
gulp.src(IMAGE).pipe(gulp.dest(DIST + '/img'))
|
|
||||||
log('Copied images')
|
|
||||||
|
|
||||||
done()
|
|
||||||
})
|
|
||||||
|
|
||||||
// create a folder img and copy the icons
|
|
||||||
gulp.task('copy-docs', function (done) {
|
|
||||||
gulp.src(DOCS).pipe(gulp.dest(DIST))
|
|
||||||
log('Copied doc')
|
|
||||||
|
|
||||||
done()
|
|
||||||
})
|
|
||||||
|
|
||||||
gulp.task('minify', function (done) {
|
|
||||||
minify(NAME)
|
|
||||||
|
|
||||||
done()
|
|
||||||
})
|
|
||||||
|
|
||||||
gulp.task('minify-minimalist', function (done) {
|
|
||||||
minify(NAME_MINIMALIST)
|
|
||||||
|
|
||||||
done()
|
|
||||||
})
|
|
||||||
|
|
||||||
// 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', gulp.series('bundle', 'bundle-css', 'copy-img', function () {
|
|
||||||
gulp.watch(['src/**/*'], gulp.series('bundle', 'bundle-css', 'copy-img'))
|
|
||||||
}))
|
|
||||||
|
|
||||||
// The default task (called when you run `gulp`)
|
|
||||||
gulp.task('default', gulp.series(
|
|
||||||
'mkdir',
|
|
||||||
'embed-json-worker',
|
|
||||||
gulp.parallel(
|
|
||||||
'copy-img',
|
|
||||||
'copy-docs',
|
|
||||||
'bundle-css',
|
|
||||||
gulp.series('bundle', 'minify'),
|
|
||||||
gulp.series('bundle-minimalist', 'minify-minimalist')
|
|
||||||
)
|
|
||||||
))
|
|
|
@ -0,0 +1,14 @@
|
||||||
|
# JSONEditor Svelte architecture choices
|
||||||
|
|
||||||
|
- Immutable -> one-way data binding
|
||||||
|
- All Actions based on JSONPatch
|
||||||
|
- It must be possible to persist all state, including expanded/collapsed and
|
||||||
|
selection.
|
||||||
|
- State with search results, and, expanded state, and selection is separate
|
||||||
|
from the JSON document itself, and are JSON objectswith the same structure,
|
||||||
|
using symbols.
|
||||||
|
- Must be able to open huge JSON files
|
||||||
|
- Must work directly on the JSON object itself, not on a wrapped object model
|
||||||
|
- Display only the first 100 items of an array etc. Or show items in groups
|
||||||
|
of 100 items.
|
||||||
|
- Search must not crash on large files. Stop at 999 results or something.
|
File diff suppressed because it is too large
Load Diff
86
package.json
86
package.json
|
@ -1,75 +1,29 @@
|
||||||
{
|
{
|
||||||
"name": "jsoneditor",
|
"name": "svelte-app",
|
||||||
"version": "8.6.6",
|
"version": "1.0.0",
|
||||||
"main": "./index",
|
|
||||||
"description": "A web-based tool to view, edit, format, and validate JSON",
|
|
||||||
"tags": [
|
|
||||||
"json",
|
|
||||||
"editor",
|
|
||||||
"viewer",
|
|
||||||
"formatter"
|
|
||||||
],
|
|
||||||
"author": "Jos de Jong <wjosdejong@gmail.com>",
|
|
||||||
"license": "Apache-2.0",
|
|
||||||
"homepage": "https://github.com/josdejong/jsoneditor",
|
|
||||||
"repository": {
|
|
||||||
"type": "git",
|
|
||||||
"url": "https://github.com/josdejong/jsoneditor.git"
|
|
||||||
},
|
|
||||||
"bugs": "https://github.com/josdejong/jsoneditor/issues",
|
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"build": "gulp",
|
"build": "rollup -c",
|
||||||
"minify": "gulp minify",
|
"dev": "rollup -c -w",
|
||||||
"start": "gulp watch",
|
"start": "sirv public",
|
||||||
"test": "mocha test --require @babel/register",
|
"test": "mocha ./src/**/*.test.js"
|
||||||
"lint": "standard --env=mocha",
|
|
||||||
"prepublishOnly": "npm test && npm run build"
|
|
||||||
},
|
},
|
||||||
|
"type": "module",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"ace-builds": "^1.4.11",
|
"@fortawesome/free-regular-svg-icons": "5.13.0",
|
||||||
"ajv": "^6.12.2",
|
"@fortawesome/free-solid-svg-icons": "5.13.0",
|
||||||
"javascript-natural-sort": "^0.7.1",
|
"svelte-awesome": "2.3.0"
|
||||||
"jmespath": "^0.15.0",
|
|
||||||
"json-source-map": "^0.6.1",
|
|
||||||
"mobius1-selectr": "^2.4.13",
|
|
||||||
"picomodal": "^3.0.0",
|
|
||||||
"vanilla-picker": "^2.10.1"
|
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@babel/core": "7.9.0",
|
"@rollup/plugin-commonjs": "11.1.0",
|
||||||
"@babel/preset-env": "7.9.5",
|
"@rollup/plugin-node-resolve": "7.1.3",
|
||||||
"@babel/register": "7.9.0",
|
|
||||||
"babel-loader": "8.1.0",
|
|
||||||
"btoa": "1.2.1",
|
|
||||||
"date-format": "3.0.0",
|
|
||||||
"fancy-log": "1.3.3",
|
|
||||||
"gulp": "4.0.2",
|
|
||||||
"gulp-clean-css": "4.3.0",
|
|
||||||
"gulp-concat-css": "3.1.0",
|
|
||||||
"gulp-sass": "4.0.2",
|
|
||||||
"jsdom": "16.2.2",
|
|
||||||
"json-loader": "0.5.7",
|
|
||||||
"mkdirp": "1.0.4",
|
|
||||||
"mocha": "7.1.1",
|
"mocha": "7.1.1",
|
||||||
"standard": "14.3.3",
|
"rollup": "2.7.2",
|
||||||
"uglify-js": "3.9.1",
|
"rollup-plugin-livereload": "1.2.0",
|
||||||
"webpack": "4.43.0"
|
"rollup-plugin-svelte": "5.2.1",
|
||||||
},
|
"rollup-plugin-terser": "5.3.0",
|
||||||
"files": [
|
"sass": "1.26.5",
|
||||||
"dist",
|
"sirv-cli": "0.4.5",
|
||||||
"docs",
|
"svelte": "3.21.0",
|
||||||
"examples",
|
"svelte-preprocess": "3.7.4"
|
||||||
"src",
|
|
||||||
"HISTORY.md",
|
|
||||||
"index.js",
|
|
||||||
"LICENSE",
|
|
||||||
"NOTICE",
|
|
||||||
"README.md"
|
|
||||||
],
|
|
||||||
"standard": {
|
|
||||||
"ignore": [
|
|
||||||
"src/js/assets",
|
|
||||||
"examples/react*"
|
|
||||||
]
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Binary file not shown.
After Width: | Height: | Size: 3.1 KiB |
|
@ -0,0 +1,16 @@
|
||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="en">
|
||||||
|
<head>
|
||||||
|
<meta charset='utf-8'>
|
||||||
|
<meta name='viewport' content='width=device-width,initial-scale=1'>
|
||||||
|
|
||||||
|
<title>JSON Editor (Svelte)</title>
|
||||||
|
|
||||||
|
<link rel='icon' type='image/png' href='/favicon.png'>
|
||||||
|
|
||||||
|
<script defer src='/build/bundle.js'></script>
|
||||||
|
</head>
|
||||||
|
|
||||||
|
<body>
|
||||||
|
</body>
|
||||||
|
</html>
|
|
@ -0,0 +1,75 @@
|
||||||
|
import svelte from 'rollup-plugin-svelte';
|
||||||
|
import resolve from '@rollup/plugin-node-resolve';
|
||||||
|
import commonjs from '@rollup/plugin-commonjs';
|
||||||
|
import livereload from 'rollup-plugin-livereload';
|
||||||
|
import { terser } from 'rollup-plugin-terser';
|
||||||
|
import autoPreprocess from 'svelte-preprocess';
|
||||||
|
|
||||||
|
const production = !process.env.ROLLUP_WATCH;
|
||||||
|
|
||||||
|
export default {
|
||||||
|
input: 'src/main.js',
|
||||||
|
output: {
|
||||||
|
sourcemap: true,
|
||||||
|
format: 'iife',
|
||||||
|
name: 'app',
|
||||||
|
file: 'public/build/bundle.js'
|
||||||
|
},
|
||||||
|
plugins: [
|
||||||
|
svelte({
|
||||||
|
// enable run-time checks when not in production
|
||||||
|
dev: !production,
|
||||||
|
|
||||||
|
// // we'll extract any component CSS out into
|
||||||
|
// // a separate file - better for performance
|
||||||
|
// css: css => {
|
||||||
|
// css.write('public/build/bundle.css');
|
||||||
|
// },
|
||||||
|
|
||||||
|
preprocess: autoPreprocess()
|
||||||
|
}),
|
||||||
|
|
||||||
|
// If you have external dependencies installed from
|
||||||
|
// npm, you'll most likely need these plugins. In
|
||||||
|
// some cases you'll need additional configuration -
|
||||||
|
// consult the documentation for details:
|
||||||
|
// https://github.com/rollup/plugins/tree/master/packages/commonjs
|
||||||
|
resolve({
|
||||||
|
browser: true,
|
||||||
|
dedupe: ['svelte']
|
||||||
|
}),
|
||||||
|
commonjs(),
|
||||||
|
|
||||||
|
// In dev mode, call `npm run start` once
|
||||||
|
// the bundle has been generated
|
||||||
|
!production && serve(),
|
||||||
|
|
||||||
|
// Watch the `public` directory and refresh the
|
||||||
|
// browser on changes when not in production
|
||||||
|
!production && livereload('public'),
|
||||||
|
|
||||||
|
// If we're building for production (npm run build
|
||||||
|
// instead of npm run dev), minify
|
||||||
|
production && terser()
|
||||||
|
],
|
||||||
|
watch: {
|
||||||
|
clearScreen: false
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
function serve() {
|
||||||
|
let started = false;
|
||||||
|
|
||||||
|
return {
|
||||||
|
writeBundle() {
|
||||||
|
if (!started) {
|
||||||
|
started = true;
|
||||||
|
|
||||||
|
require('child_process').spawn('npm', ['run', 'start', '--', '--dev'], {
|
||||||
|
stdio: ['ignore', 'inherit', 'inherit'],
|
||||||
|
shell: true
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
|
@ -0,0 +1,196 @@
|
||||||
|
<script>
|
||||||
|
import Icon from 'svelte-awesome'
|
||||||
|
import { faSearch } from '@fortawesome/free-solid-svg-icons'
|
||||||
|
import Node from './JSONNode.svelte'
|
||||||
|
import { search } from './utils'
|
||||||
|
import { beforeUpdate, afterUpdate } from 'svelte'
|
||||||
|
|
||||||
|
export let searchText = ''
|
||||||
|
|
||||||
|
let json = {
|
||||||
|
'array': [1, 2, 3, {
|
||||||
|
name: 'Item ' + 2,
|
||||||
|
id: String(2),
|
||||||
|
index: 2,
|
||||||
|
time: new Date().toISOString(),
|
||||||
|
location: {
|
||||||
|
latitude: 1.23,
|
||||||
|
longitude: 23.44,
|
||||||
|
coordinates: [23.44, 1.23]
|
||||||
|
},
|
||||||
|
}],
|
||||||
|
'boolean': true,
|
||||||
|
'color': '#82b92c',
|
||||||
|
'null': null,
|
||||||
|
'number': 123,
|
||||||
|
'object': {'a': 'b', 'c': 'd', nested: {
|
||||||
|
name: 'Item ' + 2,
|
||||||
|
id: String(2),
|
||||||
|
index: 2,
|
||||||
|
time: new Date().toISOString(),
|
||||||
|
location: {
|
||||||
|
latitude: 1.23,
|
||||||
|
longitude: 23.44,
|
||||||
|
coordinates: [23.44, 1.23]
|
||||||
|
},
|
||||||
|
}},
|
||||||
|
'string': 'Hello World'
|
||||||
|
}
|
||||||
|
|
||||||
|
let uniDirectionalValue = 'test uni directional flow in Svelte';
|
||||||
|
|
||||||
|
console.time('load editor')
|
||||||
|
|
||||||
|
beforeUpdate(() => console.time('render app'))
|
||||||
|
afterUpdate(() => console.timeEnd('render app'))
|
||||||
|
|
||||||
|
// console.log('search results "d"', search(null, json, 'd'))
|
||||||
|
// console.log('search results "e"', search(null, json, 'e'))
|
||||||
|
// console.log('search results "2"', search(null, json, '2'))
|
||||||
|
|
||||||
|
function doSearch (json, searchText) {
|
||||||
|
console.time('search')
|
||||||
|
const result = search(null, json, searchText)
|
||||||
|
console.timeEnd('search')
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
|
||||||
|
$: searchResult = searchText ? doSearch(json, searchText) : undefined
|
||||||
|
|
||||||
|
$: formattedName = `"${name}"`
|
||||||
|
|
||||||
|
$: console.log('json.number', json.number)
|
||||||
|
|
||||||
|
function loadLargeJson () {
|
||||||
|
const count = 500
|
||||||
|
|
||||||
|
console.log('create large json', { count })
|
||||||
|
console.time('create large json')
|
||||||
|
const largeJson = {
|
||||||
|
}
|
||||||
|
largeJson.numbers = []
|
||||||
|
largeJson.array = []
|
||||||
|
for (let i = 0; i < count; i++) {
|
||||||
|
const longitude = 4 + i / count;
|
||||||
|
const latitude = 51 + i / count;
|
||||||
|
|
||||||
|
largeJson.numbers.push(i);
|
||||||
|
largeJson.array.push({
|
||||||
|
name: 'Item ' + i,
|
||||||
|
id: String(i),
|
||||||
|
index: i,
|
||||||
|
time: new Date().toISOString(),
|
||||||
|
location: {
|
||||||
|
latitude,
|
||||||
|
longitude,
|
||||||
|
coordinates: [longitude, latitude]
|
||||||
|
},
|
||||||
|
random: Math.random()
|
||||||
|
})
|
||||||
|
}
|
||||||
|
console.timeEnd('create large json')
|
||||||
|
|
||||||
|
// const stringifiedSize = JSON.stringify(largeJson).length
|
||||||
|
// console.log(`large json stringified size: ${filesize(stringifiedSize)}`)
|
||||||
|
|
||||||
|
return largeJson
|
||||||
|
}
|
||||||
|
|
||||||
|
// json = loadLargeJson()
|
||||||
|
|
||||||
|
function handleChange (value, key) {
|
||||||
|
console.log('App handleChange')
|
||||||
|
json = value
|
||||||
|
}
|
||||||
|
|
||||||
|
function handleInputTextArea (event) {
|
||||||
|
console.log('on:input')
|
||||||
|
try {
|
||||||
|
json = JSON.parse(event.target.value)
|
||||||
|
} catch (err) {
|
||||||
|
console.error(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function handleLoadLargeJson () {
|
||||||
|
json = loadLargeJson()
|
||||||
|
}
|
||||||
|
|
||||||
|
function handleClearJson () {
|
||||||
|
json = {}
|
||||||
|
}
|
||||||
|
|
||||||
|
setTimeout(() => {
|
||||||
|
console.timeEnd('load editor')
|
||||||
|
console.log('loaded' )
|
||||||
|
})
|
||||||
|
|
||||||
|
function handleInputUniDirectional (event) {
|
||||||
|
console.log('change', event.target.value)
|
||||||
|
uniDirectionalValue = event.target.value
|
||||||
|
}
|
||||||
|
|
||||||
|
let inputTestValue = 'input test'
|
||||||
|
function onChangeInputTest (value) {
|
||||||
|
inputTestValue = value
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style>
|
||||||
|
html, body {
|
||||||
|
position: relative;
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
margin: 0;
|
||||||
|
padding: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
body {
|
||||||
|
color: #333;
|
||||||
|
margin: 0;
|
||||||
|
padding: 8px;
|
||||||
|
box-sizing: border-box;
|
||||||
|
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Oxygen-Sans, Ubuntu, Cantarell, "Helvetica Neue", sans-serif;
|
||||||
|
}
|
||||||
|
|
||||||
|
h1 {
|
||||||
|
color: purple;
|
||||||
|
}
|
||||||
|
|
||||||
|
.editor {
|
||||||
|
width: 500px;
|
||||||
|
height: 500px;
|
||||||
|
overflow: auto;
|
||||||
|
border: 1px solid lightgray;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
|
||||||
|
<p>
|
||||||
|
<Icon data={faSearch} /> Search: <input bind:value={searchText} />
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<div class="editor">
|
||||||
|
<Node
|
||||||
|
value={json}
|
||||||
|
searchResult={searchResult}
|
||||||
|
expanded={true}
|
||||||
|
onChange={handleChange}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- <textarea
|
||||||
|
class='code'
|
||||||
|
value={JSON.stringify(json, null, 2)}
|
||||||
|
on:input={handleInputTextArea}
|
||||||
|
/> -->
|
||||||
|
|
||||||
|
<p>
|
||||||
|
<button on:click={handleLoadLargeJson}>load large json</button>
|
||||||
|
<button on:click={handleClearJson}>clear json</button>
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<!-- <pre>
|
||||||
|
<code>
|
||||||
|
{JSON.stringify(json, null, 2)}
|
||||||
|
</code>
|
||||||
|
</pre> -->
|
|
@ -0,0 +1,136 @@
|
||||||
|
<script>
|
||||||
|
import { getType, SEARCH_PROPERTY, SEARCH_VALUE } from './utils'
|
||||||
|
|
||||||
|
export let key = 'root'
|
||||||
|
export let value
|
||||||
|
export let searchResult
|
||||||
|
export let onChange
|
||||||
|
export let expanded = true
|
||||||
|
export let indentation = 0
|
||||||
|
|
||||||
|
const DEFAULT_LIMIT = 10000
|
||||||
|
|
||||||
|
let limit = DEFAULT_LIMIT
|
||||||
|
|
||||||
|
$: type = getType(value)
|
||||||
|
|
||||||
|
$: props = type === 'object'
|
||||||
|
? Object.keys(value).map(key => {
|
||||||
|
return { key, value: value[key] }
|
||||||
|
})
|
||||||
|
: undefined
|
||||||
|
|
||||||
|
$: limited = type === 'array' && value.length > limit
|
||||||
|
|
||||||
|
$: items = type === 'array'
|
||||||
|
? limited ? value.slice(0, limit) : value
|
||||||
|
: undefined
|
||||||
|
|
||||||
|
function toggle () {
|
||||||
|
expanded = !expanded
|
||||||
|
}
|
||||||
|
|
||||||
|
function handleInput (event) {
|
||||||
|
const value = event.target.innerText
|
||||||
|
onChange(value, key)
|
||||||
|
}
|
||||||
|
|
||||||
|
function handleChange (childValue, childKey) {
|
||||||
|
// FIXME: use an immutability setIn function here
|
||||||
|
if (type === 'array') {
|
||||||
|
const updatedValue = value.slice(0) // copy the array
|
||||||
|
updatedValue[childKey] = childValue
|
||||||
|
onChange(updatedValue, key)
|
||||||
|
} else if (type === 'object') {
|
||||||
|
const updatedValue = {
|
||||||
|
...value,
|
||||||
|
[childKey]: childValue
|
||||||
|
}
|
||||||
|
onChange(updatedValue, key)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function handleShowAll () {
|
||||||
|
limit = Infinity
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style type="text/scss">
|
||||||
|
$search-background-color: gold;
|
||||||
|
|
||||||
|
.key {
|
||||||
|
color: gray;
|
||||||
|
}
|
||||||
|
.key.search {
|
||||||
|
background-color: $search-background-color;
|
||||||
|
}
|
||||||
|
.items,
|
||||||
|
.props {
|
||||||
|
padding-left: 24px;
|
||||||
|
}
|
||||||
|
.value {
|
||||||
|
display: inline-block;
|
||||||
|
padding: 3px;
|
||||||
|
}
|
||||||
|
.key.search,
|
||||||
|
.value.search {
|
||||||
|
background-color: $search-background-color;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
|
||||||
|
<div class='json-node'>
|
||||||
|
<span class="key {searchResult && searchResult[SEARCH_PROPERTY] ? 'search' : ''}">
|
||||||
|
{key} :
|
||||||
|
</span>
|
||||||
|
{#if type !== 'value'}
|
||||||
|
<button on:click={toggle}>
|
||||||
|
{#if expanded}
|
||||||
|
collapse
|
||||||
|
{:else}
|
||||||
|
expand
|
||||||
|
{/if}
|
||||||
|
</button>
|
||||||
|
{/if}
|
||||||
|
{#if type === 'array'}
|
||||||
|
{#if expanded}
|
||||||
|
<div class="items">
|
||||||
|
{#each items as item, index (index)}
|
||||||
|
<svelte:self
|
||||||
|
key={index}
|
||||||
|
value={item}
|
||||||
|
searchResult={searchResult ? searchResult[index] : undefined}
|
||||||
|
indentation={indentation + 1}
|
||||||
|
onChange={handleChange}
|
||||||
|
/>
|
||||||
|
{/each}
|
||||||
|
{#if limited}
|
||||||
|
<div>
|
||||||
|
(showing {limit} of {value.length} items <button on:click={handleShowAll}>show all</button>)
|
||||||
|
</div>
|
||||||
|
{/if}
|
||||||
|
</div>
|
||||||
|
{/if}
|
||||||
|
{:else if type === 'object'}
|
||||||
|
{#if expanded}
|
||||||
|
<div class="props">
|
||||||
|
{#each props as prop (prop.key)}
|
||||||
|
<svelte:self
|
||||||
|
key={prop.key}
|
||||||
|
value={prop.value}
|
||||||
|
searchResult={searchResult ? searchResult[prop.key] : undefined}
|
||||||
|
indentation={indentation + 1}
|
||||||
|
onChange={handleChange}
|
||||||
|
/>
|
||||||
|
{/each}
|
||||||
|
</div>
|
||||||
|
{/if}
|
||||||
|
{:else}
|
||||||
|
<div
|
||||||
|
class="value {searchResult && searchResult[SEARCH_VALUE] ? 'search' : ''}"
|
||||||
|
contenteditable="true"
|
||||||
|
on:input={handleInput}
|
||||||
|
>
|
||||||
|
{value}
|
||||||
|
</div>
|
||||||
|
{/if}
|
||||||
|
</div>
|
|
@ -1,44 +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.
|
|
||||||
- `vanilla-picker`, used as color picker.
|
|
||||||
|
|
||||||
This reduces the the size of the minified and gzipped JavaScript file
|
|
||||||
from about 210 kB to about 70 kB (one third).
|
|
||||||
|
|
||||||
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.
|
|
||||||
- You don't need the color picker, or want to provide your own
|
|
||||||
color picker using `onColorPicker`.
|
|
||||||
|
|
||||||
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
|
|
||||||
|
|
|
@ -1,429 +0,0 @@
|
||||||
'use strict'
|
|
||||||
|
|
||||||
import { createAbsoluteAnchor } from './createAbsoluteAnchor'
|
|
||||||
import { addClassName, getSelection, removeClassName, setSelection } from './util'
|
|
||||||
import { translate } from './i18n'
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 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
|
|
||||||
*/
|
|
||||||
export class ContextMenu {
|
|
||||||
constructor (items, options) {
|
|
||||||
this.dom = {}
|
|
||||||
|
|
||||||
const me = this
|
|
||||||
const 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
|
|
||||||
const root = document.createElement('div')
|
|
||||||
root.className = 'jsoneditor-contextmenu-root'
|
|
||||||
dom.root = root
|
|
||||||
|
|
||||||
// create a container element
|
|
||||||
const menu = document.createElement('div')
|
|
||||||
menu.className = 'jsoneditor-contextmenu'
|
|
||||||
dom.menu = menu
|
|
||||||
root.appendChild(menu)
|
|
||||||
|
|
||||||
// create a list to hold the menu items
|
|
||||||
const 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
|
|
||||||
const focusButton = document.createElement('button')
|
|
||||||
focusButton.type = 'button'
|
|
||||||
dom.focusButton = focusButton
|
|
||||||
const 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(item => {
|
|
||||||
if (item.type === 'separator') {
|
|
||||||
// create a separator
|
|
||||||
const separator = document.createElement('div')
|
|
||||||
separator.className = 'jsoneditor-separator'
|
|
||||||
const li = document.createElement('li')
|
|
||||||
li.appendChild(separator)
|
|
||||||
list.appendChild(li)
|
|
||||||
} else {
|
|
||||||
const domItem = {}
|
|
||||||
|
|
||||||
// create a menu item
|
|
||||||
const li = document.createElement('li')
|
|
||||||
list.appendChild(li)
|
|
||||||
|
|
||||||
// create a button in the menu item
|
|
||||||
const button = document.createElement('button')
|
|
||||||
button.type = 'button'
|
|
||||||
button.className = item.className
|
|
||||||
domItem.button = button
|
|
||||||
if (item.title) {
|
|
||||||
button.title = item.title
|
|
||||||
}
|
|
||||||
if (item.click) {
|
|
||||||
button.onclick = 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
|
|
||||||
const divIcon = document.createElement('div')
|
|
||||||
divIcon.className = 'jsoneditor-icon'
|
|
||||||
button.appendChild(divIcon)
|
|
||||||
const divText = document.createElement('div')
|
|
||||||
divText.className = 'jsoneditor-text' +
|
|
||||||
(item.click ? '' : ' jsoneditor-right-margin')
|
|
||||||
divText.appendChild(document.createTextNode(item.text))
|
|
||||||
button.appendChild(divText)
|
|
||||||
|
|
||||||
let buttonSubmenu
|
|
||||||
if (item.click) {
|
|
||||||
// submenu and a button with a click handler
|
|
||||||
button.className += ' jsoneditor-default'
|
|
||||||
|
|
||||||
const buttonExpand = document.createElement('button')
|
|
||||||
buttonExpand.type = '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
|
|
||||||
const divExpand = document.createElement('div')
|
|
||||||
divExpand.className = 'jsoneditor-expand'
|
|
||||||
button.appendChild(divExpand)
|
|
||||||
|
|
||||||
buttonSubmenu = button
|
|
||||||
}
|
|
||||||
|
|
||||||
// attach a handler to expand/collapse the submenu
|
|
||||||
buttonSubmenu.onclick = event => {
|
|
||||||
event.preventDefault()
|
|
||||||
me._onExpandItem(domItem)
|
|
||||||
buttonSubmenu.focus()
|
|
||||||
}
|
|
||||||
|
|
||||||
// create the submenu
|
|
||||||
const domSubItems = []
|
|
||||||
domItem.subItems = domSubItems
|
|
||||||
const 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>' +
|
|
||||||
'<div class="jsoneditor-text">' + translate(item.text) + '</div>'
|
|
||||||
}
|
|
||||||
|
|
||||||
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(item => {
|
|
||||||
const 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
|
|
||||||
*/
|
|
||||||
_getVisibleButtons () {
|
|
||||||
const buttons = []
|
|
||||||
const me = this
|
|
||||||
this.dom.items.forEach(item => {
|
|
||||||
buttons.push(item.button)
|
|
||||||
if (item.buttonExpand) {
|
|
||||||
buttons.push(item.buttonExpand)
|
|
||||||
}
|
|
||||||
if (item.subItems && item === me.expandedItem) {
|
|
||||||
item.subItems.forEach(subItem => {
|
|
||||||
buttons.push(subItem.button)
|
|
||||||
if (subItem.buttonExpand) {
|
|
||||||
buttons.push(subItem.buttonExpand)
|
|
||||||
}
|
|
||||||
// TODO: change to fully recursive method
|
|
||||||
})
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
return buttons
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Attach the menu to an anchor
|
|
||||||
* @param {HTMLElement} anchor Anchor where the menu will be attached as sibling.
|
|
||||||
* @param {HTMLElement} frame The root of the JSONEditor window
|
|
||||||
* @param {Boolean=} ignoreParent ignore anchor parent in regard to the calculation of the position, needed when the parent position is absolute
|
|
||||||
*/
|
|
||||||
show (anchor, frame, ignoreParent) {
|
|
||||||
this.hide()
|
|
||||||
|
|
||||||
// determine whether to display the menu below or above the anchor
|
|
||||||
let showBelow = true
|
|
||||||
const parent = anchor.parentNode
|
|
||||||
const anchorRect = anchor.getBoundingClientRect()
|
|
||||||
const parentRect = parent.getBoundingClientRect()
|
|
||||||
const frameRect = frame.getBoundingClientRect()
|
|
||||||
|
|
||||||
const me = this
|
|
||||||
this.dom.absoluteAnchor = createAbsoluteAnchor(anchor, frame, () => {
|
|
||||||
me.hide()
|
|
||||||
})
|
|
||||||
|
|
||||||
if (anchorRect.bottom + this.maxHeight < frameRect.bottom) {
|
|
||||||
// fits below -> show below
|
|
||||||
} else if (anchorRect.top - this.maxHeight > frameRect.top) {
|
|
||||||
// fits above -> show above
|
|
||||||
showBelow = false
|
|
||||||
} else {
|
|
||||||
// doesn't fit above nor below -> show below
|
|
||||||
}
|
|
||||||
|
|
||||||
const topGap = ignoreParent ? 0 : (anchorRect.top - parentRect.top)
|
|
||||||
|
|
||||||
// position the menu
|
|
||||||
if (showBelow) {
|
|
||||||
// display the menu below the anchor
|
|
||||||
const anchorHeight = anchor.offsetHeight
|
|
||||||
this.dom.menu.style.left = '0'
|
|
||||||
this.dom.menu.style.top = topGap + anchorHeight + 'px'
|
|
||||||
this.dom.menu.style.bottom = ''
|
|
||||||
} else {
|
|
||||||
// display the menu above the anchor
|
|
||||||
this.dom.menu.style.left = '0'
|
|
||||||
this.dom.menu.style.top = ''
|
|
||||||
this.dom.menu.style.bottom = '0px'
|
|
||||||
}
|
|
||||||
|
|
||||||
// attach the menu to the temporary, absolute anchor
|
|
||||||
// parent.insertBefore(this.dom.root, anchor);
|
|
||||||
this.dom.absoluteAnchor.appendChild(this.dom.root)
|
|
||||||
|
|
||||||
// move focus to the first button in the context menu
|
|
||||||
this.selection = getSelection()
|
|
||||||
this.anchor = anchor
|
|
||||||
setTimeout(() => {
|
|
||||||
me.dom.focusButton.focus()
|
|
||||||
}, 0)
|
|
||||||
|
|
||||||
if (ContextMenu.visibleMenu) {
|
|
||||||
ContextMenu.visibleMenu.hide()
|
|
||||||
}
|
|
||||||
ContextMenu.visibleMenu = this
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Hide the context menu if visible
|
|
||||||
*/
|
|
||||||
hide () {
|
|
||||||
// remove temporary absolutely positioned anchor
|
|
||||||
if (this.dom.absoluteAnchor) {
|
|
||||||
this.dom.absoluteAnchor.destroy()
|
|
||||||
delete this.dom.absoluteAnchor
|
|
||||||
}
|
|
||||||
|
|
||||||
// remove the menu from the DOM
|
|
||||||
if (this.dom.root.parentNode) {
|
|
||||||
this.dom.root.parentNode.removeChild(this.dom.root)
|
|
||||||
if (this.onClose) {
|
|
||||||
this.onClose()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (ContextMenu.visibleMenu === this) {
|
|
||||||
ContextMenu.visibleMenu = undefined
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Expand a submenu
|
|
||||||
* Any currently expanded submenu will be hided.
|
|
||||||
* @param {Object} domItem
|
|
||||||
* @private
|
|
||||||
*/
|
|
||||||
_onExpandItem (domItem) {
|
|
||||||
const me = this
|
|
||||||
const alreadyVisible = (domItem === this.expandedItem)
|
|
||||||
|
|
||||||
// hide the currently visible submenu
|
|
||||||
const expandedItem = this.expandedItem
|
|
||||||
if (expandedItem) {
|
|
||||||
// var ul = expandedItem.ul;
|
|
||||||
expandedItem.ul.style.height = '0'
|
|
||||||
expandedItem.ul.style.padding = ''
|
|
||||||
setTimeout(() => {
|
|
||||||
if (me.expandedItem !== expandedItem) {
|
|
||||||
expandedItem.ul.style.display = ''
|
|
||||||
removeClassName(expandedItem.ul.parentNode, 'jsoneditor-selected')
|
|
||||||
}
|
|
||||||
}, 300) // timeout duration must match the css transition duration
|
|
||||||
this.expandedItem = undefined
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!alreadyVisible) {
|
|
||||||
const ul = domItem.ul
|
|
||||||
ul.style.display = 'block'
|
|
||||||
// eslint-disable-next-line no-unused-expressions
|
|
||||||
ul.clientHeight // force a reflow in Firefox
|
|
||||||
setTimeout(() => {
|
|
||||||
if (me.expandedItem === domItem) {
|
|
||||||
let childsHeight = 0
|
|
||||||
for (let i = 0; i < ul.childNodes.length; i++) {
|
|
||||||
childsHeight += ul.childNodes[i].clientHeight
|
|
||||||
}
|
|
||||||
ul.style.height = childsHeight + 'px'
|
|
||||||
ul.style.padding = '5px 10px'
|
|
||||||
}
|
|
||||||
}, 0)
|
|
||||||
addClassName(ul.parentNode, 'jsoneditor-selected')
|
|
||||||
this.expandedItem = domItem
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Handle onkeydown event
|
|
||||||
* @param {Event} event
|
|
||||||
* @private
|
|
||||||
*/
|
|
||||||
_onKeyDown (event) {
|
|
||||||
const target = event.target
|
|
||||||
const keynum = event.which
|
|
||||||
let handled = false
|
|
||||||
let buttons, targetIndex, prevButton, nextButton
|
|
||||||
|
|
||||||
if (keynum === 27) { // ESC
|
|
||||||
// hide the menu on ESC key
|
|
||||||
|
|
||||||
// restore previous selection and focus
|
|
||||||
if (this.selection) {
|
|
||||||
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()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// currently displayed context menu, a singleton. We may only have one visible context menu
|
|
||||||
ContextMenu.visibleMenu = undefined
|
|
||||||
|
|
||||||
export default ContextMenu
|
|
|
@ -1,169 +0,0 @@
|
||||||
/**
|
|
||||||
* Show errors and schema warnings in a clickable table view
|
|
||||||
* @param {Object} config
|
|
||||||
* @property {boolean} errorTableVisible
|
|
||||||
* @property {function (boolean) : void} onToggleVisibility
|
|
||||||
* @property {function (number)} [onFocusLine]
|
|
||||||
* @property {function (number)} onChangeHeight
|
|
||||||
* @constructor
|
|
||||||
*/
|
|
||||||
export class ErrorTable {
|
|
||||||
constructor (config) {
|
|
||||||
this.errorTableVisible = config.errorTableVisible
|
|
||||||
this.onToggleVisibility = config.onToggleVisibility
|
|
||||||
this.onFocusLine = config.onFocusLine || (() => {})
|
|
||||||
this.onChangeHeight = config.onChangeHeight
|
|
||||||
|
|
||||||
this.dom = {}
|
|
||||||
|
|
||||||
const validationErrorsContainer = document.createElement('div')
|
|
||||||
validationErrorsContainer.className = 'jsoneditor-validation-errors-container'
|
|
||||||
this.dom.validationErrorsContainer = validationErrorsContainer
|
|
||||||
|
|
||||||
const additionalErrorsIndication = document.createElement('div')
|
|
||||||
additionalErrorsIndication.style.display = 'none'
|
|
||||||
additionalErrorsIndication.className = 'jsoneditor-additional-errors fadein'
|
|
||||||
additionalErrorsIndication.innerHTML = 'Scroll for more ▿'
|
|
||||||
this.dom.additionalErrorsIndication = additionalErrorsIndication
|
|
||||||
validationErrorsContainer.appendChild(additionalErrorsIndication)
|
|
||||||
|
|
||||||
const validationErrorIcon = document.createElement('span')
|
|
||||||
validationErrorIcon.className = 'jsoneditor-validation-error-icon'
|
|
||||||
validationErrorIcon.style.display = 'none'
|
|
||||||
this.dom.validationErrorIcon = validationErrorIcon
|
|
||||||
|
|
||||||
const validationErrorCount = document.createElement('span')
|
|
||||||
validationErrorCount.className = 'jsoneditor-validation-error-count'
|
|
||||||
validationErrorCount.style.display = 'none'
|
|
||||||
this.dom.validationErrorCount = validationErrorCount
|
|
||||||
|
|
||||||
this.dom.parseErrorIndication = document.createElement('span')
|
|
||||||
this.dom.parseErrorIndication.className = 'jsoneditor-parse-error-icon'
|
|
||||||
this.dom.parseErrorIndication.style.display = 'none'
|
|
||||||
}
|
|
||||||
|
|
||||||
getErrorTable () {
|
|
||||||
return this.dom.validationErrorsContainer
|
|
||||||
}
|
|
||||||
|
|
||||||
getErrorCounter () {
|
|
||||||
return this.dom.validationErrorCount
|
|
||||||
}
|
|
||||||
|
|
||||||
getWarningIcon () {
|
|
||||||
return this.dom.validationErrorIcon
|
|
||||||
}
|
|
||||||
|
|
||||||
getErrorIcon () {
|
|
||||||
return this.dom.parseErrorIndication
|
|
||||||
}
|
|
||||||
|
|
||||||
toggleTableVisibility () {
|
|
||||||
this.errorTableVisible = !this.errorTableVisible
|
|
||||||
this.onToggleVisibility(this.errorTableVisible)
|
|
||||||
}
|
|
||||||
|
|
||||||
setErrors (errors, errorLocations) {
|
|
||||||
// clear any previous errors
|
|
||||||
if (this.dom.validationErrors) {
|
|
||||||
this.dom.validationErrors.parentNode.removeChild(this.dom.validationErrors)
|
|
||||||
this.dom.validationErrors = null
|
|
||||||
this.dom.additionalErrorsIndication.style.display = 'none'
|
|
||||||
}
|
|
||||||
|
|
||||||
// create the table with errors
|
|
||||||
// keep default behavior for parse errors
|
|
||||||
if (this.errorTableVisible && errors.length > 0) {
|
|
||||||
const validationErrors = document.createElement('div')
|
|
||||||
validationErrors.className = 'jsoneditor-validation-errors'
|
|
||||||
validationErrors.innerHTML = '<table class="jsoneditor-text-errors"><tbody></tbody></table>'
|
|
||||||
const tbody = validationErrors.getElementsByTagName('tbody')[0]
|
|
||||||
|
|
||||||
errors.forEach(error => {
|
|
||||||
let message
|
|
||||||
if (typeof error === 'string') {
|
|
||||||
message = '<td colspan="2"><pre>' + error + '</pre></td>'
|
|
||||||
} else {
|
|
||||||
message =
|
|
||||||
'<td>' + (error.dataPath || '') + '</td>' +
|
|
||||||
'<td><pre>' + error.message + '</pre></td>'
|
|
||||||
}
|
|
||||||
|
|
||||||
let line
|
|
||||||
|
|
||||||
if (!isNaN(error.line)) {
|
|
||||||
line = error.line
|
|
||||||
} else if (error.dataPath) {
|
|
||||||
const errLoc = errorLocations.find(loc => loc.path === error.dataPath)
|
|
||||||
if (errLoc) {
|
|
||||||
line = errLoc.line + 1
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const trEl = document.createElement('tr')
|
|
||||||
trEl.className = !isNaN(line) ? 'jump-to-line' : ''
|
|
||||||
if (error.type === 'error') {
|
|
||||||
trEl.className += ' parse-error'
|
|
||||||
} else {
|
|
||||||
trEl.className += ' validation-error'
|
|
||||||
}
|
|
||||||
|
|
||||||
trEl.innerHTML = ('<td><button class="jsoneditor-schema-error"></button></td><td style="white-space:nowrap;">' + (!isNaN(line) ? ('Ln ' + line) : '') + '</td>' + message)
|
|
||||||
trEl.onclick = () => {
|
|
||||||
this.onFocusLine(line)
|
|
||||||
}
|
|
||||||
|
|
||||||
tbody.appendChild(trEl)
|
|
||||||
})
|
|
||||||
|
|
||||||
this.dom.validationErrors = validationErrors
|
|
||||||
this.dom.validationErrorsContainer.appendChild(validationErrors)
|
|
||||||
this.dom.additionalErrorsIndication.title = errors.length + ' errors total'
|
|
||||||
|
|
||||||
if (this.dom.validationErrorsContainer.clientHeight < this.dom.validationErrorsContainer.scrollHeight) {
|
|
||||||
this.dom.additionalErrorsIndication.style.display = 'block'
|
|
||||||
this.dom.validationErrorsContainer.onscroll = () => {
|
|
||||||
this.dom.additionalErrorsIndication.style.display =
|
|
||||||
(this.dom.validationErrorsContainer.clientHeight > 0 && this.dom.validationErrorsContainer.scrollTop === 0) ? 'block' : 'none'
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
this.dom.validationErrorsContainer.onscroll = undefined
|
|
||||||
}
|
|
||||||
|
|
||||||
const height = this.dom.validationErrorsContainer.clientHeight + (this.dom.statusBar ? this.dom.statusBar.clientHeight : 0)
|
|
||||||
// this.content.style.marginBottom = (-height) + 'px';
|
|
||||||
// this.content.style.paddingBottom = height + 'px';
|
|
||||||
this.onChangeHeight(height)
|
|
||||||
} else {
|
|
||||||
this.onChangeHeight(0)
|
|
||||||
}
|
|
||||||
|
|
||||||
// update the status bar
|
|
||||||
const validationErrorsCount = errors.filter(error => error.type !== 'error').length
|
|
||||||
if (validationErrorsCount > 0) {
|
|
||||||
this.dom.validationErrorCount.style.display = 'inline'
|
|
||||||
this.dom.validationErrorCount.innerText = validationErrorsCount
|
|
||||||
this.dom.validationErrorCount.onclick = this.toggleTableVisibility.bind(this)
|
|
||||||
|
|
||||||
this.dom.validationErrorIcon.style.display = 'inline'
|
|
||||||
this.dom.validationErrorIcon.title = validationErrorsCount + ' schema validation error(s) found'
|
|
||||||
this.dom.validationErrorIcon.onclick = this.toggleTableVisibility.bind(this)
|
|
||||||
} else {
|
|
||||||
this.dom.validationErrorCount.style.display = 'none'
|
|
||||||
this.dom.validationErrorIcon.style.display = 'none'
|
|
||||||
}
|
|
||||||
|
|
||||||
// update the parse error icon
|
|
||||||
const hasParseErrors = errors.some(error => error.type === 'error')
|
|
||||||
if (hasParseErrors) {
|
|
||||||
const line = errors[0].line
|
|
||||||
this.dom.parseErrorIndication.style.display = 'block'
|
|
||||||
this.dom.parseErrorIndication.title = !isNaN(line)
|
|
||||||
? ('parse error on line ' + line)
|
|
||||||
: 'parse error - check that the json is valid'
|
|
||||||
this.dom.parseErrorIndication.onclick = this.toggleTableVisibility.bind(this)
|
|
||||||
} else {
|
|
||||||
this.dom.parseErrorIndication.style.display = 'none'
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,100 +0,0 @@
|
||||||
'use strict'
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @constructor FocusTracker
|
|
||||||
* A custom focus tracker for a DOM element with complex internal DOM structure
|
|
||||||
* @param {[Object]} config A set of configurations for the FocusTracker
|
|
||||||
* {DOM Object} target * The DOM object to track (required)
|
|
||||||
* {Function} onFocus onFocus callback
|
|
||||||
* {Function} onBlur onBlur callback
|
|
||||||
*
|
|
||||||
* @return
|
|
||||||
*/
|
|
||||||
|
|
||||||
export class FocusTracker {
|
|
||||||
constructor (config) {
|
|
||||||
this.target = config.target || null
|
|
||||||
if (!this.target) {
|
|
||||||
throw new Error('FocusTracker constructor called without a "target" to track.')
|
|
||||||
}
|
|
||||||
|
|
||||||
this.onFocus = (typeof config.onFocus === 'function') ? config.onFocus : null
|
|
||||||
this.onBlur = (typeof config.onBlur === 'function') ? config.onBlur : null
|
|
||||||
this._onClick = this._onEvent.bind(this)
|
|
||||||
this._onKeyUp = function (event) {
|
|
||||||
if (event.which === 9 || event.keyCode === 9) {
|
|
||||||
this._onEvent(event)
|
|
||||||
}
|
|
||||||
}.bind(this)
|
|
||||||
|
|
||||||
this.focusFlag = false
|
|
||||||
this.firstEventFlag = true
|
|
||||||
|
|
||||||
/*
|
|
||||||
Adds required (click and keyup) event listeners to the 'document' object
|
|
||||||
to track the focus of the given 'target'
|
|
||||||
*/
|
|
||||||
if (this.onFocus || this.onBlur) {
|
|
||||||
document.addEventListener('click', this._onClick)
|
|
||||||
document.addEventListener('keyup', this._onKeyUp)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Removes the event listeners on the 'document' object
|
|
||||||
* that were added to track the focus of the given 'target'
|
|
||||||
*/
|
|
||||||
destroy () {
|
|
||||||
document.removeEventListener('click', this._onClick)
|
|
||||||
document.removeEventListener('keyup', this._onKeyUp)
|
|
||||||
this._onEvent({ target: document.body }) // calling _onEvent with body element in the hope that the FocusTracker is added to an element inside the body tag
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Tracks the focus of the target and calls the onFocus and onBlur
|
|
||||||
* event callbacks if available.
|
|
||||||
* @param {Event} [event] The 'click' or 'keyup' event object,
|
|
||||||
* from the respective events set on
|
|
||||||
* document object
|
|
||||||
* @private
|
|
||||||
*/
|
|
||||||
|
|
||||||
_onEvent (event) {
|
|
||||||
const target = event.target
|
|
||||||
let focusFlag
|
|
||||||
if (target === this.target) {
|
|
||||||
focusFlag = true
|
|
||||||
} else if (this.target.contains(target) || this.target.contains(document.activeElement)) {
|
|
||||||
focusFlag = true
|
|
||||||
} else {
|
|
||||||
focusFlag = false
|
|
||||||
}
|
|
||||||
|
|
||||||
if (focusFlag) {
|
|
||||||
if (!this.focusFlag) {
|
|
||||||
// trigger the onFocus callback
|
|
||||||
if (this.onFocus) {
|
|
||||||
this.onFocus({ type: 'focus', target: this.target })
|
|
||||||
}
|
|
||||||
this.focusFlag = true
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
if (this.focusFlag || this.firstEventFlag) {
|
|
||||||
// trigger the onBlur callback
|
|
||||||
if (this.onBlur) {
|
|
||||||
this.onBlur({ type: 'blur', target: this.target })
|
|
||||||
}
|
|
||||||
this.focusFlag = false
|
|
||||||
|
|
||||||
/*
|
|
||||||
When switching from one mode to another in the editor, the FocusTracker gets recreated.
|
|
||||||
At that time, this.focusFlag will be init to 'false' and will fail the above if condition, when blur occurs
|
|
||||||
this.firstEventFlag is added to overcome that issue
|
|
||||||
*/
|
|
||||||
if (this.firstEventFlag) {
|
|
||||||
this.firstEventFlag = false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,86 +0,0 @@
|
||||||
'use strict'
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The highlighter can highlight/unhighlight a node, and
|
|
||||||
* animate the visibility of a context menu.
|
|
||||||
* @constructor Highlighter
|
|
||||||
*/
|
|
||||||
export class Highlighter {
|
|
||||||
constructor () {
|
|
||||||
this.locked = false
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Hightlight given node and its childs
|
|
||||||
* @param {Node} node
|
|
||||||
*/
|
|
||||||
highlight (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
|
|
||||||
*/
|
|
||||||
unhighlight () {
|
|
||||||
if (this.locked) {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
const 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(() => {
|
|
||||||
me.node.setHighlight(false)
|
|
||||||
me.node = undefined
|
|
||||||
me.unhighlightTimer = undefined
|
|
||||||
}, 0)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Cancel an unhighlight action (if before the timeout of the unhighlight action)
|
|
||||||
* @private
|
|
||||||
*/
|
|
||||||
_cancelUnhighlight () {
|
|
||||||
if (this.unhighlightTimer) {
|
|
||||||
clearTimeout(this.unhighlightTimer)
|
|
||||||
this.unhighlightTimer = undefined
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Lock highlighting or unhighlighting nodes.
|
|
||||||
* methods highlight and unhighlight do not work while locked.
|
|
||||||
*/
|
|
||||||
lock () {
|
|
||||||
this.locked = true
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Unlock highlighting or unhighlighting nodes
|
|
||||||
*/
|
|
||||||
unlock () {
|
|
||||||
this.locked = false
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,85 +0,0 @@
|
||||||
|
|
||||||
/**
|
|
||||||
* Keep track on any history, be able
|
|
||||||
* @param {function} onChange
|
|
||||||
* @param {function} calculateItemSize
|
|
||||||
* @param {number} limit Maximum size of all items in history
|
|
||||||
* @constructor
|
|
||||||
*/
|
|
||||||
export class History {
|
|
||||||
constructor (onChange, calculateItemSize, limit) {
|
|
||||||
this.onChange = onChange
|
|
||||||
this.calculateItemSize = calculateItemSize || (() => 1)
|
|
||||||
this.limit = limit
|
|
||||||
|
|
||||||
this.items = []
|
|
||||||
this.index = -1
|
|
||||||
}
|
|
||||||
|
|
||||||
add (item) {
|
|
||||||
// limit number of items in history so that the total size doesn't
|
|
||||||
// always keep at least one item in memory
|
|
||||||
while (this._calculateHistorySize() > this.limit && this.items.length > 1) {
|
|
||||||
this.items.shift()
|
|
||||||
this.index--
|
|
||||||
}
|
|
||||||
|
|
||||||
// cleanup any redo action that are not valid anymore
|
|
||||||
this.items = this.items.slice(0, this.index + 1)
|
|
||||||
|
|
||||||
this.items.push(item)
|
|
||||||
this.index++
|
|
||||||
|
|
||||||
this.onChange()
|
|
||||||
}
|
|
||||||
|
|
||||||
_calculateHistorySize () {
|
|
||||||
const calculateItemSize = this.calculateItemSize
|
|
||||||
let totalSize = 0
|
|
||||||
|
|
||||||
this.items.forEach(item => {
|
|
||||||
totalSize += calculateItemSize(item)
|
|
||||||
})
|
|
||||||
|
|
||||||
return totalSize
|
|
||||||
}
|
|
||||||
|
|
||||||
undo () {
|
|
||||||
if (!this.canUndo()) {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
this.index--
|
|
||||||
|
|
||||||
this.onChange()
|
|
||||||
|
|
||||||
return this.items[this.index]
|
|
||||||
}
|
|
||||||
|
|
||||||
redo () {
|
|
||||||
if (!this.canRedo()) {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
this.index++
|
|
||||||
|
|
||||||
this.onChange()
|
|
||||||
|
|
||||||
return this.items[this.index]
|
|
||||||
}
|
|
||||||
|
|
||||||
canUndo () {
|
|
||||||
return this.index > 0
|
|
||||||
}
|
|
||||||
|
|
||||||
canRedo () {
|
|
||||||
return this.index < this.items.length - 1
|
|
||||||
}
|
|
||||||
|
|
||||||
clear () {
|
|
||||||
this.items = []
|
|
||||||
this.index = -1
|
|
||||||
|
|
||||||
this.onChange()
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,491 +0,0 @@
|
||||||
'use strict'
|
|
||||||
|
|
||||||
const ace = require('./ace') // may be undefined in case of minimalist bundle
|
|
||||||
const VanillaPicker = require('./vanilla-picker') // may be undefined in case of minimalist bundle
|
|
||||||
const { treeModeMixins } = require('./treemode')
|
|
||||||
const { textModeMixins } = require('./textmode')
|
|
||||||
const { previewModeMixins } = require('./previewmode')
|
|
||||||
const { clear, extend, getInternetExplorerVersion, parse } = require('./util')
|
|
||||||
const { tryRequireAjv } = require('./tryRequireAjv')
|
|
||||||
const { showTransformModal } = require('./showTransformModal')
|
|
||||||
const { showSortModal } = require('./showSortModal')
|
|
||||||
|
|
||||||
const Ajv = tryRequireAjv()
|
|
||||||
|
|
||||||
if (typeof Promise === 'undefined') {
|
|
||||||
console.error('Promise undefined. Please load a Promise polyfill in the browser in order to use JSONEditor')
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @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.
|
|
||||||
* Does not pass the contents itself.
|
|
||||||
* See also `onChangeJSON` and
|
|
||||||
* `onChangeText`.
|
|
||||||
* {function} onChangeJSON Callback method, triggered
|
|
||||||
* in modes on change of contents,
|
|
||||||
* passing the changed contents
|
|
||||||
* as JSON.
|
|
||||||
* Only applicable for modes
|
|
||||||
* 'tree', 'view', and 'form'.
|
|
||||||
* {function} onChangeText Callback method, triggered
|
|
||||||
* in modes on change of contents,
|
|
||||||
* passing the changed contents
|
|
||||||
* as stringified JSON.
|
|
||||||
* {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.
|
|
||||||
* {function} onSelectionChange Callback method,
|
|
||||||
* triggered on node selection change
|
|
||||||
* Only applicable for modes
|
|
||||||
* 'tree', 'view', and 'form'
|
|
||||||
* {function} onTextSelectionChange Callback method,
|
|
||||||
* triggered on text selection change
|
|
||||||
* Only applicable for modes
|
|
||||||
* {HTMLElement} modalAnchor The anchor element to apply an
|
|
||||||
* overlay and display the modals in a
|
|
||||||
* centered location.
|
|
||||||
* Defaults to document.body
|
|
||||||
* 'text' and 'code'
|
|
||||||
* {function} onEvent Callback method, triggered
|
|
||||||
* when an event occurs in
|
|
||||||
* a JSON field or value.
|
|
||||||
* Only applicable for
|
|
||||||
* modes 'form', 'tree' and
|
|
||||||
* 'view'
|
|
||||||
* {function} onFocus Callback method, triggered
|
|
||||||
* when the editor comes into focus,
|
|
||||||
* passing an object {type, target},
|
|
||||||
* Applicable for all modes
|
|
||||||
* {function} onBlur Callback method, triggered
|
|
||||||
* when the editor goes out of focus,
|
|
||||||
* passing an object {type, target},
|
|
||||||
* Applicable for all modes
|
|
||||||
* {function} onClassName Callback method, triggered
|
|
||||||
* when a Node DOM is rendered. Function returns
|
|
||||||
* a css class name to be set on a node.
|
|
||||||
* Only applicable for
|
|
||||||
* modes 'form', 'tree' and
|
|
||||||
* 'view'
|
|
||||||
* {Number} maxVisibleChilds Number of children allowed for a node
|
|
||||||
* in 'tree', 'view', or 'form' mode before
|
|
||||||
* the "show more/show all" buttons appear.
|
|
||||||
* 100 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)
|
|
||||||
const ieVersion = 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
|
|
||||||
}
|
|
||||||
|
|
||||||
// warn if onChangeJSON is used when mode can be `text` or `code`
|
|
||||||
if (options.onChangeJSON) {
|
|
||||||
if (options.mode === 'text' || options.mode === 'code' ||
|
|
||||||
(options.modes && (options.modes.indexOf('text') !== -1 || options.modes.indexOf('code') !== -1))) {
|
|
||||||
console.warn('Option "onChangeJSON" is not applicable to modes "text" and "code". ' +
|
|
||||||
'Use "onChangeText" or "onChange" instead.')
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// validate options
|
|
||||||
if (options) {
|
|
||||||
Object.keys(options).forEach(option => {
|
|
||||||
if (JSONEditor.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 validation in milliseconds
|
|
||||||
JSONEditor.prototype.DEBOUNCE_INTERVAL = 150
|
|
||||||
|
|
||||||
JSONEditor.VALID_OPTIONS = [
|
|
||||||
'ajv', 'schema', 'schemaRefs', 'templates',
|
|
||||||
'ace', 'theme', 'autocomplete',
|
|
||||||
'onChange', 'onChangeJSON', 'onChangeText',
|
|
||||||
'onEditable', 'onError', 'onEvent', 'onModeChange', 'onNodeName', 'onValidate', 'onCreateMenu',
|
|
||||||
'onSelectionChange', 'onTextSelectionChange', 'onClassName',
|
|
||||||
'onFocus', 'onBlur',
|
|
||||||
'colorPicker', 'onColorPicker',
|
|
||||||
'timestampTag', 'timestampFormat',
|
|
||||||
'escapeUnicode', 'history', 'search', 'mode', 'modes', 'name', 'indentation',
|
|
||||||
'sortObjectKeys', 'navigationBar', 'statusBar', 'mainMenuBar', 'languages', 'language', 'enableSort', 'enableTransform',
|
|
||||||
'maxVisibleChilds', 'onValidationError',
|
|
||||||
'modalAnchor', 'popupAnchor',
|
|
||||||
'createQuery', 'executeQuery', 'queryDescription'
|
|
||||||
]
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 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 || {}
|
|
||||||
|
|
||||||
const mode = this.options.mode || (this.options.modes && this.options.modes[0]) || 'tree'
|
|
||||||
this.setMode(mode)
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Destroy the editor. Clean up DOM, event listeners, and web workers.
|
|
||||||
*/
|
|
||||||
JSONEditor.prototype.destroy = () => {}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 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 = 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) {
|
|
||||||
// if the mode is the same as current mode (and it's not the first time), do nothing.
|
|
||||||
if (mode === this.options.mode && this.create) {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
const container = this.container
|
|
||||||
const options = extend({}, this.options)
|
|
||||||
const oldMode = options.mode
|
|
||||||
let data
|
|
||||||
let name
|
|
||||||
|
|
||||||
options.mode = mode
|
|
||||||
const config = JSONEditor.modes[mode]
|
|
||||||
if (config) {
|
|
||||||
try {
|
|
||||||
const asText = (config.data === 'text')
|
|
||||||
name = this.getName()
|
|
||||||
data = this[asText ? 'getText' : 'get']() // get text or json
|
|
||||||
|
|
||||||
this.destroy()
|
|
||||||
clear(this)
|
|
||||||
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
|
|
||||||
* @param {Object.<string, Object>=} schemaRefs Schemas that are referenced using the `$ref` property from the JSON schema that are set in the `schema` option,
|
|
||||||
+ the object structure in the form of `{reference_key: schemaObject}`
|
|
||||||
*/
|
|
||||||
JSONEditor.prototype.setSchema = function (schema, schemaRefs) {
|
|
||||||
// compile a JSON schema validator if a JSON schema is provided
|
|
||||||
if (schema) {
|
|
||||||
let ajv
|
|
||||||
try {
|
|
||||||
// grab ajv from options if provided, else create a new instance
|
|
||||||
if (this.options.ajv) {
|
|
||||||
ajv = this.options.ajv
|
|
||||||
} else {
|
|
||||||
ajv = Ajv({
|
|
||||||
allErrors: true,
|
|
||||||
verbose: true,
|
|
||||||
schemaId: 'auto',
|
|
||||||
$data: true
|
|
||||||
})
|
|
||||||
|
|
||||||
// support both draft-04 and draft-06 alongside the latest draft-07
|
|
||||||
ajv.addMetaSchema(require('ajv/lib/refs/json-schema-draft-04.json'))
|
|
||||||
ajv.addMetaSchema(require('ajv/lib/refs/json-schema-draft-06.json'))
|
|
||||||
}
|
|
||||||
} 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) {
|
|
||||||
if (schemaRefs) {
|
|
||||||
for (const ref in schemaRefs) {
|
|
||||||
ajv.removeSchema(ref) // When updating a schema - old refs has to be removed first
|
|
||||||
if (schemaRefs[ref]) {
|
|
||||||
ajv.addSchema(schemaRefs[ref], ref)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
this.options.schemaRefs = schemaRefs
|
|
||||||
}
|
|
||||||
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.options.schemaRefs = 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 = () => {
|
|
||||||
// must be implemented by treemode and textmode
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Refresh the rendered contents
|
|
||||||
*/
|
|
||||||
JSONEditor.prototype.refresh = () => {
|
|
||||||
// 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 = mode => {
|
|
||||||
let i, prop
|
|
||||||
|
|
||||||
if (Array.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')
|
|
||||||
const 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')
|
|
||||||
}
|
|
||||||
const 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, text, and preview modes
|
|
||||||
JSONEditor.registerMode(treeModeMixins)
|
|
||||||
JSONEditor.registerMode(textModeMixins)
|
|
||||||
JSONEditor.registerMode(previewModeMixins)
|
|
||||||
|
|
||||||
// expose some of the libraries that can be used customized
|
|
||||||
JSONEditor.ace = ace
|
|
||||||
JSONEditor.Ajv = Ajv
|
|
||||||
JSONEditor.VanillaPicker = VanillaPicker
|
|
||||||
|
|
||||||
// expose some utils (this is undocumented, unofficial)
|
|
||||||
JSONEditor.showTransformModal = showTransformModal
|
|
||||||
JSONEditor.showSortModal = showSortModal
|
|
||||||
|
|
||||||
// default export for TypeScript ES6 projects
|
|
||||||
JSONEditor.default = JSONEditor
|
|
||||||
|
|
||||||
module.exports = JSONEditor
|
|
|
@ -1,123 +0,0 @@
|
||||||
'use strict'
|
|
||||||
|
|
||||||
import { ContextMenu } from './ContextMenu'
|
|
||||||
import { translate } from './i18n'
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 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', 'preview'
|
|
||||||
* @param {String} current Available modes: 'code', 'form', 'text', 'tree', 'view', 'preview'
|
|
||||||
* @param {function(mode: string)} onSwitch Callback invoked on switch
|
|
||||||
* @constructor
|
|
||||||
*/
|
|
||||||
export class ModeSwitcher {
|
|
||||||
constructor (container, modes, current, onSwitch) {
|
|
||||||
// available modes
|
|
||||||
const availableModes = {
|
|
||||||
code: {
|
|
||||||
text: translate('modeCodeText'),
|
|
||||||
title: translate('modeCodeTitle'),
|
|
||||||
click: function () {
|
|
||||||
onSwitch('code')
|
|
||||||
}
|
|
||||||
},
|
|
||||||
form: {
|
|
||||||
text: translate('modeFormText'),
|
|
||||||
title: translate('modeFormTitle'),
|
|
||||||
click: function () {
|
|
||||||
onSwitch('form')
|
|
||||||
}
|
|
||||||
},
|
|
||||||
text: {
|
|
||||||
text: translate('modeTextText'),
|
|
||||||
title: translate('modeTextTitle'),
|
|
||||||
click: function () {
|
|
||||||
onSwitch('text')
|
|
||||||
}
|
|
||||||
},
|
|
||||||
tree: {
|
|
||||||
text: translate('modeTreeText'),
|
|
||||||
title: translate('modeTreeTitle'),
|
|
||||||
click: function () {
|
|
||||||
onSwitch('tree')
|
|
||||||
}
|
|
||||||
},
|
|
||||||
view: {
|
|
||||||
text: translate('modeViewText'),
|
|
||||||
title: translate('modeViewTitle'),
|
|
||||||
click: function () {
|
|
||||||
onSwitch('view')
|
|
||||||
}
|
|
||||||
},
|
|
||||||
preview: {
|
|
||||||
text: translate('modePreviewText'),
|
|
||||||
title: translate('modePreviewTitle'),
|
|
||||||
click: function () {
|
|
||||||
onSwitch('preview')
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// list the selected modes
|
|
||||||
const items = []
|
|
||||||
for (let i = 0; i < modes.length; i++) {
|
|
||||||
const mode = modes[i]
|
|
||||||
const 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
|
|
||||||
const currentMode = availableModes[current]
|
|
||||||
if (!currentMode) {
|
|
||||||
throw new Error('Unknown mode "' + current + '"')
|
|
||||||
}
|
|
||||||
const currentTitle = currentMode.text
|
|
||||||
|
|
||||||
// create the html element
|
|
||||||
const box = document.createElement('button')
|
|
||||||
box.type = 'button'
|
|
||||||
box.className = 'jsoneditor-modes jsoneditor-separator'
|
|
||||||
box.innerHTML = currentTitle + ' ▾'
|
|
||||||
box.title = translate('modeEditorTitle')
|
|
||||||
box.onclick = () => {
|
|
||||||
const menu = new ContextMenu(items)
|
|
||||||
menu.show(box, container)
|
|
||||||
}
|
|
||||||
|
|
||||||
const 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
|
|
||||||
*/
|
|
||||||
focus () {
|
|
||||||
this.dom.box.focus()
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Destroy the ModeSwitcher, remove from DOM
|
|
||||||
*/
|
|
||||||
destroy () {
|
|
||||||
if (this.dom && this.dom.frame && this.dom.frame.parentNode) {
|
|
||||||
this.dom.frame.parentNode.removeChild(this.dom.frame)
|
|
||||||
}
|
|
||||||
this.dom = null
|
|
||||||
}
|
|
||||||
}
|
|
4619
src/js/Node.js
4619
src/js/Node.js
File diff suppressed because it is too large
Load Diff
|
@ -1,333 +0,0 @@
|
||||||
'use strict'
|
|
||||||
|
|
||||||
import { findUniqueName } from './util'
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @constructor History
|
|
||||||
* Store action history, enables undo and redo
|
|
||||||
* @param {JSONEditor} editor
|
|
||||||
*/
|
|
||||||
export class NodeHistory {
|
|
||||||
constructor (editor) {
|
|
||||||
this.editor = editor
|
|
||||||
this.history = []
|
|
||||||
this.index = -1
|
|
||||||
|
|
||||||
this.clear()
|
|
||||||
|
|
||||||
// helper function to find a Node from a path
|
|
||||||
function findNode (path) {
|
|
||||||
return editor.node.findNodeByInternalPath(path)
|
|
||||||
}
|
|
||||||
|
|
||||||
// map with all supported actions
|
|
||||||
this.actions = {
|
|
||||||
editField: {
|
|
||||||
undo: function (params) {
|
|
||||||
const parentNode = findNode(params.parentPath)
|
|
||||||
const node = parentNode.childs[params.index]
|
|
||||||
node.updateField(params.oldValue)
|
|
||||||
},
|
|
||||||
redo: function (params) {
|
|
||||||
const parentNode = findNode(params.parentPath)
|
|
||||||
const node = parentNode.childs[params.index]
|
|
||||||
node.updateField(params.newValue)
|
|
||||||
}
|
|
||||||
},
|
|
||||||
editValue: {
|
|
||||||
undo: function (params) {
|
|
||||||
findNode(params.path).updateValue(params.oldValue)
|
|
||||||
},
|
|
||||||
redo: function (params) {
|
|
||||||
findNode(params.path).updateValue(params.newValue)
|
|
||||||
}
|
|
||||||
},
|
|
||||||
changeType: {
|
|
||||||
undo: function (params) {
|
|
||||||
findNode(params.path).changeType(params.oldType)
|
|
||||||
},
|
|
||||||
redo: function (params) {
|
|
||||||
findNode(params.path).changeType(params.newType)
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
appendNodes: {
|
|
||||||
undo: function (params) {
|
|
||||||
const parentNode = findNode(params.parentPath)
|
|
||||||
params.paths.map(findNode).forEach(node => {
|
|
||||||
parentNode.removeChild(node)
|
|
||||||
})
|
|
||||||
},
|
|
||||||
redo: function (params) {
|
|
||||||
const parentNode = findNode(params.parentPath)
|
|
||||||
params.nodes.forEach(node => {
|
|
||||||
parentNode.appendChild(node)
|
|
||||||
})
|
|
||||||
}
|
|
||||||
},
|
|
||||||
insertBeforeNodes: {
|
|
||||||
undo: function (params) {
|
|
||||||
const parentNode = findNode(params.parentPath)
|
|
||||||
params.paths.map(findNode).forEach(node => {
|
|
||||||
parentNode.removeChild(node)
|
|
||||||
})
|
|
||||||
},
|
|
||||||
redo: function (params) {
|
|
||||||
const parentNode = findNode(params.parentPath)
|
|
||||||
const beforeNode = findNode(params.beforePath)
|
|
||||||
params.nodes.forEach(node => {
|
|
||||||
parentNode.insertBefore(node, beforeNode)
|
|
||||||
})
|
|
||||||
}
|
|
||||||
},
|
|
||||||
insertAfterNodes: {
|
|
||||||
undo: function (params) {
|
|
||||||
const parentNode = findNode(params.parentPath)
|
|
||||||
params.paths.map(findNode).forEach(node => {
|
|
||||||
parentNode.removeChild(node)
|
|
||||||
})
|
|
||||||
},
|
|
||||||
redo: function (params) {
|
|
||||||
const parentNode = findNode(params.parentPath)
|
|
||||||
let afterNode = findNode(params.afterPath)
|
|
||||||
params.nodes.forEach(node => {
|
|
||||||
parentNode.insertAfter(node, afterNode)
|
|
||||||
afterNode = node
|
|
||||||
})
|
|
||||||
}
|
|
||||||
},
|
|
||||||
removeNodes: {
|
|
||||||
undo: function (params) {
|
|
||||||
const parentNode = findNode(params.parentPath)
|
|
||||||
const beforeNode = parentNode.childs[params.index] || parentNode.append
|
|
||||||
params.nodes.forEach(node => {
|
|
||||||
parentNode.insertBefore(node, beforeNode)
|
|
||||||
})
|
|
||||||
},
|
|
||||||
redo: function (params) {
|
|
||||||
const parentNode = findNode(params.parentPath)
|
|
||||||
params.paths.map(findNode).forEach(node => {
|
|
||||||
parentNode.removeChild(node)
|
|
||||||
})
|
|
||||||
}
|
|
||||||
},
|
|
||||||
duplicateNodes: {
|
|
||||||
undo: function (params) {
|
|
||||||
const parentNode = findNode(params.parentPath)
|
|
||||||
params.clonePaths.map(findNode).forEach(node => {
|
|
||||||
parentNode.removeChild(node)
|
|
||||||
})
|
|
||||||
},
|
|
||||||
redo: function (params) {
|
|
||||||
const parentNode = findNode(params.parentPath)
|
|
||||||
let afterNode = findNode(params.afterPath)
|
|
||||||
const nodes = params.paths.map(findNode)
|
|
||||||
nodes.forEach(node => {
|
|
||||||
const clone = node.clone()
|
|
||||||
if (parentNode.type === 'object') {
|
|
||||||
const existingFieldNames = parentNode.getFieldNames()
|
|
||||||
clone.field = findUniqueName(node.field, existingFieldNames)
|
|
||||||
}
|
|
||||||
parentNode.insertAfter(clone, afterNode)
|
|
||||||
afterNode = clone
|
|
||||||
})
|
|
||||||
}
|
|
||||||
},
|
|
||||||
moveNodes: {
|
|
||||||
undo: function (params) {
|
|
||||||
const oldParentNode = findNode(params.oldParentPath)
|
|
||||||
const newParentNode = findNode(params.newParentPath)
|
|
||||||
const oldBeforeNode = oldParentNode.childs[params.oldIndex] || oldParentNode.append
|
|
||||||
|
|
||||||
// first copy the nodes, then move them
|
|
||||||
const nodes = newParentNode.childs.slice(params.newIndex, params.newIndex + params.count)
|
|
||||||
|
|
||||||
nodes.forEach((node, index) => {
|
|
||||||
node.field = params.fieldNames[index]
|
|
||||||
oldParentNode.moveBefore(node, oldBeforeNode)
|
|
||||||
})
|
|
||||||
|
|
||||||
// This is a hack to work around an issue that we don't know tha original
|
|
||||||
// path of the new parent after dragging, as the node is already moved at that time.
|
|
||||||
if (params.newParentPathRedo === null) {
|
|
||||||
params.newParentPathRedo = newParentNode.getInternalPath()
|
|
||||||
}
|
|
||||||
},
|
|
||||||
redo: function (params) {
|
|
||||||
const oldParentNode = findNode(params.oldParentPathRedo)
|
|
||||||
const newParentNode = findNode(params.newParentPathRedo)
|
|
||||||
const newBeforeNode = newParentNode.childs[params.newIndexRedo] || newParentNode.append
|
|
||||||
|
|
||||||
// first copy the nodes, then move them
|
|
||||||
const nodes = oldParentNode.childs.slice(params.oldIndexRedo, params.oldIndexRedo + params.count)
|
|
||||||
|
|
||||||
nodes.forEach((node, index) => {
|
|
||||||
node.field = params.fieldNames[index]
|
|
||||||
newParentNode.moveBefore(node, newBeforeNode)
|
|
||||||
})
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
sort: {
|
|
||||||
undo: function (params) {
|
|
||||||
const node = findNode(params.path)
|
|
||||||
node.hideChilds()
|
|
||||||
node.childs = params.oldChilds
|
|
||||||
node.updateDom({ updateIndexes: true })
|
|
||||||
node.showChilds()
|
|
||||||
},
|
|
||||||
redo: function (params) {
|
|
||||||
const node = findNode(params.path)
|
|
||||||
node.hideChilds()
|
|
||||||
node.childs = params.newChilds
|
|
||||||
node.updateDom({ updateIndexes: true })
|
|
||||||
node.showChilds()
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
transform: {
|
|
||||||
undo: function (params) {
|
|
||||||
findNode(params.path).setInternalValue(params.oldValue)
|
|
||||||
|
|
||||||
// TODO: would be nice to restore the state of the node and childs
|
|
||||||
},
|
|
||||||
redo: function (params) {
|
|
||||||
findNode(params.path).setInternalValue(params.newValue)
|
|
||||||
|
|
||||||
// TODO: would be nice to restore the state of the node and childs
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// 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.
|
|
||||||
*/
|
|
||||||
onChange () {}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 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.
|
|
||||||
*/
|
|
||||||
add (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
|
|
||||||
*/
|
|
||||||
clear () {
|
|
||||||
this.history = []
|
|
||||||
this.index = -1
|
|
||||||
|
|
||||||
// fire onchange event
|
|
||||||
this.onChange()
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Check if there is an action available for undo
|
|
||||||
* @return {Boolean} canUndo
|
|
||||||
*/
|
|
||||||
canUndo () {
|
|
||||||
return (this.index >= 0)
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Check if there is an action available for redo
|
|
||||||
* @return {Boolean} canRedo
|
|
||||||
*/
|
|
||||||
canRedo () {
|
|
||||||
return (this.index < this.history.length - 1)
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Undo the last action
|
|
||||||
*/
|
|
||||||
undo () {
|
|
||||||
if (this.canUndo()) {
|
|
||||||
const obj = this.history[this.index]
|
|
||||||
if (obj) {
|
|
||||||
const action = this.actions[obj.action]
|
|
||||||
if (action && action.undo) {
|
|
||||||
action.undo(obj.params)
|
|
||||||
if (obj.params.oldSelection) {
|
|
||||||
try {
|
|
||||||
this.editor.setDomSelection(obj.params.oldSelection)
|
|
||||||
} catch (err) {
|
|
||||||
console.error(err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
console.error(new Error('unknown action "' + obj.action + '"'))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
this.index--
|
|
||||||
|
|
||||||
// fire onchange event
|
|
||||||
this.onChange()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Redo the last action
|
|
||||||
*/
|
|
||||||
redo () {
|
|
||||||
if (this.canRedo()) {
|
|
||||||
this.index++
|
|
||||||
|
|
||||||
const obj = this.history[this.index]
|
|
||||||
if (obj) {
|
|
||||||
const action = this.actions[obj.action]
|
|
||||||
if (action && action.redo) {
|
|
||||||
action.redo(obj.params)
|
|
||||||
if (obj.params.newSelection) {
|
|
||||||
try {
|
|
||||||
this.editor.setDomSelection(obj.params.newSelection)
|
|
||||||
} catch (err) {
|
|
||||||
console.error(err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
console.error(new Error('unknown action "' + obj.action + '"'))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// fire onchange event
|
|
||||||
this.onChange()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Destroy history
|
|
||||||
*/
|
|
||||||
destroy () {
|
|
||||||
this.editor = null
|
|
||||||
|
|
||||||
this.history = []
|
|
||||||
this.index = -1
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,325 +0,0 @@
|
||||||
'use strict'
|
|
||||||
import { translate } from './i18n'
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @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
|
|
||||||
*/
|
|
||||||
export class SearchBox {
|
|
||||||
constructor (editor, container) {
|
|
||||||
const searchBox = this
|
|
||||||
|
|
||||||
this.editor = editor
|
|
||||||
this.timeout = undefined
|
|
||||||
this.delay = 200 // ms
|
|
||||||
this.lastText = undefined
|
|
||||||
this.results = null
|
|
||||||
|
|
||||||
this.dom = {}
|
|
||||||
this.dom.container = container
|
|
||||||
|
|
||||||
const wrapper = document.createElement('div')
|
|
||||||
this.dom.wrapper = wrapper
|
|
||||||
wrapper.className = 'jsoneditor-search'
|
|
||||||
container.appendChild(wrapper)
|
|
||||||
|
|
||||||
const results = document.createElement('div')
|
|
||||||
this.dom.results = results
|
|
||||||
results.className = 'jsoneditor-results'
|
|
||||||
wrapper.appendChild(results)
|
|
||||||
|
|
||||||
const divInput = document.createElement('div')
|
|
||||||
this.dom.input = divInput
|
|
||||||
divInput.className = 'jsoneditor-frame'
|
|
||||||
divInput.title = translate('searchTitle')
|
|
||||||
wrapper.appendChild(divInput)
|
|
||||||
|
|
||||||
const refreshSearch = document.createElement('button')
|
|
||||||
refreshSearch.type = 'button'
|
|
||||||
refreshSearch.className = 'jsoneditor-refresh'
|
|
||||||
divInput.appendChild(refreshSearch)
|
|
||||||
|
|
||||||
const search = document.createElement('input')
|
|
||||||
search.type = 'text'
|
|
||||||
this.dom.search = search
|
|
||||||
search.oninput = event => {
|
|
||||||
searchBox._onDelayedSearch(event)
|
|
||||||
}
|
|
||||||
search.onchange = event => {
|
|
||||||
// For IE 9
|
|
||||||
searchBox._onSearch()
|
|
||||||
}
|
|
||||||
search.onkeydown = event => {
|
|
||||||
searchBox._onKeyDown(event)
|
|
||||||
}
|
|
||||||
search.onkeyup = event => {
|
|
||||||
searchBox._onKeyUp(event)
|
|
||||||
}
|
|
||||||
refreshSearch.onclick = event => {
|
|
||||||
search.select()
|
|
||||||
}
|
|
||||||
|
|
||||||
// TODO: ESC in FF restores the last input, is a FF bug, https://bugzilla.mozilla.org/show_bug.cgi?id=598819
|
|
||||||
divInput.appendChild(search)
|
|
||||||
|
|
||||||
const searchNext = document.createElement('button')
|
|
||||||
searchNext.type = 'button'
|
|
||||||
searchNext.title = translate('searchNextResultTitle')
|
|
||||||
searchNext.className = 'jsoneditor-next'
|
|
||||||
searchNext.onclick = () => {
|
|
||||||
searchBox.next()
|
|
||||||
}
|
|
||||||
|
|
||||||
divInput.appendChild(searchNext)
|
|
||||||
|
|
||||||
const searchPrevious = document.createElement('button')
|
|
||||||
searchPrevious.type = 'button'
|
|
||||||
searchPrevious.title = translate('searchPreviousResultTitle')
|
|
||||||
searchPrevious.className = 'jsoneditor-previous'
|
|
||||||
searchPrevious.onclick = () => {
|
|
||||||
searchBox.previous()
|
|
||||||
}
|
|
||||||
|
|
||||||
divInput.appendChild(searchPrevious)
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Go to the next search result
|
|
||||||
* @param {boolean} [focus] If true, focus will be set to the next result
|
|
||||||
* focus is false by default.
|
|
||||||
*/
|
|
||||||
next (focus) {
|
|
||||||
if (this.results) {
|
|
||||||
let index = this.resultIndex !== null ? 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.
|
|
||||||
*/
|
|
||||||
previous (focus) {
|
|
||||||
if (this.results) {
|
|
||||||
const max = this.results.length - 1
|
|
||||||
let index = this.resultIndex !== null ? 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
|
|
||||||
*/
|
|
||||||
_setActiveResult (index, focus) {
|
|
||||||
// de-activate current active result
|
|
||||||
if (this.activeResult) {
|
|
||||||
const prevNode = this.activeResult.node
|
|
||||||
const 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
|
|
||||||
const node = this.results[this.resultIndex].node
|
|
||||||
const 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(() => {
|
|
||||||
if (focus) {
|
|
||||||
node.focus(elem)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Cancel any running onDelayedSearch.
|
|
||||||
* @private
|
|
||||||
*/
|
|
||||||
_clearDelay () {
|
|
||||||
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
|
|
||||||
*/
|
|
||||||
_onDelayedSearch (event) {
|
|
||||||
// execute the search after a short delay (reduces the number of
|
|
||||||
// search actions while typing in the search text box)
|
|
||||||
this._clearDelay()
|
|
||||||
const searchBox = this
|
|
||||||
this.timeout = setTimeout(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
|
|
||||||
*/
|
|
||||||
_onSearch (forceSearch) {
|
|
||||||
this._clearDelay()
|
|
||||||
|
|
||||||
const value = this.dom.search.value
|
|
||||||
const 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)
|
|
||||||
const MAX_SEARCH_RESULTS = this.results[0]
|
|
||||||
? this.results[0].node.MAX_SEARCH_RESULTS
|
|
||||||
: Infinity
|
|
||||||
|
|
||||||
// try to maintain the current active result if this is still part of the new search results
|
|
||||||
let activeResultIndex = 0
|
|
||||||
if (this.activeResult) {
|
|
||||||
for (let i = 0; i < this.results.length; i++) {
|
|
||||||
if (this.results[i].node === this.activeResult.node) {
|
|
||||||
activeResultIndex = i
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
this._setActiveResult(activeResultIndex, false)
|
|
||||||
|
|
||||||
// display search results
|
|
||||||
if (text !== undefined) {
|
|
||||||
const resultCount = this.results.length
|
|
||||||
if (resultCount === 0) {
|
|
||||||
this.dom.results.innerHTML = 'no results'
|
|
||||||
} else if (resultCount === 1) {
|
|
||||||
this.dom.results.innerHTML = '1 result'
|
|
||||||
} else if (resultCount > MAX_SEARCH_RESULTS) {
|
|
||||||
this.dom.results.innerHTML = MAX_SEARCH_RESULTS + '+ results'
|
|
||||||
} else {
|
|
||||||
this.dom.results.innerHTML = resultCount + ' results'
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
this.dom.results.innerHTML = ''
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Handle onKeyDown event in the input box
|
|
||||||
* @param {Event} event
|
|
||||||
* @private
|
|
||||||
*/
|
|
||||||
_onKeyDown (event) {
|
|
||||||
const 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
|
|
||||||
*/
|
|
||||||
_onKeyUp (event) {
|
|
||||||
const keynum = event.keyCode
|
|
||||||
if (keynum !== 27 && keynum !== 13) {
|
|
||||||
// !show and !Enter
|
|
||||||
this._onDelayedSearch(event) // For IE 9
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Clear the search results
|
|
||||||
*/
|
|
||||||
clear () {
|
|
||||||
this.dom.search.value = ''
|
|
||||||
this._onSearch()
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Refresh searchResults if there is a search value
|
|
||||||
*/
|
|
||||||
forceSearch () {
|
|
||||||
this._onSearch(true)
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Test whether the search box value is empty
|
|
||||||
* @returns {boolean} Returns true when empty.
|
|
||||||
*/
|
|
||||||
isEmpty () {
|
|
||||||
return this.dom.search.value === ''
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Destroy the search box
|
|
||||||
*/
|
|
||||||
destroy () {
|
|
||||||
this.editor = null
|
|
||||||
this.dom.container.removeChild(this.dom.wrapper)
|
|
||||||
this.dom = null
|
|
||||||
|
|
||||||
this.results = null
|
|
||||||
this.activeResult = null
|
|
||||||
|
|
||||||
this._clearDelay()
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,142 +0,0 @@
|
||||||
'use strict'
|
|
||||||
|
|
||||||
import { ContextMenu } from './ContextMenu'
|
|
||||||
import { translate } from './i18n'
|
|
||||||
import { addClassName, removeClassName } from './util'
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Creates a component that visualize path selection in tree based editors
|
|
||||||
* @param {HTMLElement} container
|
|
||||||
* @param {HTMLElement} root
|
|
||||||
* @constructor
|
|
||||||
*/
|
|
||||||
export class TreePath {
|
|
||||||
constructor (container, root) {
|
|
||||||
if (container) {
|
|
||||||
this.root = root
|
|
||||||
this.path = document.createElement('div')
|
|
||||||
this.path.className = 'jsoneditor-treepath'
|
|
||||||
this.path.setAttribute('tabindex', 0)
|
|
||||||
this.contentMenuClicked = false
|
|
||||||
container.appendChild(this.path)
|
|
||||||
this.reset()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Reset component to initial status
|
|
||||||
*/
|
|
||||||
reset () {
|
|
||||||
this.path.innerHTML = translate('selectNode')
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Renders the component UI according to a given path objects
|
|
||||||
* @param {Array<{name: String, childs: Array}>} pathObjs a list of path objects
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
setPath (pathObjs) {
|
|
||||||
const me = this
|
|
||||||
|
|
||||||
this.path.innerHTML = ''
|
|
||||||
|
|
||||||
if (pathObjs && pathObjs.length) {
|
|
||||||
pathObjs.forEach((pathObj, idx) => {
|
|
||||||
const pathEl = document.createElement('span')
|
|
||||||
let 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 = () => {
|
|
||||||
me.contentMenuClicked = true
|
|
||||||
const items = []
|
|
||||||
pathObj.children.forEach(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)
|
|
||||||
})
|
|
||||||
})
|
|
||||||
const menu = new ContextMenu(items)
|
|
||||||
menu.show(sepEl, me.root, true)
|
|
||||||
}
|
|
||||||
|
|
||||||
me.path.appendChild(sepEl)
|
|
||||||
}
|
|
||||||
|
|
||||||
if (idx === pathObjs.length - 1) {
|
|
||||||
const leftRectPos = (sepEl || pathEl).getBoundingClientRect().right
|
|
||||||
if (me.path.offsetWidth < leftRectPos) {
|
|
||||||
me.path.scrollLeft = leftRectPos
|
|
||||||
}
|
|
||||||
|
|
||||||
if (me.path.scrollLeft) {
|
|
||||||
const showAllBtn = document.createElement('span')
|
|
||||||
showAllBtn.className = 'jsoneditor-treepath-show-all-btn'
|
|
||||||
showAllBtn.title = 'show all path'
|
|
||||||
showAllBtn.innerHTML = '...'
|
|
||||||
showAllBtn.onclick = _onShowAllClick.bind(me, pathObjs)
|
|
||||||
me.path.insertBefore(showAllBtn, me.path.firstChild)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
function _onShowAllClick (pathObjs) {
|
|
||||||
me.contentMenuClicked = false
|
|
||||||
addClassName(me.path, 'show-all')
|
|
||||||
me.path.style.width = me.path.parentNode.getBoundingClientRect().width - 10 + 'px'
|
|
||||||
me.path.onblur = () => {
|
|
||||||
if (me.contentMenuClicked) {
|
|
||||||
me.contentMenuClicked = false
|
|
||||||
me.path.focus()
|
|
||||||
return
|
|
||||||
}
|
|
||||||
removeClassName(me.path, 'show-all')
|
|
||||||
me.path.onblur = undefined
|
|
||||||
me.path.style.width = ''
|
|
||||||
me.setPath(pathObjs)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
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
|
|
||||||
*/
|
|
||||||
onSectionSelected (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
|
|
||||||
*/
|
|
||||||
onContextMenuItemSelected (callback) {
|
|
||||||
if (typeof callback === 'function') {
|
|
||||||
this.contextMenuCallback = callback
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,24 +0,0 @@
|
||||||
let ace
|
|
||||||
if (window.ace) {
|
|
||||||
// use the already loaded instance of Ace
|
|
||||||
ace = window.ace
|
|
||||||
} else {
|
|
||||||
try {
|
|
||||||
// load Ace editor
|
|
||||||
ace = require('ace-builds/src-noconflict/ace')
|
|
||||||
|
|
||||||
// load required Ace plugins
|
|
||||||
require('ace-builds/src-noconflict/mode-json')
|
|
||||||
require('ace-builds/src-noconflict/ext-searchbox')
|
|
||||||
|
|
||||||
// embed Ace json worker
|
|
||||||
// https://github.com/ajaxorg/ace/issues/3913
|
|
||||||
const jsonWorkerDataUrl = require('../generated/worker-json-data-url')
|
|
||||||
ace.config.setModuleUrl('ace/mode/json_worker', jsonWorkerDataUrl)
|
|
||||||
} catch (err) {
|
|
||||||
// failed to load Ace (can be minimalist bundle).
|
|
||||||
// No worries, the editor will fall back to plain text if needed.
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
module.exports = ace
|
|
|
@ -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 ***** */
|
|
||||||
|
|
||||||
window.ace.define('ace/theme/jsoneditor', ['require', 'exports', 'module', 'ace/lib/dom'], (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;
|
|
||||||
background-color: #fff;
|
|
||||||
}
|
|
||||||
.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
|
|
||||||
}`
|
|
||||||
|
|
||||||
const dom = acequire('../lib/dom')
|
|
||||||
dom.importCssString(exports.cssText, exports.cssClass)
|
|
||||||
})
|
|
|
@ -1,251 +0,0 @@
|
||||||
'use strict'
|
|
||||||
|
|
||||||
import { ContextMenu } from './ContextMenu'
|
|
||||||
import { translate } from './i18n'
|
|
||||||
import { addClassName, removeClassName } from './util'
|
|
||||||
|
|
||||||
/**
|
|
||||||
* A factory function to create an AppendNode, which depends on a Node
|
|
||||||
* @param {Node} Node
|
|
||||||
*/
|
|
||||||
export 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
|
|
||||||
const dom = this.dom
|
|
||||||
|
|
||||||
if (dom.tr) {
|
|
||||||
return dom.tr
|
|
||||||
}
|
|
||||||
|
|
||||||
this._updateEditability()
|
|
||||||
|
|
||||||
// a row for the append button
|
|
||||||
const trAppend = document.createElement('tr')
|
|
||||||
trAppend.className = 'jsoneditor-append'
|
|
||||||
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
|
|
||||||
const tdMenu = document.createElement('td')
|
|
||||||
dom.tdMenu = tdMenu
|
|
||||||
const menu = document.createElement('button')
|
|
||||||
menu.type = 'button'
|
|
||||||
menu.className = 'jsoneditor-button jsoneditor-contextmenu-button'
|
|
||||||
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')
|
|
||||||
const tdAppend = document.createElement('td')
|
|
||||||
const domText = document.createElement('div')
|
|
||||||
domText.innerHTML = '(' + translate('empty') + ')'
|
|
||||||
domText.className = 'jsoneditor-readonly'
|
|
||||||
tdAppend.appendChild(domText)
|
|
||||||
dom.td = tdAppend
|
|
||||||
dom.text = domText
|
|
||||||
|
|
||||||
this.updateDom()
|
|
||||||
|
|
||||||
return trAppend
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Append node doesn't have a path
|
|
||||||
* @returns {null}
|
|
||||||
*/
|
|
||||||
AppendNode.prototype.getPath = () => null
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Append node doesn't have an index
|
|
||||||
* @returns {null}
|
|
||||||
*/
|
|
||||||
AppendNode.prototype.getIndex = () => null
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Update the HTML dom of the Node
|
|
||||||
*/
|
|
||||||
AppendNode.prototype.updateDom = function (options) {
|
|
||||||
const dom = this.dom
|
|
||||||
const tdAppend = dom.td
|
|
||||||
if (tdAppend) {
|
|
||||||
tdAppend.style.paddingLeft = (this.getLevel() * 24 + 26) + 'px'
|
|
||||||
// TODO: not so nice hard coded offset
|
|
||||||
}
|
|
||||||
|
|
||||||
const domText = dom.text
|
|
||||||
if (domText) {
|
|
||||||
domText.innerHTML = '(' + translate('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
|
|
||||||
const 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) {
|
|
||||||
const node = this
|
|
||||||
|
|
||||||
const appendSubmenu = [
|
|
||||||
{
|
|
||||||
text: translate('auto'),
|
|
||||||
className: 'jsoneditor-type-auto',
|
|
||||||
title: translate('autoType'),
|
|
||||||
click: function () {
|
|
||||||
node._onAppend('', '', 'auto')
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
text: translate('array'),
|
|
||||||
className: 'jsoneditor-type-array',
|
|
||||||
title: translate('arrayType'),
|
|
||||||
click: function () {
|
|
||||||
node._onAppend('', [])
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
text: translate('object'),
|
|
||||||
className: 'jsoneditor-type-object',
|
|
||||||
title: translate('objectType'),
|
|
||||||
click: function () {
|
|
||||||
node._onAppend('', {})
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
text: translate('string'),
|
|
||||||
className: 'jsoneditor-type-string',
|
|
||||||
title: translate('stringType'),
|
|
||||||
click: function () {
|
|
||||||
node._onAppend('', '', 'string')
|
|
||||||
}
|
|
||||||
}
|
|
||||||
]
|
|
||||||
node.addTemplates(appendSubmenu, true)
|
|
||||||
let items = [
|
|
||||||
// create append button
|
|
||||||
{
|
|
||||||
text: translate('appendText'),
|
|
||||||
title: translate('appendTitleAuto'),
|
|
||||||
submenuTitle: translate('appendSubmenuTitle'),
|
|
||||||
className: 'jsoneditor-insert',
|
|
||||||
click: function () {
|
|
||||||
node._onAppend('', '', 'auto')
|
|
||||||
},
|
|
||||||
submenu: appendSubmenu
|
|
||||||
}
|
|
||||||
]
|
|
||||||
|
|
||||||
if (this.editor.options.onCreateMenu) {
|
|
||||||
const path = node.parent.getPath()
|
|
||||||
|
|
||||||
items = this.editor.options.onCreateMenu(items, {
|
|
||||||
type: 'append',
|
|
||||||
path: path,
|
|
||||||
paths: [path]
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
const menu = new ContextMenu(items, { close: onClose })
|
|
||||||
menu.show(anchor, this.editor.getPopupAnchor())
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Handle an event. The event is caught centrally by the editor
|
|
||||||
* @param {Event} event
|
|
||||||
*/
|
|
||||||
AppendNode.prototype.onEvent = function (event) {
|
|
||||||
const type = event.type
|
|
||||||
const target = event.target || event.srcElement
|
|
||||||
const dom = this.dom
|
|
||||||
|
|
||||||
// highlight the append nodes parent
|
|
||||||
const 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) {
|
|
||||||
const highlighter = this.editor.highlighter
|
|
||||||
highlighter.highlight(this.parent)
|
|
||||||
highlighter.lock()
|
|
||||||
addClassName(dom.menu, 'jsoneditor-selected')
|
|
||||||
this.showContextMenu(dom.menu, () => {
|
|
||||||
removeClassName(dom.menu, 'jsoneditor-selected')
|
|
||||||
highlighter.unlock()
|
|
||||||
highlighter.unhighlight()
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
if (type === 'keydown') {
|
|
||||||
this.onKeyDown(event)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return AppendNode
|
|
||||||
}
|
|
|
@ -1,15 +0,0 @@
|
||||||
The file jsonlint.js is copied from the following project:
|
|
||||||
|
|
||||||
https://github.com/josdejong/jsonlint at 85a19d7
|
|
||||||
|
|
||||||
which is a fork of the (currently not maintained) project:
|
|
||||||
|
|
||||||
https://github.com/zaach/jsonlint
|
|
||||||
|
|
||||||
The forked project contains some fixes to allow the file to be bundled with
|
|
||||||
browserify. The file is copied in this project to prevent issues with linking
|
|
||||||
to a github project from package.json, which is for example not supported
|
|
||||||
by jspm.
|
|
||||||
|
|
||||||
As soon as zaach/jsonlint is being maintained again we can push the fix
|
|
||||||
to the original library and use it as dependency again.
|
|
|
@ -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);
|
|
||||||
}
|
|
|
@ -1,6 +0,0 @@
|
||||||
This is a copy of the Selectr project
|
|
||||||
|
|
||||||
https://github.com/Mobius1/Selectr
|
|
||||||
|
|
||||||
Reason is that the project is not maintained and has some issues
|
|
||||||
loading it via `require` in a webpack project.
|
|
File diff suppressed because it is too large
Load Diff
|
@ -1,472 +0,0 @@
|
||||||
/*!
|
|
||||||
* Selectr 2.4.0
|
|
||||||
* https://github.com/Mobius1/Selectr
|
|
||||||
*
|
|
||||||
* Released under the MIT license
|
|
||||||
*/
|
|
||||||
.selectr-container {
|
|
||||||
position: relative;
|
|
||||||
}
|
|
||||||
|
|
||||||
.selectr-container li {
|
|
||||||
list-style: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
.selectr-hidden {
|
|
||||||
position: absolute;
|
|
||||||
overflow: hidden;
|
|
||||||
clip: rect(0px, 0px, 0px, 0px);
|
|
||||||
width: 1px;
|
|
||||||
height: 1px;
|
|
||||||
margin: -1px;
|
|
||||||
padding: 0;
|
|
||||||
border: 0 none;
|
|
||||||
}
|
|
||||||
|
|
||||||
.selectr-visible {
|
|
||||||
position: absolute;
|
|
||||||
left: 0;
|
|
||||||
top: 0;
|
|
||||||
width: 100%;
|
|
||||||
height: 100%;
|
|
||||||
opacity: 0;
|
|
||||||
z-index: 11;
|
|
||||||
}
|
|
||||||
|
|
||||||
.selectr-desktop.multiple .selectr-visible {
|
|
||||||
display: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
.selectr-desktop.multiple.native-open .selectr-visible {
|
|
||||||
top: 100%;
|
|
||||||
min-height: 200px !important;
|
|
||||||
height: auto;
|
|
||||||
opacity: 1;
|
|
||||||
display: block;
|
|
||||||
}
|
|
||||||
|
|
||||||
.selectr-container.multiple.selectr-mobile .selectr-selected {
|
|
||||||
z-index: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
.selectr-selected {
|
|
||||||
position: relative;
|
|
||||||
z-index: 1;
|
|
||||||
box-sizing: border-box;
|
|
||||||
width: 100%;
|
|
||||||
padding: 7px 28px 7px 14px;
|
|
||||||
cursor: pointer;
|
|
||||||
border: 1px solid $jse-grey;
|
|
||||||
border-radius: 3px;
|
|
||||||
background-color: $jse-white;
|
|
||||||
}
|
|
||||||
|
|
||||||
.selectr-selected::before {
|
|
||||||
position: absolute;
|
|
||||||
top: 50%;
|
|
||||||
right: 10px;
|
|
||||||
width: 0;
|
|
||||||
height: 0;
|
|
||||||
content: '';
|
|
||||||
-o-transform: rotate(0deg) translate3d(0px, -50%, 0px);
|
|
||||||
-ms-transform: rotate(0deg) translate3d(0px, -50%, 0px);
|
|
||||||
-moz-transform: rotate(0deg) translate3d(0px, -50%, 0px);
|
|
||||||
-webkit-transform: rotate(0deg) translate3d(0px, -50%, 0px);
|
|
||||||
transform: rotate(0deg) translate3d(0px, -50%, 0px);
|
|
||||||
border-width: 4px 4px 0 4px;
|
|
||||||
border-style: solid;
|
|
||||||
border-color: #6c7a86 transparent transparent;
|
|
||||||
}
|
|
||||||
|
|
||||||
.selectr-container.open .selectr-selected::before,
|
|
||||||
.selectr-container.native-open .selectr-selected::before {
|
|
||||||
border-width: 0 4px 4px 4px;
|
|
||||||
border-style: solid;
|
|
||||||
border-color: transparent transparent #6c7a86;
|
|
||||||
}
|
|
||||||
|
|
||||||
.selectr-label {
|
|
||||||
display: none;
|
|
||||||
overflow: hidden;
|
|
||||||
width: 100%;
|
|
||||||
white-space: nowrap;
|
|
||||||
text-overflow: ellipsis;
|
|
||||||
}
|
|
||||||
|
|
||||||
.selectr-placeholder {
|
|
||||||
color: #6c7a86;
|
|
||||||
}
|
|
||||||
|
|
||||||
.selectr-tags {
|
|
||||||
margin: 0;
|
|
||||||
padding: 0;
|
|
||||||
white-space: normal;
|
|
||||||
}
|
|
||||||
|
|
||||||
.has-selected .selectr-tags {
|
|
||||||
margin: 0 0 -2px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.selectr-tag {
|
|
||||||
list-style: none;
|
|
||||||
position: relative;
|
|
||||||
float: left;
|
|
||||||
padding: 2px 25px 2px 8px;
|
|
||||||
margin: 0 2px 2px 0;
|
|
||||||
cursor: default;
|
|
||||||
color: $jse-white;
|
|
||||||
border: medium none;
|
|
||||||
border-radius: 10px;
|
|
||||||
background: #acb7bf none repeat scroll 0 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
.selectr-container.multiple.has-selected .selectr-selected {
|
|
||||||
padding: 5px 28px 5px 5px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.selectr-options-container {
|
|
||||||
position: absolute;
|
|
||||||
z-index: 10000;
|
|
||||||
top: calc(100% - 1px);
|
|
||||||
left: 0;
|
|
||||||
display: none;
|
|
||||||
box-sizing: border-box;
|
|
||||||
width: 100%;
|
|
||||||
border-width: 0 1px 1px;
|
|
||||||
border-style: solid;
|
|
||||||
border-color: transparent $jse-grey $jse-grey;
|
|
||||||
border-radius: 0 0 3px 3px;
|
|
||||||
background-color: $jse-white;
|
|
||||||
}
|
|
||||||
|
|
||||||
.selectr-container.open .selectr-options-container {
|
|
||||||
display: block;
|
|
||||||
}
|
|
||||||
|
|
||||||
.selectr-input-container {
|
|
||||||
position: relative;
|
|
||||||
display: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
.selectr-clear,
|
|
||||||
.selectr-input-clear,
|
|
||||||
.selectr-tag-remove {
|
|
||||||
position: absolute;
|
|
||||||
top: 50%;
|
|
||||||
right: 22px;
|
|
||||||
width: 20px;
|
|
||||||
height: 20px;
|
|
||||||
padding: 0;
|
|
||||||
cursor: pointer;
|
|
||||||
-o-transform: translate3d(0px, -50%, 0px);
|
|
||||||
-ms-transform: translate3d(0px, -50%, 0px);
|
|
||||||
-moz-transform: translate3d(0px, -50%, 0px);
|
|
||||||
-webkit-transform: translate3d(0px, -50%, 0px);
|
|
||||||
transform: translate3d(0px, -50%, 0px);
|
|
||||||
border: medium none;
|
|
||||||
background-color: transparent;
|
|
||||||
z-index: 11;
|
|
||||||
}
|
|
||||||
|
|
||||||
.selectr-clear,
|
|
||||||
.selectr-input-clear {
|
|
||||||
display: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
.selectr-container.has-selected .selectr-clear,
|
|
||||||
.selectr-input-container.active .selectr-input-clear {
|
|
||||||
display: block;
|
|
||||||
}
|
|
||||||
|
|
||||||
.selectr-selected .selectr-tag-remove {
|
|
||||||
right: 2px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.selectr-clear::before,
|
|
||||||
.selectr-clear::after,
|
|
||||||
.selectr-input-clear::before,
|
|
||||||
.selectr-input-clear::after,
|
|
||||||
.selectr-tag-remove::before,
|
|
||||||
.selectr-tag-remove::after {
|
|
||||||
position: absolute;
|
|
||||||
top: 5px;
|
|
||||||
left: 9px;
|
|
||||||
width: 2px;
|
|
||||||
height: 10px;
|
|
||||||
content: ' ';
|
|
||||||
background-color: #6c7a86;
|
|
||||||
}
|
|
||||||
|
|
||||||
.selectr-tag-remove::before,
|
|
||||||
.selectr-tag-remove::after {
|
|
||||||
top: 4px;
|
|
||||||
width: 3px;
|
|
||||||
height: 12px;
|
|
||||||
background-color: $jse-white;
|
|
||||||
}
|
|
||||||
|
|
||||||
.selectr-clear:before,
|
|
||||||
.selectr-input-clear::before,
|
|
||||||
.selectr-tag-remove::before {
|
|
||||||
-o-transform: rotate(45deg);
|
|
||||||
-ms-transform: rotate(45deg);
|
|
||||||
-moz-transform: rotate(45deg);
|
|
||||||
-webkit-transform: rotate(45deg);
|
|
||||||
transform: rotate(45deg);
|
|
||||||
}
|
|
||||||
|
|
||||||
.selectr-clear:after,
|
|
||||||
.selectr-input-clear::after,
|
|
||||||
.selectr-tag-remove::after {
|
|
||||||
-o-transform: rotate(-45deg);
|
|
||||||
-ms-transform: rotate(-45deg);
|
|
||||||
-moz-transform: rotate(-45deg);
|
|
||||||
-webkit-transform: rotate(-45deg);
|
|
||||||
transform: rotate(-45deg);
|
|
||||||
}
|
|
||||||
|
|
||||||
.selectr-input-container.active,
|
|
||||||
.selectr-input-container.active .selectr-clear {
|
|
||||||
display: block;
|
|
||||||
}
|
|
||||||
|
|
||||||
.selectr-input {
|
|
||||||
top: 5px;
|
|
||||||
left: 5px;
|
|
||||||
box-sizing: border-box;
|
|
||||||
width: calc(100% - 30px);
|
|
||||||
margin: 10px 15px;
|
|
||||||
padding: 7px 30px 7px 9px;
|
|
||||||
border: 1px solid $jse-grey;
|
|
||||||
border-radius: 3px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.selectr-notice {
|
|
||||||
display: none;
|
|
||||||
box-sizing: border-box;
|
|
||||||
width: 100%;
|
|
||||||
padding: 8px 16px;
|
|
||||||
border-top: 1px solid $jse-grey;
|
|
||||||
border-radius: 0 0 3px 3px;
|
|
||||||
background-color: $jse-white;
|
|
||||||
}
|
|
||||||
|
|
||||||
.selectr-container.notice .selectr-notice {
|
|
||||||
display: block;
|
|
||||||
}
|
|
||||||
|
|
||||||
.selectr-container.notice .selectr-selected {
|
|
||||||
border-radius: 3px 3px 0 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
.selectr-options {
|
|
||||||
position: relative;
|
|
||||||
top: calc(100% + 2px);
|
|
||||||
display: none;
|
|
||||||
overflow-x: auto;
|
|
||||||
overflow-y: scroll;
|
|
||||||
max-height: 200px;
|
|
||||||
margin: 0;
|
|
||||||
padding: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
.selectr-container.open .selectr-options,
|
|
||||||
.selectr-container.open .selectr-input-container,
|
|
||||||
.selectr-container.notice .selectr-options-container {
|
|
||||||
display: block;
|
|
||||||
}
|
|
||||||
|
|
||||||
.selectr-option {
|
|
||||||
position: relative;
|
|
||||||
display: block;
|
|
||||||
padding: 5px 20px;
|
|
||||||
list-style: outside none none;
|
|
||||||
cursor: pointer;
|
|
||||||
font-weight: normal;
|
|
||||||
}
|
|
||||||
|
|
||||||
.selectr-options.optgroups > .selectr-option {
|
|
||||||
padding-left: 25px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.selectr-optgroup {
|
|
||||||
font-weight: bold;
|
|
||||||
padding: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
.selectr-optgroup--label {
|
|
||||||
font-weight: bold;
|
|
||||||
margin-top: 10px;
|
|
||||||
padding: 5px 15px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.selectr-match {
|
|
||||||
text-decoration: underline;
|
|
||||||
}
|
|
||||||
|
|
||||||
.selectr-option.selected {
|
|
||||||
background-color: #ddd;
|
|
||||||
}
|
|
||||||
|
|
||||||
.selectr-option.active {
|
|
||||||
color: $jse-white;
|
|
||||||
background-color: #5897fb;
|
|
||||||
}
|
|
||||||
|
|
||||||
.selectr-option.disabled {
|
|
||||||
opacity: 0.4;
|
|
||||||
}
|
|
||||||
|
|
||||||
.selectr-option.excluded {
|
|
||||||
display: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
.selectr-container.open .selectr-selected {
|
|
||||||
border-color: $jse-grey $jse-grey transparent $jse-grey;
|
|
||||||
border-radius: 3px 3px 0 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
.selectr-container.open .selectr-selected::after {
|
|
||||||
-o-transform: rotate(180deg) translate3d(0px, 50%, 0px);
|
|
||||||
-ms-transform: rotate(180deg) translate3d(0px, 50%, 0px);
|
|
||||||
-moz-transform: rotate(180deg) translate3d(0px, 50%, 0px);
|
|
||||||
-webkit-transform: rotate(180deg) translate3d(0px, 50%, 0px);
|
|
||||||
transform: rotate(180deg) translate3d(0px, 50%, 0px);
|
|
||||||
}
|
|
||||||
|
|
||||||
.selectr-disabled {
|
|
||||||
opacity: .6;
|
|
||||||
}
|
|
||||||
|
|
||||||
.selectr-empty,
|
|
||||||
.has-selected .selectr-placeholder {
|
|
||||||
display: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
.has-selected .selectr-label {
|
|
||||||
display: block;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* TAGGABLE */
|
|
||||||
.taggable .selectr-selected {
|
|
||||||
padding: 4px 28px 4px 4px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.taggable .selectr-selected::after {
|
|
||||||
display: table;
|
|
||||||
content: " ";
|
|
||||||
clear: both;
|
|
||||||
}
|
|
||||||
|
|
||||||
.taggable .selectr-label {
|
|
||||||
width: auto;
|
|
||||||
}
|
|
||||||
|
|
||||||
.taggable .selectr-tags {
|
|
||||||
float: left;
|
|
||||||
display: block;
|
|
||||||
}
|
|
||||||
|
|
||||||
.taggable .selectr-placeholder {
|
|
||||||
display: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
.input-tag {
|
|
||||||
float: left;
|
|
||||||
min-width: 90px;
|
|
||||||
width: auto;
|
|
||||||
}
|
|
||||||
|
|
||||||
.selectr-tag-input {
|
|
||||||
border: medium none;
|
|
||||||
padding: 3px 10px;
|
|
||||||
width: 100%;
|
|
||||||
font-family: inherit;
|
|
||||||
font-weight: inherit;
|
|
||||||
font-size: inherit;
|
|
||||||
}
|
|
||||||
|
|
||||||
.selectr-input-container.loading::after {
|
|
||||||
position: absolute;
|
|
||||||
top: 50%;
|
|
||||||
right: 20px;
|
|
||||||
width: 20px;
|
|
||||||
height: 20px;
|
|
||||||
content: '';
|
|
||||||
-o-transform: translate3d(0px, -50%, 0px);
|
|
||||||
-ms-transform: translate3d(0px, -50%, 0px);
|
|
||||||
-moz-transform: translate3d(0px, -50%, 0px);
|
|
||||||
-webkit-transform: translate3d(0px, -50%, 0px);
|
|
||||||
transform: translate3d(0px, -50%, 0px);
|
|
||||||
|
|
||||||
-o-transform-origin: 50% 0 0;
|
|
||||||
-ms-transform-origin: 50% 0 0;
|
|
||||||
-moz-transform-origin: 50% 0 0;
|
|
||||||
-webkit-transform-origin: 50% 0 0;
|
|
||||||
transform-origin: 50% 0 0;
|
|
||||||
|
|
||||||
-moz-animation: 500ms linear 0s normal forwards infinite running spin;
|
|
||||||
-webkit-animation: 500ms linear 0s normal forwards infinite running spin;
|
|
||||||
animation: 500ms linear 0s normal forwards infinite running spin;
|
|
||||||
border-width: 3px;
|
|
||||||
border-style: solid;
|
|
||||||
border-color: #aaa #ddd #ddd;
|
|
||||||
border-radius: 50%;
|
|
||||||
}
|
|
||||||
|
|
||||||
@-webkit-keyframes spin {
|
|
||||||
0% {
|
|
||||||
-webkit-transform: rotate(0deg) translate3d(0px, -50%, 0px);
|
|
||||||
transform: rotate(0deg) translate3d(0px, -50%, 0px);
|
|
||||||
}
|
|
||||||
100% {
|
|
||||||
-webkit-transform: rotate(360deg) translate3d(0px, -50%, 0px);
|
|
||||||
transform: rotate(360deg) translate3d(0px, -50%, 0px);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@keyframes spin {
|
|
||||||
0% {
|
|
||||||
-webkit-transform: rotate(0deg) translate3d(0px, -50%, 0px);
|
|
||||||
transform: rotate(0deg) translate3d(0px, -50%, 0px);
|
|
||||||
}
|
|
||||||
100% {
|
|
||||||
-webkit-transform: rotate(360deg) translate3d(0px, -50%, 0px);
|
|
||||||
transform: rotate(360deg) translate3d(0px, -50%, 0px);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
.selectr-container.open.inverted .selectr-selected {
|
|
||||||
border-color: transparent $jse-grey $jse-grey;
|
|
||||||
border-radius: 0 0 3px 3px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.selectr-container.inverted .selectr-options-container {
|
|
||||||
border-width: 1px 1px 0;
|
|
||||||
border-color: $jse-grey $jse-grey transparent;
|
|
||||||
border-radius: 3px 3px 0 0;
|
|
||||||
background-color: $jse-white;
|
|
||||||
}
|
|
||||||
|
|
||||||
.selectr-container.inverted .selectr-options-container {
|
|
||||||
top: auto;
|
|
||||||
bottom: calc(100% - 1px);
|
|
||||||
}
|
|
||||||
|
|
||||||
.selectr-container ::-webkit-input-placeholder {
|
|
||||||
color: #6c7a86;
|
|
||||||
opacity: 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
.selectr-container ::-moz-placeholder {
|
|
||||||
color: #6c7a86;
|
|
||||||
opacity: 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
.selectr-container :-ms-input-placeholder {
|
|
||||||
color: #6c7a86;
|
|
||||||
opacity: 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
.selectr-container ::placeholder {
|
|
||||||
color: #6c7a86;
|
|
||||||
opacity: 1;
|
|
||||||
}
|
|
|
@ -1,381 +0,0 @@
|
||||||
'use strict'
|
|
||||||
|
|
||||||
const defaultFilterFunction = {
|
|
||||||
start: function (token, match, config) {
|
|
||||||
return match.indexOf(token) === 0
|
|
||||||
},
|
|
||||||
contain: function (token, match, config) {
|
|
||||||
return match.indexOf(token) > -1
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export function autocomplete (config) {
|
|
||||||
config = config || {}
|
|
||||||
config.filter = config.filter || 'start'
|
|
||||||
config.trigger = config.trigger || 'keydown'
|
|
||||||
config.confirmKeys = config.confirmKeys || [39, 35, 9] // right, end, tab
|
|
||||||
config.caseSensitive = config.caseSensitive || false // autocomplete case sensitive
|
|
||||||
|
|
||||||
let fontSize = ''
|
|
||||||
let fontFamily = ''
|
|
||||||
|
|
||||||
const wrapper = document.createElement('div')
|
|
||||||
wrapper.style.position = 'relative'
|
|
||||||
wrapper.style.outline = '0'
|
|
||||||
wrapper.style.border = '0'
|
|
||||||
wrapper.style.margin = '0'
|
|
||||||
wrapper.style.padding = '0'
|
|
||||||
|
|
||||||
const dropDown = document.createElement('div')
|
|
||||||
dropDown.className = 'autocomplete dropdown'
|
|
||||||
dropDown.style.position = 'absolute'
|
|
||||||
dropDown.style.visibility = 'hidden'
|
|
||||||
|
|
||||||
let spacer
|
|
||||||
let leftSide // <-- it will contain the leftSide part of the textfield (the bit that was already autocompleted)
|
|
||||||
const createDropDownController = (elem, rs) => {
|
|
||||||
let rows = []
|
|
||||||
let ix = 0
|
|
||||||
let oldIndex = -1
|
|
||||||
|
|
||||||
// TODO: move this styling in JS to SCSS
|
|
||||||
const onMouseOver = function () { this.style.backgroundColor = '#ddd' }
|
|
||||||
const onMouseOut = function () { this.style.backgroundColor = '' }
|
|
||||||
const onMouseDown = function () { p.hide(); p.onmouseselection(this.__hint, p.rs) }
|
|
||||||
|
|
||||||
var p = {
|
|
||||||
rs: rs,
|
|
||||||
hide: function () {
|
|
||||||
elem.style.visibility = 'hidden'
|
|
||||||
// rs.hideDropDown();
|
|
||||||
},
|
|
||||||
refresh: function (token, array) {
|
|
||||||
elem.style.visibility = 'hidden'
|
|
||||||
ix = 0
|
|
||||||
elem.innerHTML = ''
|
|
||||||
const vph = (window.innerHeight || document.documentElement.clientHeight)
|
|
||||||
const rect = elem.parentNode.getBoundingClientRect()
|
|
||||||
const distanceToTop = rect.top - 6 // heuristic give 6px
|
|
||||||
const distanceToBottom = vph - rect.bottom - 6 // distance from the browser border.
|
|
||||||
|
|
||||||
rows = []
|
|
||||||
const filterFn = typeof config.filter === 'function' ? config.filter : defaultFilterFunction[config.filter]
|
|
||||||
|
|
||||||
const filtered = !filterFn ? [] : array.filter(match => filterFn(config.caseSensitive ? token : token.toLowerCase(), config.caseSensitive ? match : match.toLowerCase(), config))
|
|
||||||
|
|
||||||
rows = filtered.map(row => {
|
|
||||||
const divRow = document.createElement('div')
|
|
||||||
divRow.className = 'item'
|
|
||||||
// divRow.style.color = config.color;
|
|
||||||
divRow.onmouseover = onMouseOver
|
|
||||||
divRow.onmouseout = onMouseOut
|
|
||||||
divRow.onmousedown = onMouseDown
|
|
||||||
divRow.__hint = row
|
|
||||||
divRow.innerHTML = row.substring(0, token.length) + '<b>' + row.substring(token.length) + '</b>'
|
|
||||||
elem.appendChild(divRow)
|
|
||||||
return divRow
|
|
||||||
})
|
|
||||||
|
|
||||||
if (rows.length === 0) {
|
|
||||||
return // nothing to show.
|
|
||||||
}
|
|
||||||
if (rows.length === 1 && ((token.toLowerCase() === rows[0].__hint.toLowerCase() && !config.caseSensitive) ||
|
|
||||||
(token === rows[0].__hint && config.caseSensitive))) {
|
|
||||||
return // do not show the dropDown if it has only one element which matches what we have just displayed.
|
|
||||||
}
|
|
||||||
|
|
||||||
if (rows.length < 2) return
|
|
||||||
p.highlight(0)
|
|
||||||
|
|
||||||
if (distanceToTop > distanceToBottom * 3) { // Heuristic (only when the distance to the to top is 4 times more than distance to the bottom
|
|
||||||
elem.style.maxHeight = distanceToTop + 'px' // we display the dropDown on the top of the input text
|
|
||||||
elem.style.top = ''
|
|
||||||
elem.style.bottom = '100%'
|
|
||||||
} else {
|
|
||||||
elem.style.top = '100%'
|
|
||||||
elem.style.bottom = ''
|
|
||||||
elem.style.maxHeight = distanceToBottom + 'px'
|
|
||||||
}
|
|
||||||
elem.style.visibility = 'visible'
|
|
||||||
},
|
|
||||||
highlight: function (index) {
|
|
||||||
if (oldIndex !== -1 && rows[oldIndex]) {
|
|
||||||
rows[oldIndex].className = 'item'
|
|
||||||
}
|
|
||||||
rows[index].className = 'item hover'
|
|
||||||
oldIndex = index
|
|
||||||
},
|
|
||||||
move: function (step) { // moves the selection either up or down (unless it's not possible) step is either +1 or -1.
|
|
||||||
if (elem.style.visibility === 'hidden') return '' // nothing to move if there is no dropDown. (this happens if the user hits escape and then down or up)
|
|
||||||
if (ix + step === -1 || ix + step === rows.length) return rows[ix].__hint // NO CIRCULAR SCROLLING.
|
|
||||||
ix += step
|
|
||||||
p.highlight(ix)
|
|
||||||
return rows[ix].__hint// txtShadow.value = uRows[uIndex].__hint ;
|
|
||||||
},
|
|
||||||
onmouseselection: function () { } // it will be overwritten.
|
|
||||||
}
|
|
||||||
return p
|
|
||||||
}
|
|
||||||
|
|
||||||
function setEndOfContenteditable (contentEditableElement) {
|
|
||||||
let range, selection
|
|
||||||
if (document.createRange) {
|
|
||||||
// Firefox, Chrome, Opera, Safari, IE 9+
|
|
||||||
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
|
|
||||||
} else if (document.selection) {
|
|
||||||
// IE 8 and lower
|
|
||||||
range = document.body.createTextRange()// Create a range (a range is a like the selection but invisible)
|
|
||||||
range.moveToElementText(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
|
|
||||||
range.select()// Select the range (make it the visible selection
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function calculateWidthForText (text) {
|
|
||||||
if (spacer === undefined) { // on first call only.
|
|
||||||
spacer = document.createElement('span')
|
|
||||||
spacer.style.visibility = 'hidden'
|
|
||||||
spacer.style.position = 'fixed'
|
|
||||||
spacer.style.outline = '0'
|
|
||||||
spacer.style.margin = '0'
|
|
||||||
spacer.style.padding = '0'
|
|
||||||
spacer.style.border = '0'
|
|
||||||
spacer.style.left = '0'
|
|
||||||
spacer.style.whiteSpace = 'pre'
|
|
||||||
spacer.style.fontSize = fontSize
|
|
||||||
spacer.style.fontFamily = fontFamily
|
|
||||||
spacer.style.fontWeight = 'normal'
|
|
||||||
document.body.appendChild(spacer)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Used to encode an HTML string into a plain text.
|
|
||||||
// taken from http://stackoverflow.com/questions/1219860/javascript-jquery-html-encoding
|
|
||||||
spacer.innerHTML = String(text).replace(/&/g, '&')
|
|
||||||
.replace(/"/g, '"')
|
|
||||||
.replace(/'/g, ''')
|
|
||||||
.replace(/</g, '<')
|
|
||||||
.replace(/>/g, '>')
|
|
||||||
return spacer.getBoundingClientRect().right
|
|
||||||
}
|
|
||||||
|
|
||||||
const rs = {
|
|
||||||
onArrowDown: function () { }, // defaults to no action.
|
|
||||||
onArrowUp: function () { }, // defaults to no action.
|
|
||||||
onEnter: function () { }, // defaults to no action.
|
|
||||||
onTab: function () { }, // defaults to no action.
|
|
||||||
startFrom: 0,
|
|
||||||
options: [],
|
|
||||||
element: null,
|
|
||||||
elementHint: null,
|
|
||||||
elementStyle: null,
|
|
||||||
wrapper: wrapper, // Only to allow easy access to the HTML elements to the final user (possibly for minor customizations)
|
|
||||||
show: function (element, startPos, options) {
|
|
||||||
this.startFrom = startPos
|
|
||||||
this.wrapper.remove()
|
|
||||||
if (this.elementHint) {
|
|
||||||
this.elementHint.remove()
|
|
||||||
this.elementHint = null
|
|
||||||
}
|
|
||||||
|
|
||||||
if (fontSize === '') {
|
|
||||||
fontSize = window.getComputedStyle(element).getPropertyValue('font-size')
|
|
||||||
}
|
|
||||||
if (fontFamily === '') {
|
|
||||||
fontFamily = window.getComputedStyle(element).getPropertyValue('font-family')
|
|
||||||
}
|
|
||||||
|
|
||||||
dropDown.style.marginLeft = '0'
|
|
||||||
dropDown.style.marginTop = element.getBoundingClientRect().height + 'px'
|
|
||||||
this.options = options.map(String)
|
|
||||||
|
|
||||||
if (this.element !== element) {
|
|
||||||
this.element = element
|
|
||||||
this.elementStyle = {
|
|
||||||
zIndex: this.element.style.zIndex,
|
|
||||||
position: this.element.style.position,
|
|
||||||
backgroundColor: this.element.style.backgroundColor,
|
|
||||||
borderColor: this.element.style.borderColor
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
this.element.style.zIndex = 3
|
|
||||||
this.element.style.position = 'relative'
|
|
||||||
this.element.style.backgroundColor = 'transparent'
|
|
||||||
this.element.style.borderColor = 'transparent'
|
|
||||||
|
|
||||||
this.elementHint = element.cloneNode()
|
|
||||||
this.elementHint.className = 'autocomplete hint'
|
|
||||||
this.elementHint.style.zIndex = 2
|
|
||||||
this.elementHint.style.position = 'absolute'
|
|
||||||
this.elementHint.onfocus = () => { this.element.focus() }
|
|
||||||
|
|
||||||
if (this.element.addEventListener) {
|
|
||||||
this.element.removeEventListener('keydown', keyDownHandler)
|
|
||||||
this.element.addEventListener('keydown', keyDownHandler, false)
|
|
||||||
this.element.removeEventListener('blur', onBlurHandler)
|
|
||||||
this.element.addEventListener('blur', onBlurHandler, false)
|
|
||||||
}
|
|
||||||
|
|
||||||
wrapper.appendChild(this.elementHint)
|
|
||||||
wrapper.appendChild(dropDown)
|
|
||||||
element.parentElement.appendChild(wrapper)
|
|
||||||
|
|
||||||
this.repaint(element)
|
|
||||||
},
|
|
||||||
setText: function (text) {
|
|
||||||
this.element.innerText = text
|
|
||||||
},
|
|
||||||
getText: function () {
|
|
||||||
return this.element.innerText
|
|
||||||
},
|
|
||||||
hideDropDown: function () {
|
|
||||||
this.wrapper.remove()
|
|
||||||
if (this.elementHint) {
|
|
||||||
this.elementHint.remove()
|
|
||||||
this.elementHint = null
|
|
||||||
dropDownController.hide()
|
|
||||||
this.element.style.zIndex = this.elementStyle.zIndex
|
|
||||||
this.element.style.position = this.elementStyle.position
|
|
||||||
this.element.style.backgroundColor = this.elementStyle.backgroundColor
|
|
||||||
this.element.style.borderColor = this.elementStyle.borderColor
|
|
||||||
}
|
|
||||||
},
|
|
||||||
repaint: function (element) {
|
|
||||||
let text = element.innerText
|
|
||||||
text = text.replace('\n', '')
|
|
||||||
|
|
||||||
const optionsLength = this.options.length
|
|
||||||
|
|
||||||
// breaking text in leftSide and token.
|
|
||||||
|
|
||||||
const token = text.substring(this.startFrom)
|
|
||||||
leftSide = text.substring(0, this.startFrom)
|
|
||||||
|
|
||||||
for (let i = 0; i < optionsLength; i++) {
|
|
||||||
const opt = this.options[i]
|
|
||||||
if ((!config.caseSensitive && opt.toLowerCase().indexOf(token.toLowerCase()) === 0) ||
|
|
||||||
(config.caseSensitive && opt.indexOf(token) === 0)) { // <-- how about upperCase vs. lowercase
|
|
||||||
this.elementHint.innerText = leftSide + token + opt.substring(token.length)
|
|
||||||
this.elementHint.realInnerText = leftSide + opt
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// moving the dropDown and refreshing it.
|
|
||||||
dropDown.style.left = calculateWidthForText(leftSide) + 'px'
|
|
||||||
dropDownController.refresh(token, this.options)
|
|
||||||
this.elementHint.style.width = calculateWidthForText(this.elementHint.innerText) + 10 + 'px'
|
|
||||||
const wasDropDownHidden = (dropDown.style.visibility === 'hidden')
|
|
||||||
if (!wasDropDownHidden) { this.elementHint.style.width = calculateWidthForText(this.elementHint.innerText) + dropDown.clientWidth + 'px' }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
var dropDownController = createDropDownController(dropDown, rs)
|
|
||||||
|
|
||||||
var keyDownHandler = function (e) {
|
|
||||||
// console.log("Keydown:" + e.keyCode);
|
|
||||||
e = e || window.event
|
|
||||||
const keyCode = e.keyCode
|
|
||||||
|
|
||||||
if (this.elementHint == null) return
|
|
||||||
|
|
||||||
if (keyCode === 33) { return } // page up (do nothing)
|
|
||||||
if (keyCode === 34) { return } // page down (do nothing);
|
|
||||||
|
|
||||||
if (keyCode === 27) { // escape
|
|
||||||
rs.hideDropDown()
|
|
||||||
rs.element.focus()
|
|
||||||
e.preventDefault()
|
|
||||||
e.stopPropagation()
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
let text = this.element.innerText
|
|
||||||
text = text.replace('\n', '')
|
|
||||||
|
|
||||||
if (config.confirmKeys.indexOf(keyCode) >= 0) { // (autocomplete triggered)
|
|
||||||
if (keyCode === 9) {
|
|
||||||
if (this.elementHint.innerText.length === 0) {
|
|
||||||
rs.onTab()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (this.elementHint.innerText.length > 0) { // if there is a hint
|
|
||||||
if (this.element.innerText !== this.elementHint.realInnerText) {
|
|
||||||
this.element.innerText = this.elementHint.realInnerText
|
|
||||||
rs.hideDropDown()
|
|
||||||
setEndOfContenteditable(this.element)
|
|
||||||
if (keyCode === 9) {
|
|
||||||
rs.element.focus()
|
|
||||||
e.preventDefault()
|
|
||||||
e.stopPropagation()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
if (keyCode === 13) { // enter (autocomplete triggered)
|
|
||||||
if (this.elementHint.innerText.length === 0) { // if there is a hint
|
|
||||||
rs.onEnter()
|
|
||||||
} else {
|
|
||||||
const wasDropDownHidden = (dropDown.style.visibility === 'hidden')
|
|
||||||
dropDownController.hide()
|
|
||||||
|
|
||||||
if (wasDropDownHidden) {
|
|
||||||
rs.hideDropDown()
|
|
||||||
rs.element.focus()
|
|
||||||
rs.onEnter()
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
this.element.innerText = this.elementHint.realInnerText
|
|
||||||
rs.hideDropDown()
|
|
||||||
setEndOfContenteditable(this.element)
|
|
||||||
e.preventDefault()
|
|
||||||
e.stopPropagation()
|
|
||||||
}
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
if (keyCode === 40) { // down
|
|
||||||
const token = text.substring(this.startFrom)
|
|
||||||
const m = dropDownController.move(+1)
|
|
||||||
if (m === '') { rs.onArrowDown() }
|
|
||||||
this.elementHint.innerText = leftSide + token + m.substring(token.length)
|
|
||||||
this.elementHint.realInnerText = leftSide + m
|
|
||||||
e.preventDefault()
|
|
||||||
e.stopPropagation()
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
if (keyCode === 38) { // up
|
|
||||||
const token = text.substring(this.startFrom)
|
|
||||||
const m = dropDownController.move(-1)
|
|
||||||
if (m === '') { rs.onArrowUp() }
|
|
||||||
this.elementHint.innerText = leftSide + token + m.substring(token.length)
|
|
||||||
this.elementHint.realInnerText = leftSide + m
|
|
||||||
e.preventDefault()
|
|
||||||
e.stopPropagation()
|
|
||||||
}
|
|
||||||
}.bind(rs)
|
|
||||||
|
|
||||||
var onBlurHandler = e => {
|
|
||||||
rs.hideDropDown()
|
|
||||||
// console.log("Lost focus.");
|
|
||||||
}
|
|
||||||
|
|
||||||
dropDownController.onmouseselection = (text, rs) => {
|
|
||||||
rs.element.innerText = rs.elementHint.innerText = leftSide + text
|
|
||||||
rs.hideDropDown()
|
|
||||||
window.setTimeout(() => {
|
|
||||||
rs.element.focus()
|
|
||||||
setEndOfContenteditable(rs.element)
|
|
||||||
}, 1)
|
|
||||||
}
|
|
||||||
|
|
||||||
return rs
|
|
||||||
}
|
|
|
@ -1,5 +0,0 @@
|
||||||
|
|
||||||
export const DEFAULT_MODAL_ANCHOR = document.body
|
|
||||||
export const SIZE_LARGE = 10 * 1024 * 1024 // 10 MB
|
|
||||||
export const MAX_PREVIEW_CHARACTERS = 20000
|
|
||||||
export const PREVIEW_HISTORY_LIMIT = 2 * 1024 * 1024 * 1024 // 2 GB
|
|
|
@ -1,99 +0,0 @@
|
||||||
import { isChildOf, removeEventListener, addEventListener } from './util'
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Create an anchor element absolutely positioned in the `parent`
|
|
||||||
* element.
|
|
||||||
* @param {HTMLElement} anchor
|
|
||||||
* @param {HTMLElement} parent
|
|
||||||
* @param {function(HTMLElement)} [onDestroy] Callback when the anchor is destroyed
|
|
||||||
* @param {boolean} [destroyOnMouseOut=false] If true, anchor will be removed on mouse out
|
|
||||||
* @returns {HTMLElement}
|
|
||||||
*/
|
|
||||||
export function createAbsoluteAnchor (anchor, parent, onDestroy, destroyOnMouseOut = false) {
|
|
||||||
const root = getRootNode(anchor)
|
|
||||||
const eventListeners = {}
|
|
||||||
|
|
||||||
const anchorRect = anchor.getBoundingClientRect()
|
|
||||||
const parentRect = parent.getBoundingClientRect()
|
|
||||||
|
|
||||||
const absoluteAnchor = document.createElement('div')
|
|
||||||
absoluteAnchor.className = 'jsoneditor-anchor'
|
|
||||||
absoluteAnchor.style.position = 'absolute'
|
|
||||||
absoluteAnchor.style.left = (anchorRect.left - parentRect.left) + 'px'
|
|
||||||
absoluteAnchor.style.top = (anchorRect.top - parentRect.top) + 'px'
|
|
||||||
absoluteAnchor.style.width = (anchorRect.width - 2) + 'px'
|
|
||||||
absoluteAnchor.style.height = (anchorRect.height - 2) + 'px'
|
|
||||||
absoluteAnchor.style.boxSizing = 'border-box'
|
|
||||||
parent.appendChild(absoluteAnchor)
|
|
||||||
|
|
||||||
function destroy () {
|
|
||||||
// remove temporary absolutely positioned anchor
|
|
||||||
if (absoluteAnchor && absoluteAnchor.parentNode) {
|
|
||||||
absoluteAnchor.parentNode.removeChild(absoluteAnchor)
|
|
||||||
|
|
||||||
// remove all event listeners
|
|
||||||
// all event listeners are supposed to be attached to document.
|
|
||||||
for (const name in eventListeners) {
|
|
||||||
if (hasOwnProperty(eventListeners, name)) {
|
|
||||||
const fn = eventListeners[name]
|
|
||||||
if (fn) {
|
|
||||||
removeEventListener(root, name, fn)
|
|
||||||
}
|
|
||||||
delete eventListeners[name]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (typeof onDestroy === 'function') {
|
|
||||||
onDestroy(anchor)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function isOutside (target) {
|
|
||||||
return (target !== absoluteAnchor) && !isChildOf(target, absoluteAnchor)
|
|
||||||
}
|
|
||||||
|
|
||||||
// create and attach event listeners
|
|
||||||
function destroyIfOutside (event) {
|
|
||||||
if (isOutside(event.target)) {
|
|
||||||
destroy()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
eventListeners.mousedown = addEventListener(root, 'mousedown', destroyIfOutside)
|
|
||||||
eventListeners.mousewheel = addEventListener(root, 'mousewheel', destroyIfOutside)
|
|
||||||
|
|
||||||
if (destroyOnMouseOut) {
|
|
||||||
let destroyTimer = null
|
|
||||||
|
|
||||||
absoluteAnchor.onmouseover = () => {
|
|
||||||
clearTimeout(destroyTimer)
|
|
||||||
destroyTimer = null
|
|
||||||
}
|
|
||||||
|
|
||||||
absoluteAnchor.onmouseout = () => {
|
|
||||||
if (!destroyTimer) {
|
|
||||||
destroyTimer = setTimeout(destroy, 200)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
absoluteAnchor.destroy = destroy
|
|
||||||
|
|
||||||
return absoluteAnchor
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Node.getRootNode shim
|
|
||||||
* @param {HTMLElement} node node to check
|
|
||||||
* @return {HTMLElement} node's rootNode or `window` if there is ShadowDOM is not supported.
|
|
||||||
*/
|
|
||||||
function getRootNode (node) {
|
|
||||||
return (typeof node.getRootNode === 'function')
|
|
||||||
? node.getRootNode()
|
|
||||||
: window
|
|
||||||
}
|
|
||||||
|
|
||||||
function hasOwnProperty (object, key) {
|
|
||||||
return Object.prototype.hasOwnProperty.call(object, key)
|
|
||||||
}
|
|
|
@ -1 +0,0 @@
|
||||||
*
|
|
|
@ -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-2020 Jos de Jong, http://jsoneditoronline.org
|
|
||||||
*
|
|
||||||
* @author Jos de Jong, <wjosdejong@gmail.com>
|
|
||||||
* @version @@version
|
|
||||||
* @date @@date
|
|
||||||
*/
|
|
598
src/js/i18n.js
598
src/js/i18n.js
|
@ -1,598 +0,0 @@
|
||||||
'use strict'
|
|
||||||
|
|
||||||
/* eslint-disable no-template-curly-in-string */
|
|
||||||
|
|
||||||
import './polyfills'
|
|
||||||
|
|
||||||
const _locales = ['en', 'pt-BR', 'zh-CN', 'tr', 'ja', 'fr-FR']
|
|
||||||
const _defs = {
|
|
||||||
en: {
|
|
||||||
array: 'Array',
|
|
||||||
auto: 'Auto',
|
|
||||||
appendText: 'Append',
|
|
||||||
appendTitle: 'Append a new field with type \'auto\' after this field (Ctrl+Shift+Ins)',
|
|
||||||
appendSubmenuTitle: 'Select the type of the field to be appended',
|
|
||||||
appendTitleAuto: 'Append a new field with type \'auto\' (Ctrl+Shift+Ins)',
|
|
||||||
ascending: 'Ascending',
|
|
||||||
ascendingTitle: 'Sort the childs of this ${type} in ascending order',
|
|
||||||
actionsMenu: 'Click to open the actions menu (Ctrl+M)',
|
|
||||||
cannotParseFieldError: 'Cannot parse field into JSON',
|
|
||||||
cannotParseValueError: 'Cannot parse value into JSON',
|
|
||||||
collapseAll: 'Collapse all fields',
|
|
||||||
compactTitle: 'Compact JSON data, remove all whitespaces (Ctrl+Shift+\\)',
|
|
||||||
descending: 'Descending',
|
|
||||||
descendingTitle: 'Sort the childs of this ${type} in descending order',
|
|
||||||
drag: 'Drag to move this field (Alt+Shift+Arrows)',
|
|
||||||
duplicateKey: 'duplicate key',
|
|
||||||
duplicateText: 'Duplicate',
|
|
||||||
duplicateTitle: 'Duplicate selected fields (Ctrl+D)',
|
|
||||||
duplicateField: 'Duplicate this field (Ctrl+D)',
|
|
||||||
duplicateFieldError: 'Duplicate field name',
|
|
||||||
empty: 'empty',
|
|
||||||
expandAll: 'Expand all fields',
|
|
||||||
expandTitle: 'Click to expand/collapse this field (Ctrl+E). \n' +
|
|
||||||
'Ctrl+Click to expand/collapse including all childs.',
|
|
||||||
formatTitle: 'Format JSON data, with proper indentation and line feeds (Ctrl+\\)',
|
|
||||||
insert: 'Insert',
|
|
||||||
insertTitle: 'Insert a new field with type \'auto\' before this field (Ctrl+Ins)',
|
|
||||||
insertSub: 'Select the type of the field to be inserted',
|
|
||||||
object: 'Object',
|
|
||||||
ok: 'Ok',
|
|
||||||
redo: 'Redo (Ctrl+Shift+Z)',
|
|
||||||
removeText: 'Remove',
|
|
||||||
removeTitle: 'Remove selected fields (Ctrl+Del)',
|
|
||||||
removeField: 'Remove this field (Ctrl+Del)',
|
|
||||||
repairTitle: 'Repair JSON: fix quotes and escape characters, remove comments and JSONP notation, turn JavaScript objects into JSON.',
|
|
||||||
searchTitle: 'Search fields and values',
|
|
||||||
searchNextResultTitle: 'Next result (Enter)',
|
|
||||||
searchPreviousResultTitle: 'Previous result (Shift + Enter)',
|
|
||||||
selectNode: 'Select a node...',
|
|
||||||
showAll: 'show all',
|
|
||||||
showMore: 'show more',
|
|
||||||
showMoreStatus: 'displaying ${visibleChilds} of ${totalChilds} items.',
|
|
||||||
sort: 'Sort',
|
|
||||||
sortTitle: 'Sort the childs of this ${type}',
|
|
||||||
sortTitleShort: 'Sort contents',
|
|
||||||
sortFieldLabel: 'Field:',
|
|
||||||
sortDirectionLabel: 'Direction:',
|
|
||||||
sortFieldTitle: 'Select the nested field by which to sort the array or object',
|
|
||||||
sortAscending: 'Ascending',
|
|
||||||
sortAscendingTitle: 'Sort the selected field in ascending order',
|
|
||||||
sortDescending: 'Descending',
|
|
||||||
sortDescendingTitle: 'Sort the selected field in descending order',
|
|
||||||
string: 'String',
|
|
||||||
transform: 'Transform',
|
|
||||||
transformTitle: 'Filter, sort, or transform the childs of this ${type}',
|
|
||||||
transformTitleShort: 'Filter, sort, or transform contents',
|
|
||||||
extract: 'Extract',
|
|
||||||
extractTitle: 'Extract this ${type}',
|
|
||||||
transformQueryTitle: 'Enter a JMESPath query',
|
|
||||||
transformWizardLabel: 'Wizard',
|
|
||||||
transformWizardFilter: 'Filter',
|
|
||||||
transformWizardSortBy: 'Sort by',
|
|
||||||
transformWizardSelectFields: 'Select fields',
|
|
||||||
transformQueryLabel: 'Query',
|
|
||||||
transformPreviewLabel: 'Preview',
|
|
||||||
type: 'Type',
|
|
||||||
typeTitle: 'Change the type of this field',
|
|
||||||
openUrl: 'Ctrl+Click or Ctrl+Enter to open url in new window',
|
|
||||||
undo: 'Undo last action (Ctrl+Z)',
|
|
||||||
validationCannotMove: 'Cannot move a field into a child of itself',
|
|
||||||
autoType: 'Field type "auto". ' +
|
|
||||||
'The field type is automatically determined from the value ' +
|
|
||||||
'and can be a string, number, boolean, or null.',
|
|
||||||
objectType: 'Field type "object". ' +
|
|
||||||
'An object contains an unordered set of key/value pairs.',
|
|
||||||
arrayType: 'Field type "array". ' +
|
|
||||||
'An array contains an ordered collection of values.',
|
|
||||||
stringType: 'Field type "string". ' +
|
|
||||||
'Field type is not determined from the value, ' +
|
|
||||||
'but always returned as string.',
|
|
||||||
modeEditorTitle: 'Switch Editor Mode',
|
|
||||||
modeCodeText: 'Code',
|
|
||||||
modeCodeTitle: 'Switch to code highlighter',
|
|
||||||
modeFormText: 'Form',
|
|
||||||
modeFormTitle: 'Switch to form editor',
|
|
||||||
modeTextText: 'Text',
|
|
||||||
modeTextTitle: 'Switch to plain text editor',
|
|
||||||
modeTreeText: 'Tree',
|
|
||||||
modeTreeTitle: 'Switch to tree editor',
|
|
||||||
modeViewText: 'View',
|
|
||||||
modeViewTitle: 'Switch to tree view',
|
|
||||||
modePreviewText: 'Preview',
|
|
||||||
modePreviewTitle: 'Switch to preview mode',
|
|
||||||
examples: 'Examples',
|
|
||||||
default: 'Default'
|
|
||||||
},
|
|
||||||
'zh-CN': {
|
|
||||||
array: '数组',
|
|
||||||
auto: '自动',
|
|
||||||
appendText: '追加',
|
|
||||||
appendTitle: '在此字段后追加一个类型为“auto”的新字段 (Ctrl+Shift+Ins)',
|
|
||||||
appendSubmenuTitle: '选择要追加的字段类型',
|
|
||||||
appendTitleAuto: '追加类型为“auto”的新字段 (Ctrl+Shift+Ins)',
|
|
||||||
ascending: '升序',
|
|
||||||
ascendingTitle: '升序排列${type}的子节点',
|
|
||||||
actionsMenu: '点击打开动作菜单(Ctrl+M)',
|
|
||||||
cannotParseFieldError: '无法将字段解析为JSON',
|
|
||||||
cannotParseValueError: '无法将值解析为JSON',
|
|
||||||
collapseAll: '缩进所有字段',
|
|
||||||
compactTitle: '压缩JSON数据,删除所有空格 (Ctrl+Shift+\\)',
|
|
||||||
descending: '降序',
|
|
||||||
descendingTitle: '降序排列${type}的子节点',
|
|
||||||
drag: '拖拽移动该节点(Alt+Shift+Arrows)',
|
|
||||||
duplicateKey: '重复键',
|
|
||||||
duplicateText: '复制',
|
|
||||||
duplicateTitle: '复制选中字段(Ctrl+D)',
|
|
||||||
duplicateField: '复制该字段(Ctrl+D)',
|
|
||||||
duplicateFieldError: '重复的字段名称',
|
|
||||||
empty: '清空',
|
|
||||||
expandAll: '展开所有字段',
|
|
||||||
expandTitle: '点击 展开/收缩 该字段(Ctrl+E). \n' +
|
|
||||||
'Ctrl+Click 展开/收缩 包含所有子节点.',
|
|
||||||
formatTitle: '使用适当的缩进和换行符格式化JSON数据 (Ctrl+\\)',
|
|
||||||
insert: '插入',
|
|
||||||
insertTitle: '在此字段前插入类型为“auto”的新字段 (Ctrl+Ins)',
|
|
||||||
insertSub: '选择要插入的字段类型',
|
|
||||||
object: '对象',
|
|
||||||
ok: 'Ok',
|
|
||||||
redo: '重做 (Ctrl+Shift+Z)',
|
|
||||||
removeText: '移除',
|
|
||||||
removeTitle: '移除选中字段 (Ctrl+Del)',
|
|
||||||
removeField: '移除该字段 (Ctrl+Del)',
|
|
||||||
repairTitle: '修复JSON:修复引号和转义符,删除注释和JSONP表示法,将JavaScript对象转换为JSON。',
|
|
||||||
selectNode: '选择一个节点...',
|
|
||||||
showAll: '展示全部',
|
|
||||||
showMore: '展示更多',
|
|
||||||
showMoreStatus: '显示${totalChilds}的${visibleChilds}项目.',
|
|
||||||
sort: '排序',
|
|
||||||
sortTitle: '排序${type}的子节点',
|
|
||||||
sortTitleShort: '内容排序',
|
|
||||||
sortFieldLabel: '字段:',
|
|
||||||
sortDirectionLabel: '方向:',
|
|
||||||
sortFieldTitle: '选择用于对数组或对象排序的嵌套字段',
|
|
||||||
sortAscending: '升序排序',
|
|
||||||
sortAscendingTitle: '按照该字段升序排序',
|
|
||||||
sortDescending: '降序排序',
|
|
||||||
sortDescendingTitle: '按照该字段降序排序',
|
|
||||||
string: '字符串',
|
|
||||||
transform: '变换',
|
|
||||||
transformTitle: '筛选,排序,或者转换${type}的子节点',
|
|
||||||
transformTitleShort: '筛选,排序,或者转换内容',
|
|
||||||
extract: '提取',
|
|
||||||
extractTitle: '提取这个 ${type}',
|
|
||||||
transformQueryTitle: '输入JMESPath查询',
|
|
||||||
transformWizardLabel: '向导',
|
|
||||||
transformWizardFilter: '筛选',
|
|
||||||
transformWizardSortBy: '排序',
|
|
||||||
transformWizardSelectFields: '选择字段',
|
|
||||||
transformQueryLabel: '查询',
|
|
||||||
transformPreviewLabel: '预览',
|
|
||||||
type: '类型',
|
|
||||||
typeTitle: '更改字段类型',
|
|
||||||
openUrl: 'Ctrl+Click 或者 Ctrl+Enter 在新窗口打开链接',
|
|
||||||
undo: '撤销上次动作 (Ctrl+Z)',
|
|
||||||
validationCannotMove: '无法将字段移入其子节点',
|
|
||||||
autoType: '字段类型 "auto". ' +
|
|
||||||
'字段类型由值自动确定 ' +
|
|
||||||
'可以为 string,number,boolean,或者 null.',
|
|
||||||
objectType: '字段类型 "object". ' +
|
|
||||||
'对象包含一组无序的键/值对.',
|
|
||||||
arrayType: '字段类型 "array". ' +
|
|
||||||
'数组包含值的有序集合.',
|
|
||||||
stringType: '字段类型 "string". ' +
|
|
||||||
'字段类型由值自动确定,' +
|
|
||||||
'但始终作为字符串返回.',
|
|
||||||
modeCodeText: '代码',
|
|
||||||
modeCodeTitle: '切换至代码高亮',
|
|
||||||
modeFormText: '表单',
|
|
||||||
modeFormTitle: '切换至表单编辑',
|
|
||||||
modeTextText: '文本',
|
|
||||||
modeTextTitle: '切换至文本编辑',
|
|
||||||
modeTreeText: '树',
|
|
||||||
modeTreeTitle: '切换至树编辑',
|
|
||||||
modeViewText: '视图',
|
|
||||||
modeViewTitle: '切换至树视图',
|
|
||||||
modePreviewText: '预览',
|
|
||||||
modePreviewTitle: '切换至预览模式',
|
|
||||||
examples: '例子',
|
|
||||||
default: '缺省'
|
|
||||||
},
|
|
||||||
'pt-BR': {
|
|
||||||
array: 'Lista',
|
|
||||||
auto: 'Automatico',
|
|
||||||
appendText: 'Adicionar',
|
|
||||||
appendTitle: 'Adicionar novo campo com tipo \'auto\' depois deste campo (Ctrl+Shift+Ins)',
|
|
||||||
appendSubmenuTitle: 'Selecione o tipo do campo a ser adicionado',
|
|
||||||
appendTitleAuto: 'Adicionar novo campo com tipo \'auto\' (Ctrl+Shift+Ins)',
|
|
||||||
ascending: 'Ascendente',
|
|
||||||
ascendingTitle: 'Organizar filhor do tipo ${type} em crescente',
|
|
||||||
actionsMenu: 'Clique para abrir o menu de ações (Ctrl+M)',
|
|
||||||
cannotParseFieldError: 'Não é possível analisar o campo no JSON',
|
|
||||||
cannotParseValueError: 'Não é possível analisar o valor em JSON',
|
|
||||||
collapseAll: 'Fechar todos campos',
|
|
||||||
compactTitle: 'Dados JSON compactos, remova todos os espaços em branco (Ctrl+Shift+\\)',
|
|
||||||
descending: 'Descendente',
|
|
||||||
descendingTitle: 'Organizar o filhos do tipo ${type} em decrescente',
|
|
||||||
duplicateKey: 'chave duplicada',
|
|
||||||
drag: 'Arraste para mover este campo (Alt+Shift+Arrows)',
|
|
||||||
duplicateText: 'Duplicar',
|
|
||||||
duplicateTitle: 'Duplicar campos selecionados (Ctrl+D)',
|
|
||||||
duplicateField: 'Duplicar este campo (Ctrl+D)',
|
|
||||||
duplicateFieldError: 'Nome do campo duplicado',
|
|
||||||
empty: 'vazio',
|
|
||||||
expandAll: 'Expandir todos campos',
|
|
||||||
expandTitle: 'Clique para expandir/encolher este campo (Ctrl+E). \n' +
|
|
||||||
'Ctrl+Click para expandir/encolher incluindo todos os filhos.',
|
|
||||||
formatTitle: 'Formate dados JSON, com recuo e feeds de linha adequados (Ctrl+\\)',
|
|
||||||
insert: 'Inserir',
|
|
||||||
insertTitle: 'Inserir um novo campo do tipo \'auto\' antes deste campo (Ctrl+Ins)',
|
|
||||||
insertSub: 'Selecionar o tipo de campo a ser inserido',
|
|
||||||
object: 'Objeto',
|
|
||||||
ok: 'Ok',
|
|
||||||
redo: 'Refazer (Ctrl+Shift+Z)',
|
|
||||||
removeText: 'Remover',
|
|
||||||
removeTitle: 'Remover campos selecionados (Ctrl+Del)',
|
|
||||||
removeField: 'Remover este campo (Ctrl+Del)',
|
|
||||||
repairTitle: 'Repare JSON: corrija aspas e caracteres de escape, remova comentários e notação JSONP, transforme objetos JavaScript em JSON.',
|
|
||||||
selectNode: 'Selecione um nódulo...',
|
|
||||||
showAll: 'mostrar todos',
|
|
||||||
showMore: 'mostrar mais',
|
|
||||||
showMoreStatus: 'exibindo ${visibleChilds} de ${totalChilds} itens.',
|
|
||||||
sort: 'Organizar',
|
|
||||||
sortTitle: 'Organizar os filhos deste ${type}',
|
|
||||||
sortTitleShort: 'Organizar os filhos',
|
|
||||||
sortFieldLabel: 'Campo:',
|
|
||||||
sortDirectionLabel: 'Direção:',
|
|
||||||
sortFieldTitle: 'Selecione um campo filho pelo qual ordenar o array ou objeto',
|
|
||||||
sortAscending: 'Ascendente',
|
|
||||||
sortAscendingTitle: 'Ordenar o campo selecionado por ordem ascendente',
|
|
||||||
sortDescending: 'Descendente',
|
|
||||||
sortDescendingTitle: 'Ordenar o campo selecionado por ordem descendente',
|
|
||||||
string: 'Texto',
|
|
||||||
transform: 'Transformar',
|
|
||||||
transformTitle: 'Filtrar, ordenar ou transformar os filhos deste ${type}',
|
|
||||||
transformTitleShort: 'Filtrar, ordenar ou transformar conteúdos',
|
|
||||||
transformQueryTitle: 'Insira uma expressão JMESPath',
|
|
||||||
transformWizardLabel: 'Assistente',
|
|
||||||
transformWizardFilter: 'Filtro',
|
|
||||||
transformWizardSortBy: 'Ordenar por',
|
|
||||||
transformWizardSelectFields: 'Selecionar campos',
|
|
||||||
transformQueryLabel: 'Expressão',
|
|
||||||
transformPreviewLabel: 'Visualizar',
|
|
||||||
type: 'Tipo',
|
|
||||||
typeTitle: 'Mudar o tipo deste campo',
|
|
||||||
openUrl: 'Ctrl+Click ou Ctrl+Enter para abrir link em nova janela',
|
|
||||||
undo: 'Desfazer último ação (Ctrl+Z)',
|
|
||||||
validationCannotMove: 'Não pode mover um campo como filho dele mesmo',
|
|
||||||
autoType: 'Campo do tipo "auto". ' +
|
|
||||||
'O tipo do campo é determinao automaticamente a partir do seu valor ' +
|
|
||||||
'e pode ser texto, número, verdade/falso ou nulo.',
|
|
||||||
objectType: 'Campo do tipo "objeto". ' +
|
|
||||||
'Um objeto contém uma lista de pares com chave e valor.',
|
|
||||||
arrayType: 'Campo do tipo "lista". ' +
|
|
||||||
'Uma lista contem uma coleção de valores ordenados.',
|
|
||||||
stringType: 'Campo do tipo "string". ' +
|
|
||||||
'Campo do tipo nao é determinado através do seu valor, ' +
|
|
||||||
'mas sempre retornara um texto.',
|
|
||||||
examples: 'Exemplos',
|
|
||||||
default: 'Revelia'
|
|
||||||
},
|
|
||||||
tr: {
|
|
||||||
array: 'Dizin',
|
|
||||||
auto: 'Otomatik',
|
|
||||||
appendText: 'Ekle',
|
|
||||||
appendTitle: 'Bu alanın altına \'otomatik\' tipinde yeni bir alan ekle (Ctrl+Shift+Ins)',
|
|
||||||
appendSubmenuTitle: 'Eklenecek alanın tipini seç',
|
|
||||||
appendTitleAuto: '\'Otomatik\' tipinde yeni bir alan ekle (Ctrl+Shift+Ins)',
|
|
||||||
ascending: 'Artan',
|
|
||||||
ascendingTitle: '${type}\'ın alt tiplerini artan düzende sırala',
|
|
||||||
actionsMenu: 'Aksiyon menüsünü açmak için tıklayın (Ctrl+M)',
|
|
||||||
collapseAll: 'Tüm alanları kapat',
|
|
||||||
descending: 'Azalan',
|
|
||||||
descendingTitle: '${type}\'ın alt tiplerini azalan düzende sırala',
|
|
||||||
drag: 'Bu alanı taşımak için sürükleyin (Alt+Shift+Arrows)',
|
|
||||||
duplicateKey: 'Var olan anahtar',
|
|
||||||
duplicateText: 'Aşağıya kopyala',
|
|
||||||
duplicateTitle: 'Seçili alanlardan bir daha oluştur (Ctrl+D)',
|
|
||||||
duplicateField: 'Bu alandan bir daha oluştur (Ctrl+D)',
|
|
||||||
duplicateFieldError: 'Duplicate field name',
|
|
||||||
cannotParseFieldError: 'Alan JSON\'a ayrıştırılamıyor',
|
|
||||||
cannotParseValueError: 'JSON\'a değer ayrıştırılamıyor',
|
|
||||||
empty: 'boş',
|
|
||||||
expandAll: 'Tüm alanları aç',
|
|
||||||
expandTitle: 'Bu alanı açmak/kapatmak için tıkla (Ctrl+E). \n' +
|
|
||||||
'Alt alanlarda dahil tüm alanları açmak için Ctrl+Click ',
|
|
||||||
insert: 'Ekle',
|
|
||||||
insertTitle: 'Bu alanın üstüne \'otomatik\' tipinde yeni bir alan ekle (Ctrl+Ins)',
|
|
||||||
insertSub: 'Araya eklenecek alanın tipini seç',
|
|
||||||
object: 'Nesne',
|
|
||||||
ok: 'Tamam',
|
|
||||||
redo: 'Yeniden yap (Ctrl+Shift+Z)',
|
|
||||||
removeText: 'Kaldır',
|
|
||||||
removeTitle: 'Seçilen alanları kaldır (Ctrl+Del)',
|
|
||||||
removeField: 'Bu alanı kaldır (Ctrl+Del)',
|
|
||||||
selectNode: 'Bir nesne seç...',
|
|
||||||
showAll: 'tümünü göster',
|
|
||||||
showMore: 'daha fazla göster',
|
|
||||||
showMoreStatus: '${totalChilds} alanın ${visibleChilds} alt alanları gösteriliyor',
|
|
||||||
sort: 'Sırala',
|
|
||||||
sortTitle: '${type}\'ın alt alanlarını sırala',
|
|
||||||
sortTitleShort: 'İçerikleri sırala',
|
|
||||||
sortFieldLabel: 'Alan:',
|
|
||||||
sortDirectionLabel: 'Yön:',
|
|
||||||
sortFieldTitle: 'Diziyi veya nesneyi sıralamak için iç içe geçmiş alanı seçin',
|
|
||||||
sortAscending: 'Artan',
|
|
||||||
sortAscendingTitle: 'Seçili alanı artan düzende sırala',
|
|
||||||
sortDescending: 'Azalan',
|
|
||||||
sortDescendingTitle: 'Seçili alanı azalan düzende sırala',
|
|
||||||
string: 'Karakter Dizisi',
|
|
||||||
transform: 'Dönüştür',
|
|
||||||
transformTitle: '${type}\'ın alt alanlarını filtrele, sırala veya dönüştür',
|
|
||||||
transformTitleShort: 'İçerikleri filterele, sırala veya dönüştür',
|
|
||||||
transformQueryTitle: 'JMESPath sorgusu gir',
|
|
||||||
transformWizardLabel: 'Sihirbaz',
|
|
||||||
transformWizardFilter: 'Filtre',
|
|
||||||
transformWizardSortBy: 'Sırala',
|
|
||||||
transformWizardSelectFields: 'Alanları seç',
|
|
||||||
transformQueryLabel: 'Sorgu',
|
|
||||||
transformPreviewLabel: 'Önizleme',
|
|
||||||
type: 'Tip',
|
|
||||||
typeTitle: 'Bu alanın tipini değiştir',
|
|
||||||
openUrl: 'URL\'i yeni bir pencerede açmak için Ctrl+Click veya Ctrl+Enter',
|
|
||||||
undo: 'Son değişikliği geri al (Ctrl+Z)',
|
|
||||||
validationCannotMove: 'Alt alan olarak taşınamıyor',
|
|
||||||
autoType: 'Alan tipi "otomatik". ' +
|
|
||||||
'Alan türü otomatik olarak değerden belirlenir' +
|
|
||||||
've bir dize, sayı, boolean veya null olabilir.',
|
|
||||||
objectType: 'Alan tipi "nesne". ' +
|
|
||||||
'Bir nesne, sıralanmamış bir anahtar / değer çifti kümesi içerir.',
|
|
||||||
arrayType: 'Alan tipi "dizi". ' +
|
|
||||||
'Bir dizi, düzenli değerler koleksiyonu içerir.',
|
|
||||||
stringType: 'Alan tipi "karakter dizisi". ' +
|
|
||||||
'Alan türü değerden belirlenmez,' +
|
|
||||||
'ancak her zaman karakter dizisi olarak döndürülür.',
|
|
||||||
modeCodeText: 'Kod',
|
|
||||||
modeCodeTitle: 'Kod vurgulayıcıya geç',
|
|
||||||
modeFormText: 'Form',
|
|
||||||
modeFormTitle: 'Form düzenleyiciye geç',
|
|
||||||
modeTextText: 'Metin',
|
|
||||||
modeTextTitle: 'Düz metin düzenleyiciye geç',
|
|
||||||
modeTreeText: 'Ağaç',
|
|
||||||
modeTreeTitle: 'Ağaç düzenleyiciye geç',
|
|
||||||
modeViewText: 'Görünüm',
|
|
||||||
modeViewTitle: 'Ağaç görünümüne geç',
|
|
||||||
examples: 'Örnekler',
|
|
||||||
default: 'Varsayılan'
|
|
||||||
},
|
|
||||||
ja: {
|
|
||||||
array: '配列',
|
|
||||||
auto: 'オート',
|
|
||||||
appendText: '追加',
|
|
||||||
appendTitle: '次のフィールドに"オート"のフィールドを追加 (Ctrl+Shift+Ins)',
|
|
||||||
appendSubmenuTitle: '追加するフィールドの型を選択してください',
|
|
||||||
appendTitleAuto: '"オート"のフィールドを追加 (Ctrl+Shift+Ins)',
|
|
||||||
ascending: '昇順',
|
|
||||||
ascendingTitle: '${type}の子要素を昇順に並べ替え',
|
|
||||||
actionsMenu: 'クリックしてアクションメニューを開く (Ctrl+M)',
|
|
||||||
collapseAll: 'すべてを折りたたむ',
|
|
||||||
descending: '降順',
|
|
||||||
descendingTitle: '${type}の子要素を降順に並べ替え',
|
|
||||||
drag: 'ドラッグして選択中のフィールドを移動 (Alt+Shift+Arrows)',
|
|
||||||
duplicateKey: '複製キー',
|
|
||||||
duplicateText: '複製',
|
|
||||||
duplicateTitle: '選択中のフィールドを複製 (Ctrl+D)',
|
|
||||||
duplicateField: '選択中のフィールドを複製 (Ctrl+D)',
|
|
||||||
duplicateFieldError: 'フィールド名が重複しています',
|
|
||||||
cannotParseFieldError: 'JSONのフィールドを解析できません',
|
|
||||||
cannotParseValueError: 'JSONの値を解析できません',
|
|
||||||
empty: '空',
|
|
||||||
expandAll: 'すべてを展開',
|
|
||||||
expandTitle: 'クリックしてフィールドを展開/折りたたむ (Ctrl+E). \n' +
|
|
||||||
'Ctrl+Click ですべての子要素を展開/折りたたむ',
|
|
||||||
insert: '挿入',
|
|
||||||
insertTitle: '選択中のフィールドの前に新しいフィールドを挿入 (Ctrl+Ins)',
|
|
||||||
insertSub: '挿入するフィールドの型を選択',
|
|
||||||
object: 'オブジェクト',
|
|
||||||
ok: '実行',
|
|
||||||
redo: 'やり直す (Ctrl+Shift+Z)',
|
|
||||||
removeText: '削除',
|
|
||||||
removeTitle: '選択中のフィールドを削除 (Ctrl+Del)',
|
|
||||||
removeField: '選択中のフィールドを削除 (Ctrl+Del)',
|
|
||||||
selectNode: 'ノードを選択...',
|
|
||||||
showAll: 'すべてを表示',
|
|
||||||
showMore: 'もっと見る',
|
|
||||||
showMoreStatus: '${totalChilds}個のアイテムのうち ${visibleChilds}個を表示しています。',
|
|
||||||
sort: '並べ替え',
|
|
||||||
sortTitle: '${type}の子要素を並べ替え',
|
|
||||||
sortTitleShort: '並べ替え',
|
|
||||||
sortFieldLabel: 'フィールド:',
|
|
||||||
sortDirectionLabel: '順序:',
|
|
||||||
sortFieldTitle: '配列またはオブジェクトを並び替えるためのフィールドを選択',
|
|
||||||
sortAscending: '昇順',
|
|
||||||
sortAscendingTitle: '選択中のフィールドを昇順に並び替え',
|
|
||||||
sortDescending: '降順',
|
|
||||||
sortDescendingTitle: '選択中のフィールドを降順に並び替え',
|
|
||||||
string: '文字列',
|
|
||||||
transform: '変換',
|
|
||||||
transformTitle: '${type}の子要素をフィルター・並び替え・変換する',
|
|
||||||
transformTitleShort: '内容をフィルター・並び替え・変換する',
|
|
||||||
extract: '抽出',
|
|
||||||
extractTitle: '${type}を抽出',
|
|
||||||
transformQueryTitle: 'JMESPathクエリを入力',
|
|
||||||
transformWizardLabel: 'ウィザード',
|
|
||||||
transformWizardFilter: 'フィルター',
|
|
||||||
transformWizardSortBy: '並び替え',
|
|
||||||
transformWizardSelectFields: 'フィールドを選択',
|
|
||||||
transformQueryLabel: 'クエリ',
|
|
||||||
transformPreviewLabel: 'プレビュー',
|
|
||||||
type: '型',
|
|
||||||
typeTitle: '選択中のフィールドの型を変更',
|
|
||||||
openUrl: 'Ctrl+Click または Ctrl+Enter で 新規ウィンドウでURLを開く',
|
|
||||||
undo: '元に戻す (Ctrl+Z)',
|
|
||||||
validationCannotMove: '子要素に移動できません ',
|
|
||||||
autoType: 'オート: ' +
|
|
||||||
'フィールドの型は値から自動的に決定されます。 ' +
|
|
||||||
'(文字列・数値・ブール・null)',
|
|
||||||
objectType: 'オブジェクト: ' +
|
|
||||||
'オブジェクトは順序が決まっていないキーと値のペア組み合わせです。',
|
|
||||||
arrayType: '配列: ' +
|
|
||||||
'配列は順序が決まっている値の集合体です。',
|
|
||||||
stringType: '文字列: ' +
|
|
||||||
'フィールド型は値から決定されませんが、' +
|
|
||||||
'常に文字列として返されます。',
|
|
||||||
modeCodeText: 'コードモード',
|
|
||||||
modeCodeTitle: 'ハイライトモードに切り替え',
|
|
||||||
modeFormText: 'フォームモード',
|
|
||||||
modeFormTitle: 'フォームモードに切り替え',
|
|
||||||
modeTextText: 'テキストモード',
|
|
||||||
modeTextTitle: 'テキストモードに切り替え',
|
|
||||||
modeTreeText: 'ツリーモード',
|
|
||||||
modeTreeTitle: 'ツリーモードに切り替え',
|
|
||||||
modeViewText: 'ビューモード',
|
|
||||||
modeViewTitle: 'ビューモードに切り替え',
|
|
||||||
modePreviewText: 'プレビュー',
|
|
||||||
modePreviewTitle: 'プレビューに切り替え',
|
|
||||||
examples: '例',
|
|
||||||
default: 'デフォルト'
|
|
||||||
},
|
|
||||||
'fr-FR': {
|
|
||||||
array: 'Liste',
|
|
||||||
auto: 'Auto',
|
|
||||||
appendText: 'Ajouter',
|
|
||||||
appendTitle: 'Ajouter un champ de type \'auto\' après ce champ (Ctrl+Shift+Ins)',
|
|
||||||
appendSubmenuTitle: 'Sélectionner le type du champ à ajouter',
|
|
||||||
appendTitleAuto: 'Ajouter un champ de type \'auto\' (Ctrl+Shift+Ins)',
|
|
||||||
ascending: 'Ascendant',
|
|
||||||
ascendingTitle: 'Trier les enfants de ce ${type} par ordre ascendant',
|
|
||||||
actionsMenu: 'Ouvrir le menu des actions (Ctrl+M)',
|
|
||||||
collapseAll: 'Regrouper',
|
|
||||||
descending: 'Descendant',
|
|
||||||
descendingTitle: 'Trier les enfants de ce ${type} par ordre descendant',
|
|
||||||
drag: 'Déplacer (Alt+Shift+Arrows)',
|
|
||||||
duplicateKey: 'Dupliquer la clé',
|
|
||||||
duplicateText: 'Dupliquer',
|
|
||||||
duplicateTitle: 'Dupliquer les champs sélectionnés (Ctrl+D)',
|
|
||||||
duplicateField: 'Dupliquer ce champ (Ctrl+D)',
|
|
||||||
duplicateFieldError: 'Dupliquer le nom de champ',
|
|
||||||
cannotParseFieldError: 'Champ impossible à parser en JSON',
|
|
||||||
cannotParseValueError: 'Valeur impossible à parser en JSON',
|
|
||||||
empty: 'vide',
|
|
||||||
expandAll: 'Étendre',
|
|
||||||
expandTitle: 'Étendre/regrouper ce champ (Ctrl+E). \n' +
|
|
||||||
'Ctrl+Click pour étendre/regrouper avec tous les champs.',
|
|
||||||
insert: 'Insérer',
|
|
||||||
insertTitle: 'Insérer un champ de type \'auto\' avant ce champ (Ctrl+Ins)',
|
|
||||||
insertSub: 'Sélectionner le type de champ à insérer',
|
|
||||||
object: 'Objet',
|
|
||||||
ok: 'Ok',
|
|
||||||
redo: 'Rejouer (Ctrl+Shift+Z)',
|
|
||||||
removeText: 'Supprimer',
|
|
||||||
removeTitle: 'Supprimer les champs sélectionnés (Ctrl+Del)',
|
|
||||||
removeField: 'Supprimer ce champ (Ctrl+Del)',
|
|
||||||
searchTitle: 'Rechercher champs et valeurs',
|
|
||||||
searchNextResultTitle: 'Résultat suivant (Enter)',
|
|
||||||
searchPreviousResultTitle: 'Résultat précédent (Shift + Enter)',
|
|
||||||
selectNode: 'Sélectionner un nœud...',
|
|
||||||
showAll: 'voir tout',
|
|
||||||
showMore: 'voir plus',
|
|
||||||
showMoreStatus: '${visibleChilds} éléments affichés de ${totalChilds}.',
|
|
||||||
sort: 'Trier',
|
|
||||||
sortTitle: 'Trier les champs de ce ${type}',
|
|
||||||
sortTitleShort: 'Trier',
|
|
||||||
sortFieldLabel: 'Champ:',
|
|
||||||
sortDirectionLabel: 'Direction:',
|
|
||||||
sortFieldTitle: 'Sélectionner les champs permettant de trier les listes et objet',
|
|
||||||
sortAscending: 'Ascendant',
|
|
||||||
sortAscendingTitle: 'Trier les champs sélectionnés par ordre ascendant',
|
|
||||||
sortDescending: 'Descendant',
|
|
||||||
sortDescendingTitle: 'Trier les champs sélectionnés par ordre descendant',
|
|
||||||
string: 'Chaîne',
|
|
||||||
transform: 'Transformer',
|
|
||||||
transformTitle: 'Filtrer, trier, or transformer les enfants de ce ${type}',
|
|
||||||
transformTitleShort: 'Filtrer, trier ou transformer le contenu',
|
|
||||||
extract: 'Extraire',
|
|
||||||
extractTitle: 'Extraire ce ${type}',
|
|
||||||
transformQueryTitle: 'Saisir une requête JMESPath',
|
|
||||||
transformWizardLabel: 'Assistant',
|
|
||||||
transformWizardFilter: 'Filtrer',
|
|
||||||
transformWizardSortBy: 'Trier par',
|
|
||||||
transformWizardSelectFields: 'Sélectionner les champs',
|
|
||||||
transformQueryLabel: 'Requête',
|
|
||||||
transformPreviewLabel: 'Prévisualisation',
|
|
||||||
type: 'Type',
|
|
||||||
typeTitle: 'Changer le type de ce champ',
|
|
||||||
openUrl: 'Ctrl+Click ou Ctrl+Enter pour ouvrir l\'url dans une autre fenêtre',
|
|
||||||
undo: 'Annuler la dernière action (Ctrl+Z)',
|
|
||||||
validationCannotMove: 'Cannot move a field into a child of itself',
|
|
||||||
autoType: 'Champe de type "auto". ' +
|
|
||||||
'Ce type de champ est automatiquement déterminé en fonction de la valeur ' +
|
|
||||||
'et peut être de type "chaîne", "nombre", "booléen" ou null.',
|
|
||||||
objectType: 'Champ de type "objet". ' +
|
|
||||||
'Un objet contient un ensemble non ordonné de paires clé/valeur.',
|
|
||||||
arrayType: 'Champ de type "liste". ' +
|
|
||||||
'Une liste contient une collection ordonnée de valeurs.',
|
|
||||||
stringType: 'Champ de type "chaîne". ' +
|
|
||||||
'Ce type de champ n\'est pas déterminé en fonction de la valeur, ' +
|
|
||||||
'mais retourne systématiquement une chaîne de caractères.',
|
|
||||||
modeEditorTitle: 'Changer mode d\'édition',
|
|
||||||
modeCodeText: 'Code',
|
|
||||||
modeCodeTitle: 'Activer surlignage code',
|
|
||||||
modeFormText: 'Formulaire',
|
|
||||||
modeFormTitle: 'Activer formulaire',
|
|
||||||
modeTextText: 'Texte',
|
|
||||||
modeTextTitle: 'Activer éditeur texte',
|
|
||||||
modeTreeText: 'Arbre',
|
|
||||||
modeTreeTitle: 'Activer éditeur arbre',
|
|
||||||
modeViewText: 'Lecture seule',
|
|
||||||
modeViewTitle: 'Activer vue arbre',
|
|
||||||
modePreviewText: 'Prévisualisation',
|
|
||||||
modePreviewTitle: 'Activer mode prévisualiser',
|
|
||||||
examples: 'Exemples',
|
|
||||||
default: 'Défaut'
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const _defaultLang = 'en'
|
|
||||||
const userLang = typeof navigator !== 'undefined'
|
|
||||||
? navigator.language || navigator.userLanguage
|
|
||||||
: undefined
|
|
||||||
let _lang = _locales.find(l => l === userLang) || _defaultLang
|
|
||||||
|
|
||||||
export function setLanguage (lang) {
|
|
||||||
if (!lang) {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
const langFound = _locales.find(l => l === lang)
|
|
||||||
if (langFound) {
|
|
||||||
_lang = langFound
|
|
||||||
} else {
|
|
||||||
console.error('Language not found')
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export function setLanguages (languages) {
|
|
||||||
if (!languages) {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
for (const language in languages) {
|
|
||||||
const langFound = _locales.find(l => l === language)
|
|
||||||
if (!langFound) {
|
|
||||||
_locales.push(language)
|
|
||||||
}
|
|
||||||
_defs[language] = Object.assign({}, _defs[_defaultLang], _defs[language], languages[language])
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export function translate (key, data, lang) {
|
|
||||||
if (!lang) {
|
|
||||||
lang = _lang
|
|
||||||
}
|
|
||||||
let text = _defs[lang][key] || _defs[_defaultLang][key] || key
|
|
||||||
if (data) {
|
|
||||||
for (const dataKey in data) {
|
|
||||||
text = text.replace('${' + dataKey + '}', data[dataKey])
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return text
|
|
||||||
}
|
|
|
@ -1,75 +0,0 @@
|
||||||
import jmespath from 'jmespath'
|
|
||||||
import { get, parsePath, parseString } from './util'
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Build a JMESPath query based on query options coming from the wizard
|
|
||||||
* @param {JSON} json The JSON document for which to build the query.
|
|
||||||
* Used for context information like determining
|
|
||||||
* the type of values (string or number)
|
|
||||||
* @param {QueryOptions} queryOptions
|
|
||||||
* @return {string} Returns a query (as string)
|
|
||||||
*/
|
|
||||||
export function createQuery (json, queryOptions) {
|
|
||||||
const { sort, filter, projection } = queryOptions
|
|
||||||
let query = ''
|
|
||||||
|
|
||||||
if (filter) {
|
|
||||||
const examplePath = filter.field !== '@'
|
|
||||||
? ['0'].concat(parsePath('.' + filter.field))
|
|
||||||
: ['0']
|
|
||||||
const exampleValue = get(json, examplePath)
|
|
||||||
const value1 = typeof exampleValue === 'string'
|
|
||||||
? filter.value
|
|
||||||
: parseString(filter.value)
|
|
||||||
|
|
||||||
query += '[? ' +
|
|
||||||
filter.field + ' ' +
|
|
||||||
filter.relation + ' ' +
|
|
||||||
'`' + JSON.stringify(value1) + '`' +
|
|
||||||
']'
|
|
||||||
} else {
|
|
||||||
query += Array.isArray(json)
|
|
||||||
? '[*]'
|
|
||||||
: '@'
|
|
||||||
}
|
|
||||||
|
|
||||||
if (sort) {
|
|
||||||
if (sort.direction === 'desc') {
|
|
||||||
query += ' | reverse(sort_by(@, &' + sort.field + '))'
|
|
||||||
} else {
|
|
||||||
query += ' | sort_by(@, &' + sort.field + ')'
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (projection) {
|
|
||||||
if (query[query.length - 1] !== ']') {
|
|
||||||
query += ' | [*]'
|
|
||||||
}
|
|
||||||
|
|
||||||
if (projection.fields.length === 1) {
|
|
||||||
query += '.' + projection.fields[0]
|
|
||||||
} else if (projection.fields.length > 1) {
|
|
||||||
query += '.{' +
|
|
||||||
projection.fields.map(value => {
|
|
||||||
const parts = value.split('.')
|
|
||||||
const last = parts[parts.length - 1]
|
|
||||||
return last + ': ' + value
|
|
||||||
}).join(', ') +
|
|
||||||
'}'
|
|
||||||
} else { // values.length === 0
|
|
||||||
// ignore
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return query
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Execute a JMESPath query
|
|
||||||
* @param {JSON} json
|
|
||||||
* @param {string} query
|
|
||||||
* @return {JSON} Returns the transformed JSON
|
|
||||||
*/
|
|
||||||
export function executeQuery (json, query) {
|
|
||||||
return jmespath.search(json, query)
|
|
||||||
}
|
|
|
@ -1,197 +0,0 @@
|
||||||
'use strict'
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Convert part of a JSON object to a JSON string.
|
|
||||||
* Use case is to stringify a small part of a large JSON object so you can see
|
|
||||||
* a preview.
|
|
||||||
*
|
|
||||||
* @param {*} value
|
|
||||||
* The value to convert to a JSON string.
|
|
||||||
*
|
|
||||||
* @param {number | string | null} [space]
|
|
||||||
* A String or Number object that's used to insert white space into the output
|
|
||||||
* JSON string for readability purposes. If this is a Number, it indicates the
|
|
||||||
* number of space characters to use as white space; this number is capped at 10
|
|
||||||
* if it's larger than that. Values less than 1 indicate that no space should be
|
|
||||||
* used. If this is a String, the string (or the first 10 characters of the string,
|
|
||||||
* if it's longer than that) is used as white space. If this parameter is not
|
|
||||||
* provided (or is null), no white space is used.
|
|
||||||
*
|
|
||||||
* @param {number} [limit] Maximum size of the string output.
|
|
||||||
*
|
|
||||||
* @returns {string | undefined} Returns the string representation of the JSON object.
|
|
||||||
*/
|
|
||||||
export function stringifyPartial (value, space, limit) {
|
|
||||||
let _space // undefined by default
|
|
||||||
if (typeof space === 'number') {
|
|
||||||
if (space > 10) {
|
|
||||||
_space = repeat(' ', 10)
|
|
||||||
} else if (space >= 1) {
|
|
||||||
_space = repeat(' ', space)
|
|
||||||
}
|
|
||||||
// else ignore
|
|
||||||
} else if (typeof space === 'string' && space !== '') {
|
|
||||||
_space = space
|
|
||||||
}
|
|
||||||
|
|
||||||
const output = stringifyValue(value, _space, '', limit)
|
|
||||||
|
|
||||||
return output.length > limit
|
|
||||||
? (slice(output, limit) + '...')
|
|
||||||
: output
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Stringify a value
|
|
||||||
* @param {*} value
|
|
||||||
* @param {string} space
|
|
||||||
* @param {string} indent
|
|
||||||
* @param {number} limit
|
|
||||||
* @return {string | undefined}
|
|
||||||
*/
|
|
||||||
function stringifyValue (value, space, indent, limit) {
|
|
||||||
// boolean, null, number, string, or date
|
|
||||||
if (typeof value === 'boolean' || value instanceof Boolean ||
|
|
||||||
value === null ||
|
|
||||||
typeof value === 'number' || value instanceof Number ||
|
|
||||||
typeof value === 'string' || value instanceof String ||
|
|
||||||
value instanceof Date) {
|
|
||||||
return JSON.stringify(value)
|
|
||||||
}
|
|
||||||
|
|
||||||
// array
|
|
||||||
if (Array.isArray(value)) {
|
|
||||||
return stringifyArray(value, space, indent, limit)
|
|
||||||
}
|
|
||||||
|
|
||||||
// object (test lastly!)
|
|
||||||
if (value && typeof value === 'object') {
|
|
||||||
return stringifyObject(value, space, indent, limit)
|
|
||||||
}
|
|
||||||
|
|
||||||
return undefined
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Stringify an array
|
|
||||||
* @param {Array} array
|
|
||||||
* @param {string} space
|
|
||||||
* @param {string} indent
|
|
||||||
* @param {number} limit
|
|
||||||
* @return {string}
|
|
||||||
*/
|
|
||||||
function stringifyArray (array, space, indent, limit) {
|
|
||||||
const childIndent = space ? (indent + space) : undefined
|
|
||||||
let str = space ? '[\n' : '['
|
|
||||||
|
|
||||||
for (let i = 0; i < array.length; i++) {
|
|
||||||
const item = array[i]
|
|
||||||
|
|
||||||
if (space) {
|
|
||||||
str += childIndent
|
|
||||||
}
|
|
||||||
|
|
||||||
if (typeof item !== 'undefined' && typeof item !== 'function') {
|
|
||||||
str += stringifyValue(item, space, childIndent, limit)
|
|
||||||
} else {
|
|
||||||
str += 'null'
|
|
||||||
}
|
|
||||||
|
|
||||||
if (i < array.length - 1) {
|
|
||||||
str += space ? ',\n' : ','
|
|
||||||
}
|
|
||||||
|
|
||||||
// stop as soon as we're exceeding the limit
|
|
||||||
if (str.length > limit) {
|
|
||||||
return str + '...'
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
str += space ? ('\n' + indent + ']') : ']'
|
|
||||||
return str
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Stringify an object
|
|
||||||
* @param {Object} object
|
|
||||||
* @param {string} space
|
|
||||||
* @param {string} indent
|
|
||||||
* @param {number} limit
|
|
||||||
* @return {string}
|
|
||||||
*/
|
|
||||||
function stringifyObject (object, space, indent, limit) {
|
|
||||||
const childIndent = space ? (indent + space) : undefined
|
|
||||||
let first = true
|
|
||||||
let str = space ? '{\n' : '{'
|
|
||||||
|
|
||||||
if (typeof object.toJSON === 'function') {
|
|
||||||
return stringifyValue(object.toJSON(), space, indent, limit)
|
|
||||||
}
|
|
||||||
|
|
||||||
for (const key in object) {
|
|
||||||
if (hasOwnProperty(object, key)) {
|
|
||||||
const value = object[key]
|
|
||||||
|
|
||||||
if (first) {
|
|
||||||
first = false
|
|
||||||
} else {
|
|
||||||
str += space ? ',\n' : ','
|
|
||||||
}
|
|
||||||
|
|
||||||
str += space
|
|
||||||
? (childIndent + '"' + key + '": ')
|
|
||||||
: ('"' + key + '":')
|
|
||||||
|
|
||||||
str += stringifyValue(value, space, childIndent, limit)
|
|
||||||
|
|
||||||
// stop as soon as we're exceeding the limit
|
|
||||||
if (str.length > limit) {
|
|
||||||
return str + '...'
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
str += space ? ('\n' + indent + '}') : '}'
|
|
||||||
return str
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Repeat a string a number of times.
|
|
||||||
* Simple linear solution, we only need up to 10 iterations in practice
|
|
||||||
* @param {string} text
|
|
||||||
* @param {number} times
|
|
||||||
* @return {string}
|
|
||||||
*/
|
|
||||||
function repeat (text, times) {
|
|
||||||
let res = ''
|
|
||||||
while (times-- > 0) {
|
|
||||||
res += text
|
|
||||||
}
|
|
||||||
return res
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Limit the length of text
|
|
||||||
* @param {string} text
|
|
||||||
* @param {number} [limit]
|
|
||||||
* @return {string}
|
|
||||||
*/
|
|
||||||
function slice (text, limit) {
|
|
||||||
return typeof limit === 'number'
|
|
||||||
? text.slice(0, limit)
|
|
||||||
: text
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Test whether some text contains a JSON array, i.e. the first
|
|
||||||
* non-white space character is a [
|
|
||||||
* @param {string} jsonText
|
|
||||||
* @return {boolean}
|
|
||||||
*/
|
|
||||||
export function containsArray (jsonText) {
|
|
||||||
return /^\s*\[/.test(jsonText)
|
|
||||||
}
|
|
||||||
|
|
||||||
function hasOwnProperty (object, key) {
|
|
||||||
return Object.prototype.hasOwnProperty.call(object, key)
|
|
||||||
}
|
|
|
@ -1,54 +0,0 @@
|
||||||
|
|
||||||
if (typeof Element !== 'undefined') {
|
|
||||||
// Polyfill for array remove
|
|
||||||
(() => {
|
|
||||||
function polyfill (item) {
|
|
||||||
if ('remove' in item) {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
Object.defineProperty(item, 'remove', {
|
|
||||||
configurable: true,
|
|
||||||
enumerable: true,
|
|
||||||
writable: true,
|
|
||||||
value: function remove () {
|
|
||||||
if (this.parentNode !== undefined) { this.parentNode.removeChild(this) }
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
if (typeof window.Element !== 'undefined') { polyfill(window.Element.prototype) }
|
|
||||||
if (typeof window.CharacterData !== 'undefined') { polyfill(window.CharacterData.prototype) }
|
|
||||||
if (typeof window.DocumentType !== 'undefined') { polyfill(window.DocumentType.prototype) }
|
|
||||||
})()
|
|
||||||
}
|
|
||||||
|
|
||||||
// simple polyfill for Array.findIndex
|
|
||||||
if (!Array.prototype.findIndex) {
|
|
||||||
// eslint-disable-next-line no-extend-native
|
|
||||||
Array.prototype.findIndex = function (predicate) {
|
|
||||||
for (let i = 0; i < this.length; i++) {
|
|
||||||
const element = this[i]
|
|
||||||
if (predicate.call(this, element, i, this)) {
|
|
||||||
return i
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return -1
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Polyfill for Array.find
|
|
||||||
if (!Array.prototype.find) {
|
|
||||||
// eslint-disable-next-line no-extend-native
|
|
||||||
Array.prototype.find = function (predicate) {
|
|
||||||
const i = this.findIndex(predicate)
|
|
||||||
return this[i]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Polyfill for String.trim
|
|
||||||
if (!String.prototype.trim) {
|
|
||||||
// eslint-disable-next-line no-extend-native
|
|
||||||
String.prototype.trim = function () {
|
|
||||||
return this.replace(/^[\s\uFEFF\xA0]+|[\s\uFEFF\xA0]+$/g, '')
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,689 +0,0 @@
|
||||||
'use strict'
|
|
||||||
|
|
||||||
import { setLanguage, setLanguages, translate } from './i18n'
|
|
||||||
import { ModeSwitcher } from './ModeSwitcher'
|
|
||||||
import { ErrorTable } from './ErrorTable'
|
|
||||||
import { showSortModal } from './showSortModal'
|
|
||||||
import { showTransformModal } from './showTransformModal'
|
|
||||||
import { textModeMixins } from './textmode'
|
|
||||||
import { DEFAULT_MODAL_ANCHOR, MAX_PREVIEW_CHARACTERS, PREVIEW_HISTORY_LIMIT, SIZE_LARGE } from './constants'
|
|
||||||
import { FocusTracker } from './FocusTracker'
|
|
||||||
import {
|
|
||||||
addClassName,
|
|
||||||
debounce,
|
|
||||||
escapeUnicodeChars,
|
|
||||||
formatSize,
|
|
||||||
isObject,
|
|
||||||
limitCharacters,
|
|
||||||
parse,
|
|
||||||
removeClassName,
|
|
||||||
repair,
|
|
||||||
sort,
|
|
||||||
sortObjectKeys
|
|
||||||
} from './util'
|
|
||||||
import { History } from './History'
|
|
||||||
import { createQuery, executeQuery } from './jmespathQuery'
|
|
||||||
|
|
||||||
const textmode = textModeMixins[0].mixin
|
|
||||||
|
|
||||||
// create a mixin with the functions for text mode
|
|
||||||
const previewmode = {}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Create a JSON document preview, suitable for processing of large documents
|
|
||||||
* @param {Element} container
|
|
||||||
* @param {Object} [options] Object with options. See docs for details.
|
|
||||||
* @private
|
|
||||||
*/
|
|
||||||
previewmode.create = function (container, options = {}) {
|
|
||||||
if (typeof options.statusBar === 'undefined') {
|
|
||||||
options.statusBar = true
|
|
||||||
}
|
|
||||||
|
|
||||||
// setting default for previewmode
|
|
||||||
options.mainMenuBar = options.mainMenuBar !== false
|
|
||||||
options.enableSort = options.enableSort !== false
|
|
||||||
options.enableTransform = options.enableTransform !== false
|
|
||||||
options.createQuery = options.createQuery || createQuery
|
|
||||||
options.executeQuery = options.executeQuery || executeQuery
|
|
||||||
|
|
||||||
this.options = options
|
|
||||||
|
|
||||||
// indentation
|
|
||||||
if (typeof options.indentation === 'number') {
|
|
||||||
this.indentation = Number(options.indentation)
|
|
||||||
} else {
|
|
||||||
this.indentation = 2 // number of spaces
|
|
||||||
}
|
|
||||||
|
|
||||||
// language
|
|
||||||
setLanguages(this.options.languages)
|
|
||||||
setLanguage(this.options.language)
|
|
||||||
|
|
||||||
// determine mode
|
|
||||||
this.mode = 'preview'
|
|
||||||
|
|
||||||
const me = this
|
|
||||||
this.container = container
|
|
||||||
this.dom = {}
|
|
||||||
|
|
||||||
this.json = undefined
|
|
||||||
this.text = ''
|
|
||||||
|
|
||||||
// TODO: JSON Schema support
|
|
||||||
|
|
||||||
// create a debounced validate function
|
|
||||||
this._debouncedValidate = 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-preview'
|
|
||||||
this.frame.onclick = event => {
|
|
||||||
// prevent default submit action when the editor is located inside a form
|
|
||||||
event.preventDefault()
|
|
||||||
}
|
|
||||||
|
|
||||||
// setting the FocusTracker on 'this.frame' to track the editor's focus event
|
|
||||||
const focusTrackerConfig = {
|
|
||||||
target: this.frame,
|
|
||||||
onFocus: this.options.onFocus || null,
|
|
||||||
onBlur: this.options.onBlur || null
|
|
||||||
}
|
|
||||||
|
|
||||||
this.frameFocusTracker = new FocusTracker(focusTrackerConfig)
|
|
||||||
|
|
||||||
this.content = document.createElement('div')
|
|
||||||
this.content.className = 'jsoneditor-outer'
|
|
||||||
|
|
||||||
this.dom.busy = document.createElement('div')
|
|
||||||
this.dom.busy.className = 'jsoneditor-busy'
|
|
||||||
this.dom.busyContent = document.createElement('span')
|
|
||||||
this.dom.busyContent.innerHTML = 'busy...'
|
|
||||||
this.dom.busy.appendChild(this.dom.busyContent)
|
|
||||||
this.content.appendChild(this.dom.busy)
|
|
||||||
|
|
||||||
this.dom.previewContent = document.createElement('pre')
|
|
||||||
this.dom.previewContent.className = 'jsoneditor-preview'
|
|
||||||
this.dom.previewText = document.createTextNode('')
|
|
||||||
this.dom.previewContent.appendChild(this.dom.previewText)
|
|
||||||
this.content.appendChild(this.dom.previewContent)
|
|
||||||
|
|
||||||
if (this.options.mainMenuBar) {
|
|
||||||
addClassName(this.content, 'has-main-menu-bar')
|
|
||||||
|
|
||||||
// create menu
|
|
||||||
this.menu = document.createElement('div')
|
|
||||||
this.menu.className = 'jsoneditor-menu'
|
|
||||||
this.frame.appendChild(this.menu)
|
|
||||||
|
|
||||||
// create format button
|
|
||||||
const buttonFormat = document.createElement('button')
|
|
||||||
buttonFormat.type = 'button'
|
|
||||||
buttonFormat.className = 'jsoneditor-format'
|
|
||||||
buttonFormat.title = translate('formatTitle')
|
|
||||||
this.menu.appendChild(buttonFormat)
|
|
||||||
buttonFormat.onclick = function handleFormat () {
|
|
||||||
me.executeWithBusyMessage(() => {
|
|
||||||
try {
|
|
||||||
me.format()
|
|
||||||
} catch (err) {
|
|
||||||
me._onError(err)
|
|
||||||
}
|
|
||||||
}, 'formatting...')
|
|
||||||
}
|
|
||||||
|
|
||||||
// create compact button
|
|
||||||
const buttonCompact = document.createElement('button')
|
|
||||||
buttonCompact.type = 'button'
|
|
||||||
buttonCompact.className = 'jsoneditor-compact'
|
|
||||||
buttonCompact.title = translate('compactTitle')
|
|
||||||
this.menu.appendChild(buttonCompact)
|
|
||||||
buttonCompact.onclick = function handleCompact () {
|
|
||||||
me.executeWithBusyMessage(() => {
|
|
||||||
try {
|
|
||||||
me.compact()
|
|
||||||
} catch (err) {
|
|
||||||
me._onError(err)
|
|
||||||
}
|
|
||||||
}, 'compacting...')
|
|
||||||
}
|
|
||||||
|
|
||||||
// create sort button
|
|
||||||
if (this.options.enableSort) {
|
|
||||||
const sort = document.createElement('button')
|
|
||||||
sort.type = 'button'
|
|
||||||
sort.className = 'jsoneditor-sort'
|
|
||||||
sort.title = translate('sortTitleShort')
|
|
||||||
sort.onclick = () => {
|
|
||||||
me._showSortModal()
|
|
||||||
}
|
|
||||||
this.menu.appendChild(sort)
|
|
||||||
}
|
|
||||||
|
|
||||||
// create transform button
|
|
||||||
if (this.options.enableTransform) {
|
|
||||||
const transform = document.createElement('button')
|
|
||||||
transform.type = 'button'
|
|
||||||
transform.title = translate('transformTitleShort')
|
|
||||||
transform.className = 'jsoneditor-transform'
|
|
||||||
transform.onclick = () => {
|
|
||||||
me._showTransformModal()
|
|
||||||
}
|
|
||||||
this.dom.transform = transform
|
|
||||||
this.menu.appendChild(transform)
|
|
||||||
}
|
|
||||||
|
|
||||||
// create repair button
|
|
||||||
const buttonRepair = document.createElement('button')
|
|
||||||
buttonRepair.type = 'button'
|
|
||||||
buttonRepair.className = 'jsoneditor-repair'
|
|
||||||
buttonRepair.title = translate('repairTitle')
|
|
||||||
this.menu.appendChild(buttonRepair)
|
|
||||||
buttonRepair.onclick = () => {
|
|
||||||
if (me.json === undefined) { // only repair if we don't have valid JSON
|
|
||||||
me.executeWithBusyMessage(() => {
|
|
||||||
try {
|
|
||||||
me.repair()
|
|
||||||
} catch (err) {
|
|
||||||
me._onError(err)
|
|
||||||
}
|
|
||||||
}, 'repairing...')
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// create history and undo/redo buttons
|
|
||||||
if (this.options.history !== false) { // default option value is true
|
|
||||||
const onHistoryChange = () => {
|
|
||||||
me.dom.undo.disabled = !me.history.canUndo()
|
|
||||||
me.dom.redo.disabled = !me.history.canRedo()
|
|
||||||
}
|
|
||||||
|
|
||||||
const calculateItemSize = item => // times two to account for the json object
|
|
||||||
item.text.length * 2
|
|
||||||
|
|
||||||
this.history = new History(onHistoryChange, calculateItemSize, PREVIEW_HISTORY_LIMIT)
|
|
||||||
|
|
||||||
// create undo button
|
|
||||||
const undo = document.createElement('button')
|
|
||||||
undo.type = 'button'
|
|
||||||
undo.className = 'jsoneditor-undo jsoneditor-separator'
|
|
||||||
undo.title = translate('undo')
|
|
||||||
undo.onclick = () => {
|
|
||||||
const action = me.history.undo()
|
|
||||||
if (action) {
|
|
||||||
me._applyHistory(action)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
this.menu.appendChild(undo)
|
|
||||||
this.dom.undo = undo
|
|
||||||
|
|
||||||
// create redo button
|
|
||||||
const redo = document.createElement('button')
|
|
||||||
redo.type = 'button'
|
|
||||||
redo.className = 'jsoneditor-redo'
|
|
||||||
redo.title = translate('redo')
|
|
||||||
redo.onclick = () => {
|
|
||||||
const action = me.history.redo()
|
|
||||||
if (action) {
|
|
||||||
me._applyHistory(action)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
this.menu.appendChild(redo)
|
|
||||||
this.dom.redo = redo
|
|
||||||
|
|
||||||
// force enabling/disabling the undo/redo button
|
|
||||||
this.history.onChange()
|
|
||||||
}
|
|
||||||
|
|
||||||
// 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.errorTable = new ErrorTable({
|
|
||||||
errorTableVisible: true,
|
|
||||||
onToggleVisibility: function () {
|
|
||||||
me.validate()
|
|
||||||
},
|
|
||||||
onFocusLine: null,
|
|
||||||
onChangeHeight: function (height) {
|
|
||||||
// TODO: change CSS to using flex box, remove setting height using JavaScript
|
|
||||||
const statusBarHeight = me.dom.statusBar ? me.dom.statusBar.clientHeight : 0
|
|
||||||
const totalHeight = height + statusBarHeight + 1
|
|
||||||
me.content.style.marginBottom = (-totalHeight) + 'px'
|
|
||||||
me.content.style.paddingBottom = totalHeight + 'px'
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
this.frame.appendChild(this.content)
|
|
||||||
this.frame.appendChild(this.errorTable.getErrorTable())
|
|
||||||
this.container.appendChild(this.frame)
|
|
||||||
|
|
||||||
if (options.statusBar) {
|
|
||||||
addClassName(this.content, 'has-status-bar')
|
|
||||||
|
|
||||||
const statusBar = document.createElement('div')
|
|
||||||
this.dom.statusBar = statusBar
|
|
||||||
statusBar.className = 'jsoneditor-statusbar'
|
|
||||||
this.frame.appendChild(statusBar)
|
|
||||||
|
|
||||||
this.dom.fileSizeInfo = document.createElement('span')
|
|
||||||
this.dom.fileSizeInfo.className = 'jsoneditor-size-info'
|
|
||||||
this.dom.fileSizeInfo.innerText = ''
|
|
||||||
statusBar.appendChild(this.dom.fileSizeInfo)
|
|
||||||
|
|
||||||
this.dom.arrayInfo = document.createElement('span')
|
|
||||||
this.dom.arrayInfo.className = 'jsoneditor-size-info'
|
|
||||||
this.dom.arrayInfo.innerText = ''
|
|
||||||
statusBar.appendChild(this.dom.arrayInfo)
|
|
||||||
|
|
||||||
statusBar.appendChild(this.errorTable.getErrorCounter())
|
|
||||||
statusBar.appendChild(this.errorTable.getWarningIcon())
|
|
||||||
statusBar.appendChild(this.errorTable.getErrorIcon())
|
|
||||||
}
|
|
||||||
|
|
||||||
this._renderPreview()
|
|
||||||
|
|
||||||
this.setSchema(this.options.schema, this.options.schemaRefs)
|
|
||||||
}
|
|
||||||
|
|
||||||
previewmode._renderPreview = function () {
|
|
||||||
const text = this.getText()
|
|
||||||
|
|
||||||
this.dom.previewText.nodeValue = limitCharacters(text, MAX_PREVIEW_CHARACTERS)
|
|
||||||
|
|
||||||
if (this.dom.fileSizeInfo) {
|
|
||||||
this.dom.fileSizeInfo.innerText = 'Size: ' + formatSize(text.length)
|
|
||||||
}
|
|
||||||
|
|
||||||
if (this.dom.arrayInfo) {
|
|
||||||
if (Array.isArray(this.json)) {
|
|
||||||
this.dom.arrayInfo.innerText = ('Array: ' + this.json.length + ' items')
|
|
||||||
} else {
|
|
||||||
this.dom.arrayInfo.innerText = ''
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Handle a change:
|
|
||||||
* - Validate JSON schema
|
|
||||||
* - Send a callback to the onChange listener if provided
|
|
||||||
* @private
|
|
||||||
*/
|
|
||||||
previewmode._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)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// trigger the onChangeJSON callback
|
|
||||||
if (this.options.onChangeJSON) {
|
|
||||||
try {
|
|
||||||
this.options.onChangeJSON(this.get())
|
|
||||||
} catch (err) {
|
|
||||||
console.error('Error in onChangeJSON callback: ', err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// trigger the onChangeText callback
|
|
||||||
if (this.options.onChangeText) {
|
|
||||||
try {
|
|
||||||
this.options.onChangeText(this.getText())
|
|
||||||
} catch (err) {
|
|
||||||
console.error('Error in onChangeText callback: ', err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Open a sort modal
|
|
||||||
* @private
|
|
||||||
*/
|
|
||||||
previewmode._showSortModal = function () {
|
|
||||||
const me = this
|
|
||||||
|
|
||||||
function onSort (json, sortedBy) {
|
|
||||||
if (Array.isArray(json)) {
|
|
||||||
const sortedArray = sort(json, sortedBy.path, sortedBy.direction)
|
|
||||||
|
|
||||||
me.sortedBy = sortedBy
|
|
||||||
me._setAndFireOnChange(sortedArray)
|
|
||||||
}
|
|
||||||
|
|
||||||
if (isObject(json)) {
|
|
||||||
const sortedObject = sortObjectKeys(json, sortedBy.direction)
|
|
||||||
|
|
||||||
me.sortedBy = sortedBy
|
|
||||||
me._setAndFireOnChange(sortedObject)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
this.executeWithBusyMessage(() => {
|
|
||||||
const container = me.options.modalAnchor || DEFAULT_MODAL_ANCHOR
|
|
||||||
const json = me.get()
|
|
||||||
me._renderPreview() // update array count
|
|
||||||
|
|
||||||
showSortModal(container, json, sortedBy => {
|
|
||||||
me.executeWithBusyMessage(() => {
|
|
||||||
onSort(json, sortedBy)
|
|
||||||
}, 'sorting...')
|
|
||||||
}, me.sortedBy)
|
|
||||||
}, 'parsing...')
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Open a transform modal
|
|
||||||
* @private
|
|
||||||
*/
|
|
||||||
previewmode._showTransformModal = function () {
|
|
||||||
this.executeWithBusyMessage(() => {
|
|
||||||
const { createQuery, executeQuery, modalAnchor, queryDescription } = this.options
|
|
||||||
const json = this.get()
|
|
||||||
|
|
||||||
this._renderPreview() // update array count
|
|
||||||
|
|
||||||
showTransformModal({
|
|
||||||
anchor: modalAnchor || DEFAULT_MODAL_ANCHOR,
|
|
||||||
json,
|
|
||||||
queryDescription, // can be undefined
|
|
||||||
createQuery,
|
|
||||||
executeQuery,
|
|
||||||
onTransform: query => {
|
|
||||||
this.executeWithBusyMessage(() => {
|
|
||||||
const updatedJson = executeQuery(json, query)
|
|
||||||
this._setAndFireOnChange(updatedJson)
|
|
||||||
}, 'transforming...')
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}, 'parsing...')
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Destroy the editor. Clean up DOM, event listeners, and web workers.
|
|
||||||
*/
|
|
||||||
previewmode.destroy = function () {
|
|
||||||
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._debouncedValidate = null
|
|
||||||
|
|
||||||
if (this.history) {
|
|
||||||
this.history.clear()
|
|
||||||
this.history = null
|
|
||||||
}
|
|
||||||
|
|
||||||
// Removing the FocusTracker set to track the editor's focus event
|
|
||||||
this.frameFocusTracker.destroy()
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Compact the code in the text editor
|
|
||||||
*/
|
|
||||||
previewmode.compact = function () {
|
|
||||||
const json = this.get()
|
|
||||||
const text = JSON.stringify(json)
|
|
||||||
|
|
||||||
// we know that in this case the json is still the same, so we pass json too
|
|
||||||
this._setTextAndFireOnChange(text, json)
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Format the code in the text editor
|
|
||||||
*/
|
|
||||||
previewmode.format = function () {
|
|
||||||
const json = this.get()
|
|
||||||
const text = JSON.stringify(json, null, this.indentation)
|
|
||||||
|
|
||||||
// we know that in this case the json is still the same, so we pass json too
|
|
||||||
this._setTextAndFireOnChange(text, json)
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Repair the code in the text editor
|
|
||||||
*/
|
|
||||||
previewmode.repair = function () {
|
|
||||||
const text = this.getText()
|
|
||||||
const repairedText = repair(text)
|
|
||||||
|
|
||||||
this._setTextAndFireOnChange(repairedText)
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Set focus to the editor
|
|
||||||
*/
|
|
||||||
previewmode.focus = function () {
|
|
||||||
// we don't really have a place to focus,
|
|
||||||
// let's focus on the transform button
|
|
||||||
this.dom.transform.focus()
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Set json data in the editor
|
|
||||||
* @param {*} json
|
|
||||||
*/
|
|
||||||
previewmode.set = function (json) {
|
|
||||||
if (this.history) {
|
|
||||||
this.history.clear()
|
|
||||||
}
|
|
||||||
|
|
||||||
this._set(json)
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Update data. Same as calling `set` in text/code mode.
|
|
||||||
* @param {*} json
|
|
||||||
*/
|
|
||||||
previewmode.update = function (json) {
|
|
||||||
this._set(json)
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Set json data
|
|
||||||
* @param {*} json
|
|
||||||
*/
|
|
||||||
previewmode._set = function (json) {
|
|
||||||
this.text = undefined
|
|
||||||
this.json = json
|
|
||||||
|
|
||||||
this._renderPreview()
|
|
||||||
|
|
||||||
this._pushHistory()
|
|
||||||
|
|
||||||
// validate JSON schema
|
|
||||||
this._debouncedValidate()
|
|
||||||
}
|
|
||||||
|
|
||||||
previewmode._setAndFireOnChange = function (json) {
|
|
||||||
this._set(json)
|
|
||||||
this._onChange()
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Get json data
|
|
||||||
* @return {*} json
|
|
||||||
*/
|
|
||||||
previewmode.get = function () {
|
|
||||||
if (this.json === undefined) {
|
|
||||||
const text = this.getText()
|
|
||||||
|
|
||||||
this.json = parse(text) // this can throw an error
|
|
||||||
}
|
|
||||||
|
|
||||||
return this.json
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Get the text contents of the editor
|
|
||||||
* @return {String} jsonText
|
|
||||||
*/
|
|
||||||
previewmode.getText = function () {
|
|
||||||
if (this.text === undefined) {
|
|
||||||
this.text = JSON.stringify(this.json, null, this.indentation)
|
|
||||||
|
|
||||||
if (this.options.escapeUnicode === true) {
|
|
||||||
this.text = escapeUnicodeChars(this.text)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return this.text
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Set the text contents of the editor
|
|
||||||
* @param {String} jsonText
|
|
||||||
*/
|
|
||||||
previewmode.setText = function (jsonText) {
|
|
||||||
if (this.history) {
|
|
||||||
this.history.clear()
|
|
||||||
}
|
|
||||||
|
|
||||||
this._setText(jsonText)
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Update the text contents
|
|
||||||
* @param {string} jsonText
|
|
||||||
*/
|
|
||||||
previewmode.updateText = function (jsonText) {
|
|
||||||
// don't update if there are no changes
|
|
||||||
if (this.getText() === jsonText) {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
this._setText(jsonText)
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Set the text contents of the editor
|
|
||||||
* @param {string} jsonText
|
|
||||||
* @param {*} [json] Optional JSON instance of the text
|
|
||||||
* @private
|
|
||||||
*/
|
|
||||||
previewmode._setText = function (jsonText, json) {
|
|
||||||
if (this.options.escapeUnicode === true) {
|
|
||||||
this.text = escapeUnicodeChars(jsonText)
|
|
||||||
} else {
|
|
||||||
this.text = jsonText
|
|
||||||
}
|
|
||||||
this.json = json
|
|
||||||
|
|
||||||
this._renderPreview()
|
|
||||||
|
|
||||||
if (this.json === undefined) {
|
|
||||||
const me = this
|
|
||||||
this.executeWithBusyMessage(() => {
|
|
||||||
try {
|
|
||||||
// force parsing the json now, else it will be done in validate without feedback
|
|
||||||
me.json = me.get()
|
|
||||||
me._renderPreview()
|
|
||||||
me._pushHistory()
|
|
||||||
} catch (err) {
|
|
||||||
// no need to throw an error, validation will show an error
|
|
||||||
}
|
|
||||||
}, 'parsing...')
|
|
||||||
} else {
|
|
||||||
this._pushHistory()
|
|
||||||
}
|
|
||||||
|
|
||||||
this._debouncedValidate()
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Set text and fire onChange callback
|
|
||||||
* @param {string} jsonText
|
|
||||||
* @param {*} [json] Optional JSON instance of the text
|
|
||||||
* @private
|
|
||||||
*/
|
|
||||||
previewmode._setTextAndFireOnChange = function (jsonText, json) {
|
|
||||||
this._setText(jsonText, json)
|
|
||||||
this._onChange()
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Apply history to the current state
|
|
||||||
* @param {{json?: JSON, text?: string}} action
|
|
||||||
* @private
|
|
||||||
*/
|
|
||||||
previewmode._applyHistory = function (action) {
|
|
||||||
this.json = action.json
|
|
||||||
this.text = action.text
|
|
||||||
|
|
||||||
this._renderPreview()
|
|
||||||
|
|
||||||
this._debouncedValidate()
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Push the current state to history
|
|
||||||
* @private
|
|
||||||
*/
|
|
||||||
previewmode._pushHistory = function () {
|
|
||||||
if (!this.history) {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
const action = {
|
|
||||||
text: this.text,
|
|
||||||
json: this.json
|
|
||||||
}
|
|
||||||
|
|
||||||
this.history.add(action)
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Execute a heavy, blocking action.
|
|
||||||
* Before starting the action, show a message on screen like "parsing..."
|
|
||||||
* @param {function} fn
|
|
||||||
* @param {string} message
|
|
||||||
*/
|
|
||||||
previewmode.executeWithBusyMessage = function (fn, message) {
|
|
||||||
const size = this.getText().length
|
|
||||||
|
|
||||||
if (size > SIZE_LARGE) {
|
|
||||||
const me = this
|
|
||||||
addClassName(me.frame, 'busy')
|
|
||||||
me.dom.busyContent.innerText = message
|
|
||||||
|
|
||||||
setTimeout(() => {
|
|
||||||
fn()
|
|
||||||
removeClassName(me.frame, 'busy')
|
|
||||||
me.dom.busyContent.innerText = ''
|
|
||||||
}, 100)
|
|
||||||
} else {
|
|
||||||
fn()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// TODO: refactor into composable functions instead of this shaky mixin-like structure
|
|
||||||
previewmode.validate = textmode.validate
|
|
||||||
previewmode._renderErrors = textmode._renderErrors
|
|
||||||
|
|
||||||
// define modes
|
|
||||||
export const previewModeMixins = [
|
|
||||||
{
|
|
||||||
mode: 'preview',
|
|
||||||
mixin: previewmode,
|
|
||||||
data: 'json'
|
|
||||||
}
|
|
||||||
]
|
|
|
@ -1,154 +0,0 @@
|
||||||
'use strict'
|
|
||||||
|
|
||||||
import { translate } from './i18n'
|
|
||||||
|
|
||||||
/**
|
|
||||||
* A factory function to create an ShowMoreNode, which depends on a Node
|
|
||||||
* @param {function} Node
|
|
||||||
*/
|
|
||||||
export function showMoreNodeFactory (Node) {
|
|
||||||
/**
|
|
||||||
* @constructor ShowMoreNode
|
|
||||||
* @extends Node
|
|
||||||
* @param {TreeEditor} editor
|
|
||||||
* @param {Node} parent
|
|
||||||
* Create a new ShowMoreNode. This is a special node which is created
|
|
||||||
* for arrays or objects having more than 100 items
|
|
||||||
*/
|
|
||||||
function ShowMoreNode (editor, parent) {
|
|
||||||
/** @type {TreeEditor} */
|
|
||||||
this.editor = editor
|
|
||||||
this.parent = parent
|
|
||||||
this.dom = {}
|
|
||||||
}
|
|
||||||
|
|
||||||
ShowMoreNode.prototype = new Node()
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Return a table row with an append button.
|
|
||||||
* @return {Element} dom TR element
|
|
||||||
*/
|
|
||||||
ShowMoreNode.prototype.getDom = function () {
|
|
||||||
if (this.dom.tr) {
|
|
||||||
return this.dom.tr
|
|
||||||
}
|
|
||||||
|
|
||||||
this._updateEditability()
|
|
||||||
|
|
||||||
// display "show more"
|
|
||||||
if (!this.dom.tr) {
|
|
||||||
const me = this
|
|
||||||
const parent = this.parent
|
|
||||||
const showMoreButton = document.createElement('a')
|
|
||||||
showMoreButton.appendChild(document.createTextNode(translate('showMore')))
|
|
||||||
showMoreButton.href = '#'
|
|
||||||
showMoreButton.onclick = event => {
|
|
||||||
// TODO: use callback instead of accessing a method of the parent
|
|
||||||
parent.visibleChilds = Math.floor(parent.visibleChilds / parent.getMaxVisibleChilds() + 1) *
|
|
||||||
parent.getMaxVisibleChilds()
|
|
||||||
me.updateDom()
|
|
||||||
parent.showChilds()
|
|
||||||
|
|
||||||
event.preventDefault()
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
const showAllButton = document.createElement('a')
|
|
||||||
showAllButton.appendChild(document.createTextNode(translate('showAll')))
|
|
||||||
showAllButton.href = '#'
|
|
||||||
showAllButton.onclick = event => {
|
|
||||||
// TODO: use callback instead of accessing a method of the parent
|
|
||||||
parent.visibleChilds = Infinity
|
|
||||||
me.updateDom()
|
|
||||||
parent.showChilds()
|
|
||||||
|
|
||||||
event.preventDefault()
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
const moreContents = document.createElement('div')
|
|
||||||
const moreText = document.createTextNode(this._getShowMoreText())
|
|
||||||
moreContents.className = 'jsoneditor-show-more'
|
|
||||||
moreContents.appendChild(moreText)
|
|
||||||
moreContents.appendChild(showMoreButton)
|
|
||||||
moreContents.appendChild(document.createTextNode('. '))
|
|
||||||
moreContents.appendChild(showAllButton)
|
|
||||||
moreContents.appendChild(document.createTextNode('. '))
|
|
||||||
|
|
||||||
const tdContents = document.createElement('td')
|
|
||||||
tdContents.appendChild(moreContents)
|
|
||||||
|
|
||||||
const moreTr = document.createElement('tr')
|
|
||||||
if (this.editor.options.mode === 'tree') {
|
|
||||||
moreTr.appendChild(document.createElement('td'))
|
|
||||||
moreTr.appendChild(document.createElement('td'))
|
|
||||||
}
|
|
||||||
moreTr.appendChild(tdContents)
|
|
||||||
moreTr.className = 'jsoneditor-show-more'
|
|
||||||
this.dom.tr = moreTr
|
|
||||||
this.dom.moreContents = moreContents
|
|
||||||
this.dom.moreText = moreText
|
|
||||||
}
|
|
||||||
|
|
||||||
this.updateDom()
|
|
||||||
|
|
||||||
return this.dom.tr
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Update the HTML dom of the Node
|
|
||||||
*/
|
|
||||||
ShowMoreNode.prototype.updateDom = function (options) {
|
|
||||||
if (this.isVisible()) {
|
|
||||||
// attach to the right child node (the first non-visible child)
|
|
||||||
this.dom.tr.node = this.parent.childs[this.parent.visibleChilds]
|
|
||||||
|
|
||||||
if (!this.dom.tr.parentNode) {
|
|
||||||
const nextTr = this.parent._getNextTr()
|
|
||||||
if (nextTr) {
|
|
||||||
nextTr.parentNode.insertBefore(this.dom.tr, nextTr)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// update the counts in the text
|
|
||||||
this.dom.moreText.nodeValue = this._getShowMoreText()
|
|
||||||
|
|
||||||
// update left margin
|
|
||||||
this.dom.moreContents.style.marginLeft = (this.getLevel() + 1) * 24 + 'px'
|
|
||||||
} else {
|
|
||||||
if (this.dom.tr && this.dom.tr.parentNode) {
|
|
||||||
this.dom.tr.parentNode.removeChild(this.dom.tr)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
ShowMoreNode.prototype._getShowMoreText = function () {
|
|
||||||
return translate('showMoreStatus', {
|
|
||||||
visibleChilds: this.parent.visibleChilds,
|
|
||||||
totalChilds: this.parent.childs.length
|
|
||||||
}) + ' '
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Check whether the ShowMoreNode is currently visible.
|
|
||||||
* the ShowMoreNode is visible when it's parent has more childs than
|
|
||||||
* the current visibleChilds
|
|
||||||
* @return {boolean} isVisible
|
|
||||||
*/
|
|
||||||
ShowMoreNode.prototype.isVisible = function () {
|
|
||||||
return this.parent.expanded && this.parent.childs.length > this.parent.visibleChilds
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Handle an event. The event is caught centrally by the editor
|
|
||||||
* @param {Event} event
|
|
||||||
*/
|
|
||||||
ShowMoreNode.prototype.onEvent = function (event) {
|
|
||||||
const type = event.type
|
|
||||||
if (type === 'keydown') {
|
|
||||||
this.onKeyDown(event)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return ShowMoreNode
|
|
||||||
}
|
|
|
@ -1,131 +0,0 @@
|
||||||
import picoModal from 'picomodal'
|
|
||||||
import { translate } from './i18n'
|
|
||||||
import { contains, getChildPaths } from './util'
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Show advanced sorting modal
|
|
||||||
* @param {HTMLElement} container The container where to center
|
|
||||||
* the modal and create an overlay
|
|
||||||
* @param {JSON} json The JSON data to be sorted.
|
|
||||||
* @param {function} onSort Callback function, invoked with
|
|
||||||
* an object containing the selected
|
|
||||||
* path and direction
|
|
||||||
* @param {Object} options
|
|
||||||
* Available options:
|
|
||||||
* - {string} path The selected path
|
|
||||||
* - {'asc' | 'desc'} direction The selected direction
|
|
||||||
*/
|
|
||||||
export function showSortModal (container, json, onSort, options) {
|
|
||||||
const paths = Array.isArray(json)
|
|
||||||
? getChildPaths(json)
|
|
||||||
: ['']
|
|
||||||
const selectedPath = options && options.path && contains(paths, options.path)
|
|
||||||
? options.path
|
|
||||||
: paths[0]
|
|
||||||
const selectedDirection = (options && options.direction) || 'asc'
|
|
||||||
|
|
||||||
const content = '<div class="pico-modal-contents">' +
|
|
||||||
'<div class="pico-modal-header">' + translate('sort') + '</div>' +
|
|
||||||
'<form>' +
|
|
||||||
'<table>' +
|
|
||||||
'<tbody>' +
|
|
||||||
'<tr>' +
|
|
||||||
' <td>' + translate('sortFieldLabel') + ' </td>' +
|
|
||||||
' <td class="jsoneditor-modal-input">' +
|
|
||||||
' <div class="jsoneditor-select-wrapper">' +
|
|
||||||
' <select id="field" title="' + translate('sortFieldTitle') + '">' +
|
|
||||||
' </select>' +
|
|
||||||
' </div>' +
|
|
||||||
' </td>' +
|
|
||||||
'</tr>' +
|
|
||||||
'<tr>' +
|
|
||||||
' <td>' + translate('sortDirectionLabel') + ' </td>' +
|
|
||||||
' <td class="jsoneditor-modal-input">' +
|
|
||||||
' <div id="direction" class="jsoneditor-button-group">' +
|
|
||||||
'<input type="button" ' +
|
|
||||||
'value="' + translate('sortAscending') + '" ' +
|
|
||||||
'title="' + translate('sortAscendingTitle') + '" ' +
|
|
||||||
'data-value="asc" ' +
|
|
||||||
'class="jsoneditor-button-first jsoneditor-button-asc"/>' +
|
|
||||||
'<input type="button" ' +
|
|
||||||
'value="' + translate('sortDescending') + '" ' +
|
|
||||||
'title="' + translate('sortDescendingTitle') + '" ' +
|
|
||||||
'data-value="desc" ' +
|
|
||||||
'class="jsoneditor-button-last jsoneditor-button-desc"/>' +
|
|
||||||
' </div>' +
|
|
||||||
' </td>' +
|
|
||||||
'</tr>' +
|
|
||||||
'<tr>' +
|
|
||||||
'<td colspan="2" class="jsoneditor-modal-input jsoneditor-modal-actions">' +
|
|
||||||
' <input type="submit" id="ok" value="' + translate('ok') + '" />' +
|
|
||||||
'</td>' +
|
|
||||||
'</tr>' +
|
|
||||||
'</tbody>' +
|
|
||||||
'</table>' +
|
|
||||||
'</form>' +
|
|
||||||
'</div>'
|
|
||||||
|
|
||||||
picoModal({
|
|
||||||
parent: container,
|
|
||||||
content: content,
|
|
||||||
overlayClass: 'jsoneditor-modal-overlay',
|
|
||||||
overlayStyles: {
|
|
||||||
backgroundColor: 'rgb(1,1,1)',
|
|
||||||
opacity: 0.3
|
|
||||||
},
|
|
||||||
modalClass: 'jsoneditor-modal jsoneditor-modal-sort'
|
|
||||||
})
|
|
||||||
.afterCreate(modal => {
|
|
||||||
const form = modal.modalElem().querySelector('form')
|
|
||||||
const ok = modal.modalElem().querySelector('#ok')
|
|
||||||
const field = modal.modalElem().querySelector('#field')
|
|
||||||
const direction = modal.modalElem().querySelector('#direction')
|
|
||||||
|
|
||||||
function preprocessPath (path) {
|
|
||||||
return (path === '')
|
|
||||||
? '@'
|
|
||||||
: (path[0] === '.')
|
|
||||||
? path.slice(1)
|
|
||||||
: path
|
|
||||||
}
|
|
||||||
|
|
||||||
paths.forEach(path => {
|
|
||||||
const option = document.createElement('option')
|
|
||||||
option.text = preprocessPath(path)
|
|
||||||
option.value = path
|
|
||||||
field.appendChild(option)
|
|
||||||
})
|
|
||||||
|
|
||||||
function setDirection (value) {
|
|
||||||
direction.value = value
|
|
||||||
direction.className = 'jsoneditor-button-group jsoneditor-button-group-value-' + direction.value
|
|
||||||
}
|
|
||||||
|
|
||||||
field.value = selectedPath || paths[0]
|
|
||||||
setDirection(selectedDirection || 'asc')
|
|
||||||
|
|
||||||
direction.onclick = event => {
|
|
||||||
setDirection(event.target.getAttribute('data-value'))
|
|
||||||
}
|
|
||||||
|
|
||||||
ok.onclick = event => {
|
|
||||||
event.preventDefault()
|
|
||||||
event.stopPropagation()
|
|
||||||
|
|
||||||
modal.close()
|
|
||||||
|
|
||||||
onSort({
|
|
||||||
path: field.value,
|
|
||||||
direction: direction.value
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
if (form) { // form is not available when JSONEditor is created inside a form
|
|
||||||
form.onsubmit = ok.onclick
|
|
||||||
}
|
|
||||||
})
|
|
||||||
.afterClose(modal => {
|
|
||||||
modal.destroy()
|
|
||||||
})
|
|
||||||
.show()
|
|
||||||
}
|
|
|
@ -1,296 +0,0 @@
|
||||||
import picoModal from 'picomodal'
|
|
||||||
import Selectr from './assets/selectr/selectr'
|
|
||||||
import { translate } from './i18n'
|
|
||||||
import { stringifyPartial } from './jsonUtils'
|
|
||||||
import { getChildPaths, debounce } from './util'
|
|
||||||
import { MAX_PREVIEW_CHARACTERS } from './constants'
|
|
||||||
|
|
||||||
const DEFAULT_DESCRIPTION =
|
|
||||||
'Enter a <a href="http://jmespath.org" target="_blank">JMESPath</a> query to filter, sort, or transform the JSON data.<br/>' +
|
|
||||||
'To learn JMESPath, go to <a href="http://jmespath.org/tutorial.html" target="_blank">the interactive tutorial</a>.'
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Show advanced filter and transform modal using JMESPath
|
|
||||||
* @param {Object} params
|
|
||||||
* @property {HTMLElement} container The container where to center
|
|
||||||
* the modal and create an overlay
|
|
||||||
* @property {JSON} json The json data to be transformed
|
|
||||||
* @property {string} [queryDescription] Optional custom description explaining
|
|
||||||
* the transform functionality
|
|
||||||
* @property {function} createQuery Function called to create a query
|
|
||||||
* from the wizard form
|
|
||||||
* @property {function} executeQuery Execute a query for the preview pane
|
|
||||||
* @property {function} onTransform Callback invoked with the created
|
|
||||||
* query as callback
|
|
||||||
*/
|
|
||||||
export function showTransformModal (
|
|
||||||
{
|
|
||||||
container,
|
|
||||||
json,
|
|
||||||
queryDescription = DEFAULT_DESCRIPTION,
|
|
||||||
createQuery,
|
|
||||||
executeQuery,
|
|
||||||
onTransform
|
|
||||||
}
|
|
||||||
) {
|
|
||||||
const value = json
|
|
||||||
|
|
||||||
const content = '<label class="pico-modal-contents">' +
|
|
||||||
'<div class="pico-modal-header">' + translate('transform') + '</div>' +
|
|
||||||
'<p>' + queryDescription + '</p>' +
|
|
||||||
'<div class="jsoneditor-jmespath-label">' + translate('transformWizardLabel') + ' </div>' +
|
|
||||||
'<div id="wizard" class="jsoneditor-jmespath-block jsoneditor-jmespath-wizard">' +
|
|
||||||
' <table class="jsoneditor-jmespath-wizard-table">' +
|
|
||||||
' <tbody>' +
|
|
||||||
' <tr>' +
|
|
||||||
' <th>' + translate('transformWizardFilter') + '</th>' +
|
|
||||||
' <td class="jsoneditor-jmespath-filter">' +
|
|
||||||
' <div class="jsoneditor-inline jsoneditor-jmespath-filter-field" >' +
|
|
||||||
' <select id="filterField">' +
|
|
||||||
' </select>' +
|
|
||||||
' </div>' +
|
|
||||||
' <div class="jsoneditor-inline jsoneditor-jmespath-filter-relation" >' +
|
|
||||||
' <select id="filterRelation">' +
|
|
||||||
' <option value="==">==</option>' +
|
|
||||||
' <option value="!=">!=</option>' +
|
|
||||||
' <option value="<"><</option>' +
|
|
||||||
' <option value="<="><=</option>' +
|
|
||||||
' <option value=">">></option>' +
|
|
||||||
' <option value=">=">>=</option>' +
|
|
||||||
' </select>' +
|
|
||||||
' </div>' +
|
|
||||||
' <div class="jsoneditor-inline jsoneditor-jmespath-filter-value" >' +
|
|
||||||
' <input type="text" class="value" placeholder="value..." id="filterValue" />' +
|
|
||||||
' </div>' +
|
|
||||||
' </td>' +
|
|
||||||
' </tr>' +
|
|
||||||
' <tr>' +
|
|
||||||
' <th>' + translate('transformWizardSortBy') + '</th>' +
|
|
||||||
' <td class="jsoneditor-jmespath-filter">' +
|
|
||||||
' <div class="jsoneditor-inline jsoneditor-jmespath-sort-field">' +
|
|
||||||
' <select id="sortField">' +
|
|
||||||
' </select>' +
|
|
||||||
' </div>' +
|
|
||||||
' <div class="jsoneditor-inline jsoneditor-jmespath-sort-order" >' +
|
|
||||||
' <select id="sortOrder">' +
|
|
||||||
' <option value="asc">Ascending</option>' +
|
|
||||||
' <option value="desc">Descending</option>' +
|
|
||||||
' </select>' +
|
|
||||||
' </div>' +
|
|
||||||
' </td>' +
|
|
||||||
' </tr>' +
|
|
||||||
' <tr id="selectFieldsPart">' +
|
|
||||||
' <th>' + translate('transformWizardSelectFields') + '</th>' +
|
|
||||||
' <td class="jsoneditor-jmespath-filter">' +
|
|
||||||
' <select class="jsoneditor-jmespath-select-fields" id="selectFields" multiple></select>' +
|
|
||||||
' </td>' +
|
|
||||||
' </tr>' +
|
|
||||||
' </tbody>' +
|
|
||||||
' </table>' +
|
|
||||||
'</div>' +
|
|
||||||
'<div class="jsoneditor-jmespath-label">' + translate('transformQueryLabel') + ' </div>' +
|
|
||||||
'<div class="jsoneditor-jmespath-block">' +
|
|
||||||
' <textarea id="query" ' +
|
|
||||||
' rows="4" ' +
|
|
||||||
' autocomplete="off" ' +
|
|
||||||
' autocorrect="off" ' +
|
|
||||||
' autocapitalize="off" ' +
|
|
||||||
' spellcheck="false"' +
|
|
||||||
' title="' + translate('transformQueryTitle') + '">[*]</textarea>' +
|
|
||||||
'</div>' +
|
|
||||||
'<div class="jsoneditor-jmespath-label">' + translate('transformPreviewLabel') + ' </div>' +
|
|
||||||
'<div class="jsoneditor-jmespath-block">' +
|
|
||||||
' <textarea id="preview" ' +
|
|
||||||
' class="jsoneditor-transform-preview"' +
|
|
||||||
' readonly> </textarea>' +
|
|
||||||
'</div>' +
|
|
||||||
'<div class="jsoneditor-jmespath-block jsoneditor-modal-actions">' +
|
|
||||||
' <input type="submit" id="ok" value="' + translate('ok') + '" autofocus />' +
|
|
||||||
'</div>' +
|
|
||||||
'</div>'
|
|
||||||
|
|
||||||
picoModal({
|
|
||||||
parent: container,
|
|
||||||
content: content,
|
|
||||||
overlayClass: 'jsoneditor-modal-overlay',
|
|
||||||
overlayStyles: {
|
|
||||||
backgroundColor: 'rgb(1,1,1)',
|
|
||||||
opacity: 0.3
|
|
||||||
},
|
|
||||||
modalClass: 'jsoneditor-modal jsoneditor-modal-transform',
|
|
||||||
focus: false
|
|
||||||
})
|
|
||||||
.afterCreate(modal => {
|
|
||||||
const elem = modal.modalElem()
|
|
||||||
|
|
||||||
const wizard = elem.querySelector('#wizard')
|
|
||||||
const ok = elem.querySelector('#ok')
|
|
||||||
const filterField = elem.querySelector('#filterField')
|
|
||||||
const filterRelation = elem.querySelector('#filterRelation')
|
|
||||||
const filterValue = elem.querySelector('#filterValue')
|
|
||||||
const sortField = elem.querySelector('#sortField')
|
|
||||||
const sortOrder = elem.querySelector('#sortOrder')
|
|
||||||
const selectFields = elem.querySelector('#selectFields')
|
|
||||||
const query = elem.querySelector('#query')
|
|
||||||
const preview = elem.querySelector('#preview')
|
|
||||||
|
|
||||||
if (!Array.isArray(value)) {
|
|
||||||
wizard.style.fontStyle = 'italic'
|
|
||||||
wizard.innerHTML = '(wizard not available for objects, only for arrays)'
|
|
||||||
}
|
|
||||||
|
|
||||||
const sortablePaths = getChildPaths(json)
|
|
||||||
|
|
||||||
sortablePaths.forEach(path => {
|
|
||||||
const formattedPath = preprocessPath(path)
|
|
||||||
const filterOption = document.createElement('option')
|
|
||||||
filterOption.text = formattedPath
|
|
||||||
filterOption.value = formattedPath
|
|
||||||
filterField.appendChild(filterOption)
|
|
||||||
|
|
||||||
const sortOption = document.createElement('option')
|
|
||||||
sortOption.text = formattedPath
|
|
||||||
sortOption.value = formattedPath
|
|
||||||
sortField.appendChild(sortOption)
|
|
||||||
})
|
|
||||||
|
|
||||||
const selectablePaths = getChildPaths(json, true).filter(path => path !== '')
|
|
||||||
if (selectablePaths.length > 0) {
|
|
||||||
selectablePaths.forEach(path => {
|
|
||||||
const formattedPath = preprocessPath(path)
|
|
||||||
const option = document.createElement('option')
|
|
||||||
option.text = formattedPath
|
|
||||||
option.value = formattedPath
|
|
||||||
selectFields.appendChild(option)
|
|
||||||
})
|
|
||||||
} else {
|
|
||||||
const selectFieldsPart = elem.querySelector('#selectFieldsPart')
|
|
||||||
if (selectFieldsPart) {
|
|
||||||
selectFieldsPart.style.display = 'none'
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const selectrFilterField = new Selectr(filterField, { defaultSelected: false, clearable: true, allowDeselect: true, placeholder: 'field...' })
|
|
||||||
const selectrFilterRelation = new Selectr(filterRelation, { defaultSelected: false, clearable: true, allowDeselect: true, placeholder: 'compare...' })
|
|
||||||
const selectrSortField = new Selectr(sortField, { defaultSelected: false, clearable: true, allowDeselect: true, placeholder: 'field...' })
|
|
||||||
const selectrSortOrder = new Selectr(sortOrder, { defaultSelected: false, clearable: true, allowDeselect: true, placeholder: 'order...' })
|
|
||||||
const selectrSelectFields = new Selectr(selectFields, { multiple: true, clearable: true, defaultSelected: false, placeholder: 'select fields...' })
|
|
||||||
|
|
||||||
selectrFilterField.on('selectr.change', generateQueryFromWizard)
|
|
||||||
selectrFilterRelation.on('selectr.change', generateQueryFromWizard)
|
|
||||||
filterValue.oninput = generateQueryFromWizard
|
|
||||||
selectrSortField.on('selectr.change', generateQueryFromWizard)
|
|
||||||
selectrSortOrder.on('selectr.change', generateQueryFromWizard)
|
|
||||||
selectrSelectFields.on('selectr.change', generateQueryFromWizard)
|
|
||||||
|
|
||||||
elem.querySelector('.pico-modal-contents').onclick = event => {
|
|
||||||
// prevent the first clear button (in any select box) from getting
|
|
||||||
// focus when clicking anywhere in the modal. Only allow clicking links.
|
|
||||||
if (event.target.nodeName !== 'A') {
|
|
||||||
event.preventDefault()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function preprocessPath (path) {
|
|
||||||
return (path === '')
|
|
||||||
? '@'
|
|
||||||
: (path[0] === '.')
|
|
||||||
? path.slice(1)
|
|
||||||
: path
|
|
||||||
}
|
|
||||||
|
|
||||||
function updatePreview () {
|
|
||||||
try {
|
|
||||||
const transformed = executeQuery(value, query.value)
|
|
||||||
|
|
||||||
preview.className = 'jsoneditor-transform-preview'
|
|
||||||
preview.value = stringifyPartial(transformed, 2, MAX_PREVIEW_CHARACTERS)
|
|
||||||
|
|
||||||
ok.disabled = false
|
|
||||||
} catch (err) {
|
|
||||||
preview.className = 'jsoneditor-transform-preview jsoneditor-error'
|
|
||||||
preview.value = err.toString()
|
|
||||||
ok.disabled = true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const debouncedUpdatePreview = debounce(updatePreview, 300)
|
|
||||||
|
|
||||||
function tryCreateQuery (json, queryOptions) {
|
|
||||||
try {
|
|
||||||
query.value = createQuery(json, queryOptions)
|
|
||||||
ok.disabled = false
|
|
||||||
|
|
||||||
debouncedUpdatePreview()
|
|
||||||
} catch (err) {
|
|
||||||
const message = 'Error: an error happened when executing "createQuery": ' + (err.message || err.toString())
|
|
||||||
|
|
||||||
query.value = ''
|
|
||||||
ok.disabled = true
|
|
||||||
|
|
||||||
preview.className = 'jsoneditor-transform-preview jsoneditor-error'
|
|
||||||
preview.value = message
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function generateQueryFromWizard () {
|
|
||||||
const queryOptions = {}
|
|
||||||
|
|
||||||
if (filterField.value && filterRelation.value && filterValue.value) {
|
|
||||||
queryOptions.filter = {
|
|
||||||
field: filterField.value,
|
|
||||||
relation: filterRelation.value,
|
|
||||||
value: filterValue.value
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (sortField.value && sortOrder.value) {
|
|
||||||
queryOptions.sort = {
|
|
||||||
field: sortField.value,
|
|
||||||
direction: sortOrder.value
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (selectFields.value) {
|
|
||||||
const fields = []
|
|
||||||
for (let i = 0; i < selectFields.options.length; i++) {
|
|
||||||
if (selectFields.options[i].selected) {
|
|
||||||
const selectedField = selectFields.options[i].value
|
|
||||||
fields.push(selectedField)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
queryOptions.projection = {
|
|
||||||
fields
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
tryCreateQuery(json, queryOptions)
|
|
||||||
}
|
|
||||||
|
|
||||||
query.oninput = debouncedUpdatePreview
|
|
||||||
|
|
||||||
ok.onclick = event => {
|
|
||||||
event.preventDefault()
|
|
||||||
event.stopPropagation()
|
|
||||||
|
|
||||||
modal.close()
|
|
||||||
|
|
||||||
onTransform(query.value)
|
|
||||||
}
|
|
||||||
|
|
||||||
// initialize with empty query
|
|
||||||
tryCreateQuery(json, {})
|
|
||||||
|
|
||||||
setTimeout(() => {
|
|
||||||
query.select()
|
|
||||||
query.focus()
|
|
||||||
query.selectionStart = 3
|
|
||||||
query.selectionEnd = 3
|
|
||||||
})
|
|
||||||
})
|
|
||||||
.afterClose(modal => {
|
|
||||||
modal.destroy()
|
|
||||||
})
|
|
||||||
.show()
|
|
||||||
}
|
|
1059
src/js/textmode.js
1059
src/js/textmode.js
File diff suppressed because it is too large
Load Diff
1826
src/js/treemode.js
1826
src/js/treemode.js
File diff suppressed because it is too large
Load Diff
|
@ -1,7 +0,0 @@
|
||||||
exports.tryRequireAjv = function () {
|
|
||||||
try {
|
|
||||||
return require('ajv')
|
|
||||||
} catch (err) {
|
|
||||||
// no problem... when we need Ajv we will throw a neat exception
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,7 +0,0 @@
|
||||||
exports.tryRequireThemeJsonEditor = function () {
|
|
||||||
try {
|
|
||||||
require('./ace/theme-jsoneditor')
|
|
||||||
} catch (err) {
|
|
||||||
console.error(err)
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,25 +0,0 @@
|
||||||
|
|
||||||
/**
|
|
||||||
* @typedef {object} QueryOptions
|
|
||||||
* @property {FilterOptions} [filter]
|
|
||||||
* @property {SortOptions} [sort]
|
|
||||||
* @property {ProjectionOptions} [projection]
|
|
||||||
*/
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @typedef {object} FilterOptions
|
|
||||||
* @property {string} field
|
|
||||||
* @property {string} relation Can be '==', '<', etc
|
|
||||||
* @property {string} value
|
|
||||||
*/
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @typedef {object} SortOptions
|
|
||||||
* @property {string} field
|
|
||||||
* @property {string} direction Can be 'asc' or 'desc'
|
|
||||||
*/
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @typedef {object} ProjectionOptions
|
|
||||||
* @property {string[]} fields
|
|
||||||
*/
|
|
1529
src/js/util.js
1529
src/js/util.js
File diff suppressed because it is too large
Load Diff
|
@ -1,47 +0,0 @@
|
||||||
import { isPromise, isValidValidationError, stringifyPath } from './util'
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Execute custom validation if configured.
|
|
||||||
*
|
|
||||||
* Returns a promise resolving with the custom errors (or an empty array).
|
|
||||||
*/
|
|
||||||
export function validateCustom (json, onValidate) {
|
|
||||||
if (!onValidate) {
|
|
||||||
return Promise.resolve([])
|
|
||||||
}
|
|
||||||
|
|
||||||
try {
|
|
||||||
const customValidateResults = onValidate(json)
|
|
||||||
|
|
||||||
const resultPromise = isPromise(customValidateResults)
|
|
||||||
? customValidateResults
|
|
||||||
: Promise.resolve(customValidateResults)
|
|
||||||
|
|
||||||
return resultPromise.then(customValidationPathErrors => {
|
|
||||||
if (Array.isArray(customValidationPathErrors)) {
|
|
||||||
return customValidationPathErrors
|
|
||||||
.filter(error => {
|
|
||||||
const valid = isValidValidationError(error)
|
|
||||||
|
|
||||||
if (!valid) {
|
|
||||||
console.warn('Ignoring a custom validation error with invalid structure. ' +
|
|
||||||
'Expected structure: {path: [...], message: "..."}. ' +
|
|
||||||
'Actual error:', error)
|
|
||||||
}
|
|
||||||
|
|
||||||
return valid
|
|
||||||
})
|
|
||||||
.map(error => // change data structure into the structure matching the JSON schema errors
|
|
||||||
({
|
|
||||||
dataPath: stringifyPath(error.path),
|
|
||||||
message: error.message,
|
|
||||||
type: 'customValidation'
|
|
||||||
}))
|
|
||||||
} else {
|
|
||||||
return []
|
|
||||||
}
|
|
||||||
})
|
|
||||||
} catch (err) {
|
|
||||||
return Promise.reject(err)
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,15 +0,0 @@
|
||||||
let VanillaPicker
|
|
||||||
|
|
||||||
if (window.Picker) {
|
|
||||||
// use the already loaded instance of VanillaPicker
|
|
||||||
VanillaPicker = window.Picker
|
|
||||||
} else {
|
|
||||||
try {
|
|
||||||
// load color picker
|
|
||||||
VanillaPicker = require('vanilla-picker')
|
|
||||||
} catch (err) {
|
|
||||||
// probably running the minimalist bundle
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
module.exports = VanillaPicker
|
|
|
@ -0,0 +1,9 @@
|
||||||
|
import App from './App.svelte';
|
||||||
|
|
||||||
|
const app = new App({
|
||||||
|
target: document.body,
|
||||||
|
props: {
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
export default app;
|
|
@ -1,14 +0,0 @@
|
||||||
JSON Editor Icons
|
|
||||||
|
|
||||||
size: outer: 24x24 px
|
|
||||||
inner: 16x16 px
|
|
||||||
|
|
||||||
blue background: RGBA 97b0f8ff
|
|
||||||
gray background: RGBA 4d4d4dff
|
|
||||||
grey background: RGBA d3d3d3ff
|
|
||||||
|
|
||||||
red foreground: RGBA ff3300ff
|
|
||||||
green foreground: RGBA 13ae00ff
|
|
||||||
|
|
||||||
characters are based on the Arial font
|
|
||||||
|
|
|
@ -1,749 +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="240"
|
|
||||||
height="144"
|
|
||||||
id="svg4136"
|
|
||||||
version="1.1"
|
|
||||||
inkscape:version="0.91 r13725"
|
|
||||||
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="#ff63ff"
|
|
||||||
bordercolor="#666666"
|
|
||||||
borderopacity="1"
|
|
||||||
objecttolerance="10"
|
|
||||||
gridtolerance="10"
|
|
||||||
guidetolerance="10"
|
|
||||||
inkscape:pageopacity="0"
|
|
||||||
inkscape:pageshadow="2"
|
|
||||||
inkscape:window-width="1920"
|
|
||||||
inkscape:window-height="1026"
|
|
||||||
id="namedview4144"
|
|
||||||
showgrid="true"
|
|
||||||
inkscape:zoom="4"
|
|
||||||
inkscape:cx="13.229181"
|
|
||||||
inkscape:cy="119.82429"
|
|
||||||
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/ -->
|
|
||||||
<rect
|
|
||||||
style="fill:#4c4c4c;fill-opacity:1;stroke:none;stroke-width:0"
|
|
||||||
id="svg_1"
|
|
||||||
height="16"
|
|
||||||
width="16"
|
|
||||||
y="4"
|
|
||||||
x="4" />
|
|
||||||
<rect
|
|
||||||
id="svg_1-7"
|
|
||||||
height="16"
|
|
||||||
width="16"
|
|
||||||
y="3.999995"
|
|
||||||
x="28.000006"
|
|
||||||
style="fill:#ec3f29;fill-opacity:0.94117647;stroke:none;stroke-width:0" />
|
|
||||||
<rect
|
|
||||||
style="fill:#4c4c4c;fill-opacity:1;stroke:none;stroke-width:0"
|
|
||||||
x="52.000004"
|
|
||||||
y="3.999995"
|
|
||||||
width="16"
|
|
||||||
height="16"
|
|
||||||
id="rect4165" />
|
|
||||||
<rect
|
|
||||||
id="rect4175"
|
|
||||||
height="16"
|
|
||||||
width="16"
|
|
||||||
y="3.9999852"
|
|
||||||
x="172.00002"
|
|
||||||
style="fill:#4c4c4c;fill-opacity:1;stroke:none;stroke-width:0" />
|
|
||||||
<rect
|
|
||||||
id="rect4175-3"
|
|
||||||
height="16"
|
|
||||||
width="16"
|
|
||||||
y="3.999995"
|
|
||||||
x="196"
|
|
||||||
style="fill:#4c4c4c;fill-opacity:1;stroke:none;stroke-width:0" />
|
|
||||||
<g
|
|
||||||
id="g4299"
|
|
||||||
style="stroke:none">
|
|
||||||
<rect
|
|
||||||
x="7.0000048"
|
|
||||||
y="10.999998"
|
|
||||||
width="9.9999924"
|
|
||||||
height="1.9999986"
|
|
||||||
id="svg_1-1"
|
|
||||||
style="fill:#ffffff;fill-opacity:1;stroke:none;stroke-width:0" />
|
|
||||||
<rect
|
|
||||||
x="11.000005"
|
|
||||||
y="7.0000114"
|
|
||||||
width="1.9999955"
|
|
||||||
height="9.9999838"
|
|
||||||
id="svg_1-1-1"
|
|
||||||
style="fill:#ffffff;fill-opacity:1;stroke:none;stroke-width:0" />
|
|
||||||
</g>
|
|
||||||
<g
|
|
||||||
id="g4299-3"
|
|
||||||
transform="matrix(0.70710678,-0.70710678,0.70710678,0.70710678,19.029435,12.000001)"
|
|
||||||
style="stroke:none">
|
|
||||||
<rect
|
|
||||||
x="7.0000048"
|
|
||||||
y="10.999998"
|
|
||||||
width="9.9999924"
|
|
||||||
height="1.9999986"
|
|
||||||
id="svg_1-1-0"
|
|
||||||
style="fill:#ffffff;fill-opacity:1;stroke:none;stroke-width:0" />
|
|
||||||
<rect
|
|
||||||
x="11.000005"
|
|
||||||
y="7.0000114"
|
|
||||||
width="1.9999955"
|
|
||||||
height="9.9999838"
|
|
||||||
id="svg_1-1-1-9"
|
|
||||||
style="fill:#ffffff;fill-opacity:1;stroke:none;stroke-width:0" />
|
|
||||||
</g>
|
|
||||||
<rect
|
|
||||||
id="svg_1-7-5"
|
|
||||||
height="6.9999905"
|
|
||||||
width="6.9999909"
|
|
||||||
y="7.0000048"
|
|
||||||
x="55.000004"
|
|
||||||
style="fill:#ffffff;fill-opacity:1;stroke:#000000;stroke-width:0" />
|
|
||||||
<rect
|
|
||||||
style="fill:#ffffff;fill-opacity:1;stroke:#4c4c4c;stroke-width:2;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
|
|
||||||
x="58"
|
|
||||||
y="10.00001"
|
|
||||||
width="6.9999909"
|
|
||||||
height="6.9999905"
|
|
||||||
id="rect4354" />
|
|
||||||
<rect
|
|
||||||
id="svg_1-7-5-7"
|
|
||||||
height="6.9999905"
|
|
||||||
width="6.9999909"
|
|
||||||
y="10.000005"
|
|
||||||
x="58.000004"
|
|
||||||
style="fill:#ffffff;fill-opacity:1;stroke:#3c80df;stroke-width:0;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:0.94117647" />
|
|
||||||
<g
|
|
||||||
id="g4378">
|
|
||||||
<rect
|
|
||||||
style="fill:#ffffff;fill-opacity:1;stroke:#000000;stroke-width:0"
|
|
||||||
x="198"
|
|
||||||
y="10.999999"
|
|
||||||
width="7.9999909"
|
|
||||||
height="1.9999965"
|
|
||||||
id="svg_1-7-5-3" />
|
|
||||||
<rect
|
|
||||||
id="rect4374"
|
|
||||||
height="1.9999946"
|
|
||||||
width="11.999995"
|
|
||||||
y="7.0000005"
|
|
||||||
x="198"
|
|
||||||
style="fill:#ffffff;fill-opacity:1;stroke:#000000;stroke-width:0" />
|
|
||||||
<rect
|
|
||||||
id="rect4376"
|
|
||||||
height="1.9999995"
|
|
||||||
width="3.9999928"
|
|
||||||
y="14.999996"
|
|
||||||
x="198"
|
|
||||||
style="fill:#ffffff;fill-opacity:1;stroke:#000000;stroke-width:0" />
|
|
||||||
</g>
|
|
||||||
<g
|
|
||||||
transform="matrix(1,0,0,-1,-23.999995,23.999995)"
|
|
||||||
id="g4383">
|
|
||||||
<rect
|
|
||||||
id="rect4385"
|
|
||||||
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="rect4387" />
|
|
||||||
<rect
|
|
||||||
style="fill:#ffffff;fill-opacity:1;stroke:#000000;stroke-width:0"
|
|
||||||
x="198"
|
|
||||||
y="14.999996"
|
|
||||||
width="3.9999928"
|
|
||||||
height="1.9999995"
|
|
||||||
id="rect4389" />
|
|
||||||
</g>
|
|
||||||
<rect
|
|
||||||
style="fill:#4c4c4c;fill-opacity:1;stroke:none"
|
|
||||||
id="rect3754-4"
|
|
||||||
width="16"
|
|
||||||
height="16"
|
|
||||||
x="76"
|
|
||||||
y="3.9999199" />
|
|
||||||
<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.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"
|
|
||||||
id="path4351"
|
|
||||||
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.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"
|
|
||||||
id="path4351-9"
|
|
||||||
inkscape:connector-curvature="0"
|
|
||||||
sodipodi:nodetypes="cccccccc" />
|
|
||||||
<rect
|
|
||||||
style="fill:#4c4c4c;fill-opacity:1;stroke:none"
|
|
||||||
id="rect3754-25"
|
|
||||||
width="16"
|
|
||||||
height="16"
|
|
||||||
x="100"
|
|
||||||
y="3.9999199" />
|
|
||||||
<path
|
|
||||||
style="fill:#ffffff;fill-opacity:1;stroke:none"
|
|
||||||
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"
|
|
||||||
id="path2987"
|
|
||||||
inkscape:connector-curvature="0" />
|
|
||||||
<path
|
|
||||||
style="fill:#ffffff;fill-opacity:1;stroke:none"
|
|
||||||
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"
|
|
||||||
id="path2987-1"
|
|
||||||
inkscape:connector-curvature="0" />
|
|
||||||
<rect
|
|
||||||
style="fill:#4c4c4c;fill-opacity:1;stroke:none"
|
|
||||||
id="rect3754-73"
|
|
||||||
width="16"
|
|
||||||
height="16"
|
|
||||||
x="124"
|
|
||||||
y="3.9999199" />
|
|
||||||
<path
|
|
||||||
style="fill:#ffffff;fill-opacity:1;stroke:none"
|
|
||||||
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"
|
|
||||||
id="path3780"
|
|
||||||
inkscape:connector-curvature="0"
|
|
||||||
sodipodi:nodetypes="ccccccccc" />
|
|
||||||
<path
|
|
||||||
style="fill:#4c4c4c;fill-opacity:1;stroke:none"
|
|
||||||
d="m 129.72704,13.478838 4.60852,0.01 -2.30426,-5.5497996 z"
|
|
||||||
id="path3782"
|
|
||||||
inkscape:connector-curvature="0" />
|
|
||||||
<rect
|
|
||||||
style="fill:#4c4c4c;fill-opacity:1;stroke:none"
|
|
||||||
id="rect3754-35"
|
|
||||||
width="16"
|
|
||||||
height="16"
|
|
||||||
x="148"
|
|
||||||
y="3.9999199" />
|
|
||||||
<path
|
|
||||||
style="fill:#ffffff;fill-opacity:1;stroke:none"
|
|
||||||
d="m 156.47655,5.8917384 0,2.1797 0.46093,2.3983996 1.82813,0 0.39844,-2.3983996 0,-2.1797 z"
|
|
||||||
id="path5008-2"
|
|
||||||
inkscape:connector-curvature="0"
|
|
||||||
sodipodi:nodetypes="ccccccc" />
|
|
||||||
<path
|
|
||||||
style="fill:#ffffff;fill-opacity:1;stroke:none"
|
|
||||||
d="m 152.51561,5.8906384 0,2.1797 0.46094,2.3983996 1.82812,0 0.39844,-2.3983996 0,-2.1797 z"
|
|
||||||
id="path5008-2-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.8;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.8;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.8;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.8;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.8;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.8;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.8;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.96599996;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.96599996;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.96599996;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.96599996;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.96599996;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.96599996;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" />
|
|
||||||
<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.8"
|
|
||||||
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" />
|
|
||||||
<rect
|
|
||||||
id="rect4175-3-5"
|
|
||||||
height="16"
|
|
||||||
width="16"
|
|
||||||
y="4"
|
|
||||||
x="220"
|
|
||||||
style="fill:#4c4c4c;fill-opacity:1;stroke:none;stroke-width:0" />
|
|
||||||
<path
|
|
||||||
style="fill:#ffffff;fill-rule:evenodd;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
|
|
||||||
d="m 234,6 0,2 -5,5 0,5 -2,0 0,-5 -5,-5 0,-2"
|
|
||||||
id="path3546"
|
|
||||||
inkscape:connector-curvature="0"
|
|
||||||
sodipodi:nodetypes="cccccccc" />
|
|
||||||
<g
|
|
||||||
transform="matrix(1.3333328,0,0,-1.5999992,-139.9999,127.19999)"
|
|
||||||
id="g4383-6">
|
|
||||||
<rect
|
|
||||||
id="rect4385-2"
|
|
||||||
height="1.2499905"
|
|
||||||
width="5.9999924"
|
|
||||||
y="12.625005"
|
|
||||||
x="198.00002"
|
|
||||||
style="fill:#ffffff;fill-opacity:0.8;stroke:#000000;stroke-width:0" />
|
|
||||||
<rect
|
|
||||||
style="fill:#ffffff;fill-opacity:0.8;stroke:#000000;stroke-width:0"
|
|
||||||
x="198.00002"
|
|
||||||
y="15.125007"
|
|
||||||
width="7.4999928"
|
|
||||||
height="1.2499949"
|
|
||||||
id="rect4387-9" />
|
|
||||||
<rect
|
|
||||||
style="fill:#ffffff;fill-opacity:0.8;stroke:#000000;stroke-width:0"
|
|
||||||
x="198.00002"
|
|
||||||
y="7.6250024"
|
|
||||||
width="2.9999909"
|
|
||||||
height="1.2499905"
|
|
||||||
id="rect4389-1-0" />
|
|
||||||
<rect
|
|
||||||
style="fill:#ffffff;fill-opacity:0.8;stroke:#000000;stroke-width:0"
|
|
||||||
x="198.00002"
|
|
||||||
y="10.125004"
|
|
||||||
width="4.4999919"
|
|
||||||
height="1.2499905"
|
|
||||||
id="rect4389-1-9" />
|
|
||||||
<path
|
|
||||||
style="fill:#ffffff;fill-opacity:0.8;fill-rule:evenodd;stroke:none;stroke-width:0.68465352px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
|
|
||||||
d="m 207.00001,16.375004 0,-5.625005 -2.25,0 3,-3.1250014 3,3.1250014 -2.25,0 0,5.625005 -1.5,0"
|
|
||||||
id="path4402"
|
|
||||||
inkscape:connector-curvature="0"
|
|
||||||
sodipodi:nodetypes="cccccccc" />
|
|
||||||
</g>
|
|
||||||
<path
|
|
||||||
style="fill:#ffffff;fill-opacity:0.8;fill-rule:evenodd;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
|
|
||||||
d="m 164,100 0,3 -6,6 0,7 -4,0 0,-7 -6,-6 0,-3"
|
|
||||||
id="path3546-2-2"
|
|
||||||
inkscape:connector-curvature="0"
|
|
||||||
sodipodi:nodetypes="cccccccc" />
|
|
||||||
<rect
|
|
||||||
style="fill:#4c4c4c;fill-opacity:1;stroke:none;stroke-width:0"
|
|
||||||
id="svg_1-3"
|
|
||||||
height="16"
|
|
||||||
width="16"
|
|
||||||
y="28"
|
|
||||||
x="4" />
|
|
||||||
<path
|
|
||||||
sodipodi:nodetypes="ccccccccc"
|
|
||||||
inkscape:connector-curvature="0"
|
|
||||||
id="path4402-5-7"
|
|
||||||
d="m 15,41 0,-7 -4,0 0,3 -5,-4 5,-4 0,3 6,0 0,9"
|
|
||||||
style="fill:#ffffff;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:0.68465352px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" />
|
|
||||||
</svg>
|
|
Before Width: | Height: | Size: 31 KiB |
|
@ -1,11 +0,0 @@
|
||||||
@import "jsoneditor/reset";
|
|
||||||
@import "jsoneditor/variables";
|
|
||||||
@import "jsoneditor/autocomplete";
|
|
||||||
@import "jsoneditor/contextmenu";
|
|
||||||
@import "jsoneditor/editor";
|
|
||||||
@import "jsoneditor/menu";
|
|
||||||
@import "jsoneditor/navigationbar";
|
|
||||||
@import "jsoneditor/searchbox";
|
|
||||||
@import "jsoneditor/statusbar";
|
|
||||||
@import "jsoneditor/treepath";
|
|
||||||
@import "./../js/assets/selectr/selectr";
|
|
|
@ -1,30 +0,0 @@
|
||||||
.jsoneditor {
|
|
||||||
.autocomplete {
|
|
||||||
&.dropdown {
|
|
||||||
position: absolute;
|
|
||||||
background: $jse-white;
|
|
||||||
box-shadow: $jse-box-shadow;
|
|
||||||
border: 1px solid $jse-bar-border;
|
|
||||||
overflow-x: hidden;
|
|
||||||
overflow-y: auto;
|
|
||||||
cursor: default;
|
|
||||||
margin: 0;
|
|
||||||
padding: 5px;
|
|
||||||
text-align: left;
|
|
||||||
outline: 0;
|
|
||||||
font-family: $jse-font-mono;
|
|
||||||
font-size: $jse-font-size;
|
|
||||||
.item {
|
|
||||||
color: #333;
|
|
||||||
&.hover {
|
|
||||||
background-color: #ddd;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
&.hint {
|
|
||||||
color: #aaa;
|
|
||||||
top: 4px;
|
|
||||||
left: 4px;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,443 +0,0 @@
|
||||||
.jsoneditor-contextmenu-root {
|
|
||||||
position: relative;
|
|
||||||
width: 0;
|
|
||||||
height: 0;
|
|
||||||
}
|
|
||||||
.jsoneditor-contextmenu {
|
|
||||||
position: absolute;
|
|
||||||
box-sizing: content-box;
|
|
||||||
z-index: 1;
|
|
||||||
.jsoneditor-menu {
|
|
||||||
position: relative;
|
|
||||||
left: 0;
|
|
||||||
top: 0;
|
|
||||||
width: 128px;
|
|
||||||
height: auto;
|
|
||||||
background: $jse-white;
|
|
||||||
border: 1px solid $jse-bar-border;
|
|
||||||
box-shadow: $jse-box-shadow;
|
|
||||||
list-style: none;
|
|
||||||
margin: 0;
|
|
||||||
padding: 0;
|
|
||||||
button {
|
|
||||||
position: relative;
|
|
||||||
padding: 0 4px 0 0;
|
|
||||||
margin: 0;
|
|
||||||
width: 128px;
|
|
||||||
height: auto;
|
|
||||||
border: none;
|
|
||||||
cursor: pointer;
|
|
||||||
color: $jse-contextmenu-color;
|
|
||||||
background: transparent;
|
|
||||||
font-size: $jse-font-size;
|
|
||||||
font-family: $jse-font;
|
|
||||||
box-sizing: border-box;
|
|
||||||
text-align: left;
|
|
||||||
&::-moz-focus-inner {
|
|
||||||
padding: 0;
|
|
||||||
border: 0;
|
|
||||||
}
|
|
||||||
&.jsoneditor-default {
|
|
||||||
width: 96px;
|
|
||||||
}
|
|
||||||
&.jsoneditor-expand {
|
|
||||||
float: right;
|
|
||||||
width: 32px;
|
|
||||||
height: 24px;
|
|
||||||
border-left: 1px solid $jse-separator;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
li {
|
|
||||||
overflow: hidden;
|
|
||||||
ul {
|
|
||||||
display: none;
|
|
||||||
position: relative;
|
|
||||||
left: -10px;
|
|
||||||
top: 0;
|
|
||||||
border: none;
|
|
||||||
box-shadow: $jse-box-shadow-inner;
|
|
||||||
padding: 0 10px;
|
|
||||||
-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;
|
|
||||||
.jsoneditor-icon {
|
|
||||||
margin-left: 24px;
|
|
||||||
}
|
|
||||||
li {
|
|
||||||
button {
|
|
||||||
padding-left: 24px;
|
|
||||||
animation: all ease-in-out 1s;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
button {
|
|
||||||
.jsoneditor-expand {
|
|
||||||
position: absolute;
|
|
||||||
top: 0;
|
|
||||||
right: 0;
|
|
||||||
width: 24px;
|
|
||||||
height: 24px;
|
|
||||||
padding: 0;
|
|
||||||
margin: 0 4px 0 0;
|
|
||||||
background: url($jse-icons) 0 -72px;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
.jsoneditor-icon {
|
|
||||||
position: absolute;
|
|
||||||
top: 0;
|
|
||||||
left: 0;
|
|
||||||
width: 24px;
|
|
||||||
height: 24px;
|
|
||||||
border: none;
|
|
||||||
padding: 0;
|
|
||||||
margin: 0;
|
|
||||||
background-image: url($jse-icons);
|
|
||||||
}
|
|
||||||
.jsoneditor-text {
|
|
||||||
padding: 4px 0 4px 24px;
|
|
||||||
word-wrap: break-word;
|
|
||||||
&.jsoneditor-right-margin {
|
|
||||||
padding-right: 24px;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
.jsoneditor-separator {
|
|
||||||
height: 0;
|
|
||||||
border-top: 1px solid $jse-separator;
|
|
||||||
padding-top: 5px;
|
|
||||||
margin-top: 5px;
|
|
||||||
}
|
|
||||||
button {
|
|
||||||
&.jsoneditor-remove {
|
|
||||||
.jsoneditor-icon {
|
|
||||||
background-position: -24px 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
&.jsoneditor-append {
|
|
||||||
.jsoneditor-icon {
|
|
||||||
background-position: 0 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
&.jsoneditor-insert {
|
|
||||||
.jsoneditor-icon {
|
|
||||||
background-position: 0 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
&.jsoneditor-duplicate {
|
|
||||||
.jsoneditor-icon {
|
|
||||||
background-position: -48px 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
&.jsoneditor-sort-asc {
|
|
||||||
.jsoneditor-icon {
|
|
||||||
background-position: -168px 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
&.jsoneditor-sort-desc {
|
|
||||||
.jsoneditor-icon {
|
|
||||||
background-position: -192px 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
&.jsoneditor-transform {
|
|
||||||
.jsoneditor-icon {
|
|
||||||
background-position: -216px 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
&.jsoneditor-extract {
|
|
||||||
.jsoneditor-icon {
|
|
||||||
background-position: 0 -24px;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
&.jsoneditor-type-string {
|
|
||||||
.jsoneditor-icon {
|
|
||||||
background-position: -144px 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
&.jsoneditor-type-auto {
|
|
||||||
.jsoneditor-icon {
|
|
||||||
background-position: -120px 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
&.jsoneditor-type-object {
|
|
||||||
.jsoneditor-icon {
|
|
||||||
background-position: -72px 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
&.jsoneditor-type-array {
|
|
||||||
.jsoneditor-icon {
|
|
||||||
background-position: -96px 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
&.jsoneditor-type-modes {
|
|
||||||
.jsoneditor-icon {
|
|
||||||
background-image: none;
|
|
||||||
width: 6px;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
.jsoneditor-contextmenu ul,
|
|
||||||
.jsoneditor-contextmenu li {
|
|
||||||
box-sizing: content-box;
|
|
||||||
position: relative;
|
|
||||||
}
|
|
||||||
.jsoneditor-contextmenu .jsoneditor-menu button:hover,
|
|
||||||
.jsoneditor-contextmenu .jsoneditor-menu button:focus {
|
|
||||||
color: $jse-content-color;
|
|
||||||
background-color: $jse-preview;
|
|
||||||
outline: none;
|
|
||||||
}
|
|
||||||
.jsoneditor-contextmenu .jsoneditor-menu li button.jsoneditor-selected,
|
|
||||||
.jsoneditor-contextmenu .jsoneditor-menu li button.jsoneditor-selected:hover,
|
|
||||||
.jsoneditor-contextmenu .jsoneditor-menu li button.jsoneditor-selected:focus {
|
|
||||||
color: $jse-white;
|
|
||||||
background-color: $jse-number;
|
|
||||||
}
|
|
||||||
.jsoneditor-contextmenu .jsoneditor-menu li ul li button:hover,
|
|
||||||
.jsoneditor-contextmenu .jsoneditor-menu li ul li button:focus {
|
|
||||||
background-color: $jse-preview;
|
|
||||||
}
|
|
||||||
|
|
||||||
.jsoneditor-modal {
|
|
||||||
max-width: 95%;
|
|
||||||
border-radius: 2px !important;
|
|
||||||
padding: 45px 15px 15px 15px !important;
|
|
||||||
box-shadow: $jse-box-shadow;
|
|
||||||
color: $jse-contextmenu-color;
|
|
||||||
line-height: 1.3em;
|
|
||||||
&.jsoneditor-modal-transform {
|
|
||||||
width: 600px !important;
|
|
||||||
}
|
|
||||||
.pico-modal-header {
|
|
||||||
position: absolute;
|
|
||||||
box-sizing: border-box;
|
|
||||||
top: 0;
|
|
||||||
left: 0;
|
|
||||||
width: 100%;
|
|
||||||
padding: 0 10px;
|
|
||||||
height: 30px;
|
|
||||||
line-height: 30px;
|
|
||||||
font-family: $jse-font;
|
|
||||||
font-size: 11pt;
|
|
||||||
background: $jse-blue;
|
|
||||||
color: $jse-white;
|
|
||||||
}
|
|
||||||
table {
|
|
||||||
width: 100%;
|
|
||||||
td {
|
|
||||||
padding: 3px 0;
|
|
||||||
&.jsoneditor-modal-input {
|
|
||||||
text-align: right;
|
|
||||||
padding-right: 0;
|
|
||||||
white-space: nowrap;
|
|
||||||
}
|
|
||||||
&.jsoneditor-modal-actions {
|
|
||||||
padding-top: 15px;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
th {
|
|
||||||
vertical-align: middle;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
p {
|
|
||||||
&:first-child {
|
|
||||||
margin-top: 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
a {
|
|
||||||
color: $jse-blue;
|
|
||||||
}
|
|
||||||
.jsoneditor-jmespath-block {
|
|
||||||
margin-bottom: 10px;
|
|
||||||
}
|
|
||||||
.pico-close {
|
|
||||||
background: none !important;
|
|
||||||
font-size: 24px !important;
|
|
||||||
top: 7px !important;
|
|
||||||
right: 7px !important;
|
|
||||||
color: $jse-white;
|
|
||||||
}
|
|
||||||
input {
|
|
||||||
padding: 4px;
|
|
||||||
}
|
|
||||||
input[type="text"] {
|
|
||||||
cursor: inherit;
|
|
||||||
}
|
|
||||||
input[disabled] {
|
|
||||||
background: $jse-empty;
|
|
||||||
color: $jse-readonly;
|
|
||||||
}
|
|
||||||
.jsoneditor-select-wrapper {
|
|
||||||
position: relative;
|
|
||||||
display: inline-block;
|
|
||||||
&:after {
|
|
||||||
content: "";
|
|
||||||
width: 0;
|
|
||||||
height: 0;
|
|
||||||
border-left: 5px solid transparent;
|
|
||||||
border-right: 5px solid transparent;
|
|
||||||
border-top: 6px solid #666;
|
|
||||||
position: absolute;
|
|
||||||
right: 8px;
|
|
||||||
top: 14px;
|
|
||||||
pointer-events: none;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
select {
|
|
||||||
padding: 3px 24px 3px 10px;
|
|
||||||
min-width: 180px;
|
|
||||||
max-width: 350px;
|
|
||||||
-webkit-appearance: none;
|
|
||||||
-moz-appearance: none;
|
|
||||||
appearance: none;
|
|
||||||
text-indent: 0;
|
|
||||||
text-overflow: "";
|
|
||||||
font-size: $jse-font-size;
|
|
||||||
line-height: 1.5em;
|
|
||||||
&::-ms-expand {
|
|
||||||
display: none;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
.jsoneditor-button-group {
|
|
||||||
input {
|
|
||||||
padding: 4px 10px;
|
|
||||||
margin: 0;
|
|
||||||
border-radius: 0;
|
|
||||||
border-left-style: none;
|
|
||||||
&.jsoneditor-button-first {
|
|
||||||
border-top-left-radius: 3px;
|
|
||||||
border-bottom-left-radius: 3px;
|
|
||||||
border-left-style: solid;
|
|
||||||
}
|
|
||||||
&.jsoneditor-button-last {
|
|
||||||
border-top-right-radius: 3px;
|
|
||||||
border-bottom-right-radius: 3px;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
.jsoneditor-transform-preview {
|
|
||||||
background: $jse-preview;
|
|
||||||
height: 200px;
|
|
||||||
&.jsoneditor-error {
|
|
||||||
color: $jse-number;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
.jsoneditor-jmespath-wizard {
|
|
||||||
line-height: 1.2em;
|
|
||||||
width: 100%;
|
|
||||||
padding: 0;
|
|
||||||
border-radius: 3px;
|
|
||||||
}
|
|
||||||
.jsoneditor-jmespath-label {
|
|
||||||
font-weight: bold;
|
|
||||||
color: dodgerblue;
|
|
||||||
margin-top: 20px;
|
|
||||||
margin-bottom: 5px;
|
|
||||||
}
|
|
||||||
.jsoneditor-jmespath-wizard-table {
|
|
||||||
width: 100%;
|
|
||||||
border-collapse: collapse;
|
|
||||||
}
|
|
||||||
.jsoneditor-jmespath-wizard-label {
|
|
||||||
font-style: italic;
|
|
||||||
margin: 4px 0 2px 0;
|
|
||||||
}
|
|
||||||
.jsoneditor-inline {
|
|
||||||
position: relative;
|
|
||||||
display: inline-block;
|
|
||||||
width: 100%;
|
|
||||||
padding-top: 2px;
|
|
||||||
padding-bottom: 2px;
|
|
||||||
&:not(:last-child) {
|
|
||||||
padding-right: 2px;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
.jsoneditor-jmespath-filter {
|
|
||||||
display: flex;
|
|
||||||
flex-wrap: wrap;
|
|
||||||
}
|
|
||||||
.jsoneditor-jmespath-filter-field {
|
|
||||||
width: 180px;
|
|
||||||
}
|
|
||||||
.jsoneditor-jmespath-filter-relation {
|
|
||||||
width: 100px;
|
|
||||||
}
|
|
||||||
.jsoneditor-jmespath-filter-value {
|
|
||||||
min-width: 180px;
|
|
||||||
flex: 1;
|
|
||||||
}
|
|
||||||
.jsoneditor-jmespath-sort-field {
|
|
||||||
width: 170px;
|
|
||||||
}
|
|
||||||
.jsoneditor-jmespath-sort-order {
|
|
||||||
width: 150px;
|
|
||||||
}
|
|
||||||
.jsoneditor-jmespath-select-fields {
|
|
||||||
width: 100%;
|
|
||||||
}
|
|
||||||
.selectr-selected {
|
|
||||||
border-color: $jse-bar-border;
|
|
||||||
padding: 4px 28px 4px 8px;
|
|
||||||
.selectr-tag {
|
|
||||||
background-color: $jse-blue;
|
|
||||||
border-radius: 5px;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
.jsoneditor-modal table th,
|
|
||||||
.jsoneditor-modal table td {
|
|
||||||
text-align: left;
|
|
||||||
vertical-align: middle;
|
|
||||||
font-weight: normal;
|
|
||||||
color: $jse-contextmenu-color;
|
|
||||||
border-spacing: 0;
|
|
||||||
border-collapse: collapse;
|
|
||||||
}
|
|
||||||
.jsoneditor-modal select,
|
|
||||||
.jsoneditor-modal textarea,
|
|
||||||
.jsoneditor-modal input,
|
|
||||||
.jsoneditor-modal input[type="text"],
|
|
||||||
.jsoneditor-modal input[type="text"]:focus,
|
|
||||||
.jsoneditor-modal #query {
|
|
||||||
background: #ffffff;
|
|
||||||
border: 1px solid $jse-bar-border;
|
|
||||||
color: $jse-contextmenu-color;
|
|
||||||
border-radius: 3px;
|
|
||||||
padding: 4px;
|
|
||||||
}
|
|
||||||
.jsoneditor-modal,
|
|
||||||
.jsoneditor-modal table td,
|
|
||||||
.jsoneditor-modal table th,
|
|
||||||
.jsoneditor-modal select,
|
|
||||||
.jsoneditor-modal option,
|
|
||||||
.jsoneditor-modal textarea,
|
|
||||||
.jsoneditor-modal input,
|
|
||||||
.jsoneditor-modal input[type="text"],
|
|
||||||
.jsoneditor-modal #query {
|
|
||||||
font-size: 10.5pt;
|
|
||||||
font-family: $jse-font;
|
|
||||||
}
|
|
||||||
.jsoneditor-modal #query,
|
|
||||||
.jsoneditor-modal .jsoneditor-transform-preview {
|
|
||||||
font-family: $jse-font-mono;
|
|
||||||
font-size: $jse-font-size;
|
|
||||||
width: 100%;
|
|
||||||
box-sizing: border-box;
|
|
||||||
}
|
|
||||||
.jsoneditor-modal input[type="button"],
|
|
||||||
.jsoneditor-modal input[type="submit"] {
|
|
||||||
background: $jse-preview;
|
|
||||||
padding: 4px 20px;
|
|
||||||
}
|
|
||||||
.jsoneditor-modal select,
|
|
||||||
.jsoneditor-modal input {
|
|
||||||
cursor: pointer;
|
|
||||||
}
|
|
||||||
.jsoneditor-modal .jsoneditor-button-group.jsoneditor-button-group-value-asc input.jsoneditor-button-asc,
|
|
||||||
.jsoneditor-modal .jsoneditor-button-group.jsoneditor-button-group-value-desc input.jsoneditor-button-desc {
|
|
||||||
background: $jse-blue;
|
|
||||||
border-color: $jse-blue;
|
|
||||||
color: $jse-white;
|
|
||||||
}
|
|
|
@ -1,583 +0,0 @@
|
||||||
.jsoneditor {
|
|
||||||
color: $jse-content-color;
|
|
||||||
border: thin solid $jse-blue;
|
|
||||||
-moz-box-sizing: border-box;
|
|
||||||
-webkit-box-sizing: border-box;
|
|
||||||
box-sizing: border-box;
|
|
||||||
width: 100%;
|
|
||||||
height: 100%;
|
|
||||||
position: relative;
|
|
||||||
padding: 0;
|
|
||||||
line-height: 100%;
|
|
||||||
}
|
|
||||||
div.jsoneditor-field,
|
|
||||||
div.jsoneditor-value,
|
|
||||||
div.jsoneditor-readonly,
|
|
||||||
div.jsoneditor-default {
|
|
||||||
border: 1px solid transparent;
|
|
||||||
min-height: 16px;
|
|
||||||
min-width: 32px;
|
|
||||||
padding: 2px;
|
|
||||||
margin: 1px;
|
|
||||||
word-wrap: break-word;
|
|
||||||
float: left;
|
|
||||||
}
|
|
||||||
div.jsoneditor-field p,
|
|
||||||
div.jsoneditor-value p {
|
|
||||||
margin: 0;
|
|
||||||
}
|
|
||||||
div {
|
|
||||||
&.jsoneditor-value {
|
|
||||||
word-break: break-word;
|
|
||||||
&.jsoneditor-empty {
|
|
||||||
&::after {
|
|
||||||
content: "value";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
&.jsoneditor-string {
|
|
||||||
color: $jse-string;
|
|
||||||
}
|
|
||||||
&.jsoneditor-number {
|
|
||||||
color: $jse-number;
|
|
||||||
}
|
|
||||||
&.jsoneditor-boolean {
|
|
||||||
color: $jse-boolean;
|
|
||||||
}
|
|
||||||
&.jsoneditor-null {
|
|
||||||
color: $jse-null;
|
|
||||||
}
|
|
||||||
&.jsoneditor-invalid {
|
|
||||||
color: $jse-invalid;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
&.jsoneditor-readonly {
|
|
||||||
min-width: 16px;
|
|
||||||
color: $jse-readonly;
|
|
||||||
}
|
|
||||||
&.jsoneditor-empty {
|
|
||||||
border-color: $jse-bar-border;
|
|
||||||
border-style: dashed;
|
|
||||||
border-radius: 2px;
|
|
||||||
}
|
|
||||||
&.jsoneditor-field {
|
|
||||||
&.jsoneditor-empty {
|
|
||||||
&::after {
|
|
||||||
content: "field";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
&.jsoneditor {
|
|
||||||
td {
|
|
||||||
vertical-align: top;
|
|
||||||
&.jsoneditor-separator {
|
|
||||||
padding: 3px 0;
|
|
||||||
vertical-align: top;
|
|
||||||
color: $jse-readonly;
|
|
||||||
}
|
|
||||||
&.jsoneditor-tree {
|
|
||||||
vertical-align: top;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
&.busy {
|
|
||||||
pre {
|
|
||||||
&.jsoneditor-preview {
|
|
||||||
background: $jse-preview;
|
|
||||||
color: $jse-readonly;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
div {
|
|
||||||
&.jsoneditor-busy {
|
|
||||||
display: inherit;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
code {
|
|
||||||
&.jsoneditor-preview {
|
|
||||||
background: none;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
&.jsoneditor-mode-preview {
|
|
||||||
pre {
|
|
||||||
&.jsoneditor-preview {
|
|
||||||
width: 100%;
|
|
||||||
height: 100%;
|
|
||||||
box-sizing: border-box;
|
|
||||||
overflow: auto;
|
|
||||||
padding: 2px;
|
|
||||||
margin: 0;
|
|
||||||
white-space: pre-wrap;
|
|
||||||
word-break: break-all;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
&.jsoneditor-default {
|
|
||||||
color: $jse-readonly;
|
|
||||||
padding-left: 10px;
|
|
||||||
}
|
|
||||||
&.jsoneditor-tree {
|
|
||||||
width: 100%;
|
|
||||||
height: 100%;
|
|
||||||
position: relative;
|
|
||||||
overflow: auto;
|
|
||||||
button {
|
|
||||||
&.jsoneditor-button {
|
|
||||||
width: 24px;
|
|
||||||
height: 24px;
|
|
||||||
padding: 0;
|
|
||||||
margin: 0;
|
|
||||||
border: none;
|
|
||||||
cursor: pointer;
|
|
||||||
background: transparent url($jse-icons);
|
|
||||||
&:focus {
|
|
||||||
background-color: $jse-preview;
|
|
||||||
outline: #e5e5e5 solid 1px;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
&.jsoneditor-collapsed {
|
|
||||||
background-position: 0 -48px;
|
|
||||||
}
|
|
||||||
&.jsoneditor-expanded {
|
|
||||||
background-position: 0 -72px;
|
|
||||||
}
|
|
||||||
&.jsoneditor-contextmenu-button {
|
|
||||||
background-position: -48px -72px;
|
|
||||||
}
|
|
||||||
&.jsoneditor-invisible {
|
|
||||||
visibility: hidden;
|
|
||||||
background: none;
|
|
||||||
}
|
|
||||||
&.jsoneditor-dragarea {
|
|
||||||
background: url($jse-icons) -72px -72px;
|
|
||||||
cursor: move;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
*:focus {
|
|
||||||
outline: none;
|
|
||||||
}
|
|
||||||
div {
|
|
||||||
&.jsoneditor-show-more {
|
|
||||||
display: inline-block;
|
|
||||||
padding: 3px 4px;
|
|
||||||
margin: 2px 0;
|
|
||||||
background-color: $jse-separator;
|
|
||||||
border-radius: 3px;
|
|
||||||
color: $jse-readonly;
|
|
||||||
font-family: $jse-font;
|
|
||||||
font-size: $jse-font-size;
|
|
||||||
a {
|
|
||||||
display: inline-block;
|
|
||||||
color: $jse-readonly;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
&.jsoneditor-color {
|
|
||||||
display: inline-block;
|
|
||||||
width: 12px;
|
|
||||||
height: 12px;
|
|
||||||
margin: 4px;
|
|
||||||
border: 1px solid $jse-readonly;
|
|
||||||
cursor: pointer;
|
|
||||||
}
|
|
||||||
&.jsoneditor-date {
|
|
||||||
background: $jse-date;
|
|
||||||
color: $jse-white;
|
|
||||||
font-family: $jse-font;
|
|
||||||
border-radius: 3px;
|
|
||||||
display: inline-block;
|
|
||||||
padding: 3px;
|
|
||||||
margin: 0 3px;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
table {
|
|
||||||
&.jsoneditor-tree {
|
|
||||||
border-collapse: collapse;
|
|
||||||
border-spacing: 0;
|
|
||||||
width: 100%;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
.jsoneditor-button {
|
|
||||||
&.jsoneditor-schema-error {
|
|
||||||
width: 24px;
|
|
||||||
height: 24px;
|
|
||||||
padding: 0;
|
|
||||||
margin: 0 4px 0 0;
|
|
||||||
background: url($jse-icons) -168px -48px;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
&.jsoneditor-outer {
|
|
||||||
position: static;
|
|
||||||
width: 100%;
|
|
||||||
height: 100%;
|
|
||||||
margin: 0;
|
|
||||||
padding: 0;
|
|
||||||
-moz-box-sizing: border-box;
|
|
||||||
-webkit-box-sizing: border-box;
|
|
||||||
box-sizing: border-box;
|
|
||||||
&.has-nav-bar {
|
|
||||||
margin-top: -26px;
|
|
||||||
padding-top: 26px;
|
|
||||||
&.has-main-menu-bar {
|
|
||||||
margin-top: -61px;
|
|
||||||
padding-top: 61px;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
&.has-status-bar {
|
|
||||||
margin-bottom: -26px;
|
|
||||||
padding-bottom: 26px;
|
|
||||||
}
|
|
||||||
&.has-main-menu-bar {
|
|
||||||
margin-top: -35px;
|
|
||||||
padding-top: 35px;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
&.jsoneditor-busy {
|
|
||||||
position: absolute;
|
|
||||||
top: 15%;
|
|
||||||
left: 0;
|
|
||||||
box-sizing: border-box;
|
|
||||||
width: 100%;
|
|
||||||
text-align: center;
|
|
||||||
display: none;
|
|
||||||
span {
|
|
||||||
background-color: $jse-busy;
|
|
||||||
border: 1px solid $jse-busy-border-color;
|
|
||||||
border-radius: 3px;
|
|
||||||
padding: 5px 15px;
|
|
||||||
box-shadow: $jse-box-shadow-sm;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
div.jsoneditor-field.jsoneditor-empty::after,
|
|
||||||
div.jsoneditor-value.jsoneditor-empty::after {
|
|
||||||
pointer-events: none;
|
|
||||||
color: $jse-empty;
|
|
||||||
font-size: 8pt;
|
|
||||||
}
|
|
||||||
div.jsoneditor-value.jsoneditor-url,
|
|
||||||
a.jsoneditor-value.jsoneditor-url {
|
|
||||||
color: $jse-string;
|
|
||||||
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: $jse-number;
|
|
||||||
}
|
|
||||||
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: $jse-busy;
|
|
||||||
border: 1px solid $jse-busy-border-color;
|
|
||||||
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: $jse-highlight-bg;
|
|
||||||
border: 1px solid $jse-highlight-border-color;
|
|
||||||
border-radius: 2px;
|
|
||||||
}
|
|
||||||
div.jsoneditor-value.jsoneditor-object,
|
|
||||||
div.jsoneditor-value.jsoneditor-array {
|
|
||||||
min-width: 16px;
|
|
||||||
}
|
|
||||||
div.jsoneditor-tree button.jsoneditor-contextmenu-button:hover,
|
|
||||||
div.jsoneditor-tree button.jsoneditor-contextmenu-button:focus,
|
|
||||||
div.jsoneditor-tree button.jsoneditor-contextmenu-button.jsoneditor-selected,
|
|
||||||
tr.jsoneditor-selected.jsoneditor-first button.jsoneditor-contextmenu-button {
|
|
||||||
background-position: -48px -48px;
|
|
||||||
}
|
|
||||||
div.jsoneditor-tree div.jsoneditor-show-more a:hover,
|
|
||||||
div.jsoneditor-tree div.jsoneditor-show-more a:focus {
|
|
||||||
color: $jse-number;
|
|
||||||
}
|
|
||||||
textarea.jsoneditor-text,
|
|
||||||
.ace-jsoneditor {
|
|
||||||
min-height: 150px;
|
|
||||||
|
|
||||||
* {
|
|
||||||
font-family: $jse-font-mono;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
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: $jse-white;
|
|
||||||
resize: none;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
tr.jsoneditor-highlight,
|
|
||||||
tr.jsoneditor-selected {
|
|
||||||
background-color: $jse-empty;
|
|
||||||
}
|
|
||||||
tr.jsoneditor-selected button.jsoneditor-dragarea,
|
|
||||||
tr.jsoneditor-selected button.jsoneditor-contextmenu-button {
|
|
||||||
visibility: hidden;
|
|
||||||
}
|
|
||||||
tr.jsoneditor-selected.jsoneditor-first button.jsoneditor-dragarea,
|
|
||||||
tr.jsoneditor-selected.jsoneditor-first button.jsoneditor-contextmenu-button {
|
|
||||||
visibility: visible;
|
|
||||||
}
|
|
||||||
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-field,
|
|
||||||
div.jsoneditor-value,
|
|
||||||
div.jsoneditor td,
|
|
||||||
div.jsoneditor th,
|
|
||||||
div.jsoneditor textarea,
|
|
||||||
pre.jsoneditor-preview,
|
|
||||||
.jsoneditor-schema-error,
|
|
||||||
.jsoneditor-popover{
|
|
||||||
font-family: $jse-font-mono;
|
|
||||||
font-size: $jse-font-size;
|
|
||||||
color: $jse-content-color;
|
|
||||||
}
|
|
||||||
|
|
||||||
.jsoneditor-schema-error {
|
|
||||||
cursor: default;
|
|
||||||
display: inline-block;
|
|
||||||
height: 24px;
|
|
||||||
line-height: 24px;
|
|
||||||
position: relative;
|
|
||||||
text-align: center;
|
|
||||||
width: 24px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.jsoneditor-popover {
|
|
||||||
background-color: $jse-popover-bg;
|
|
||||||
border-radius: 3px;
|
|
||||||
box-shadow: $jse-box-shadow-sm;
|
|
||||||
color: $jse-white;
|
|
||||||
padding: 7px 10px;
|
|
||||||
position: absolute;
|
|
||||||
cursor: auto;
|
|
||||||
width: 200px;
|
|
||||||
&.jsoneditor-above {
|
|
||||||
bottom: 32px;
|
|
||||||
left: -98px;
|
|
||||||
&:before {
|
|
||||||
border-top: 7px solid $jse-popover-bg;
|
|
||||||
bottom: -7px;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
&.jsoneditor-below {
|
|
||||||
top: 32px;
|
|
||||||
left: -98px;
|
|
||||||
&:before {
|
|
||||||
border-bottom: 7px solid $jse-popover-bg;
|
|
||||||
top: -7px;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
&.jsoneditor-left {
|
|
||||||
top: -7px;
|
|
||||||
right: 32px;
|
|
||||||
&:before {
|
|
||||||
border-left: 7px solid $jse-popover-bg;
|
|
||||||
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-right {
|
|
||||||
top: -7px;
|
|
||||||
left: 32px;
|
|
||||||
&:before {
|
|
||||||
border-right: 7px solid $jse-popover-bg;
|
|
||||||
border-top: 7px solid transparent;
|
|
||||||
border-bottom: 7px solid transparent;
|
|
||||||
content: "";
|
|
||||||
top: 19px;
|
|
||||||
left: -14px;
|
|
||||||
margin-left: inherit;
|
|
||||||
margin-top: -7px;
|
|
||||||
position: absolute;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
&:before {
|
|
||||||
border-right: 7px solid transparent;
|
|
||||||
border-left: 7px solid transparent;
|
|
||||||
content: "";
|
|
||||||
display: block;
|
|
||||||
left: 50%;
|
|
||||||
margin-left: -7px;
|
|
||||||
position: absolute;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.jsoneditor-text-errors {
|
|
||||||
tr {
|
|
||||||
&.jump-to-line {
|
|
||||||
&:hover {
|
|
||||||
text-decoration: underline;
|
|
||||||
cursor: pointer;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
.jsoneditor-schema-error:hover .jsoneditor-popover,
|
|
||||||
.jsoneditor-schema-error:focus .jsoneditor-popover {
|
|
||||||
display: block;
|
|
||||||
animation: fade-in 0.3s linear 1, move-up 0.3s linear 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
@keyframes fade-in {
|
|
||||||
from {
|
|
||||||
opacity: 0;
|
|
||||||
}
|
|
||||||
to {
|
|
||||||
opacity: 1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/* JSON schema errors displayed at the bottom of the editor in mode text and code */
|
|
||||||
|
|
||||||
.jsoneditor {
|
|
||||||
.jsoneditor-validation-errors-container {
|
|
||||||
max-height: 130px;
|
|
||||||
overflow-y: auto;
|
|
||||||
}
|
|
||||||
.jsoneditor-validation-errors {
|
|
||||||
width: 100%;
|
|
||||||
overflow: hidden;
|
|
||||||
}
|
|
||||||
.jsoneditor-additional-errors {
|
|
||||||
position: absolute;
|
|
||||||
margin: auto;
|
|
||||||
bottom: 31px;
|
|
||||||
left: calc(50% - 92px);
|
|
||||||
color: $jse-readonly;
|
|
||||||
background-color: $jse-light-bg;
|
|
||||||
padding: 7px 15px;
|
|
||||||
border-radius: 8px;
|
|
||||||
&.visible {
|
|
||||||
visibility: visible;
|
|
||||||
opacity: 1;
|
|
||||||
transition: opacity 2s linear;
|
|
||||||
}
|
|
||||||
&.hidden {
|
|
||||||
visibility: hidden;
|
|
||||||
opacity: 0;
|
|
||||||
transition: visibility 0s 2s, opacity 2s linear;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
.jsoneditor-text-errors {
|
|
||||||
width: 100%;
|
|
||||||
border-collapse: collapse;
|
|
||||||
border-top: 1px solid $jse-highlight-border-color;
|
|
||||||
td {
|
|
||||||
padding: 3px 6px;
|
|
||||||
vertical-align: middle;
|
|
||||||
pre {
|
|
||||||
margin: 0;
|
|
||||||
white-space: normal;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
tr {
|
|
||||||
background-color: $jse-busy;
|
|
||||||
&.parse-error {
|
|
||||||
background-color: $jse-error;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
.jsoneditor-text-errors {
|
|
||||||
.jsoneditor-schema-error {
|
|
||||||
border: none;
|
|
||||||
width: 24px;
|
|
||||||
height: 24px;
|
|
||||||
padding: 0;
|
|
||||||
margin: 0 4px 0 0;
|
|
||||||
cursor: pointer;
|
|
||||||
}
|
|
||||||
tr {
|
|
||||||
.jsoneditor-schema-error {
|
|
||||||
background: url($jse-icons) -168px -48px;
|
|
||||||
}
|
|
||||||
&.parse-error {
|
|
||||||
.jsoneditor-schema-error {
|
|
||||||
background: url($jse-icons) -25px 0px;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.jsoneditor-anchor {
|
|
||||||
cursor: pointer;
|
|
||||||
|
|
||||||
.picker_wrapper {
|
|
||||||
&.popup {
|
|
||||||
&.popup_bottom {
|
|
||||||
top: 28px;
|
|
||||||
left: -10px;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.fadein {
|
|
||||||
-webkit-animation: fadein 0.3s;
|
|
||||||
animation: fadein 0.3s;
|
|
||||||
-moz-animation: fadein 0.3s;
|
|
||||||
-o-animation: fadein 0.3s;
|
|
||||||
}
|
|
||||||
|
|
||||||
@keyframes fadein {
|
|
||||||
0% {
|
|
||||||
opacity: 0;
|
|
||||||
}
|
|
||||||
100% {
|
|
||||||
opacity: 1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// override some styles which where cleared in reset.scss
|
|
||||||
.jsoneditor-modal {
|
|
||||||
input[type="search"].selectr-input {
|
|
||||||
border: 1px solid #d3d3d3;
|
|
||||||
width: calc(100% - 4px);
|
|
||||||
margin: 2px;
|
|
||||||
padding: 4px;
|
|
||||||
box-sizing: border-box;
|
|
||||||
}
|
|
||||||
|
|
||||||
button.selectr-input-clear {
|
|
||||||
right: 8px;
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,126 +0,0 @@
|
||||||
.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: $jse-white;
|
|
||||||
background-color: $jse-blue;
|
|
||||||
border-bottom: 1px solid $jse-blue;
|
|
||||||
}
|
|
||||||
|
|
||||||
.jsoneditor-menu > button,
|
|
||||||
.jsoneditor-menu > .jsoneditor-modes > button {
|
|
||||||
width: 26px;
|
|
||||||
height: 26px;
|
|
||||||
margin: 2px;
|
|
||||||
padding: 0;
|
|
||||||
border-radius: 2px;
|
|
||||||
border: 1px solid transparent;
|
|
||||||
background: transparent url($jse-icons);
|
|
||||||
color: $jse-white;
|
|
||||||
opacity: 0.8;
|
|
||||||
|
|
||||||
font-family: $jse-font;
|
|
||||||
font-size: $jse-font-size;
|
|
||||||
|
|
||||||
float: left;
|
|
||||||
}
|
|
||||||
|
|
||||||
.jsoneditor-menu > button:hover,
|
|
||||||
.jsoneditor-menu > .jsoneditor-modes > button:hover {
|
|
||||||
background-color: rgba(255,255,255,0.2);
|
|
||||||
border: 1px solid rgba(255,255,255,0.4);
|
|
||||||
}
|
|
||||||
.jsoneditor-menu > button:focus,
|
|
||||||
.jsoneditor-menu > button:active,
|
|
||||||
.jsoneditor-menu > .jsoneditor-modes > button:focus,
|
|
||||||
.jsoneditor-menu > .jsoneditor-modes > button:active {
|
|
||||||
background-color: rgba(255,255,255,0.3);
|
|
||||||
}
|
|
||||||
.jsoneditor-menu > button:disabled,
|
|
||||||
.jsoneditor-menu > .jsoneditor-modes > button:disabled {
|
|
||||||
opacity: 0.5;
|
|
||||||
background-color: transparent;
|
|
||||||
border: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
.jsoneditor-menu > button.jsoneditor-collapse-all {
|
|
||||||
background-position: 0 -96px;
|
|
||||||
}
|
|
||||||
.jsoneditor-menu > button.jsoneditor-expand-all {
|
|
||||||
background-position: 0 -120px;
|
|
||||||
}
|
|
||||||
.jsoneditor-menu > button.jsoneditor-sort {
|
|
||||||
background-position: -120px -96px;
|
|
||||||
}
|
|
||||||
.jsoneditor-menu > button.jsoneditor-transform {
|
|
||||||
background-position: -144px -96px;
|
|
||||||
}
|
|
||||||
.jsoneditor.jsoneditor-mode-view > .jsoneditor-menu > button.jsoneditor-sort,
|
|
||||||
.jsoneditor.jsoneditor-mode-form > .jsoneditor-menu > button.jsoneditor-sort,
|
|
||||||
.jsoneditor.jsoneditor-mode-view > .jsoneditor-menu > button.jsoneditor-transform,
|
|
||||||
.jsoneditor.jsoneditor-mode-form > .jsoneditor-menu > button.jsoneditor-transform {
|
|
||||||
display: none;
|
|
||||||
}
|
|
||||||
.jsoneditor-menu > button.jsoneditor-undo {
|
|
||||||
background-position: -24px -96px;
|
|
||||||
}
|
|
||||||
.jsoneditor-menu > button.jsoneditor-undo:disabled {
|
|
||||||
background-position: -24px -120px;
|
|
||||||
}
|
|
||||||
.jsoneditor-menu > button.jsoneditor-redo {
|
|
||||||
background-position: -48px -96px;
|
|
||||||
}
|
|
||||||
.jsoneditor-menu > button.jsoneditor-redo:disabled {
|
|
||||||
background-position: -48px -120px;
|
|
||||||
}
|
|
||||||
.jsoneditor-menu > button.jsoneditor-compact {
|
|
||||||
background-position: -72px -96px;
|
|
||||||
}
|
|
||||||
.jsoneditor-menu > button.jsoneditor-format {
|
|
||||||
background-position: -72px -120px;
|
|
||||||
}
|
|
||||||
.jsoneditor-menu > button.jsoneditor-repair {
|
|
||||||
background-position: -96px -96px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.jsoneditor-menu > .jsoneditor-modes {
|
|
||||||
display: inline-block;
|
|
||||||
float: left;
|
|
||||||
}
|
|
||||||
|
|
||||||
.jsoneditor-menu > .jsoneditor-modes > button {
|
|
||||||
background-image: none;
|
|
||||||
width: auto;
|
|
||||||
padding-left: 6px;
|
|
||||||
padding-right: 6px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.jsoneditor-menu > button.jsoneditor-separator,
|
|
||||||
.jsoneditor-menu > .jsoneditor-modes > button.jsoneditor-separator {
|
|
||||||
margin-left: 10px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.jsoneditor-menu a {
|
|
||||||
font-family: $jse-font;
|
|
||||||
font-size: $jse-font-size;
|
|
||||||
color: $jse-white;
|
|
||||||
opacity: 0.8;
|
|
||||||
vertical-align: middle;
|
|
||||||
}
|
|
||||||
|
|
||||||
.jsoneditor-menu a:hover {
|
|
||||||
opacity: 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
.jsoneditor-menu a.jsoneditor-poweredBy {
|
|
||||||
font-size: 8pt;
|
|
||||||
position: absolute;
|
|
||||||
right: 0;
|
|
||||||
top: 0;
|
|
||||||
padding: 10px;
|
|
||||||
}
|
|
|
@ -1,17 +0,0 @@
|
||||||
.jsoneditor-navigation-bar {
|
|
||||||
width: 100%;
|
|
||||||
height: 26px;
|
|
||||||
line-height: 26px;
|
|
||||||
padding: 0;
|
|
||||||
margin: 0;
|
|
||||||
border-bottom: 1px solid $jse-bar-border;
|
|
||||||
-moz-box-sizing: border-box;
|
|
||||||
-webkit-box-sizing: border-box;
|
|
||||||
box-sizing: border-box;
|
|
||||||
color: $jse-readonly;
|
|
||||||
background-color: $jse-light-bg;
|
|
||||||
overflow: hidden;
|
|
||||||
|
|
||||||
font-family: $jse-font;
|
|
||||||
font-size: $jse-font-size;
|
|
||||||
}
|
|
|
@ -1,51 +0,0 @@
|
||||||
.jsoneditor,
|
|
||||||
.jsoneditor-modal {
|
|
||||||
input,
|
|
||||||
input:not([type]),
|
|
||||||
input[type="text"],
|
|
||||||
input[type="search"], {
|
|
||||||
height: auto;
|
|
||||||
border: inherit;
|
|
||||||
box-shadow: none;
|
|
||||||
font-size: inherit;
|
|
||||||
box-sizing: inherit;
|
|
||||||
padding: inherit;
|
|
||||||
font-family: inherit;
|
|
||||||
transition: none;
|
|
||||||
line-height: inherit;
|
|
||||||
|
|
||||||
&:focus {
|
|
||||||
border: inherit;
|
|
||||||
box-shadow: inherit;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
textarea {
|
|
||||||
height: inherit;
|
|
||||||
}
|
|
||||||
|
|
||||||
select {
|
|
||||||
display: inherit;
|
|
||||||
height: inherit;
|
|
||||||
}
|
|
||||||
|
|
||||||
label {
|
|
||||||
font-size: inherit;
|
|
||||||
font-weight: inherit;
|
|
||||||
color: inherit;
|
|
||||||
}
|
|
||||||
|
|
||||||
table {
|
|
||||||
border-collapse: collapse;
|
|
||||||
width: auto;
|
|
||||||
}
|
|
||||||
|
|
||||||
td,
|
|
||||||
th {
|
|
||||||
padding: 0;
|
|
||||||
display: table-cell;
|
|
||||||
text-align: left;
|
|
||||||
vertical-align: inherit;
|
|
||||||
border-radius: inherit;
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,72 +0,0 @@
|
||||||
.jsoneditor {
|
|
||||||
&-search {
|
|
||||||
font-family: $jse-font;
|
|
||||||
position: absolute;
|
|
||||||
right: 4px;
|
|
||||||
top: 4px;
|
|
||||||
border-collapse: collapse;
|
|
||||||
border-spacing: 0;
|
|
||||||
display: flex;
|
|
||||||
|
|
||||||
input {
|
|
||||||
color: $jse-content-color;
|
|
||||||
width: 120px;
|
|
||||||
border: none;
|
|
||||||
outline: none;
|
|
||||||
margin: 1px;
|
|
||||||
line-height: 20px;
|
|
||||||
font-family: $jse-font;
|
|
||||||
}
|
|
||||||
|
|
||||||
button {
|
|
||||||
width: 16px;
|
|
||||||
height: 24px;
|
|
||||||
padding: 0;
|
|
||||||
margin: 0;
|
|
||||||
border: none;
|
|
||||||
background: url($jse-icons);
|
|
||||||
vertical-align: top;
|
|
||||||
|
|
||||||
&:hover {
|
|
||||||
background-color: transparent;
|
|
||||||
}
|
|
||||||
|
|
||||||
&.jsoneditor-refresh {
|
|
||||||
width: 18px;
|
|
||||||
background-position: -99px -73px;
|
|
||||||
}
|
|
||||||
|
|
||||||
&.jsoneditor-next {
|
|
||||||
cursor: pointer;
|
|
||||||
background-position: -124px -73px;
|
|
||||||
|
|
||||||
&:hover {
|
|
||||||
background-position: -124px -49px;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
&.jsoneditor-previous {
|
|
||||||
cursor: pointer;
|
|
||||||
background-position: -148px -73px;
|
|
||||||
margin-right: 2px;
|
|
||||||
|
|
||||||
&:hover {
|
|
||||||
background-position: -148px -49px;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
&-results {
|
|
||||||
font-family: $jse-font;
|
|
||||||
color: $jse-white;
|
|
||||||
padding-right: 5px;
|
|
||||||
line-height: 26px;
|
|
||||||
}
|
|
||||||
|
|
||||||
&-frame {
|
|
||||||
border: 1px solid transparent;
|
|
||||||
background-color: $jse-white;
|
|
||||||
padding: 0 2px;
|
|
||||||
margin: 0;
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,48 +0,0 @@
|
||||||
.jsoneditor-statusbar {
|
|
||||||
line-height: 26px;
|
|
||||||
height: 26px;
|
|
||||||
color: $jse-readonly;
|
|
||||||
background-color: $jse-bar-bg;
|
|
||||||
border-top: 1px solid $jse-bar-border;
|
|
||||||
-moz-box-sizing: border-box;
|
|
||||||
-webkit-box-sizing: border-box;
|
|
||||||
box-sizing: border-box;
|
|
||||||
font-size: $jse-font-size;
|
|
||||||
& > .jsoneditor-curserinfo-val {
|
|
||||||
margin-right: 12px;
|
|
||||||
}
|
|
||||||
& > .jsoneditor-curserinfo-count {
|
|
||||||
margin-left: 4px;
|
|
||||||
}
|
|
||||||
& > .jsoneditor-validation-error-icon {
|
|
||||||
float: right;
|
|
||||||
width: 24px;
|
|
||||||
height: 24px;
|
|
||||||
padding: 0;
|
|
||||||
margin-top: 1px;
|
|
||||||
background: url($jse-icons) -168px -48px;
|
|
||||||
cursor: pointer;
|
|
||||||
}
|
|
||||||
& > .jsoneditor-validation-error-count {
|
|
||||||
float: right;
|
|
||||||
margin: 0 4px 0 0;
|
|
||||||
cursor: pointer;
|
|
||||||
}
|
|
||||||
& > .jsoneditor-parse-error-icon {
|
|
||||||
float: right;
|
|
||||||
width: 24px;
|
|
||||||
height: 24px;
|
|
||||||
padding: 0;
|
|
||||||
margin: 1px;
|
|
||||||
background: url($jse-icons) -25px 0px;
|
|
||||||
}
|
|
||||||
.jsoneditor-array-info {
|
|
||||||
a {
|
|
||||||
color: inherit;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
div.jsoneditor-statusbar > .jsoneditor-curserinfo-label,
|
|
||||||
div.jsoneditor-statusbar > .jsoneditor-size-info {
|
|
||||||
margin: 0 4px;
|
|
||||||
}
|
|
|
@ -1,48 +0,0 @@
|
||||||
.jsoneditor-treepath {
|
|
||||||
padding: 0 5px;
|
|
||||||
overflow: hidden;
|
|
||||||
white-space: nowrap;
|
|
||||||
outline: none;
|
|
||||||
&.show-all {
|
|
||||||
word-wrap: break-word;
|
|
||||||
white-space: normal;
|
|
||||||
position: absolute;
|
|
||||||
background-color: $jse-light-bg;
|
|
||||||
z-index: 1;
|
|
||||||
box-shadow: $jse-box-shadow;
|
|
||||||
span {
|
|
||||||
&.jsoneditor-treepath-show-all-btn {
|
|
||||||
display: none;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
div {
|
|
||||||
&.jsoneditor-contextmenu-root {
|
|
||||||
position: absolute;
|
|
||||||
left: 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
.jsoneditor-treepath-show-all-btn {
|
|
||||||
position: absolute;
|
|
||||||
background-color: $jse-light-bg;
|
|
||||||
left: 0;
|
|
||||||
height: 20px;
|
|
||||||
padding: 0 3px;
|
|
||||||
cursor: pointer;
|
|
||||||
}
|
|
||||||
.jsoneditor-treepath-element {
|
|
||||||
margin: 1px;
|
|
||||||
font-family: $jse-font;
|
|
||||||
font-size: $jse-font-size;
|
|
||||||
}
|
|
||||||
.jsoneditor-treepath-seperator {
|
|
||||||
margin: 2px;
|
|
||||||
font-size: 9pt;
|
|
||||||
font-family: $jse-font;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
.jsoneditor-treepath span.jsoneditor-treepath-element:hover,
|
|
||||||
.jsoneditor-treepath span.jsoneditor-treepath-seperator:hover {
|
|
||||||
cursor: pointer;
|
|
||||||
text-decoration: underline;
|
|
||||||
}
|
|
|
@ -1,41 +0,0 @@
|
||||||
$jse-white: #ffffff !default;
|
|
||||||
$jse-grey:#999999 !default;
|
|
||||||
$jse-light-bg: #ebebeb !default;
|
|
||||||
$jse-blue: #3883fa !default;
|
|
||||||
$jse-content-color: #1a1a1a !default;
|
|
||||||
|
|
||||||
$jse-string: #006000 !default;
|
|
||||||
$jse-number: #ee422e !default;
|
|
||||||
$jse-boolean: #ff8c00 !default;
|
|
||||||
$jse-null: #004ed0 !default;
|
|
||||||
$jse-invalid: #000000 !default;
|
|
||||||
$jse-readonly: #808080 !default;
|
|
||||||
$jse-empty: #d3d3d3 !default;
|
|
||||||
$jse-preview: #f5f5f5 !default;
|
|
||||||
$jse-busy: #ffffab !default;
|
|
||||||
$jse-busy-border-color: #ffee00 !default;
|
|
||||||
|
|
||||||
$jse-error: #ee2e2e70 !default;
|
|
||||||
$jse-separator: #e5e5e5 !default;
|
|
||||||
$jse-highlight-bg: #ffee00 !default;
|
|
||||||
$jse-highlight-border-color: #ffc700 !default;
|
|
||||||
|
|
||||||
$jse-popover-bg: #4c4c4c !default;
|
|
||||||
$jse-bar-bg: $jse-light-bg !default;
|
|
||||||
$jse-bar-border: $jse-empty !default;
|
|
||||||
|
|
||||||
$jse-menu-color: $jse-empty !default;
|
|
||||||
|
|
||||||
$jse-contextmenu-color: #4d4d4d !default;
|
|
||||||
|
|
||||||
$jse-box-shadow: 2px 2px 12px rgba(128, 128, 128, 0.3) !default;
|
|
||||||
$jse-box-shadow-sm: 0 0 5px rgba(0, 0, 0, 0.4) !default;
|
|
||||||
$jse-box-shadow-inner: inset 0 0 10px rgba(128, 128, 128, 0.5) !default;
|
|
||||||
|
|
||||||
$jse-date: #a1a1a1 !default;
|
|
||||||
|
|
||||||
$jse-font: arial, sans-serif !default;
|
|
||||||
$jse-font-mono: "dejavu sans mono", "droid sans mono", consolas, monaco, "lucida console", "courier new", courier, monospace, sans-serif !default;
|
|
||||||
$jse-font-size: 10pt !default;;
|
|
||||||
|
|
||||||
$jse-icons: "./img/jsoneditor-icons.svg" !default;
|
|
|
@ -0,0 +1,67 @@
|
||||||
|
export const SEARCH_PROPERTY = Symbol('searchProperty')
|
||||||
|
export const SEARCH_VALUE = Symbol('searchValue')
|
||||||
|
|
||||||
|
export function getType (json) {
|
||||||
|
return Array.isArray(json)
|
||||||
|
? 'array'
|
||||||
|
: json && typeof json === 'object'
|
||||||
|
? 'object'
|
||||||
|
: 'value'
|
||||||
|
}
|
||||||
|
|
||||||
|
export function search (key, value, searchText) {
|
||||||
|
let results
|
||||||
|
|
||||||
|
if (typeof key === 'string' && containsCaseInsensitive(key, searchText)) {
|
||||||
|
results = createOrAdd(results, SEARCH_PROPERTY, true)
|
||||||
|
}
|
||||||
|
|
||||||
|
const type = getType(value)
|
||||||
|
if (type === 'array') {
|
||||||
|
value.forEach((item, index) => {
|
||||||
|
let childResults = search(index, item, searchText)
|
||||||
|
if (childResults) {
|
||||||
|
results = createOrAdd(results, index, childResults)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
} else if (type === 'object') {
|
||||||
|
Object.keys(value).forEach(prop => {
|
||||||
|
let childResults = search(prop, value[prop], searchText)
|
||||||
|
if (childResults) {
|
||||||
|
results = createOrAdd(results, prop, childResults)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
} else { // type === 'value'
|
||||||
|
if (containsCaseInsensitive(value, searchText)) {
|
||||||
|
results = createOrAdd(results, SEARCH_VALUE, true)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return results
|
||||||
|
}
|
||||||
|
|
||||||
|
function createOrAdd(object, key, value) {
|
||||||
|
if (object) {
|
||||||
|
object[key] = value
|
||||||
|
return object
|
||||||
|
|
||||||
|
// return {
|
||||||
|
// ...object,
|
||||||
|
// [key]: value
|
||||||
|
// }
|
||||||
|
} else {
|
||||||
|
return {
|
||||||
|
[key]: value
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Do a case insensitive search for a search text in a text
|
||||||
|
* @param {String} text
|
||||||
|
* @param {String} searchText
|
||||||
|
* @return {boolean} Returns true if `search` is found in `text`
|
||||||
|
*/
|
||||||
|
export function containsCaseInsensitive (text, searchText) {
|
||||||
|
return String(text).toLowerCase().indexOf(searchText.toLowerCase()) !== -1
|
||||||
|
}
|
|
@ -0,0 +1,13 @@
|
||||||
|
import { getType } from './utils.js'
|
||||||
|
import assert from 'assert'
|
||||||
|
|
||||||
|
describe('utils', () => {
|
||||||
|
it('should test equality of positive values', function () {
|
||||||
|
assert.strictEqual(getType([]), 'array')
|
||||||
|
assert.strictEqual(getType({}), 'object')
|
||||||
|
assert.strictEqual(getType(null), 'value')
|
||||||
|
assert.strictEqual(getType(2), 'value')
|
||||||
|
assert.strictEqual(getType('hello'), 'value')
|
||||||
|
assert.strictEqual(getType('hello'), 'value')
|
||||||
|
})
|
||||||
|
})
|
|
@ -1,285 +0,0 @@
|
||||||
import assert from 'assert'
|
|
||||||
import './setup'
|
|
||||||
import { Node } from '../src/js/Node'
|
|
||||||
|
|
||||||
describe('Node', () => {
|
|
||||||
describe('_findSchema', () => {
|
|
||||||
it('should find schema', () => {
|
|
||||||
const schema = {
|
|
||||||
type: 'object',
|
|
||||||
properties: {
|
|
||||||
child: {
|
|
||||||
type: 'string'
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
const path = ['child']
|
|
||||||
assert.strictEqual(Node._findSchema(schema, {}, path), schema.properties.child)
|
|
||||||
})
|
|
||||||
|
|
||||||
it('should find schema inside an array item', () => {
|
|
||||||
const schema = {
|
|
||||||
properties: {
|
|
||||||
job: {
|
|
||||||
type: 'array',
|
|
||||||
items: {
|
|
||||||
type: 'object',
|
|
||||||
properties: {
|
|
||||||
company: {
|
|
||||||
enum: ['test1', 'test2']
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
assert.strictEqual(Node._findSchema(schema, {}, []), schema)
|
|
||||||
|
|
||||||
assert.strictEqual(Node._findSchema(schema, {}, ['job']), schema.properties.job)
|
|
||||||
|
|
||||||
assert.strictEqual(Node._findSchema(schema, {}, ['job', 0]),
|
|
||||||
schema.properties.job.items)
|
|
||||||
|
|
||||||
assert.strictEqual(Node._findSchema(schema, {}, ['job', 0, 'company']),
|
|
||||||
schema.properties.job.items.properties.company)
|
|
||||||
})
|
|
||||||
|
|
||||||
it('should find schema within multi-level object properties', () => {
|
|
||||||
const schema = {
|
|
||||||
type: 'object',
|
|
||||||
properties: {
|
|
||||||
levelTwo: {
|
|
||||||
type: 'object',
|
|
||||||
properties: {
|
|
||||||
levelThree: {
|
|
||||||
type: 'object',
|
|
||||||
properties: {
|
|
||||||
bool: {
|
|
||||||
type: 'boolean'
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
let path = []
|
|
||||||
assert.strictEqual(Node._findSchema(schema, {}, path), schema)
|
|
||||||
path = ['levelTwo']
|
|
||||||
assert.strictEqual(Node._findSchema(schema, {}, path), schema.properties.levelTwo)
|
|
||||||
path = ['levelTwo', 'levelThree']
|
|
||||||
assert.strictEqual(Node._findSchema(schema, {}, path), schema.properties.levelTwo.properties.levelThree)
|
|
||||||
path = ['levelTwo', 'levelThree', 'bool']
|
|
||||||
assert.strictEqual(
|
|
||||||
Node._findSchema(schema, {}, path),
|
|
||||||
schema.properties.levelTwo.properties.levelThree.properties.bool
|
|
||||||
)
|
|
||||||
})
|
|
||||||
|
|
||||||
it('should return null for path that has no schema', () => {
|
|
||||||
const schema = {
|
|
||||||
type: 'object',
|
|
||||||
properties: {
|
|
||||||
foo: {
|
|
||||||
type: 'object',
|
|
||||||
properties: {
|
|
||||||
baz: {
|
|
||||||
type: 'number'
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
let path = ['bar']
|
|
||||||
assert.strictEqual(Node._findSchema(schema, {}, path), null)
|
|
||||||
path = ['foo', 'bar']
|
|
||||||
assert.strictEqual(Node._findSchema(schema, {}, path), null)
|
|
||||||
})
|
|
||||||
|
|
||||||
describe('with $ref', () => {
|
|
||||||
it('should find a referenced schema', () => {
|
|
||||||
const schema = {
|
|
||||||
type: 'object',
|
|
||||||
properties: {
|
|
||||||
foo: {
|
|
||||||
$ref: 'foo'
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
const fooSchema = {
|
|
||||||
type: 'number',
|
|
||||||
title: 'Foo'
|
|
||||||
}
|
|
||||||
const path = ['foo']
|
|
||||||
assert.strictEqual(Node._findSchema(schema, { foo: fooSchema }, path), fooSchema)
|
|
||||||
})
|
|
||||||
})
|
|
||||||
|
|
||||||
describe('with pattern properties', () => {
|
|
||||||
it('should find schema', () => {
|
|
||||||
const schema = {
|
|
||||||
type: 'object',
|
|
||||||
properties: {
|
|
||||||
str: {
|
|
||||||
title: 'str',
|
|
||||||
type: 'boolean'
|
|
||||||
}
|
|
||||||
},
|
|
||||||
patternProperties: {
|
|
||||||
'^foo[0-9]': {
|
|
||||||
title: 'foo[0-] pattern property',
|
|
||||||
type: 'string'
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
let path = []
|
|
||||||
assert.strictEqual(Node._findSchema(schema, {}, path), schema, 'top level')
|
|
||||||
path = ['str']
|
|
||||||
assert.strictEqual(Node._findSchema(schema, {}, path), schema.properties.str, 'normal property')
|
|
||||||
})
|
|
||||||
|
|
||||||
it('should find schema within multi-level object properties', () => {
|
|
||||||
const schema = {
|
|
||||||
type: 'object',
|
|
||||||
properties: {
|
|
||||||
levelTwo: {
|
|
||||||
type: 'object',
|
|
||||||
properties: {
|
|
||||||
levelThree: {
|
|
||||||
type: 'object',
|
|
||||||
properties: {
|
|
||||||
bool: {
|
|
||||||
title: 'bool',
|
|
||||||
type: 'boolean'
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
patternProperties: {
|
|
||||||
'^foo[0-9]': {
|
|
||||||
title: 'foo[0-9] pattern property',
|
|
||||||
type: 'string'
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
let path = []
|
|
||||||
assert.strictEqual(Node._findSchema(schema, {}, path), schema, 'top level')
|
|
||||||
path = ['levelTwo']
|
|
||||||
assert.strictEqual(Node._findSchema(schema, {}, path), schema.properties.levelTwo, 'level two')
|
|
||||||
path = ['levelTwo', 'levelThree']
|
|
||||||
assert.strictEqual(Node._findSchema(schema, {}, path), schema.properties.levelTwo.properties.levelThree, 'level three')
|
|
||||||
path = ['levelTwo', 'levelThree', 'bool']
|
|
||||||
assert.strictEqual(
|
|
||||||
Node._findSchema(schema, {}, path),
|
|
||||||
schema.properties.levelTwo.properties.levelThree.properties.bool,
|
|
||||||
'normal property'
|
|
||||||
)
|
|
||||||
})
|
|
||||||
|
|
||||||
it('should find schema for pattern properties', () => {
|
|
||||||
const schema = {
|
|
||||||
type: 'object',
|
|
||||||
patternProperties: {
|
|
||||||
'^foo[0-9]': {
|
|
||||||
title: 'foo[0-9] pattern property',
|
|
||||||
type: 'string'
|
|
||||||
},
|
|
||||||
'^bar[0-9]': {
|
|
||||||
title: 'bar[0-9] pattern property',
|
|
||||||
type: 'string'
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
let path = ['foo1']
|
|
||||||
assert.strictEqual(
|
|
||||||
Node._findSchema(schema, {}, path),
|
|
||||||
schema.patternProperties['^foo[0-9]'],
|
|
||||||
'first pattern property'
|
|
||||||
)
|
|
||||||
path = ['bar5']
|
|
||||||
assert.strictEqual(
|
|
||||||
Node._findSchema(schema, {}, path),
|
|
||||||
schema.patternProperties['^bar[0-9]'],
|
|
||||||
'second pattern property'
|
|
||||||
)
|
|
||||||
})
|
|
||||||
|
|
||||||
it('should find schema for multi-level pattern properties', () => {
|
|
||||||
const schema = {
|
|
||||||
type: 'object',
|
|
||||||
patternProperties: {
|
|
||||||
'^foo[0-9]': {
|
|
||||||
title: 'foo[0-9] pattern property',
|
|
||||||
type: 'object',
|
|
||||||
properties: {
|
|
||||||
fooChild: {
|
|
||||||
type: 'object',
|
|
||||||
properties: {
|
|
||||||
fooChild2: {
|
|
||||||
type: 'string'
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
'^bar[0-9]': {
|
|
||||||
title: 'bar[0-9] pattern property',
|
|
||||||
type: 'object',
|
|
||||||
properties: {
|
|
||||||
barChild: {
|
|
||||||
type: 'string'
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
let path = ['foo1', 'fooChild', 'fooChild2']
|
|
||||||
assert.strictEqual(
|
|
||||||
Node._findSchema(schema, {}, path),
|
|
||||||
schema.patternProperties['^foo[0-9]'].properties.fooChild.properties.fooChild2,
|
|
||||||
'first pattern property child of child'
|
|
||||||
)
|
|
||||||
path = ['bar5', 'barChild']
|
|
||||||
assert.strictEqual(
|
|
||||||
Node._findSchema(schema, {}, path),
|
|
||||||
schema.patternProperties['^bar[0-9]'].properties.barChild,
|
|
||||||
'second pattern property child'
|
|
||||||
)
|
|
||||||
})
|
|
||||||
|
|
||||||
it('should return null for path that has no schema', () => {
|
|
||||||
const schema = {
|
|
||||||
type: 'object',
|
|
||||||
properties: {
|
|
||||||
levelTwo: {
|
|
||||||
type: 'object',
|
|
||||||
properties: {
|
|
||||||
levelThree: {
|
|
||||||
type: 'number'
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
patternProperties: {
|
|
||||||
'^foo[0-9]': {
|
|
||||||
title: 'foo[0-9] pattern property',
|
|
||||||
type: 'string'
|
|
||||||
},
|
|
||||||
'^bar[0-9]': {
|
|
||||||
title: 'bar[0-9] pattern property',
|
|
||||||
type: 'string'
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
let path = ['not-in-schema']
|
|
||||||
assert.strictEqual(Node._findSchema(schema, {}, path), null)
|
|
||||||
path = ['levelOne', 'not-in-schema']
|
|
||||||
assert.strictEqual(Node._findSchema(schema, {}, path), null)
|
|
||||||
})
|
|
||||||
})
|
|
||||||
})
|
|
||||||
})
|
|
|
@ -1,100 +0,0 @@
|
||||||
<!DOCTYPE HTML>
|
|
||||||
<html>
|
|
||||||
<head>
|
|
||||||
<title>CouchDB Document Editor</title>
|
|
||||||
|
|
||||||
<meta name="description" content="CouchDB Document Editor">
|
|
||||||
<meta name="keywords" content="json, editor, couchdb, online, javascript, javascript object notation, treeview, open source, free">
|
|
||||||
<meta name="author" content="Jos de Jong">
|
|
||||||
|
|
||||||
<link rel="shortcut icon" href="../app/web/favicon.ico">
|
|
||||||
|
|
||||||
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.7.0/jquery.min.js"></script>
|
|
||||||
<script src="../jsoneditor/js/jsoneditor.js"></script>
|
|
||||||
<link rel="stylesheet" type="text/css" href="../jsoneditor/css/jsoneditor.css">
|
|
||||||
|
|
||||||
<style type="text/css">
|
|
||||||
body, html {
|
|
||||||
font-family: arial, verdana;
|
|
||||||
font-size: 11pt;
|
|
||||||
width: 100%;
|
|
||||||
height: 100%;
|
|
||||||
margin: 0px;
|
|
||||||
padding: 0px;
|
|
||||||
}
|
|
||||||
|
|
||||||
h1 {
|
|
||||||
color: gray;
|
|
||||||
}
|
|
||||||
|
|
||||||
input[type=text] {
|
|
||||||
border: 1px solid gray;
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
|
|
||||||
<script>
|
|
||||||
var editor = null;
|
|
||||||
|
|
||||||
function init() {
|
|
||||||
var container = document.getElementById('jsoneditor');
|
|
||||||
editor = new JSONEditor(container);
|
|
||||||
|
|
||||||
document.getElementById('url').focus();
|
|
||||||
}
|
|
||||||
|
|
||||||
function load() {
|
|
||||||
var url = document.getElementById("url").value;
|
|
||||||
$.ajax({
|
|
||||||
'type': 'GET',
|
|
||||||
'url': url,
|
|
||||||
'dataType': 'json',
|
|
||||||
'success': function (doc) {
|
|
||||||
editor.set(doc);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
function save() {
|
|
||||||
var doc = editor.get();
|
|
||||||
var url = document.getElementById("url").value;
|
|
||||||
$.ajax({
|
|
||||||
'type': 'PUT',
|
|
||||||
'url': url,
|
|
||||||
'data': JSON.stringify(doc),
|
|
||||||
'success': function (response) {
|
|
||||||
load();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
</script>
|
|
||||||
</head>
|
|
||||||
|
|
||||||
<body onload="init();">
|
|
||||||
|
|
||||||
<table align="center" width="790px" height="100%">
|
|
||||||
<tr>
|
|
||||||
<td style="height: 50px;"><h1>CouchDB Document Editor</h1></td>
|
|
||||||
</tr>
|
|
||||||
<tr>
|
|
||||||
<td style="height: 30px;">
|
|
||||||
<table width="100%">
|
|
||||||
<col width="100px"></col>
|
|
||||||
<col ></col>
|
|
||||||
<col width="50px"></col>
|
|
||||||
<col width="50px"></col>
|
|
||||||
<tr>
|
|
||||||
<td>Document Url:</td>
|
|
||||||
<td><input type="text" id="url" style="width: 100%;" value="http://localhost:5984/test/jos"></td>
|
|
||||||
<td><input type="button" value="Load" onclick="load();" /></td>
|
|
||||||
<td><input type="button" value="Save" onclick="save();" /></td>
|
|
||||||
</tr>
|
|
||||||
</table>
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
<tr>
|
|
||||||
<td id="jsoneditor"></td>
|
|
||||||
</tr>
|
|
||||||
</table>
|
|
||||||
|
|
||||||
</body>
|
|
||||||
</html>
|
|
|
@ -1,95 +0,0 @@
|
||||||
import assert from 'assert'
|
|
||||||
import { stringifyPartial, containsArray } from '../src/js/jsonUtils'
|
|
||||||
|
|
||||||
describe('jsonUtils', () => {
|
|
||||||
it('should stringify a small object', () => {
|
|
||||||
const json = {
|
|
||||||
a: 2,
|
|
||||||
b: 'foo',
|
|
||||||
c: null,
|
|
||||||
d: false,
|
|
||||||
e: [1, 2, 3],
|
|
||||||
f: { g: 'h' }
|
|
||||||
}
|
|
||||||
|
|
||||||
assert.strictEqual(stringifyPartial(json), '{"a":2,"b":"foo","c":null,"d":false,"e":[1,2,3],"f":{"g":"h"}}')
|
|
||||||
})
|
|
||||||
|
|
||||||
it('should stringify a small object with formatting', () => {
|
|
||||||
const json = {
|
|
||||||
a: 2,
|
|
||||||
b: 'foo',
|
|
||||||
c: null,
|
|
||||||
d: false,
|
|
||||||
e: [1, 2, 3],
|
|
||||||
f: { g: 'h' }
|
|
||||||
}
|
|
||||||
|
|
||||||
assert.strictEqual(stringifyPartial(json, 2),
|
|
||||||
'{\n' +
|
|
||||||
' "a": 2,\n' +
|
|
||||||
' "b": "foo",\n' +
|
|
||||||
' "c": null,\n' +
|
|
||||||
' "d": false,\n' +
|
|
||||||
' "e": [\n' +
|
|
||||||
' 1,\n' +
|
|
||||||
' 2,\n' +
|
|
||||||
' 3\n' +
|
|
||||||
' ],\n' +
|
|
||||||
' "f": {\n' +
|
|
||||||
' "g": "h"\n' +
|
|
||||||
' }\n' +
|
|
||||||
'}')
|
|
||||||
|
|
||||||
assert.strictEqual(stringifyPartial(json, ' '), '{\n' +
|
|
||||||
' "a": 2,\n' +
|
|
||||||
' "b": "foo",\n' +
|
|
||||||
' "c": null,\n' +
|
|
||||||
' "d": false,\n' +
|
|
||||||
' "e": [\n' +
|
|
||||||
' 1,\n' +
|
|
||||||
' 2,\n' +
|
|
||||||
' 3\n' +
|
|
||||||
' ],\n' +
|
|
||||||
' "f": {\n' +
|
|
||||||
' "g": "h"\n' +
|
|
||||||
' }\n' +
|
|
||||||
'}')
|
|
||||||
})
|
|
||||||
|
|
||||||
it('should limit stringified output', () => {
|
|
||||||
const json = {
|
|
||||||
a: 2,
|
|
||||||
b: 'foo',
|
|
||||||
c: null,
|
|
||||||
d: false,
|
|
||||||
e: [1, 2, 3],
|
|
||||||
f: { g: 'h' }
|
|
||||||
}
|
|
||||||
|
|
||||||
const all = '{"a":2,"b":"foo","c":null,"d":false,"e":[1,2,3],"f":{"g":"h"}}'
|
|
||||||
const limit = 20
|
|
||||||
|
|
||||||
assert.strictEqual(stringifyPartial(json, undefined, limit),
|
|
||||||
all.slice(0, limit) + '...')
|
|
||||||
|
|
||||||
assert.strictEqual(stringifyPartial(json, undefined, all.length), all)
|
|
||||||
|
|
||||||
assert.strictEqual(stringifyPartial([1, 2, 3, 4, 5, 6, 7, 8, 9, 10], undefined, 10),
|
|
||||||
'[1,2,3,4,5...')
|
|
||||||
|
|
||||||
assert.strictEqual(stringifyPartial(12345678, undefined, 4), '1234...')
|
|
||||||
})
|
|
||||||
|
|
||||||
it('should count array items', () => {
|
|
||||||
// assert.strictEqual(countArrayItems('[1,2,3]'), 3)
|
|
||||||
assert.strictEqual(containsArray('[]'), true)
|
|
||||||
assert.strictEqual(containsArray(' []'), true)
|
|
||||||
assert.strictEqual(containsArray(' \t []'), true)
|
|
||||||
assert.strictEqual(containsArray(' \t\n []'), true)
|
|
||||||
assert.strictEqual(containsArray('"["'), false)
|
|
||||||
assert.strictEqual(containsArray('2'), false)
|
|
||||||
assert.strictEqual(containsArray('null'), false)
|
|
||||||
assert.strictEqual(containsArray('{}'), false)
|
|
||||||
})
|
|
||||||
})
|
|
|
@ -1,22 +0,0 @@
|
||||||
import { JSDOM } from 'jsdom'
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Set up the test environment by simulating browser globals.
|
|
||||||
* @param {string} [locale=en] A locale to set in navigator.language
|
|
||||||
* @return {void}
|
|
||||||
*/
|
|
||||||
function setUpTestEnvironment (locale) {
|
|
||||||
if (!locale) {
|
|
||||||
locale = 'en'
|
|
||||||
}
|
|
||||||
|
|
||||||
const dom = new JSDOM('...')
|
|
||||||
global.window = dom.window
|
|
||||||
global.document = dom.window.document
|
|
||||||
global.navigator = dom.window.navigator
|
|
||||||
|
|
||||||
// JSDom has no setter defined for navigator.language, so defineProperty is necessary in order to override it
|
|
||||||
Object.defineProperty(navigator, 'language', { value: locale })
|
|
||||||
};
|
|
||||||
|
|
||||||
setUpTestEnvironment()
|
|
|
@ -1,54 +0,0 @@
|
||||||
<!DOCTYPE HTML>
|
|
||||||
<html>
|
|
||||||
<head>
|
|
||||||
<link href="//cdnjs.cloudflare.com/ajax/libs/twitter-bootstrap/3.3.6/css/bootstrap.min.css" rel="stylesheet" type="text/css">
|
|
||||||
<!--<link href="//cdnjs.cloudflare.com/ajax/libs/twitter-bootstrap/4.0.0-alpha/css/bootstrap.min.css" rel="stylesheet" type="text/css">-->
|
|
||||||
|
|
||||||
<link href="../dist/jsoneditor.css" rel="stylesheet" type="text/css">
|
|
||||||
<script src="../dist/jsoneditor.js"></script>
|
|
||||||
|
|
||||||
<style type="text/css">
|
|
||||||
#jsoneditor {
|
|
||||||
width: 500px;
|
|
||||||
height: 500px;
|
|
||||||
}
|
|
||||||
|
|
||||||
</style>
|
|
||||||
</head>
|
|
||||||
<body>
|
|
||||||
<p>
|
|
||||||
Test with bootstrap
|
|
||||||
</p>
|
|
||||||
|
|
||||||
<div id="jsoneditor"></div>
|
|
||||||
|
|
||||||
<script>
|
|
||||||
var container = document.getElementById('jsoneditor');
|
|
||||||
var options = {
|
|
||||||
"mode": "tree",
|
|
||||||
"modes": [
|
|
||||||
"tree",
|
|
||||||
"form",
|
|
||||||
"code",
|
|
||||||
"text",
|
|
||||||
"view"
|
|
||||||
],
|
|
||||||
"history": false,
|
|
||||||
onChange: function () {
|
|
||||||
if (editor) {
|
|
||||||
// if you comment out the next line of code, the problem is solved
|
|
||||||
// editor.get() throws an exception when the editor does not
|
|
||||||
// contain valid JSON.
|
|
||||||
console.log('change', editor.get())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
var editor = new JSONEditor(container, options);
|
|
||||||
|
|
||||||
var text = "[{\"test\":\"test\"}]";
|
|
||||||
var json = JSON.parse(text);
|
|
||||||
editor.set(json);
|
|
||||||
|
|
||||||
</script>
|
|
||||||
</body>
|
|
||||||
</html>
|
|
|
@ -1,119 +0,0 @@
|
||||||
<!DOCTYPE HTML>
|
|
||||||
<html>
|
|
||||||
<head>
|
|
||||||
<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">
|
|
||||||
body {
|
|
||||||
font: 10.5pt arial;
|
|
||||||
color: #4d4d4d;
|
|
||||||
line-height: 150%;
|
|
||||||
width: 500px;
|
|
||||||
padding-left: 40px;
|
|
||||||
}
|
|
||||||
|
|
||||||
code {
|
|
||||||
background-color: #f5f5f5;
|
|
||||||
}
|
|
||||||
|
|
||||||
#jsoneditor {
|
|
||||||
width: 500px;
|
|
||||||
height: 500px;
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
</head>
|
|
||||||
<body>
|
|
||||||
|
|
||||||
<p>
|
|
||||||
Switch editor mode using the mode box.
|
|
||||||
Note that the mode can be changed programmatically as well using the method
|
|
||||||
<code>editor.setMode(mode)</code>, try it in the console of your browser.
|
|
||||||
</p>
|
|
||||||
|
|
||||||
<form>
|
|
||||||
<div id="jsoneditor"></div>
|
|
||||||
</form>
|
|
||||||
|
|
||||||
<p>
|
|
||||||
<button id="update">Update</button>
|
|
||||||
</p>
|
|
||||||
|
|
||||||
<script>
|
|
||||||
var container, options, json, editor;
|
|
||||||
|
|
||||||
container = document.getElementById('jsoneditor');
|
|
||||||
|
|
||||||
options = {
|
|
||||||
mode: 'tree',
|
|
||||||
modes: ['code', 'form', 'text', 'tree', 'view', 'preview'], // allowed modes
|
|
||||||
onError: function (err) {
|
|
||||||
alert(err.toString());
|
|
||||||
},
|
|
||||||
onChange: function () {
|
|
||||||
console.log('change');
|
|
||||||
},
|
|
||||||
onChangeJSON: function (json) {
|
|
||||||
console.log('onChangeJSON', json);
|
|
||||||
},
|
|
||||||
onChangeText: function (text) {
|
|
||||||
console.log('onChangeText', text);
|
|
||||||
},
|
|
||||||
onFocus: function(event) {
|
|
||||||
console.log("Focus : ",event);
|
|
||||||
},
|
|
||||||
onBlur: function(event) {
|
|
||||||
console.log("Blur : ",event);
|
|
||||||
},
|
|
||||||
indentation: 4,
|
|
||||||
escapeUnicode: true
|
|
||||||
};
|
|
||||||
|
|
||||||
json = {
|
|
||||||
"array": [1, 2, [3,4,5]],
|
|
||||||
"boolean": true,
|
|
||||||
"color": "#82b92c",
|
|
||||||
"htmlcode": '"',
|
|
||||||
"escaped_unicode": '\\u20b9',
|
|
||||||
"unicode": '\u20b9,\uD83D\uDCA9',
|
|
||||||
"return": '\n',
|
|
||||||
"null": null,
|
|
||||||
"number": 123,
|
|
||||||
"object": {"a": "b", "c": "d"},
|
|
||||||
"string": "Hello World",
|
|
||||||
"timestamp": 1534952749890,
|
|
||||||
"url": "http://jsoneditoronline.org"
|
|
||||||
};
|
|
||||||
|
|
||||||
editorTest = new JSONEditor(container, options, json);
|
|
||||||
|
|
||||||
console.log('json', json);
|
|
||||||
console.log('string', JSON.stringify(json));
|
|
||||||
|
|
||||||
|
|
||||||
const update = document.getElementById('update')
|
|
||||||
update.onclick = function () {
|
|
||||||
const json2 = {
|
|
||||||
"array": [1, 2, [3,4,5]],
|
|
||||||
"array2": [1, 2, [3,4,5]],
|
|
||||||
"url": "http://jsoneditoronline.org",
|
|
||||||
"boolean": true,
|
|
||||||
"color": "#82b92c",
|
|
||||||
"htmlcode": '"',
|
|
||||||
"escaped_unicode": '\\u20b9',
|
|
||||||
"unicode": '\u20b9,\uD83D\uDCA9',
|
|
||||||
"return": '\n',
|
|
||||||
"null": null,
|
|
||||||
"number": 123,
|
|
||||||
"object": {"a": "b", "c": "d"},
|
|
||||||
"string": "Hello World",
|
|
||||||
"timestamp": 1534952749890
|
|
||||||
};
|
|
||||||
|
|
||||||
editorTest.update(json2)
|
|
||||||
}
|
|
||||||
</script>
|
|
||||||
</body>
|
|
||||||
</html>
|
|
|
@ -1,63 +0,0 @@
|
||||||
<!DOCTYPE HTML>
|
|
||||||
<html>
|
|
||||||
<head>
|
|
||||||
<meta http-equiv="Content-Type" content="text/html;charset=utf-8">
|
|
||||||
|
|
||||||
<link href="../dist/jsoneditor.min.css" rel="stylesheet" type="text/css">
|
|
||||||
<script src="../dist/jsoneditor.min.js"></script>
|
|
||||||
|
|
||||||
<style type="text/css">
|
|
||||||
body {
|
|
||||||
font: 10.5pt arial;
|
|
||||||
color: #4d4d4d;
|
|
||||||
line-height: 150%;
|
|
||||||
width: 500px;
|
|
||||||
}
|
|
||||||
|
|
||||||
code {
|
|
||||||
background-color: #f5f5f5;
|
|
||||||
}
|
|
||||||
|
|
||||||
#jsoneditor {
|
|
||||||
width: 500px;
|
|
||||||
height: 500px;
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
</head>
|
|
||||||
<body>
|
|
||||||
|
|
||||||
<p>
|
|
||||||
Switch editor mode using the mode box.
|
|
||||||
Note that the mode can be changed programmatically as well using the method
|
|
||||||
<code>editor.setMode(mode)</code>, try it in the console of your browser.
|
|
||||||
</p>
|
|
||||||
|
|
||||||
<div id="jsoneditor"></div>
|
|
||||||
|
|
||||||
<script>
|
|
||||||
var container, options, json, editor;
|
|
||||||
|
|
||||||
container = document.getElementById('jsoneditor');
|
|
||||||
|
|
||||||
options = {
|
|
||||||
mode: 'tree',
|
|
||||||
modes: ['code', 'form', 'text', 'tree', 'view', 'preview'], // allowed modes
|
|
||||||
onError: function (err) {
|
|
||||||
alert(err.toString());
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
json = {
|
|
||||||
"array": [1, 2, 3],
|
|
||||||
"boolean": true,
|
|
||||||
"color": "#82b92c",
|
|
||||||
"null": null,
|
|
||||||
"number": 123,
|
|
||||||
"object": {"a": "b", "c": "d"},
|
|
||||||
"string": "Hello World"
|
|
||||||
};
|
|
||||||
|
|
||||||
editor = new JSONEditor(container, options, json);
|
|
||||||
</script>
|
|
||||||
</body>
|
|
||||||
</html>
|
|
|
@ -1,89 +0,0 @@
|
||||||
<!DOCTYPE HTML>
|
|
||||||
<html>
|
|
||||||
<head>
|
|
||||||
<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">
|
|
||||||
body {
|
|
||||||
font: 10.5pt arial;
|
|
||||||
color: #4d4d4d;
|
|
||||||
line-height: 150%;
|
|
||||||
width: 500px;
|
|
||||||
padding-left: 40px;
|
|
||||||
}
|
|
||||||
|
|
||||||
code {
|
|
||||||
background-color: #f5f5f5;
|
|
||||||
}
|
|
||||||
|
|
||||||
#jsoneditor {
|
|
||||||
width: 500px;
|
|
||||||
height: 500px;
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
</head>
|
|
||||||
<body>
|
|
||||||
|
|
||||||
<p>
|
|
||||||
Switch editor mode using the mode box.
|
|
||||||
Note that the mode can be changed programmatically as well using the method
|
|
||||||
<code>editor.setMode(mode)</code>, try it in the console of your browser.
|
|
||||||
</p>
|
|
||||||
|
|
||||||
<form>
|
|
||||||
<div id="jsoneditor"></div>
|
|
||||||
</form>
|
|
||||||
|
|
||||||
<script>
|
|
||||||
var container, options, json, editor;
|
|
||||||
|
|
||||||
container = document.getElementById('jsoneditor');
|
|
||||||
|
|
||||||
options = {
|
|
||||||
mode: 'code',
|
|
||||||
modes: ['code', 'form', 'text', 'tree', 'view', 'preview'], // allowed modes
|
|
||||||
onError: function (err) {
|
|
||||||
alert(err.toString());
|
|
||||||
},
|
|
||||||
onChange: function () {
|
|
||||||
console.log('change');
|
|
||||||
},
|
|
||||||
onChangeJSON: function (json) {
|
|
||||||
console.log('onChangeJSON', json);
|
|
||||||
},
|
|
||||||
onChangeText: function (text) {
|
|
||||||
console.log('onChangeText', text);
|
|
||||||
},
|
|
||||||
indentation: 4,
|
|
||||||
escapeUnicode: true
|
|
||||||
};
|
|
||||||
|
|
||||||
var json = [];
|
|
||||||
for (var i = 0; i < 10000; i++) {
|
|
||||||
var longitude = 4 + i / 10000;
|
|
||||||
var latitude = 51 + i / 10000;
|
|
||||||
|
|
||||||
json.push({
|
|
||||||
name: 'Item ' + i,
|
|
||||||
id: String(i),
|
|
||||||
index: i,
|
|
||||||
time: new Date().toISOString(),
|
|
||||||
location: {
|
|
||||||
latitude: longitude,
|
|
||||||
longitude: latitude,
|
|
||||||
coordinates: [longitude, latitude]
|
|
||||||
},
|
|
||||||
random: Math.random()
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
editorTest = new JSONEditor(container, options, json);
|
|
||||||
|
|
||||||
// console.log('json', json);
|
|
||||||
console.log('stringified size: ', Math.round(JSON.stringify(json).length / 1024 / 1024), 'MB');
|
|
||||||
</script>
|
|
||||||
</body>
|
|
||||||
</html>
|
|
|
@ -1,92 +0,0 @@
|
||||||
<!DOCTYPE HTML>
|
|
||||||
<html>
|
|
||||||
<head>
|
|
||||||
<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">
|
|
||||||
body {
|
|
||||||
font: 10.5pt arial;
|
|
||||||
color: #4d4d4d;
|
|
||||||
line-height: 150%;
|
|
||||||
width: 500px;
|
|
||||||
padding-left: 40px;
|
|
||||||
}
|
|
||||||
|
|
||||||
code {
|
|
||||||
background-color: #f5f5f5;
|
|
||||||
}
|
|
||||||
|
|
||||||
#jsoneditor {
|
|
||||||
width: 500px;
|
|
||||||
height: 500px;
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
</head>
|
|
||||||
<body>
|
|
||||||
|
|
||||||
<p>
|
|
||||||
Test color picker firing onChange on every change instead of onDone.
|
|
||||||
</p>
|
|
||||||
|
|
||||||
<form>
|
|
||||||
<div id="jsoneditor"></div>
|
|
||||||
</form>
|
|
||||||
|
|
||||||
<script>
|
|
||||||
var container, options, json, editor;
|
|
||||||
|
|
||||||
container = document.getElementById('jsoneditor');
|
|
||||||
|
|
||||||
options = {
|
|
||||||
onChangeJSON: function (json) {
|
|
||||||
console.log('onChangeJSON', json);
|
|
||||||
},
|
|
||||||
onColorPicker: function (parent, color, onChange) {
|
|
||||||
new JSONEditor.VanillaPicker({
|
|
||||||
parent: parent,
|
|
||||||
color: color,
|
|
||||||
popup: 'bottom',
|
|
||||||
onChange: function (color) {
|
|
||||||
console.log('onChange', color)
|
|
||||||
var alpha = color.rgba[3]
|
|
||||||
var hex = (alpha === 1)
|
|
||||||
? color.hex.substr(0, 7) // return #RRGGBB
|
|
||||||
: color.hex // return #RRGGBBAA
|
|
||||||
onChange(hex)
|
|
||||||
},
|
|
||||||
onDone: function (color) {
|
|
||||||
console.log('onDone', color)
|
|
||||||
},
|
|
||||||
onClose: function (color) {
|
|
||||||
console.log('onClose', color)
|
|
||||||
}
|
|
||||||
}).show();
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
json = {
|
|
||||||
"array": [1, 2, [3,4,5]],
|
|
||||||
"boolean": true,
|
|
||||||
"color": "#82b92c",
|
|
||||||
"htmlcode": '"',
|
|
||||||
"escaped_unicode": '\\u20b9',
|
|
||||||
"unicode": '\u20b9,\uD83D\uDCA9',
|
|
||||||
"return": '\n',
|
|
||||||
"null": null,
|
|
||||||
"number": 123,
|
|
||||||
"object": {"a": "b", "c": "d"},
|
|
||||||
"string": "Hello World",
|
|
||||||
"timestamp": 1534952749890,
|
|
||||||
"url": "http://jsoneditoronline.org"
|
|
||||||
};
|
|
||||||
|
|
||||||
editor = new JSONEditor(container, options, json);
|
|
||||||
|
|
||||||
console.log('json', json);
|
|
||||||
console.log('string', JSON.stringify(json));
|
|
||||||
</script>
|
|
||||||
</body>
|
|
||||||
</html>
|
|
|
@ -1,87 +0,0 @@
|
||||||
<!DOCTYPE HTML>
|
|
||||||
<html>
|
|
||||||
<head>
|
|
||||||
<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">
|
|
||||||
body {
|
|
||||||
font: 10.5pt arial;
|
|
||||||
color: #4d4d4d;
|
|
||||||
line-height: 150%;
|
|
||||||
width: 500px;
|
|
||||||
padding-left: 40px;
|
|
||||||
}
|
|
||||||
|
|
||||||
code {
|
|
||||||
background-color: #f5f5f5;
|
|
||||||
}
|
|
||||||
|
|
||||||
#jsoneditor {
|
|
||||||
width: 500px;
|
|
||||||
height: 500px;
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
</head>
|
|
||||||
<body>
|
|
||||||
|
|
||||||
<p>
|
|
||||||
Switch editor mode using the mode box.
|
|
||||||
Note that the mode can be changed programmatically as well using the method
|
|
||||||
<code>editor.setMode(mode)</code>, try it in the console of your browser.
|
|
||||||
</p>
|
|
||||||
|
|
||||||
<form>
|
|
||||||
<div id="jsoneditor"></div>
|
|
||||||
</form>
|
|
||||||
|
|
||||||
<p>
|
|
||||||
<button id="destroy">Destroy</button>
|
|
||||||
</p>
|
|
||||||
|
|
||||||
<script>
|
|
||||||
var container, options, json, editor;
|
|
||||||
|
|
||||||
container = document.getElementById('jsoneditor');
|
|
||||||
|
|
||||||
options = {
|
|
||||||
mode: 'tree',
|
|
||||||
modes: ['code', 'form', 'text', 'tree', 'view', 'preview'], // allowed modes
|
|
||||||
onError: function (err) {
|
|
||||||
alert(err.toString());
|
|
||||||
},
|
|
||||||
onChange: function () {
|
|
||||||
console.log('change');
|
|
||||||
},
|
|
||||||
indentation: 4,
|
|
||||||
escapeUnicode: true
|
|
||||||
};
|
|
||||||
|
|
||||||
json = {
|
|
||||||
"array": [1, 2, [3,4,5]],
|
|
||||||
"boolean": true,
|
|
||||||
"htmlcode": '"',
|
|
||||||
"escaped_unicode": '\\u20b9',
|
|
||||||
"unicode": '\u20b9,\uD83D\uDCA9',
|
|
||||||
"return": '\n',
|
|
||||||
"null": null,
|
|
||||||
"number": 123,
|
|
||||||
"object": {"a": "b", "c": "d"},
|
|
||||||
"string": "Hello World",
|
|
||||||
"url": "http://jsoneditoronline.org"
|
|
||||||
};
|
|
||||||
|
|
||||||
editor = new JSONEditor(container, options, json);
|
|
||||||
|
|
||||||
console.log('json', json);
|
|
||||||
console.log('string', JSON.stringify(json));
|
|
||||||
|
|
||||||
document.querySelector('#destroy').onclick = function () {
|
|
||||||
editor.destroy();
|
|
||||||
editor = null;
|
|
||||||
}
|
|
||||||
</script>
|
|
||||||
</body>
|
|
||||||
</html>
|
|
|
@ -1,100 +0,0 @@
|
||||||
<!DOCTYPE HTML>
|
|
||||||
<html>
|
|
||||||
<head>
|
|
||||||
<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">
|
|
||||||
body {
|
|
||||||
font: 10.5pt arial;
|
|
||||||
color: #4d4d4d;
|
|
||||||
line-height: 150%;
|
|
||||||
width: 500px;
|
|
||||||
padding-left: 40px;
|
|
||||||
}
|
|
||||||
|
|
||||||
code {
|
|
||||||
background-color: #f5f5f5;
|
|
||||||
}
|
|
||||||
|
|
||||||
#jsoneditor {
|
|
||||||
width: 500px;
|
|
||||||
height: 500px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.input {
|
|
||||||
display: block;
|
|
||||||
margin: 5px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.input:focus {
|
|
||||||
border: 5px solid red;
|
|
||||||
}
|
|
||||||
.input:blur {
|
|
||||||
border: unset;
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
</head>
|
|
||||||
<body>
|
|
||||||
|
|
||||||
<p>
|
|
||||||
Switch editor mode using the mode box.
|
|
||||||
Note that the mode can be changed programmatically as well using the method
|
|
||||||
<code>editor.setMode(mode)</code>, try it in the console of your browser.
|
|
||||||
</p>
|
|
||||||
<input type="text" class="input">
|
|
||||||
<input type="text" class="input">
|
|
||||||
<form>
|
|
||||||
<div id="jsoneditor"></div>
|
|
||||||
</form>
|
|
||||||
<input type="text" class="input">
|
|
||||||
<input type="text" class="input">
|
|
||||||
|
|
||||||
<script>
|
|
||||||
var container, options, json, editor;
|
|
||||||
|
|
||||||
container = document.getElementById('jsoneditor');
|
|
||||||
|
|
||||||
options = {
|
|
||||||
mode: 'tree',
|
|
||||||
modes: ['code', 'form', 'text', 'tree', 'view', 'preview'], // allowed modes
|
|
||||||
onError: function (err) {
|
|
||||||
alert(err.toString());
|
|
||||||
},
|
|
||||||
onFocus: function(event) {
|
|
||||||
container.style.border = '5px solid red';
|
|
||||||
console.log("Focus : ",event);
|
|
||||||
},
|
|
||||||
onBlur: function(event) {
|
|
||||||
container.style.border = 'unset';
|
|
||||||
console.log("Blur : ",event);
|
|
||||||
},
|
|
||||||
indentation: 4,
|
|
||||||
escapeUnicode: true
|
|
||||||
};
|
|
||||||
|
|
||||||
json = {
|
|
||||||
"array": [1, 2, [3,4,5]],
|
|
||||||
"boolean": true,
|
|
||||||
"color": "#82b92c",
|
|
||||||
"htmlcode": '"',
|
|
||||||
"escaped_unicode": '\\u20b9',
|
|
||||||
"unicode": '\u20b9,\uD83D\uDCA9',
|
|
||||||
"return": '\n',
|
|
||||||
"null": null,
|
|
||||||
"number": 123,
|
|
||||||
"object": {"a": "b", "c": "d"},
|
|
||||||
"string": "Hello World",
|
|
||||||
"timestamp": 1534952749890,
|
|
||||||
"url": "http://jsoneditoronline.org"
|
|
||||||
};
|
|
||||||
|
|
||||||
editorTest = new JSONEditor(container, options, json);
|
|
||||||
|
|
||||||
console.log('json', json);
|
|
||||||
console.log('string', JSON.stringify(json));
|
|
||||||
</script>
|
|
||||||
</body>
|
|
||||||
</html>
|
|
|
@ -1,84 +0,0 @@
|
||||||
<!DOCTYPE HTML>
|
|
||||||
<html>
|
|
||||||
<head>
|
|
||||||
<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">
|
|
||||||
body {
|
|
||||||
font: 10.5pt arial;
|
|
||||||
color: #4d4d4d;
|
|
||||||
line-height: 150%;
|
|
||||||
width: 500px;
|
|
||||||
padding-left: 40px;
|
|
||||||
}
|
|
||||||
|
|
||||||
code {
|
|
||||||
background-color: #f5f5f5;
|
|
||||||
}
|
|
||||||
|
|
||||||
#jsoneditor {
|
|
||||||
width: 500px;
|
|
||||||
height: 500px;
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
</head>
|
|
||||||
<body>
|
|
||||||
|
|
||||||
<p>
|
|
||||||
Switch editor mode using the mode box.
|
|
||||||
Note that the mode can be changed programmatically as well using the method
|
|
||||||
<code>editor.setMode(mode)</code>, try it in the console of your browser.
|
|
||||||
</p>
|
|
||||||
|
|
||||||
<div id="jsoneditor"></div>
|
|
||||||
|
|
||||||
<script>
|
|
||||||
var container = document.getElementById('jsoneditor');
|
|
||||||
|
|
||||||
var options = {
|
|
||||||
mode: 'tree',
|
|
||||||
modes: ['code', 'form', 'text', 'tree', 'view', 'preview'], // allowed modes
|
|
||||||
onError: function (err) {
|
|
||||||
console.error(err);
|
|
||||||
alert(err.toString());
|
|
||||||
},
|
|
||||||
onChange: function () {
|
|
||||||
console.log('change');
|
|
||||||
},
|
|
||||||
indentation: 4,
|
|
||||||
escapeUnicode: true
|
|
||||||
};
|
|
||||||
|
|
||||||
var json = {
|
|
||||||
empty: [],
|
|
||||||
numbers: [],
|
|
||||||
abc: 123,
|
|
||||||
array: [],
|
|
||||||
object: { a: 2, b: 3}
|
|
||||||
};
|
|
||||||
for (var i = 0; i < 10000; i++) {
|
|
||||||
var longitude = 4 + i / 10000;
|
|
||||||
var latitude = 51 + i / 10000;
|
|
||||||
|
|
||||||
json.numbers.push(i);
|
|
||||||
json.array.push({
|
|
||||||
name: 'Item ' + i,
|
|
||||||
id: String(i),
|
|
||||||
index: i,
|
|
||||||
time: new Date().toISOString(),
|
|
||||||
location: {
|
|
||||||
latitude: longitude,
|
|
||||||
longitude: latitude,
|
|
||||||
coordinates: [longitude, latitude]
|
|
||||||
},
|
|
||||||
random: Math.random()
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
var editor = new JSONEditor(container, options, json);
|
|
||||||
</script>
|
|
||||||
</body>
|
|
||||||
</html>
|
|
|
@ -1,82 +0,0 @@
|
||||||
<!DOCTYPE HTML>
|
|
||||||
<html>
|
|
||||||
<head>
|
|
||||||
<meta http-equiv="Content-Type" content="text/html;charset=utf-8">
|
|
||||||
|
|
||||||
<!-- materialize css -->
|
|
||||||
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/materialize/0.97.6/css/materialize.min.css">
|
|
||||||
<script src="https://cdnjs.cloudflare.com/ajax/libs/materialize/0.97.6/js/materialize.min.js"></script>
|
|
||||||
|
|
||||||
<link href="../dist/jsoneditor.css" rel="stylesheet" type="text/css">
|
|
||||||
<script src="../dist/jsoneditor.js"></script>
|
|
||||||
|
|
||||||
<style>
|
|
||||||
body {
|
|
||||||
font: 10.5pt arial;
|
|
||||||
color: #4d4d4d;
|
|
||||||
line-height: 150%;
|
|
||||||
width: 500px;
|
|
||||||
padding-left: 40px;
|
|
||||||
}
|
|
||||||
|
|
||||||
code {
|
|
||||||
background-color: #f5f5f5;
|
|
||||||
}
|
|
||||||
|
|
||||||
#jsoneditor {
|
|
||||||
width: 500px;
|
|
||||||
height: 500px;
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
</head>
|
|
||||||
<body>
|
|
||||||
|
|
||||||
<p>
|
|
||||||
Switch editor mode using the mode box.
|
|
||||||
Note that the mode can be changed programmatically as well using the method
|
|
||||||
<code>editor.setMode(mode)</code>, try it in the console of your browser.
|
|
||||||
</p>
|
|
||||||
|
|
||||||
<form>
|
|
||||||
<div id="jsoneditor"></div>
|
|
||||||
</form>
|
|
||||||
|
|
||||||
<script>
|
|
||||||
var container, options, json, editor;
|
|
||||||
|
|
||||||
container = document.getElementById('jsoneditor');
|
|
||||||
|
|
||||||
options = {
|
|
||||||
mode: 'tree',
|
|
||||||
modes: ['code', 'form', 'text', 'tree', 'view', 'preview'], // allowed modes
|
|
||||||
onError: function (err) {
|
|
||||||
alert(err.toString());
|
|
||||||
},
|
|
||||||
onChange: function () {
|
|
||||||
console.log('change');
|
|
||||||
},
|
|
||||||
indentation: 4,
|
|
||||||
escapeUnicode: true
|
|
||||||
};
|
|
||||||
|
|
||||||
json = {
|
|
||||||
"array": [1, 2, [3,4,5]],
|
|
||||||
"boolean": true,
|
|
||||||
"htmlcode": '"',
|
|
||||||
"escaped_unicode": '\\u20b9',
|
|
||||||
"unicode": '\u20b9,\uD83D\uDCA9',
|
|
||||||
"return": '\n',
|
|
||||||
"null": null,
|
|
||||||
"number": 123,
|
|
||||||
"object": {"a": "b", "c": "d"},
|
|
||||||
"string": "Hello World",
|
|
||||||
"url": "http://jsoneditoronline.org"
|
|
||||||
};
|
|
||||||
|
|
||||||
editor = new JSONEditor(container, options, json);
|
|
||||||
|
|
||||||
console.log('json', json);
|
|
||||||
console.log('string', JSON.stringify(json));
|
|
||||||
</script>
|
|
||||||
</body>
|
|
||||||
</html>
|
|
|
@ -1,63 +0,0 @@
|
||||||
<!DOCTYPE HTML>
|
|
||||||
<html>
|
|
||||||
<head>
|
|
||||||
<meta http-equiv="Content-Type" content="text/html;charset=utf-8">
|
|
||||||
|
|
||||||
<link href="../dist/jsoneditor.min.css" rel="stylesheet" type="text/css">
|
|
||||||
<script src="../dist/jsoneditor-minimalist.min.js"></script>
|
|
||||||
|
|
||||||
<style type="text/css">
|
|
||||||
body {
|
|
||||||
font: 10.5pt arial;
|
|
||||||
color: #4d4d4d;
|
|
||||||
line-height: 150%;
|
|
||||||
width: 500px;
|
|
||||||
}
|
|
||||||
|
|
||||||
code {
|
|
||||||
background-color: #f5f5f5;
|
|
||||||
}
|
|
||||||
|
|
||||||
#jsoneditor {
|
|
||||||
width: 500px;
|
|
||||||
height: 500px;
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
</head>
|
|
||||||
<body>
|
|
||||||
|
|
||||||
<p>
|
|
||||||
Switch editor mode using the mode box.
|
|
||||||
Note that the mode can be changed programmatically as well using the method
|
|
||||||
<code>editor.setMode(mode)</code>, try it in the console of your browser.
|
|
||||||
</p>
|
|
||||||
|
|
||||||
<div id="jsoneditor"></div>
|
|
||||||
|
|
||||||
<script>
|
|
||||||
var container, options, json, editor;
|
|
||||||
|
|
||||||
container = document.getElementById('jsoneditor');
|
|
||||||
|
|
||||||
options = {
|
|
||||||
mode: 'tree',
|
|
||||||
modes: ['code', 'form', 'text', 'tree', 'view', 'preview'], // allowed modes
|
|
||||||
onError: function (err) {
|
|
||||||
alert(err.toString());
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
json = {
|
|
||||||
"array": [1, 2, 3],
|
|
||||||
"boolean": true,
|
|
||||||
"color": "#82b92c",
|
|
||||||
"null": null,
|
|
||||||
"number": 123,
|
|
||||||
"object": {"a": "b", "c": "d"},
|
|
||||||
"string": "Hello World"
|
|
||||||
};
|
|
||||||
|
|
||||||
editor = new JSONEditor(container, options, json);
|
|
||||||
</script>
|
|
||||||
</body>
|
|
||||||
</html>
|
|
|
@ -1,146 +0,0 @@
|
||||||
<!DOCTYPE HTML>
|
|
||||||
<html>
|
|
||||||
<head>
|
|
||||||
<title>JSONEditor | JSON schema validation</title>
|
|
||||||
|
|
||||||
<link href="../dist/jsoneditor.css" rel="stylesheet" type="text/css">
|
|
||||||
<script src="../dist/jsoneditor.js"></script>
|
|
||||||
|
|
||||||
<style type="text/css">
|
|
||||||
body {
|
|
||||||
width: 600px;
|
|
||||||
font: 11pt sans-serif;
|
|
||||||
}
|
|
||||||
|
|
||||||
#anchor {
|
|
||||||
position: relative;
|
|
||||||
}
|
|
||||||
|
|
||||||
#jsoneditor {
|
|
||||||
width: 320px;
|
|
||||||
height: 300px;
|
|
||||||
position: relative;
|
|
||||||
overflow: hidden;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* custom bold styling for non-default JSON schema values */
|
|
||||||
.jsoneditor-is-not-default {
|
|
||||||
font-weight: bold;
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
</head>
|
|
||||||
<body>
|
|
||||||
<h1>Test custom tooltip anchor</h1>
|
|
||||||
<p>
|
|
||||||
The JSON Schema error tooltips and the color picker should have correct placing and overflow the editor, also in combination with scrolling.
|
|
||||||
</p>
|
|
||||||
|
|
||||||
<div id="anchor">
|
|
||||||
<div id="jsoneditor"></div>
|
|
||||||
</div>
|
|
||||||
<div style="height: 2000px"></div>
|
|
||||||
|
|
||||||
<script>
|
|
||||||
const schema = {
|
|
||||||
"title": "Employee",
|
|
||||||
"description": "Object containing employee details",
|
|
||||||
"type": "object",
|
|
||||||
"properties": {
|
|
||||||
"firstName": {
|
|
||||||
"title": "First Name",
|
|
||||||
"description": "The given name.",
|
|
||||||
"examples": [
|
|
||||||
"John"
|
|
||||||
],
|
|
||||||
"type": "string"
|
|
||||||
},
|
|
||||||
"lastName": {
|
|
||||||
"title": "Last Name",
|
|
||||||
"description": "The family name.",
|
|
||||||
"examples": [
|
|
||||||
"Smith"
|
|
||||||
],
|
|
||||||
"type": "string"
|
|
||||||
},
|
|
||||||
"gender": {
|
|
||||||
"title": "Gender",
|
|
||||||
"enum": ["male", "female"]
|
|
||||||
},
|
|
||||||
"availableToHire": {
|
|
||||||
"type": "boolean",
|
|
||||||
"default": false
|
|
||||||
},
|
|
||||||
"age": {
|
|
||||||
"description": "Age in years",
|
|
||||||
"type": "integer",
|
|
||||||
"minimum": 0,
|
|
||||||
"examples": [28, 32]
|
|
||||||
},
|
|
||||||
"job": {
|
|
||||||
"$ref": "job"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"required": ["firstName", "lastName"]
|
|
||||||
}
|
|
||||||
|
|
||||||
const job = {
|
|
||||||
"title": "Job description",
|
|
||||||
"type": "object",
|
|
||||||
"required": ["address"],
|
|
||||||
"properties": {
|
|
||||||
"company": {
|
|
||||||
"type": "string",
|
|
||||||
"examples": [
|
|
||||||
"ACME",
|
|
||||||
"Dexter Industries"
|
|
||||||
]
|
|
||||||
},
|
|
||||||
"role": {
|
|
||||||
"description": "Job title.",
|
|
||||||
"type": "string",
|
|
||||||
"examples": [
|
|
||||||
"Human Resources Coordinator",
|
|
||||||
"Software Developer"
|
|
||||||
],
|
|
||||||
"default": "Software Developer"
|
|
||||||
},
|
|
||||||
"address": {
|
|
||||||
"type": "string"
|
|
||||||
},
|
|
||||||
"salary": {
|
|
||||||
"type": "number",
|
|
||||||
"minimum": 120,
|
|
||||||
"examples": [100, 110, 120]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const json = {
|
|
||||||
firstName: 'John',
|
|
||||||
lastName: 'Doe',
|
|
||||||
gender: null,
|
|
||||||
age: "28",
|
|
||||||
availableToHire: true,
|
|
||||||
favoriteColor: 'red',
|
|
||||||
job: {
|
|
||||||
company: 'freelance',
|
|
||||||
role: 'developer',
|
|
||||||
salary: 100
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const options = {
|
|
||||||
schema: schema,
|
|
||||||
schemaRefs: {"job": job},
|
|
||||||
mode: 'tree',
|
|
||||||
modes: ['code', 'text', 'tree', 'preview'],
|
|
||||||
popupAnchor: document.getElementById('anchor')
|
|
||||||
}
|
|
||||||
|
|
||||||
// create the editor
|
|
||||||
const container = document.getElementById('jsoneditor')
|
|
||||||
const editor = new JSONEditor(container, options, json)
|
|
||||||
editor.expandAll()
|
|
||||||
</script>
|
|
||||||
</body>
|
|
||||||
</html>
|
|
|
@ -1,77 +0,0 @@
|
||||||
<!DOCTYPE HTML>
|
|
||||||
<html>
|
|
||||||
<head>
|
|
||||||
<title>JSONEditor | Preview mode load and save</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>
|
|
||||||
<script src="https://cdnjs.cloudflare.com/ajax/libs/FileSaver.js/2014-11-29/FileSaver.min.js"></script>
|
|
||||||
|
|
||||||
<style>
|
|
||||||
html, body {
|
|
||||||
font: 11pt sans-serif;
|
|
||||||
}
|
|
||||||
#jsoneditor {
|
|
||||||
width: 500px;
|
|
||||||
height: 500px;
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
</head>
|
|
||||||
<body>
|
|
||||||
<h1>Load and save JSON documents in Preview mode</h1>
|
|
||||||
<p>
|
|
||||||
This examples uses HTML5 to load/save local files.
|
|
||||||
Powered by <a href="http://bgrins.github.io/filereader.js/">FileReader.js</a> and
|
|
||||||
<a href="https://github.com/eligrey/FileSaver.js">FileSaver.js</a>.<br>
|
|
||||||
Only supported on modern browsers (Chrome, FireFox, IE10+, Safari 6.1+, Opera 15+).
|
|
||||||
</p>
|
|
||||||
<p>
|
|
||||||
Load a JSON document: <input type="file" id="loadDocument" value="Load"/>
|
|
||||||
</p>
|
|
||||||
<p>
|
|
||||||
Save a JSON document: <input type="button" id="saveDocument" value="Save" />
|
|
||||||
</p>
|
|
||||||
|
|
||||||
<div id="jsoneditor"></div>
|
|
||||||
|
|
||||||
<script>
|
|
||||||
// create the editor
|
|
||||||
var editor = new JSONEditor(document.getElementById('jsoneditor'), {
|
|
||||||
mode: 'preview'
|
|
||||||
});
|
|
||||||
|
|
||||||
// Load a JSON document
|
|
||||||
FileReaderJS.setupInput(document.getElementById('loadDocument'), {
|
|
||||||
readAsDefault: 'Text',
|
|
||||||
on: {
|
|
||||||
load: function (event, file) {
|
|
||||||
editor.setText(event.target.result);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
// Save a JSON document
|
|
||||||
document.getElementById('saveDocument').onclick = function () {
|
|
||||||
// Save Dialog
|
|
||||||
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"){
|
|
||||||
// Nothing to do
|
|
||||||
}else{
|
|
||||||
fname = fname.split('.')[0] + ".json";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
var blob = new Blob([editor.getText()], {type: 'application/json;charset=utf-8'});
|
|
||||||
saveAs(blob, fname);
|
|
||||||
};
|
|
||||||
</script>
|
|
||||||
</body>
|
|
||||||
</html>
|
|
||||||
|
|
||||||
|
|
|
@ -1,109 +0,0 @@
|
||||||
<!DOCTYPE HTML>
|
|
||||||
<html>
|
|
||||||
<head>
|
|
||||||
<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">
|
|
||||||
body {
|
|
||||||
font: 10.5pt arial;
|
|
||||||
color: #4d4d4d;
|
|
||||||
line-height: 150%;
|
|
||||||
width: 600px;
|
|
||||||
padding-left: 40px;
|
|
||||||
}
|
|
||||||
|
|
||||||
html, body {
|
|
||||||
width: 100%;
|
|
||||||
padding: 0;
|
|
||||||
box-sizing: border-box;
|
|
||||||
}
|
|
||||||
|
|
||||||
code {
|
|
||||||
background-color: #f5f5f5;
|
|
||||||
}
|
|
||||||
|
|
||||||
#jsoneditor {
|
|
||||||
max-width: 600px;
|
|
||||||
width: 90%;
|
|
||||||
height: 500px;
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
</head>
|
|
||||||
<body>
|
|
||||||
|
|
||||||
<p>
|
|
||||||
Switch editor mode using the mode box.
|
|
||||||
Note that the mode can be changed programmatically as well using the method
|
|
||||||
<code>editor.setMode(mode)</code>, try it in the console of your browser.
|
|
||||||
</p>
|
|
||||||
|
|
||||||
<div id="jsoneditor"></div>
|
|
||||||
|
|
||||||
<script>
|
|
||||||
var container = document.getElementById('jsoneditor');
|
|
||||||
|
|
||||||
var schema = {
|
|
||||||
"title": "User",
|
|
||||||
"type": "object",
|
|
||||||
"properties": {
|
|
||||||
"firstName": {
|
|
||||||
"type": "string"
|
|
||||||
},
|
|
||||||
"lastName": {
|
|
||||||
"type": "string"
|
|
||||||
},
|
|
||||||
"gender": {
|
|
||||||
"enum": ["male", "female"]
|
|
||||||
},
|
|
||||||
"age": {
|
|
||||||
"description": "Age in years",
|
|
||||||
"examples": [18, 65],
|
|
||||||
"type": "integer",
|
|
||||||
"minimum": 0
|
|
||||||
},
|
|
||||||
"hobbies": {
|
|
||||||
"$ref": "hobbies.json"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"required": ["firstName", "lastName"]
|
|
||||||
};
|
|
||||||
|
|
||||||
var hobbiesSchema = {
|
|
||||||
"type": "array",
|
|
||||||
"items": {
|
|
||||||
"type": "string"
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
var options = {
|
|
||||||
mode: 'tree',
|
|
||||||
modes: ['code', 'form', 'text', 'tree', 'view', 'preview'], // allowed modes
|
|
||||||
onError: function (err) {
|
|
||||||
console.error(err);
|
|
||||||
},
|
|
||||||
schema: schema,
|
|
||||||
schemaRefs: {"hobbies.json": hobbiesSchema}
|
|
||||||
};
|
|
||||||
|
|
||||||
var json = {
|
|
||||||
"firstName": "Jos",
|
|
||||||
"lastName": "de Jong",
|
|
||||||
gender: null,
|
|
||||||
"age": 34.2,
|
|
||||||
"hobbies": [
|
|
||||||
"programming",
|
|
||||||
"movies",
|
|
||||||
"bicycling"
|
|
||||||
]
|
|
||||||
};
|
|
||||||
|
|
||||||
var editor = new JSONEditor(container, options, json);
|
|
||||||
|
|
||||||
console.log('json', json);
|
|
||||||
console.log('schema', schema);
|
|
||||||
</script>
|
|
||||||
</body>
|
|
||||||
</html>
|
|
|
@ -1,98 +0,0 @@
|
||||||
<!DOCTYPE HTML>
|
|
||||||
<html>
|
|
||||||
<head>
|
|
||||||
<title>JSONEditor | Update JSON</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">
|
|
||||||
body, html {
|
|
||||||
font: 10.5pt arial;
|
|
||||||
color: #4d4d4d;
|
|
||||||
line-height: 150%;
|
|
||||||
width: 100%;
|
|
||||||
height: 100%;
|
|
||||||
padding: 0;
|
|
||||||
margin: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
.main {
|
|
||||||
display: flex;
|
|
||||||
flex-direction: column;
|
|
||||||
width: 100%;
|
|
||||||
height: 100%;
|
|
||||||
}
|
|
||||||
|
|
||||||
.main > div {
|
|
||||||
margin: 10px;
|
|
||||||
}
|
|
||||||
|
|
||||||
#jsoneditor {
|
|
||||||
flex: 1;
|
|
||||||
width: 100%;
|
|
||||||
max-width: 500px;
|
|
||||||
height: 200px;
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
</head>
|
|
||||||
<body>
|
|
||||||
<div class="main">
|
|
||||||
<div>
|
|
||||||
<button id="update">update JSON</button>
|
|
||||||
<button id="updateText">update JSON Text</button>
|
|
||||||
</div>
|
|
||||||
<div id="jsoneditor"></div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<script>
|
|
||||||
var container = document.getElementById('jsoneditor');
|
|
||||||
var updateButton = document.getElementById('update');
|
|
||||||
var updateTextButton = document.getElementById('updateText');
|
|
||||||
|
|
||||||
var options = {
|
|
||||||
mode: 'tree',
|
|
||||||
modes: ['code', 'form', 'text', 'tree', 'view', 'preview'] // allowed modes
|
|
||||||
};
|
|
||||||
|
|
||||||
var json = {
|
|
||||||
"arrayToObject": [1, 2, 3],
|
|
||||||
"arrayGrow": [1, 2, 3],
|
|
||||||
"arrayShrink": [1, 2, 3],
|
|
||||||
"autoToArray": 123,
|
|
||||||
"arrayToAuto": [1, 2, 3],
|
|
||||||
"objectGrow": {"a": "b", "c": "d"},
|
|
||||||
"objectShrink": {"a": "b", "c": "d"},
|
|
||||||
"objectToArray": {"a": "b", "c": "d"},
|
|
||||||
"removeField": "old"
|
|
||||||
};
|
|
||||||
|
|
||||||
var updatedJson = {
|
|
||||||
"arrayToObject": {"a": "b", "c": "d"},
|
|
||||||
"arrayGrow": [1, 2, 3, 4, 5],
|
|
||||||
"arrayShrink": [1, 3],
|
|
||||||
"autoToArray": [1, 2, 3],
|
|
||||||
"arrayToAuto": 123,
|
|
||||||
"objectGrow": {"a": "b", "c": "ddd", "e": "f"},
|
|
||||||
"objectShrink": {"c": "d"},
|
|
||||||
"objectToArray": [1, 2, 3],
|
|
||||||
"newField": "new"
|
|
||||||
};
|
|
||||||
|
|
||||||
var editor = new JSONEditor(container, options, json);
|
|
||||||
|
|
||||||
editor.expandAll();
|
|
||||||
|
|
||||||
updateButton.onclick = function () {
|
|
||||||
editor.update(updatedJson);
|
|
||||||
};
|
|
||||||
|
|
||||||
updateTextButton.onclick = function () {
|
|
||||||
editor.updateText(JSON.stringify(updatedJson, null, 2));
|
|
||||||
};
|
|
||||||
</script>
|
|
||||||
</body>
|
|
||||||
</html>
|
|
|
@ -1,561 +0,0 @@
|
||||||
import assert from 'assert'
|
|
||||||
import {
|
|
||||||
compileJSONPointer,
|
|
||||||
findUniqueName,
|
|
||||||
formatSize,
|
|
||||||
get,
|
|
||||||
getChildPaths,
|
|
||||||
getIndexForPosition,
|
|
||||||
isObject,
|
|
||||||
isTimestamp,
|
|
||||||
limitCharacters,
|
|
||||||
makeFieldTooltip,
|
|
||||||
parsePath,
|
|
||||||
parseString,
|
|
||||||
repair,
|
|
||||||
sort,
|
|
||||||
sortObjectKeys,
|
|
||||||
stringifyPath,
|
|
||||||
isValidationErrorChanged
|
|
||||||
} from '../src/js/util'
|
|
||||||
|
|
||||||
describe('util', () => {
|
|
||||||
describe('repair', () => {
|
|
||||||
it('should leave valid JSON as is', () => {
|
|
||||||
assert.strictEqual(repair('{"a":2}'), '{"a":2}')
|
|
||||||
})
|
|
||||||
|
|
||||||
it('should replace JavaScript with JSON', () => {
|
|
||||||
assert.strictEqual(repair('{a:2}'), '{"a":2}')
|
|
||||||
assert.strictEqual(repair('{a: 2}'), '{"a": 2}')
|
|
||||||
assert.strictEqual(repair('{\n a: 2\n}'), '{\n "a": 2\n}')
|
|
||||||
assert.strictEqual(repair('{\'a\':2}'), '{"a":2}')
|
|
||||||
assert.strictEqual(repair('{a:\'foo\'}'), '{"a":"foo"}')
|
|
||||||
assert.strictEqual(repair('{a:\'foo\',b:\'bar\'}'), '{"a":"foo","b":"bar"}')
|
|
||||||
|
|
||||||
// should leave string content untouched
|
|
||||||
assert.strictEqual(repair('"{a:b}"'), '"{a:b}"')
|
|
||||||
})
|
|
||||||
|
|
||||||
it('should add/remove escape characters', () => {
|
|
||||||
assert.strictEqual(repair('"foo\'bar"'), '"foo\'bar"')
|
|
||||||
assert.strictEqual(repair('"foo\\"bar"'), '"foo\\"bar"')
|
|
||||||
assert.strictEqual(repair('\'foo"bar\''), '"foo\\"bar"')
|
|
||||||
assert.strictEqual(repair('\'foo\\\'bar\''), '"foo\'bar"')
|
|
||||||
assert.strictEqual(repair('"foo\\\'bar"'), '"foo\'bar"')
|
|
||||||
})
|
|
||||||
|
|
||||||
it('should replace special white characters', () => {
|
|
||||||
assert.strictEqual(repair('{"a":\u00a0"foo\u00a0bar"}'), '{"a": "foo\u00a0bar"}')
|
|
||||||
assert.strictEqual(repair('{"a":\u2009"foo"}'), '{"a": "foo"}')
|
|
||||||
})
|
|
||||||
|
|
||||||
it('should escape unescaped control characters', () => {
|
|
||||||
assert.strictEqual(repair('"hello\bworld"'), '"hello\\bworld"')
|
|
||||||
assert.strictEqual(repair('"hello\fworld"'), '"hello\\fworld"')
|
|
||||||
assert.strictEqual(repair('"hello\nworld"'), '"hello\\nworld"')
|
|
||||||
assert.strictEqual(repair('"hello\rworld"'), '"hello\\rworld"')
|
|
||||||
assert.strictEqual(repair('"hello\tworld"'), '"hello\\tworld"')
|
|
||||||
assert.strictEqual(repair('{"value\n": "dc=hcm,dc=com"}'), '{"value\\n": "dc=hcm,dc=com"}')
|
|
||||||
})
|
|
||||||
|
|
||||||
it('should replace left/right quotes', () => {
|
|
||||||
assert.strictEqual(repair('\u2018foo\u2019'), '"foo"')
|
|
||||||
assert.strictEqual(repair('\u201Cfoo\u201D'), '"foo"')
|
|
||||||
assert.strictEqual(repair('\u0060foo\u00B4'), '"foo"')
|
|
||||||
})
|
|
||||||
|
|
||||||
it('remove comments', () => {
|
|
||||||
assert.strictEqual(repair('/* foo */ {}'), ' {}')
|
|
||||||
assert.strictEqual(repair('/* foo */ {}'), ' {}')
|
|
||||||
assert.strictEqual(repair('{a:\'foo\',/*hello*/b:\'bar\'}'), '{"a":"foo","b":"bar"}')
|
|
||||||
assert.strictEqual(repair('{\na:\'foo\',//hello\nb:\'bar\'\n}'), '{\n"a":"foo",\n"b":"bar"\n}')
|
|
||||||
|
|
||||||
// should not remove comments in string
|
|
||||||
assert.strictEqual(repair('{"str":"/* foo */"}'), '{"str":"/* foo */"}')
|
|
||||||
})
|
|
||||||
|
|
||||||
it('should strip JSONP notation', () => {
|
|
||||||
// matching
|
|
||||||
assert.strictEqual(repair('callback_123({});'), '{}')
|
|
||||||
assert.strictEqual(repair('callback_123([]);'), '[]')
|
|
||||||
assert.strictEqual(repair('callback_123(2);'), '2')
|
|
||||||
assert.strictEqual(repair('callback_123("foo");'), '"foo"')
|
|
||||||
assert.strictEqual(repair('callback_123(null);'), 'null')
|
|
||||||
assert.strictEqual(repair('callback_123(true);'), 'true')
|
|
||||||
assert.strictEqual(repair('callback_123(false);'), 'false')
|
|
||||||
assert.strictEqual(repair('/* foo bar */ callback_123 ({})'), '{}')
|
|
||||||
assert.strictEqual(repair('/* foo bar */ callback_123 ({})'), '{}')
|
|
||||||
assert.strictEqual(repair('/* foo bar */\ncallback_123({})'), '{}')
|
|
||||||
assert.strictEqual(repair('/* foo bar */ callback_123 ( {} )'), ' {} ')
|
|
||||||
assert.strictEqual(repair(' /* foo bar */ callback_123 ({}); '), '{}')
|
|
||||||
assert.strictEqual(repair('\n/* foo\nbar */\ncallback_123 ({});\n\n'), '{}')
|
|
||||||
|
|
||||||
// non-matching
|
|
||||||
assert.strictEqual(repair('callback {}'), 'callback {}')
|
|
||||||
assert.strictEqual(repair('callback({}'), 'callback({}')
|
|
||||||
})
|
|
||||||
|
|
||||||
it('should strip trailing commas', () => {
|
|
||||||
// matching
|
|
||||||
assert.strictEqual(repair('[1,2,3,]'), '[1,2,3]')
|
|
||||||
assert.strictEqual(repair('[1,2,3,\n]'), '[1,2,3\n]')
|
|
||||||
assert.strictEqual(repair('[1,2,3, \n ]'), '[1,2,3 \n ]')
|
|
||||||
assert.strictEqual(repair('{"a":2,}'), '{"a":2}')
|
|
||||||
|
|
||||||
// not matching
|
|
||||||
assert.strictEqual(repair('"[1,2,3,]"'), '"[1,2,3,]"')
|
|
||||||
assert.strictEqual(repair('"{a:2,}"'), '"{a:2,}"')
|
|
||||||
})
|
|
||||||
|
|
||||||
it('should strip MongoDB data types', () => {
|
|
||||||
const mongoDocument = '{\n' +
|
|
||||||
' "_id" : ObjectId("123"),\n' +
|
|
||||||
' "isoDate" : ISODate("2012-12-19T06:01:17.171Z"),\n' +
|
|
||||||
' "regularNumber" : 67,\n' +
|
|
||||||
' "long" : NumberLong("2"),\n' +
|
|
||||||
' "long2" : NumberLong(2),\n' +
|
|
||||||
' "int" : NumberInt("3"),\n' +
|
|
||||||
' "int2" : NumberInt(3),\n' +
|
|
||||||
' "decimal" : NumberDecimal("4"),\n' +
|
|
||||||
' "decimal2" : NumberDecimal(4)\n' +
|
|
||||||
'}'
|
|
||||||
|
|
||||||
const expectedJson = '{\n' +
|
|
||||||
' "_id" : "123",\n' +
|
|
||||||
' "isoDate" : "2012-12-19T06:01:17.171Z",\n' +
|
|
||||||
' "regularNumber" : 67,\n' +
|
|
||||||
' "long" : "2",\n' +
|
|
||||||
' "long2" : 2,\n' +
|
|
||||||
' "int" : "3",\n' +
|
|
||||||
' "int2" : 3,\n' +
|
|
||||||
' "decimal" : "4",\n' +
|
|
||||||
' "decimal2" : 4\n' +
|
|
||||||
'}'
|
|
||||||
|
|
||||||
assert.strictEqual(repair(mongoDocument), expectedJson)
|
|
||||||
})
|
|
||||||
|
|
||||||
it('should replace Python constants None, True, False', () => {
|
|
||||||
const pythonDocument = '{\n' +
|
|
||||||
' "null": None,\n' +
|
|
||||||
' "true": True,\n' +
|
|
||||||
' "false": False\n' +
|
|
||||||
' "array": [1, foo, None, True, False]\n' +
|
|
||||||
'}'
|
|
||||||
|
|
||||||
const expectedJson = '{\n' +
|
|
||||||
' "null": null,\n' +
|
|
||||||
' "true": true,\n' +
|
|
||||||
' "false": false\n' +
|
|
||||||
' "array": [1, "foo", null, true, false]\n' +
|
|
||||||
'}'
|
|
||||||
|
|
||||||
assert.strictEqual(repair(pythonDocument), expectedJson)
|
|
||||||
})
|
|
||||||
|
|
||||||
it('should repair missing comma between objects', () => {
|
|
||||||
const text = '{"aray": [{}{}]}'
|
|
||||||
const expected = '{"aray": [{},{}]}'
|
|
||||||
|
|
||||||
assert.strictEqual(repair(text), expected)
|
|
||||||
})
|
|
||||||
|
|
||||||
it('should not repair normal array with comma separated objects', () => {
|
|
||||||
const text = '[\n{},\n{}\n]'
|
|
||||||
|
|
||||||
assert.strictEqual(repair(text), text)
|
|
||||||
})
|
|
||||||
|
|
||||||
it('should repair line separated json (for example from robo3t', () => {
|
|
||||||
const text = '' +
|
|
||||||
'/* 1 */\n' +
|
|
||||||
'{}\n' +
|
|
||||||
'\n' +
|
|
||||||
'/* 2 */\n' +
|
|
||||||
'{}\n' +
|
|
||||||
'\n' +
|
|
||||||
'/* 3 */\n' +
|
|
||||||
'{}\n'
|
|
||||||
const expected = '[\n{},\n\n{},\n\n{}\n\n]'
|
|
||||||
|
|
||||||
assert.strictEqual(repair(text), expected)
|
|
||||||
})
|
|
||||||
})
|
|
||||||
|
|
||||||
describe('jsonPath', () => {
|
|
||||||
it('should stringify an array of paths', () => {
|
|
||||||
assert.deepStrictEqual(stringifyPath([]), '')
|
|
||||||
assert.deepStrictEqual(stringifyPath(['foo']), '.foo')
|
|
||||||
assert.deepStrictEqual(stringifyPath(['foo', 'bar']), '.foo.bar')
|
|
||||||
assert.deepStrictEqual(stringifyPath(['foo', 2]), '.foo[2]')
|
|
||||||
assert.deepStrictEqual(stringifyPath(['foo', 2, 'bar']), '.foo[2].bar')
|
|
||||||
assert.deepStrictEqual(stringifyPath(['foo', 2, 'bar_baz']), '.foo[2].bar_baz')
|
|
||||||
assert.deepStrictEqual(stringifyPath([2]), '[2]')
|
|
||||||
assert.deepStrictEqual(stringifyPath(['foo', 'prop-with-hyphens']), '.foo["prop-with-hyphens"]')
|
|
||||||
assert.deepStrictEqual(stringifyPath(['foo', 'prop with spaces']), '.foo["prop with spaces"]')
|
|
||||||
})
|
|
||||||
|
|
||||||
it('should parse a json path', () => {
|
|
||||||
assert.deepStrictEqual(parsePath(''), [])
|
|
||||||
assert.deepStrictEqual(parsePath('.foo'), ['foo'])
|
|
||||||
assert.deepStrictEqual(parsePath('.foo.bar'), ['foo', 'bar'])
|
|
||||||
assert.deepStrictEqual(parsePath('.foo[2]'), ['foo', 2])
|
|
||||||
assert.deepStrictEqual(parsePath('.foo[2].bar'), ['foo', 2, 'bar'])
|
|
||||||
assert.deepStrictEqual(parsePath('.foo["prop with spaces"]'), ['foo', 'prop with spaces'])
|
|
||||||
assert.deepStrictEqual(parsePath('.foo[\'prop with single quotes as outputted by ajv library\']'), ['foo', 'prop with single quotes as outputted by ajv library'])
|
|
||||||
assert.deepStrictEqual(parsePath('.foo["prop with . dot"]'), ['foo', 'prop with . dot'])
|
|
||||||
assert.deepStrictEqual(parsePath('.foo["prop with ] character"]'), ['foo', 'prop with ] character'])
|
|
||||||
assert.deepStrictEqual(parsePath('.foo[*].bar'), ['foo', '*', 'bar'])
|
|
||||||
assert.deepStrictEqual(parsePath('[2]'), [2])
|
|
||||||
})
|
|
||||||
|
|
||||||
it('should throw an exception in case of an invalid path', () => {
|
|
||||||
assert.throws(() => { parsePath('.') }, /Invalid JSON path: property name expected at index 1/)
|
|
||||||
assert.throws(() => { parsePath('[') }, /Invalid JSON path: unexpected end, character ] expected/)
|
|
||||||
assert.throws(() => { parsePath('[]') }, /Invalid JSON path: array value expected at index 1/)
|
|
||||||
assert.throws(() => { parsePath('.foo[ ]') }, /Invalid JSON path: array value expected at index 7/)
|
|
||||||
assert.throws(() => { parsePath('.[]') }, /Invalid JSON path: property name expected at index 1/)
|
|
||||||
assert.throws(() => { parsePath('["23]') }, /Invalid JSON path: unexpected end, character " expected/)
|
|
||||||
assert.throws(() => { parsePath('.foo bar') }, /Invalid JSON path: unexpected character " " at index 4/)
|
|
||||||
})
|
|
||||||
})
|
|
||||||
|
|
||||||
describe('getIndexForPosition', () => {
|
|
||||||
const el = {
|
|
||||||
value: 'Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.\nUt enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat.\nDuis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur.\nExcepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.'
|
|
||||||
}
|
|
||||||
|
|
||||||
it('happy flows - row and column in range', () => {
|
|
||||||
assert.strictEqual(getIndexForPosition(el, 1, 1), 0)
|
|
||||||
assert.strictEqual(getIndexForPosition(el, 2, 1), 124)
|
|
||||||
assert.strictEqual(getIndexForPosition(el, 3, 8), 239)
|
|
||||||
assert.strictEqual(getIndexForPosition(el, 4, 22), 356)
|
|
||||||
})
|
|
||||||
|
|
||||||
it('if range exceeds it should be considered as if it is last row or column length', () => {
|
|
||||||
assert.strictEqual(getIndexForPosition(el, 1, 100000), 123)
|
|
||||||
assert.strictEqual(getIndexForPosition(el, 100000, 1), 335)
|
|
||||||
assert.strictEqual(getIndexForPosition(el, 100000, 100000), 445)
|
|
||||||
})
|
|
||||||
|
|
||||||
it('missing or wrong input sould return -1', () => {
|
|
||||||
assert.strictEqual(getIndexForPosition(el), -1)
|
|
||||||
assert.strictEqual(getIndexForPosition(el, undefined, 1), -1)
|
|
||||||
assert.strictEqual(getIndexForPosition(el, 1, undefined), -1)
|
|
||||||
assert.strictEqual(getIndexForPosition(el, -2, -2), -1)
|
|
||||||
})
|
|
||||||
})
|
|
||||||
|
|
||||||
describe('isValidationErrorChanged', () => {
|
|
||||||
const err1 = { keyword: 'enum', dataPath: '.gender', schemaPath: '#/properties/gender/enum', params: { allowedValues: ['male', 'female'] }, message: 'should be equal to one of: "male", "female"', schema: ['male', 'female'], parentSchema: { title: 'Gender', enum: ['male', 'female'] }, data: null, type: 'validation' }
|
|
||||||
const err2 = { keyword: 'type', dataPath: '.age', schemaPath: '#/properties/age/type', params: { type: 'integer' }, message: 'should be integer', schema: 'integer', parentSchema: { description: 'Age in years', type: 'integer', minimum: 0, examples: [28, 32] }, data: '28', type: 'validation' }
|
|
||||||
const err3 = { dataPath: '.gender', message: 'Member must be an object with properties "name" and "age"' }
|
|
||||||
|
|
||||||
it('empty value for both current and previoues error should return false', () => {
|
|
||||||
assert.strictEqual(isValidationErrorChanged(), false)
|
|
||||||
})
|
|
||||||
|
|
||||||
it('empty value for one of current and previoues error should return true', () => {
|
|
||||||
assert.strictEqual(isValidationErrorChanged([err1]), true)
|
|
||||||
assert.strictEqual(isValidationErrorChanged(undefined, [err1]), true)
|
|
||||||
})
|
|
||||||
|
|
||||||
it('different length of current and previoues errors should return true', () => {
|
|
||||||
assert.strictEqual(isValidationErrorChanged([err1], []), true)
|
|
||||||
assert.strictEqual(isValidationErrorChanged([err1], [err1, err2]), true)
|
|
||||||
})
|
|
||||||
|
|
||||||
it('same values for current and previoues errors should return false', () => {
|
|
||||||
assert.strictEqual(isValidationErrorChanged([err1, err2, err3], [err2, err3, err1]), false)
|
|
||||||
})
|
|
||||||
|
|
||||||
it('different values for current and previoues errors should return true', () => {
|
|
||||||
assert.strictEqual(isValidationErrorChanged([err1, err2], [err3, err1]), true)
|
|
||||||
})
|
|
||||||
})
|
|
||||||
|
|
||||||
describe('get', () => {
|
|
||||||
it('should get a nested property from an object', () => {
|
|
||||||
const obj = {
|
|
||||||
a: {
|
|
||||||
b: 2
|
|
||||||
},
|
|
||||||
c: 3,
|
|
||||||
d: null,
|
|
||||||
e: undefined
|
|
||||||
}
|
|
||||||
|
|
||||||
assert.strictEqual(get(obj, ['a', 'b']), 2)
|
|
||||||
assert.strictEqual(get(obj, ['c']), 3)
|
|
||||||
assert.deepStrictEqual(get(obj, ['a']), { b: 2 })
|
|
||||||
assert.strictEqual(get(obj, ['a', 'foo']), undefined)
|
|
||||||
assert.strictEqual(get(obj, ['a', 'foo', 'bar']), undefined)
|
|
||||||
assert.strictEqual(get(obj, ['d']), null)
|
|
||||||
assert.strictEqual(get(obj, ['d', 'foo', 'bar']), null)
|
|
||||||
assert.strictEqual(get(obj, ['e']), undefined)
|
|
||||||
})
|
|
||||||
})
|
|
||||||
|
|
||||||
describe('makeFieldTooltip', () => {
|
|
||||||
it('should return empty string when the schema is missing all relevant fields', () => {
|
|
||||||
assert.strictEqual(makeFieldTooltip({}), '')
|
|
||||||
assert.strictEqual(makeFieldTooltip({ additionalProperties: false }), '')
|
|
||||||
assert.strictEqual(makeFieldTooltip(), '')
|
|
||||||
})
|
|
||||||
|
|
||||||
it('should make tooltips with only title', () => {
|
|
||||||
assert.strictEqual(makeFieldTooltip({ title: 'foo' }), 'foo')
|
|
||||||
})
|
|
||||||
|
|
||||||
it('should make tooltips with only description', () => {
|
|
||||||
assert.strictEqual(makeFieldTooltip({ description: 'foo' }), 'foo')
|
|
||||||
})
|
|
||||||
|
|
||||||
it('should make tooltips with only default', () => {
|
|
||||||
assert.strictEqual(makeFieldTooltip({ default: 'foo' }), 'Default\n"foo"')
|
|
||||||
})
|
|
||||||
|
|
||||||
it('should make tooltips with only examples', () => {
|
|
||||||
assert.strictEqual(makeFieldTooltip({ examples: ['foo', 'bar'] }), 'Examples\n"foo"\n"bar"')
|
|
||||||
})
|
|
||||||
|
|
||||||
it('should make tooltips with title and description', () => {
|
|
||||||
assert.strictEqual(makeFieldTooltip({ title: 'foo', description: 'bar' }), 'foo\nbar')
|
|
||||||
|
|
||||||
const longTitle = 'Lorem Ipsum Dolor'
|
|
||||||
const longDescription = 'Duis id elit non ante gravida vestibulum non nec est. ' +
|
|
||||||
'Proin vitae ligula at elit dapibus tempor. ' +
|
|
||||||
'Etiam lacinia augue vel condimentum interdum. '
|
|
||||||
assert.strictEqual(
|
|
||||||
makeFieldTooltip({ title: longTitle, description: longDescription }),
|
|
||||||
longTitle + '\n' + longDescription
|
|
||||||
)
|
|
||||||
})
|
|
||||||
|
|
||||||
it('should make tooltips with title, description, and examples', () => {
|
|
||||||
assert.strictEqual(
|
|
||||||
makeFieldTooltip({ title: 'foo', description: 'bar', examples: ['baz'] }),
|
|
||||||
'foo\nbar\n\nExamples\n"baz"'
|
|
||||||
)
|
|
||||||
})
|
|
||||||
|
|
||||||
it('should make tooltips with title, description, default, and examples', () => {
|
|
||||||
assert.strictEqual(
|
|
||||||
makeFieldTooltip({ title: 'foo', description: 'bar', default: 'bat', examples: ['baz'] }),
|
|
||||||
'foo\nbar\n\nDefault\n"bat"\n\nExamples\n"baz"'
|
|
||||||
)
|
|
||||||
})
|
|
||||||
|
|
||||||
it('should handle empty fields', () => {
|
|
||||||
assert.strictEqual(makeFieldTooltip({ title: '', description: 'bar' }), 'bar')
|
|
||||||
assert.strictEqual(makeFieldTooltip({ title: 'foo', description: '' }), 'foo')
|
|
||||||
assert.strictEqual(makeFieldTooltip({ description: 'bar', examples: [] }), 'bar')
|
|
||||||
assert.strictEqual(makeFieldTooltip({ description: 'bar', examples: [''] }), 'bar\n\nExamples\n""')
|
|
||||||
})
|
|
||||||
|
|
||||||
it('should internationalize "Defaults" correctly', () => {
|
|
||||||
assert.strictEqual(makeFieldTooltip({ default: 'foo' }, 'pt-BR'), 'Revelia\n"foo"')
|
|
||||||
})
|
|
||||||
|
|
||||||
it('should internationalize "Examples" correctly', () => {
|
|
||||||
assert.strictEqual(makeFieldTooltip({ examples: ['foo'] }, 'pt-BR'), 'Exemplos\n"foo"')
|
|
||||||
})
|
|
||||||
})
|
|
||||||
|
|
||||||
describe('getChildPaths', () => {
|
|
||||||
it('should extract all child paths of an array containing objects', () => {
|
|
||||||
const json = [
|
|
||||||
{ name: 'A', location: { latitude: 1, longitude: 2 } },
|
|
||||||
{ name: 'B', location: { latitude: 1, longitude: 2 } },
|
|
||||||
{ name: 'C', timestamp: 0 }
|
|
||||||
]
|
|
||||||
|
|
||||||
assert.deepStrictEqual(getChildPaths(json), [
|
|
||||||
'.location.latitude',
|
|
||||||
'.location.longitude',
|
|
||||||
'.name',
|
|
||||||
'.timestamp'
|
|
||||||
])
|
|
||||||
})
|
|
||||||
|
|
||||||
it('should extract all child paths of an array containing objects, including objects', () => {
|
|
||||||
const json = [
|
|
||||||
{ name: 'A', location: { latitude: 1, longitude: 2 } },
|
|
||||||
{ name: 'B', location: { latitude: 1, longitude: 2 } },
|
|
||||||
{ name: 'C', timestamp: 0 }
|
|
||||||
]
|
|
||||||
|
|
||||||
assert.deepStrictEqual(getChildPaths(json, true), [
|
|
||||||
'',
|
|
||||||
'.location',
|
|
||||||
'.location.latitude',
|
|
||||||
'.location.longitude',
|
|
||||||
'.name',
|
|
||||||
'.timestamp'
|
|
||||||
])
|
|
||||||
})
|
|
||||||
|
|
||||||
it('should extract all child paths of an array containing values', () => {
|
|
||||||
const json = [1, 2, 3]
|
|
||||||
|
|
||||||
assert.deepStrictEqual(getChildPaths(json), [
|
|
||||||
''
|
|
||||||
])
|
|
||||||
})
|
|
||||||
|
|
||||||
it('should extract all child paths of a non-array', () => {
|
|
||||||
assert.deepStrictEqual(getChildPaths({ a: 2, b: { c: 3 } }), [''])
|
|
||||||
assert.deepStrictEqual(getChildPaths('foo'), [''])
|
|
||||||
assert.deepStrictEqual(getChildPaths(123), [''])
|
|
||||||
})
|
|
||||||
})
|
|
||||||
|
|
||||||
it('should test whether something is an object', () => {
|
|
||||||
assert.strictEqual(isObject({}), true)
|
|
||||||
assert.strictEqual(isObject(new Date()), true)
|
|
||||||
assert.strictEqual(isObject([]), false)
|
|
||||||
assert.strictEqual(isObject(2), false)
|
|
||||||
assert.strictEqual(isObject(null), false)
|
|
||||||
assert.strictEqual(isObject(undefined), false)
|
|
||||||
assert.strictEqual(isObject(), false)
|
|
||||||
})
|
|
||||||
|
|
||||||
describe('sort', () => {
|
|
||||||
it('should sort an array', () => {
|
|
||||||
const array = [4, 1, 10, 2]
|
|
||||||
assert.deepStrictEqual(sort(array), [1, 2, 4, 10])
|
|
||||||
assert.deepStrictEqual(sort(array, '.', 'desc'), [10, 4, 2, 1])
|
|
||||||
})
|
|
||||||
|
|
||||||
it('should sort an array containing objects', () => {
|
|
||||||
const array = [
|
|
||||||
{ value: 4 },
|
|
||||||
{ value: 1 },
|
|
||||||
{ value: 10 },
|
|
||||||
{ value: 2 }
|
|
||||||
]
|
|
||||||
|
|
||||||
assert.deepStrictEqual(sort(array, '.value'), [
|
|
||||||
{ value: 1 },
|
|
||||||
{ value: 2 },
|
|
||||||
{ value: 4 },
|
|
||||||
{ value: 10 }
|
|
||||||
])
|
|
||||||
|
|
||||||
assert.deepStrictEqual(sort(array, '.value', 'desc'), [
|
|
||||||
{ value: 10 },
|
|
||||||
{ value: 4 },
|
|
||||||
{ value: 2 },
|
|
||||||
{ value: 1 }
|
|
||||||
])
|
|
||||||
})
|
|
||||||
})
|
|
||||||
|
|
||||||
describe('sortObjectKeys', () => {
|
|
||||||
it('should sort the keys of an object', () => {
|
|
||||||
const object = {
|
|
||||||
c: 'c',
|
|
||||||
a: 'a',
|
|
||||||
b: 'b'
|
|
||||||
}
|
|
||||||
assert.strictEqual(JSON.stringify(object), '{"c":"c","a":"a","b":"b"}')
|
|
||||||
assert.strictEqual(JSON.stringify(sortObjectKeys(object)), '{"a":"a","b":"b","c":"c"}')
|
|
||||||
assert.strictEqual(JSON.stringify(sortObjectKeys(object, 'asc')), '{"a":"a","b":"b","c":"c"}')
|
|
||||||
assert.strictEqual(JSON.stringify(sortObjectKeys(object, 'desc')), '{"c":"c","b":"b","a":"a"}')
|
|
||||||
})
|
|
||||||
})
|
|
||||||
|
|
||||||
it('should parse a string', () => {
|
|
||||||
assert.strictEqual(parseString('foo'), 'foo')
|
|
||||||
assert.strictEqual(parseString('234foo'), '234foo')
|
|
||||||
assert.strictEqual(parseString(' 234'), 234)
|
|
||||||
assert.strictEqual(parseString('234 '), 234)
|
|
||||||
assert.strictEqual(parseString('2.3'), 2.3)
|
|
||||||
assert.strictEqual(parseString('null'), null)
|
|
||||||
assert.strictEqual(parseString('true'), true)
|
|
||||||
assert.strictEqual(parseString('false'), false)
|
|
||||||
assert.strictEqual(parseString('+1'), 1)
|
|
||||||
assert.strictEqual(parseString(' '), ' ')
|
|
||||||
assert.strictEqual(parseString(''), '')
|
|
||||||
assert.strictEqual(parseString('"foo"'), '"foo"')
|
|
||||||
assert.strictEqual(parseString('"2"'), '"2"')
|
|
||||||
assert.strictEqual(parseString('\'foo\''), '\'foo\'')
|
|
||||||
})
|
|
||||||
|
|
||||||
it('should find a unique name', () => {
|
|
||||||
assert.strictEqual(findUniqueName('other', [
|
|
||||||
'a',
|
|
||||||
'b',
|
|
||||||
'c'
|
|
||||||
]), 'other')
|
|
||||||
|
|
||||||
assert.strictEqual(findUniqueName('b', [
|
|
||||||
'a',
|
|
||||||
'b',
|
|
||||||
'c'
|
|
||||||
]), 'b (copy)')
|
|
||||||
|
|
||||||
assert.strictEqual(findUniqueName('b', [
|
|
||||||
'a',
|
|
||||||
'b',
|
|
||||||
'c',
|
|
||||||
'b (copy)'
|
|
||||||
]), 'b (copy 2)')
|
|
||||||
|
|
||||||
assert.strictEqual(findUniqueName('b', [
|
|
||||||
'a',
|
|
||||||
'b',
|
|
||||||
'c',
|
|
||||||
'b (copy)',
|
|
||||||
'b (copy 2)'
|
|
||||||
]), 'b (copy 3)')
|
|
||||||
|
|
||||||
assert.strictEqual(findUniqueName('b (copy)', [
|
|
||||||
'a',
|
|
||||||
'b',
|
|
||||||
'b (copy)',
|
|
||||||
'b (copy 2)',
|
|
||||||
'c'
|
|
||||||
]), 'b (copy 3)')
|
|
||||||
|
|
||||||
assert.strictEqual(findUniqueName('b (copy 2)', [
|
|
||||||
'a',
|
|
||||||
'b',
|
|
||||||
'b (copy)',
|
|
||||||
'b (copy 2)',
|
|
||||||
'c'
|
|
||||||
]), 'b (copy 3)')
|
|
||||||
})
|
|
||||||
|
|
||||||
it('should format a document size in a human readable way', () => {
|
|
||||||
assert.strictEqual(formatSize(500), '500 B')
|
|
||||||
assert.strictEqual(formatSize(900), '0.9 KB')
|
|
||||||
assert.strictEqual(formatSize(77.89 * 1000), '77.9 KB')
|
|
||||||
assert.strictEqual(formatSize(950 * 1000), '0.9 MB')
|
|
||||||
assert.strictEqual(formatSize(7.22 * 1000 * 1000), '7.2 MB')
|
|
||||||
assert.strictEqual(formatSize(945.4 * 1000 * 1000), '0.9 GB')
|
|
||||||
assert.strictEqual(formatSize(22.37 * 1000 * 1000 * 1000), '22.4 GB')
|
|
||||||
assert.strictEqual(formatSize(1000 * 1000 * 1000 * 1000), '1.0 TB')
|
|
||||||
})
|
|
||||||
|
|
||||||
it('should limit characters', () => {
|
|
||||||
assert.strictEqual(limitCharacters('hello world', 11), 'hello world')
|
|
||||||
assert.strictEqual(limitCharacters('hello world', 5), 'hello...')
|
|
||||||
assert.strictEqual(limitCharacters('hello world', 100), 'hello world')
|
|
||||||
})
|
|
||||||
|
|
||||||
it('should compile a JSON pointer', () => {
|
|
||||||
assert.strictEqual(compileJSONPointer(['foo', 'bar']), '/foo/bar')
|
|
||||||
assert.strictEqual(compileJSONPointer(['foo', '/~ ~/']), '/foo/~1~0 ~0~1')
|
|
||||||
assert.strictEqual(compileJSONPointer(['']), '/')
|
|
||||||
assert.strictEqual(compileJSONPointer([]), '')
|
|
||||||
})
|
|
||||||
|
|
||||||
it('should test whether a field is a timestamp', () => {
|
|
||||||
assert.strictEqual(isTimestamp('foo', 1574809200000), true)
|
|
||||||
assert.strictEqual(isTimestamp('foo', 1574809200000.2), false)
|
|
||||||
})
|
|
||||||
|
|
||||||
// TODO: thoroughly test all util methods
|
|
||||||
})
|
|
Loading…
Reference in New Issue