diff --git a/examples/08_custom_ace.html b/examples/08_custom_ace.html
new file mode 100644
index 0000000..39813fa
--- /dev/null
+++ b/examples/08_custom_ace.html
@@ -0,0 +1,61 @@
+
+
+
+ JSONEditor | Custom Ace Editor
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ In this example, the we use the minimalist version of jsoneditor and load
+ and configure Ace editor our selves.
+
+
+
+
+
+
+
diff --git a/gulpfile.js b/gulpfile.js
index 0e2e1f2..f38e373 100644
--- a/gulpfile.js
+++ b/gulpfile.js
@@ -81,8 +81,8 @@ var compilerMinimalist = webpack({
},
plugins: [
bannerPlugin,
- new webpack.NormalModuleReplacementPlugin(new RegExp('^brace$'), EMPTY),
- new webpack.NormalModuleReplacementPlugin(new RegExp('^ajv'), EMPTY),
+ new webpack.NormalModuleReplacementPlugin(new RegExp('^./assets/ace$'), EMPTY),
+ new webpack.NormalModuleReplacementPlugin(new RegExp('^ajv$'), EMPTY),
new webpack.optimize.UglifyJsPlugin()
],
module: {
@@ -165,4 +165,4 @@ gulp.task(WATCH, ['bundle'], function() {
})
// The default task (called when you run `gulp`)
-gulp.task('default', [ 'bundle', 'bundle-minimalist' ])
+gulp.task('default', [ 'bundle', 'bundle-minimalist', 'copy' ])
diff --git a/package.json b/package.json
index eb20cfc..15d81e7 100644
--- a/package.json
+++ b/package.json
@@ -23,18 +23,18 @@
"test": "ava test/*.test.js test/**/*.test.js --verbose"
},
"dependencies": {
- "ajv": "4.7.5",
+ "ajv": "4.7.7",
"brace": "0.8.0",
"javascript-natural-sort": "0.7.1",
- "lodash": "4.16.2",
- "preact": "6.1.0"
+ "lodash": "4.16.4",
+ "preact": "6.3.0"
},
"devDependencies": {
"ava": "0.16.0",
- "babel-core": "6.16.0",
+ "babel-core": "6.17.0",
"babel-loader": "6.2.5",
- "babel-preset-stage-2": "6.16.0",
- "babel-preset-stage-3": "6.16.0",
+ "babel-preset-stage-2": "6.17.0",
+ "babel-preset-stage-3": "6.17.0",
"browser-sync": "2.17.3",
"css-loader": "0.25.0",
"gulp": "3.9.1",
diff --git a/src/CodeMode.js b/src/CodeMode.js
new file mode 100644
index 0000000..02981d6
--- /dev/null
+++ b/src/CodeMode.js
@@ -0,0 +1,127 @@
+import { h } from 'preact'
+import TextMode from './TextMode'
+import ace from './assets/ace'
+
+/**
+ * CodeMode (powered by Ace editor)
+ *
+ * Usage:
+ *
+ *
+ *
+ * Methods:
+ *
+ * setText(text)
+ * getText() : text
+ * set(json : JSON)
+ * get() : JSON
+ * patch(actions: JSONPatch)
+ * format()
+ * compact()
+ * destroy()
+ *
+ */
+export default class CodeMode extends TextMode {
+ constructor (props) {
+ super(props)
+
+ this.state = {}
+
+ this.id = 'id' + Math.round(Math.random() * 1e6) // unique enough id within the JSONEditor
+ this.aceEditor = null
+ }
+
+ render (props, state) {
+ return h('div', {class: 'jsoneditor jsoneditor-mode-code'}, [
+ this.renderMenu(),
+
+ h('div', {class: 'jsoneditor-contents', id: this.id})
+ ])
+ }
+
+ componentDidMount () {
+ const options = this.props.options || {}
+
+ const container = this.base.querySelector('#' + this.id)
+
+ // use ace from bundle, and if not available try to use from global
+ const _ace = ace || window['ace']
+
+ let aceEditor = null
+ if (_ace && _ace.edit) {
+ // create ace editor
+ aceEditor = _ace.edit(container)
+
+ // bundle and load jsoneditor theme for ace editor
+ require('./assets/ace/theme-jsoneditor')
+
+ // configure ace editor
+ aceEditor.$blockScrolling = Infinity
+ aceEditor.setTheme('ace/theme/jsoneditor')
+ aceEditor.setShowPrintMargin(false)
+ aceEditor.setFontSize(13)
+ aceEditor.getSession().setMode('ace/mode/json')
+ aceEditor.getSession().setTabSize(options.indentation)
+ aceEditor.getSession().setUseSoftTabs(true)
+ aceEditor.getSession().setUseWrapMode(true)
+ aceEditor.commands.bindKey('Ctrl-L', null) // disable Ctrl+L (is used by the browser to select the address bar)
+ aceEditor.commands.bindKey('Command-L', null) // disable Ctrl+L (is used by the browser to select the address bar)
+ }
+ else {
+ // ace is excluded from the bundle.
+ }
+
+ // allow changing the config or completely replacing aceEditor
+ this.aceEditor = options.onLoadAce
+ ? options.onLoadAce(aceEditor, container, options) || aceEditor
+ : aceEditor
+
+ // register onchange event
+ this.aceEditor.on('change', this.handleChange)
+
+ // set initial text
+ this.setText('{}')
+ }
+
+ componentWillUnmount () {
+ this.destroy()
+ }
+
+ /**
+ * Destroy the editor
+ */
+ destroy () {
+ // neatly destroy ace editor
+ this.aceEditor.destroy()
+ }
+
+ componentDidUpdate () {
+ // TODO: handle changes in props
+ }
+
+ handleChange = () => {
+ // TODO: handle changes
+ // console.log('ace editor changed')
+ }
+
+ /**
+ * Set a string containing a JSON document
+ * @param {string} text
+ */
+ setText (text) {
+ this.aceEditor.setValue(text, -1)
+ }
+
+ /**
+ * Get the JSON document as text
+ * @return {string} text
+ */
+ getText () {
+ return this.aceEditor.getValue()
+ }
+}
\ No newline at end of file
diff --git a/src/TextMode.js b/src/TextMode.js
index d7d0136..7aa165d 100644
--- a/src/TextMode.js
+++ b/src/TextMode.js
@@ -3,8 +3,30 @@ import { parseJSON } from './utils/jsonUtils'
import { jsonToData, dataToJson, patchData } from './jsonData'
import ModeButton from './menu/ModeButton'
+/**
+ * TextMode
+ *
+ * Usage:
+ *
+ *
+ *
+ * Methods:
+ *
+ * setText(text)
+ * getText() : text
+ * set(json : JSON)
+ * get() : JSON
+ * patch(actions: JSONPatch)
+ * format()
+ * compact()
+ * destroy()
+ *
+ */
export default class TextMode extends Component {
- // TODO: define propTypes
constructor (props) {
super(props)
@@ -16,29 +38,7 @@ export default class TextMode extends Component {
render (props, state) {
return h('div', {class: 'jsoneditor jsoneditor-mode-text'}, [
- h('div', {class: 'jsoneditor-menu'}, [
- h('button', {
- class: 'jsoneditor-format',
- title: 'Format the JSON document',
- onClick: this.handleFormat
- }),
- h('button', {
- class: 'jsoneditor-compact',
- title: 'Compact the JSON document',
- onClick: this.handleCompact
- }),
-
- // TODO: implement a button "Fix JSON"
-
- h('div', {class: 'jsoneditor-vertical-menu-separator'}),
-
- this.props.options.modes && h(ModeButton, {
- modes: this.props.options.modes,
- mode: this.props.mode,
- onMode: this.props.onMode,
- onError: this.handleError
- })
- ]),
+ this.renderMenu(),
h('div', {class: 'jsoneditor-contents'}, [
h('textarea', {
@@ -50,10 +50,37 @@ export default class TextMode extends Component {
])
}
+ /** @protected */
+ renderMenu () {
+ return h('div', {class: 'jsoneditor-menu'}, [
+ h('button', {
+ class: 'jsoneditor-format',
+ title: 'Format the JSON document',
+ onClick: this.handleFormat
+ }),
+ h('button', {
+ class: 'jsoneditor-compact',
+ title: 'Compact the JSON document',
+ onClick: this.handleCompact
+ }),
+
+ // TODO: implement a button "Repair"
+
+ h('div', {class: 'jsoneditor-vertical-menu-separator'}),
+
+ this.props.options.modes && h(ModeButton, {
+ modes: this.props.options.modes,
+ mode: this.props.mode,
+ onChangeMode: this.props.onChangeMode,
+ onError: this.handleError
+ })
+ ])
+ }
+
/**
* Get the configured indentation
* @return {number}
- * @private
+ * @protected
*/
getIndentation () {
return this.props.options && this.props.options.indentation || 2
@@ -62,15 +89,13 @@ export default class TextMode extends Component {
/**
* handle changed text input in the textarea
* @param {Event} event
- * @private
+ * @protected
*/
handleChange = (event) => {
- this.setState({
- text: event.target.value
- })
+ this.setText(event.target.value)
}
- /** @private */
+ /** @protected */
handleFormat = () => {
try {
this.format()
@@ -80,7 +105,7 @@ export default class TextMode extends Component {
}
}
- /** @private */
+ /** @protected */
handleCompact = () => {
try {
this.compact()
@@ -90,7 +115,7 @@ export default class TextMode extends Component {
}
}
- /** @private */
+ /** @protected */
handleError = (err) => {
if (this.props.options && this.props.options.onError) {
this.props.options.onError(err)
@@ -145,9 +170,7 @@ export default class TextMode extends Component {
* @param {Object | Array | string | number | boolean | null} json JSON data
*/
set (json) {
- this.setState({
- text: JSON.stringify(json, null, this.getIndentation())
- })
+ this.setText(JSON.stringify(json, null, this.getIndentation()))
}
/**
@@ -155,7 +178,7 @@ export default class TextMode extends Component {
* @returns {Object | Array | string | number | boolean | null} json
*/
get () {
- return parseJSON(this.state.text)
+ return parseJSON(this.getText())
}
/**
@@ -173,4 +196,11 @@ export default class TextMode extends Component {
getText () {
return this.state.text
}
+
+ /**
+ * Destroy the editor
+ */
+ destroy () {
+
+ }
}
\ No newline at end of file
diff --git a/src/TreeMode.js b/src/TreeMode.js
index df07443..2dafd7e 100644
--- a/src/TreeMode.js
+++ b/src/TreeMode.js
@@ -48,7 +48,7 @@ export default class TreeMode extends Component {
}
render (props, state) {
- // TODO: make mode tree dynamic
+ // TODO: make mode tree dynamic: can be 'tree', 'form', 'view'
return h('div', {
class: 'jsoneditor jsoneditor-mode-tree',
'data-jsoneditor': 'true'
@@ -104,7 +104,7 @@ export default class TreeMode extends Component {
this.props.options.modes && h(ModeButton, {
modes: this.props.options.modes,
mode: this.props.mode,
- onMode: this.props.onMode,
+ onChangeMode: this.props.onChangeMode,
onError: this.handleError
})
])
@@ -365,6 +365,13 @@ export default class TreeMode extends Component {
})
}
+ /**
+ * Destroy the editor
+ */
+ destroy () {
+
+ }
+
/**
* Default function to determine whether or not to expand a node initially
*
diff --git a/src/assets/ace/index.js b/src/assets/ace/index.js
new file mode 100644
index 0000000..ebd2f5d
--- /dev/null
+++ b/src/assets/ace/index.js
@@ -0,0 +1,8 @@
+// load brace
+import ace from 'brace'
+
+// load required ace plugins
+import 'brace/mode/json'
+import 'brace/ext/searchbox'
+
+export default ace
diff --git a/src/assets/ace/theme-jsoneditor.js b/src/assets/ace/theme-jsoneditor.js
new file mode 100644
index 0000000..6cec3a6
--- /dev/null
+++ b/src/assets/ace/theme-jsoneditor.js
@@ -0,0 +1,144 @@
+/* ***** BEGIN LICENSE BLOCK *****
+ * Distributed under the BSD license:
+ *
+ * Copyright (c) 2010, Ajax.org B.V.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * * Neither the name of Ajax.org B.V. nor the
+ * names of its contributors may be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL AJAX.ORG B.V. BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * ***** END LICENSE BLOCK ***** */
+
+ace.define('ace/theme/jsoneditor', ['require', 'exports', 'module', 'ace/lib/dom'], function(acequire, exports, module) {
+
+exports.isDark = false;
+exports.cssClass = "ace-jsoneditor";
+exports.cssText = ".ace-jsoneditor .ace_gutter {\
+background: #ebebeb;\
+color: #333\
+}\
+\
+.ace-jsoneditor.ace_editor {\
+font-family: droid sans mono, consolas, monospace, courier new, courier, sans-serif;\
+line-height: 1.3;\
+}\
+.ace-jsoneditor .ace_print-margin {\
+width: 1px;\
+background: #e8e8e8\
+}\
+.ace-jsoneditor .ace_scroller {\
+background-color: #FFFFFF\
+}\
+.ace-jsoneditor .ace_text-layer {\
+color: gray\
+}\
+.ace-jsoneditor .ace_variable {\
+color: #1a1a1a\
+}\
+.ace-jsoneditor .ace_cursor {\
+border-left: 2px solid #000000\
+}\
+.ace-jsoneditor .ace_overwrite-cursors .ace_cursor {\
+border-left: 0px;\
+border-bottom: 1px solid #000000\
+}\
+.ace-jsoneditor .ace_marker-layer .ace_selection {\
+background: lightgray\
+}\
+.ace-jsoneditor.ace_multiselect .ace_selection.ace_start {\
+box-shadow: 0 0 3px 0px #FFFFFF;\
+border-radius: 2px\
+}\
+.ace-jsoneditor .ace_marker-layer .ace_step {\
+background: rgb(255, 255, 0)\
+}\
+.ace-jsoneditor .ace_marker-layer .ace_bracket {\
+margin: -1px 0 0 -1px;\
+border: 1px solid #BFBFBF\
+}\
+.ace-jsoneditor .ace_marker-layer .ace_active-line {\
+background: #FFFBD1\
+}\
+.ace-jsoneditor .ace_gutter-active-line {\
+background-color : #dcdcdc\
+}\
+.ace-jsoneditor .ace_marker-layer .ace_selected-word {\
+border: 1px solid lightgray\
+}\
+.ace-jsoneditor .ace_invisible {\
+color: #BFBFBF\
+}\
+.ace-jsoneditor .ace_keyword,\
+.ace-jsoneditor .ace_meta,\
+.ace-jsoneditor .ace_support.ace_constant.ace_property-value {\
+color: #AF956F\
+}\
+.ace-jsoneditor .ace_keyword.ace_operator {\
+color: #484848\
+}\
+.ace-jsoneditor .ace_keyword.ace_other.ace_unit {\
+color: #96DC5F\
+}\
+.ace-jsoneditor .ace_constant.ace_language {\
+color: darkorange\
+}\
+.ace-jsoneditor .ace_constant.ace_numeric {\
+color: red\
+}\
+.ace-jsoneditor .ace_constant.ace_character.ace_entity {\
+color: #BF78CC\
+}\
+.ace-jsoneditor .ace_invalid {\
+color: #FFFFFF;\
+background-color: #FF002A;\
+}\
+.ace-jsoneditor .ace_fold {\
+background-color: #AF956F;\
+border-color: #000000\
+}\
+.ace-jsoneditor .ace_storage,\
+.ace-jsoneditor .ace_support.ace_class,\
+.ace-jsoneditor .ace_support.ace_function,\
+.ace-jsoneditor .ace_support.ace_other,\
+.ace-jsoneditor .ace_support.ace_type {\
+color: #C52727\
+}\
+.ace-jsoneditor .ace_string {\
+color: green\
+}\
+.ace-jsoneditor .ace_comment {\
+color: #BCC8BA\
+}\
+.ace-jsoneditor .ace_entity.ace_name.ace_tag,\
+.ace-jsoneditor .ace_entity.ace_other.ace_attribute-name {\
+color: #606060\
+}\
+.ace-jsoneditor .ace_markup.ace_underline {\
+text-decoration: underline\
+}\
+.ace-jsoneditor .ace_indent-guide {\
+background: url(\"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAACCAYAAACZgbYnAAAAE0lEQVQImWP4////f4bLly//BwAmVgd1/w11/gAAAABJRU5ErkJggg==\") right repeat-y\
+}";
+
+var dom = acequire("../lib/dom");
+dom.importCssString(exports.cssText, exports.cssClass);
+});
diff --git a/src/develop.html b/src/develop.html
index 5d9bfb6..3d93dde 100644
--- a/src/develop.html
+++ b/src/develop.html
@@ -7,7 +7,7 @@
-
+